V4L2-core(基于linux5.4.258)

目录

  • V4L2目录结构
    • 包含文件及其功能
  • 每个文件中函数解析
    • tuner-core.c
    • v4l2-async.c
      • 结构体
        • struct v4l2_async_notifier
        • struct v4l2_async_subdev
        • struct v4l2_async_notifier_operations
      • 函数
    • v4l2-clk.c
    • v4l2-common.c
    • v4l2-compat-ioctl32.c
      • 函数解析
    • v4l2-ctrls.c
    • v4l2-dev.c
    • v4l2-device.c
      • 数据结构
      • 函数解析
    • v4l2-dv-timings.c
    • v4l2-event.c
      • 结构体
        • struct v4l2_kevent
        • struct v4l2_subscribed_event
      • 函数
      • V4L2 event包含关系
    • v4l2-fh.c
    • v4l2-flash-led-class.c
    • v4l2-fwnode.c
    • v4l2-i2c.c
    • v4l2-ioctl.c
    • v4l2-mc.c
    • v4l2-mem2mem.c
      • v4l2-spi.c
    • v4l2-subdev.c
      • 函数解析
      • 支持的ioctl
    • v4l2-trace.c
    • videobuf-core.c
    • videobuf-dma-contig.c
    • videobuf-dma-sg.c
    • videobuf-vmalloc.c
  • V4L2支持的ops功能
    • 1. subdev
    • 2. video device
    • 3. v4l2 dev
  • V4L2与vb2的关联
  • 用户态调用流程(yavta)

V4L2目录结构

V4L2-core(基于linux5.4.258)_第1张图片

包含文件及其功能

tuner-core.c: i2c tv tuner chip device driver.

 /*
 * This driver supports many devices and the idea is to let the driver
 * detect which device is present. So rather than listing all supported
 * devices here, we pretend to support a single, fake device type that will
 * handle both radio and analog TV tuning.
 * /

v4l2-async.c: v4l2子设备之间异步注册接口。
v4l2-clk.c: v4l2的时钟操作接口,提供接口来配置设备时钟。
v4l2-common.c:v4l2通用底层接口。
v4l2-compat-ioctl32.c:提供32位ioctl回调函数供v4l2-dev调用。
v4l2-ctrls.c:v4l2控制框架实现。
v4l2-dev.c:v4l2核心代码,驱动入口。以及cdev的fops实现。
v4l2-device.c:v4l2关于v4l2_device的相关接口。注册、绑定subdev等。
v4l2-dv-timings.c: dv-timings helper functions
v4l2-event.c:event事件订阅和处理接口。
v4l2-fh.c:v4l2 file handles基础接口。
用到file handle的文件:

  1. v4l2-compat-ioctl32.c:从file->private_data中获取,主要用了它的ctrl_handler成员。
  2. v4l2-ctrls.c: 从file->private_data中获取,主要用了它的event和ctrl_handler成员。
  3. v4l2-event.c: 几乎用了file handles的所有成员,似乎file handles是与event绑定的。
  4. v4l2-ioctl.c:从file->private_data中获取,主要是把fh当作入参传入用户自定义的ops回调。
  5. v4l2-mem2mem.c:从file->private_data中获取,只用了其中的m2m_ctx成员
  6. v4l2-subdev.c:从file->private_data中获取,用来配置ctrl_handler和event

v4l2-flash-led-class.c:flash LED驱动,包含subdev,media,ctrl, async功能。
v4l2-fwnode.c:以v4l2规定的dts格式parse对应节点。
v4l2-i2c.c:给v4l2_dev创建一个i2c的subdev
v4l2-ioctl.c: export一个ioctl接口,调用注册video_device时注册到vfd->ioctl_ops中的相应回调。
v4l2-mc.c:v4l2框架对media框架的封装,主要是创建media graph以及对graph里面节点的pm操作。
v4l2-mem2mem.c:这是被实现video设备驱动调用的,它负责内存的管理。记住,它只是一个中间的管理者,因为真正分配内存的不是mem2mem,而是videobuf2。
v4l2-spi.c:给v4l2_dev创建一个spi 的subdev
v4l2-subdev.c:v4l2子设备相关接口。
v4l2-trace.c:只有几个接口:

EXPORT_TRACEPOINT_SYMBOL_GPL(vb2_v4l2_buf_done);
EXPORT_TRACEPOINT_SYMBOL_GPL(vb2_v4l2_buf_queue);
EXPORT_TRACEPOINT_SYMBOL_GPL(vb2_v4l2_dqbuf);
EXPORT_TRACEPOINT_SYMBOL_GPL(vb2_v4l2_qbuf);

videobuf-core.c:对以下三个文件的封装,为用户驱动程序提供内存操作接口。
videobuf-dma-contig.c
videobuf-dma-sg.c
videobuf-vmalloc.c

每个文件中函数解析

tuner-core.c

v4l2-async.c

结构体

struct v4l2_async_notifier

asd_list:v4l2_async_subdev链表
v4l2_dev:Notifier所在的v4l2_dev

struct v4l2_async_notifier {
	const struct v4l2_async_notifier_operations *ops;
	struct v4l2_device *v4l2_dev;
	struct v4l2_subdev *sd;
	struct v4l2_async_notifier *parent;
	struct list_head asd_list;
	struct list_head waiting;
	struct list_head done;
	struct list_head list;
};
struct v4l2_async_subdev

match_type:async subdev的匹配模式
match:async subdev匹配时方法。有如下几种:

  1. V4L2_ASYNC_MATCH_FWNODE:使用fwnode,如果async subdev和subdev在同一组fwnode下,则匹配成功。
  2. V4L2_ASYNC_MATCH_DEVNAME:使用device name,判断方法:!strcmp(asd->match.device_name, dev_name(sd->dev));
  3. V4L2_ASYNC_MATCH_I2C:使用i2c.adapter_id和i2c.address如果相同则匹配成功
  4. V4L2_ASYNC_MATCH_CUSTOM:使用驱动自定义的match函数
struct v4l2_async_subdev {
	enum v4l2_async_match_type match_type;
	union {
		struct fwnode_handle *fwnode;
		const char *device_name;
		struct {
			int adapter_id;
			unsigned short address;
		} i2c;
		struct {
			bool (*match)(struct device *dev,
				      struct v4l2_async_subdev *sd);
			void *priv;
		} custom;
	} match;

	/* v4l2-async core private: not to be used by drivers */
	struct list_head list;
	struct list_head asd_list;
};
struct v4l2_async_notifier_operations

complete:当所有的subdev被probed会调用此接口,只有root notifier的complete接口会被调用。

struct v4l2_async_notifier_operations {
	int (*bound)(struct v4l2_async_notifier *notifier,
		     struct v4l2_subdev *subdev,
		     struct v4l2_async_subdev *asd);
	int (*complete)(struct v4l2_async_notifier *notifier);
	void (*unbind)(struct v4l2_async_notifier *notifier,
		       struct v4l2_subdev *subdev,
		       struct v4l2_async_subdev *asd);
};

函数

v4l2_async_notifier_init
初始化asd_list

v4l2_async_match_notify

  1. 调用v4l2_device_register_subdev将subdev注册到v4l2框架
  2. 调用驱动的bound接口绑定sd和asd
  3. 把asd从waiting列表删除
  4. 把subdev移动到done列表
  5. 发现有没有其他的notifier需要注册,循环执行

v4l2_async_notifier_try_all_subdevs
从注册到async框架的subdev和notifier->waiting里面找到匹配的asd和subdev,然后调用v4l2_async_match_notify将两者绑定到一起。
需要注意的是,subdev和asd不一定是一一对应的,只需要按照匹配规则,能匹配起来就会绑定到一起,可能多个subdev绑定一个asd。

v4l2_async_notifier_register
注册notifier到v4l2_dev。

  1. 遍历notifier->asd_list,判断asd是否有效,并将其加入到waiting列表中
  2. 调用v4l2_async_notifier_try_all_subdevs将所有注册到async框架的subdev和asd绑定起来
  3. 检查是否可以调用驱动实现的copmlete函数并执行
  4. 将notifier加入到async框架的notifier链表

v4l2_async_subdev_notifier_register

v4l2_async_notifier_unregister
取消绑定所有subdev和asd,并将subdev加入到async框架的subdev列表。

v4l2_async_register_subdev
如果此时没有注册notifier,那么将这个subdev加入到async框架的subdev列表中;
如果此时已经注册过notifier了,那么选择合适的notifier下waiting列表的asd,完成绑定,并将这个subdev加入到async框架的subdev列表中。

v4l2_async_notifier_cleanup
清理notifier下的asd列表

v4l2_async_notifier_add_subdev
往notifier上添加asd,作为匹配subdev的条件。

v4l2_async_notifier_add_fwnode_subdev
往notifier上添加fwnode格式的asd,作为匹配subdev的条件。

v4l2_async_notifier_add_fwnode_remote_subdev
往notifier上添加fwnode格式的asd,remote node作为匹配subdev的条件。

v4l2_async_notifier_add_i2c_subdev
往notifier上添加i2c格式的asd,作为匹配subdev的条件。

v4l2_async_notifier_add_devname_subdev
往notifier上添加devname格式的asd,作为匹配subdev的条件。

v4l2-clk.c

v4l2-common.c

v4l2-compat-ioctl32.c

函数解析

v4l2_compat_ioctl32:作为v4l2-device的ioctl32回调。如果cmd是v4l2定义的控制字,则调用do_video_ioctl,如果是用户自定义的控制字,则调用用户自定义的ioctl回调。

v4l2-ctrls.c

v4l2-dev.c

videodev_init
驱动入口函数,创建v4l2的class,分配cdev的主设备号,videoX设备节点是在__video_register_device函数中被创建的。

__video_register_device
根据不同的设备类型,创建不同的cdev设备节点。
设备类型包括:
VFL_TYPE_GRABBER: “video”
VFL_TYPE_VBI: “vbi”
VFL_TYPE_RADIO: “radio”
VFL_TYPE_SUBDEV: “v4l-subdev”
VFL_TYPE_SDR: “swradio”
VFL_TYPE_TOUCH: “v4l-touch”
在注册video device的时候,会把v4l2_fops注册进去,v4l2_fops会回调video device的fops和ioctl_ops,这些ops是在创建video device时,由驱动实现并指定的。

video_device_alloc
分配一个空的video device结构体

v4l2-device.c

数据结构

函数解析

v4l2_device_register
初始化v4l2_dev上下文

v4l2_device_release
释放v4l2_dev

v4l2_device_set_name
设置v4l2_dev的名字

v4l2_device_disconnect
解除v4l2_dev与对应dev的绑定

v4l2_device_unregister

  1. 解除v4l2_dev与对应dev的绑定
  2. unregister subdev

v4l2_device_register_subdev
向v4l2_dev注册subdev
会检查如下参数:

	/* Check for valid input */
	if (!v4l2_dev || !sd || sd->v4l2_dev || !sd->name[0])
		return -EINVAL;

v4l2_subdev_release
释放subdev

v4l2_device_register_subdev_nodes
为v4l2_dev下所有的subdev注册node节点(如果配置为生成节点的模式的话)

v4l2_device_unregister_subdev
unregister subdev

v4l2-dv-timings.c

v4l2-event.c

结构体

struct v4l2_kevent
struct v4l2_kevent {
	struct list_head	list;
	struct v4l2_subscribed_event *sev;
	struct v4l2_event	event;
	u64			ts;
};
struct v4l2_subscribed_event
struct v4l2_subscribed_event {
	struct list_head	list;
	u32			type;
	u32			id;
	u32			flags;
	struct v4l2_fh		*fh;
	struct list_head	node;
	const struct v4l2_subscribed_event_ops *ops;
	unsigned int		elems;
	unsigned int		first;
	unsigned int		in_use;
	struct v4l2_kevent	events[];
};

函数

v4l2_event_dequeue
分为阻塞和非阻塞两种情况:

  1. 非阻塞情况:直接调用__v4l2_event_dequeuefh->available列表中取可用的event
  2. 阻塞情况:先等待fh->wait信号量,被唤醒后再调用__v4l2_event_dequeuefh->available列表中取可用的event

v4l2_event_queue
需要用到:ev->type, ev->id

  1. 根据type和id从fh->subscribed列表中获取sev
  2. 获取可用kev并将kev加入到fh->available列表
  3. 唤醒fh->wait信号量

v4l2_event_queue_fh
参考v4l2_event_queue实现,不过入参不是video device而是fh

v4l2_event_pending
返回就绪的event的数量

v4l2_event_subscribe
根据驱动传入的event类型和event深度,创建sev结构体,并将其加入到fh->subscribed列表中

v4l2_event_unsubscribe_all
取消订阅所有event

v4l2_event_unsubscribe
移除指定type和id的订阅

v4l2_src_change_event_subscribe
订阅source change类型的event

v4l2_src_change_event_subdev_subscribe
取消订阅source change类型的event

V4L2 event包含关系

从下图可以看出,event的订阅和通知都是基于fh的,也就是说用户下发订阅是以fh为单位,通知用户也是以fh为单位。用户态在open video节点之后,基于这个fd进行event的订阅与通知。
V4L2-core(基于linux5.4.258)_第2张图片

v4l2-fh.c

v4l2-flash-led-class.c

v4l2-fwnode.c

v4l2-i2c.c

v4l2-ioctl.c

v4l2-mc.c

参考链接: media controller framework

v4l2-mem2mem.c

v4l2-spi.c

v4l2-subdev.c

函数解析

v4l2_subdev_link_validate_default
默认的检查两个subdev link有效的接口

v4l2_subdev_link_validate
检查link的source和sink是否匹配,如果有用户自定义检查接口,就调用用户自定义接口,否则调用上面的默认接口。

v4l2_subdev_alloc_pad_config

v4l2_subdev_init
初始化subdev上下文

v4l2_subdev_notify_event
将对应的event通知到subdev下的订阅列表中

支持的ioctl

  1. VIDIOC_QUERYCTRL
  2. VIDIOC_QUERY_EXT_CTRL
  3. VIDIOC_QUERYMENU
  4. VIDIOC_G_CTRL
  5. VIDIOC_S_CTRL
  6. VIDIOC_G_EXT_CTRLS
  7. VIDIOC_S_EXT_CTRLS
  8. VIDIOC_TRY_EXT_CTRLS
  9. VIDIOC_DQEVENT
  10. VIDIOC_SUBSCRIBE_EVENT
  11. VIDIOC_UNSUBSCRIBE_EVENT
  12. VIDIOC_LOG_STATUS
  13. VIDIOC_SUBDEV_G_FMT
  14. VIDIOC_SUBDEV_S_FMT
  15. VIDIOC_SUBDEV_G_CROP
  16. VIDIOC_SUBDEV_S_CROP
  17. VIDIOC_SUBDEV_ENUM_MBUS_CODE
  18. VIDIOC_SUBDEV_ENUM_FRAME_SIZE
  19. VIDIOC_SUBDEV_G_FRAME_INTERVAL
  20. VIDIOC_SUBDEV_S_FRAME_INTERVAL
  21. VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL
  22. VIDIOC_SUBDEV_G_SELECTION
  23. VIDIOC_SUBDEV_S_SELECTION
  24. VIDIOC_G_EDID
  25. VIDIOC_S_EDID
  26. VIDIOC_SUBDEV_DV_TIMINGS_CAP
  27. VIDIOC_SUBDEV_ENUM_DV_TIMINGS
  28. VIDIOC_SUBDEV_QUERY_DV_TIMINGS
  29. VIDIOC_SUBDEV_G_DV_TIMINGS
  30. VIDIOC_SUBDEV_G_STD
  31. VIDIOC_SUBDEV_S_STD
  32. VIDIOC_SUBDEV_ENUMSTD
  33. VIDIOC_SUBDEV_QUERYSTD
  34. 自定义ioctl

v4l2-trace.c

videobuf-core.c

videobuf-dma-contig.c

videobuf-dma-sg.c

videobuf-vmalloc.c

V4L2支持的ops功能

1. subdev

subdev的ops在调用v4l2_subdev_init函数时指定,包含以下ops:

/**
 * struct v4l2_subdev_ops - Subdev operations
 *
 * @core: pointer to &struct v4l2_subdev_core_ops. Can be %NULL
 * @tuner: pointer to &struct v4l2_subdev_tuner_ops. Can be %NULL
 * @audio: pointer to &struct v4l2_subdev_audio_ops. Can be %NULL
 * @video: pointer to &struct v4l2_subdev_video_ops. Can be %NULL
 * @vbi: pointer to &struct v4l2_subdev_vbi_ops. Can be %NULL
 * @ir: pointer to &struct v4l2_subdev_ir_ops. Can be %NULL
 * @sensor: pointer to &struct v4l2_subdev_sensor_ops. Can be %NULL
 * @pad: pointer to &struct v4l2_subdev_pad_ops. Can be %NULL
 */
struct v4l2_subdev_ops {
	const struct v4l2_subdev_core_ops	*core;
	const struct v4l2_subdev_tuner_ops	*tuner;
	const struct v4l2_subdev_audio_ops	*audio;
	const struct v4l2_subdev_video_ops	*video;
	const struct v4l2_subdev_vbi_ops	*vbi;
	const struct v4l2_subdev_ir_ops		*ir;
	const struct v4l2_subdev_sensor_ops	*sensor;
	const struct v4l2_subdev_pad_ops	*pad;
};

其中,内部的分类ops如下:

struct v4l2_subdev_core_ops {
	int (*log_status)(struct v4l2_subdev *sd);
	int (*s_io_pin_config)(struct v4l2_subdev *sd, size_t n,
				      struct v4l2_subdev_io_pin_config *pincfg);
	int (*init)(struct v4l2_subdev *sd, u32 val);
	int (*load_fw)(struct v4l2_subdev *sd);
	int (*reset)(struct v4l2_subdev *sd, u32 val);
	int (*s_gpio)(struct v4l2_subdev *sd, u32 val);
	long (*command)(struct v4l2_subdev *sd, unsigned int cmd, void *arg);
	long (*ioctl)(struct v4l2_subdev *sd, unsigned int cmd, void *arg);
#ifdef CONFIG_COMPAT
	long (*compat_ioctl32)(struct v4l2_subdev *sd, unsigned int cmd,
			       unsigned long arg);
#endif
#ifdef CONFIG_VIDEO_ADV_DEBUG
	int (*g_register)(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg);
	int (*s_register)(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg);
#endif
	int (*s_power)(struct v4l2_subdev *sd, int on);
	int (*interrupt_service_routine)(struct v4l2_subdev *sd,
						u32 status, bool *handled);
	int (*subscribe_event)(struct v4l2_subdev *sd, struct v4l2_fh *fh,
			       struct v4l2_event_subscription *sub);
	int (*unsubscribe_event)(struct v4l2_subdev *sd, struct v4l2_fh *fh,
				 struct v4l2_event_subscription *sub);
};
struct v4l2_subdev_tuner_ops {
	int (*standby)(struct v4l2_subdev *sd);
	int (*s_radio)(struct v4l2_subdev *sd);
	int (*s_frequency)(struct v4l2_subdev *sd, const struct v4l2_frequency *freq);
	int (*g_frequency)(struct v4l2_subdev *sd, struct v4l2_frequency *freq);
	int (*enum_freq_bands)(struct v4l2_subdev *sd, struct v4l2_frequency_band *band);
	int (*g_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner *vt);
	int (*s_tuner)(struct v4l2_subdev *sd, const struct v4l2_tuner *vt);
	int (*g_modulator)(struct v4l2_subdev *sd, struct v4l2_modulator *vm);
	int (*s_modulator)(struct v4l2_subdev *sd, const struct v4l2_modulator *vm);
	int (*s_type_addr)(struct v4l2_subdev *sd, struct tuner_setup *type);
	int (*s_config)(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *config);
};
struct v4l2_subdev_audio_ops {
	int (*s_clock_freq)(struct v4l2_subdev *sd, u32 freq);
	int (*s_i2s_clock_freq)(struct v4l2_subdev *sd, u32 freq);
	int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config);
	int (*s_stream)(struct v4l2_subdev *sd, int enable);
};
struct v4l2_subdev_vbi_ops {
	int (*decode_vbi_line)(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi_line);
	int (*s_vbi_data)(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *vbi_data);
	int (*g_vbi_data)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *vbi_data);
	int (*g_sliced_vbi_cap)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_cap *cap);
	int (*s_raw_fmt)(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt);
	int (*g_sliced_fmt)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt);
	int (*s_sliced_fmt)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt);
};
struct v4l2_subdev_ir_ops {
	/* Receiver */
	int (*rx_read)(struct v4l2_subdev *sd, u8 *buf, size_t count,
				ssize_t *num);

	int (*rx_g_parameters)(struct v4l2_subdev *sd,
				struct v4l2_subdev_ir_parameters *params);
	int (*rx_s_parameters)(struct v4l2_subdev *sd,
				struct v4l2_subdev_ir_parameters *params);

	/* Transmitter */
	int (*tx_write)(struct v4l2_subdev *sd, u8 *buf, size_t count,
				ssize_t *num);

	int (*tx_g_parameters)(struct v4l2_subdev *sd,
				struct v4l2_subdev_ir_parameters *params);
	int (*tx_s_parameters)(struct v4l2_subdev *sd,
				struct v4l2_subdev_ir_parameters *params);
};
struct v4l2_subdev_sensor_ops {
	int (*g_skip_top_lines)(struct v4l2_subdev *sd, u32 *lines);
	int (*g_skip_frames)(struct v4l2_subdev *sd, u32 *frames);
};
struct v4l2_subdev_pad_ops {
	int (*init_cfg)(struct v4l2_subdev *sd,
			struct v4l2_subdev_pad_config *cfg);
	int (*enum_mbus_code)(struct v4l2_subdev *sd,
			      struct v4l2_subdev_pad_config *cfg,
			      struct v4l2_subdev_mbus_code_enum *code);
	int (*enum_frame_size)(struct v4l2_subdev *sd,
			       struct v4l2_subdev_pad_config *cfg,
			       struct v4l2_subdev_frame_size_enum *fse);
	int (*enum_frame_interval)(struct v4l2_subdev *sd,
				   struct v4l2_subdev_pad_config *cfg,
				   struct v4l2_subdev_frame_interval_enum *fie);
	int (*get_fmt)(struct v4l2_subdev *sd,
		       struct v4l2_subdev_pad_config *cfg,
		       struct v4l2_subdev_format *format);
	int (*set_fmt)(struct v4l2_subdev *sd,
		       struct v4l2_subdev_pad_config *cfg,
		       struct v4l2_subdev_format *format);
	int (*get_selection)(struct v4l2_subdev *sd,
			     struct v4l2_subdev_pad_config *cfg,
			     struct v4l2_subdev_selection *sel);
	int (*set_selection)(struct v4l2_subdev *sd,
			     struct v4l2_subdev_pad_config *cfg,
			     struct v4l2_subdev_selection *sel);
	int (*get_edid)(struct v4l2_subdev *sd, struct v4l2_edid *edid);
	int (*set_edid)(struct v4l2_subdev *sd, struct v4l2_edid *edid);
	int (*dv_timings_cap)(struct v4l2_subdev *sd,
			      struct v4l2_dv_timings_cap *cap);
	int (*enum_dv_timings)(struct v4l2_subdev *sd,
			       struct v4l2_enum_dv_timings *timings);
#ifdef CONFIG_MEDIA_CONTROLLER
	int (*link_validate)(struct v4l2_subdev *sd, struct media_link *link,
			     struct v4l2_subdev_format *source_fmt,
			     struct v4l2_subdev_format *sink_fmt);
#endif /* CONFIG_MEDIA_CONTROLLER */
	int (*get_frame_desc)(struct v4l2_subdev *sd, unsigned int pad,
			      struct v4l2_mbus_frame_desc *fd);
	int (*set_frame_desc)(struct v4l2_subdev *sd, unsigned int pad,
			      struct v4l2_mbus_frame_desc *fd);
};

subdev的ops调用流程:

  1. 用户驱动在调用函数:v4l2_subdev_init时传入用户自定义subdev_ops,框架会将ops配置到subdev的结构体中。
  2. subdev是使用了video device的设备节点注册流程(也会创建video device上下文),区别在于,用户驱动在注册video device时,需要指定用户自己实现的fops,而subdev在注册的时候,指定的fops为v4l2_subdev_ops
  3. 用户驱动在调用函数:v4l2_device_register_subdev_nodes函数时,会遍历v4l2_dev下面所有的subdev节点,将此节点生成一个subdev node到用户态。
  4. v4l2_device_register_subdev_nodes函数会调用__video_register_device函数创建subdev节点,在此之前,v4l2_subdev_fops变量配置到vdev的fops成员里。
  5. __video_register_device函数创建subdev节点是通过cdev_add添加的,用户态节点第一个调用的函数其实是v4l2_fops.read/write/ioctl(),在v4l2_fops.read/write/ioctl()中再回调vdev->fops.read/write/ioctl()也就是v4l2_subdev_fops
  6. v4l2_subdev_fops里面的unlocked_ioctl会调用subdev_do_ioctl,里面会从file结构体中解析出subdev结构体,进而获取到用户自定义ops
  7. 自此,用户自定义ops与用户空间设备节点已经完成绑定。

2. video device

video device的ops调用流程与subdev的调用流程相似,区别在于,在调用video_register_device函数时,需要用户自定义fops与ioctl_ops,以实现用户自定义的功能。
其中,ioctl_ops被fops.unlocked_ioctl()调用,以实现video device的视频控制功能。
ioctl_ops定义如下:

struct v4l2_ioctl_ops {
	/* ioctl callbacks */

	/* VIDIOC_QUERYCAP handler */
	int (*vidioc_querycap)(struct file *file, void *fh,
			       struct v4l2_capability *cap);

	/* VIDIOC_ENUM_FMT handlers */
	int (*vidioc_enum_fmt_vid_cap)(struct file *file, void *fh,
				       struct v4l2_fmtdesc *f);
	int (*vidioc_enum_fmt_vid_overlay)(struct file *file, void *fh,
					   struct v4l2_fmtdesc *f);
	int (*vidioc_enum_fmt_vid_out)(struct file *file, void *fh,
				       struct v4l2_fmtdesc *f);
	int (*vidioc_enum_fmt_sdr_cap)(struct file *file, void *fh,
				       struct v4l2_fmtdesc *f);
	int (*vidioc_enum_fmt_sdr_out)(struct file *file, void *fh,
				       struct v4l2_fmtdesc *f);
	int (*vidioc_enum_fmt_meta_cap)(struct file *file, void *fh,
					struct v4l2_fmtdesc *f);
	int (*vidioc_enum_fmt_meta_out)(struct file *file, void *fh,
					struct v4l2_fmtdesc *f);

	/* VIDIOC_G_FMT handlers */
	int (*vidioc_g_fmt_vid_cap)(struct file *file, void *fh,
				    struct v4l2_format *f);
	int (*vidioc_g_fmt_vid_overlay)(struct file *file, void *fh,
					struct v4l2_format *f);
	int (*vidioc_g_fmt_vid_out)(struct file *file, void *fh,
				    struct v4l2_format *f);
	int (*vidioc_g_fmt_vid_out_overlay)(struct file *file, void *fh,
					    struct v4l2_format *f);
	int (*vidioc_g_fmt_vbi_cap)(struct file *file, void *fh,
				    struct v4l2_format *f);
	int (*vidioc_g_fmt_vbi_out)(struct file *file, void *fh,
				    struct v4l2_format *f);
	int (*vidioc_g_fmt_sliced_vbi_cap)(struct file *file, void *fh,
					   struct v4l2_format *f);
	int (*vidioc_g_fmt_sliced_vbi_out)(struct file *file, void *fh,
					   struct v4l2_format *f);
	int (*vidioc_g_fmt_vid_cap_mplane)(struct file *file, void *fh,
					   struct v4l2_format *f);
	int (*vidioc_g_fmt_vid_out_mplane)(struct file *file, void *fh,
					   struct v4l2_format *f);
	int (*vidioc_g_fmt_sdr_cap)(struct file *file, void *fh,
				    struct v4l2_format *f);
	int (*vidioc_g_fmt_sdr_out)(struct file *file, void *fh,
				    struct v4l2_format *f);
	int (*vidioc_g_fmt_meta_cap)(struct file *file, void *fh,
				     struct v4l2_format *f);
	int (*vidioc_g_fmt_meta_out)(struct file *file, void *fh,
				     struct v4l2_format *f);

	/* VIDIOC_S_FMT handlers */
	int (*vidioc_s_fmt_vid_cap)(struct file *file, void *fh,
				    struct v4l2_format *f);
	int (*vidioc_s_fmt_vid_overlay)(struct file *file, void *fh,
					struct v4l2_format *f);
	int (*vidioc_s_fmt_vid_out)(struct file *file, void *fh,
				    struct v4l2_format *f);
	int (*vidioc_s_fmt_vid_out_overlay)(struct file *file, void *fh,
					    struct v4l2_format *f);
	int (*vidioc_s_fmt_vbi_cap)(struct file *file, void *fh,
				    struct v4l2_format *f);
	int (*vidioc_s_fmt_vbi_out)(struct file *file, void *fh,
				    struct v4l2_format *f);
	int (*vidioc_s_fmt_sliced_vbi_cap)(struct file *file, void *fh,
					   struct v4l2_format *f);
	int (*vidioc_s_fmt_sliced_vbi_out)(struct file *file, void *fh,
					   struct v4l2_format *f);
	int (*vidioc_s_fmt_vid_cap_mplane)(struct file *file, void *fh,
					   struct v4l2_format *f);
	int (*vidioc_s_fmt_vid_out_mplane)(struct file *file, void *fh,
					   struct v4l2_format *f);
	int (*vidioc_s_fmt_sdr_cap)(struct file *file, void *fh,
				    struct v4l2_format *f);
	int (*vidioc_s_fmt_sdr_out)(struct file *file, void *fh,
				    struct v4l2_format *f);
	int (*vidioc_s_fmt_meta_cap)(struct file *file, void *fh,
				     struct v4l2_format *f);
	int (*vidioc_s_fmt_meta_out)(struct file *file, void *fh,
				     struct v4l2_format *f);

	/* VIDIOC_TRY_FMT handlers */
	int (*vidioc_try_fmt_vid_cap)(struct file *file, void *fh,
				      struct v4l2_format *f);
	int (*vidioc_try_fmt_vid_overlay)(struct file *file, void *fh,
					  struct v4l2_format *f);
	int (*vidioc_try_fmt_vid_out)(struct file *file, void *fh,
				      struct v4l2_format *f);
	int (*vidioc_try_fmt_vid_out_overlay)(struct file *file, void *fh,
					     struct v4l2_format *f);
	int (*vidioc_try_fmt_vbi_cap)(struct file *file, void *fh,
				      struct v4l2_format *f);
	int (*vidioc_try_fmt_vbi_out)(struct file *file, void *fh,
				      struct v4l2_format *f);
	int (*vidioc_try_fmt_sliced_vbi_cap)(struct file *file, void *fh,
					     struct v4l2_format *f);
	int (*vidioc_try_fmt_sliced_vbi_out)(struct file *file, void *fh,
					     struct v4l2_format *f);
	int (*vidioc_try_fmt_vid_cap_mplane)(struct file *file, void *fh,
					     struct v4l2_format *f);
	int (*vidioc_try_fmt_vid_out_mplane)(struct file *file, void *fh,
					     struct v4l2_format *f);
	int (*vidioc_try_fmt_sdr_cap)(struct file *file, void *fh,
				      struct v4l2_format *f);
	int (*vidioc_try_fmt_sdr_out)(struct file *file, void *fh,
				      struct v4l2_format *f);
	int (*vidioc_try_fmt_meta_cap)(struct file *file, void *fh,
				       struct v4l2_format *f);
	int (*vidioc_try_fmt_meta_out)(struct file *file, void *fh,
				       struct v4l2_format *f);

	/* Buffer handlers */
	int (*vidioc_reqbufs)(struct file *file, void *fh,
			      struct v4l2_requestbuffers *b);
	int (*vidioc_querybuf)(struct file *file, void *fh,
			       struct v4l2_buffer *b);
	int (*vidioc_qbuf)(struct file *file, void *fh,
			   struct v4l2_buffer *b);
	int (*vidioc_expbuf)(struct file *file, void *fh,
			     struct v4l2_exportbuffer *e);
	int (*vidioc_dqbuf)(struct file *file, void *fh,
			    struct v4l2_buffer *b);

	int (*vidioc_create_bufs)(struct file *file, void *fh,
				  struct v4l2_create_buffers *b);
	int (*vidioc_prepare_buf)(struct file *file, void *fh,
				  struct v4l2_buffer *b);

	int (*vidioc_overlay)(struct file *file, void *fh, unsigned int i);
	int (*vidioc_g_fbuf)(struct file *file, void *fh,
			     struct v4l2_framebuffer *a);
	int (*vidioc_s_fbuf)(struct file *file, void *fh,
			     const struct v4l2_framebuffer *a);

		/* Stream on/off */
	int (*vidioc_streamon)(struct file *file, void *fh,
			       enum v4l2_buf_type i);
	int (*vidioc_streamoff)(struct file *file, void *fh,
				enum v4l2_buf_type i);

		/*
		 * Standard handling
		 *
		 * Note: ENUMSTD is handled by videodev.c
		 */
	int (*vidioc_g_std)(struct file *file, void *fh, v4l2_std_id *norm);
	int (*vidioc_s_std)(struct file *file, void *fh, v4l2_std_id norm);
	int (*vidioc_querystd)(struct file *file, void *fh, v4l2_std_id *a);

		/* Input handling */
	int (*vidioc_enum_input)(struct file *file, void *fh,
				 struct v4l2_input *inp);
	int (*vidioc_g_input)(struct file *file, void *fh, unsigned int *i);
	int (*vidioc_s_input)(struct file *file, void *fh, unsigned int i);

		/* Output handling */
	int (*vidioc_enum_output)(struct file *file, void *fh,
				  struct v4l2_output *a);
	int (*vidioc_g_output)(struct file *file, void *fh, unsigned int *i);
	int (*vidioc_s_output)(struct file *file, void *fh, unsigned int i);

		/* Control handling */
	int (*vidioc_queryctrl)(struct file *file, void *fh,
				struct v4l2_queryctrl *a);
	int (*vidioc_query_ext_ctrl)(struct file *file, void *fh,
				     struct v4l2_query_ext_ctrl *a);
	int (*vidioc_g_ctrl)(struct file *file, void *fh,
			     struct v4l2_control *a);
	int (*vidioc_s_ctrl)(struct file *file, void *fh,
			     struct v4l2_control *a);
	int (*vidioc_g_ext_ctrls)(struct file *file, void *fh,
				  struct v4l2_ext_controls *a);
	int (*vidioc_s_ext_ctrls)(struct file *file, void *fh,
				  struct v4l2_ext_controls *a);
	int (*vidioc_try_ext_ctrls)(struct file *file, void *fh,
				    struct v4l2_ext_controls *a);
	int (*vidioc_querymenu)(struct file *file, void *fh,
				struct v4l2_querymenu *a);

	/* Audio ioctls */
	int (*vidioc_enumaudio)(struct file *file, void *fh,
				struct v4l2_audio *a);
	int (*vidioc_g_audio)(struct file *file, void *fh,
			      struct v4l2_audio *a);
	int (*vidioc_s_audio)(struct file *file, void *fh,
			      const struct v4l2_audio *a);

	/* Audio out ioctls */
	int (*vidioc_enumaudout)(struct file *file, void *fh,
				 struct v4l2_audioout *a);
	int (*vidioc_g_audout)(struct file *file, void *fh,
			       struct v4l2_audioout *a);
	int (*vidioc_s_audout)(struct file *file, void *fh,
			       const struct v4l2_audioout *a);
	int (*vidioc_g_modulator)(struct file *file, void *fh,
				  struct v4l2_modulator *a);
	int (*vidioc_s_modulator)(struct file *file, void *fh,
				  const struct v4l2_modulator *a);
	/* Crop ioctls */
	int (*vidioc_g_pixelaspect)(struct file *file, void *fh,
				    int buf_type, struct v4l2_fract *aspect);
	int (*vidioc_g_selection)(struct file *file, void *fh,
				  struct v4l2_selection *s);
	int (*vidioc_s_selection)(struct file *file, void *fh,
				  struct v4l2_selection *s);
	/* Compression ioctls */
	int (*vidioc_g_jpegcomp)(struct file *file, void *fh,
				 struct v4l2_jpegcompression *a);
	int (*vidioc_s_jpegcomp)(struct file *file, void *fh,
				 const struct v4l2_jpegcompression *a);
	int (*vidioc_g_enc_index)(struct file *file, void *fh,
				  struct v4l2_enc_idx *a);
	int (*vidioc_encoder_cmd)(struct file *file, void *fh,
				  struct v4l2_encoder_cmd *a);
	int (*vidioc_try_encoder_cmd)(struct file *file, void *fh,
				      struct v4l2_encoder_cmd *a);
	int (*vidioc_decoder_cmd)(struct file *file, void *fh,
				  struct v4l2_decoder_cmd *a);
	int (*vidioc_try_decoder_cmd)(struct file *file, void *fh,
				      struct v4l2_decoder_cmd *a);

	/* Stream type-dependent parameter ioctls */
	int (*vidioc_g_parm)(struct file *file, void *fh,
			     struct v4l2_streamparm *a);
	int (*vidioc_s_parm)(struct file *file, void *fh,
			     struct v4l2_streamparm *a);

	/* Tuner ioctls */
	int (*vidioc_g_tuner)(struct file *file, void *fh,
			      struct v4l2_tuner *a);
	int (*vidioc_s_tuner)(struct file *file, void *fh,
			      const struct v4l2_tuner *a);
	int (*vidioc_g_frequency)(struct file *file, void *fh,
				  struct v4l2_frequency *a);
	int (*vidioc_s_frequency)(struct file *file, void *fh,
				  const struct v4l2_frequency *a);
	int (*vidioc_enum_freq_bands)(struct file *file, void *fh,
				      struct v4l2_frequency_band *band);

	/* Sliced VBI cap */
	int (*vidioc_g_sliced_vbi_cap)(struct file *file, void *fh,
				       struct v4l2_sliced_vbi_cap *a);

	/* Log status ioctl */
	int (*vidioc_log_status)(struct file *file, void *fh);

	int (*vidioc_s_hw_freq_seek)(struct file *file, void *fh,
				     const struct v4l2_hw_freq_seek *a);

	/* Debugging ioctls */
#ifdef CONFIG_VIDEO_ADV_DEBUG
	int (*vidioc_g_register)(struct file *file, void *fh,
				 struct v4l2_dbg_register *reg);
	int (*vidioc_s_register)(struct file *file, void *fh,
				 const struct v4l2_dbg_register *reg);

	int (*vidioc_g_chip_info)(struct file *file, void *fh,
				  struct v4l2_dbg_chip_info *chip);
#endif

	int (*vidioc_enum_framesizes)(struct file *file, void *fh,
				      struct v4l2_frmsizeenum *fsize);

	int (*vidioc_enum_frameintervals)(struct file *file, void *fh,
					  struct v4l2_frmivalenum *fival);

	/* DV Timings IOCTLs */
	int (*vidioc_s_dv_timings)(struct file *file, void *fh,
				   struct v4l2_dv_timings *timings);
	int (*vidioc_g_dv_timings)(struct file *file, void *fh,
				   struct v4l2_dv_timings *timings);
	int (*vidioc_query_dv_timings)(struct file *file, void *fh,
				       struct v4l2_dv_timings *timings);
	int (*vidioc_enum_dv_timings)(struct file *file, void *fh,
				      struct v4l2_enum_dv_timings *timings);
	int (*vidioc_dv_timings_cap)(struct file *file, void *fh,
				     struct v4l2_dv_timings_cap *cap);
	int (*vidioc_g_edid)(struct file *file, void *fh,
			     struct v4l2_edid *edid);
	int (*vidioc_s_edid)(struct file *file, void *fh,
			     struct v4l2_edid *edid);

	int (*vidioc_subscribe_event)(struct v4l2_fh *fh,
				      const struct v4l2_event_subscription *sub);
	int (*vidioc_unsubscribe_event)(struct v4l2_fh *fh,
					const struct v4l2_event_subscription *sub);

	/* For other private ioctls */
	long (*vidioc_default)(struct file *file, void *fh,
			       bool valid_prio, unsigned int cmd, void *arg);
};

3. v4l2 dev

v4l2 dev只有一个ops:v4l2_ops,作为用户态设备节点往内核态调用的入口,调用对应设备节点 私有数据里面video device成员的fops。

V4L2与vb2的关联

链接: VB2在V4L2框架下的流程解读

用户态调用流程(yavta)

见图:
比较关键的ioctl流程已经用红色自己标注
V4L2-core(基于linux5.4.258)_第3张图片

你可能感兴趣的:(linux)