linux v4l2学习之-常用结构体介绍

感兴趣可以加QQ群85486140,大家一起交流相互学习下!


开发Camera也有4年了,说实话对v4l2架构也知道一些框架,但是真的没有好好研究过。还是决定抽十几个夜晚和2个周末来仔细探索一下。学习开始之前先搜索了其它大神的笔记,有的写的很不错。比如下面几个,自上而下都有介绍,链接如下。
http://blog.chinaunix.net/uid-27411029-id-3784956.html
http://blog.chinaunix.net/uid-27411029-id-3789850.html
http://blog.chinaunix.net/uid-27411029-id-3789851.html

后续的几篇博文即为学习笔记。大概分成下面几个学习部分。

  • 1.v4l2常用结构体介绍
  • 2.v4l2设备注册过程及各个设备之间的联系
  • 3.v4l2 ctrl机制介绍
  • 4.v4l2消息机制
  • 5.v4l2 buffer申请和管理机制
  • 6.v4l2使用过程中需要注意的一些技巧
  • 7.v4l2实战演练

一、v4l2粗略线条

二、v4l2关联的几个重要文件

videodev-objs	:=	v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \ v4l2-event.o v4l2-ctrls.o v4l2-subdev.o
obj-$(CONFIG_VIDEOBUF2_CORE)		+= videobuf2-core.o
obj-$(CONFIG_VIDEOBUF2_MEMOPS)		+= videobuf2-memops.o
obj-$(CONFIG_VIDEOBUF2_VMALLOC)		+= videobuf2-vmalloc.o
obj-$(CONFIG_VIDEOBUF2_DMA_CONTIG)	+= videobuf2-dma-contig.o
obj-$(CONFIG_VIDEOBUF2_DMA_SG)		+= videobuf2-dma-sg.o
obj-$(CONFIG_VIDEOBUF2_CMA_PHYS)	+= videobuf2-cma-phys.o
obj-$(CONFIG_VIDEOBUF2_ION)		+= videobuf2-ion.o
obj-$(CONFIG_VIDEOBUF2_ION_NXP)		+= videobuf2-ion-nxp.o

obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o

由Makefile可以发现,一定参与编译的有下面几个文件

  • 1.v4l2-dev.c:对接上层的标准v4l2设备文件。看过代码的都知道,v4l2针对具体的物理Camera都会对应一个videox的字符设备节点,而这个文件中都包含标准的open,close,ioctl等字符设备接口(我们打开设备时也是先打开这个设备,进而找到对应的子设备),此外文件中还有视频设备注册接口以及一些设置权限的接口,注册接口如下所示:
int __video_register_device(struct video_device *vdev, int type, int nr,
		int warn_if_nr_in_use, struct module *owner)
{
	vdev->cdev = cdev_alloc();
	if (vdev->cdev == NULL) {
		ret = -ENOMEM;
		goto cleanup;
	}
	vdev->cdev->ops = &v4l2_fops;
	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);
  • 2.v4l2-ioctl.c:由于视频设备相当复杂,对应的ioctl命令相当的多,这里直接将ioctl单独拿出来,便于其它软件模块复用,如下面STREAM_ON实现。
static long __video_do_ioctl(struct file *file,
		unsigned int cmd, void *arg){
	switch(cmd) {
	    case VIDIOC_QUERYCAP: break;
	    case VIDIOC_REQBUFS: break;
	    case VIDIOC_DQBUF: break;
	    case VIDIOC_STREAMON: 
	    {
	    		enum v4l2_buf_type i = *(int *)arg;
		    if (!ops->vidioc_streamon)
			    break;
		    if (ret_prio) {
			    ret = ret_prio;
			    break;
		    }
		    dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names));
		    ret = ops->vidioc_streamon(file, fh, i);
		    break;
	    }
	    case VIDIOC_STREAMOFF: break;
	}	
}
  • 3.v4l2-device.c:主要包含一些v4l2设备一些公共api,用于绑定v4l2设备结构体以及注册子设备节点。说白了,它存在的作用就是便于访问video设备的子设备,因为它的数据域包含了一个struct list_head subdevs;用于遍历子设备的。

  • 4.v4l2-fh.c:这fh就是file handle的意思,它存在的意义就是它就是消息队列。如下是三星4418 jpeg编码模块,可以发现结构体中嵌入了v4l2_fh。这些v4l2_fh结构体最终都会链接到strcut video_device中的struct list_head fh_list;链表中,用于存放其它模块发过来的消息。

struct s5p_jpeg_ctx {
	struct s5p_jpeg		*jpeg;
    //-------
	struct s5p_jpeg_q_data	out_q;
	struct s5p_jpeg_q_data	cap_q;
	struct v4l2_fh		fh;
	bool			hdr_parsed;
	struct v4l2_ctrl_handler ctrl_handler;
};
  • 5.v4l2-event.c:字面意思一看就知道是和事件有关系的,文件中主要包含事件入队列和出队列,查找等操作。事件队列都是在v4l2_fh结构体中描述的,而其中subscribed和available,分别是订阅事件列表和可用消息列表。
struct v4l2_fh {
  .....
	wait_queue_head_t	wait;
	struct list_head	subscribed; /* Subscribed events */
	struct list_head	available; /* Dequeueable event */
	unsigned int		navailable;
	u32			sequence;
}
  • 6.v4l2-ctrls.c:主要是视频设备控制操作的api集合。这里所有struct v4l2_ctrl都会链接到下面ctrls域中,而这个结构体又嵌套在struct v4l2_subdev大结构体中。就暂且把它理解成子设备的ioctl吧,道理都是一样的。
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;
};

这里再科普一下,所有的struct v4l2_ctrl都会注册到根设备的ctrl_handler链表中,如下所示,在每注册一个子设备时,都会调用v4l2_ctrl_add_handler,将子设备的struct v4l2_ctrl插入的根设备中,方便后续Ioctl时能快速查找的命令集合。

int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
				struct v4l2_subdev *sd)
{
	/* This just returns 0 if either of the two args is NULL */
	err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler);
}
  • 7.v4l2-subdev.c:所有操作子设备的集合。如下是标准的接口,这些接口会被字符设备对应的接口调用。v4l2-dev.c --->v4l2-subdev.c
const struct v4l2_file_operations v4l2_subdev_fops = {
	.owner = THIS_MODULE,
	.open = subdev_open,
	.unlocked_ioctl = subdev_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl32 = subdev_compat_ioctl32,
#endif
	.release = subdev_close,
	.poll = subdev_poll,
};
  • 8 videobuf2-core.c
    这是v4l2的申请内存,暴露给具体平台设备驱动标准接口,具体实现视具体平台而定。如下是vivi.c视频驱动操作buffer的接口,可以发现种类直接调用的videobuf2-core.c中的标准接口。由此可以发现他们调用层次为:
调用层次
device_driver(vivi.c)------->videobuf2-core.c--------->videobuf2-vmalloc.c
static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
	struct vivi_dev *dev = video_drvdata(file);
	return vb2_qbuf(&dev->vb_vidq, p);
}

static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
	struct vivi_dev *dev = video_drvdata(file);
	return vb2_dqbuf(&dev->vb_vidq, p, file->f_flags & O_NONBLOCK);
}

static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
	struct vivi_dev *dev = video_drvdata(file);
	return vb2_streamon(&dev->vb_vidq, i);
}

static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
	struct vivi_dev *dev = video_drvdata(file);
	return vb2_streamoff(&dev->vb_vidq, i);
}

上面介绍的buffer标准接口,下面都是各种申请buffer的方法,具体使用哪种buffer申请方式,视使用请看而定。如上面的vivi.c就是使用videobuf2-vmalloc接口申请内存的。

obj-$(CONFIG_VIDEOBUF2_MEMOPS)		+= videobuf2-memops.o
obj-$(CONFIG_VIDEOBUF2_VMALLOC)		+= videobuf2-vmalloc.o
obj-$(CONFIG_VIDEOBUF2_DMA_CONTIG)	+= videobuf2-dma-contig.o
obj-$(CONFIG_VIDEOBUF2_DMA_SG)		+= videobuf2-dma-sg.o
obj-$(CONFIG_VIDEOBUF2_CMA_PHYS)	+= videobuf2-cma-phys.o
obj-$(CONFIG_VIDEOBUF2_ION)		+= videobuf2-ion.o
obj-$(CONFIG_VIDEOBUF2_ION_NXP)		+= videobuf2-ion-nxp.o

三、重要的结构体

3.1 struct v4l2_device

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

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的控制方式。

/** struct v4l2_ctrl_handler - The control handler keeps track of all the
  * controls: both the controls owned by the handler and those inherited
  * from other handlers.
  * @lock:	Lock to control access to this handler and its controls.
  * @ctrls:	The list of controls owned by this handler.
  * @ctrl_refs:	The list of control references.
  * @cached:	The last found control reference. It is common that the same
  *		control is needed multiple times, so this is a simple
  *		optimization.
  * @buckets:	Buckets for the hashing. Allows for quick control lookup.
  * @nr_of_buckets: Total number of buckets in the array.
  * @error:	The error 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;
};

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

3.3 struct v4l2_ctrl

/** struct v4l2_ctrl - The control structure.
  * @node:	The list node.
  * @ev_subs:	The list of control event subscriptions.
  * @handler:	The handler that owns the control.
  * @cluster:	Point to start of cluster array.
  * @ncontrols:	Number of controls in cluster array.
  * @done:	Internal flag: set for each processed control.
  * @is_new:	Set when the user specified a new value for this control. It
  *		is also set when called from v4l2_ctrl_handler_setup. Drivers
  *		should never set this flag.
  * @is_private: If set, then this control is private to its handler and it
  *		will not be added to any other handlers. Drivers can set
  *		this flag.
  * @is_auto:   If set, then this control selects whether the other cluster
  *		members are in 'automatic' mode or 'manual' mode. This is
  *		used for autogain/gain type clusters. Drivers should never
  *		set this flag directly.
  * @has_volatiles: If set, then one or more members of the cluster are volatile.
  *		Drivers should never touch this flag.
  * @manual_mode_value: If the is_auto flag is set, then this is the value
  *		of the auto control that determines if that control is in
  *		manual mode. So if the value of the auto control equals this
  *		value, then the whole cluster is in manual mode. Drivers should
  *		never set this flag directly.
  * @ops:	The control ops.
  * @id:	The control ID.
  * @name:	The control name.
  * @type:	The control type.
  * @minimum:	The control's minimum value.
  * @maximum:	The control's maximum value.
  * @default_value: The control's default value.
  * @step:	The control's step value for non-menu controls.
  * @menu_skip_mask: The control's skip mask for menu controls. This makes it
  *		easy to skip menu items that are not valid. If bit X is set,
  *		then menu item X is skipped. Of course, this only works for
  *		menus with <= 32 menu items. There are no menus that come
  *		close to that number, so this is OK. Should we ever need more,
  *		then this will have to be extended to a u64 or a bit array.
  * @qmenu:	A const char * array for all menu items. Array entries that are
  *		empty strings ("") correspond to non-existing menu items (this
  *		is in addition to the menu_skip_mask above). The last entry
  *		must be NULL.
  * @flags:	The control's flags.
  * @cur:	The control's current value.
  * @val:	The control's new s32 value.
  * @val64:	The control's new s64 value.
  * @string:	The control's new string value.
  * @priv:	The control's private pointer. For use by the driver. It is
  *		untouched by the control framework. Note that this pointer is
  *		not freed when the control is deleted. Should this be needed
  *		then a new internal bitfield can be added to tell the framework
  *		to free this pointer.
  */
struct v4l2_ctrl {
	/* Administrative fields */
	struct list_head node;
	struct list_head ev_subs;
	struct v4l2_ctrl_handler *handler;
	struct v4l2_ctrl **cluster;
	unsigned ncontrols;
	unsigned int done:1;

	unsigned int is_new:1;
	unsigned int is_private:1;
	unsigned int is_auto:1;
	unsigned int has_volatiles:1;
	unsigned int manual_mode_value:8;

	const struct v4l2_ctrl_ops *ops;
	u32 id;
	const char *name;
	enum v4l2_ctrl_type type;
	s32 minimum, maximum, default_value;
	union {
		u32 step;
		u32 menu_skip_mask;
	};
	const char * const *qmenu;
	unsigned long flags;
	union {
		s32 val;
		s64 val64;
		char *string;
	} cur;
	union {
		s32 val;
		s64 val64;
		char *string;
	};
	void *priv;
};

上面最重要的就是

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

3.4 struct v4l2_ctrl_ops

/** 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的数据域。

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 list_head	subscribed; /* Subscribed events */
	struct list_head	available; /* Dequeueable event */
	unsigned int		navailable;
	u32			sequence;
};
  • 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分配内存。

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

3.6 struct video_device

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

struct video_device
{
#if defined(CONFIG_MEDIA_CONTROLLER)
	struct media_entity entity;
#endif
	/* device ops */
	const struct v4l2_file_operations *fops;

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

	/* Set either parent or v4l2_dev if your driver uses v4l2_device */
	struct device *parent;		/* device parent */
	struct v4l2_device *v4l2_dev;	/* v4l2_device parent */

	/* Control handler associated with this device node. May be NULL. */
	struct v4l2_ctrl_handler *ctrl_handler;

	/* Priority state. If NULL, then v4l2_dev->prio will be used. */
	struct v4l2_prio_state *prio;

	/* device info */
	char name[32];
	int vfl_type;
	/* 'minor' is set to -1 if the registration failed */
	int minor;
	u16 num;
	/* use bitops to set/clear/test flags */
	unsigned long flags;
	/* attribute to differentiate multiple indices on one physical device */
	int index;

	/* V4L2 file handles */
	spinlock_t		fh_lock; /* Lock for all v4l2_fhs */
	struct list_head	fh_list; /* List of struct v4l2_fh */

	int debug;			/* Activates debug level*/

	/* Video standard vars */
	v4l2_std_id tvnorms;		/* Supported tv norms */
	v4l2_std_id current_norm;	/* Current tvnorm */

	/* callbacks */
	void (*release)(struct video_device *vdev);

	/* ioctl callbacks */
	const struct v4l2_ioctl_ops *ioctl_ops;

	/* serialization lock */
	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

struct v4l2_ioctl_ops {
	/* ioctl callbacks */

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

	/* Priority handling */
	int (*vidioc_g_priority)   (struct file *file, void *fh,
				    enum v4l2_priority *p);
	int (*vidioc_s_priority)   (struct file *file, void *fh,
				    enum v4l2_priority p);

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

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

3.8 struct v4l2_subdev

struct v4l2_subdev {
#if defined(CONFIG_MEDIA_CONTROLLER)
	struct media_entity entity;
#endif
	struct list_head list;
	struct module *owner;
	u32 flags;
	struct v4l2_device *v4l2_dev;
	const struct v4l2_subdev_ops *ops;
	/* Never call these internal ops from within a driver! */
	const struct v4l2_subdev_internal_ops *internal_ops;
	/* The control handler of this subdev. May be NULL. */
	struct v4l2_ctrl_handler *ctrl_handler;
	/* name must be unique */
	char name[V4L2_SUBDEV_NAME_SIZE];
	/* can be used to group similar subdevs, value is driver-specific */
	u32 grp_id;
	/* pointer to private data */
	void *dev_priv;
	void *host_priv;
	/* subdev device node */
	struct video_device *devnode;
};
  • 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

  • 接口结构体介绍
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.此为用户自己定义的,同样被上一级调用

  • ioctl扩展
static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
	struct video_device *vdev = video_devdata(file);
	struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
	struct v4l2_fh *vfh = file->private_data;
	switch (cmd) {
		case VIDIOC_SUBDEV_ENUM_FRAME_SIZE: {//标准接口
		struct v4l2_subdev_frame_size_enum *fse = arg;

		if (fse->pad >= sd->entity.num_pads)
			return -EINVAL;

		return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh,
					fse);
	}
	//此处省略n行
		default:
		return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
	}

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

小结

经过上面的总结,可以大概知道设备从属关系如下所示:
在这里插入图片描述
上面只是简单记录了一下设备从属关系图,有了图就不会迷路。

  • 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子设备的拓扑结构。所以也不会记录太深入。

  • struct media_device
/**
 * struct media_device - Media device
 * @dev:	Parent device
 * @devnode:	Media device node
 * @model:	Device model name
 * @serial:	Device serial number (optional)
 * @bus_info:	Unique and stable device location identifier
 * @hw_revision: Hardware device revision
 * @driver_version: Device driver version
 * @entity_id:	ID of the next entity to be registered
 * @entities:	List of registered entities
 * @lock:	Entities list lock
 * @graph_mutex: Entities graph operation lock
 * @link_notify: Link state change notification callback
 *
 * This structure represents an abstract high-level media device. It allows easy
 * access to entities and provides basic media device-level support. The
 * structure can be allocated directly or embedded in a larger structure.
 *
 * The parent @dev is a physical device. It must be set before registering the
 * media device.
 *
 * @model is a descriptive model name exported through sysfs. It doesn't have to
 * be unique.
 */
struct media_device {
	/* dev->driver_data points to this struct. */
	struct device *dev;
	struct media_devnode devnode;

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

	u32 entity_id;
	struct list_head entities;

	/* Protects the entities list */
	spinlock_t lock;
	/* Serializes graph operations. */
	struct mutex graph_mutex;

	int (*link_notify)(struct media_link *link, u32 flags,
			   unsigned int notification);
};

部分域介绍

  • 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

/**
 * struct media_devnode - Media device node
 * @parent:	parent device
 * @minor:	device node minor number
 * @flags:	flags, combination of the MEDIA_FLAG_* constants
 *
 * This structure represents a media-related device node.
 *
 * The @parent is a physical device. It must be set by core or device drivers
 * before registering the node.
 */
struct media_devnode {
	/* device ops */
	const struct media_file_operations *fops;//(media_device.c)

	/* 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 *mdev);
};

此结构对应一个字符设备

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

4.3 struct media_file_operations

static const struct media_file_operations media_device_fops = {
	.owner = THIS_MODULE,
	.open = media_device_open,
	.ioctl = media_device_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl = media_device_compat_ioctl,
#endif /* CONFIG_COMPAT */
	.release = media_device_close,
};

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

五、总结

经过上面结构体的总结,初步了解了v4l2中常用的结构体的定义,也了解了部分域的使用。脑海里有个初步框架就可以了。继续学习吧!!!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值