Mercurial > vloopback
diff vloopback.c @ 7:2fce9e157b8d
Added some work around to work with kernel 2.6.27-rc3, added debug param.
author | AngelCarpintero |
---|---|
date | Sun, 24 Aug 2008 02:20:51 +0000 |
parents | dc1f4ad7010c |
children | 80590d10a596 |
line wrap: on
line diff
--- a/vloopback.c Sat Jul 12 17:12:25 2008 +0000 +++ b/vloopback.c Sun Aug 24 02:20:51 2008 +0000 @@ -7,12 +7,12 @@ * * Published under the GNU Public License. * - * The Video Loopback Device is no longer systematically maintained. + * The Video loopback Loopback Device is no longer systematically maintained. * The project is a secondary project for the project "motion" found at * http://motion.sourceforge.net/ and * http://www.lavrsen.dk/twiki/bin/view/Motion/WebHome * and with the vloopback stored at - * http://www.lavrsen.dk/twiki/bin/view/Motion/VideoFourLinuxLoopbackDevice + * http://www.lavrsen.dk/twiki/bin/view/Motion/Video loopbackFourLinuxLoopbackDevice * * CHANGE HISTORY * @@ -136,7 +136,11 @@ * Change -ENOIOCTLCMD by more appropiate error -ENOTTY. * * 18.05.08 (Angel Carpintero) - * Release 1.1-rc1 as 1.1 stable working with 2.6.24 + * Release 1.1-rc1 as 1.1 stable working with 2.6.24 + * + * 17.08.08 (Angel Carpintero) + * kill_proc() deprecated ,pid API changed , type and owner not available in + * video_device struct, added param debug. */ @@ -150,7 +154,7 @@ #include <linux/pagemap.h> #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -#include <media/v4l2-common.h> + #include <media/v4l2-common.h> #endif #include <linux/videodev.h> @@ -183,12 +187,24 @@ #define VIDIOCSINVALID _IO('v',BASE_VIDIOCPRIVATE+1) -#define info(format, arg...) printk(KERN_INFO __FILE__ ": " format "\n" "", ## arg) +#define verbose(format, arg...) if (printk_ratelimit()) \ + printk(KERN_INFO "[%s] %s: " format "\n" "", \ + __FUNCTION__, __FILE__, ## arg) + +#define info(format, arg...) if (printk_ratelimit()) \ + printk(KERN_INFO "[%s] : " format "\n" "", \ + __FUNCTION__, ## arg) + +#define LOG_NODEBUG 0 +#define LOG_FUNCTIONS 1 +#define LOG_IOCTL 2 +#define LOG_VERBOSE 3 struct vloopback_private { int pipenr; - int in; /* bool */ + int in; /* bool , is being feed ? */ }; + typedef struct vloopback_private *priv_ptr; struct vloopback_pipe { @@ -206,7 +222,11 @@ struct semaphore lock; wait_queue_head_t wait; unsigned int frame; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) unsigned int pid; +#else + struct pid *pid; +#endif unsigned int zerocopy; unsigned long int ioctlnr; unsigned int invalid_ioctl; /* 0 .. none invalid; 1 .. invalid */ @@ -219,11 +239,12 @@ #define N_BUFFS 2 /* Number of buffers used for pipes */ static struct vloopback_pipe *loops[MAX_PIPES]; -static int nr_o_pipes=0; -static int pipes=-1; -static int spares=0; -static int pipesused=0; -static int dev_offset=-1; +static int nr_o_pipes = 0; +static int pipes = -1; +static int spares = 0; +static int pipesused = 0; +static int dev_offset = -1; +static unsigned int debug = LOG_NODEBUG; /********************************************************************** * @@ -237,7 +258,7 @@ */ static inline unsigned long kvirt_to_pa(unsigned long adr) { - unsigned long kva; + unsigned long kva; kva = (unsigned long)page_address(vmalloc_to_page((void *)adr)); kva |= adr & (PAGE_SIZE-1); /* restore the offset */ @@ -303,14 +324,23 @@ static int fake_ioctl(int nr, unsigned long int cmd, void *arg) { unsigned long fw; - - loops[nr]->ioctlnr=cmd; + + if (debug > LOG_NODEBUG) + info("Video loopback %d cmd %lu", nr, cmd); + + loops[nr]->ioctlnr = cmd; memcpy(loops[nr]->ioctldata, arg, _IOC_SIZE(cmd)); - loops[nr]->ioctllength=_IOC_SIZE(cmd); - kill_proc(loops[nr]->pid, SIGIO, 1); /* Signal the pipe feeder */ + loops[nr]->ioctllength = _IOC_SIZE(cmd); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) + kill_proc(loops[nr]->pid, SIGIO, 1); /* Signal the pipe feeder */ +#else + kill_pid(loops[nr]->pid, SIGIO, 1); +#endif + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) fw = loops[nr]->frameswrite; - wait_event_interruptible(loops[nr]->wait, fw!=loops[nr]->frameswrite); + wait_event_interruptible(loops[nr]->wait, fw != loops[nr]->frameswrite); #else interruptible_sleep_on(&loops[nr]->wait); #endif @@ -325,28 +355,31 @@ static int vloopback_open(struct inode *inod, struct file *f) { - struct video_device *loopdev=video_devdata(f); - priv_ptr ptr=(priv_ptr)loopdev->priv; - int nr=ptr->pipenr; - + struct video_device *loopdev = video_devdata(f); + priv_ptr ptr = (priv_ptr)loopdev->priv; + int nr = ptr->pipenr; + + if (debug > LOG_NODEBUG) + info("Video loopback %d", nr); /* Only allow a output to be opened if there is someone feeding * the pipe. */ if (!ptr->in) { - if (loops[nr]->buffer==NULL) { + if (loops[nr]->buffer == NULL) return -EINVAL; - } - loops[nr]->framesread=0; - loops[nr]->ropen=1; + + loops[nr]->framesread = 0; + loops[nr]->ropen = 1; } else { if (loops[nr]->ropen || loops[nr]->wopen) return -EBUSY; - loops[nr]->framesdumped=0; - loops[nr]->frameswrite=0; - loops[nr]->wopen=1; - loops[nr]->zerocopy=0; - loops[nr]->ioctlnr=-1; + + loops[nr]->framesdumped = 0; + loops[nr]->frameswrite = 0; + loops[nr]->wopen = 1; + loops[nr]->zerocopy = 0; + loops[nr]->ioctlnr = -1; pipesused++; if (nr_o_pipes-pipesused<spares) { if (!create_pipe(nr_o_pipes)) { @@ -359,47 +392,61 @@ nr_o_pipes++; } } - loops[nr]->pid=current->pid; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) + loops[nr]->pid = current->pid; +#else + // TODO : Check in stable 2.6.27 + loops[nr]->pid = task_pid(find_task_by_vpid(current->pid)); + //loops[nr]->pid = task_pid(current); +#endif + + if (debug > LOG_NODEBUG) + info("Current pid %d", current->pid); } return 0; } static int vloopback_release(struct inode * inod, struct file *f) { - struct video_device *loopdev=video_devdata(f); - priv_ptr ptr=(priv_ptr)loopdev->priv; - int nr=ptr->pipenr; - + struct video_device *loopdev = video_devdata(f); + priv_ptr ptr = (priv_ptr)loopdev->priv; + int nr = ptr->pipenr; + + if (debug > LOG_NODEBUG) + info("Video loopback %d", nr); + if (ptr->in) { down(&loops[nr]->lock); if (loops[nr]->buffer && !loops[nr]->ropen) { - rvfree(loops[nr]->buffer, - loops[nr]->buflength*N_BUFFS); - loops[nr]->buffer=NULL; + rvfree(loops[nr]->buffer, loops[nr]->buflength * N_BUFFS); + loops[nr]->buffer = NULL; } up(&loops[nr]->lock); loops[nr]->frameswrite++; if (waitqueue_active(&loops[nr]->wait)) wake_up(&loops[nr]->wait); - loops[nr]->width=0; - loops[nr]->height=0; - loops[nr]->palette=0; - loops[nr]->wopen=0; + loops[nr]->width = 0; + loops[nr]->height = 0; + loops[nr]->palette = 0; + loops[nr]->wopen = 0; pipesused--; } else { down(&loops[nr]->lock); if (loops[nr]->buffer && !loops[nr]->wopen) { - rvfree(loops[nr]->buffer, - loops[nr]->buflength*N_BUFFS); - loops[nr]->buffer=NULL; + rvfree(loops[nr]->buffer, loops[nr]->buflength * N_BUFFS); + loops[nr]->buffer = NULL; } up(&loops[nr]->lock); - loops[nr]->ropen=0; + loops[nr]->ropen = 0; if (loops[nr]->zerocopy && loops[nr]->buffer) { - loops[nr]->ioctlnr=0; - loops[nr]->ioctllength=0; - kill_proc(loops[nr]->pid, SIGIO, 1); + loops[nr]->ioctlnr = 0; + loops[nr]->ioctllength = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) + kill_proc(loops[nr]->pid, SIGIO, 1); +#else + kill_pid(loops[nr]->pid, SIGIO, 1); +#endif } } @@ -407,22 +454,25 @@ } static ssize_t vloopback_write(struct file *f, const char *buf, - size_t count, loff_t *offset) + size_t count, loff_t *offset) { - struct video_device *loopdev=video_devdata(f); - priv_ptr ptr=(priv_ptr)loopdev->priv; - int nr=ptr->pipenr; - unsigned long realcount=count; - + struct video_device *loopdev = video_devdata(f); + priv_ptr ptr = (priv_ptr)loopdev->priv; + int nr = ptr->pipenr; + unsigned long realcount = count; + + if (debug > LOG_IOCTL) + info("Video loopback %d", nr); + if (!ptr->in) return -EINVAL; + if (loops[nr]->zerocopy) return -EINVAL; - if (loops[nr]->buffer==NULL) { + if (loops[nr]->buffer == NULL) return -EINVAL; - } - + /* Anybody want some pictures??? */ if (!waitqueue_active(&loops[nr]->wait)) { loops[nr]->framesdumped++; @@ -434,17 +484,18 @@ up(&loops[nr]->lock); return -EINVAL; } + if (realcount > loops[nr]->buflength) { realcount = loops[nr]->buflength; - info("Too much data! Only %ld bytes used.", realcount); + info("Too much data for Video loopback %d ! Only %ld bytes used.", + nr, realcount); } - if (copy_from_user( - loops[nr]->buffer+loops[nr]->frame*loops[nr]->buflength, - buf, realcount - )) return -EFAULT; + if (copy_from_user(loops[nr]->buffer + loops[nr]->frame * loops[nr]->buflength, + buf, realcount)) + return -EFAULT; - loops[nr]->frame=0; + loops[nr]->frame = 0; up(&loops[nr]->lock); loops[nr]->frameswrite++; @@ -453,24 +504,32 @@ return realcount; } -static ssize_t vloopback_read (struct file * f, char * buf, size_t count, loff_t *offset) +static ssize_t vloopback_read(struct file * f, char * buf, size_t count, + loff_t *offset) { - struct video_device *loopdev=video_devdata(f); - priv_ptr ptr=(priv_ptr)loopdev->priv; - int nr=ptr->pipenr; - unsigned long realcount=count; + struct video_device *loopdev = video_devdata(f); + priv_ptr ptr = (priv_ptr)loopdev->priv; + int nr = ptr->pipenr; + unsigned long realcount = count; + + if (debug > LOG_IOCTL) + info("Video loopback %d", nr); if (loops[nr]->zerocopy) { if (ptr->in) { - if (realcount > loops[nr]->ioctllength+sizeof(unsigned long int)) - realcount=loops[nr]->ioctllength+sizeof(unsigned long int); + if (realcount > loops[nr]->ioctllength + sizeof(unsigned long int)) + realcount = loops[nr]->ioctllength + sizeof(unsigned long int); + if (copy_to_user(buf , &loops[nr]->ioctlnr, sizeof(unsigned long int))) return -EFAULT; - if (copy_to_user(buf+sizeof(unsigned long int) , loops[nr]->ioctldata, - realcount-sizeof(unsigned long int))) + + if (copy_to_user(buf + sizeof(unsigned long int), loops[nr]->ioctldata, + realcount - sizeof(unsigned long int))) return -EFAULT; - if (loops[nr]->ioctlnr==0) - loops[nr]->ioctlnr=-1; + + if (loops[nr]->ioctlnr == 0) + loops[nr]->ioctlnr = -1; + return realcount; } else { struct video_window vidwin; @@ -480,30 +539,34 @@ fake_ioctl(nr, VIDIOCGWIN, &vidwin); fake_ioctl(nr, VIDIOCGPICT, &vidpic); - vidmmap.height=vidwin.height; - vidmmap.width=vidwin.width; - vidmmap.format=vidpic.palette; - vidmmap.frame=0; + vidmmap.height = vidwin.height; + vidmmap.width = vidwin.width; + vidmmap.format = vidpic.palette; + vidmmap.frame = 0; + if (fake_ioctl(nr, VIDIOCMCAPTURE, &vidmmap)) return 0; + if (fake_ioctl(nr, VIDIOCSYNC, &vidmmap)) return 0; - realcount=vidwin.height*vidwin.width*vidpic.depth; + + realcount = vidwin.height * vidwin.width * vidpic.depth; } } + if (ptr->in) return -EINVAL; if (realcount > loops[nr]->buflength) { realcount = loops[nr]->buflength; - info("Not so much data in buffer!"); + info("Not so much data in buffer! for Video loopback %d", nr); } if (!loops[nr]->zerocopy) { #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) - unsigned long fw=loops[nr]->frameswrite; + unsigned long fw = loops[nr]->frameswrite; - wait_event_interruptible(loops[nr]->wait, fw!=loops[nr]->frameswrite); + wait_event_interruptible(loops[nr]->wait, fw != loops[nr]->frameswrite); #else interruptible_sleep_on(&loops[nr]->wait); #endif @@ -514,8 +577,10 @@ up(&loops[nr]->lock); return 0; } + if (copy_to_user(buf, loops[nr]->buffer, realcount)) return -EFAULT; + up(&loops[nr]->lock); loops[nr]->framesread++; @@ -524,95 +589,118 @@ static int vloopback_mmap(struct file *f, struct vm_area_struct *vma) { - struct video_device *loopdev=video_devdata(f); - priv_ptr ptr=(priv_ptr)loopdev->priv; - int nr=ptr->pipenr; + struct video_device *loopdev = video_devdata(f); + priv_ptr ptr = (priv_ptr)loopdev->priv; + int nr = ptr->pipenr; unsigned long start = (unsigned long)vma->vm_start; long size = vma->vm_end - vma->vm_start; unsigned long page, pos; + if (debug > LOG_NODEBUG) + info("Video loopback %d", nr); + down(&loops[nr]->lock); + if (ptr->in) { - loops[nr]->zerocopy=1; + loops[nr]->zerocopy = 1; + if (loops[nr]->ropen) { - info("Can't change size while opened for read"); - up(&loops[nr]->lock); - return -EINVAL; - } - if (!size) { + info("Can't change size while opened for read in Video loopback %d", + nr); up(&loops[nr]->lock); return -EINVAL; } + + if (!size) { + up(&loops[nr]->lock); + info("Invalid size Video loopback %d", nr); + return -EINVAL; + } + if (loops[nr]->buffer) - rvfree(loops[nr]->buffer, loops[nr]->buflength*N_BUFFS); - loops[nr]->buflength=size; - loops[nr]->buffer=rvmalloc(loops[nr]->buflength*N_BUFFS); - } - if (loops[nr]->buffer == NULL) { - up(&loops[nr]->lock); - return -EINVAL; + rvfree(loops[nr]->buffer, loops[nr]->buflength * N_BUFFS); + + loops[nr]->buflength = size; + loops[nr]->buffer = rvmalloc(loops[nr]->buflength * N_BUFFS); } - if (size > (((N_BUFFS * loops[nr]->buflength) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) { + if (loops[nr]->buffer == NULL) { up(&loops[nr]->lock); - return -EINVAL; + return -EINVAL; } - pos = (unsigned long)loops[nr]->buffer; - while (size > 0) { + if (size > (((N_BUFFS * loops[nr]->buflength) + PAGE_SIZE - 1) + & ~(PAGE_SIZE - 1))) { + up(&loops[nr]->lock); + return -EINVAL; + } + + pos = (unsigned long)loops[nr]->buffer; + + while (size > 0) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9) - page = kvirt_to_pa(pos); - if (remap_page_range(vma,start, page, PAGE_SIZE, PAGE_SHARED)) { + page = kvirt_to_pa(pos); + if (remap_page_range(vma,start, page, PAGE_SIZE, PAGE_SHARED)) { #else page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, - PAGE_SHARED)) { + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { #endif - up(&loops[nr]->lock); - return -EAGAIN; + up(&loops[nr]->lock); + return -EAGAIN; } - start += PAGE_SIZE; - pos += PAGE_SIZE; + start += PAGE_SIZE; + pos += PAGE_SIZE; size -= PAGE_SIZE; - } + } + up(&loops[nr]->lock); return 0; } -static int vloopback_ioctl(struct inode *inod, struct file *f, unsigned int cmd, unsigned long arg) +static int vloopback_ioctl(struct inode *inod, struct file *f, unsigned int cmd, + unsigned long arg) { - struct video_device *loopdev=video_devdata(f); - priv_ptr ptr=(priv_ptr)loopdev->priv; - int nr=ptr->pipenr; + struct video_device *loopdev = video_devdata(f); + priv_ptr ptr = (priv_ptr)loopdev->priv; + int nr = ptr->pipenr; int i; + if (debug > LOG_NODEBUG) + info("Video loopback %d cmd %u", nr, cmd); + if (loops[nr]->zerocopy) { if (!ptr->in) { - loops[nr]->ioctlnr=cmd; - loops[nr]->ioctllength=_IOC_SIZE(cmd); + loops[nr]->ioctlnr = cmd; + loops[nr]->ioctllength = _IOC_SIZE(cmd); /* info("DEBUG: vl_ioctl: !loop->in"); */ /* info("DEBUG: vl_ioctl: cmd %lu", cmd); */ /* info("DEBUG: vl_ioctl: len %lu", loops[nr]->ioctllength); */ - if(copy_from_user(loops[nr]->ioctldata, (void*)arg, _IOC_SIZE(cmd))) + if (copy_from_user(loops[nr]->ioctldata, (void*)arg, _IOC_SIZE(cmd))) return -EFAULT; - kill_proc(loops[nr]->pid, SIGIO, 1); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) + kill_proc(loops[nr]->pid, SIGIO, 1); +#else + kill_pid(loops[nr]->pid, SIGIO, 1); +#endif + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) - wait_event_interruptible(loops[nr]->wait, loops[nr]->ioctlnr==-1); + wait_event_interruptible(loops[nr]->wait, loops[nr]->ioctlnr == -1); #else interruptible_sleep_on(&loops[nr]->wait); #endif if (loops[nr]->invalid_ioctl) { - //info ("DEBUG: There was an invalid ioctl"); - loops[nr]->invalid_ioctl = 0; + info ("There was an invalid ioctl in Video loopback %d", nr); + loops[nr]->invalid_ioctl = 0; return -ENOTTY; } + if (cmd & IOC_IN && !(cmd & IOC_OUT)) { //info("DEBUG: vl_ioctl: cmd & IOC_IN 1"); - if (memcmp(loops[nr]->ioctlretdata, loops[nr]->ioctldata, _IOC_SIZE(cmd))) { + if (memcmp(loops[nr]->ioctlretdata, loops[nr]->ioctldata, _IOC_SIZE(cmd))) return -EINVAL; - } + //info("DEBUG: vl_ioctl: cmd & IOC_IN 2"); return 0; } else { @@ -622,309 +710,366 @@ return 0; } } else { - if ( (loops[nr]->ioctlnr!=cmd) && (cmd != (VIDIOCSINVALID))) { + if ((loops[nr]->ioctlnr != cmd) && (cmd != (VIDIOCSINVALID))) { /* wrong ioctl */ - info("DEBUG: vo_ioctl: Wrong IOCTL"); - return 0; + info("Wrong IOCTL %u in Video loopback %d", cmd, nr); + return 0; } + if (cmd == VIDIOCSINVALID) { loops[nr]->invalid_ioctl = 1; - } else { - if (copy_from_user(loops[nr]->ioctlretdata, (void*)arg, loops[nr]->ioctllength)) - return -EFAULT; + } else if (copy_from_user(loops[nr]->ioctlretdata, + (void*)arg, loops[nr]->ioctllength)) { + return -EFAULT; } - loops[nr]->ioctlnr=-1; + + loops[nr]->ioctlnr = -1; + if (waitqueue_active(&loops[nr]->wait)) wake_up(&loops[nr]->wait); + return 0; } } + switch(cmd) { - /* Get capabilities */ - case VIDIOCGCAP: - { - struct video_capability b; - if (ptr->in) { - sprintf(b.name, "Video loopback %d input", - ptr->pipenr); - b.type = 0; - } else { - sprintf(b.name, "Video loopback %d output", - ptr->pipenr); - b.type = VID_TYPE_CAPTURE; - } - b.channels=1; - b.audios=0; - b.maxwidth=loops[nr]->width; - b.maxheight=loops[nr]->height; - b.minwidth=20; - b.minheight=20; - if(copy_to_user((void*)arg, &b, sizeof(b))) - return -EFAULT; - return 0; + /* Get capabilities */ + case VIDIOCGCAP: + { + struct video_capability b; + if (ptr->in) { + sprintf(b.name, "Video loopback %d input", nr); + b.type = 0; + } else { + sprintf(b.name, "Video loopback %d output", nr); + b.type = VID_TYPE_CAPTURE; + } + + b.channels = 1; + b.audios = 0; + b.maxwidth = loops[nr]->width; + b.maxheight = loops[nr]->height; + b.minwidth = 20; + b.minheight = 20; + + if (copy_to_user((void*)arg, &b, sizeof(b))) + return -EFAULT; + + return 0; + } + /* Get channel info (sources) */ + case VIDIOCGCHAN: + { + struct video_channel v; + if (copy_from_user(&v, (void*)arg, sizeof(v))) + return -EFAULT; + + if (v.channel != 0) { + info("VIDIOCGCHAN: Invalid Channel, was %d", v.channel); + v.channel = 0; + //return -EINVAL; } - /* Get channel info (sources) */ - case VIDIOCGCHAN: - { - struct video_channel v; - if(copy_from_user(&v, (void*)arg, sizeof(v))) - return -EFAULT; - if(v.channel!=0) { - info("VIDIOCGCHAN: Invalid Channel, was %d", v.channel); - v.channel=0; - //return -EINVAL; - } - v.flags=0; - v.tuners=0; - v.norm=0; - v.type = VIDEO_TYPE_CAMERA; - /*strcpy(v.name, "Loopback"); -- tibit */ - strcpy(v.name, "Composite1"); - if(copy_to_user((void*)arg, &v, sizeof(v))) - return -EFAULT; - return 0; + + v.flags = 0; + v.tuners = 0; + v.norm = 0; + v.type = VIDEO_TYPE_CAMERA; + /*strcpy(v.name, "Loopback"); -- tibit */ + strcpy(v.name, "Composite1"); + + if (copy_to_user((void*)arg, &v, sizeof(v))) + return -EFAULT; + + return 0; + } + /* Set channel */ + case VIDIOCSCHAN: + { + int v; + + if (copy_from_user(&v, (void*)arg, sizeof(v))) + return -EFAULT; + + if (v != 0) { + info("VIDIOCSCHAN: Invalid Channel, was %d", v); + return -EINVAL; + } + + return 0; + } + /* Get tuner abilities */ + case VIDIOCGTUNER: + { + struct video_tuner v; + + if (copy_from_user(&v, (void*)arg, sizeof(v)) != 0) + return -EFAULT; + + if (v.tuner) { + info("VIDIOCGTUNER: Invalid Tuner, was %d", v.tuner); + return -EINVAL; } - /* Set channel */ - case VIDIOCSCHAN: - { - int v; - if(copy_from_user(&v, (void*)arg, sizeof(v))) - return -EFAULT; - if(v!=0) { - info("VIDIOCSCHAN: Invalid Channel, was %d", v); - return -EINVAL; - } - return 0; - } - /* Get tuner abilities */ - case VIDIOCGTUNER: - { - struct video_tuner v; - if(copy_from_user(&v, (void*)arg, sizeof(v))!=0) - return -EFAULT; - if(v.tuner) { - info("VIDIOCGTUNER: Invalid Tuner, was %d", v.tuner); + + strcpy(v.name, "Format"); + v.rangelow = 0; + v.rangehigh = 0; + v.flags = 0; + v.mode = VIDEO_MODE_AUTO; + + if (copy_to_user((void*)arg,&v, sizeof(v)) != 0) + return -EFAULT; + + return 0; + } + /* Get picture properties */ + case VIDIOCGPICT: + { + struct video_picture p; + + p.colour = 0x8000; + p.hue = 0x8000; + p.brightness = 0x8000; + p.contrast = 0x8000; + p.whiteness = 0x8000; + p.depth = 0x8000; + p.palette = loops[nr]->palette; + + if (copy_to_user((void*)arg, &p, sizeof(p))) + return -EFAULT; + + return 0; + } + /* Set picture properties */ + case VIDIOCSPICT: + { + struct video_picture p; + + if (copy_from_user(&p, (void*)arg, sizeof(p))) + return -EFAULT; + + if (!ptr->in) { + if (p.palette != loops[nr]->palette) return -EINVAL; - } - strcpy(v.name, "Format"); - v.rangelow=0; - v.rangehigh=0; - v.flags=0; - v.mode=VIDEO_MODE_AUTO; - if(copy_to_user((void*)arg,&v, sizeof(v))!=0) - return -EFAULT; - return 0; - } - /* Get picture properties */ - case VIDIOCGPICT: - { - struct video_picture p; - p.colour=0x8000; - p.hue=0x8000; - p.brightness=0x8000; - p.contrast=0x8000; - p.whiteness=0x8000; - p.depth=0x8000; - p.palette=loops[nr]->palette; - if(copy_to_user((void*)arg, &p, sizeof(p))) - return -EFAULT; + } else { + loops[nr]->palette = p.palette; + } + + return 0; + } + /* Get the video overlay window */ + case VIDIOCGWIN: + { + struct video_window vw; + + vw.x = 0; + vw.y = 0; + vw.width = loops[nr]->width; + vw.height = loops[nr]->height; + vw.chromakey = 0; + vw.flags = 0; + vw.clipcount = 0; + + if (copy_to_user((void*)arg, &vw, sizeof(vw))) + return -EFAULT; + + return 0; + } + /* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */ + case VIDIOCSWIN: + { + struct video_window vw; + + if (copy_from_user(&vw, (void*)arg, sizeof(vw))) + return -EFAULT; + + if (vw.flags) + return -EINVAL; + + if (vw.clipcount) + return -EINVAL; + + if (loops[nr]->height == vw.height && + loops[nr]->width == vw.width) return 0; - } - /* Set picture properties */ - case VIDIOCSPICT: - { - struct video_picture p; - if(copy_from_user(&p, (void*)arg, sizeof(p))) - return -EFAULT; - if (!ptr->in) { - if (p.palette!=loops[nr]->palette) - return -EINVAL; - } else - loops[nr]->palette=p.palette; - return 0; - } - /* Get the video overlay window */ - case VIDIOCGWIN: - { - struct video_window vw; - vw.x=0; - vw.y=0; - vw.width=loops[nr]->width; - vw.height=loops[nr]->height; - vw.chromakey=0; - vw.flags=0; - vw.clipcount=0; - if(copy_to_user((void*)arg, &vw, sizeof(vw))) - return -EFAULT; - return 0; - } - /* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */ - case VIDIOCSWIN: - { - struct video_window vw; - - if(copy_from_user(&vw, (void*)arg, sizeof(vw))) - return -EFAULT; - if(vw.flags) - return -EINVAL; - if(vw.clipcount) - return -EINVAL; - if (loops[nr]->height==vw.height && - loops[nr]->width==vw.width) - return 0; - if(!ptr->in) { - return -EINVAL; - } else { - loops[nr]->height=vw.height; - loops[nr]->width=vw.width; - /* Make sure nobody is using the buffer while we - fool around with it. - We are also not allowing changes while - somebody using mmap has the output open. - */ - down(&loops[nr]->lock); - if (loops[nr]->ropen) { - info("Can't change size while opened for read"); - up(&loops[nr]->lock); - return -EINVAL; - } - if (loops[nr]->buffer) - rvfree(loops[nr]->buffer, loops[nr]->buflength*N_BUFFS); - loops[nr]->buflength=vw.width*vw.height*4; - loops[nr]->buffer=rvmalloc(loops[nr]->buflength*N_BUFFS); + if (!ptr->in) { + return -EINVAL; + } else { + loops[nr]->height = vw.height; + loops[nr]->width = vw.width; + /* Make sure nobody is using the buffer while we + fool around with it. + We are also not allowing changes while + somebody using mmap has the output open. + */ + down(&loops[nr]->lock); + if (loops[nr]->ropen) { + info("Can't change size while opened for read"); up(&loops[nr]->lock); - } - return 0; - } - /* Memory map buffer info */ - case VIDIOCGMBUF: - { - struct video_mbuf vm; - - vm.size=loops[nr]->buflength*N_BUFFS; - vm.frames=N_BUFFS; - for (i=0; i<vm.frames; i++) - vm.offsets[i]=i*loops[nr]->buflength; - if(copy_to_user((void*)arg, &vm, sizeof(vm))) - return -EFAULT; - return 0; - } - /* Grab frames */ - case VIDIOCMCAPTURE: - { - struct video_mmap vm; - - if (ptr->in) - return -EINVAL; - if (!loops[nr]->buffer) return -EINVAL; - if (copy_from_user(&vm, (void*)arg, sizeof(vm))) - return -EFAULT; - if (vm.format!=loops[nr]->palette) - return -EINVAL; - if (vm.frame > N_BUFFS) - return -EINVAL; - return 0; - } - /* Sync with mmap grabbing */ - case VIDIOCSYNC: - { - int frame; - unsigned long fw; + } + + if (loops[nr]->buffer) + rvfree(loops[nr]->buffer, loops[nr]->buflength * N_BUFFS); - if (copy_from_user((void *)&frame, (void*)arg, sizeof(int))) - return -EFAULT; - if (ptr->in) - return -EINVAL; - if (!loops[nr]->buffer) - return -EINVAL; - /* Ok, everything should be alright since the program - should have called VIDIOMCAPTURE and we are ready to - do the 'capturing' */ - if (frame > 1) - return -EINVAL; - loops[nr]->frame=frame; -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) - fw = loops[nr]->frameswrite; - wait_event_interruptible(loops[nr]->wait, fw!=loops[nr]->frameswrite); -#else - interruptible_sleep_on(&loops[nr]->wait); -#endif - if (!loops[nr]->buffer) /* possibly released during sleep */ - return -EINVAL; - loops[nr]->framesread++; - return 0; + loops[nr]->buflength = vw.width * vw.height * 4; + loops[nr]->buffer = rvmalloc(loops[nr]->buflength * N_BUFFS); + up(&loops[nr]->lock); } - /* Get attached units */ - case VIDIOCGUNIT: - { - struct video_unit vu; + return 0; + } + /* Memory map buffer info */ + case VIDIOCGMBUF: + { + struct video_mbuf vm; - if (ptr->in) - vu.video=loops[nr]->vloopout->minor; - else - vu.video=loops[nr]->vloopin->minor; - vu.vbi=VIDEO_NO_UNIT; - vu.radio=VIDEO_NO_UNIT; - vu.audio=VIDEO_NO_UNIT; - vu.teletext=VIDEO_NO_UNIT; - if (copy_to_user((void*)arg, &vu, sizeof(vu))) - return -EFAULT; - return 0; - } - /* Get frame buffer */ - case VIDIOCGFBUF: - { - struct video_buffer vb; + vm.size = loops[nr]->buflength * N_BUFFS; + vm.frames = N_BUFFS; + for (i = 0; i < vm.frames; i++) + vm.offsets[i] = i * loops[nr]->buflength; + + if (copy_to_user((void*)arg, &vm, sizeof(vm))) + return -EFAULT; + + return 0; + } + /* Grab frames */ + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + + if (ptr->in) + return -EINVAL; + + if (!loops[nr]->buffer) + return -EINVAL; + + if (copy_from_user(&vm, (void*)arg, sizeof(vm))) + return -EFAULT; + + if (vm.format != loops[nr]->palette) + return -EINVAL; + + if (vm.frame > N_BUFFS) + return -EINVAL; - memset(&vb, 0, sizeof(vb)); - vb.base=NULL; + return 0; + } + /* Sync with mmap grabbing */ + case VIDIOCSYNC: + { + int frame; + unsigned long fw; + + if (copy_from_user((void *)&frame, (void*)arg, sizeof(int))) + return -EFAULT; + + if (ptr->in) + return -EINVAL; - if(copy_to_user((void *)arg, (void *)&vb, sizeof(vb))) - return -EFAULT; + if (!loops[nr]->buffer) + return -EINVAL; + + /* Ok, everything should be alright since the program + should have called VIDIOMCAPTURE and we are ready to + do the 'capturing' */ + if (frame > 1) + return -EINVAL; + + loops[nr]->frame = frame; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + fw = loops[nr]->frameswrite; + wait_event_interruptible(loops[nr]->wait, fw != loops[nr]->frameswrite); +#else + interruptible_sleep_on(&loops[nr]->wait); +#endif + if (!loops[nr]->buffer) /* possibly released during sleep */ + return -EINVAL; - return 0; - } - /* Start, end capture */ - case VIDIOCCAPTURE: - { - int start; - if (copy_from_user(&start, (void*)arg, sizeof(int))) - return -EFAULT; - if (start) info ("Capture started"); - else info ("Capture stopped"); + loops[nr]->framesread++; + + return 0; + } + /* Get attached units */ + case VIDIOCGUNIT: + { + struct video_unit vu; + + if (ptr->in) + vu.video = loops[nr]->vloopout->minor; + else + vu.video = loops[nr]->vloopin->minor; + + vu.vbi = VIDEO_NO_UNIT; + vu.radio = VIDEO_NO_UNIT; + vu.audio = VIDEO_NO_UNIT; + vu.teletext = VIDEO_NO_UNIT; + + if (copy_to_user((void*)arg, &vu, sizeof(vu))) + return -EFAULT; + + return 0; + } + /* Get frame buffer */ + case VIDIOCGFBUF: + { + struct video_buffer vb; + + memset(&vb, 0, sizeof(vb)); + vb.base = NULL; - return 0; - } + if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb))) + return -EFAULT; + + return 0; + } + /* Start, end capture */ + case VIDIOCCAPTURE: + { + int start; + + if (copy_from_user(&start, (void*)arg, sizeof(int))) + return -EFAULT; - case VIDIOCGFREQ: - case VIDIOCSFREQ: - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - return -EINVAL; - case VIDIOCKEY: - return 0; - default: - return -ENOTTY; - //return -ENOIOCTLCMD; + if (start) { + info ("Video loopback %d Capture started", nr); + } else { + info ("Video loopback %d Capture stopped", nr); + } + + return 0; + } + case VIDIOCGFREQ: + case VIDIOCSFREQ: + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + case VIDIOCKEY: + return 0; + default: + return -ENOTTY; + //return -ENOIOCTLCMD; } return 0; } static unsigned int vloopback_poll(struct file *f, struct poll_table_struct *wait) { - struct video_device *loopdev=video_devdata(f); - priv_ptr ptr=(priv_ptr)loopdev->priv; - int nr=ptr->pipenr; + struct video_device *loopdev = video_devdata(f); + priv_ptr ptr = (priv_ptr)loopdev->priv; + int nr = ptr->pipenr; - if (loopdev==NULL) + if (debug > LOG_NODEBUG) + info("Video loopback %d", nr); + + if (loopdev == NULL) return -EFAULT; + if (!ptr->in) return 0; - if (loops[nr]->ioctlnr!=-1) { + if (loops[nr]->ioctlnr != -1) { if (loops[nr]->zerocopy) { return (POLLIN | POLLPRI | POLLOUT | POLLRDNORM); } else { @@ -934,7 +1079,7 @@ return 0; } -static struct file_operations fileops_template= +static struct file_operations fileops_template = { owner: THIS_MODULE, open: vloopback_open, @@ -946,11 +1091,14 @@ mmap: vloopback_mmap, }; -static struct video_device vloopback_template= +static struct video_device vloopback_template = { - owner: THIS_MODULE, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) + owner: THIS_MODULE, + type: VID_TYPE_CAPTURE, +#endif + minor: -1, name: "Video Loopback", - type: VID_TYPE_CAPTURE, fops: &fileops_template, #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) release: video_device_release, @@ -960,39 +1108,46 @@ static int create_pipe(int nr) { int minor_in, minor_out , ret; - - if (dev_offset == -1) + + if (debug > LOG_NODEBUG) + info("Video loopback %d", nr); + + if (dev_offset == -1) { minor_in = minor_out = -1; /* autoassign */ - else { - minor_in = 2*nr + dev_offset; - minor_out = 2*nr+1 + dev_offset; + } else { + minor_in = 2 * nr + dev_offset; + minor_out = 2 * nr + 1 + dev_offset; } /* allocate space for this pipe */ loops[nr]= kmalloc(sizeof(struct vloopback_pipe), GFP_KERNEL); + if (!loops[nr]) return -ENOMEM; /* set up a new video device plus our private area */ - loops[nr]->vloopin= video_device_alloc(); + loops[nr]->vloopin = video_device_alloc(); + if (loops[nr]->vloopin == NULL) return -ENOMEM; *loops[nr]->vloopin = vloopback_template; - loops[nr]->vloopin->priv= kmalloc(sizeof(struct vloopback_private), - GFP_KERNEL); + loops[nr]->vloopin->priv = kmalloc(sizeof(struct vloopback_private), + GFP_KERNEL); if (loops[nr]->vloopin->priv == NULL) { kfree(loops[nr]->vloopin); return -ENOMEM; } /* repeat for the output device */ - loops[nr]->vloopout= video_device_alloc(); + loops[nr]->vloopout = video_device_alloc(); + if (loops[nr]->vloopout == NULL) { kfree(loops[nr]->vloopin->priv); kfree(loops[nr]->vloopin); return -ENOMEM; } *loops[nr]->vloopout = vloopback_template; - loops[nr]->vloopout->priv= kmalloc(sizeof(struct vloopback_private), - GFP_KERNEL); + loops[nr]->vloopout->priv = kmalloc(sizeof(struct vloopback_private), + GFP_KERNEL); + if (loops[nr]->vloopout->priv == NULL) { kfree(loops[nr]->vloopin->priv); kfree(loops[nr]->vloopin); @@ -1000,43 +1155,49 @@ return -ENOMEM; } - ((priv_ptr)loops[nr]->vloopin->priv)->pipenr=nr; - ((priv_ptr)loops[nr]->vloopout->priv)->pipenr=nr; + ((priv_ptr)loops[nr]->vloopin->priv)->pipenr = nr; + ((priv_ptr)loops[nr]->vloopout->priv)->pipenr = nr; loops[nr]->invalid_ioctl = 0; /* tibit */ - loops[nr]->buffer=NULL; - loops[nr]->width=0; - loops[nr]->height=0; - loops[nr]->palette=0; - loops[nr]->frameswrite=0; - loops[nr]->framesread=0; - loops[nr]->framesdumped=0; - loops[nr]->wopen=0; - loops[nr]->ropen=0; - loops[nr]->frame=0; + loops[nr]->buffer = NULL; + loops[nr]->width = 0; + loops[nr]->height = 0; + loops[nr]->palette = 0; + loops[nr]->frameswrite = 0; + loops[nr]->framesread = 0; + loops[nr]->framesdumped = 0; + loops[nr]->wopen = 0; + loops[nr]->ropen = 0; + loops[nr]->frame = 0; - ((priv_ptr)loops[nr]->vloopin->priv)->in=1; - ((priv_ptr)loops[nr]->vloopout->priv)->in=0; - loops[nr]->vloopin->type=0; - sprintf(loops[nr]->vloopin->name, "Video loopback %d input", nr); - loops[nr]->vloopout->type=VID_TYPE_CAPTURE; - sprintf(loops[nr]->vloopout->name, "Video loopback %d output", nr); + ((priv_ptr)loops[nr]->vloopin->priv)->in = 1; + ((priv_ptr)loops[nr]->vloopout->priv)->in = 0; + sprintf(loops[nr]->vloopin->name, "Video loopback %d input", nr); + sprintf(loops[nr]->vloopout->name, "Video loopback %d output", nr); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) + loops[nr]->vloopin->type = 0; + loops[nr]->vloopout->type = VID_TYPE_CAPTURE; +#endif + loops[nr]->vloopout->minor = minor_out; + loops[nr]->vloopin->minor = minor_in; + init_waitqueue_head(&loops[nr]->wait); init_MUTEX(&loops[nr]->lock); - ret = video_register_device(loops[nr]->vloopin, VFL_TYPE_GRABBER,minor_in); + ret = video_register_device(loops[nr]->vloopin, VFL_TYPE_GRABBER, minor_in); if ((ret == -1 ) || ( ret == -23 )) { - info("error registering device %s",loops[nr]->vloopin->name); + info("error registering device %s", loops[nr]->vloopin->name); kfree(loops[nr]->vloopin->priv); kfree(loops[nr]->vloopin); kfree(loops[nr]->vloopout->priv); kfree(loops[nr]->vloopout); kfree(loops[nr]); - loops[nr]=NULL; + loops[nr] = NULL; return ret; } - ret = video_register_device(loops[nr]->vloopout, VFL_TYPE_GRABBER,minor_out); + ret = video_register_device(loops[nr]->vloopout, VFL_TYPE_GRABBER, minor_out); if ((ret ==-1) || (ret == -23)) { info("error registering device %s", loops[nr]->vloopout->name); @@ -1045,12 +1206,12 @@ kfree(loops[nr]->vloopout->priv); kfree(loops[nr]->vloopout); kfree(loops[nr]); - loops[nr]=NULL; + loops[nr] = NULL; return ret; } - loops[nr]->ioctldata=kmalloc(1024, GFP_KERNEL); - loops[nr]->ioctlretdata=kmalloc(1024, GFP_KERNEL); + loops[nr]->ioctldata = kmalloc(1024, GFP_KERNEL); + loops[nr]->ioctlretdata = kmalloc(1024, GFP_KERNEL); return 0; } @@ -1086,6 +1247,16 @@ #endif MODULE_PARM_DESC(dev_offset, "Prefered offset for video device numbers"); + + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +module_param(debug, int, 000); +#else +MODULE_PARM(debug_param, "i"); +#endif + +MODULE_PARM_DESC(debug, "Enable module debug level 0-3 (by default 0)"); + MODULE_LICENSE("GPL"); MODULE_VERSION( VLOOPBACK_VERSION ); @@ -1093,25 +1264,27 @@ { int i,ret; - info("Video4linux loopback driver v"VLOOPBACK_VERSION); + info("video4linux loopback driver v"VLOOPBACK_VERSION); - if (pipes==-1) pipes=1; + if (pipes == -1) + pipes = 1; + if (pipes > MAX_PIPES) { - pipes=MAX_PIPES; + pipes = MAX_PIPES; info("Nr of pipes is limited to: %d", MAX_PIPES); } - for (i=0; i<pipes; i++) { + for (i = 0; i < pipes; i++) { ret = create_pipe(i); if (ret == 0) { info("Loopback %d registered, input: video%d," - "output: video%d", + " output: video%d", i, loops[i]->vloopin->minor, loops[i]->vloopout->minor); - nr_o_pipes=i+1; - }else{ + nr_o_pipes = i + 1; + } else { return ret; } } @@ -1123,16 +1296,22 @@ int i; info("Unregistering video4linux loopback devices"); - for (i=0; i<nr_o_pipes; i++) if (loops[i]) { - kfree(loops[i]->vloopin->priv); - video_unregister_device(loops[i]->vloopin); - kfree(loops[i]->vloopout->priv); - video_unregister_device(loops[i]->vloopout); - if (loops[i]->buffer) rvfree(loops[i]->buffer, loops[i]->buflength*N_BUFFS); - kfree(loops[i]->ioctldata); - kfree(loops[i]->ioctlretdata); - kfree(loops[i]); - } + + for (i = 0; i < nr_o_pipes; i++) { + if (loops[i]) { + kfree(loops[i]->vloopin->priv); + video_unregister_device(loops[i]->vloopin); + kfree(loops[i]->vloopout->priv); + video_unregister_device(loops[i]->vloopout); + + if (loops[i]->buffer) + rvfree(loops[i]->buffer, loops[i]->buflength * N_BUFFS); + + kfree(loops[i]->ioctldata); + kfree(loops[i]->ioctlretdata); + kfree(loops[i]); + } + } } module_init(vloopback_init);