V4L2核心框架分析

驱动的结构
------------------------------------------------------
1)一个为设备实例定义的,并且包含设备状态信息的结构;
2)一种初始化和命令子设备(sub-devices)的方式;
3)创建V4L2设备节点(/dev/videoX, /dev/vbiX, /dev/radioX and /dev/vtxX)
并且 keeping track of device-node specific data.
4)Filehandle-specific structs containing per-filehandle data;
5)视频buffer处理;

下面有个大略的关系描述图:

device instances
|
+-sub-device instances
|
\-V4L2 device nodes
|
\-filehandle instances


框架的结构
------------------------------------------------------------
框架结构与驱动结构类似:
它有:
一个用于device实例数据的结构体: v4l2_device ;
一个用于sub-device实例的结构体:v4l2_subdev ;
一个存储设备节点数据的结构体:video_device ;
将来会用于保持对文件操作实例的追踪的结构体:v4l2_fh ;


v4l2_device结构体(struct v4l2_device)
-----------------------------------------------------------------------
每个设备实例是由一个struct v4l2_device结构(v4l2-device.h)来描述的。
很简单的设备可以只分配这个结构,但是,大部分时候,你将嵌入这个结构到一个更大的与描述特定设备的结构。
你必须注册设备实例(在drivers/media/video/v4l2-device.c中):

  1. v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev);

注册将初始化v4l2_device结构,和连接(link) dev->driver_data 到v4l2_dev。
如果v4l2_dev->name 是空的,那么它将被设置成一个值,这个值是由dev参数得到的(严格来说,驱动名跟随bus_id)。
如果你在调用v4l2_device_register()之前设置了v4l2_dev->name,那么v4l2_dev->name将不被改变(即使用你之前设置的值)。
如果dev是NULL,那么你必须在调用v4l2_device_register()之前填充v4l2_dev->name。

你可以使用 v4l2_device_set_name()来设置这个名称为驱动的名字 和 一个 driver-global atomic_t instance。
这将产生一些名字如: ivtv0, ivtv1 等。如果这些名字(ivtv0中的ivtv就是一个名字)以一个数字结束,那么它将插入一个“-”,然后跟上一个顺序的编号。
v4l2_device_set_name()这个函数返回这个实例号。

  1. int v4l2_device_set_name(struct v4l2_device *v4l2_dev, const char *basename,
  2. atomic_t *instance)
  3. {
  4. int num = atomic_inc_return(instance) - 1;
  5. int len = strlen(basename);
  6. if (basename[len - 1] >= '0' && basename[len - 1] <= '9')//如果名字的最后是数字;
  7. snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),
  8. "%s-%d", basename, num);//那么名字后面加“-”
  9. else
  10. snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),
  11. "%s%d", basename, num);
  12. return num;
  13. }
  14. EXPORT_SYMBOL_GPL(v4l2_device_set_name);


现在回头说v4l2_device_register函数。它的第一个参数dev通常都是一个pci_dev
、usb_interface 或者 platform_device 的“struct device”类型的指针。
dev基本上不会是NULL,但是在ISA devices或者当一个设备创建了多个PCI设备的时候,这是可能发生的。
这时候它与v4l2_dev产生了关系,v4l2_dev下可能有个dev成员(即v4l2_dev是父,dev是子)。

你也可以提供一个 notify()的回调函数,子设备(sub-devices)可以调用这个回调函数通知你一些事件(events);
你是否需要设置这个回调函数,取决于这个子设备。一个子设备支持的任何通知(notifications)必须被定义在头文件“include/media/.h”当中。
比如include/media/ 下有如下文件:
v4l2-chip-ident.h v4l2-fh.h v4l2-mem2mem.h videobuf-dvb.h
v4l2-common.h v4l2-i2c-drv.h v4l2-subdev.h videobuf-vmalloc.h
v4l2-dev.h v4l2-int-device.h videobuf-core.h tvp5150.h(这个就是subdevice的头文件)
v4l2-device.h v4l2-ioctl.h videobuf-dma-contig.h
v4l2-event.h v4l2-mediabus.h videobuf-dma-sg.h …………

有注册,就有反注册。那对应的反注册的函数是:

  1. v4l2_device_unregister(struct v4l2_device *v4l2_dev);

反注册,也将会自动从设备反注册掉所有的子设备(subdevs)。

如果你有一个可热插拔的(hotpluggable)的设备(例如USB设备),那么的那个一个disconnect(断开)发生时,父设备变成无效的(invalid)。
因为v4l2_device中有一个指针指向父设备,所以他可以同时被清除掉,并且标记父设备is gone(这里应该是在父设备的结构里标记子设备已经无效了)。
v4l2_device_disconnect(struct v4l2_device *v4l2_dev); //热插拔使用的断开函数

  1. void v4l2_device_disconnect(struct v4l2_device *v4l2_dev)
  2. {
  3. if (v4l2_dev->dev) { //这个函数制式标记了父设备那边告诉子设备已经无效。
  4. dev_set_drvdata(v4l2_dev->dev, NULL);
  5. v4l2_dev->dev = NULL;
  6. }
  7. }
  8. EXPORT_SYMBOL_GPL(v4l2_device_disconnect);



v4l2_device_disconnect不会反注册掉子设备,所以你依然需要为子设备调用v4l2_device_unregister这个函数。
事实上,v4l2_device_unregister函数中还是会调用v4l2_device_disconnect的。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值