videodev.c
来自「linux 内核源代码」· C语言 代码 · 共 1,829 行 · 第 1/3 页
C
1,829 行
/* * 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. * * 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. * * Authors: Alan Cox, <alan@redhat.com> (version 1) * Mauro Carvalho Chehab <mchehab@infradead.org> (version 2) * * Fixes: 20000516 Claudio Matsuoka <claudio@conectiva.com> * - 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 <linux/module.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/mm.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/kmod.h>#include <linux/slab.h>#include <asm/uaccess.h>#include <asm/system.h>#define __OLD_VIDIOC_ /* To allow fixing old calls*/#include <linux/videodev2.h>#ifdef CONFIG_VIDEO_V4L1#include <linux/videodev.h>#endif#include <media/v4l2-common.h>#define VIDEO_NUM_DEVICES 256#define VIDEO_NAME "video4linux"/* * sysfs stuff */static ssize_t show_name(struct device *cd, struct device_attribute *attr, char *buf){ struct video_device *vfd = container_of(cd, struct video_device, class_dev); return sprintf(buf, "%.*s\n", (int)sizeof(vfd->name), vfd->name);}struct video_device *video_device_alloc(void){ struct video_device *vfd; vfd = kzalloc(sizeof(*vfd),GFP_KERNEL); return vfd;}void video_device_release(struct video_device *vfd){ kfree(vfd);}static void video_release(struct device *cd){ struct video_device *vfd = container_of(cd, struct video_device, class_dev);#if 1 /* needed until all drivers are fixed */ if (!vfd->release) return;#endif vfd->release(vfd);}static struct device_attribute video_device_attrs[] = { __ATTR(name, S_IRUGO, show_name, NULL), __ATTR_NULL};static struct class video_class = { .name = VIDEO_NAME, .dev_attrs = video_device_attrs, .dev_release = video_release,};/* * Active devices */static struct video_device *video_device[VIDEO_NUM_DEVICES];static DEFINE_MUTEX(videodev_lock);struct video_device* video_devdata(struct file *file){ return video_device[iminor(file->f_path.dentry->d_inode)];}/* * 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; const struct file_operations *old_fops; if(minor>=VIDEO_NUM_DEVICES) return -ENODEV; mutex_lock(&videodev_lock); vfl=video_device[minor]; if(vfl==NULL) { mutex_unlock(&videodev_lock); request_module("char-major-%d-%d", VIDEO_MAJOR, minor); mutex_lock(&videodev_lock); vfl=video_device[minor]; if (vfl==NULL) { mutex_unlock(&videodev_lock); return -ENODEV; } } old_fops = file->f_op; file->f_op = fops_get(vfl->fops); if(file->f_op->open) err = file->f_op->open(inode,file); if (err) { fops_put(file->f_op); file->f_op = fops_get(old_fops); } fops_put(old_fops); mutex_unlock(&videodev_lock); return err;}/* * helper function -- handles userspace copying for ioctl arguments */#ifdef __OLD_VIDIOC_static unsigned intvideo_fix_command(unsigned int cmd){ switch (cmd) { case VIDIOC_OVERLAY_OLD: cmd = VIDIOC_OVERLAY; break; case VIDIOC_S_PARM_OLD: cmd = VIDIOC_S_PARM; break; case VIDIOC_S_CTRL_OLD: cmd = VIDIOC_S_CTRL; break; case VIDIOC_G_AUDIO_OLD: cmd = VIDIOC_G_AUDIO; break; case VIDIOC_G_AUDOUT_OLD: cmd = VIDIOC_G_AUDOUT; break; case VIDIOC_CROPCAP_OLD: cmd = VIDIOC_CROPCAP; break; } return cmd;}#endif/* * Obsolete usercopy function - Should be removed soon */intvideo_usercopy(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg, int (*func)(struct inode *inode, struct file *file, unsigned int cmd, void *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; } } /* 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)) { 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;}/* * 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; mutex_lock(&vfl->lock); if (vfl->users) { retval = -EBUSY; } else { vfl->users++; } mutex_unlock(&vfl->lock); return retval;}int video_exclusive_release(struct inode *inode, struct file *file){ struct video_device *vfl = video_devdata(file); vfl->users--; 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_VIDEO_OUTPUT_OVERLAY] = "video-out-over", [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", [V4L2_FIELD_INTERLACED_TB] = "interlaced-tb", [V4L2_FIELD_INTERLACED_BT] = "interlaced-bt",};#define prt_names(a,arr) (((a)>=0)&&((a)<ARRAY_SIZE(arr)))?arr[a]:"unknown"static void dbgbuf(unsigned int cmd, struct video_device *vfd, struct v4l2_buffer *p){ struct v4l2_timecode *tc=&p->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_VIDEO_OUTPUT_OVERLAY: if (vfd->vidioc_try_fmt_output_overlay) 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); }#ifdef CONFIG_VIDEO_V4L1_COMPAT /*********************************************************** Handles calls to the obsoleted V4L1 API Due to the nature of VIDIOCGMBUF, each driver that supports V4L1 should implement its own handler for this ioctl. ***********************************************************/ /* --- streaming capture ------------------------------------- */ if (cmd == VIDIOCGMBUF) { struct video_mbuf *p=arg; memset(p, 0, sizeof(*p)); if (!vfd->vidiocgmbuf) return ret; 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); return ret; } /******************************************************** All other V4L1 calls are handled by v4l1_compat module. Those calls will be translated into V4L2 calls, and __video_do_ioctl will be called again, with one or more V4L2 ioctls. ********************************************************/ if (_IOC_TYPE(cmd)=='v') return v4l_compat_translate_ioctl(inode,file,cmd,arg, __video_do_ioctl);#endif 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_VIDEO_OUTPUT_OVERLAY: if (vfd->vidioc_enum_fmt_output_overlay) ret=vfd->vidioc_enum_fmt_output_overlay(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)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?