Thanks for your answer,
a few problems though:
> The videodevX package Supports v4l1 and v4l2 and should work on 2.4.0
> series kernels.
It needed some hacking to get it to work. Attached is the patch
against videodevX-050900.tgz. It works for me (2.4.0test8 kernel), and
all the code I introduced is ifdef'ed KERNEL_VERSION >= 2.4.0. The only
missing thing is /proc filesystem support for devices registered with
v4l2_register_device (video_register_device has /proc support).
Just one problem. In the original driver vm_area_struct.vm_offset is
used. Since that doesn't appear to exist in newer kernels, i've used
vm_area_struct.vm_pgoff*PAGE_SIZE, but i don't really know if that's
correct. I works with videodevX+bttv and videodevX+bttv2, though.
The bttv2 driver compiled without problems.
regards,
I�aki
PD: I've noticed some possible bugs in the bttv2 driver. Should I mail
them to you directly or to the list?
--- videodevX.c Tue Sep 5 08:49:08 2000
+++ videodevX.c Sun Oct 1 18:16:49 2000
@@ -362,6 +362,14 @@
return -EINVAL;
}
+/* forward declarations */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+ static void videodev_proc_create_dev (struct video_device *vfd, char *name);
+ static void videodev_proc_destroy_dev (struct video_device *vfd);
+#endif
+#endif
+
/*
* Video For Linux device drivers request registration here.
*/
@@ -372,24 +380,29 @@
int base;
int err;
int end;
+ char *name_base;
switch(type)
{
case VFL_TYPE_GRABBER:
base=0;
end=64;
+ name_base = "video";
break;
case VFL_TYPE_VTX:
base=192;
end=224;
+ name_base = "vtx";
break;
case VFL_TYPE_VBI:
base=224;
end=240;
+ name_base = "vbi";
break;
case VFL_TYPE_RADIO:
base=64;
end=128;
+ name_base = "radio";
break;
default:
return -1;
@@ -399,6 +412,8 @@
{
if((video_device[i]==NULL)&&(v4l2_device[i]==NULL))
{
+ char name[16];
+
video_device[i]=vfd;
vfd->minor=i;
/* The init call may sleep so we book the slot out
@@ -414,6 +429,13 @@
return err;
}
}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+ sprintf (name, "%s%d", name_base, i - base);
+ videodev_proc_create_dev (vfd, name);
+#endif
+#endif
return 0;
}
}
@@ -428,14 +450,17 @@
{
if(video_device[vfd->minor]!=vfd)
panic("vfd: bad unregister");
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+ videodev_proc_destroy_dev (vfd);
+#endif
+#endif
+
video_device[vfd->minor]=NULL;
MOD_DEC_USE_COUNT;
}
-
-
-
-
/*
* Active devices
*/
@@ -657,14 +682,23 @@
/* For v4l compatibility. v4l apps typically pass zero */
/* for the offset, so replace it with the real value */
/* saved from before */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
if (vma->vm_offset == 0 && vfl->ioctl)
+#else
+ if (vma->vm_pgoff == 0 && vfl->ioctl)
+#endif
{
struct v4l2_buffer buf;
buf.index = 0;
buf.type = V4L2_BUF_TYPE_CAPTURE;
if (vfl->ioctl(file->private_data,
VIDIOC_QUERYBUF, &buf) == 0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
vma->vm_offset = buf.offset;
+#else
+ /* FIXME: where is vm_offset in newer kernels? */
+ vma->vm_pgoff = buf.offset/PAGE_SIZE;
+#endif
}
err = vfl->mmap(file->private_data, vma);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,3)
@@ -914,6 +948,7 @@
#ifndef HAVE_DO_SELECT
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
/* Note: This code, inside the #ifndef HAVE_DO_SELECT ... #endif,
is copied from fs/select.c, and is only included
here because do_select() is not exported to modules. In the future
@@ -1016,6 +1051,145 @@
unlock_kernel();
return retval;
}
+#else // KERNEL_VERSION < 2.4.0
+#include <linux/smp_lock.h>
+#include <linux/file.h>
+
+#define MEM(i,m) ((m)+(unsigned)(i)/__NFDBITS)
+#define ISSET(i,m) (((i)&*(m)) != 0)
+
+#define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)
+
+#undef __IN
+#undef __OUT
+#define __IN(fds, n) (fds->in + n)
+#define __OUT(fds, n) (fds->out + n)
+#define __EX(fds, n) (fds->ex + n)
+#define __RES_IN(fds, n) (fds->res_in + n)
+#define __RES_OUT(fds, n) (fds->res_out + n)
+#define __RES_EX(fds, n) (fds->res_ex + n)
+
+#define BITS(fds, n) (*__IN(fds, n)|*__OUT(fds, n)|*__EX(fds, n))
+
+#define POLLIN_SET (POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR)
+#define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
+#define POLLEX_SET (POLLPRI)
+/* This stuff comes from the fs/select.c present in a 2.0.0test8 */
+/* kernel. It should be kept in sync with that. */
+static int my_max_select_fd(unsigned long n, fd_set_bits *fds)
+{
+ unsigned long *open_fds;
+ unsigned long set;
+ int max;
+
+ /* handle last in-complete long-word first */
+ set = ~(~0UL << (n & (__NFDBITS-1)));
+ n /= __NFDBITS;
+ open_fds = current->files->open_fds->fds_bits+n;
+ max = 0;
+ if (set) {
+ set &= BITS(fds, n);
+ if (set) {
+ if (!(set & ~*open_fds))
+ goto get_max;
+ return -EBADF;
+ }
+ }
+ while (n) {
+ open_fds--;
+ n--;
+ set = BITS(fds, n);
+ if (!set)
+ continue;
+ if (set & ~*open_fds)
+ return -EBADF;
+ if (max)
+ continue;
+get_max:
+ do {
+ max++;
+ set >>= 1;
+ } while (set);
+ max += n * __NFDBITS;
+ }
+
+ return max;
+}
+
+static int
+my_do_select(int n, fd_set_bits *fds, long *timeout)
+{
+ poll_table table, *wait;
+ int retval, i, off;
+ long __timeout = *timeout;
+
+ read_lock(¤t->files->file_lock);
+ retval = my_max_select_fd(n, fds);
+ read_unlock(¤t->files->file_lock);
+
+ if (retval < 0)
+ return retval;
+ n = retval;
+
+ poll_initwait(&table);
+ wait = &table;
+ if (!__timeout)
+ wait = NULL;
+ retval = 0;
+ for (;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ for (i = 0 ; i < n; i++) {
+ unsigned long bit = BIT(i);
+ unsigned long mask;
+ struct file *file;
+
+ off = i / __NFDBITS;
+ if (!(bit & BITS(fds, off)))
+ continue;
+ file = fget(i);
+ mask = POLLNVAL;
+ if (file) {
+ mask = DEFAULT_POLLMASK;
+ if (file->f_op && file->f_op->poll)
+ mask = file->f_op->poll(file, wait);
+ fput(file);
+ }
+ if ((mask & POLLIN_SET) && ISSET(bit, __IN(fds,off))) {
+ SET(bit, __RES_IN(fds,off));
+ retval++;
+ wait = NULL;
+ }
+ if ((mask & POLLOUT_SET) && ISSET(bit, __OUT(fds,off))) {
+ SET(bit, __RES_OUT(fds,off));
+ retval++;
+ wait = NULL;
+ }
+ if ((mask & POLLEX_SET) && ISSET(bit, __EX(fds,off))) {
+ SET(bit, __RES_EX(fds,off));
+ retval++;
+ wait = NULL;
+ }
+ }
+ wait = NULL;
+ if (retval || !__timeout || signal_pending(current))
+ break;
+ if(table.error) {
+ retval = table.error;
+ break;
+ }
+ __timeout = schedule_timeout(__timeout);
+ }
+ current->state = TASK_RUNNING;
+
+ poll_freewait(&table);
+
+ /*
+ * Up-to-date the caller timeout.
+ */
+ *timeout = __timeout;
+ return retval;
+}
+#endif // KERNEL_VERSION 2.4.0
#endif // HAVE_DO_SELECT
static int
@@ -1056,7 +1230,11 @@
memset(bits, 0, 6 * size);
SET(BIT(fd), __FD_IN((&fds), fd / __NFDBITS));
+#ifndef HAVE_DO_SELECT
ret = my_do_select(n, &fds, &timeout);
+#else
+ ret = do_select(n, &fds, &timeout);
+#endif
if (ret < 0)
goto out;
@@ -1774,8 +1952,7 @@
MOD_DEC_USE_COUNT;
}
-
-#ifdef CONFIG_PROC_FS
+#if defined(CONFIG_PROC_FS) && (defined(CONFIG_VIDEO_PROC_FS) || (LINUX_VERSION_CODE
+< KERNEL_VERSION(2,4,0)))
/*
* / p r o c / v i d e o d e v H A N D L E R
*/
@@ -1787,8 +1964,11 @@
"vbi", "vtr", "teletext", "radio",
"undef", "undef", "undef", "undef",
};
+
+/* Code common to 2.4.0 and pre2.4.0 implementations. */
static int
-video_read_proc(char *buf, char **start, off_t offset, int len, int unused)
+video_build_proc(char *buf, char **start, off_t offset, int len,
+ void *data)
{
struct v4l2_device *vfl;
struct video_device *vfl1;
@@ -1832,12 +2012,133 @@
return len;
}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+static int
+video_read_proc(char *buf, char **start, off_t offset, int len, int unused)
+{
+ return video_build_proc(buf, start, offset, len, unused);
+}
+
+struct videodev_proc_data {
+ struct list_head proc_list;
+ char name[16];
+ struct video_device *vdev;
+ struct proc_dir_entry *proc_entry;
+};
+
/* proc file for /proc/videodev */
static struct proc_dir_entry video_proc_entry =
{
0, 8, "videodev", S_IFREG | S_IRUGO, 1, 0, 0, 0, NULL,
&video_read_proc
};
+
+#else // LINUX_VERSION_CODE >= 2.4.0
+struct videodev_proc_data {
+ struct list_head proc_list;
+ char name[16];
+ struct video_device *vdev;
+ struct proc_dir_entry *proc_entry;
+};
+
+static struct proc_dir_entry *video_dev_proc_entry = NULL;
+struct proc_dir_entry *video_proc_entry = NULL;
+EXPORT_SYMBOL(video_proc_entry);
+LIST_HEAD(videodev_proc_list);
+
+static int videodev_proc_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = video_build_proc(page, start, off, count, data);
+
+ /* FIXME: Why does the videodev.c included in kernel do this? */
+ len -= off;
+
+ if (len < count) {
+ *eof = 1;
+ if (len <= 0)
+ return 0;
+ }
+ else
+ len = count;
+
+ *start = page + off;
+
+ return len;
+}
+
+static void videodev_proc_create(void)
+{
+ video_proc_entry = create_proc_entry("video", S_IFDIR, &proc_root);
+
+ if (video_proc_entry == NULL) {
+ printk("video_dev: unable to initialise /proc/video\n");
+ return;
+ }
+
+ video_proc_entry->owner = THIS_MODULE;
+ video_dev_proc_entry = create_proc_entry("dev", S_IFDIR, video_proc_entry);
+
+ if (video_dev_proc_entry == NULL) {
+ printk("video_dev: unable to initialise /proc/video/dev\n");
+ return;
+ }
+
+ video_dev_proc_entry->owner = THIS_MODULE;
+}
+
+#ifdef MODULE
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+static void videodev_proc_destroy(void)
+{
+ if (video_dev_proc_entry != NULL)
+ remove_proc_entry("dev", video_proc_entry);
+
+ if (video_proc_entry != NULL)
+ remove_proc_entry("video", &proc_root);
+}
+#endif
+#endif
+
+static void videodev_proc_create_dev (struct video_device *vfd, char *name)
+{
+ struct videodev_proc_data *d;
+ struct proc_dir_entry *p;
+
+ if (video_dev_proc_entry == NULL)
+ return;
+
+ d = kmalloc (sizeof (struct videodev_proc_data), GFP_KERNEL);
+ if (!d)
+ return;
+
+ p = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, video_dev_proc_entry);
+ p->data = vfd;
+ p->read_proc = videodev_proc_read;
+
+ d->proc_entry = p;
+ d->vdev = vfd;
+ strcpy (d->name, name);
+
+ list_add (&d->proc_list, &videodev_proc_list);
+}
+
+static void videodev_proc_destroy_dev (struct video_device *vfd)
+{
+ struct list_head *tmp;
+ struct videodev_proc_data *d;
+
+ list_for_each (tmp, &videodev_proc_list) {
+ d = list_entry(tmp, struct videodev_proc_data, proc_list);
+ if (vfd == d->vdev) {
+ remove_proc_entry(d->name, video_dev_proc_entry);
+ list_del (&d->proc_list);
+ kfree (d);
+ break;
+ }
+ }
+}
+#endif
#endif
/*
@@ -1846,6 +2147,7 @@
static struct file_operations video_fops =
{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
v4l2_video_llseek,
v4l2_video_read,
v4l2_video_write,
@@ -1858,6 +2160,17 @@
NULL,
#endif
v4l2_video_release
+#else // LINUX_VERSION_CODE >= 2.4.0
+ owner: THIS_MODULE,
+ llseek: v4l2_video_llseek,
+ read: v4l2_video_read,
+ write: v4l2_video_write,
+ ioctl: v4l2_video_ioctl,
+ mmap: v4l2_video_mmap,
+ open: v4l2_video_open,
+ release: v4l2_video_release,
+ poll: v4l2_video_poll
+#endif
};
/*
@@ -1883,9 +2196,16 @@
v4l2_device[i] = NULL;
for (i = 0; i < VIDEO_NUM_DEVICES; i++)
video_device[i] = NULL;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
#ifdef CONFIG_PROC_FS
proc_register(&proc_root, &video_proc_entry);
#endif
+#else // KERNEL >= 2.4.0
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+ videodev_proc_create ();
+#endif
+#endif
+
masterclock = NULL;
while(vfli->init!=NULL)
@@ -1905,9 +2225,16 @@
void cleanup_module(void)
{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
#ifdef CONFIG_PROC_FS
proc_unregister(&proc_root, video_proc_entry.low_ino);
#endif
+#else // KERNEL >= 2.4.0
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+ videodev_proc_destroy ();
+#endif
+#endif
+
unregister_chrdev(VIDEO_MAJOR, "v4l1/2");
}
#endif