2.2 Structure of a V4L driver

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

2.4 Video device

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

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

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

最后通过 video_register_device() 注册。

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 中定义。
dev: cdev: 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.

其中一些 fields 需要我们手动去初始化,包括 fops, device_caps, v4l2_dev, queue, name, vfl_dir, ioctl_ops, lock.

如果需要和 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 可以并行运行。

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 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 在这里实现。

v4l2-dev.h:


多平面的像素格式:

struct v4l2_pix_format_mplane {
	__u32				width;
	__u32				height;
	__u32				pixelformat;
	__u32				field;
	__u32				colorspace;
	struct v4l2_plane_pix_format	plane_fmt[VIDEO_MAX_PLANES];
	__u8				num_planes;
	__u8				flags;
	 union {
		__u8				ycbcr_enc;
		__u8				hsv_enc;
	};
	__u8				quantization;
	__u8				xfer_func;
}

width: 图像宽度。 height: 图像高度。 pixelformat: 像素 fourcc 格式。 field: enum v4l2_field,场序。 colorspace: enum v4l2_colorspace, 颜色空间。 plane_fmt: 每个平面具体的信息。 num_planes: 平面数量。 flags: V4L2_PIX_FMT_FLAG_XXX, 标志位。 ycbcr_enc: enum v4l2_ycbcr_encoding, yuv 色彩编码。 quantization: enum v4l2_quantization, 采样方式,full range/limited range。 xfer_func: enum v4l2_xfer_func, 色彩传输函数。

struct v4l2_plane_pix_format {
	__u32		sizeimage;
	__u32		bytesperline;
}

sizeimage: 平面最大包含的字节数。 bytesperline: 每个平面一行的字节数。

struct v4l2_format_info {
	u32 format;
	u8 pixel_enc;
	u8 mem_planes;
	u8 comp_planes;
	u8 bpp[4];
	u8 bpp_div[4];
	u8 hdiv;
	u8 vdiv;
	u8 block_w[4];
	u8 block_h[4];
};

format: 像素格式 fourcc code。
pixel_enc: 像素编码,enum v4l2_pixel_encoding, rgb/yuv/bayer。
mem_planes: memory 平面数量,包括 alpha plane,可以有 1-4 个平面,比如 nv12, 只有一个连续的 memory 平面。
comp_planes: component 平面数量,包括 alpha plane,可以有 1-4 个平面,比如 nv12,有两个 component 平面。
bpp: 每个平面每个像素的位数。
bpp_div: 支持分数像素位数的除数。
hdiv: 水平方向的缩放因子。
vdiv: 垂直方向的缩放因子。
block_w: 每个平面每个像素的宽度。 block_h: 每个平面每个像素的高度。

video device 相关的 APIs:

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

int video_device_pipeline_start(struct video_device *vdev,
					     struct media_pipeline *pipe);
void video_device_pipeline_stop(struct video_device *vdev);
struct media_pipeline *video_device_pipeline(struct video_device *vdev);