虚拟视频驱动程序vivi.c源码分析[转]
来源:互联网 发布:雀巢中国总部 知乎 编辑:程序博客网 时间:2024/06/06 00:47
原文地址:
http://blog.chinaunix.net/uid-26765074-id-3546104.html
虚拟视频驱动程序vivi.c源码分析
以下先把上一篇文章中的最后一段,放在这里利于程序源码的分析:
vivi.c 虚拟视频驱动程序----- 此代码模拟一个真正的视频设备V4L2 API (位于drivers/media/video目录下)
入口:+int __init vivi_init(void)
+ vivi_create_instance(i) /*创建设备*//**/。
+ 分配一个vivi_dev的结构体 /*它嵌套这结构体v4l2_device 和video_device*/
+ v4l2_device_register(NULL, &dev->v4l2_dev);/*注册vivi_dev中的V4l2_device*/
+ 初始化视频的DMA队列
+ 初始化锁
+ video_device_alloc(); 动态分配video_device结构体
+ 构建一个video_device结构体 vivi_template 并赋给上面分配的video_device
static struct video_device vivi_template = {
.name = "vivi",
.fops = &vivi_fops,
.ioctl_ops = &vivi_ioctl_ops,
.minor = -1,
.release = video_device_release,
.tvnorms = V4L2_STD_525_60,
.current_norm = V4L2_STD_NTSC_M,
};
+ video_set_drvdata(vfd, dev);设置驱动程序专有数据
+ 所有控件设置为其默认值
+ list_add_tail(&dev->vivi_devlist, &vivi_devlist);添加到设备列表
+ 构建 v4l2_file_operations 结构体vivi_fops 并实现.open .release .read .poll .mmap函数----- .ioctl 用标准的v4l2控制处理程序
+ 构建 v4l2_ioctl_ops结构体 vivi_ioctl_ops
static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
.vidioc_s_std = vidioc_s_std,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
#endif
};
+ int vivi_open(struct file *file)
+ vivi_dev *dev = video_drvdata(file); 访问驱动程序专用数据
+ 分配+初始化句柄(vivi_fh)数据
+ 重置帧计数器
+ videobuf_queue_vmalloc_init(); 初始化视频缓冲队列
+ 开启一个新线程用于开始和暂停
+ 实现自定义的v4l2_ioctl_ops 函数
现在开始分析程序源码,利于之后对V4L2驱动的开发,学习
首先就行驱动的入口开始:
- static int __init vivi_init(void)
- {
- const struct font_desc *font = find_font("VGA8x16");
- int ret = 0, i;
- if (font== NULL) {
- printk(KERN_ERR "vivi: could not find font\n");
- return -ENODEV;
- }
- font8x16 = font->data;
- if (n_devs<= 0)
- n_devs = 1;
- for (i = 0; i < n_devs; i++){
- //Hereis the most important
- ret = vivi_create_instance(i);
- if (ret){
- /*If some instantiations succeeded, keep driver*/
- if (i)
- ret = 0;
- break;
- }
- }
- if (ret< 0) {
- printk(KERN_ERR "vivi: error %d while loading driver\n", ret);
- return ret;
- }
- printk(KERN_INFO "Video Technology Magazine Virtual Video "
- "Capture Board ver %u.%u.%u successfully loaded.\n",
- (VIVI_VERSION >> 16) & 0xFF, (VIVI_VERSION>> 8)& 0xFF,
- VIVI_VERSION & 0xFF);
- /* n_devs will reflect the actual number of allocated devices*/
- n_devs = i;
- return ret;
- }
- static void __exit vivi_exit(void)
- {
- vivi_release();
- }
- module_init(vivi_init);
- module_exit(vivi_exit);
- 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);
- if (!dev)
- return -ENOMEM;
- // set the v4l2_device(the name)
- snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
- "%s-%03d", VIVI_MODULE_NAME, inst);
-
- /*
- * register the v4l2_device, but you should pay attention here
- * the "dev == NULL" it means v4l2_device.dev== NULL
- * You did'tset the v4l2_device.dev, you willset it later
- */
- ret = v4l2_device_register(NULL,&dev->v4l2_dev);
- if (ret)
- goto free_dev;
- /* init the handle, learn it later*/
- dev->fmt= &formats[0];
- dev->width= 640;
- dev->height= 480;
- hdl= &dev->ctrl_handler;
- v4l2_ctrl_handler_init(hdl, 11);
- 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->button= v4l2_ctrl_new_custom(hdl,&vivi_ctrl_button,NULL);
- dev->int32= v4l2_ctrl_new_custom(hdl,&vivi_ctrl_int32,NULL);
- dev->int64= v4l2_ctrl_new_custom(hdl,&vivi_ctrl_int64,NULL);
- dev->boolean= v4l2_ctrl_new_custom(hdl,&vivi_ctrl_boolean,NULL);
- dev->menu= v4l2_ctrl_new_custom(hdl,&vivi_ctrl_menu,NULL);
- dev->string= v4l2_ctrl_new_custom(hdl,&vivi_ctrl_string,NULL);
- if (hdl->error){
- ret = hdl->error;
- goto unreg_dev;
- }
- dev->v4l2_dev.ctrl_handler= hdl;
- /* initialize locks*/
- spin_lock_init(&dev->slock);
- /* initialize queue, learn it later*/
- q = &dev->vb_vidq;
- memset(q, 0, sizeof(dev->vb_vidq));
- q->type= V4L2_BUF_TYPE_VIDEO_CAPTURE;
- q->io_modes= VB2_MMAP | VB2_USERPTR| 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;
- vb2_queue_init(q);
- mutex_init(&dev->mutex);
- /* init video dma queues*/
- INIT_LIST_HEAD(&dev->vidq.active);
- init_waitqueue_head(&dev->vidq.wq);
- /* before register the video_device, init the video_device data*/
- ret = -ENOMEM;
- vfd = video_device_alloc();
- if (!vfd)
- goto unreg_dev;
- *vfd = vivi_template;/* the most important struct*/
- vfd->debug= debug;
- vfd->v4l2_dev= &dev->v4l2_dev;/* here set the v4l2_device, you have already registered it*/
- set_bit(V4L2_FL_USE_FH_PRIO,&vfd->flags);
- /*
- * Provide a mutex to v4l2 core. It will be used to protect
- * all fops and v4l2 ioctls.
- */
- vfd->lock= &dev->mutex;
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
- if (ret< 0)
- goto rel_vdev;
- /*
- * You should pay attention to this method
- * here you set the vivi_dev into the vedio_devicefor the later use in fops
- * When you want to use the vivi_dev, you use vedio_get_drvdata()to get
- */
- video_set_drvdata(vfd, dev);
- /* Now that everything is fine,let's add it to device list */
- list_add_tail(&dev->vivi_devlist,&vivi_devlist);
- if (video_nr!= -1)
- video_nr++;
- dev->vfd= vfd;
- /* the debug message*/
- v4l2_info(&dev->v4l2_dev,"V4L2 device registered as %s\n",
- video_device_node_name(vfd));
- return 0;
- rel_vdev:
- video_device_release(vfd);
- unreg_dev:
- v4l2_ctrl_handler_free(hdl);
- v4l2_device_unregister(&dev->v4l2_dev);
- free_dev:
- kfree(dev);
- return ret;
- }
1.首先通过v4l2_device_register() 方法注册 v4l2_device
2.ctrl_handler初始化
3.互斥锁,自旋锁等初始化
4.vb2_quene初始化
5.init video dma queues
6.填充video_device,并且调用video_register_device注册video_device
7.把vivi_dev结构set进video_device中,方便之后使用,使用video_set_drvdata(vfd, dev);
8.设备信息加入的链表结构
下面针对以上步骤做详细分析:
1.首先通过v4l2_device_register() 方法注册 v4l2_device
- int v4l2_device_register(struct device*dev, struct v4l2_device*v4l2_dev)
- {
- if (v4l2_dev== NULL)
- return -EINVAL;
- INIT_LIST_HEAD(&v4l2_dev->subdevs);
- spin_lock_init(&v4l2_dev->lock);
- mutex_init(&v4l2_dev->ioctl_lock);
- /* initial the global priotity*/
- v4l2_prio_init(&v4l2_dev->prio);
- kref_init(&v4l2_dev->ref);
- v4l2_dev->dev= dev;
- if (dev== NULL) {
- /* If dev ==NULL, then name must be filled in by the caller*/
- WARN_ON(!v4l2_dev->name[0]);
- /* Here give the caller a WARN, tell the callerto set the dev*/
- return 0;
- }
- /* Set name to driver name + device name if it is empty.*/
- if (!v4l2_dev->name[0])
- snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),"%s %s",
- dev->driver->name, dev_name(dev));
- /* Hereis also very important, you canget v4l2_device by use dev_get_drvdata*/
- if (!dev_get_drvdata(dev))
- dev_set_drvdata(dev, v4l2_dev);
- return 0;
- }
- EXPORT_SYMBOL_GPL(v4l2_device_register);
另外这里说一下kref结构定义的ref变量,这个变量保存打开设备的计数,这里第一次注册设备,初始化ref为1,若之后重复注册则会先检查ref这个变量,
如果ref为1,则表示已经注册过了,避免重复注册v4l2_device,这个初始化在kref_init方法中实现,这是我的理解,可是我暂时还没有找到检查ref的地方,暂且跳过
最后一步根据dev 结构体决定,如果dev不为空,则这里setv4l2_device的name,并且将v4l2_device结构set进dev中,方便后面获取使用
2.ctrl_handler初始化
- /* Initialize the handler*/
- int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler*hdl,
- unsigned nr_of_controls_hint)
- {
- mutex_init(&hdl->lock);
- INIT_LIST_HEAD(&hdl->ctrls);
- INIT_LIST_HEAD(&hdl->ctrl_refs);
- hdl->nr_of_buckets= 1 + nr_of_controls_hint/ 8;
- hdl->buckets= kzalloc(sizeof(hdl->buckets[0])* hdl->nr_of_buckets,
- GFP_KERNEL);
- hdl->error= hdl->buckets? 0 : -ENOMEM;
- return hdl->error;
- }
- /** struct v4l2_ctrl_handler- The control handler keeps track of all the
- * controls: both the controls owned by the handlerand those inherited
- * from other handlers.
- * @lock: Lockto control access to this handlerand its controls.
- * @ctrls: The list of controls owned by this handler.
- * @ctrl_refs: The list of control references.
- * @cached: The last found control reference. Itis common that the same
- * control is needed multiple times, so this is a simple
- * optimization.
- * @buckets: Bucketsfor the hashing. Allowsfor quick control lookup.
- * @nr_of_buckets: Total number of bucketsin the array.
- * @error: Theerror code of the first failed control addition.
- */
- struct v4l2_ctrl_handler {
- struct mutex lock;
- struct list_head ctrls;
- struct list_head ctrl_refs;
- struct v4l2_ctrl_ref *cached;
- struct v4l2_ctrl_ref **buckets;
- u16 nr_of_buckets;
- int error;
- };
dev->v4l2_dev.ctrl_handler = hdl;最后,关联vivi_dev
问题点:
1.v4l2_ctrl_new_std
2.v4l2_ctrl_new_custom
以上两个方法不是很理解,待以后研究
3.互斥锁,自旋锁等初始化
这个比较简单,就不在做说明了
4.vb2_quene初始化
首先还是很有必要看一下这个结构体
- /**
- * struct vb2_queue - a videobuf queue
- *
- * @type: queue type(see V4L2_BUF_TYPE_*in linux/videodev2.h
- * @io_modes: supported io methods(see vb2_io_modes enum)
- * @io_flags: additional io flags(see vb2_fileio_flags enum)
- * @ops: driver-specific callbacks
- * @mem_ops: memory allocator specific callbacks
- * @drv_priv: driverprivate data
- * @buf_struct_size: size of the driver-specific buffer structure;
- * "0" indicates the driver doesn't wantto use a custom buffer
- * structure type, so sizeof(struct vb2_buffer) willis used
- *
- * @memory: current memory type used
- * @bufs: videobuf buffer structures
- * @num_buffers: number of allocated/used buffers
- * @queued_list: list of buffers currently queued from userspace
- * @queued_count: number of buffers owned by the driver
- * @done_list: list of buffers readyto be dequeued to userspace
- * @done_lock: lockto protect done_list list
- * @done_wq: waitqueuefor processes waiting for buffers ready to be dequeued
- * @alloc_ctx: memory type/allocator-specific contextsfor each plane
- * @streaming: current streaming state
- * @fileio: file io emulator internal data, used onlyif emulator is active
- */
- struct vb2_queue {
- enum v4l2_buf_type type;
- unsigned int io_modes;
- unsigned int io_flags;
- const struct vb2_ops *ops;
- const struct vb2_mem_ops *mem_ops;
- void *drv_priv;
- unsigned int buf_struct_size;
- /* private: internal use only */
- enum v4l2_memory memory;
- struct vb2_buffer *bufs[VIDEO_MAX_FRAME];
- unsigned int num_buffers;
- struct list_head queued_list;
- atomic_t queued_count;
- struct list_head done_list;
- spinlock_t done_lock;
- wait_queue_head_t done_wq;
- void *alloc_ctx[VIDEO_MAX_PLANES];
- unsigned int streaming:1;
- struct vb2_fileio_data *fileio;
- };
q->ops = &vivi_video_qops;
q->mem_ops = &vb2_vmalloc_memops;
这两条简单的复制语句责任重大啊,这里先知道有这么一回事,后面补充
- /**
- * struct vb2_ops - driver-specific callbacks
- *
- * @queue_setup: called from a VIDIOC_REQBUFS handler, before
- * memory allocation; driver should return the required
- * number of buffers in num_buffers, the required number
- * of planes per buffer in num_planes; the size of each
- * plane should be set in the sizes[]array and optional
- * per-plane allocator specific contextin alloc_ctxs[]
- * array
- * @wait_prepare: release any locks takenwhile calling vb2 functions;
- * it is called before an ioctl needsto wait for a new
- * buffer to arrive; required to avoid a deadlock in
- * blocking access type
- * @wait_finish: reacquire all locks releasedin the previous callback;
- * required to continue operation after sleeping while
- * waiting for a new buffer to arrive
- * @buf_init: called once after allocating a buffer(in MMAP case)
- * or after acquiring a new USERPTR buffer; drivers may
- * perform additional buffer-related initialization;
- * initialization failure (return != 0) will prevent
- * queue setup from completing successfully; optional
- * @buf_prepare: called everytime the buffer is queued from userspace;
- * drivers may perform any initialization required before
- * each hardware operationin this callback;
- * if anerror is returned, the buffer willnot be queued
- * in driver; optional
- * @buf_finish: called before every dequeue of the buffer backto
- * userspace; drivers may perform any operations required
- * before userspace accesses the buffer; optional
- * @buf_cleanup: called once before the bufferis freed; drivers may
- * perform any additional cleanup; optional
- * @start_streaming: called once before entering'streaming' state; enables
- * driver to receive buffers over buf_queue() callback
- * @stop_streaming: called when'streaming' state must be disabled; driver
- * should stop any DMA transactionsor wait until they
- * finish and give back all buffers it got from buf_queue()
- * callback; may use vb2_wait_for_all_buffers()function
- * @buf_queue: passes buffer vbto the driver; driver may start
- * hardware operation on this buffer; driver should give
- * the buffer back by calling vb2_buffer_done()function
- */
- struct vb2_ops {
- int (*queue_setup)(struct vb2_queue*q, unsigned int *num_buffers,
- unsigned int *num_planes, unsigned long sizes[],
- void *alloc_ctxs[]);
- void (*wait_prepare)(struct vb2_queue*q);
- void (*wait_finish)(struct vb2_queue*q);
- int (*buf_init)(struct vb2_buffer*vb);
- int (*buf_prepare)(struct vb2_buffer*vb);
- int (*buf_finish)(struct vb2_buffer*vb);
- void (*buf_cleanup)(struct vb2_buffer*vb);
- int (*start_streaming)(struct vb2_queue*q);
- int (*stop_streaming)(struct vb2_queue*q);
- void (*buf_queue)(struct vb2_buffer*vb);
- };
- /**
- * struct vb2_mem_ops - memory handling/memory allocator operations
- * @alloc: allocate video memoryand, optionally, allocatorprivate data,
- * return NULLon failure or a pointerto allocator private,
- * per-buffer dataon success; the returnedprivate structure
- * will then be passed as buf_priv argumentto other ops in this
- * structure
- * @put: inform the allocator that the buffer will no longer be used;
- * usually will result in the allocator freeing the buffer (if
- * no other users of this buffer are present); the buf_priv
- * argument is the allocator private per-buffer structure
- * previously returned from the alloc callback
- * @get_userptr: acquire userspace memoryfor a hardware operation; usedfor
- * USERPTR memory types; vaddris the address passed to the
- * videobuf layer when queuing a video buffer of USERPTR type;
- * should return an allocator private per-buffer structure
- * associated with the buffer on success, NULLon failure;
- * the returned private structure will then be passed as buf_priv
- * argument to other ops in this structure
- * @put_userptr: inform the allocator that a USERPTR buffer will no longer
- * be used
- * @vaddr: return a kernel virtual addressto a given memory buffer
- * associated with the passed private structure or NULL if no
- * such mapping exists
- * @cookie: return allocator specific cookiefor a given memory buffer
- * associated with the passed private structure or NULL if not
- * available
- * @num_users: return the current number of users of a memory buffer;
- * return 1 if the videobuf layer (or actually the driver using
- * it)is the only user
- * @mmap: setup a userspace mappingfor a given memory buffer under
- * the provided virtual memory region
- *
- * Required ops for USERPTR types: get_userptr, put_userptr.
- * Required ops for MMAP types: alloc, put, num_users, mmap.
- * Required ops for read/write access types: alloc, put, num_users, vaddr
- */
- struct vb2_mem_ops {
- void *(*alloc)(void*alloc_ctx, unsigned long size);
- void (*put)(void*buf_priv);
- void *(*get_userptr)(void*alloc_ctx, unsigned long vaddr,
- unsigned long size, int write);
- void (*put_userptr)(void*buf_priv);
- void *(*vaddr)(void*buf_priv);
- void *(*cookie)(void*buf_priv);
- unsigned int (*num_users)(void*buf_priv);
- int (*mmap)(void*buf_priv, struct vm_area_struct*vma);
- };
- /**
- * vb2_queue_init()- initialize a videobuf2 queue
- * @q: videobuf2 queue; this structure should be allocatedin driver
- *
- * The vb2_queue structure should be allocated by the driver. The driveris
- * responsible of clearing it's contentand setting initial values for some
- * required entries before calling this function.
- * q->ops, q->mem_ops, q->type and q->io_modes are mandatory. Please refer
- * to the struct vb2_queue descriptionin include/media/videobuf2-core.h
- * for more information.
- */
- int vb2_queue_init(struct vb2_queue*q)
- {
- BUG_ON(!q);
- BUG_ON(!q->ops);
- BUG_ON(!q->mem_ops);
- BUG_ON(!q->type);
- BUG_ON(!q->io_modes);
- BUG_ON(!q->ops->queue_setup);
- BUG_ON(!q->ops->buf_queue);
- INIT_LIST_HEAD(&q->queued_list);
- INIT_LIST_HEAD(&q->done_list);
- spin_lock_init(&q->done_lock);
- init_waitqueue_head(&q->done_wq);
- if (q->buf_struct_size== 0)
- q->buf_struct_size= sizeof(struct vb2_buffer);
- return 0;
- }
- EXPORT_SYMBOL_GPL(vb2_queue_init);
5.init video dma queues
只有两条语句进行初始化
INIT_LIST_HEAD(&dev->vidq.active);
init_waitqueue_head(&dev->vidq.wq);
6.填充video_device,并且调用video_register_device注册video_device
这里终于到了重点了,很重要,开始了
开始简单,申请内存空间并进行填充,然后才真正调用用video_register_device这个方法,开始了
但是在调用之前的这条语句你必须关注vfd->v4l2_dev = &dev->v4l2_dev;从这里也可以知道v4l2_device和video_device的注册顺序
- int __video_register_device(struct video_device*vdev, int type, int nr,
- int warn_if_nr_in_use, struct module*owner)
- {
- int i = 0;
- int ret;
- int minor_offset = 0;
- int minor_cnt = VIDEO_NUM_DEVICES;
- const char *name_base;
- /* A minor value of-1 marks this video device as never
- having been registered */
- vdev->minor= -1;
- /* the release callback MUST be present*/
- WARN_ON(!vdev->release);
- if (!vdev->release)
- return -EINVAL;
- /* v4l2_fh support*/
- spin_lock_init(&vdev->fh_lock);
- INIT_LIST_HEAD(&vdev->fh_list);
- /* Part 1: check device type*/
- /* after here, you can see videx...the char devicein /dev */
- //这里还是单独说一下吧,最终你在/dev目录下看到的video0就是在这里决定的,大家可以知道,可不是一定名字叫video的
- switch (type){
- case VFL_TYPE_GRABBER:
- name_base = "video";
- break;
- case VFL_TYPE_VBI:
- name_base = "vbi";
- break;
- case VFL_TYPE_RADIO:
- name_base = "radio";
- break;
- case VFL_TYPE_SUBDEV:
- name_base = "v4l-subdev";
- break;
- default:
- printk(KERN_ERR "%s called with unknown type: %d\n",
- __func__, type);
- return -EINVAL;
- }
- vdev->vfl_type= type;
- vdev->cdev= NULL;
- if (vdev->v4l2_dev){
- if (vdev->v4l2_dev->dev)
- //这里说明vdev和保存在v4l2_device的dev具有共同的parent,对之后sys 接口那里有用
- vdev->parent= vdev->v4l2_dev->dev;
- if (vdev->ctrl_handler== NULL)
- //上面初始化的ctrl_handler在这里要派上用处了,而且同时指向video_device和v4l2_device的化ctrl_handler
- vdev->ctrl_handler= vdev->v4l2_dev->ctrl_handler;
- /* If the prio state pointer is NULL, then use the v4l2_device
- prio state. */
- if (vdev->prio== NULL)
- //上面初始化的prio在这里要派上用处了,而且同时指向video_device和v4l2_device的化prio
- vdev->prio= &vdev->v4l2_dev->prio;
- }
- /* Part 2: find a free minor, device node numberand device index.*/
- #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
- /* Keep the rangesfor the first four types for historical
- * reasons.
- * Newer devices (not yet in place) should use the range
- * of 128-191and just pick the first free minor there
- * (new style).*/
- switch (type){
- case VFL_TYPE_GRABBER:
- minor_offset = 0;
- minor_cnt = 64;
- break;
- case VFL_TYPE_RADIO:
- minor_offset = 64;
- minor_cnt = 64;
- break;
- case VFL_TYPE_VBI:
- minor_offset = 224;
- minor_cnt = 32;
- break;
- default:
- minor_offset = 128;
- minor_cnt = 64;
- break;
- }
- #endif
- /* Pick a device node number*/
- mutex_lock(&videodev_lock);
- nr = devnode_find(vdev, nr== -1 ? 0 : nr, minor_cnt);
- if (nr == minor_cnt)
- nr = devnode_find(vdev, 0, minor_cnt);
- if (nr == minor_cnt){
- printk(KERN_ERR "could not get a free device node number\n");
- mutex_unlock(&videodev_lock);
- return -ENFILE;
- }
- #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
- /* 1-on-1 mapping of device node numberto minor number */
- i = nr;
- #else
- /* The device node numberand minor numbers are independent, so
- we just find the first free minor number.*/
- for (i = 0; i < VIDEO_NUM_DEVICES; i++)
- if (video_device[i]== NULL)
- break;
- if (i == VIDEO_NUM_DEVICES){
- mutex_unlock(&videodev_lock);
- printk(KERN_ERR "could not get a free minor\n");
- return -ENFILE;
- }
- #endif
- vdev->minor= i + minor_offset;
- vdev->num= nr;
- devnode_set(vdev);
- /* Shouldnot happen since we thought this minor was free */
- WARN_ON(video_device[vdev->minor]!= NULL);
- vdev->index= get_index(vdev);
- mutex_unlock(&videodev_lock);
- /* Part 3: Initialize the character device*/
- vdev->cdev= cdev_alloc();
- if (vdev->cdev== NULL) {
- ret = -ENOMEM;
- goto cleanup;
- }
- vdev->cdev->ops= &v4l2_fops;//most important part,操作设备的通道
- vdev->cdev->owner= owner;
- ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
- if (ret< 0) {
- printk(KERN_ERR "%s: cdev_add failed\n", __func__);
- kfree(vdev->cdev);
- vdev->cdev= NULL;
- goto cleanup;
- }
- /* Part 4: register the device with sysfs*/
- vdev->dev.class= &video_class;
- vdev->dev.devt= MKDEV(VIDEO_MAJOR, vdev->minor);
- if (vdev->parent)
- vdev->dev.parent= vdev->parent;
- dev_set_name(&vdev->dev,"%s%d", name_base, vdev->num);
- ret = device_register(&vdev->dev);
- if (ret< 0) {
- printk(KERN_ERR "%s: device_register failed\n", __func__);
- goto cleanup;
- }
- /* Register the release callback that will be called when the last
- reference to the device goes away.*/
- vdev->dev.release= v4l2_device_release;
- if (nr != -1 && nr != vdev->num&& warn_if_nr_in_use)
- printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,
- name_base, nr, video_device_node_name(vdev));
- /* Increase v4l2_device refcount*/
- if (vdev->v4l2_dev)
- v4l2_device_get(vdev->v4l2_dev);
- #if defined(CONFIG_MEDIA_CONTROLLER)
//意思就是如果这个驱动中需要用到media controler的时候就需要在这里注册media_device
//这里同样先不做深入研究,media_device和media_entity这两个重要结构体之后还要研究
- /* Part 5: Register the entity.*/
- if (vdev->v4l2_dev&& vdev->v4l2_dev->mdev&&
- vdev->vfl_type!= VFL_TYPE_SUBDEV){
- vdev->entity.type= MEDIA_ENT_T_DEVNODE_V4L;
- vdev->entity.name= vdev->name;
- vdev->entity.v4l.major= VIDEO_MAJOR;
- vdev->entity.v4l.minor= vdev->minor;
- ret = media_device_register_entity(vdev->v4l2_dev->mdev,
- &vdev->entity);
- if (ret< 0)
- printk(KERN_WARNING
- "%s: media_device_register_entity failed\n",
- __func__);
- }
- #endif
- //保存注册成功标记,并将注册成功的video_device保存到全局数组video_device中,大功告成
- /* Part 6: Activate this minor. The char device cannow be used. */
- set_bit(V4L2_FL_REGISTERED,&vdev->flags);//设置标志位,之后还会遇到test_bit方法用来check flags的第nr位是否为1.
- //这里还是多说一点,另外还有两中标志位需要知道:V4L2_FL_USES_V4L2_FH, V4L2_FL_USE_FH_PRIO
- mutex_lock(&videodev_lock);
- video_device[vdev->minor]= vdev;
- mutex_unlock(&videodev_lock);
- return 0;
- cleanup:
- mutex_lock(&videodev_lock);
- if (vdev->cdev)
- cdev_del(vdev->cdev);
- devnode_clear(vdev);
- mutex_unlock(&videodev_lock);
- /* Mark this video device as never having been registered.*/
- vdev->minor= -1;
- return ret;
- }
- EXPORT_SYMBOL(__video_register_device);
7.把vivi_dev结构set进video_device中,方便之后使用,设备信息加入的链表结构
最后结尾的这段代码这里我决定单独放在下面分析,也算妥善收尾吧
- /*
- * You should pay attention to this method
- * here you set the vivi_dev into the vedio_devicefor the later use in fops
- * When you want to use the vivi_dev, you use vedio_get_drvdata()to get
- */
- video_set_drvdata(vfd, dev);
- /* Now that everything is fine,let's add it to device list */
- list_add_tail(&dev->vivi_devlist,&vivi_devlist);//添加的device list当中
- if (video_nr!= -1)
- video_nr++;//用于计数,找到设备
- dev->vfd= vfd;关联video_device和vivi_dev
首先说说这个方法,他有什么用处呢?其实用处可大了,就行我上面注释的那样,这里把vivi_dev设置到vedio_device中,是为了之后字符设备访问接口中使用
这里多说一点,也算顺便说一下用户空间操作设备的流程了
首先当时用户空间访问设备了,这个做驱动的不懂那可糗大了,用户空间open时,也就是启动了上面video_device_register方法中的很重要的下面结构中的open方法
- 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,
- };
- /* Overridefor the open function*/
- static int v4l2_open(struct inode*inode, struct file*filp)
- {
- struct video_device *vdev;
- int ret = 0;
- /* Checkif the video device is available*/
- mutex_lock(&videodev_lock);
- vdev = video_devdata(filp);
- /* return ENODEVif the video device has already been removed.*/
- if (vdev== NULL || !video_is_registered(vdev)){
- mutex_unlock(&videodev_lock);
- return -ENODEV;
- }
- /* and increase the device refcount */
- video_get(vdev);//这里是用来计数的
- mutex_unlock(&videodev_lock);
- /*
- * Here using the API you get the method you get the open() method write
- * The other methods in fops use the same method to use you own code
- */
- if (vdev->fops->open){
- if (vdev->lock&& mutex_lock_interruptible(vdev->lock)){
- ret = -ERESTARTSYS;
- goto err;
- }
- if (video_is_registered(vdev))
- ret = vdev->fops->open(filp);
- else
- ret = -ENODEV;
- if (vdev->lock)
- mutex_unlock(vdev->lock);
- }
- err:
- /* decrease the refcountin case of an error */
- if (ret)
- video_put(vdev);
- return ret;
- }
这个open方法在哪里呢?那就沿着箭头方向找吧,是video_device内部的fops中的open方法,这个方法不是有在哪里呢?
接着找,在video_device_register方法之前
*vfd = vivi_template;/* the most important struct */我还特意在这里写了最重要的结构体
所以最终调用的是vivi_template中fops中定义的open方法
- static const struct v4l2_file_operations vivi_fops= {
- .owner = THIS_MODULE,
- .open = v4l2_fh_open,
- .release = vivi_close,
- .read = vivi_read,
- .poll = vivi_poll,
- .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
- .mmap = vivi_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_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_s_std = vidioc_s_std,
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- };
- static struct video_device vivi_template = {
- .name ="vivi",
- .fops= &vivi_fops,
- .ioctl_ops = &vivi_ioctl_ops,
- .release = video_device_release,
- .tvnorms = V4L2_STD_525_60,
- .current_norm = V4L2_STD_NTSC_M,
- };
- int v4l2_fh_open(struct file*filp)
- {
- struct video_device *vdev = video_devdata(filp);
- struct v4l2_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- /*
- * IN the open method,do only one job
- * set v4l2_fh into filp->private_datafor later use, and initial v4l2_fh
- */
- filp->private_data= fh;
- if (fh == NULL)
- return -ENOMEM;
- v4l2_fh_init(fh, vdev);
- v4l2_fh_add(fh);
- return 0;
- }
- EXPORT_SYMBOL_GPL(v4l2_fh_open);
这里fops中的其他接口的实现比起open,方法是一样的,而且更简单
但是,这里我真正想表达的东西还没有出现,大家看到这里的filp->private_data= fh;//问题就在这里了,我们真正在read,write中需要传递的想要使用的是vivi_dev其实,
而不是v4l2_fh这个数据结构,我们还是直接看看vivi_template中fops中定义的read方法吧
- static ssize_t
- vivi_read(struct file *file, char __user *data, size_t count, loff_t*ppos)
- {
- struct vivi_dev *dev = video_drvdata(file);
- dprintk(dev, 1,"read called\n");
- return vb2_read(&dev->vb_vidq, data, count, ppos,
- file->f_flags& O_NONBLOCK);
- }
而这个vivi_dev是通过video_drvdata方法获得的,这就是为什么在上面我强调的要用
video_set_drvdata(vfd, dev);把vivi_dev装载到video_device中
到这里基本的过程都整理完了,其实大头还在后面呢,待续。。。。。。
- 虚拟视频驱动程序vivi.c源码分析
- 虚拟视频驱动程序vivi.c源码分析
- 虚拟视频驱动程序vivi.c源码分析
- 虚拟视频驱动程序vivi.c源码分析
- 虚拟视频驱动程序vivi.c源码分析
- 虚拟视频驱动程序vivi.c源码分析[转]
- 虚拟视频驱动vivi.c分析(linux-3.4.2版本)
- VIVI源码分析
- vivi源码分析
- 虚拟摄像头vivi驱动分析
- 分析vivi.c
- (视频)vivi驱动层代码分析
- (视频)vivi驱动层代码分析二
- vivi分析-head.S->main.c (转)
- v4l2视频驱动中关于vivi.c的个人分析(菜鸟入门,请轻拍!)
- 3.虚拟驱动vivi调用过程彻底分析
- V4L2(二)虚拟摄像头驱动vivi深入分析
- 摄像头驱动3_虚拟驱动vivi彻底分析
- 表的聚合函数
- JQuery DataTables学习
- LeetCode106 Construct Binary Tree from Inorder and Postorder Traversal
- php视频直播
- Android:图形
- 虚拟视频驱动程序vivi.c源码分析[转]
- oracle sql 优化相关
- HDU 5514 Frogs(容斥)
- Python之IPython开发实践
- 调试一个MFC的点云可视化(PCL)程序
- [web安全] web安全小知识点汇总
- oracle解除表锁定
- SecureCRT乱码解决
- 数据存取之Preference浅析