Introduction

linux/samples/v4l/v4l2-pci-skeleton.c 有一个 driver 模板。

2.2 Structure of a V4L driver

device instances
  |
  +-sub-device instances
  |
  \-V4L2 device nodes
      |
      \-filehandle instances

2.3 Structure of the V4L2 framework

v4l2 framework 中分别使用以下几个结构体来依次代表上面的结构:

  • struct v4l2_device
  • struct v4l2_subdev
  • struct video_device
  • struct v4l2_fh

2.4 Video device

video device 用于抽象系统注册的 v4l2 /dev 设备节点,以便用户空间可以进行交互。

可以通过 video_device_alloc() 分配,也可以嵌入在更大的结构体中,这样则需要自定义 release 函数。

如果是嵌入在更大的结构体中,并且没有要释放的资源,可以使用 video_device_release_empty()

最后通过 video_register_device() 注册。

struct video_device 其中一些 fields 需要我们手动去初始化,包括:

fops, device_caps, queue, ioctl_ops, lock, prio.

  • video_device->v4l2_dev: 指向 v4l2_device。
  • video_device->name: 名称。
  • video_device->vfl_dir: VFL_DIR_RX/VFL_DIR_TX/VFL_DIR_M2M 表示 capture device/output device/m2m device.
  • video_device->fops: v4l2_file_operations, 文件操作函数。
  • video_device->ioctl_ops: ioctl 函数。
  • video_device->lock: 初始化为一个 mutex。如果设置了,那么所有的 ioctls 都会串行化。
  • video_device->queue: 指向 vb2_queue, 注意这边 m2m device 不用初始化该 field,因为 m2m device 每个打开 device node 的 app 都要维护一个 vb2_queue,而不是一个全局的 vb2_queue.
  • video_device->prio: 用来实现 VIDIOC_G/S_PRIORITY, 如果为 NULL, 则会使用 v4l2_device->prio

如果需要和 media framework 联合使用,需要初始化 entity 成员,并调用 media_entity_pads_init().

如果 v4l2 driver 使用 v4l2_ioctl_ops, 则 v4l2_file_operations 中的.unlock_ioctl 回调需要使用 video_ioctl2.

2.4.1 ioctls and locking

设置 video_device->lock 和 video_device->queue->lock 两个 mutex.

这样 v4l2 core 会自动调用 video_device->lock 和 video_device->queue->lock 来串行化 v4l2 ioctls 的执行。

video_device->queue->lock 用来单独串行化 v4l2 buffer 相关的 ioctls,这样和其他 ioctls 可以并行运行。

如果使用的是 queue->lock,那么 vb2_ops 中的 wait_prepare/finish 回调可以使用 vb2_ops_wait_prepare/finish() helper function.

2.4.2 Video device registration

err = video_register_device(vdev, VFL_TYPE_VIDEO, -1);

video_register_device() 最后一个参数是生成设备节点的编号,如果为 -1 则由内核自动分配,也可以自行指定一个编号。

不同的设备类型会生成不同的设备节点:

2.4.3 Video device debugging

/sys/class/video4linux/<devX>/dev_debug写入数值:

2.4.4 Video device cleanup

video_unregister_device(), 或者使用 vb2_video_unregister_device()

在这之前别忘记 clean up media_entity:

media_entity_cleanup (&vdev->entity)

2.4.5 helper functions

static inline void *video_get_drvdata(struct video_device *vdev)
static inline void video_set_drvdata(struct video_device *vdev, void *data)
struct video_device *video_devdata(struct file *file)

2.4.6 functions and data structures

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[64];
	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:

fops: v4l2 file operations.
device_caps: 设备支持的能力,在 videodev2.h 中定义。
v4l2_dev: 上层的 v4l2_device 设备。
dev_parent: 上层的 device 设备,如果为 NULL 在 video_register_device 中会指向 v4l2_dev->dev
ctrl_handler: 如果为 NULL, 在 video_register_device 中会指向 v4l2_dev->ctrl_handler.
queue: 和该 device node 相关的 vb2_queue 结构体。
prio: 如果为 NULL, 在 video_register_device 中会指向 v4l2_dev->prio .
name: video device name.
vfl_type: enum vfl_devnode_type, v4l2 设备的类型,在 video_register_device 中传入。
vfl_dir: enum vfl_devnode_direction, 表示 v4l2 设备的方向,RX(capture device)/TX(output device)/M2M(mem2mem, codec).
minor: device node 次设备号。
num: video device node 的编号,/dev/videoX 的 X, 在 video_register_device 最后一个参数传入。
flags: enum v4l2_video_device_flags, 一些辅助 flags.
index: 一个物理设备对应多个 v4l2 设备节点,分别的 index. 每次调用 video_register_device() 都会增加 1.
fh_lock: 用来 lock v4l2_fhs.
fh_list: v4l2_fh 链表。
dev_debug: userspace 通过 sysfs 设置的 debug level. /sys/class/video4linux/video0/dev_debug
tvnorms: 支持的电视标准 PAL/NTSC/SECAM.
release: 释放函数。在 video_device 没有 subclass 的情况下可以使用 video_device_release().
如果 video_device subclass 或者是 static 分配的,不需要释放内存使用 video_device_release_empty().
ioctl_ops: v4l2 file ioctl operations.
valid_ioctls: 支持的 ioctrl bitmap.
lock: 用来串行化 v4l2 device 的 unlock_ioctl.

struct v4l2_ioctl_ops {
	/* 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_out)(struct file *file, void *fh,
				       struct v4l2_fmtdesc *f);
	int (*vidioc_enum_fmt_vid_cap_mplane)(struct file *file, void *fh,
					     struct v4l2_fmtdesc *f);

			
	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_streamon)(struct file *file, void *fh,
			       enum v4l2_buf_type i);
	int (*vidioc_streamoff)(struct file *file, void *fh,
				enum v4l2_buf_type i);
	int (*vidioc_try_fmt_vid_cap_mplane)(struct file *file, void *fh,
					     struct v4l2_format *f);
	int (*vidioc_s_fmt_meta_cap)(struct file *file, void *fh,
				     struct v4l2_format *f);
	long (*vidioc_default)(struct file *file, void *fh,
			       bool valid_prio, unsigned int cmd, void *arg);
}

vidioc_default: private ioctls 在这里实现。


video device 相关的 APIs, v4l2-dev.h:

// priority 相关
void v4l2_prio_init(struct v4l2_prio_state *global);
int v4l2_prio_change(struct v4l2_prio_state *global, enum v4l2_priority *local,
		     enum v4l2_priority new);
void v4l2_prio_open(struct v4l2_prio_state *global, enum v4l2_priority *local);
void v4l2_prio_close(struct v4l2_prio_state *global, enum v4l2_priority local);
enum v4l2_priority v4l2_prio_max(struct v4l2_prio_state *global);
int v4l2_prio_check(struct v4l2_prio_state *global, enum v4l2_priority local);

// media entity to video device
#define media_entity_to_video_device(__entity) \
	container_of(__entity, struct video_device, entity)
// device to video device, drvier 中基本没用上
#define to_video_device(cd) container_of(cd, struct video_device, dev)

// video device 注册相关
static inline int video_register_device(struct video_device *vdev,
				enum vfl_devnode_type type, int nr);
void video_unregister_device(struct video_device *vdev);
struct video_device *video_device_alloc(void);
void video_device_release(struct video_device *vdev);
void video_device_release_empty(struct video_device *vdev);

// private data 相关
static inline void *video_get_drvdata(struct video_device *vdev)
static inline void video_set_drvdata(struct video_device *vdev, void *data)
struct video_device *video_devdata(struct file *file);
static inline void *video_drvdata(struct file *file)

// pipeline 相关
int video_device_pipeline_start(struct video_device *vdev,
					     struct media_pipeline *pipe);
void video_device_pipeline_stop(struct video_device *vdev);
int video_device_pipeline_alloc_start(struct video_device *vdev);
struct media_pipeline *video_device_pipeline(struct video_device *vdev);