android的camera学习(4)——V4L2框架及源码整理_android+v4l2(1)

struct v4l2\_device \*v4l2_dev;	//指向父设备
const struct v4l2\_subdev\_ops \*ops;	//提供一些控制v4l2设备的接口
const struct v4l2\_subdev\_internal\_ops \*internal_ops;	//向V4L2框架提供的接口函数
struct v4l2\_ctrl\_handler \*ctrl_handler;	//subdev控制接口
char name[V4L2_SUBDEV_NAME_SIZE];	//子设备名
u32 grp_id;
void \*dev_priv;
void \*host_priv;
struct video\_device \*devnode;	//video\_device 设备节点
struct device \*dev;
struct fwnode\_handle \*fwnode;
struct list\_head async_list;
struct v4l2\_async\_subdev \*asd;
struct v4l2\_async\_notifier \*notifier;
struct v4l2\_async\_notifier \*subdev_notifier;
struct v4l2\_subdev\_platform\_data \*pdata;

};


下面两个结构体是



const struct v4l2_subdev_ops *ops; //提供一些控制v4l2设备的接口
const struct v4l2_subdev_internal_ops *internal_ops; //向V4L2框架提供的接口函数


这两个属性的结构体:



struct v4l2_subdev_ops {
const struct v4l2_subdev_core_ops *core; //视频设备通用的操作:初始化、加载FW、上电和RESET等
const struct v4l2_subdev_tuner_ops *tuner; //tuner特有的操作
const struct v4l2_subdev_audio_ops *audio; //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;
};
/**
* struct v4l2_subdev_internal_ops - V4L2 subdev internal ops
*
* @registered: called when this subdev is registered. When called the v4l2_dev
* field is set to the correct v4l2_device.
*
* @unregistered: called when this subdev is unregistered. When called the
* v4l2_dev field is still set to the correct v4l2_device.
*
* @open: called when the subdev device node is opened by an application.
*
* @close: called when the subdev device node is closed. Please note that
* it is possible for @close to be called after @unregistered!
*
* @release: called when the last user of the subdev device is gone. This
* happens after the @unregistered callback and when the last open
* filehandle to the v4l-subdevX device node was closed. If no device
* node was created for this sub-device, then the @release callback
* is called right after the @unregistered callback.
* The @release callback is typically used to free the memory containing
* the v4l2_subdev structure. It is almost certainly required for any
* sub-device that sets the V4L2_SUBDEV_FL_HAS_DEVNODE flag.
*
* … note::
* Never call this from drivers, only the v4l2 framework can call
* these ops.
*/
struct v4l2_subdev_internal_ops {
/* 当subdev注册时被调用,读取IC的ID来进行识别 */
int (*registered)(struct v4l2_subdev *sd);
void (*unregistered)(struct v4l2_subdev *sd);
/* 当设备节点被打开时调用,通常会给设备上电和设置视频捕捉FMT */
int (*open)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh);
int (*close)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh);
/* 释放对应的subdev */
void (*release)(struct v4l2_subdev *sd);
};


调用 video\_device 结构体进行设备的注册



// kernel_imx/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;	// v4l 设备
struct cdev \*cdev;	// 字符设备

struct v4l2\_device \*v4l2_dev;		// v4l2\_device 父设备 
struct device \*dev_parent;	// 父设备

struct v4l2\_ctrl\_handler \*ctrl_handler;	// 设备节点的控制处理结构,可能为NULL

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 \*/
/\* ioctl回调函数集,提供file\_operations中的ioctl调用 \*/
void (\*release)(struct video\_device \*vdev);	// 释放函数
const struct v4l2\_ioctl\_ops \*ioctl_ops;	
DECLARE\_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE);

struct mutex \*lock;

ANDROID\_KABI\_RESERVE(1);
ANDROID\_KABI\_RESERVE(2);

};


**v4l2\_fh**是用来保存子设备的特有操作方法,也就是下面要分析到的v4l2\_ctrl\_handler,内核提供一组v4l2\_fh的操作方法,通常在打开设备节点时进行v4l2\_fh注册。



// kernel_imx/include/media/v4l2-fh.h

/**
* struct v4l2_fh - Describes a V4L2 file handler
*
* @list: list of file handlers
* @vdev: pointer to &struct video_device
* @ctrl_handler: pointer to &struct v4l2_ctrl_handler
* @prio: priority of the file handler, as defined by &enum v4l2_priority
*
* @wait: event’ s wait queue
* @subscribe_lock: serialise changes to the subscribed list; guarantee that
* the add and del event callbacks are orderly called
* @subscribed: list of subscribed events
* @available: list of events waiting to be dequeued
* @navailable: number of available events at @available list
* @sequence: event sequence number
*
* @m2m_ctx: pointer to &struct v4l2_m2m_ctx
*/
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;

};

/*这个头文件中还包含了如何去初始化子设备等、添加子设备、删除子设备*/
void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev);

/**
* v4l2_fh_add - Add the fh to the list of file handles on a video_device.
*
* @fh: pointer to &struct v4l2_fh
*
* … note::
* The @fh file handle must be initialised first.
*/
void v4l2_fh_add(struct v4l2_fh *fh);

/**
* v4l2_fh_open - Ancillary routine that can be used as the open() op
* of v4l2_file_operations.
*
* @filp: pointer to struct file
*
* It allocates a v4l2_fh and inits and adds it to the &struct video_device
* associated with the file pointer.
*/
int v4l2_fh_open(struct file *filp);

/**
* v4l2_fh_del - Remove file handle from the list of file handles.
*
* @fh: pointer to &struct v4l2_fh
*
* On error filp->private_data will be %NULL, otherwise it will point to
* the &struct v4l2_fh.
*
* … note::


**v4l2\_ctrl\_handler**是用于保存子设备控制方法集的结构体,对于视频设备这些ctrls包括设置亮度、饱和度、对比度和清晰度等,用链表的方式来保存ctrls,可以通过v4l2\_ctrl\_new\_std函数向链表添加ctrls。  
 其实下面的注释已经有对应的说明了



// kernel_imx/include/media/v4l2-ctrls.h

struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
const struct v4l2_ctrl_ops *ops,
u32 id, s64 min, s64 max, u64 step,
s64 def);

/**
* v4l2_ctrl_new_std_menu() - Allocate and initialize a new standard V4L2
* menu control.
*
* @hdl: The control handler. 已经初始化的v4l2_ctrl_handler的结构体
* @ops: The control ops. 已经设置好的v4l2_ctrl_ops结构体
* @id: The control ID. 对应的ID通过;ioctl传递的参数,也就是序列号
* @max: The control’s maximum value. 操作范围的最大值和最小值
* @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 <= 64 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 bit array.
* @def: The control’s default value.
*
* Same as v4l2_ctrl_new_std(), but @min is set to 0 and the @mask value
* determines which menu items are to be skipped.
*
* If @id refers to a non-menu control, then this function will return NULL.
*/


上面基本就是V4L2的框架图的一些源码解释,至于在代码中如何实现,还是需要根据代码去查看逻辑。主要是从注册设备去跟踪代码吧,因为工作中暂时没这些方面的需求,所以我这里也知识做一个简单的介绍。


## 四、V4l2的buf管理


因为我们可以通过v4l2的相关命令进行摄像头的数据流的抓取,在这过程中就涉及到一些文件的读写。  
 v4l2有三种:使用read/write方式;内存映射方式(mmap)和用户指针模式(USERPTR)。


**read和write** 是基本帧IO访问方式,通过read读取每一帧数据,数据需要在内核和用户之间拷贝,这种方式访问速度可能会非常慢;


**内存映射缓冲区 (V4L2\_MEMORY\_MMAP):** 是在内核空间开辟缓冲区,应用通过mmap()系统调用映射到用户地址空间。这些缓冲区可以是大而连续DMA缓冲区、通过vmalloc()创建的虚拟缓冲区,或者直接在设备的IO内存中开辟的缓冲区(如果硬件支持);


**用户空间缓冲区(V4L2\_MEMORY\_USERPTR)** 是用户空间的应用中开辟缓冲区,用户与内核空间之间交换缓冲区指针。很明显,在这种情况下是不需要mmap()调用的,但驱动为有效的支持用户空间缓冲区,其工作将也会更困难。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/720b45486f7042e8acf296919bf4a36a.png)   Camera sensor捕捉到图像数据通过并口或MIPI传输到CAMIF(camera interface),CAMIF可以对图像数据进行调整(翻转、裁剪和格式转换等)。然后DMA控制器设置DMA通道请求AHB将图像数据传到分配好的DMA缓冲区。待图像数据传输到DMA缓冲区之后,mmap操作把缓冲区映射到用户空间,应用就可以直接访问缓冲区的数据。而为了使设备支持流IO这种方式,v4l2需要实现对video buffer的管理,即实现:



/* vb2_queue代表一个videobuffer队列,vb2_buffer是这个队列中的成员,vb2_mem_ops是缓冲内存的操作函数集,vb2_ops用来管理队列 */
struct vb2_queue {
enum v4l2_buf_type type; //buffer类型
unsigned int io_modes; //访问IO的方式:mmap、userptr etc
const struct vb2_ops *ops; //buffer队列操作函数集合
const struct vb2_mem_ops *mem_ops; //buffer memory操作集合
struct vb2_buffer *bufs[VIDEO_MAX_FRAME]; //代表每个frame buffer
unsignedint num_buffers; //分配的buffer个数

};

/* vb2_mem_ops包含了内存映射缓冲区、用户空间缓冲区的内存操作方法 */
struct vb2_mem_ops {
void *(*alloc)(void *alloc_ctx, unsignedlong size); //分配视频缓存
void (*put)(void *buf_priv); //释放视频缓存

/\* 获取用户空间视频缓冲区指针 \*/
void \*(\*get_userptr)(void \*alloc_ctx, unsigned long vaddr, unsignedlong size, int write);
void (\*put_userptr)(void \*buf_priv);  //释放用户空间视频缓冲区指针
/\* 用于缓存同步 \*/
void (\*prepare)(void \*buf_priv);
void (\*finish)(void \*buf_priv);
/\* 缓存虚拟地址 & 物理地址 \*/
void \*(\*vaddr)(void \*buf_priv);
void \*(\*cookie)(void \*buf_priv);

unsignedint (\*num_users)(void \*buf_priv);  //返回当期在用户空间的buffer数
int (\*mmap)(void \*buf_priv, structvm_area_struct \*vma);  //把缓冲区映射到用户空间
..............

};

/* mem_ops由kernel自身实现并提供了三种类型的视频缓存区操作方法:连续的DMA缓冲区、集散的DMA缓冲区以及vmalloc创建的缓冲区,分别由videobuf2-dma-contig.c、videobuf2-dma-sg.c和videobuf-vmalloc.c文件实现,可以根据实际情况来使用。*/

/* vb2_ops是用来管理buffer队列的函数集合,包括队列和缓冲区初始化等 */
struct vb2_ops {
//队列初始化
int(*queue_setup)(struct vb2_queue *q, const struct v4l2_format *fmt,
unsigned int *num_buffers, unsigned int*num_planes,
unsigned int sizes[], void *alloc_ctxs[]);

//释放和获取设备操作锁
void(\*wait_prepare)(struct vb2\_queue \*q);
void(\*wait_finish)(struct vb2\_queue \*q);

//对buffer的操作
int(\*buf_init)(struct vb2\_buffer \*vb);
int(\*buf_prepare)(struct vb2\_buffer \*vb);
int(\*buf_finish)(struct vb2\_buffer \*vb);
void(\*buf_cleanup)(struct vb2\_buffer \*vb);

//开始/停止视频流
int(\*start_streaming)(struct vb2\_queue \*q, unsigned int count);
int(\*stop_streaming)(struct vb2\_queue \*q);

//把VB传递给驱动,以填充frame数据
void(\*buf_queue)(struct vb2\_buffer \*vb);

};



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

struct v4l2_buffer32 {
__u32 index; // buffer的序号
__u32 type; /* enum v4l2_buf_type 类型*/
__u32 bytesused; // 已经使用的byte数
__u32 flags;
__u32 field; /* enum v4l2_field */
struct {
compat_s64 tv_sec;
compat_s64 tv_usec;
} timestamp; // 时间戳,代表帧捕获的时间
struct v4l2_timecode timecode;
__u32 sequence;

/\* memory location \*/
__u32           memory; /\* enum v4l2\_memory \*/
union {
    __u32           offset;
    compat\_long\_t   userptr;
    compat\_caddr\_t  planes;
    __s32       fd;
} m;
__u32           length;	// 缓冲区大小,单位byte
__u32           reserved2;
__s32           request_fd;

};


## 五、V4L2的整体流程


因为我这里自己跟踪流程的时候我已经跟蒙了,所以这里我就后续学会了再补充,这里我是根据设备注册开始跟踪的



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

int __video_register_device(struct video_device *vdev,
enum vfl_devnode_type type,
int nr, int warn_if_nr_in_use,
struct module *owner)
{
int i = 0;
int ret;
int minor_offset = 0;
int minor_cnt = VIDEO_NUM_DEVICES;
const char *name_base;

/\* A minor value of -1 marks this video device as never

having been registered */
vdev->minor = -1;

/\* the release callback MUST be present \*/
if (WARN\_ON(!vdev->release))
	return -EINVAL;
/\* the v4l2\_dev pointer MUST be present \*/
if (WARN\_ON(!vdev->v4l2_dev))
	return -EINVAL;
/\* the device\_caps field MUST be set for all but subdevs \*/
if (WARN\_ON(type != VFL_TYPE_SUBDEV && !vdev->device_caps))
	return -EINVAL;

/\* v4l2\_fh support \*/
spin\_lock\_init(&vdev->fh_lock);
INIT\_LIST\_HEAD(&vdev->fh_list);

/\* Part 1: check device type \*/
switch (type) {
case VFL_TYPE_VIDEO:
	name_base = "video";
	break;
case VFL_TYPE_VBI:
	name_base = "vbi";
	break;
case VFL_TYPE_RADIO:
	name_base = "radio";
	break;
case VFL_TYPE_SUBDEV:
	name_base = "v4l-subdev";
	break;
case VFL_TYPE_SDR:
	/\* Use device name 'swradio' because 'sdr' was already taken. \*/
	name_base = "swradio";
	break;
case VFL_TYPE_TOUCH:
	name_base = "v4l-touch";
	break;
default:
	pr\_err("%s called with unknown type: %d\n",
	       \_\_func\_\_, type);
	return -EINVAL;
}

vdev->vfl_type = type;
vdev->cdev = NULL;
if (vdev->dev_parent == NULL)
	vdev->dev_parent = vdev->v4l2_dev->dev;
if (vdev->ctrl_handler == NULL)
	vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
/\* If the prio state pointer is NULL, then use the v4l2\_device

prio state. */
if (vdev->prio == NULL)
vdev->prio = &vdev->v4l2_dev->prio;

/\* Part 2: find a free minor, device node number and device index. \*/

#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* Keep the ranges for the first four types for historical
* reasons.
* Newer devices (not yet in place) should use the range
* of 128-191 and just pick the first free minor there
* (new style). */
switch (type) {
case VFL_TYPE_VIDEO:
minor_offset = 0;
minor_cnt = 64;
break;
case VFL_TYPE_RADIO:
minor_offset = 64;
minor_cnt = 64;
break;
case VFL_TYPE_VBI:
minor_offset = 224;
minor_cnt = 32;
break;
default:
minor_offset = 128;
minor_cnt = 64;
break;
}
#endif

/\* Pick a device node number \*/
mutex\_lock(&videodev_lock);
nr = devnode\_find(vdev, nr == -1 ? 0 : nr, minor_cnt);
if (nr == minor_cnt)
	nr = devnode\_find(vdev, 0, minor_cnt);
if (nr == minor_cnt) {
	pr\_err("could not get a free device node number\n");
	mutex\_unlock(&videodev_lock);
	return -ENFILE;
}

#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* 1-on-1 mapping of device node number to minor number */
i = nr;
#else
/* The device node number and minor numbers are independent, so
we just find the first free minor number. */
for (i = 0; i < VIDEO_NUM_DEVICES; i++)
if (video_devices[i] == NULL)
break;
if (i == VIDEO_NUM_DEVICES) {
mutex_unlock(&videodev_lock);
pr_err(“could not get a free minor\n”);
return -ENFILE;
}
#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值