感兴趣可以加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 file
的private_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中常用的结构体的定义,也了解了部分域的使用。脑海里有个初步框架就可以了。继续学习吧!!!!!