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 + -
显示快捷键?