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