Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 4 Dec 2009 21:06:55 +0000 (UTC)
From:      Alexander Leidinger <netchild@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r200110 - head/sys/compat/linux
Message-ID:  <200912042106.nB4L6tsr065541@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: netchild
Date: Fri Dec  4 21:06:54 2009
New Revision: 200110
URL: http://svn.freebsd.org/changeset/base/200110

Log:
  This is v4l support for the linuxulator. This allows to access FreeBSD
  native devices which support the v4l API from processes running within
  the linuxulator, e.g. skype or flash can access the multimedia/pwcbsd driver.
  
  Not tested is firmware upload, framebuffer stuff and video tuner stuff
  due to lack of hardware.
  The clipping part (VIDIOCSWIN) needs a little bit of further work (partly
  in progress, but can not be tested due to lack of a suitable device).
  
  The submitter tested this sucessfully with Skype and flash apps on amd64 and
  i386 with the multimedia/pwcbsd driver.
  
  Submitted by:	J.R. Oldroyd <fbsd@opal.com>

Added:
  head/sys/compat/linux/linux_videodev_compat.h   (contents, props changed)
Modified:
  head/sys/compat/linux/linux_ioctl.c
  head/sys/compat/linux/linux_ioctl.h
  head/sys/compat/linux/linux_videodev.h   (contents, props changed)

Modified: head/sys/compat/linux/linux_ioctl.c
==============================================================================
--- head/sys/compat/linux/linux_ioctl.c	Fri Dec  4 20:46:45 2009	(r200109)
+++ head/sys/compat/linux/linux_ioctl.c	Fri Dec  4 21:06:54 2009	(r200110)
@@ -78,6 +78,9 @@ __FBSDID("$FreeBSD$");
 #include <compat/linux/linux_socket.h>
 #include <compat/linux/linux_util.h>
 
+#include <compat/linux/linux_videodev.h>
+#include <compat/linux/linux_videodev_compat.h>
+
 CTASSERT(LINUX_IFNAMSIZ == IFNAMSIZ);
 
 static linux_ioctl_function_t linux_ioctl_cdrom;
@@ -91,6 +94,7 @@ static linux_ioctl_function_t linux_ioct
 static linux_ioctl_function_t linux_ioctl_private;
 static linux_ioctl_function_t linux_ioctl_drm;
 static linux_ioctl_function_t linux_ioctl_sg;
+static linux_ioctl_function_t linux_ioctl_v4l;
 static linux_ioctl_function_t linux_ioctl_special;
 
 static struct linux_ioctl_handler cdrom_handler =
@@ -115,6 +119,8 @@ static struct linux_ioctl_handler drm_ha
 { linux_ioctl_drm, LINUX_IOCTL_DRM_MIN, LINUX_IOCTL_DRM_MAX };
 static struct linux_ioctl_handler sg_handler =
 { linux_ioctl_sg, LINUX_IOCTL_SG_MIN, LINUX_IOCTL_SG_MAX };
+static struct linux_ioctl_handler video_handler =
+{ linux_ioctl_v4l, LINUX_IOCTL_VIDEO_MIN, LINUX_IOCTL_VIDEO_MAX };
 
 DATA_SET(linux_ioctl_handler_set, cdrom_handler);
 DATA_SET(linux_ioctl_handler_set, vfat_handler);
@@ -127,6 +133,7 @@ DATA_SET(linux_ioctl_handler_set, termio
 DATA_SET(linux_ioctl_handler_set, private_handler);
 DATA_SET(linux_ioctl_handler_set, drm_handler);
 DATA_SET(linux_ioctl_handler_set, sg_handler);
+DATA_SET(linux_ioctl_handler_set, video_handler);
 
 struct handler_element
 {
@@ -2589,6 +2596,295 @@ linux_ioctl_sg(struct thread *td, struct
 }
 
 /*
+ * Video4Linux (V4L) ioctl handler
+ */
+static int
+linux_to_bsd_v4l_tuner(struct l_video_tuner *lvt, struct video_tuner *vt)
+{
+	vt->tuner = lvt->tuner;
+	strlcpy(vt->name, lvt->name, LINUX_VIDEO_TUNER_NAME_SIZE);
+	vt->rangelow = lvt->rangelow;	/* possible long size conversion */
+	vt->rangehigh = lvt->rangehigh;	/* possible long size conversion */
+	vt->flags = lvt->flags;
+	vt->mode = lvt->mode;
+	vt->signal = lvt->signal;
+	return (0);
+}
+
+static int
+bsd_to_linux_v4l_tuner(struct video_tuner *vt, struct l_video_tuner *lvt)
+{
+	lvt->tuner = vt->tuner;
+	strlcpy(lvt->name, vt->name, LINUX_VIDEO_TUNER_NAME_SIZE);
+	lvt->rangelow = vt->rangelow;	/* possible long size conversion */
+	lvt->rangehigh = vt->rangehigh;	/* possible long size conversion */
+	lvt->flags = vt->flags;
+	lvt->mode = vt->mode;
+	lvt->signal = vt->signal;
+	return (0);
+}
+
+#if 0
+static int
+linux_to_bsd_v4l_clip(struct l_video_clip *lvc, struct video_clip *vc)
+{
+	vc->x = lvc->x;
+	vc->y = lvc->y;
+	vc->width = lvc->width;
+	vc->height = lvc->height;
+	vc->next = PTRIN(lvc->next);	/* possible pointer size conversion */
+	return (0);
+}
+#endif
+
+static int
+linux_to_bsd_v4l_window(struct l_video_window *lvw, struct video_window *vw)
+{
+	vw->x = lvw->x;
+	vw->y = lvw->y;
+	vw->width = lvw->width;
+	vw->height = lvw->height;
+	vw->chromakey = lvw->chromakey;
+	vw->flags = lvw->flags;
+	vw->clips = PTRIN(lvw->clips);	/* possible pointer size conversion */
+	vw->clipcount = lvw->clipcount;
+	return (0);
+}
+
+static int
+bsd_to_linux_v4l_window(struct video_window *vw, struct l_video_window *lvw)
+{
+	lvw->x = vw->x;
+	lvw->y = vw->y;
+	lvw->width = vw->width;
+	lvw->height = vw->height;
+	lvw->chromakey = vw->chromakey;
+	lvw->flags = vw->flags;
+	lvw->clips = PTROUT(vw->clips);	/* possible pointer size conversion */
+	lvw->clipcount = vw->clipcount;
+	return (0);
+}
+
+static int
+linux_to_bsd_v4l_buffer(struct l_video_buffer *lvb, struct video_buffer *vb)
+{
+	vb->base = PTRIN(lvb->base);	/* possible pointer size conversion */
+	vb->height = lvb->height;
+	vb->width = lvb->width;
+	vb->depth = lvb->depth;
+	vb->bytesperline = lvb->bytesperline;
+	return (0);
+}
+
+static int
+bsd_to_linux_v4l_buffer(struct video_buffer *vb, struct l_video_buffer *lvb)
+{
+	lvb->base = PTROUT(vb->base);	/* possible pointer size conversion */
+	lvb->height = vb->height;
+	lvb->width = vb->width;
+	lvb->depth = vb->depth;
+	lvb->bytesperline = vb->bytesperline;
+	return (0);
+}
+
+static int
+linux_to_bsd_v4l_code(struct l_video_code *lvc, struct video_code *vc)
+{
+	strlcpy(vc->loadwhat, lvc->loadwhat, LINUX_VIDEO_CODE_LOADWHAT_SIZE);
+	vc->datasize = lvc->datasize;
+	vc->data = PTRIN(lvc->data);	/* possible pointer size conversion */
+	return (0);
+}
+
+#if 0
+static int
+linux_v4l_cliplist_copy(struct l_video_window *lvw, struct video_window *vw)
+{
+	struct video_clip vclip;
+	struct l_video_clip l_vclip;
+	struct video_clip **ppvc;
+	struct l_video_clip *plvc;
+	int error;
+
+	ppvc = &(vw->clips);
+	for (plvc = (struct l_video_clip *) PTRIN(lvw->clips);
+	    plvc != NULL;
+	    plvc = (struct l_video_clip *) PTRIN(plvc->next)) {
+		error = copyin((void *) plvc, &l_vclip, sizeof(l_vclip));
+		if (error) return (error);
+		linux_to_bsd_v4l_clip(&l_vclip, &vclip);
+		/* XXX: If there can be no concurrency: s/M_NOWAIT/M_WAITOK/ */
+		if ((*ppvc = malloc(sizeof(**ppvc), M_LINUX, M_NOWAIT)) == NULL)
+			return (ENOMEM);    /* XXX: linux has no ENOMEM here */
+		memcpy(&vclip, *ppvc, sizeof(vclip));
+		ppvc = &((*ppvc)->next);
+	}
+	return (0);
+}
+
+static int
+linux_v4l_cliplist_free(struct video_window *vw)
+{
+	struct video_clip **ppvc;
+	struct video_clip **ppvc_next;
+
+	for (ppvc = &(vw->clips); *ppvc != NULL; ppvc = ppvc_next) {
+		ppvc_next = &((*ppvc)->next);
+		free(*ppvc, M_LINUX);
+	}
+	return (0);
+}
+#endif
+
+static int
+linux_ioctl_v4l(struct thread *td, struct linux_ioctl_args *args)
+{
+	struct file *fp;
+	int error;
+	struct video_tuner vtun;
+	struct video_window vwin;
+	struct video_buffer vbuf;
+	struct video_code vcode;
+	struct l_video_tuner l_vtun;
+	struct l_video_window l_vwin;
+	struct l_video_buffer l_vbuf;
+	struct l_video_code l_vcode;
+
+	switch (args->cmd & 0xffff) {
+	case LINUX_VIDIOCGCAP:		args->cmd = VIDIOCGCAP; break;
+	case LINUX_VIDIOCGCHAN:		args->cmd = VIDIOCGCHAN; break;
+	case LINUX_VIDIOCSCHAN:		args->cmd = VIDIOCSCHAN; break;
+
+	case LINUX_VIDIOCGTUNER:
+		if ((error = fget(td, args->fd, &fp)) != 0)
+			return (error);
+		error = fo_ioctl(fp, VIDIOCGTUNER, &vtun, td->td_ucred, td);
+		if (!error) {
+			bsd_to_linux_v4l_tuner(&vtun, &l_vtun);
+			error = copyout(&l_vtun, (void *) args->arg,
+			    sizeof(l_vtun));
+		}
+		fdrop(fp, td);
+		return (error);
+
+	case LINUX_VIDIOCSTUNER:
+		if ((error = fget(td, args->fd, &fp)) != 0)
+			return (error);
+		error = copyin((void *) args->arg, &l_vtun, sizeof(l_vtun));
+		if (error) {
+			fdrop(fp, td);
+			return (error);
+		}
+		linux_to_bsd_v4l_tuner(&l_vtun, &vtun);
+		error = fo_ioctl(fp, VIDIOCSMICROCODE, &vtun, td->td_ucred, td);
+		fdrop(fp, td);
+		return (error);
+
+	case LINUX_VIDIOCGPICT:		args->cmd = VIDIOCGPICT; break;
+	case LINUX_VIDIOCSPICT:		args->cmd = VIDIOCSPICT; break;
+	case LINUX_VIDIOCCAPTURE:	args->cmd = VIDIOCCAPTURE; break;
+
+	case LINUX_VIDIOCGWIN:
+		if ((error = fget(td, args->fd, &fp)) != 0)
+			return (error);
+		error = fo_ioctl(fp, VIDIOCGWIN, &vwin, td->td_ucred, td);
+		if (!error) {
+			bsd_to_linux_v4l_window(&vwin, &l_vwin);
+			error = copyout(&l_vwin, (void *) args->arg,
+			    sizeof(l_vwin));
+		}
+		fdrop(fp, td);
+		return (error);
+
+	case LINUX_VIDIOCSWIN:
+		if ((error = fget(td, args->fd, &fp)) != 0)
+			return (error);
+		error = copyin((void *) args->arg, &l_vwin, sizeof(l_vwin));
+		if (error) {
+			fdrop(fp, td);
+			return (error);
+		}
+		linux_to_bsd_v4l_window(&l_vwin, &vwin);
+#if 0
+		/*
+		 * XXX: some Linux apps call SWIN but do not store valid
+		 *	values in clipcount or in the clips pointer.  Until
+		 *	we have someone calling to support this, the code
+		 *	to handle the list of video_clip structures is removed.
+		 */
+		error = linux_v4l_cliplist_copy(&l_vwin, &vwin);
+#endif
+		if (!error)
+			error = fo_ioctl(fp, VIDIOCSWIN, &vwin, td->td_ucred, td);
+		fdrop(fp, td);
+#if 0
+		linux_v4l_cliplist_free(&vwin);
+#endif
+		return (error);
+
+	case LINUX_VIDIOCGFBUF:
+		if ((error = fget(td, args->fd, &fp)) != 0)
+			return (error);
+		error = fo_ioctl(fp, VIDIOCGFBUF, &vbuf, td->td_ucred, td);
+		if (!error) {
+			bsd_to_linux_v4l_buffer(&vbuf, &l_vbuf);
+			error = copyout(&l_vbuf, (void *) args->arg,
+			    sizeof(l_vbuf));
+		}
+		fdrop(fp, td);
+		return (error);
+
+	case LINUX_VIDIOCSFBUF:
+		if ((error = fget(td, args->fd, &fp)) != 0)
+			return (error);
+		error = copyin((void *) args->arg, &l_vbuf, sizeof(l_vbuf));
+		if (error) {
+			fdrop(fp, td);
+			return (error);
+		}
+		linux_to_bsd_v4l_buffer(&l_vbuf, &vbuf);
+		error = fo_ioctl(fp, VIDIOCSFBUF, &vbuf, td->td_ucred, td);
+		fdrop(fp, td);
+		return (error);
+
+	case LINUX_VIDIOCKEY:		args->cmd = VIDIOCKEY; break;
+	case LINUX_VIDIOCGFREQ:		args->cmd = VIDIOCGFREQ; break;
+	case LINUX_VIDIOCSFREQ:		args->cmd = VIDIOCSFREQ; break;
+	case LINUX_VIDIOCGAUDIO:	args->cmd = VIDIOCGAUDIO; break;
+	case LINUX_VIDIOCSAUDIO:	args->cmd = VIDIOCSAUDIO; break;
+	case LINUX_VIDIOCSYNC:		args->cmd = VIDIOCSYNC; break;
+	case LINUX_VIDIOCMCAPTURE:	args->cmd = VIDIOCMCAPTURE; break;
+	case LINUX_VIDIOCGMBUF:		args->cmd = VIDIOCGMBUF; break;
+	case LINUX_VIDIOCGUNIT:		args->cmd = VIDIOCGUNIT; break;
+	case LINUX_VIDIOCGCAPTURE:	args->cmd = VIDIOCGCAPTURE; break;
+	case LINUX_VIDIOCSCAPTURE:	args->cmd = VIDIOCSCAPTURE; break;
+	case LINUX_VIDIOCSPLAYMODE:	args->cmd = VIDIOCSPLAYMODE; break;
+	case LINUX_VIDIOCSWRITEMODE:	args->cmd = VIDIOCSWRITEMODE; break;
+	case LINUX_VIDIOCGPLAYINFO:	args->cmd = VIDIOCGPLAYINFO; break;
+
+	case LINUX_VIDIOCSMICROCODE:
+		if ((error = fget(td, args->fd, &fp)) != 0)
+			return (error);
+		error = copyin((void *) args->arg, &l_vcode, sizeof(l_vcode));
+		if (error) {
+			fdrop(fp, td);
+			return (error);
+		}
+		linux_to_bsd_v4l_code(&l_vcode, &vcode);
+		error = fo_ioctl(fp, VIDIOCSTUNER, &vcode, td->td_ucred, td);
+		fdrop(fp, td);
+		return (error);
+
+	case LINUX_VIDIOCGVBIFMT:	args->cmd = VIDIOCGVBIFMT; break;
+	case LINUX_VIDIOCSVBIFMT:	args->cmd = VIDIOCSVBIFMT; break;
+	default:			return (ENOIOCTL);
+	}
+
+	error = ioctl(td, (struct ioctl_args *)args);
+	return (error);
+}
+
+/*
  * Special ioctl handler
  */
 static int

Modified: head/sys/compat/linux/linux_ioctl.h
==============================================================================
--- head/sys/compat/linux/linux_ioctl.h	Fri Dec  4 20:46:45 2009	(r200109)
+++ head/sys/compat/linux/linux_ioctl.h	Fri Dec  4 21:06:54 2009	(r200110)
@@ -575,4 +575,40 @@
 struct ifnet;
 int		 linux_ifname(struct ifnet *, char *, size_t);
 
+/*
+ * video
+ */
+#define LINUX_VIDIOCGCAP		0x7601
+#define LINUX_VIDIOCGCHAN		0x7602
+#define LINUX_VIDIOCSCHAN		0x7603
+#define LINUX_VIDIOCGTUNER		0x7604
+#define LINUX_VIDIOCSTUNER		0x7605
+#define LINUX_VIDIOCGPICT		0x7606
+#define LINUX_VIDIOCSPICT		0x7607
+#define LINUX_VIDIOCCAPTURE		0x7608
+#define LINUX_VIDIOCGWIN		0x7609
+#define LINUX_VIDIOCSWIN		0x760a
+#define LINUX_VIDIOCGFBUF		0x760b
+#define LINUX_VIDIOCSFBUF		0x760c
+#define LINUX_VIDIOCKEY			0x760d
+#define LINUX_VIDIOCGFREQ		0x760e
+#define LINUX_VIDIOCSFREQ		0x760f
+#define LINUX_VIDIOCGAUDIO		0x7610
+#define LINUX_VIDIOCSAUDIO		0x7611
+#define LINUX_VIDIOCSYNC		0x7623
+#define LINUX_VIDIOCMCAPTURE		0x7613
+#define LINUX_VIDIOCGMBUF		0x7614
+#define LINUX_VIDIOCGUNIT		0x7615
+#define LINUX_VIDIOCGCAPTURE		0x7616
+#define LINUX_VIDIOCSCAPTURE		0x7617
+#define LINUX_VIDIOCSPLAYMODE		0x7618
+#define LINUX_VIDIOCSWRITEMODE		0x7619
+#define LINUX_VIDIOCGPLAYINFO		0x761a
+#define LINUX_VIDIOCSMICROCODE		0x761b
+#define LINUX_VIDIOCGVBIFMT		0x761c
+#define LINUX_VIDIOCSVBIFMT		0x761d
+
+#define LINUX_IOCTL_VIDEO_MIN	LINUX_VIDIOCGCAP
+#define LINUX_IOCTL_VIDEO_MAX	LINUX_VIDIOCSVBIFMT
+
 #endif /* !_LINUX_IOCTL_H_ */

Modified: head/sys/compat/linux/linux_videodev.h
==============================================================================
--- head/sys/compat/linux/linux_videodev.h	Fri Dec  4 20:46:45 2009	(r200109)
+++ head/sys/compat/linux/linux_videodev.h	Fri Dec  4 21:06:54 2009	(r200110)
@@ -1,48 +1,41 @@
+/*
+ * This header comes from linux, but it has no license. The author
+ * (Alan Cox @ Redhat) gave explicit permissions to use it in FreeBSD.
+ * The freeBSD vendor branch for v4l gives a more detailed description
+ * about this.
+ *
+ * $FreeBSD$
+ */
+
 #ifndef __LINUX_VIDEODEV_H
 #define __LINUX_VIDEODEV_H
 
-#include <linux/types.h>
+#include <sys/types.h>
+typedef int32_t __s32;
+typedef uint32_t __u32;
+typedef uint16_t __u16;
+typedef uint8_t __u8;
 
+#if 0
 #define HAVE_V4L1 1
 
 #include <linux/videodev2.h>
+#endif 
 
-#ifdef __KERNEL__
-
-#include <linux/mm.h>
-
-extern struct video_device* video_devdata(struct file*);
-
-#define to_video_device(cd) container_of(cd, struct video_device, class_dev)
-static inline void
-video_device_create_file(struct video_device *vfd,
-			 struct class_device_attribute *attr)
-{
-	class_device_create_file(&vfd->class_dev, attr);
-}
-static inline void
-video_device_remove_file(struct video_device *vfd,
-			 struct class_device_attribute *attr)
-{
-	class_device_remove_file(&vfd->class_dev, attr);
-}
-
-#if OBSOLETE_OWNER /* to be removed in 2.6.15 */
-/* helper functions to access driver private data. */
-static inline void *video_get_drvdata(struct video_device *dev)
-{
-	return dev->priv;
-}
-
-static inline void video_set_drvdata(struct video_device *dev, void *data)
-{
-	dev->priv = data;
-}
-#endif
-
-extern int video_exclusive_open(struct inode *inode, struct file *file);
-extern int video_exclusive_release(struct inode *inode, struct file *file);
-#endif /* __KERNEL__ */
+#define VID_TYPE_CAPTURE	1	/* Can capture */
+#define VID_TYPE_TUNER		2	/* Can tune */
+#define VID_TYPE_TELETEXT	4	/* Does teletext */
+#define VID_TYPE_OVERLAY	8	/* Overlay onto frame buffer */
+#define VID_TYPE_CHROMAKEY	16	/* Overlay by chromakey */
+#define VID_TYPE_CLIPPING	32	/* Can clip */
+#define VID_TYPE_FRAMERAM	64	/* Uses the frame buffer memory */
+#define VID_TYPE_SCALES		128	/* Scalable */
+#define VID_TYPE_MONOCHROME	256	/* Monochrome only */
+#define VID_TYPE_SUBCAPTURE	512	/* Can capture subareas of the image */
+#define VID_TYPE_MPEG_DECODER	1024	/* Can decode MPEG streams */
+#define VID_TYPE_MPEG_ENCODER	2048	/* Can encode MPEG streams */
+#define VID_TYPE_MJPEG_DECODER	4096	/* Can decode MJPEG streams */
+#define VID_TYPE_MJPEG_ENCODER	8192	/* Can encode MJPEG streams */
 
 struct video_capability
 {
@@ -157,7 +150,7 @@ struct video_window
 	__u32	width,height;		/* Its size */
 	__u32	chromakey;
 	__u32	flags;
-	struct	video_clip __user *clips;	/* Set only */
+	struct	video_clip *clips;	/* Set only */
 	int	clipcount;
 #define VIDEO_WINDOW_INTERLACE	1
 #define VIDEO_WINDOW_CHROMAKEY	16	/* Overlay by chromakey */
@@ -197,6 +190,8 @@ struct video_key
 	__u32	flags;
 };
 
+#define VIDEO_MAX_FRAME		32
+
 struct video_mbuf
 {
 	int	size;		/* Total memory to map */

Added: head/sys/compat/linux/linux_videodev_compat.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/compat/linux/linux_videodev_compat.h	Fri Dec  4 21:06:54 2009	(r200110)
@@ -0,0 +1,59 @@
+/*
+ * $FreeBSD$
+ */
+
+/*
+ * This file defines compatibility versions of several video structures
+ * defined in the Linux videodev.h header (linux_videodev.h).  The
+ * structures defined in this file are the ones that have been determined
+ * to have 32- to 64-bit size dependencies.
+ */
+
+#ifndef _LINUX_VIDEODEV_COMPAT_H_
+#define	_LINUX_VIDEODEV_COMPAT_H_
+
+struct l_video_tuner
+{
+	l_int		tuner;
+#define LINUX_VIDEO_TUNER_NAME_SIZE	32
+	char		name[LINUX_VIDEO_TUNER_NAME_SIZE];
+	l_ulong		rangelow, rangehigh;
+	uint32_t	flags;
+	uint16_t	mode;
+	uint16_t	signal;
+};
+
+struct l_video_clip
+{
+	int32_t		x, y;
+	int32_t		width, height;
+	l_uintptr_t	next;
+};
+
+struct l_video_window
+{
+	uint32_t	x, y;
+	uint32_t	width, height;
+	uint32_t	chromakey;
+	uint32_t	flags;
+	l_uintptr_t	clips;
+	l_int		clipcount;
+};
+
+struct l_video_buffer
+{
+	l_uintptr_t	base;
+	l_int		height, width;
+	l_int		depth;
+	l_int		bytesperline;
+};
+
+struct l_video_code
+{
+#define LINUX_VIDEO_CODE_LOADWHAT_SIZE	16
+	char		loadwhat[LINUX_VIDEO_CODE_LOADWHAT_SIZE];
+	l_int		datasize;
+	l_uintptr_t	data;
+};
+
+#endif /* !_LINUX_VIDEODEV_COMPAT_H_ */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200912042106.nB4L6tsr065541>