(五):v4l2: vivi的分析

/drivers/media/platform/vivi.c

module_init(vivi_init);

module_exit(vivi_exit);

 

static int __init vivi_init(void)

            ret = vivi_create_instance(i);

 

struct vivi_dev {   // 这个结构体还是非常的重要的
	struct list_head           vivi_devlist;
	struct v4l2_device 	   v4l2_dev;
	struct v4l2_ctrl_handler   ctrl_handler;
	struct video_device	   vdev;

	/* controls */
	struct v4l2_ctrl	   *brightness;
	struct v4l2_ctrl	   *contrast;
	struct v4l2_ctrl	   *saturation;
	struct v4l2_ctrl	   *hue;
	struct {
		/* autogain/gain cluster */
		struct v4l2_ctrl	   *autogain;
		struct v4l2_ctrl	   *gain;
	};
	struct v4l2_ctrl	   *volume;
	struct v4l2_ctrl	   *alpha;
	struct v4l2_ctrl	   *button;
	struct v4l2_ctrl	   *boolean;
	struct v4l2_ctrl	   *int32;
	struct v4l2_ctrl	   *int64;
	struct v4l2_ctrl	   *menu;
	struct v4l2_ctrl	   *string;
	struct v4l2_ctrl	   *bitmask;
	struct v4l2_ctrl	   *int_menu;

	spinlock_t                 slock;
	struct mutex		   mutex;

	struct vivi_dmaqueue       vidq;

	/* Several counters */
	unsigned 		   ms;
	unsigned long              jiffies;
	unsigned		   button_pressed;

	int			   mv_count;	/* Controls bars movement */

	/* Input Number */
	int			   input;

	/* video capture */
	const struct vivi_fmt      *fmt;
	struct v4l2_fract          timeperframe;
	unsigned int               width, height;
	struct vb2_queue	   vb_vidq;
	unsigned int		   field_count;

	u8			   bars[9][3];
	u8			   line[MAX_WIDTH * 8] __attribute__((__aligned__(4)));
	unsigned int		   pixelsize;
	u8			   alpha_component;
	u32			   textfg, textbg;
};
static int __init vivi_create_instance(int inst):
    struct vivi_dev *dev;
	struct video_device *vfd;
	struct v4l2_ctrl_handler *hdl;
	struct vb2_queue *q;
	int ret;

    dev = kzalloc(sizeof(*dev), GFP_KERNEL);

    snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
			"%s-%03d", VIVI_MODULE_NAME, inst);

    
    ret = v4l2_device_register(NULL, &dev->v4l2_dev);

    // 下面就是填充这个 struct vivi_dev *dev; 结构体
    dev->fmt = &formats[0];  //  格式
            static const struct vivi_fmt formats[] = {
	                                            {
		                                        .name     = "4:2:2, packed, YUYV",
		                                        .fourcc   = V4L2_PIX_FMT_YUYV,
		                                        .depth    = 16,
		                                        .is_yuv   = true,
	                                            },
    dev->timeperframe = tpf_default;  //
            tpf_default = {.numerator = 1001,	.denominator = 30000};	/* NTSC */

    dev->width = 640;
	dev->height = 480;
	dev->pixelsize = dev->fmt->depth / 8;

    hdl = &dev->ctrl_handler; // 统一指针

    v4l2_ctrl_handler_init(hdl, 11);   // 初始化v4l2_ctrl_handler 

    dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
			V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
	dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
			V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
	dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
			V4L2_CID_CONTRAST, 0, 255, 1, 16);
	dev->saturation = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
			V4L2_CID_SATURATION, 0, 255, 1, 127);
	dev->hue = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
			V4L2_CID_HUE, -128, 127, 1, 0);
	dev->autogain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
	dev->gain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
			V4L2_CID_GAIN, 0, 255, 1, 100);
	dev->alpha = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
			V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0);

    。。。。

     dev->v4l2_dev.ctrl_handler = hdl;
    
    /* initialize locks */
	spin_lock_init(&dev->slock);


    
    /* initialize queue */  //初始化 缓冲队列
	q = &dev->vb_vidq;
	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
	q->drv_priv = dev;
	q->buf_struct_size = sizeof(struct vivi_buffer);
	q->ops = &vivi_video_qops;
	q->mem_ops = &vb2_vmalloc_memops;
	q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;

    ret = vb2_queue_init(q);


   // 初始化 struct video_device *vfd;
    vfd = &dev->vdev;
    *vfd = vivi_template;  //重点
            
            static const struct video_device vivi_template = {
	                                .name		= "vivi",
	                                .fops           = &vivi_fops,
	                                .ioctl_ops 	= &vivi_ioctl_ops,
	                                .release	= video_device_release_empty,
                };

    
            static const struct v4l2_file_operations vivi_fops = {
	                                    .owner		= THIS_MODULE,
		                                .open           = v4l2_fh_open,
		                                .release        = vb2_fop_release,
		                                .read           = vb2_fop_read,
		                                .poll		= vb2_fop_poll,
		                                .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
		                                .mmap           = vb2_fop_mmap,
	                                };

	         static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
		                                .vidioc_querycap      = vidioc_querycap,
		                                .vidioc_enum_fmt_vid_cap  = vidioc_enum_fmt_vid_cap,
		                                .vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,
		                                .vidioc_try_fmt_vid_cap   = vidioc_try_fmt_vid_cap,
		                                .vidioc_s_fmt_vid_cap     = vidioc_s_fmt_vid_cap,
		                                .vidioc_enum_framesizes   = vidioc_enum_framesizes,
		                                .vidioc_reqbufs       = vb2_ioctl_reqbufs,
		                                .vidioc_create_bufs   = vb2_ioctl_create_bufs,
		                                .vidioc_prepare_buf   = vb2_ioctl_prepare_buf,
		                                .vidioc_querybuf      = vb2_ioctl_querybuf,
		                                .vidioc_qbuf          = vb2_ioctl_qbuf,
		                                .vidioc_dqbuf         = vb2_ioctl_dqbuf,
		                                .vidioc_enum_input    = vidioc_enum_input,
		                                .vidioc_g_input       = vidioc_g_input,
		                                .vidioc_s_input       = vidioc_s_input,
		                                .vidioc_enum_frameintervals = vidioc_enum_frameintervals,
		                                .vidioc_g_parm        = vidioc_g_parm,
		                                .vidioc_s_parm        = vidioc_s_parm,
		                                .vidioc_streamon      = vb2_ioctl_streamon,
		                                .vidioc_streamoff     = vb2_ioctl_streamoff,
		                                .vidioc_log_status    = v4l2_ctrl_log_status,
		                                .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
		                                .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
	                                };

    
    vfd->debug = debug;
	vfd->v4l2_dev = &dev->v4l2_dev;  //重点
	vfd->queue = q;// 重点
	set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);

    vfd->lock = &dev->mutex;
	video_set_drvdata(vfd, dev);

    ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr); //重点
            int __video_register_device(struct video_device *vdev, int type, int nr,...
                        vdev->cdev = cdev_alloc();
                        vdev->cdev->ops = &v4l2_fops; // 初始话字符设备
                                static const struct file_operations v4l2_fops = {
	                                                    .owner = THIS_MODULE,
		                                                .read = v4l2_read,
		                                                .write = v4l2_write,
		                                                .open = v4l2_open,
		                                                .get_unmapped_area = v4l2_get_unmapped_area,
		                                                .mmap = v4l2_mmap,
		                                                .unlocked_ioctl = v4l2_ioctl,
	                                                #ifdef CONFIG_COMPAT
		                                                .compat_ioctl = v4l2_compat_ioctl32,
	                                                #endif
		                                                .release = v4l2_release,
		                                                .poll = v4l2_poll,
		                                                .llseek = no_llseek,
	                                                };
                        ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
                        
                        /* Part 4: register the device with sysfs */
	                    vdev->dev.class = &video_class;
	                    vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
                        dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
	                    ret = device_register(&vdev->dev);

                        vdev->dev.release = v4l2_device_release;

                        video_device[vdev->minor] = vdev;  // 为了后面方便获得vdev
                        
    list_add_tail(&dev->vivi_devlist, &vivi_devlist);
    v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
		  video_device_node_name(vfd));
	return 0;

以上就初始化完成了摄像头的 字符设备。下来就是看应用层如何调用底层的 


app:     open("/dev/video0",....)

字符设备是哪里注册的,去哪里找fops
vdev->cdev->ops = &v4l2_fops;
        v4l2_fops.open = v4l2_open,

static int v4l2_open(struct inode *inode, struct file *filp)
    vdev = video_devdata(filp);
            return video_device[iminor(file_inode(file))];  //全局变量  在注册字符设备的时候
                    static inline struct inode *file_inode(struct file *f)
                    {
	                        return f->f_inode;
                    }
     video_get(vdev);    

    if (vdev->fops->open) {
		if (video_is_registered(vdev))
			ret = vdev->fops->open(filp);  // 最终调用的是video_device结构体的open函数
		else
			ret = -ENODEV;
	}               


在vivi初始化的时候
*vfd = vivi_template;
    static const struct video_device vivi_template = {
            .fops           = &vivi_fops,
                    static const struct v4l2_file_operations vivi_fops = {
                            .open           = v4l2_fh_open,

int v4l2_fh_open(struct file *filp)
       	v4l2_fh_init(fh, vdev);
	    v4l2_fh_add(fh);

  主要是就是初始化了 struct v4l2_fh *fh

struct v4l2_fh {
	struct list_head	list;
	struct video_device	*vdev;
	struct v4l2_ctrl_handler *ctrl_handler;
	enum v4l2_priority	prio;

	/* Events */
	wait_queue_head_t	wait;
	struct list_head	subscribed; /* Subscribed events */
	struct list_head	available; /* Dequeueable event */
	unsigned int		navailable;
	u32			sequence;
};
app: read 

也是找字符设备注册的fops
    v4l2_fops.read = v4l2_read,
        struct video_device *vdev = video_devdata(filp);
        ret = vdev->fops->read(filp, buf, sz, off);

也是最终调用video_device 中的read
    vivi_template.fops           = &vivi_fops,
            .read           = vb2_fop_read,

ssize_t vb2_fop_read(struct file *file, char __user *buf,
		size_t count, loff_t *ppos)
    if (vb2_queue_is_busy(vdev, file))
		goto exit;
    err = vb2_read(vdev->queue, buf, count, ppos,
		       file->f_flags & O_NONBLOCK);

    // 读的就是 缓冲区的中内容

size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
		loff_t *ppos, int nonblocking)
{
	return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1);
}
EXPORT_SYMBOL_GPL(vb2_read);

size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count,
		loff_t *ppos, int nonblocking)
{
	return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 0);
}
EXPORT_SYMBOL_GPL(vb2_write);


app: ioctl ...
     v4l2_fops.unlocked_ioctl = v4l2_ioctl,
                struct video_device *vdev = video_devdata(filp);
                ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);

 vivi_template.fops           = &vivi_fops
        .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */

long video_ioctl2(struct file *file,
	       unsigned int cmd, unsigned long arg)
{
	return video_usercopy(file, cmd, arg, __video_do_ioctl);
            /* Handles IOCTL */
	         err = func(file, cmd, parg);
}

//看函数之后前  先看一个结构体
struct v4l2_ioctl_info {
	unsigned int ioctl;
	u32 flags;
	const char * const name;
	union {
		u32 offset;
		int (*func)(const struct v4l2_ioctl_ops *ops,
				struct file *file, void *fh, void *p);
	} u;
	void (*debug)(const void *arg, bool write_only);
};


 static long __video_do_ioctl(struct file *file, unsigned int cmd, void *arg) :
	struct video_device *vfd = video_devdata(file);
	const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;   //重点
	bool write_only = false;
	struct v4l2_ioctl_info default_info;
	const struct v4l2_ioctl_info *info;
	void *fh = file->private_data;  //重点
	struct v4l2_fh *vfh = NULL;
	int use_fh_prio = 0;
	int debug = vfd->debug;  
	long ret = -ENOTTY;

    if (v4l2_is_known_ioctl(cmd)) {    //return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd; //最下面有这个全局变量
        info = &v4l2_ioctls[_IOC_NR(cmd)];  //  info保存了这个地址
     // 如不在这个全局变量里面的话
    } else {
		    default_info.ioctl = cmd;
		    default_info.flags = 0;
		    default_info.debug = v4l_print_default;
		    info = &default_info;
	}

    write_only = _IOC_DIR(cmd) == _IOC_WRITE;
    
    if (info->flags & INFO_FL_STD) {    //  如果是IOCTL_INFO_STD开头的
        typedef int (*vidioc_op)(struct file *file, void *fh, void *p);
		const void *p = vfd->ioctl_ops;  //  p取ioctl_ops的基地址
		const vidioc_op *vidioc = p + info->u.offset;  // + 在(v4l2_ioctl_ops)中函数的偏移量,就得到了这个函数的地址
        ret = (*vidioc)(file, fh, arg);  // 重点
    } else if (info->flags & INFO_FL_FUNC) {
		ret = info->u.func(ops, file, fh, arg);
    。。。。
    } else {
		ret = ops->vidioc_default(file, fh,
			use_fh_prio ? v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0,
			cmd, arg);
	}



 ===============================================================================     

#define IOCTL_INFO_STD(_ioctl, _vidioc, _debug, _flags)			\
	[_IOC_NR(_ioctl)] = {						\
		.ioctl = _ioctl,					\
		.flags = _flags | INFO_FL_STD,				\
		.name = #_ioctl,					\
		.u.offset = offsetof(struct v4l2_ioctl_ops, _vidioc),	\
		.debug = _debug,					\
	}

#define IOCTL_INFO_FNC(_ioctl, _func, _debug, _flags)			\
	[_IOC_NR(_ioctl)] = {						\
		.ioctl = _ioctl,					\
		.flags = _flags | INFO_FL_FUNC,				\
		.name = #_ioctl,					\
		.u.func = _func,					\
		.debug = _debug,					\
	}

  
static struct v4l2_ioctl_info v4l2_ioctls[] = {
	IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0),
	IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)),
	IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, INFO_FL_CLEAR(v4l2_format, type)),
	IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO),
	IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE),
	IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)),
	IOCTL_INFO_STD(VIDIOC_G_FBUF, vidioc_g_fbuf, v4l_print_framebuffer, 0),        

到这里基本就完成了 应用层到底层的调用关系。

猜你喜欢

转载自blog.csdn.net/zmjames2000/article/details/88578626