【V4L2】 v4l2框架分析之v4l2_device

👀👉本系列文章基于linux内核版本4.1.15分析media子系统下的V4L2。先分析组成V4L2的核心数据结构以及各组成元素的含义和作用。相关文章:


❤(1)《【V4L2】v4l2框架分析之video_device
❤(2)《V4L2-PCI驱动程序样例分析(一)

🔺一、v4l2_device简介

相关源码文件:

  • /drivers/media/v4l2-core/v4l2-device.h
  • /drivers/media/v4l2-core/v4l2-device.c

对于想要接入V4L2子系统的设备,每个设备实例都由一个struct v4l2_device表示。对于简单的设备可以只分配这个结构,但大多数情况下,可以把这个结构嵌入到更大的结构中。v4l2_device定义如下(/include/media/v4l2-device.h):

struct v4l2_device {
	struct device *dev;    //指向struct device的指针。
#if defined(CONFIG_MEDIA_CONTROLLER)
	struct media_device *mdev; //指向struct media_device的指针,值可以为NULL。struct media_device表示一个媒体设备。
#endif
	struct list_head subdevs;  //用来跟踪注册的子设备。
	spinlock_t lock;  //自旋锁。用于锁定这个结构体;如果这个结构体被嵌入到一个更大的结构体中,也可以被驱动程序使用。
	char name[V4L2_DEVICE_NAME_SIZE]; //唯一的设备名称,默认是驱动程序名称+总线ID
	void (*notify)(struct v4l2_subdev *sd,
			unsigned int notification, void *arg);  //被一些子设备调用,可用于执行通知操作。
	struct v4l2_ctrl_handler *ctrl_handler;  //control处理程序,可以为NULL。
	struct v4l2_prio_state prio;  //设备优先级状态。
	struct mutex ioctl_lock; //互斥锁。
	struct kref ref; //用于跟踪对这个结构体的引用。
	void (*release)(struct v4l2_device *v4l2_dev); //当ref的计数值变为0时调用这个函数。
};

🔺二、注册v4l2_device

在实际开发中,需使用下列函数:

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

注册v4l2_device设备实例。

函数实现如下:

该函数会初始化v4l2_device结构。如果dev->driver_data字段是NULL,他将被连接到v4l2_dev。如果驱动程序想要与媒体设备框架集成,则需要手动设置dev->driver_data,用于指向v4l2_device实例的驱动程序的特定设备结构。这是通过在注册V4L2设备实例之前调用dev_set_drvdata()实现的。还必须设置v4l2_device结构的mdev字段,用于指向初始化和注册的媒体设备实例。

如果v4l2_dev->name为空,那么它将被设置为派生自dev的值,如果在调用v4l2_device_register()之前已经设置了v4l2_dev->name,那么它将不受到影响。如果dev为NULL,那么在调用v4l2_device_register之前,则必须设置v4l2_dev->name参数。

对于v4l2_device_register()函数,第一个’dev’参数通常是指向pci_devusb_interfaceplatform_device的设备指针。dev很少为NULL,但是ISA设备或一个设备创建多个PCI设备时会出现dev为NULL的情况,因此不可能将v4l2_dev与特定的父设备进行关联。

在实际驱动程序设计中,可以提供一个notify()回调函数,子设备可以调用它来通知事件。是否需要设置notify回调函数取决于子设备。子设备支持的任何通知都必须在include/media/subdevice.h的头文件中定义。

🔺三、移除v4l2_device

在驱动程序设计中,使用:

v4l2_device_unregister(struct v4l2_device *v4l2_dev);

来移除注册的设备。如果dev->driver_data字段指向v4l2_dev,那么dev->driver_data将被重置为NULL。移除操作也会自动从设备上移除所有子设备。

如果有一个可热插拔的设备(例如USB设备),那么当断开连接时,父设备就会失效。因为v4l2_device有一个指向父设备的指针,所以它也必须被清除,以标记父设备已经消失。这时候需要调用:

v4l2_device_disconnect(struct v4l2_device *v4l2_dev);

该函数不会取消注册的子设备,所以仍然需要调用v4l2_device_unregister()函数。如果驱动程序不可以热插拔,则不需要调用v4l2_device_disconnect()

有时需要遍历由特定驱动程序注册的所有设备。如果多个设备驱动程序使用相同的硬件,通常会出现这种情况:例如,ivtvfb驱动程序是一个使用ivtv硬件的帧缓冲区驱动程序。同样的情况也适用于alsa驱动。

可以使用下列所示的代码遍历所有已经注册的设备:

static int callback(struct device *dev, void *p)
{
  struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);

  /* 检测该设备是否已经被初始化 */
  if (v4l2_dev == NULL)
  	return 0;
  ...
  return 0;
}

int iterate(void *p)
{
  struct device_driver *drv;
  int err;

  /* Find driver 'ivtv' on the PCI bus.
     pci_bus_type is a global. For USB busses use usb_bus_type. */
  drv = driver_find("ivtv", &pci_bus_type);
  /* iterate over all ivtv device instances */
  err = driver_for_each_device(drv, NULL, p, callback);
  put_driver(drv);
  return err;
}

🔺四、引用计数

如果有多个设备节点,那么很难知道何时为可热插拔设备注销v4l2_device是安全的操作。因此,v4l2_device支持引用计数:在video_register_device()被调用时增加引用计数值,在设备节点被释放时减少引用计数值,当引用计数值达到零时,则会调用v4l2_devicerelease()回调。在实际驱动程序设计中,则可以在这个回调函数中执行最后的清理操作。

如果创建了其他设备节点(例如ALSA),那么也可以通过调用:

void v4l2_device_get(struct v4l2_device *v4l2_dev);

或者:

int v4l2_device_put(struct v4l2_device *v4l2_dev);

手动增加、减少引用计数。

由于初始引用计数是1,所以还需要在disconnect()remove()回调函数(例如PCI设备)中调用v4l2_device_put(),否则引用计数将不会达到0。

参考资料:
https://docs.kernel.org/driver-api/media/v4l2-device.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

iriczhao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值