Linux V4l2子系统分析2

Linux V4l2子系统分析2(基于Linux6.6)---结构体介绍

 

 

一、v4l2调用过程

 

在 Linux 系统中,/dev/videoX 设备节点代表一个视频设备,通常用于视频捕捉(例如网络摄像头)或视频回放(例如电视卡)。当用户空间的应用程序访问 /dev/videoX 时,内核会通过 V4L2(Video for Linux 2)子系统管理硬件设备和数据流,完成视频数据的捕获、输出、控制等功能。

下面是用户空间程序通过 /dev/videoX 设备节点进行操作时,内核处理过程的详细介绍。

1.1、用户空间请求过程

假设用户空间应用程序执行了一些常见的操作,如打开设备、设置参数、捕获图像等。以下是主要操作的过程:

1.打开设备

int fd = open("/dev/video0", O_RDWR);
  • 用户空间应用程序通过 open() 系统调用打开 /dev/video0 设备文件。
  • 内核通过 字符设备驱动 查找设备节点 /dev/video0 对应的设备驱动程序,找到与 video_device 结构相关的函数,完成设备的初始化和打开过程。

2.内核如何处理设备打开

当用户调用 open("/dev/videoX") 时,内核会调用以下过程:

  1. 字符设备驱动注册:视频设备驱动会在内核启动时通过 register_chrdev()v4l2_device_register() 等函数注册到 V4L2 子系统,并与 /dev/videoX 设备节点进行关联。

  2. 文件操作结构(file_operations):当应用程序打开设备时,内核会通过 struct file_operations 中的 open() 函数来处理打开设备的请求。对于 V4L2 设备,内核通常会调用 v4l2_open() 或驱动程序自定义的打开函数。

  3. 视频设备实例化:设备的初始化和资源分配会在 open() 函数中进行,驱动程序会通过 V4L2 API 初始化相关设备资源(如设备上下文、缓冲区队列等)。

  4. 返回文件描述符:如果设备打开成功,内核返回一个文件描述符(fd),用户可以通过该描述符进一步进行操作。

3.设置视频格式

struct v4l2_format fmt;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 640;
fmt.fmt.pix.height = 480;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field = V4L2_FIELD_NONE;
ioctl(fd, VIDIOC_S_FMT, &fmt);
  • 用户通过 ioctl() 调用设置设备的格式,例如分辨率、像素格式等。
  • 内核通过 VIDIOC_S_FMT 控制命令来设置设备的格式,这个命令会被传递到驱动程序中。

4.内核如何处理 VIDIOC_S_FMT 命令

当内核接收到 VIDIOC_S_FMT IOCTL 请求时,驱动程序会执行以下步骤:

  1. IOCTL 调用ioctl() 系统调用会根据命令类型查找对应的设备操作函数,例如 video_ioctl2()

  2. 格式设置:驱动程序会根据用户空间传入的 v4l2_format 结构,设置设备的捕获或回放格式,通常会设置图像的宽度、高度、像素格式等。

  3. 验证格式:驱动程序会检查设备是否支持所请求的格式,如果不支持,返回错误。

  4. 更新设备参数:驱动程序会更新设备的状态和参数,准备好以新的格式进行捕获或回放。

5.捕获图像数据

ioctl(fd, VIDIOC_STREAMON, &buf_type);
ioctl(fd, VIDIOC_QBUF, &buf);
ioctl(fd, VIDIOC_DQBUF, &buf);
  • 用户调用 VIDIOC_STREAMON 启动视频流,告诉驱动开始传输视频数据。
  • 然后,用户通过 VIDIOC_QBUF 向设备队列中提交缓冲区,并通过 VIDIOC_DQBUF 获取捕获的数据。

6.内核如何处理视频数据流

  1. 缓冲区队列管理:内核通过缓冲区队列管理(通常是内存映射缓冲区或用户指针缓冲区)来处理数据的传输。缓冲区被分配并提交到设备,以便视频数据可以捕获到这些缓冲区中。

  2. VIDIOC_STREAMON:当内核接收到 VIDIOC_STREAMON 命令时,会通知设备开始捕获数据或播放数据。设备开始将数据流传输到缓冲区队列中。

  3. 缓冲区排队与出队:应用程序通过 VIDIOC_QBUF 向设备提交缓冲区,等待设备将捕获的数据写入缓冲区。完成捕获后,设备通过 VIDIOC_DQBUF 命令将数据从缓冲区中取出。

  4. 数据传输:视频捕获设备将帧数据(例如图像或视频流)从硬件中传输到用户空间的缓冲区。

7.关闭设备

close(fd);
  • 用户关闭设备后,内核会调用相应的 release() 函数来释放设备资源,终止缓冲区队列等操作。

1.2、内核调用过程

当用户空间应用程序操作 /dev/videoX 时,内核调用过程如下:

  1. 设备文件操作

    • 用户空间通过文件系统的接口(如 open()read()ioctl() 等)与内核交互,访问 /dev/videoX
    • 内核通过字符设备驱动的 file_operations 表查找相关的驱动函数。
  2. 驱动程序处理请求

    • 内核将请求传递给设备的驱动程序,驱动程序通过实现的 V4L2 函数处理设备请求。例如,v4l2_open() 处理设备的打开,v4l2_ioctl() 处理 IOCTL 命令等。
  3. 设备控制

    • 驱动程序根据请求设置硬件设备参数,如图像格式、分辨率等。
  4. 数据传输

    • 驱动程序通过缓冲区队列和 V4L2 API 与硬件进行数据交换,将视频帧传输到用户空间,或者从用户空间传输到设备。
  5. 事件通知

    • 内核通过事件机制(如 select()poll() 等)向应用程序报告设备状态变化,支持异步操作。
  6. 设备释放

    • 用户调用 close() 时,内核会通过 release() 函数释放设备资源,停止视频流,清理缓冲区等。

052ec9ea80b7431abe29e464ec4925b7.png 

 

二、v4l2关联的文件

 

在 Linux 内核中,V4L2(Video for Linux 2)是用于视频捕捉、播放和控制的子系统,它提供了标准的 API 接口供用户空间程序与硬件设备交互。V4L2 在内核中的实现涉及多个关键文件,这些文件主要位于 drivers/media 目录下,涉及的模块主要与视频设备的注册、操作、缓冲区管理等相关。

以下是一些与 V4L2 关联的重要文件及其作用:

1. drivers/media/v4l2-core/v4l2-ioctl.c

  • 作用:处理与 V4L2 设备的控制命令(ioctl 请求)相关的函数。
  • 关键功能:包括处理视频设备的各种 IOCTL 操作,如设置格式(VIDIOC_S_FMT)、获取帧缓冲(VIDIOC_QBUF)等。这个文件处理了用户空间发起的命令,并将其转交给对应的设备驱动。
  • 常见函数
    • v4l2_ioctl()
    • v4l2_device_ioctl()

2. drivers/media/v4l2-core/v4l2-dev.c

  • 作用:管理 V4L2 设备的注册和初始化。
  • 关键功能:包括视频设备的注册、注销、以及设备控制结构的创建和销毁。它为设备提供了一个通用的接口,使得不同的驱动程序能够通过 V4L2 进行统一的设备管理。
  • 常见函数
    • v4l2_device_register()
    • v4l2_device_unregister()
    • v4l2_device_init()

3. drivers/media/v4l2-core/v4l2-fh.c

  • 作用:处理文件句柄(file handle)相关的操作,管理与设备相关的打开和关闭操作。
  • 关键功能:这个文件主要涉及与文件描述符和设备的关联管理。每个打开的视频设备都会分配一个文件句柄,用来跟踪设备的状态。
  • 常见函数
    • v4l2_fh_init()
    • v4l2_fh_release()

4. drivers/media/v4l2-core/v4l2-buffer.c

  • 作用:管理视频设备的缓冲区(buffer)相关操作。
  • 关键功能:处理缓冲区的队列操作,分配、释放和操作缓冲区。对于视频捕获或输出,缓冲区管理至关重要,V4L2 使用缓冲区队列来实现帧的捕获和显示。
  • 常见函数
    • v4l2_buffer_alloc()
    • v4l2_buffer_free()
    • v4l2_buf_queue()
    • v4l2_buf_dq() (入队与出队操作)

5. drivers/media/v4l2-core/v4l2-device.c

  • 作用:处理与 V4L2 设备相关的具体实现,包括设备实例的创建、设备管理。
  • 关键功能:这个文件定义了视频设备的抽象接口,设备注册时需要设置一些特性(如设备类型、操作方法等)。它帮助驱动程序管理与设备硬件的交互。
  • 常见函数
    • v4l2_device_init()
    • v4l2_device_unregister()

6. drivers/media/v4l2-core/v4l2-compat-ioctl32.c

  • 作用:处理 32 位和 64 位系统之间的兼容性问题,特别是在进行 IOCTL 操作时。
  • 关键功能:当在 64 位系统中运行 32 位应用程序时,V4L2 提供了兼容性支持,确保数据结构能够正确地在不同位宽的系统之间传递。
  • 常见函数
    • v4l2_ioctl32()(用于处理32位用户空间请求)

7. drivers/media/v4l2-core/v4l2-async.c

  • 作用:处理与 V4L2 设备异步事件相关的操作。
  • 关键功能:V4L2 支持异步事件,如设备插拔、信号变化等。这个文件处理与异步事件的注册、回调等操作。
  • 常见函数
    • v4l2_async_notifier_register()
    • v4l2_async_notifier_unregister()

8. include/linux/videodev2.h

  • 作用:V4L2 的核心头文件,定义了 V4L2 API、数据结构、常量和控制命令。
  • 关键功能:这是与 V4L2 操作相关的所有常量和数据结构的定义,包括格式、缓冲区、控制命令等。用户空间应用程序和内核驱动程序都依赖此文件定义的接口。
  • 重要数据结构
    • struct v4l2_format(描述视频格式)
    • struct v4l2_buffer(描述视频缓冲区)
    • struct v4l2_control(描述视频控制)
  • 常见常量
    • V4L2_BUF_TYPE_VIDEO_CAPTURE(视频捕获缓冲区类型)
    • V4L2_PIX_FMT_YUYV(视频像素格式)

9. drivers/media/v4l2-core/v4l2-ctrls.c

  • 作用:管理 V4L2 控制接口,包括视频设备的各项参数控制。
  • 关键功能:V4L2 支持通过控制接口来调整设备的各种参数(如亮度、对比度、色彩饱和度等)。v4l2-ctrls.c 负责管理和更新这些控制参数。
  • 常见函数
    • v4l2_ctrl_handler_init()
    • v4l2_ctrl_new_std()
    • v4l2_ctrl_g_ctrl()
    • v4l2_ctrl_s_ctrl()

10. drivers/media/v4l2-core/v4l2-mem2mem.c

  • 作用:处理 V4L2 内存到内存(mem-to-mem)操作,主要用于视频编码、解码等。
  • 关键功能:这个文件提供了视频流的内存到内存的处理功能,例如视频压缩、解压缩等操作。
  • 常见函数
    • v4l2_m2m_queue_setup()
    • v4l2_m2m_buffer_prepare()

 

三、v4l2重要的结构体

3.1、struct v4l2_device

每一个使用v4l2的设备都要创建一个struct v4l2_device,用来记录其他的media_device和子设备。该大结构体可以单独创建也可以嵌套进一个更大的结构体中。

include/media/v4l2-device.h 

struct v4l2_device {
	/* dev->driver_data points to this struct.
	   Note: dev might be NULL if there is no parent device
	   as is the case with e.g. ISA devices. */
	struct device *dev;
#if defined(CONFIG_MEDIA_CONTROLLER)
	struct media_device *mdev;
#endif
	/* used to keep track of the registered subdevs */
	struct list_head subdevs;//这个很重要的,用来记录所有子设备的。
	/* lock this struct; can be used by the driver as well if this
	   struct is embedded into a larger struct. */
	spinlock_t lock;
	/* unique device name, by default the driver name + bus ID */
	char name[V4L2_DEVICE_NAME_SIZE];
	/* notify callback called by some sub-devices. */
	/* 子设备通知母设备时,就会回调这个接口*/
	void (*notify)(struct v4l2_subdev *sd,
			unsigned int notification, void *arg);
	/* The control handler. May be NULL. */
	/* 在注册子设备时,会将所有子设备的控制实例,链接到母设备中,便于user层
	   发送命令时,母设备能发送给对应的控制实例*/
	struct v4l2_ctrl_handler *ctrl_handler;
	/* Device's priority state */
	struct v4l2_prio_state prio;
	/* BKL replacement mutex. Temporary solution only. */
	struct mutex ioctl_lock;
	/* Keep track of the references to this struct. */
	struct kref ref;
	/* Release function that is called when the ref count goes to 0. */
	void (*release)(struct v4l2_device *v4l2_dev);
};

该结构体存在的意义在于方便访问子设备,以及方便根据media_entry查找设备属性。其它的如果预先知道了设备名称,可以直接访问设备。V4L2 device是必须创建的。

  • dev:dev一般存放设备的父亲设备,可以为空。
  • mdev: media_device设备对象,一般用作设备的入口,用来查找设备类型。比如是source模块,还是sink模块。
  • ctrl_handler:这是struct v4l2_ctrl对象的聚集地,当使用struct v4l2_ctrl功能时,v4l2_device会记录所有子设备的ctrl对象,便于查找。

3.2、struct v4l2_ctrl_handler

该结构体专门用来记录一个或多个控制命令集合(例如设置gain,曝光时间等),目前发现三星平台是以ctrl_hanler的形式来表达sensor的控制方式。

include/media/v4l2-ctrls.h 

struct v4l2_ctrl_handler {
	struct mutex _lock;
	struct mutex *lock;
	struct list_head ctrls;
	struct list_head ctrl_refs;
	struct v4l2_ctrl_ref *cached;
	struct v4l2_ctrl_ref **buckets;
	v4l2_ctrl_notify_fnc notify;
	void *notify_priv;
	u16 nr_of_buckets;
	int error;
	bool request_is_queued;
	struct list_head requests;
	struct list_head requests_queued;
	struct media_request_object req_obj;
};

它一般和struct v4l2_ctrl配合使用。可以理解成v4l2_ctrl_handler是v4l2_ctrl的仓库。查找v4l2_ctrl即可通过handler来查找。后面会举例子来解释这一层意思的。

3.3、struct v4l2_ctrl

include/media/v4l2-ctrls.h 

struct v4l2_ctrl {
	/* Administrative fields */
	struct list_head node;
	struct list_head ev_subs;
	struct v4l2_ctrl_handler *handler;
	struct v4l2_ctrl **cluster;
	unsigned int ncontrols;

	unsigned int done:1;

	unsigned int is_new:1;
	unsigned int has_changed:1;
	unsigned int is_private:1;
	unsigned int is_auto:1;
	unsigned int is_int:1;
	unsigned int is_string:1;
	unsigned int is_ptr:1;
	unsigned int is_array:1;
	unsigned int is_dyn_array:1;
	unsigned int has_volatiles:1;
	unsigned int call_notify:1;
	unsigned int manual_mode_value:8;

	const struct v4l2_ctrl_ops *ops;
	const struct v4l2_ctrl_type_ops *type_ops;
	u32 id;
	const char *name;
	enum v4l2_ctrl_type type;
	s64 minimum, maximum, default_value;
	u32 elems;
	u32 elem_size;
	u32 new_elems;
	u32 dims[V4L2_CTRL_MAX_DIMS];
	u32 nr_of_dims;
	union {
		u64 step;
		u64 menu_skip_mask;
	};
	union {
		const char * const *qmenu;
		const s64 *qmenu_int;
	};
	unsigned long flags;
	void *priv;
	void *p_array;
	u32 p_array_alloc_elems;
	s32 val;
	struct {
		s32 val;
	} cur;

	union v4l2_ctrl_ptr p_def;
	union v4l2_ctrl_ptr p_new;
	union v4l2_ctrl_ptr p_cur;
};

上面最重要的就是

  • ev_subs:这个表示控件事件订阅列表,也就是说,所有订阅该 v4l2_ctrl的消息的对象,都会在该消息可用时收到一个事件。该事件会自动queue到对应订阅者对象的subscribed列表中。
  • handler:该控制对象所属的struct v4l2_ctrl_handler的控制仓库对象。
  • ops:操作ctrl的方法,具体各个方法的含义,下方备注都很清楚。

3.4、struct v4l2_ctrl_ops

include/media/v4l2-ctrls.h  

/** struct v4l2_ctrl_ops - The control operations that the driver has to provide.
  * @g_volatile_ctrl: Get a new value for this control. Generally only relevant
  *		for volatile (and usually read-only) controls such as a control
  *		that returns the current signal strength which changes
  *		continuously.
  *		If not set, then the currently cached value will be returned.
  * @try_ctrl:	Test whether the control's value is valid. Only relevant when
  *		the usual min/max/step checks are not sufficient.
  * @s_ctrl:	Actually set the new control value. s_ctrl is compulsory. The
  *		ctrl->handler->lock is held when these ops are called, so no
  *		one else can access controls owned by that handler.
  */
struct v4l2_ctrl_ops {
	int (*g_volatile_ctrl)(struct v4l2_ctrl *ctrl);
	int (*try_ctrl)(struct v4l2_ctrl *ctrl);
	int (*s_ctrl)(struct v4l2_ctrl *ctrl);
};

3.5、struct v4l2_fh

此结构用于描述消息队列,设备的消息都会链接在此对象中,我们也可以在struct video_device结构体中看到有struct v4l2_fh的数据域。

include/media/v4l2-fh.h 

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 mutex		subscribe_lock;
	struct list_head	subscribed;
	struct list_head	available;
	unsigned int		navailable;
	u32			sequence;

	struct v4l2_m2m_ctx	*m2m_ctx;
};
  • list:链表入口,因为一个设备可能有好几条消息队列。(一般情况下打开一次设备会创建一个消息队列,不过一般请看下只会打开一次)
  • vdev:拥有此消息队列的video设备
  • ctrl_handler:这其中也牵扯很多其它结构体,只需要了解这与控制元素相关就可以了。
  • prio:消息队列优先级。
  • wait:等待队列,由于可能有几个线程同时访问消息队列,所有等待消息队列可用的线程都会记录在这个等待列表中。
  • subscribed:这个是订阅消息链表。只有订阅的消息才能够queue到消息队列中,反之是无法queue到队列中的。
  • available:消息队列,可用的消息都以链表的形式保存在这里。
  • navailable:消息队列中可用消息的数量。
  • sequence:序列号,此数据域会一直自加下去,直到内核对象消亡。

注意:
struct v4l2_fh结构体一般保存在struct fileprivate_data数据域。一般打开video设备文件时,会在标准的open接口中调用v4l2_fh_open接口。如下的代码中可以发现在open时会为struct v4l2_fh分配内存。

drivers/media/v4l2-core/v4l2-fh.c 

int v4l2_fh_open(struct file *filp)
{
	struct video_device *vdev = video_devdata(filp);
	struct v4l2_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);

	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);

3.6、struct video_device

video_device一般用来描述一个出帧的设备,所以从kernel中的dev目录下,发现有几个videoX设备,即可认为有几个Camera.

include/media/v4l2-dev.h 


struct video_device {
#if defined(CONFIG_MEDIA_CONTROLLER)
	struct media_entity entity;
	struct media_intf_devnode *intf_devnode;
	struct media_pipeline pipe;
#endif
	const struct v4l2_file_operations *fops;

	u32 device_caps;

	/* sysfs */
	struct device dev;
	struct cdev *cdev;

	struct v4l2_device *v4l2_dev;
	struct device *dev_parent;

	struct v4l2_ctrl_handler *ctrl_handler;

	struct vb2_queue *queue;

	struct v4l2_prio_state *prio;

	/* device info */
	char name[32];
	enum vfl_devnode_type vfl_type;
	enum vfl_devnode_direction vfl_dir;
	int minor;
	u16 num;
	unsigned long flags;
	int index;

	/* V4L2 file handles */
	spinlock_t		fh_lock;
	struct list_head	fh_list;

	int dev_debug;

	v4l2_std_id tvnorms;

	/* callbacks */
	void (*release)(struct video_device *vdev);
	const struct v4l2_ioctl_ops *ioctl_ops;
	DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE);

	struct mutex *lock;
};
  • entity:video设备的入口属性,可用于查找video设备的名字和属性组。
  • fops:此fops包含标准的open,ioctl,close等接口,该一系列接口会被包含在v4l2-dev.c中的标准字符设备的open,ioctl,close接口调用,后面会画一个简单的包含调用关系。
  • cdev:为在v4l2-dev.c中创建的字符设备对象
  • v4l2_dev:该video设备所依附的v4l2_device设备对象,不能为空。
  • fh_lock:该video设备的消息队列锁,消息队列任何时候只能有一个线程在访问。
  • fh_list:此video设备的消息队列,可以包含多个消息队列(每当打开一次video设备即会创建一个消息队列)
  • ioctl_ops:v4l2核心调用的标准接口,包含stream_on,stream_off等。

3.7、struct v4l2_ioctl_ops

include/media/v4l2-ioctl.h 

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);
};

此结构体中包含了很多方法,鉴于篇幅太长,这里就列举几个简单常用的示意一下。详细代码还是看源码吧。

  • vidioc_querycap:这个用于获取设备能力的接口,一般在操作设备之前,应用软件需要先获取到设备的能力,才能下发与之匹配的命令。要不然有些命令,很难服从。
  • vidioc_g_priority:见名知意,获取权限相关的。(标准的set,get)

3.8、struct v4l2_subdev

include/media/v4l2-subdev.h 

struct v4l2_subdev {
#if defined(CONFIG_MEDIA_CONTROLLER)
	struct media_entity entity;
#endif
	struct list_head list;
	struct module *owner;
	bool owner_v4l2_dev;
	u32 flags;
	struct v4l2_device *v4l2_dev;
	const struct v4l2_subdev_ops *ops;
	const struct v4l2_subdev_internal_ops *internal_ops;
	struct v4l2_ctrl_handler *ctrl_handler;
	char name[V4L2_SUBDEV_NAME_SIZE];
	u32 grp_id;
	void *dev_priv;
	void *host_priv;
	struct video_device *devnode;
	struct device *dev;
	struct fwnode_handle *fwnode;
	struct list_head async_list;
	struct list_head async_subdev_endpoint_list;
	struct v4l2_async_notifier *subdev_notifier;
	struct list_head asc_list;
	struct v4l2_subdev_platform_data *pdata;
	struct mutex *state_lock;

	/*
	 * The fields below are private, and should only be accessed via
	 * appropriate functions.
	 */

	struct led_classdev *privacy_led;

	/*
	 * TODO: active_state should most likely be changed from a pointer to an
	 * embedded field. For the time being it's kept as a pointer to more
	 * easily catch uses of active_state in the cases where the driver
	 * doesn't support it.
	 */
	struct v4l2_subdev_state *active_state;
	u64 enabled_streams;
};
  • v4l2_dev:该子设备所依附的v4l2_device(注意v4l2_device存在的作用就是便于访问和记录子设备)
  • internal_ops:子设备的注册/注销以及打开/关闭接口,这个会被v4l2-subdev.c中标准的open/close接口调用。所以是内部的。
  • ctrl_handler:该子设备的消息队列管理对象。
  • devnode:子设备的设备对象,其实subdev也是一个video_device,只是由v4l2核心把设备节点名字更改了罢了

3.9、struct v4l2_subdev_ops

include/media/v4l2-subdev.h 

  • 接口结构体介绍
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;
};

上面包含所以子设备的操作接口,可以看到有video,audio,ir,sensor等媒体设备。这里其实常用的只是core域所包含的方法。

static const struct v4l2_subdev_internal_ops msm_eeprom_internal_ops = {
	.open = msm_eeprom_open,
	.close = msm_eeprom_close,
};
//上面包含标准的接口,会被v4l2-subdev.c中的open/close调用
static struct v4l2_subdev_core_ops msm_eeprom_subdev_core_ops = {
	.ioctl = msm_eeprom_subdev_ioctl,
};
//如上面实现了当前eeprom专属的ioctl,用户可以在此接口添加自己的ioctl命令
static struct v4l2_subdev_ops msm_eeprom_subdev_ops = {
	.core = &msm_eeprom_subdev_core_ops,
};
//上面

上面以高通的eeprom子设备为例子,介绍了子设备的接口实现。最后的msm_eeprom_subdev_ops 会被注册到v4l2_subdev的ops域。

v4l2-dev.c
	|(1.open/clsoe/ioctl)
	|
	|----->v4l2-subdev.c
				|(2.sub-dev,open,close,ioctl)
				|
				|-------->msm_eeprom.c
								|-------->3.(open,close,ioctl)

调用流程如上所示
1.v4l2-dev.c中实现了标准的open,close,ioctl,这些是真正被字符设备使用的。
2.v4l2-subdev.c中的open,close,ioctl被v4l2-dev中对应的接口调用
3.此为用户自己定义的,同样被上一级调用

drivers/media/v4l2-core/v4l2-subdev.c 

  • ioctl扩展
static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
			    struct v4l2_subdev_state *state)
{
	struct video_device *vdev = video_devdata(file);
	struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
	struct v4l2_fh *vfh = file->private_data;
	struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
	bool ro_subdev = test_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags);
	bool streams_subdev = sd->flags & V4L2_SUBDEV_FL_STREAMS;
	bool client_supports_streams = subdev_fh->client_caps &
				       V4L2_SUBDEV_CLIENT_CAP_STREAMS;
	int rval;

	/*
	 * If the streams API is not enabled, remove V4L2_SUBDEV_CAP_STREAMS.
	 * Remove this when the API is no longer experimental.
	 */
	if (!v4l2_subdev_enable_streams_api)
		streams_subdev = false;

	switch (cmd) {
	case VIDIOC_SUBDEV_QUERYCAP: {
		struct v4l2_subdev_capability *cap = arg;

		memset(cap->reserved, 0, sizeof(cap->reserved));
		cap->version = LINUX_VERSION_CODE;
		cap->capabilities =
			(ro_subdev ? V4L2_SUBDEV_CAP_RO_SUBDEV : 0) |
			(streams_subdev ? V4L2_SUBDEV_CAP_STREAMS : 0);

		return 0;
	}

	case VIDIOC_QUERYCTRL:
		/*
		 * TODO: this really should be folded into v4l2_queryctrl (this
		 * currently returns -EINVAL for NULL control handlers).
		 * However, v4l2_queryctrl() is still called directly by
		 * drivers as well and until that has been addressed I believe
		 * it is safer to do the check here. The same is true for the
		 * other control ioctls below.
		 */
		if (!vfh->ctrl_handler)
			return -ENOTTY;
		return v4l2_queryctrl(vfh->ctrl_handler, arg);

	case VIDIOC_QUERY_EXT_CTRL:
		if (!vfh->ctrl_handler)
			return -ENOTTY;
		return v4l2_query_ext_ctrl(vfh->ctrl_handler, arg);

	case VIDIOC_QUERYMENU:
		if (!vfh->ctrl_handler)
			return -ENOTTY;
		return v4l2_querymenu(vfh->ctrl_handler, arg);

	case VIDIOC_G_CTRL:
		if (!vfh->ctrl_handler)
			return -ENOTTY;
		return v4l2_g_ctrl(vfh->ctrl_handler, arg);

	case VIDIOC_S_CTRL:
		if (!vfh->ctrl_handler)
			return -ENOTTY;
		return v4l2_s_ctrl(vfh, vfh->ctrl_handler, arg);

	case VIDIOC_G_EXT_CTRLS:
		if (!vfh->ctrl_handler)
			return -ENOTTY;
		return v4l2_g_ext_ctrls(vfh->ctrl_handler,
					vdev, sd->v4l2_dev->mdev, arg);

	case VIDIOC_S_EXT_CTRLS:
		if (!vfh->ctrl_handler)
			return -ENOTTY;
		return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler,
					vdev, sd->v4l2_dev->mdev, arg);

	case VIDIOC_TRY_EXT_CTRLS:
		if (!vfh->ctrl_handler)
			return -ENOTTY;
		return v4l2_try_ext_ctrls(vfh->ctrl_handler,
					  vdev, sd->v4l2_dev->mdev, arg);

	case VIDIOC_DQEVENT:
		if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
			return -ENOIOCTLCMD;

		return v4l2_event_dequeue(vfh, arg, file->f_flags & O_NONBLOCK);

	case VIDIOC_SUBSCRIBE_EVENT:
		return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg);

	case VIDIOC_UNSUBSCRIBE_EVENT:
		return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg);

#ifdef CONFIG_VIDEO_ADV_DEBUG
	case VIDIOC_DBG_G_REGISTER:
	{
		struct v4l2_dbg_register *p = arg;

		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;
		return v4l2_subdev_call(sd, core, g_register, p);
	}
	case VIDIOC_DBG_S_REGISTER:
	{
		struct v4l2_dbg_register *p = arg;

		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;
		return v4l2_subdev_call(sd, core, s_register, p);
	}
	case VIDIOC_DBG_G_CHIP_INFO:
	{
		struct v4l2_dbg_chip_info *p = arg;

		if (p->match.type != V4L2_CHIP_MATCH_SUBDEV || p->match.addr)
			return -EINVAL;
		if (sd->ops->core && sd->ops->core->s_register)
			p->flags |= V4L2_CHIP_FL_WRITABLE;
		if (sd->ops->core && sd->ops->core->g_register)
			p->flags |= V4L2_CHIP_FL_READABLE;
		strscpy(p->name, sd->name, sizeof(p->name));
		return 0;
	}
#endif

	case VIDIOC_LOG_STATUS: {
		int ret;

		pr_info("%s: =================  START STATUS  =================\n",
			sd->name);
		ret = v4l2_subdev_call(sd, core, log_status);
		pr_info("%s: ==================  END STATUS  ==================\n",
			sd->name);
		return ret;
	}

	case VIDIOC_SUBDEV_G_FMT: {
		struct v4l2_subdev_format *format = arg;

		if (!client_supports_streams)
			format->stream = 0;

		memset(format->reserved, 0, sizeof(format->reserved));
		memset(format->format.reserved, 0, sizeof(format->format.reserved));
		return v4l2_subdev_call(sd, pad, get_fmt, state, format);
	}

	case VIDIOC_SUBDEV_S_FMT: {
		struct v4l2_subdev_format *format = arg;

		if (format->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)
			return -EPERM;

		if (!client_supports_streams)
			format->stream = 0;

		memset(format->reserved, 0, sizeof(format->reserved));
		memset(format->format.reserved, 0, sizeof(format->format.reserved));
		return v4l2_subdev_call(sd, pad, set_fmt, state, format);
	}

	case VIDIOC_SUBDEV_G_CROP: {
		struct v4l2_subdev_crop *crop = arg;
		struct v4l2_subdev_selection sel;

		if (!client_supports_streams)
			crop->stream = 0;

		memset(crop->reserved, 0, sizeof(crop->reserved));
		memset(&sel, 0, sizeof(sel));
		sel.which = crop->which;
		sel.pad = crop->pad;
		sel.target = V4L2_SEL_TGT_CROP;

		rval = v4l2_subdev_call(
			sd, pad, get_selection, state, &sel);

		crop->rect = sel.r;

		return rval;
	}

	case VIDIOC_SUBDEV_S_CROP: {
		struct v4l2_subdev_crop *crop = arg;
		struct v4l2_subdev_selection sel;

		if (crop->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)
			return -EPERM;

		if (!client_supports_streams)
			crop->stream = 0;

		memset(crop->reserved, 0, sizeof(crop->reserved));
		memset(&sel, 0, sizeof(sel));
		sel.which = crop->which;
		sel.pad = crop->pad;
		sel.target = V4L2_SEL_TGT_CROP;
		sel.r = crop->rect;

		rval = v4l2_subdev_call(
			sd, pad, set_selection, state, &sel);

		crop->rect = sel.r;

		return rval;
	}

	case VIDIOC_SUBDEV_ENUM_MBUS_CODE: {
		struct v4l2_subdev_mbus_code_enum *code = arg;

		if (!client_supports_streams)
			code->stream = 0;

		memset(code->reserved, 0, sizeof(code->reserved));
		return v4l2_subdev_call(sd, pad, enum_mbus_code, state,
					code);
	}

	case VIDIOC_SUBDEV_ENUM_FRAME_SIZE: {
		struct v4l2_subdev_frame_size_enum *fse = arg;

		if (!client_supports_streams)
			fse->stream = 0;

		memset(fse->reserved, 0, sizeof(fse->reserved));
		return v4l2_subdev_call(sd, pad, enum_frame_size, state,
					fse);
	}

	case VIDIOC_SUBDEV_G_FRAME_INTERVAL: {
		struct v4l2_subdev_frame_interval *fi = arg;

		if (!client_supports_streams)
			fi->stream = 0;

		memset(fi->reserved, 0, sizeof(fi->reserved));
		return v4l2_subdev_call(sd, video, g_frame_interval, arg);
	}

	case VIDIOC_SUBDEV_S_FRAME_INTERVAL: {
		struct v4l2_subdev_frame_interval *fi = arg;

		if (ro_subdev)
			return -EPERM;

		if (!client_supports_streams)
			fi->stream = 0;

		memset(fi->reserved, 0, sizeof(fi->reserved));
		return v4l2_subdev_call(sd, video, s_frame_interval, arg);
	}

	case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: {
		struct v4l2_subdev_frame_interval_enum *fie = arg;

		if (!client_supports_streams)
			fie->stream = 0;

		memset(fie->reserved, 0, sizeof(fie->reserved));
		return v4l2_subdev_call(sd, pad, enum_frame_interval, state,
					fie);
	}

	case VIDIOC_SUBDEV_G_SELECTION: {
		struct v4l2_subdev_selection *sel = arg;

		if (!client_supports_streams)
			sel->stream = 0;

		memset(sel->reserved, 0, sizeof(sel->reserved));
		return v4l2_subdev_call(
			sd, pad, get_selection, state, sel);
	}

	case VIDIOC_SUBDEV_S_SELECTION: {
		struct v4l2_subdev_selection *sel = arg;

		if (sel->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)
			return -EPERM;

		if (!client_supports_streams)
			sel->stream = 0;

		memset(sel->reserved, 0, sizeof(sel->reserved));
		return v4l2_subdev_call(
			sd, pad, set_selection, state, sel);
	}

	case VIDIOC_G_EDID: {
		struct v4l2_subdev_edid *edid = arg;

		return v4l2_subdev_call(sd, pad, get_edid, edid);
	}

	case VIDIOC_S_EDID: {
		struct v4l2_subdev_edid *edid = arg;

		return v4l2_subdev_call(sd, pad, set_edid, edid);
	}

	case VIDIOC_SUBDEV_DV_TIMINGS_CAP: {
		struct v4l2_dv_timings_cap *cap = arg;

		return v4l2_subdev_call(sd, pad, dv_timings_cap, cap);
	}

	case VIDIOC_SUBDEV_ENUM_DV_TIMINGS: {
		struct v4l2_enum_dv_timings *dvt = arg;

		return v4l2_subdev_call(sd, pad, enum_dv_timings, dvt);
	}

	case VIDIOC_SUBDEV_QUERY_DV_TIMINGS:
		return v4l2_subdev_call(sd, video, query_dv_timings, arg);

	case VIDIOC_SUBDEV_G_DV_TIMINGS:
		return v4l2_subdev_call(sd, video, g_dv_timings, arg);

	case VIDIOC_SUBDEV_S_DV_TIMINGS:
		if (ro_subdev)
			return -EPERM;

		return v4l2_subdev_call(sd, video, s_dv_timings, arg);

	case VIDIOC_SUBDEV_G_STD:
		return v4l2_subdev_call(sd, video, g_std, arg);

	case VIDIOC_SUBDEV_S_STD: {
		v4l2_std_id *std = arg;

		if (ro_subdev)
			return -EPERM;

		return v4l2_subdev_call(sd, video, s_std, *std);
	}

	case VIDIOC_SUBDEV_ENUMSTD: {
		struct v4l2_standard *p = arg;
		v4l2_std_id id;

		if (v4l2_subdev_call(sd, video, g_tvnorms, &id))
			return -EINVAL;

		return v4l_video_std_enumstd(p, id);
	}

	case VIDIOC_SUBDEV_QUERYSTD:
		return v4l2_subdev_call(sd, video, querystd, arg);

	case VIDIOC_SUBDEV_G_ROUTING: {
		struct v4l2_subdev_routing *routing = arg;
		struct v4l2_subdev_krouting *krouting;

		if (!v4l2_subdev_enable_streams_api)
			return -ENOIOCTLCMD;

		if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS))
			return -ENOIOCTLCMD;

		memset(routing->reserved, 0, sizeof(routing->reserved));

		krouting = &state->routing;

		if (routing->num_routes < krouting->num_routes) {
			routing->num_routes = krouting->num_routes;
			return -ENOSPC;
		}

		memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes,
		       krouting->routes,
		       krouting->num_routes * sizeof(*krouting->routes));
		routing->num_routes = krouting->num_routes;

		return 0;
	}

	case VIDIOC_SUBDEV_S_ROUTING: {
		struct v4l2_subdev_routing *routing = arg;
		struct v4l2_subdev_route *routes =
			(struct v4l2_subdev_route *)(uintptr_t)routing->routes;
		struct v4l2_subdev_krouting krouting = {};
		unsigned int i;

		if (!v4l2_subdev_enable_streams_api)
			return -ENOIOCTLCMD;

		if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS))
			return -ENOIOCTLCMD;

		if (routing->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)
			return -EPERM;

		memset(routing->reserved, 0, sizeof(routing->reserved));

		for (i = 0; i < routing->num_routes; ++i) {
			const struct v4l2_subdev_route *route = &routes[i];
			const struct media_pad *pads = sd->entity.pads;

			if (route->sink_stream > V4L2_SUBDEV_MAX_STREAM_ID ||
			    route->source_stream > V4L2_SUBDEV_MAX_STREAM_ID)
				return -EINVAL;

			if (route->sink_pad >= sd->entity.num_pads)
				return -EINVAL;

			if (!(pads[route->sink_pad].flags &
			      MEDIA_PAD_FL_SINK))
				return -EINVAL;

			if (route->source_pad >= sd->entity.num_pads)
				return -EINVAL;

			if (!(pads[route->source_pad].flags &
			      MEDIA_PAD_FL_SOURCE))
				return -EINVAL;
		}

		krouting.num_routes = routing->num_routes;
		krouting.routes = routes;

		return v4l2_subdev_call(sd, pad, set_routing, state,
					routing->which, &krouting);
	}

	case VIDIOC_SUBDEV_G_CLIENT_CAP: {
		struct v4l2_subdev_client_capability *client_cap = arg;

		client_cap->capabilities = subdev_fh->client_caps;

		return 0;
	}

	case VIDIOC_SUBDEV_S_CLIENT_CAP: {
		struct v4l2_subdev_client_capability *client_cap = arg;

		/*
		 * Clear V4L2_SUBDEV_CLIENT_CAP_STREAMS if streams API is not
		 * enabled. Remove this when streams API is no longer
		 * experimental.
		 */
		if (!v4l2_subdev_enable_streams_api)
			client_cap->capabilities &= ~V4L2_SUBDEV_CLIENT_CAP_STREAMS;

		/* Filter out unsupported capabilities */
		client_cap->capabilities &= V4L2_SUBDEV_CLIENT_CAP_STREAMS;

		subdev_fh->client_caps = client_cap->capabilities;

		return 0;
	}

	default:
		return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
	}

	return 0;
}

上面是v4l2-subdev中实现的ioctl,其它命令都是标准的,用户需要实现对应的功能才能调用,要不然可能会出异常。default即可以调用用户自己定义的命令,可以看到对应的就是core方法中的ioctl方法,这也就是上面高通定义的。

3.10、小结

 经过上面的总结,可以大概知道设备从属关系如下所示:

ee06c2d3663245888f31bfdcb6661894.png

  • v4l2_device记录所有子设备和支持的control命令,便于用户查找设备(只是记录)。其实用户查找设备,一般通过下面即将介绍的struct media_entity entity结构来记录。所有子设备的media_entiry对象都会挂接到v4l2_device对应的media设备上
  • video和sub_device的等级属于同一级别,只是在注册设备时取了不同名字而已。

四、meida_device介绍

  • media_device.c:该为media设备的通用操作公共接口
  • media_devnode.c:此为media设备对应的字符设备驱动标准接口,注册的media设备都是使用一套接口的。注意做一下客制化。
media_devnode.c
	|
	|------------>media_device.c(公用)

4.1、media_device重要的结构体介绍

就目前个人了解到,media设备存在的意义,便于发现媒体设备内部的拓扑结构。就目前v4l2框架使用media设备,只是为了发现v4l2子设备的拓扑结构。所以也不会记录太深入。

include/media/media-device.h

struct media_device {
	/* dev->driver_data points to this struct. */
	struct device *dev;
	struct media_devnode *devnode;

	char model[32];
	char driver_name[32];
	char serial[40];
	char bus_info[32];
	u32 hw_revision;

	u64 topology_version;

	u32 id;
	struct ida entity_internal_idx;
	int entity_internal_idx_max;

	struct list_head entities;
	struct list_head interfaces;
	struct list_head pads;
	struct list_head links;

	/* notify callback list invoked when a new entity is registered */
	struct list_head entity_notify;

	/* Serializes graph operations. */
	struct mutex graph_mutex;
	struct media_graph pm_count_walk;

	void *source_priv;
	int (*enable_source)(struct media_entity *entity,
			     struct media_pipeline *pipe);
	void (*disable_source)(struct media_entity *entity);

	const struct media_device_ops *ops;

	struct mutex req_queue_mutex;
	atomic_t request_id;
};

部分域介绍

  • dev:对应的父设备对象,一般是v4l2-device对象。
  • devnode:当前media设备对象,其实还是字符设备。
  • model:设备模型,可以根据这个来查找设备
  • entities:设备入口双向链表,所有设备都会挂接到这里。

之前一直没看结构体的注释,老是认为dev指针域就是指media设备的设备对象。看到注释才发现是父设备。真正的media设备是struct media_devnode devnode, 但是这个结构体中有很多猫腻。具体请看下面结构体。每一个media设备都是字符设备,他们也有自己的属性文件(这一注册过程会记录在下一篇笔记中)。

ret = device_create_file(&mdev->devnode.dev, &dev_attr_model);

属性文件的路径是:/sys/bus/media/devices/media0(高通平台为例)

msm:/sys/bus/media/devices # ls -l
total 0
lrwxrwxrwx 1 root root 0 1970-01-06 01:01 media0 -> …/…/…/devices/soc/1b00000.qcom,msm-cam/media0
lrwxrwxrwx 1 root root 0 1970-01-06 01:01 media1 -> …/…/…/devices/soc/1b0c000.qcom,cci/1b0c000.qcom,cci:qcom,camera@0/media1
lrwxrwxrwx 1 root root 0 1970-01-06 01:01 media2 -> …/…/…/devices/soc/1b0c000.qcom,cci/1b0c000.qcom,cci:qcom,camera@2/media2

4.2、media_devnode

include/media/media-devnode.h 

struct media_devnode {
	struct media_device *media_dev;

	/* device ops */
	const struct media_file_operations *fops;

	/* sysfs */
	struct device dev;		/* media device */
	struct cdev cdev;		/* character device */
	struct device *parent;		/* device parent */

	/* device info */
	int minor;
	unsigned long flags;		/* Use bitops to access flags */

	/* callbacks */
	void (*release)(struct media_devnode *devnode);
};

此结构对应一个字符设备

  • 1dev:这里明确指出是meida设备。
  • 2 cdev:也明确指出是字符设备。
  • 3 parent:明确指出是父亲节点,这个就是上面media_device设备的父亲节点。可以把media_device中的dev指针域当作所有子节点的的父节点。

4.3、struct media_file_operations

include/media/media-devnode.h 

struct media_file_operations {
	struct module *owner;
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	__poll_t (*poll) (struct file *, struct poll_table_struct *);
	long (*ioctl) (struct file *, unsigned int, unsigned long);
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	int (*open) (struct file *);
	int (*release) (struct file *);
};

上面的接口定义在media_device.c中,但会被media_devnode.c中的标准接口调用。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值