X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fmedia%2Fvideo%2Fvideodev.c;h=6a0e8ca7294803056cf29d11716d7ed175b35e5f;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=078880e4c8c0a89711a2b079d35553a0a3571e19;hpb=76828883507a47dae78837ab5dec5a5b4513c667;p=linux-2.6.git diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index 078880e4c..6a0e8ca72 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -1,20 +1,32 @@ /* - * Video capture interface for Linux + * Video capture interface for Linux version 2 * - * A generic video device interface for the LINUX operating system - * using a set of device structures/vectors for low level operations. + * A generic video device interface for the LINUX operating system + * using a set of device structures/vectors for low level operations. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. * - * Author: Alan Cox, + * Authors: Alan Cox, (version 1) + * Mauro Carvalho Chehab (version 2) * * Fixes: 20000516 Claudio Matsuoka * - Added procfs support */ +#define dbgarg(cmd, fmt, arg...) \ + if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { \ + printk (KERN_DEBUG "%s: ", vfd->name); \ + v4l_printk_ioctl(cmd); \ + printk (KERN_DEBUG "%s: " fmt, vfd->name, ## arg); \ + } + +#define dbgarg2(fmt, arg...) \ + if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) \ + printk (KERN_DEBUG "%s: " fmt, vfd->name, ## arg); + #include #include #include @@ -26,11 +38,16 @@ #include #include #include -#include #include #include +#define __OLD_VIDIOC_ /* To allow fixing old calls*/ +#include + +#ifdef CONFIG_VIDEO_V4L1 #include +#endif +#include #define VIDEO_NUM_DEVICES 256 #define VIDEO_NAME "video4linux" @@ -41,7 +58,8 @@ static ssize_t show_name(struct class_device *cd, char *buf) { - struct video_device *vfd = container_of(cd, struct video_device, class_dev); + struct video_device *vfd = container_of(cd, struct video_device, + class_dev); return sprintf(buf,"%.*s\n",(int)sizeof(vfd->name),vfd->name); } @@ -62,7 +80,8 @@ void video_device_release(struct video_device *vfd) static void video_release(struct class_device *cd) { - struct video_device *vfd = container_of(cd, struct video_device, class_dev); + struct video_device *vfd = container_of(cd, struct video_device, + class_dev); #if 1 /* needed until all drivers are fixed */ @@ -86,18 +105,18 @@ static DEFINE_MUTEX(videodev_lock); struct video_device* video_devdata(struct file *file) { - return video_device[iminor(file->f_dentry->d_inode)]; + return video_device[iminor(file->f_path.dentry->d_inode)]; } /* - * Open a video device. + * Open a video device - FIXME: Obsoleted */ static int video_open(struct inode *inode, struct file *file) { unsigned int minor = iminor(inode); int err = 0; struct video_device *vfl; - struct file_operations *old_fops; + const struct file_operations *old_fops; if(minor>=VIDEO_NUM_DEVICES) return -ENODEV; @@ -130,6 +149,7 @@ static int video_open(struct inode *inode, struct file *file) * helper function -- handles userspace copying for ioctl arguments */ +#ifdef __OLD_VIDIOC_ static unsigned int video_fix_command(unsigned int cmd) { @@ -155,7 +175,11 @@ video_fix_command(unsigned int cmd) } return cmd; } +#endif +/* + * Obsolete usercopy function - Should be removed soon + */ int video_usercopy(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg, @@ -166,8 +190,15 @@ video_usercopy(struct inode *inode, struct file *file, void *mbuf = NULL; void *parg = NULL; int err = -EINVAL; + int is_ext_ctrl; + size_t ctrls_size = 0; + void __user *user_ptr = NULL; +#ifdef __OLD_VIDIOC_ cmd = video_fix_command(cmd); +#endif + is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS || + cmd == VIDIOC_TRY_EXT_CTRLS); /* Copy arguments into temp kernel buffer */ switch (_IOC_DIR(cmd)) { @@ -193,14 +224,43 @@ video_usercopy(struct inode *inode, struct file *file, goto out; break; } + if (is_ext_ctrl) { + struct v4l2_ext_controls *p = parg; + + /* In case of an error, tell the caller that it wasn't + a specific control that caused it. */ + p->error_idx = p->count; + user_ptr = (void __user *)p->controls; + if (p->count) { + ctrls_size = sizeof(struct v4l2_ext_control) * p->count; + /* Note: v4l2_ext_controls fits in sbuf[] so mbuf is still NULL. */ + mbuf = kmalloc(ctrls_size, GFP_KERNEL); + err = -ENOMEM; + if (NULL == mbuf) + goto out_ext_ctrl; + err = -EFAULT; + if (copy_from_user(mbuf, user_ptr, ctrls_size)) + goto out_ext_ctrl; + p->controls = mbuf; + } + } /* call driver */ err = func(inode, file, cmd, parg); if (err == -ENOIOCTLCMD) err = -EINVAL; + if (is_ext_ctrl) { + struct v4l2_ext_controls *p = parg; + + p->controls = (void *)user_ptr; + if (p->count && err == 0 && copy_to_user(user_ptr, mbuf, ctrls_size)) + err = -EFAULT; + goto out_ext_ctrl; + } if (err < 0) goto out; +out_ext_ctrl: /* Copy results into user buffer */ switch (_IOC_DIR(cmd)) { @@ -218,19 +278,20 @@ out: /* * open/release helper functions -- handle exclusive opens + * Should be removed soon */ int video_exclusive_open(struct inode *inode, struct file *file) { struct video_device *vfl = video_devdata(file); int retval = 0; - down(&vfl->lock); + mutex_lock(&vfl->lock); if (vfl->users) { retval = -EBUSY; } else { vfl->users++; } - up(&vfl->lock); + mutex_unlock(&vfl->lock); return retval; } @@ -242,6 +303,1264 @@ int video_exclusive_release(struct inode *inode, struct file *file) return 0; } +static char *v4l2_memory_names[] = { + [V4L2_MEMORY_MMAP] = "mmap", + [V4L2_MEMORY_USERPTR] = "userptr", + [V4L2_MEMORY_OVERLAY] = "overlay", +}; + + +/* FIXME: Those stuff are replicated also on v4l2-common.c */ +static char *v4l2_type_names_FIXME[] = { + [V4L2_BUF_TYPE_VIDEO_CAPTURE] = "video-cap", + [V4L2_BUF_TYPE_VIDEO_OVERLAY] = "video-over", + [V4L2_BUF_TYPE_VIDEO_OUTPUT] = "video-out", + [V4L2_BUF_TYPE_VBI_CAPTURE] = "vbi-cap", + [V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out", + [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT] = "sliced-vbi-out", + [V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-capture", + [V4L2_BUF_TYPE_PRIVATE] = "private", +}; + +static char *v4l2_field_names_FIXME[] = { + [V4L2_FIELD_ANY] = "any", + [V4L2_FIELD_NONE] = "none", + [V4L2_FIELD_TOP] = "top", + [V4L2_FIELD_BOTTOM] = "bottom", + [V4L2_FIELD_INTERLACED] = "interlaced", + [V4L2_FIELD_SEQ_TB] = "seq-tb", + [V4L2_FIELD_SEQ_BT] = "seq-bt", + [V4L2_FIELD_ALTERNATE] = "alternate", +}; + +#define prt_names(a,arr) (((a)>=0)&&((a)timecode; + + dbgarg (cmd, "%02ld:%02d:%02d.%08ld index=%d, type=%s, " + "bytesused=%d, flags=0x%08d, " + "field=%0d, sequence=%d, memory=%s, offset/userptr=0x%08lx, length=%d\n", + (p->timestamp.tv_sec/3600), + (int)(p->timestamp.tv_sec/60)%60, + (int)(p->timestamp.tv_sec%60), + p->timestamp.tv_usec, + p->index, + prt_names(p->type,v4l2_type_names_FIXME), + p->bytesused,p->flags, + p->field,p->sequence, + prt_names(p->memory,v4l2_memory_names), + p->m.userptr, p->length); + dbgarg2 ("timecode= %02d:%02d:%02d type=%d, " + "flags=0x%08d, frames=%d, userbits=0x%08x\n", + tc->hours,tc->minutes,tc->seconds, + tc->type, tc->flags, tc->frames, *(__u32 *) tc->userbits); +} + +static inline void dbgrect(struct video_device *vfd, char *s, + struct v4l2_rect *r) +{ + dbgarg2 ("%sRect start at %dx%d, size= %dx%d\n", s, r->left, r->top, + r->width, r->height); +}; + +static inline void v4l_print_pix_fmt (struct video_device *vfd, + struct v4l2_pix_format *fmt) +{ + dbgarg2 ("width=%d, height=%d, format=%c%c%c%c, field=%s, " + "bytesperline=%d sizeimage=%d, colorspace=%d\n", + fmt->width,fmt->height, + (fmt->pixelformat & 0xff), + (fmt->pixelformat >> 8) & 0xff, + (fmt->pixelformat >> 16) & 0xff, + (fmt->pixelformat >> 24) & 0xff, + prt_names(fmt->field,v4l2_field_names_FIXME), + fmt->bytesperline,fmt->sizeimage,fmt->colorspace); +}; + + +static int check_fmt (struct video_device *vfd, enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (vfd->vidioc_try_fmt_cap) + return (0); + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + if (vfd->vidioc_try_fmt_overlay) + return (0); + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (vfd->vidioc_try_fmt_vbi) + return (0); + break; + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + if (vfd->vidioc_try_fmt_vbi_output) + return (0); + break; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + if (vfd->vidioc_try_fmt_vbi_capture) + return (0); + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (vfd->vidioc_try_fmt_video_output) + return (0); + break; + case V4L2_BUF_TYPE_VBI_OUTPUT: + if (vfd->vidioc_try_fmt_vbi_output) + return (0); + break; + case V4L2_BUF_TYPE_PRIVATE: + if (vfd->vidioc_try_fmt_type_private) + return (0); + break; + } + return (-EINVAL); +} + +static int __video_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *vfd = video_devdata(file); + void *fh = file->private_data; + int ret = -EINVAL; + + if ( (vfd->debug & V4L2_DEBUG_IOCTL) && + !(vfd->debug | V4L2_DEBUG_IOCTL_ARG)) { + v4l_print_ioctl(vfd->name, cmd); + } + + if (_IOC_TYPE(cmd)=='v') + return v4l_compat_translate_ioctl(inode,file,cmd,arg, + __video_do_ioctl); + + switch(cmd) { + /* --- capabilities ------------------------------------------ */ + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *cap = (struct v4l2_capability*)arg; + memset(cap, 0, sizeof(*cap)); + + if (!vfd->vidioc_querycap) + break; + + ret=vfd->vidioc_querycap(file, fh, cap); + if (!ret) + dbgarg (cmd, "driver=%s, card=%s, bus=%s, " + "version=0x%08x, " + "capabilities=0x%08x\n", + cap->driver,cap->card,cap->bus_info, + cap->version, + cap->capabilities); + break; + } + + /* --- priority ------------------------------------------ */ + case VIDIOC_G_PRIORITY: + { + enum v4l2_priority *p=arg; + + if (!vfd->vidioc_g_priority) + break; + ret=vfd->vidioc_g_priority(file, fh, p); + if (!ret) + dbgarg(cmd, "priority is %d\n", *p); + break; + } + case VIDIOC_S_PRIORITY: + { + enum v4l2_priority *p=arg; + + if (!vfd->vidioc_s_priority) + break; + dbgarg(cmd, "setting priority to %d\n", *p); + ret=vfd->vidioc_s_priority(file, fh, *p); + break; + } + + /* --- capture ioctls ---------------------------------------- */ + case VIDIOC_ENUM_FMT: + { + struct v4l2_fmtdesc *f = arg; + enum v4l2_buf_type type; + unsigned int index; + + index = f->index; + type = f->type; + memset(f,0,sizeof(*f)); + f->index = index; + f->type = type; + + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (vfd->vidioc_enum_fmt_cap) + ret=vfd->vidioc_enum_fmt_cap(file, fh, f); + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + if (vfd->vidioc_enum_fmt_overlay) + ret=vfd->vidioc_enum_fmt_overlay(file, fh, f); + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (vfd->vidioc_enum_fmt_vbi) + ret=vfd->vidioc_enum_fmt_vbi(file, fh, f); + break; + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + if (vfd->vidioc_enum_fmt_vbi_output) + ret=vfd->vidioc_enum_fmt_vbi_output(file, + fh, f); + break; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + if (vfd->vidioc_enum_fmt_vbi_capture) + ret=vfd->vidioc_enum_fmt_vbi_capture(file, + fh, f); + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (vfd->vidioc_enum_fmt_video_output) + ret=vfd->vidioc_enum_fmt_video_output(file, + fh, f); + break; + case V4L2_BUF_TYPE_VBI_OUTPUT: + if (vfd->vidioc_enum_fmt_vbi_output) + ret=vfd->vidioc_enum_fmt_vbi_output(file, + fh, f); + break; + case V4L2_BUF_TYPE_PRIVATE: + if (vfd->vidioc_enum_fmt_type_private) + ret=vfd->vidioc_enum_fmt_type_private(file, + fh, f); + break; + } + if (!ret) + dbgarg (cmd, "index=%d, type=%d, flags=%d, " + "pixelformat=%c%c%c%c, description='%s'\n", + f->index, f->type, f->flags, + (f->pixelformat & 0xff), + (f->pixelformat >> 8) & 0xff, + (f->pixelformat >> 16) & 0xff, + (f->pixelformat >> 24) & 0xff, + f->description); + break; + } + case VIDIOC_G_FMT: + { + struct v4l2_format *f = (struct v4l2_format *)arg; + enum v4l2_buf_type type=f->type; + + memset(&f->fmt.pix,0,sizeof(f->fmt.pix)); + f->type=type; + + /* FIXME: Should be one dump per type */ + dbgarg (cmd, "type=%s\n", prt_names(type, + v4l2_type_names_FIXME)); + + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (vfd->vidioc_g_fmt_cap) + ret=vfd->vidioc_g_fmt_cap(file, fh, f); + if (!ret) + v4l_print_pix_fmt(vfd,&f->fmt.pix); + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + if (vfd->vidioc_g_fmt_overlay) + ret=vfd->vidioc_g_fmt_overlay(file, fh, f); + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (vfd->vidioc_g_fmt_vbi) + ret=vfd->vidioc_g_fmt_vbi(file, fh, f); + break; + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + if (vfd->vidioc_g_fmt_vbi_output) + ret=vfd->vidioc_g_fmt_vbi_output(file, fh, f); + break; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + if (vfd->vidioc_g_fmt_vbi_capture) + ret=vfd->vidioc_g_fmt_vbi_capture(file, fh, f); + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (vfd->vidioc_g_fmt_video_output) + ret=vfd->vidioc_g_fmt_video_output(file, + fh, f); + break; + case V4L2_BUF_TYPE_VBI_OUTPUT: + if (vfd->vidioc_g_fmt_vbi_output) + ret=vfd->vidioc_g_fmt_vbi_output(file, fh, f); + break; + case V4L2_BUF_TYPE_PRIVATE: + if (vfd->vidioc_g_fmt_type_private) + ret=vfd->vidioc_g_fmt_type_private(file, + fh, f); + break; + } + + break; + } + case VIDIOC_S_FMT: + { + struct v4l2_format *f = (struct v4l2_format *)arg; + + /* FIXME: Should be one dump per type */ + dbgarg (cmd, "type=%s\n", prt_names(f->type, + v4l2_type_names_FIXME)); + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + v4l_print_pix_fmt(vfd,&f->fmt.pix); + if (vfd->vidioc_s_fmt_cap) + ret=vfd->vidioc_s_fmt_cap(file, fh, f); + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + if (vfd->vidioc_s_fmt_overlay) + ret=vfd->vidioc_s_fmt_overlay(file, fh, f); + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (vfd->vidioc_s_fmt_vbi) + ret=vfd->vidioc_s_fmt_vbi(file, fh, f); + break; + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + if (vfd->vidioc_s_fmt_vbi_output) + ret=vfd->vidioc_s_fmt_vbi_output(file, fh, f); + break; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + if (vfd->vidioc_s_fmt_vbi_capture) + ret=vfd->vidioc_s_fmt_vbi_capture(file, fh, f); + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (vfd->vidioc_s_fmt_video_output) + ret=vfd->vidioc_s_fmt_video_output(file, + fh, f); + break; + case V4L2_BUF_TYPE_VBI_OUTPUT: + if (vfd->vidioc_s_fmt_vbi_output) + ret=vfd->vidioc_s_fmt_vbi_output(file, + fh, f); + break; + case V4L2_BUF_TYPE_PRIVATE: + if (vfd->vidioc_s_fmt_type_private) + ret=vfd->vidioc_s_fmt_type_private(file, + fh, f); + break; + } + break; + } + case VIDIOC_TRY_FMT: + { + struct v4l2_format *f = (struct v4l2_format *)arg; + + /* FIXME: Should be one dump per type */ + dbgarg (cmd, "type=%s\n", prt_names(f->type, + v4l2_type_names_FIXME)); + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (vfd->vidioc_try_fmt_cap) + ret=vfd->vidioc_try_fmt_cap(file, fh, f); + if (!ret) + v4l_print_pix_fmt(vfd,&f->fmt.pix); + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + if (vfd->vidioc_try_fmt_overlay) + ret=vfd->vidioc_try_fmt_overlay(file, fh, f); + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (vfd->vidioc_try_fmt_vbi) + ret=vfd->vidioc_try_fmt_vbi(file, fh, f); + break; + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + if (vfd->vidioc_try_fmt_vbi_output) + ret=vfd->vidioc_try_fmt_vbi_output(file, + fh, f); + break; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + if (vfd->vidioc_try_fmt_vbi_capture) + ret=vfd->vidioc_try_fmt_vbi_capture(file, + fh, f); + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (vfd->vidioc_try_fmt_video_output) + ret=vfd->vidioc_try_fmt_video_output(file, + fh, f); + break; + case V4L2_BUF_TYPE_VBI_OUTPUT: + if (vfd->vidioc_try_fmt_vbi_output) + ret=vfd->vidioc_try_fmt_vbi_output(file, + fh, f); + break; + case V4L2_BUF_TYPE_PRIVATE: + if (vfd->vidioc_try_fmt_type_private) + ret=vfd->vidioc_try_fmt_type_private(file, + fh, f); + break; + } + + break; + } + /* FIXME: Those buf reqs could be handled here, + with some changes on videobuf to allow its header to be included at + videodev2.h or being merged at videodev2. + */ + case VIDIOC_REQBUFS: + { + struct v4l2_requestbuffers *p=arg; + + if (!vfd->vidioc_reqbufs) + break; + ret = check_fmt (vfd, p->type); + if (ret) + break; + + ret=vfd->vidioc_reqbufs(file, fh, p); + dbgarg (cmd, "count=%d, type=%s, memory=%s\n", + p->count, + prt_names(p->type,v4l2_type_names_FIXME), + prt_names(p->memory,v4l2_memory_names)); + break; + } + case VIDIOC_QUERYBUF: + { + struct v4l2_buffer *p=arg; + + if (!vfd->vidioc_querybuf) + break; + ret = check_fmt (vfd, p->type); + if (ret) + break; + + ret=vfd->vidioc_querybuf(file, fh, p); + if (!ret) + dbgbuf(cmd,vfd,p); + break; + } + case VIDIOC_QBUF: + { + struct v4l2_buffer *p=arg; + + if (!vfd->vidioc_qbuf) + break; + ret = check_fmt (vfd, p->type); + if (ret) + break; + + ret=vfd->vidioc_qbuf(file, fh, p); + if (!ret) + dbgbuf(cmd,vfd,p); + break; + } + case VIDIOC_DQBUF: + { + struct v4l2_buffer *p=arg; + if (!vfd->vidioc_dqbuf) + break; + ret = check_fmt (vfd, p->type); + if (ret) + break; + + ret=vfd->vidioc_dqbuf(file, fh, p); + if (!ret) + dbgbuf(cmd,vfd,p); + break; + } + case VIDIOC_OVERLAY: + { + int *i = arg; + + if (!vfd->vidioc_overlay) + break; + dbgarg (cmd, "value=%d\n",*i); + ret=vfd->vidioc_overlay(file, fh, *i); + break; + } +#ifdef CONFIG_VIDEO_V4L1_COMPAT + /* --- streaming capture ------------------------------------- */ + case VIDIOCGMBUF: + { + struct video_mbuf *p=arg; + + memset(p,0,sizeof(p)); + + if (!vfd->vidiocgmbuf) + break; + ret=vfd->vidiocgmbuf(file, fh, p); + if (!ret) + dbgarg (cmd, "size=%d, frames=%d, offsets=0x%08lx\n", + p->size, p->frames, + (unsigned long)p->offsets); + break; + } +#endif + case VIDIOC_G_FBUF: + { + struct v4l2_framebuffer *p=arg; + if (!vfd->vidioc_g_fbuf) + break; + ret=vfd->vidioc_g_fbuf(file, fh, arg); + if (!ret) { + dbgarg (cmd, "capability=%d, flags=%d, base=0x%08lx\n", + p->capability,p->flags, + (unsigned long)p->base); + v4l_print_pix_fmt (vfd, &p->fmt); + } + break; + } + case VIDIOC_S_FBUF: + { + struct v4l2_framebuffer *p=arg; + if (!vfd->vidioc_s_fbuf) + break; + + dbgarg (cmd, "capability=%d, flags=%d, base=0x%08lx\n", + p->capability,p->flags,(unsigned long)p->base); + v4l_print_pix_fmt (vfd, &p->fmt); + ret=vfd->vidioc_s_fbuf(file, fh, arg); + + break; + } + case VIDIOC_STREAMON: + { + enum v4l2_buf_type i = *(int *)arg; + if (!vfd->vidioc_streamon) + break; + dbgarg (cmd, "type=%s\n", prt_names(i,v4l2_type_names_FIXME)); + ret=vfd->vidioc_streamon(file, fh,i); + break; + } + case VIDIOC_STREAMOFF: + { + enum v4l2_buf_type i = *(int *)arg; + + if (!vfd->vidioc_streamoff) + break; + dbgarg (cmd, "type=%s\n", prt_names(i,v4l2_type_names_FIXME)); + ret=vfd->vidioc_streamoff(file, fh, i); + break; + } + /* ---------- tv norms ---------- */ + case VIDIOC_ENUMSTD: + { + struct v4l2_standard *p = arg; + v4l2_std_id id = vfd->tvnorms,curr_id=0; + unsigned int index = p->index,i; + + if (index<0) { + ret=-EINVAL; + break; + } + + /* Return norm array on a canonical way */ + for (i=0;i<= index && id; i++) { + if ( (id & V4L2_STD_PAL) == V4L2_STD_PAL) { + curr_id = V4L2_STD_PAL; + } else if ( (id & V4L2_STD_PAL_BG) == V4L2_STD_PAL_BG) { + curr_id = V4L2_STD_PAL_BG; + } else if ( (id & V4L2_STD_PAL_DK) == V4L2_STD_PAL_DK) { + curr_id = V4L2_STD_PAL_DK; + } else if ( (id & V4L2_STD_PAL_B) == V4L2_STD_PAL_B) { + curr_id = V4L2_STD_PAL_B; + } else if ( (id & V4L2_STD_PAL_B1) == V4L2_STD_PAL_B1) { + curr_id = V4L2_STD_PAL_B1; + } else if ( (id & V4L2_STD_PAL_G) == V4L2_STD_PAL_G) { + curr_id = V4L2_STD_PAL_G; + } else if ( (id & V4L2_STD_PAL_H) == V4L2_STD_PAL_H) { + curr_id = V4L2_STD_PAL_H; + } else if ( (id & V4L2_STD_PAL_I) == V4L2_STD_PAL_I) { + curr_id = V4L2_STD_PAL_I; + } else if ( (id & V4L2_STD_PAL_D) == V4L2_STD_PAL_D) { + curr_id = V4L2_STD_PAL_D; + } else if ( (id & V4L2_STD_PAL_D1) == V4L2_STD_PAL_D1) { + curr_id = V4L2_STD_PAL_D1; + } else if ( (id & V4L2_STD_PAL_K) == V4L2_STD_PAL_K) { + curr_id = V4L2_STD_PAL_K; + } else if ( (id & V4L2_STD_PAL_M) == V4L2_STD_PAL_M) { + curr_id = V4L2_STD_PAL_M; + } else if ( (id & V4L2_STD_PAL_N) == V4L2_STD_PAL_N) { + curr_id = V4L2_STD_PAL_N; + } else if ( (id & V4L2_STD_PAL_Nc) == V4L2_STD_PAL_Nc) { + curr_id = V4L2_STD_PAL_Nc; + } else if ( (id & V4L2_STD_PAL_60) == V4L2_STD_PAL_60) { + curr_id = V4L2_STD_PAL_60; + } else if ( (id & V4L2_STD_NTSC) == V4L2_STD_NTSC) { + curr_id = V4L2_STD_NTSC; + } else if ( (id & V4L2_STD_NTSC_M) == V4L2_STD_NTSC_M) { + curr_id = V4L2_STD_NTSC_M; + } else if ( (id & V4L2_STD_NTSC_M_JP) == V4L2_STD_NTSC_M_JP) { + curr_id = V4L2_STD_NTSC_M_JP; + } else if ( (id & V4L2_STD_NTSC_443) == V4L2_STD_NTSC_443) { + curr_id = V4L2_STD_NTSC_443; + } else if ( (id & V4L2_STD_NTSC_M_KR) == V4L2_STD_NTSC_M_KR) { + curr_id = V4L2_STD_NTSC_M_KR; + } else if ( (id & V4L2_STD_SECAM) == V4L2_STD_SECAM) { + curr_id = V4L2_STD_SECAM; + } else if ( (id & V4L2_STD_SECAM_DK) == V4L2_STD_SECAM_DK) { + curr_id = V4L2_STD_SECAM_DK; + } else if ( (id & V4L2_STD_SECAM_B) == V4L2_STD_SECAM_B) { + curr_id = V4L2_STD_SECAM_B; + } else if ( (id & V4L2_STD_SECAM_D) == V4L2_STD_SECAM_D) { + curr_id = V4L2_STD_SECAM_D; + } else if ( (id & V4L2_STD_SECAM_G) == V4L2_STD_SECAM_G) { + curr_id = V4L2_STD_SECAM_G; + } else if ( (id & V4L2_STD_SECAM_H) == V4L2_STD_SECAM_H) { + curr_id = V4L2_STD_SECAM_H; + } else if ( (id & V4L2_STD_SECAM_K) == V4L2_STD_SECAM_K) { + curr_id = V4L2_STD_SECAM_K; + } else if ( (id & V4L2_STD_SECAM_K1) == V4L2_STD_SECAM_K1) { + curr_id = V4L2_STD_SECAM_K1; + } else if ( (id & V4L2_STD_SECAM_L) == V4L2_STD_SECAM_L) { + curr_id = V4L2_STD_SECAM_L; + } else if ( (id & V4L2_STD_SECAM_LC) == V4L2_STD_SECAM_LC) { + curr_id = V4L2_STD_SECAM_LC; + } else { + break; + } + id &= ~curr_id; + } + if (i<=index) + return -EINVAL; + + v4l2_video_std_construct(p, curr_id,v4l2_norm_to_name(curr_id)); + p->index = index; + + dbgarg (cmd, "index=%d, id=%Ld, name=%s, fps=%d/%d, " + "framelines=%d\n", p->index, + (unsigned long long)p->id, p->name, + p->frameperiod.numerator, + p->frameperiod.denominator, + p->framelines); + + ret=0; + break; + } + case VIDIOC_G_STD: + { + v4l2_std_id *id = arg; + + *id = vfd->current_norm; + + dbgarg (cmd, "value=%Lu\n", (long long unsigned) *id); + + ret=0; + break; + } + case VIDIOC_S_STD: + { + v4l2_std_id *id = arg,norm; + + dbgarg (cmd, "value=%Lu\n", (long long unsigned) *id); + + norm = (*id) & vfd->tvnorms; + if ( vfd->tvnorms && !norm) /* Check if std is supported */ + break; + + /* Calls the specific handler */ + if (vfd->vidioc_s_std) + ret=vfd->vidioc_s_std(file, fh, &norm); + else + ret=-EINVAL; + + /* Updates standard information */ + if (ret>=0) + vfd->current_norm=norm; + + break; + } + case VIDIOC_QUERYSTD: + { + v4l2_std_id *p=arg; + + if (!vfd->vidioc_querystd) + break; + ret=vfd->vidioc_querystd(file, fh, arg); + if (!ret) + dbgarg (cmd, "detected std=%Lu\n", + (unsigned long long)*p); + break; + } + /* ------ input switching ---------- */ + /* FIXME: Inputs can be handled inside videodev2 */ + case VIDIOC_ENUMINPUT: + { + struct v4l2_input *p=arg; + int i=p->index; + + if (!vfd->vidioc_enum_input) + break; + memset(p, 0, sizeof(*p)); + p->index=i; + + ret=vfd->vidioc_enum_input(file, fh, p); + if (!ret) + dbgarg (cmd, "index=%d, name=%s, type=%d, " + "audioset=%d, " + "tuner=%d, std=%Ld, status=%d\n", + p->index,p->name,p->type,p->audioset, + p->tuner, + (unsigned long long)p->std, + p->status); + break; + } + case VIDIOC_G_INPUT: + { + unsigned int *i = arg; + + if (!vfd->vidioc_g_input) + break; + ret=vfd->vidioc_g_input(file, fh, i); + if (!ret) + dbgarg (cmd, "value=%d\n",*i); + break; + } + case VIDIOC_S_INPUT: + { + unsigned int *i = arg; + + if (!vfd->vidioc_s_input) + break; + dbgarg (cmd, "value=%d\n",*i); + ret=vfd->vidioc_s_input(file, fh, *i); + break; + } + + /* ------ output switching ---------- */ + case VIDIOC_G_OUTPUT: + { + unsigned int *i = arg; + + if (!vfd->vidioc_g_output) + break; + ret=vfd->vidioc_g_output(file, fh, i); + if (!ret) + dbgarg (cmd, "value=%d\n",*i); + break; + } + case VIDIOC_S_OUTPUT: + { + unsigned int *i = arg; + + if (!vfd->vidioc_s_output) + break; + dbgarg (cmd, "value=%d\n",*i); + ret=vfd->vidioc_s_output(file, fh, *i); + break; + } + + /* --- controls ---------------------------------------------- */ + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *p=arg; + + if (!vfd->vidioc_queryctrl) + break; + ret=vfd->vidioc_queryctrl(file, fh, p); + + if (!ret) + dbgarg (cmd, "id=%d, type=%d, name=%s, " + "min/max=%d/%d," + " step=%d, default=%d, flags=0x%08x\n", + p->id,p->type,p->name,p->minimum, + p->maximum,p->step,p->default_value, + p->flags); + break; + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *p = arg; + + if (!vfd->vidioc_g_ctrl) + break; + dbgarg(cmd, "Enum for index=%d\n", p->id); + + ret=vfd->vidioc_g_ctrl(file, fh, p); + if (!ret) + dbgarg2 ( "id=%d, value=%d\n", p->id, p->value); + break; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *p = arg; + + if (!vfd->vidioc_s_ctrl) + break; + dbgarg (cmd, "id=%d, value=%d\n", p->id, p->value); + + ret=vfd->vidioc_s_ctrl(file, fh, p); + break; + } + case VIDIOC_G_EXT_CTRLS: + { + struct v4l2_ext_controls *p = arg; + + if (vfd->vidioc_g_ext_ctrls) { + dbgarg(cmd, "count=%d\n", p->count); + + ret=vfd->vidioc_g_ext_ctrls(file, fh, p); + } + break; + } + case VIDIOC_S_EXT_CTRLS: + { + struct v4l2_ext_controls *p = arg; + + if (vfd->vidioc_s_ext_ctrls) { + dbgarg(cmd, "count=%d\n", p->count); + + ret=vfd->vidioc_s_ext_ctrls(file, fh, p); + } + break; + } + case VIDIOC_TRY_EXT_CTRLS: + { + struct v4l2_ext_controls *p = arg; + + if (vfd->vidioc_try_ext_ctrls) { + dbgarg(cmd, "count=%d\n", p->count); + + ret=vfd->vidioc_try_ext_ctrls(file, fh, p); + } + break; + } + case VIDIOC_QUERYMENU: + { + struct v4l2_querymenu *p=arg; + if (!vfd->vidioc_querymenu) + break; + ret=vfd->vidioc_querymenu(file, fh, p); + if (!ret) + dbgarg (cmd, "id=%d, index=%d, name=%s\n", + p->id,p->index,p->name); + break; + } + /* --- audio ---------------------------------------------- */ + case VIDIOC_ENUMAUDIO: + { + struct v4l2_audio *p=arg; + + if (!vfd->vidioc_enumaudio) + break; + dbgarg(cmd, "Enum for index=%d\n", p->index); + ret=vfd->vidioc_enumaudio(file, fh, p); + if (!ret) + dbgarg2("index=%d, name=%s, capability=%d, " + "mode=%d\n",p->index,p->name, + p->capability, p->mode); + break; + } + case VIDIOC_G_AUDIO: + { + struct v4l2_audio *p=arg; + __u32 index=p->index; + + if (!vfd->vidioc_g_audio) + break; + + memset(p,0,sizeof(*p)); + p->index=index; + dbgarg(cmd, "Get for index=%d\n", p->index); + ret=vfd->vidioc_g_audio(file, fh, p); + if (!ret) + dbgarg2("index=%d, name=%s, capability=%d, " + "mode=%d\n",p->index, + p->name,p->capability, p->mode); + break; + } + case VIDIOC_S_AUDIO: + { + struct v4l2_audio *p=arg; + + if (!vfd->vidioc_s_audio) + break; + dbgarg(cmd, "index=%d, name=%s, capability=%d, " + "mode=%d\n", p->index, p->name, + p->capability, p->mode); + ret=vfd->vidioc_s_audio(file, fh, p); + break; + } + case VIDIOC_ENUMAUDOUT: + { + struct v4l2_audioout *p=arg; + + if (!vfd->vidioc_enumaudout) + break; + dbgarg(cmd, "Enum for index=%d\n", p->index); + ret=vfd->vidioc_enumaudout(file, fh, p); + if (!ret) + dbgarg2("index=%d, name=%s, capability=%d, " + "mode=%d\n", p->index, p->name, + p->capability,p->mode); + break; + } + case VIDIOC_G_AUDOUT: + { + struct v4l2_audioout *p=arg; + + if (!vfd->vidioc_g_audout) + break; + dbgarg(cmd, "Enum for index=%d\n", p->index); + ret=vfd->vidioc_g_audout(file, fh, p); + if (!ret) + dbgarg2("index=%d, name=%s, capability=%d, " + "mode=%d\n", p->index, p->name, + p->capability,p->mode); + break; + } + case VIDIOC_S_AUDOUT: + { + struct v4l2_audioout *p=arg; + + if (!vfd->vidioc_s_audout) + break; + dbgarg(cmd, "index=%d, name=%s, capability=%d, " + "mode=%d\n", p->index, p->name, + p->capability,p->mode); + + ret=vfd->vidioc_s_audout(file, fh, p); + break; + } + case VIDIOC_G_MODULATOR: + { + struct v4l2_modulator *p=arg; + if (!vfd->vidioc_g_modulator) + break; + ret=vfd->vidioc_g_modulator(file, fh, p); + if (!ret) + dbgarg(cmd, "index=%d, name=%s, " + "capability=%d, rangelow=%d," + " rangehigh=%d, txsubchans=%d\n", + p->index, p->name,p->capability, + p->rangelow, p->rangehigh, + p->txsubchans); + break; + } + case VIDIOC_S_MODULATOR: + { + struct v4l2_modulator *p=arg; + if (!vfd->vidioc_s_modulator) + break; + dbgarg(cmd, "index=%d, name=%s, capability=%d, " + "rangelow=%d, rangehigh=%d, txsubchans=%d\n", + p->index, p->name,p->capability,p->rangelow, + p->rangehigh,p->txsubchans); + ret=vfd->vidioc_s_modulator(file, fh, p); + break; + } + case VIDIOC_G_CROP: + { + struct v4l2_crop *p=arg; + if (!vfd->vidioc_g_crop) + break; + ret=vfd->vidioc_g_crop(file, fh, p); + if (!ret) { + dbgarg(cmd, "type=%d\n", p->type); + dbgrect(vfd, "", &p->c); + } + break; + } + case VIDIOC_S_CROP: + { + struct v4l2_crop *p=arg; + if (!vfd->vidioc_s_crop) + break; + dbgarg(cmd, "type=%d\n", p->type); + dbgrect(vfd, "", &p->c); + ret=vfd->vidioc_s_crop(file, fh, p); + break; + } + case VIDIOC_CROPCAP: + { + struct v4l2_cropcap *p=arg; + /*FIXME: Should also show v4l2_fract pixelaspect */ + if (!vfd->vidioc_cropcap) + break; + dbgarg(cmd, "type=%d\n", p->type); + dbgrect(vfd, "bounds ", &p->bounds); + dbgrect(vfd, "defrect ", &p->defrect); + ret=vfd->vidioc_cropcap(file, fh, p); + break; + } + case VIDIOC_G_MPEGCOMP: + { + struct v4l2_mpeg_compression *p=arg; + + /*FIXME: Several fields not shown */ + if (!vfd->vidioc_g_mpegcomp) + break; + ret=vfd->vidioc_g_mpegcomp(file, fh, p); + if (!ret) + dbgarg (cmd, "ts_pid_pmt=%d, ts_pid_audio=%d," + " ts_pid_video=%d, ts_pid_pcr=%d, " + "ps_size=%d, au_sample_rate=%d, " + "au_pesid=%c, vi_frame_rate=%d, " + "vi_frames_per_gop=%d, " + "vi_bframes_count=%d, vi_pesid=%c\n", + p->ts_pid_pmt,p->ts_pid_audio, + p->ts_pid_video,p->ts_pid_pcr, + p->ps_size, p->au_sample_rate, + p->au_pesid, p->vi_frame_rate, + p->vi_frames_per_gop, + p->vi_bframes_count, p->vi_pesid); + break; + } + case VIDIOC_S_MPEGCOMP: + { + struct v4l2_mpeg_compression *p=arg; + /*FIXME: Several fields not shown */ + if (!vfd->vidioc_s_mpegcomp) + break; + dbgarg (cmd, "ts_pid_pmt=%d, ts_pid_audio=%d, " + "ts_pid_video=%d, ts_pid_pcr=%d, ps_size=%d, " + "au_sample_rate=%d, au_pesid=%c, " + "vi_frame_rate=%d, vi_frames_per_gop=%d, " + "vi_bframes_count=%d, vi_pesid=%c\n", + p->ts_pid_pmt,p->ts_pid_audio, p->ts_pid_video, + p->ts_pid_pcr, p->ps_size, p->au_sample_rate, + p->au_pesid, p->vi_frame_rate, + p->vi_frames_per_gop, p->vi_bframes_count, + p->vi_pesid); + ret=vfd->vidioc_s_mpegcomp(file, fh, p); + break; + } + case VIDIOC_G_JPEGCOMP: + { + struct v4l2_jpegcompression *p=arg; + if (!vfd->vidioc_g_jpegcomp) + break; + ret=vfd->vidioc_g_jpegcomp(file, fh, p); + if (!ret) + dbgarg (cmd, "quality=%d, APPn=%d, " + "APP_len=%d, COM_len=%d, " + "jpeg_markers=%d\n", + p->quality,p->APPn,p->APP_len, + p->COM_len,p->jpeg_markers); + break; + } + case VIDIOC_S_JPEGCOMP: + { + struct v4l2_jpegcompression *p=arg; + if (!vfd->vidioc_g_jpegcomp) + break; + dbgarg (cmd, "quality=%d, APPn=%d, APP_len=%d, " + "COM_len=%d, jpeg_markers=%d\n", + p->quality,p->APPn,p->APP_len, + p->COM_len,p->jpeg_markers); + ret=vfd->vidioc_s_jpegcomp(file, fh, p); + break; + } + case VIDIOC_G_PARM: + { + struct v4l2_streamparm *p=arg; + if (vfd->vidioc_g_parm) { + ret=vfd->vidioc_g_parm(file, fh, p); + } else { + struct v4l2_standard s; + + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + v4l2_video_std_construct(&s, vfd->current_norm, + v4l2_norm_to_name(vfd->current_norm)); + + memset(p,0,sizeof(*p)); + + p->parm.capture.timeperframe = s.frameperiod; + ret=0; + } + + dbgarg (cmd, "type=%d\n", p->type); + break; + } + case VIDIOC_S_PARM: + { + struct v4l2_streamparm *p=arg; + if (!vfd->vidioc_s_parm) + break; + dbgarg (cmd, "type=%d\n", p->type); + ret=vfd->vidioc_s_parm(file, fh, p); + break; + } + case VIDIOC_G_TUNER: + { + struct v4l2_tuner *p=arg; + __u32 index=p->index; + + if (!vfd->vidioc_g_tuner) + break; + + memset(p,0,sizeof(*p)); + p->index=index; + + ret=vfd->vidioc_g_tuner(file, fh, p); + if (!ret) + dbgarg (cmd, "index=%d, name=%s, type=%d, " + "capability=%d, rangelow=%d, " + "rangehigh=%d, signal=%d, afc=%d, " + "rxsubchans=%d, audmode=%d\n", + p->index, p->name, p->type, + p->capability, p->rangelow, + p->rangehigh, p->rxsubchans, + p->audmode, p->signal, p->afc); + break; + } + case VIDIOC_S_TUNER: + { + struct v4l2_tuner *p=arg; + if (!vfd->vidioc_s_tuner) + break; + dbgarg (cmd, "index=%d, name=%s, type=%d, " + "capability=%d, rangelow=%d, rangehigh=%d, " + "signal=%d, afc=%d, rxsubchans=%d, " + "audmode=%d\n",p->index, p->name, p->type, + p->capability, p->rangelow,p->rangehigh, + p->rxsubchans, p->audmode, p->signal, + p->afc); + ret=vfd->vidioc_s_tuner(file, fh, p); + break; + } + case VIDIOC_G_FREQUENCY: + { + struct v4l2_frequency *p=arg; + if (!vfd->vidioc_g_frequency) + break; + + memset(p,0,sizeof(*p)); + + ret=vfd->vidioc_g_frequency(file, fh, p); + if (!ret) + dbgarg (cmd, "tuner=%d, type=%d, frequency=%d\n", + p->tuner,p->type,p->frequency); + break; + } + case VIDIOC_S_FREQUENCY: + { + struct v4l2_frequency *p=arg; + if (!vfd->vidioc_s_frequency) + break; + dbgarg (cmd, "tuner=%d, type=%d, frequency=%d\n", + p->tuner,p->type,p->frequency); + ret=vfd->vidioc_s_frequency(file, fh, p); + break; + } + case VIDIOC_G_SLICED_VBI_CAP: + { + struct v4l2_sliced_vbi_cap *p=arg; + if (!vfd->vidioc_g_sliced_vbi_cap) + break; + ret=vfd->vidioc_g_sliced_vbi_cap(file, fh, p); + if (!ret) + dbgarg (cmd, "service_set=%d\n", p->service_set); + break; + } + case VIDIOC_LOG_STATUS: + { + if (!vfd->vidioc_log_status) + break; + ret=vfd->vidioc_log_status(file, fh); + break; + } + } /* switch */ + + if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { + if (ret<0) { + printk ("%s: err:\n", vfd->name); + v4l_print_ioctl(vfd->name, cmd); + } + } + + return ret; +} + +int video_ioctl2 (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + char sbuf[128]; + void *mbuf = NULL; + void *parg = NULL; + int err = -EINVAL; + int is_ext_ctrl; + size_t ctrls_size = 0; + void __user *user_ptr = NULL; + +#ifdef __OLD_VIDIOC_ + cmd = video_fix_command(cmd); +#endif + is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS || + cmd == VIDIOC_TRY_EXT_CTRLS); + + /* Copy arguments into temp kernel buffer */ + switch (_IOC_DIR(cmd)) { + case _IOC_NONE: + parg = NULL; + break; + case _IOC_READ: + case _IOC_WRITE: + case (_IOC_WRITE | _IOC_READ): + if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { + parg = sbuf; + } else { + /* too big to allocate from stack */ + mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL); + if (NULL == mbuf) + return -ENOMEM; + parg = mbuf; + } + + err = -EFAULT; + if (_IOC_DIR(cmd) & _IOC_WRITE) + if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) + goto out; + break; + } + + if (is_ext_ctrl) { + struct v4l2_ext_controls *p = parg; + + /* In case of an error, tell the caller that it wasn't + a specific control that caused it. */ + p->error_idx = p->count; + user_ptr = (void __user *)p->controls; + if (p->count) { + ctrls_size = sizeof(struct v4l2_ext_control) * p->count; + /* Note: v4l2_ext_controls fits in sbuf[] so mbuf is still NULL. */ + mbuf = kmalloc(ctrls_size, GFP_KERNEL); + err = -ENOMEM; + if (NULL == mbuf) + goto out_ext_ctrl; + err = -EFAULT; + if (copy_from_user(mbuf, user_ptr, ctrls_size)) + goto out_ext_ctrl; + p->controls = mbuf; + } + } + + /* Handles IOCTL */ + err = __video_do_ioctl(inode, file, cmd, parg); + if (err == -ENOIOCTLCMD) + err = -EINVAL; + if (is_ext_ctrl) { + struct v4l2_ext_controls *p = parg; + + p->controls = (void *)user_ptr; + if (p->count && err == 0 && copy_to_user(user_ptr, mbuf, ctrls_size)) + err = -EFAULT; + goto out_ext_ctrl; + } + if (err < 0) + goto out; + +out_ext_ctrl: + /* Copy results into user buffer */ + switch (_IOC_DIR(cmd)) + { + case _IOC_READ: + case (_IOC_WRITE | _IOC_READ): + if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) + err = -EFAULT; + break; + } + +out: + kfree(mbuf); + return err; +} + + static struct file_operations video_fops; /** @@ -274,31 +1593,34 @@ int video_register_device(struct video_device *vfd, int type, int nr) int i=0; int base; int end; + int ret; char *name_base; switch(type) { case VFL_TYPE_GRABBER: - base=0; - end=64; + base=MINOR_VFL_TYPE_GRABBER_MIN; + end=MINOR_VFL_TYPE_GRABBER_MAX+1; name_base = "video"; break; case VFL_TYPE_VTX: - base=192; - end=224; + base=MINOR_VFL_TYPE_VTX_MIN; + end=MINOR_VFL_TYPE_VTX_MAX+1; name_base = "vtx"; break; case VFL_TYPE_VBI: - base=224; - end=256; + base=MINOR_VFL_TYPE_VBI_MIN; + end=MINOR_VFL_TYPE_VBI_MAX+1; name_base = "vbi"; break; case VFL_TYPE_RADIO: - base=64; - end=128; + base=MINOR_VFL_TYPE_RADIO_MIN; + end=MINOR_VFL_TYPE_RADIO_MAX+1; name_base = "radio"; break; default: + printk(KERN_ERR "%s called with unknown type: %d\n", + __FUNCTION__, type); return -1; } @@ -324,11 +1646,7 @@ int video_register_device(struct video_device *vfd, int type, int nr) video_device[i]=vfd; vfd->minor=i; mutex_unlock(&videodev_lock); - - sprintf(vfd->devfs_name, "v4l/%s%d", name_base, i - base); - devfs_mk_cdev(MKDEV(VIDEO_MAJOR, vfd->minor), - S_IFCHR | S_IRUSR | S_IWUSR, vfd->devfs_name); - init_MUTEX(&vfd->lock); + mutex_init(&vfd->lock); /* sysfs class */ memset(&vfd->class_dev, 0x00, sizeof(vfd->class_dev)); @@ -336,10 +1654,19 @@ int video_register_device(struct video_device *vfd, int type, int nr) vfd->class_dev.dev = vfd->dev; vfd->class_dev.class = &video_class; vfd->class_dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor); - strlcpy(vfd->class_dev.class_id, vfd->devfs_name + 4, BUS_ID_SIZE); - class_device_register(&vfd->class_dev); - class_device_create_file(&vfd->class_dev, - &class_device_attr_name); + sprintf(vfd->class_dev.class_id, "%s%d", name_base, i - base); + ret = class_device_register(&vfd->class_dev); + if (ret < 0) { + printk(KERN_ERR "%s: class_device_register failed\n", + __FUNCTION__); + goto fail_minor; + } + ret = class_device_create_file(&vfd->class_dev, &class_device_attr_name); + if (ret < 0) { + printk(KERN_ERR "%s: class_device_create_file 'name' failed\n", + __FUNCTION__); + goto fail_classdev; + } #if 1 /* needed until all drivers are fixed */ @@ -349,6 +1676,15 @@ int video_register_device(struct video_device *vfd, int type, int nr) "http://lwn.net/Articles/36850/\n", vfd->name); #endif return 0; + +fail_classdev: + class_device_unregister(&vfd->class_dev); +fail_minor: + mutex_lock(&videodev_lock); + video_device[vfd->minor] = NULL; + vfd->minor = -1; + mutex_unlock(&videodev_lock); + return ret; } /** @@ -365,13 +1701,14 @@ void video_unregister_device(struct video_device *vfd) if(video_device[vfd->minor]!=vfd) panic("videodev: bad unregister"); - devfs_remove(vfd->devfs_name); video_device[vfd->minor]=NULL; class_device_unregister(&vfd->class_dev); mutex_unlock(&videodev_lock); } - +/* + * Video fs operations + */ static struct file_operations video_fops= { .owner = THIS_MODULE, @@ -387,7 +1724,7 @@ static int __init videodev_init(void) { int ret; - printk(KERN_INFO "Linux video capture interface: v1.00\n"); + printk(KERN_INFO "Linux video capture interface: v2.00\n"); if (register_chrdev(VIDEO_MAJOR, VIDEO_NAME, &video_fops)) { printk(KERN_WARNING "video_dev: unable to get major %d\n", VIDEO_MAJOR); return -EIO; @@ -418,11 +1755,12 @@ EXPORT_SYMBOL(video_devdata); EXPORT_SYMBOL(video_usercopy); EXPORT_SYMBOL(video_exclusive_open); EXPORT_SYMBOL(video_exclusive_release); +EXPORT_SYMBOL(video_ioctl2); EXPORT_SYMBOL(video_device_alloc); EXPORT_SYMBOL(video_device_release); -MODULE_AUTHOR("Alan Cox"); -MODULE_DESCRIPTION("Device registrar for Video4Linux drivers"); +MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab "); +MODULE_DESCRIPTION("Device registrar for Video4Linux drivers v2"); MODULE_LICENSE("GPL");