mirror of
https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git
synced 2025-11-30 20:11:03 +08:00
tmp update
This commit is contained in:
@@ -142,6 +142,211 @@ APP操作buffer的示意图如下:
|
||||
|
||||
|
||||
|
||||
#### 1.3.2 三类操作结构体
|
||||
|
||||
V4L2中使用vb2_queue来管理缓冲区,里面有3个操作结构体:
|
||||
|
||||

|
||||
|
||||
这3个操作结构体的作用为:
|
||||
|
||||
* `const struct vb2_ops *ops`:驱动相关的回调函数,通过下面几个宏来调用
|
||||

|
||||
* `const struct vb2_mem_ops *mem_ops`:分配内存用的回调函数
|
||||
* `const struct vb2_buf_ops *buf_ops`:在用户空间、内核空间之间传递buffer信息
|
||||
|
||||
|
||||
|
||||
#### 1.3.3 vb2_ops
|
||||
|
||||
`struct vb2_ops`示例如下:
|
||||
|
||||

|
||||
|
||||
原型如下:
|
||||
|
||||
```c
|
||||
struct vb2_ops {
|
||||
int (*queue_setup)(struct vb2_queue *q,
|
||||
unsigned int *num_buffers, unsigned int *num_planes,
|
||||
unsigned int sizes[], struct device *alloc_devs[]);
|
||||
|
||||
void (*wait_prepare)(struct vb2_queue *q);
|
||||
void (*wait_finish)(struct vb2_queue *q);
|
||||
|
||||
int (*buf_init)(struct vb2_buffer *vb);
|
||||
int (*buf_prepare)(struct vb2_buffer *vb);
|
||||
void (*buf_finish)(struct vb2_buffer *vb);
|
||||
void (*buf_cleanup)(struct vb2_buffer *vb);
|
||||
|
||||
int (*start_streaming)(struct vb2_queue *q, unsigned int count);
|
||||
void (*stop_streaming)(struct vb2_queue *q);
|
||||
|
||||
void (*buf_queue)(struct vb2_buffer *vb);
|
||||
};
|
||||
```
|
||||
|
||||
各成员的作用为:
|
||||
|
||||
| vb2_ops结构体成员 | 作用 |
|
||||
| ----------------- | ------------------------------------------------------------ |
|
||||
| queue_setup | APP调用ioctl VIDIOC_REQBUFS或VIDIOC_CREATE_BUFS时,<br />驱动程序在分配内存之前,会调用此函数。<br />作用:通过它来询问驱动程序"你需要多少个buffer?每个buffer需要多少个plane?"<br />这个函数被调用2次:第1次用来表明驱动程序对buffer的需求,但是不一定能全部分配这些buffer,当分配出buffer后,再调用第2次以验证"这些buffer是否足够"。 |
|
||||
| wait_prepare | 释放驱动自己的互斥锁 |
|
||||
| wait_finish | 申请驱动自己的互斥锁 |
|
||||
| buf_init | 分配vb2_buffer及它内部存储数据的buffer后,使用buf_init进行驱动相关的初始化 |
|
||||
| buf_prepare | APP调用ioctl VIDIOC_QBUF或VIDIOC_PREPARE_BUF时,驱动程序会在执行硬件操作前,调用此函数进行必要的初始化。 |
|
||||
| buf_finish | APP调用ioctl VIDIOC_DQBUF后,在驱动程序返回用户空间之前,会调用此函数,可以在这个函数里修改buffer。或者驱动程序内部停止或暂停streaming时,也会调用此函数。 |
|
||||
| buf_cleanup | 在buffer被释放前调用,驱动程序在这个函数里执行额外的清除工作。 |
|
||||
| start_streaming | 驱动相关的"启动streaming"函数 |
|
||||
| stop_streaming | 驱动相关的"停止streaming"函数 |
|
||||
| buf_queue | 把buffer传送给驱动,驱动获得数据、填充好buffer后会调用vb2_buffer_done函数返还buffer。 |
|
||||
|
||||
|
||||
|
||||
#### 1.3.4 vb2_mem_ops
|
||||
|
||||
`struct vb2_mem_ops`示例如下:
|
||||
|
||||

|
||||
|
||||
原型如下:
|
||||
|
||||
```c
|
||||
struct vb2_mem_ops {
|
||||
void *(*alloc)(struct device *dev, unsigned long attrs,
|
||||
unsigned long size,
|
||||
enum dma_data_direction dma_dir,
|
||||
gfp_t gfp_flags);
|
||||
void (*put)(void *buf_priv);
|
||||
struct dma_buf *(*get_dmabuf)(void *buf_priv, unsigned long flags);
|
||||
|
||||
void *(*get_userptr)(struct device *dev, unsigned long vaddr,
|
||||
unsigned long size,
|
||||
enum dma_data_direction dma_dir);
|
||||
void (*put_userptr)(void *buf_priv);
|
||||
|
||||
void (*prepare)(void *buf_priv);
|
||||
void (*finish)(void *buf_priv);
|
||||
|
||||
void *(*attach_dmabuf)(struct device *dev,
|
||||
struct dma_buf *dbuf,
|
||||
unsigned long size,
|
||||
enum dma_data_direction dma_dir);
|
||||
void (*detach_dmabuf)(void *buf_priv);
|
||||
int (*map_dmabuf)(void *buf_priv);
|
||||
void (*unmap_dmabuf)(void *buf_priv);
|
||||
|
||||
void *(*vaddr)(void *buf_priv);
|
||||
void *(*cookie)(void *buf_priv);
|
||||
|
||||
unsigned int (*num_users)(void *buf_priv);
|
||||
|
||||
int (*mmap)(void *buf_priv, struct vm_area_struct *vma);
|
||||
};
|
||||
```
|
||||
|
||||
各成员的作用为:
|
||||
|
||||
| vb2_mem_ops结构体成员 | 作用 |
|
||||
| --------------------- | ------------------------------------------------------------ |
|
||||
| alloc | 分配真正用于存储视频数据的buffer,可能还分配私有数据 |
|
||||
| put | 通知分配器:这块buffer不再使用了。通常会释放内存。 |
|
||||
| get_dmabuf | 申请驱动自己的互斥锁 |
|
||||
| get_userptr | 如果存储视频数据的buffer是userptr(由APP提供),那么APP调用ioctl VIDIOC_QBUF时,需要传入APP的buffer指针。驱动程序内部通过此函数把用户空间的buffer映射到内核空间。 |
|
||||
| put_userptr | 通知分配器:这块USERPTR缓冲区不再使用 |
|
||||
| attach_dmabuf | 如果存储视频数据的buffer是DMA Buf,那么在把这个buffer放入队列前会调用此函数:记录这个DMA Buf。 |
|
||||
| detach_dmabuf | 不再使用这个DMA Buf时,做些清理工作(比如在attach_dmabuf里分配了数据,就在这里释放掉) |
|
||||
| map_dmabuf | 把DMA Buf映射到内核空间 |
|
||||
| unmap_dmabuf | map_dmabuf的反操作 |
|
||||
| prepare | 每当buffer被从用户空间传递到驱动时,此函数被调用,可以用来做某些同步操作。可选。 |
|
||||
| finish | 每当buffer被从驱动传递到用户空间时,此函数被调用,可以用来做某些同步操作。可选。 |
|
||||
| vaddr | 返回这块内存的内核空间地址 |
|
||||
| cookie | 没什么用 |
|
||||
| num_users | 返回这块内存的引用计数 |
|
||||
| mmap | 把这块内存,映射到用户空间 |
|
||||
|
||||
|
||||
|
||||
#### 1.3.5 vb2_buf_ops
|
||||
|
||||
`struct vb2_buf_ops`示例如下:
|
||||
|
||||

|
||||
|
||||
原型如下:
|
||||
|
||||
```c
|
||||
struct vb2_buf_ops {
|
||||
int (*verify_planes_array)(struct vb2_buffer *vb, const void *pb);
|
||||
void (*fill_user_buffer)(struct vb2_buffer *vb, void *pb);
|
||||
int (*fill_vb2_buffer)(struct vb2_buffer *vb, const void *pb,
|
||||
struct vb2_plane *planes);
|
||||
void (*copy_timestamp)(struct vb2_buffer *vb, const void *pb);
|
||||
};
|
||||
```
|
||||
|
||||
各成员的作用为:
|
||||
|
||||
| vb2_buf_ops结构体成员 | 作用 |
|
||||
| --------------------- | ------------------------------------------------------------ |
|
||||
| verify_planes_array | APP调用ioctl VIDIOC_DQBUF时,在驱动内部会调用此函数,用来验证这个buffer含有足够多的plane。 |
|
||||
| fill_user_buffer | 使用驱动的vb2_buffer结构体来填充一个v4l2_buffer结构体,用来给用户空间提供更多信息。APP调用ioctl VIDIOC_QUERYBUF、VIDIOC_PREPARE_BUF、VIDIOC_QBUF、VIDIOC_DQBUF时,都会传入一个v4l2_buffer结构体。 |
|
||||
| fill_vb2_buffer | APP调用ioctl VIDIOC_QBUF时,传入一个v4l2_buffer结构体,驱动里会用它来填充vb2_buffer结构体。 |
|
||||
| copy_timestamp | APP调用ioctl VIDIOC_QBUF时,传入一个v4l2_buffer结构体,用户程序可以在它的timestamp里记下时间。驱动程序可以调用此函数把timestamp写入vb2_buffer.timestamp里。 |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```c
|
||||
APP ioctl VIDIOC_PREPARE_BUF
|
||||
------------------------------
|
||||
v4l2_ioctl_ops.vidioc_prepare_buf
|
||||
vb2_ioctl_prepare_buf
|
||||
vb2_prepare_buf(vdev->queue, p);
|
||||
vb2_core_prepare_buf(q, b->index, b);
|
||||
ret = __buf_prepare(vb, pb);
|
||||
ret = __qbuf_mmap(vb, pb);
|
||||
if (pb)
|
||||
ret = call_bufop(vb->vb2_queue, fill_vb2_buffer,
|
||||
vb, pb, vb->planes);
|
||||
__fill_vb2_buffer
|
||||
return ret ? ret : call_vb_qop(vb, buf_prepare, vb);
|
||||
|
||||
/* Fill buffer information for the userspace */
|
||||
call_void_bufop(q, fill_user_buffer, vb, pb);
|
||||
__fill_v4l2_buffer
|
||||
|
||||
vb2_core_dqbuf
|
||||
ret = __vb2_get_done_vb(q, &vb, pb, nonblocking);
|
||||
ret = __vb2_wait_for_done_vb(q, nonblocking);
|
||||
call_void_qop(q, wait_prepare, q);
|
||||
wait_event_interruptible
|
||||
call_void_qop(q, wait_finish, q);
|
||||
*vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry);
|
||||
if (pb)
|
||||
ret = call_bufop(q, verify_planes_array, *vb, pb);
|
||||
__verify_planes_array_core
|
||||
__verify_planes_array(vb, pb);
|
||||
if (!ret)
|
||||
list_del(&(*vb)->done_entry);
|
||||
call_void_vb_qop(vb, buf_finish, vb);
|
||||
|
||||
/* go back to dequeued state */
|
||||
__vb2_dqbuf(vb);
|
||||
call_void_memop(vb, unmap_dmabuf, vb->planes[i].mem_priv);
|
||||
vb2_vmalloc_unmap_dmabuf
|
||||
dma_buf_vunmap(buf->dbuf, buf->vaddr);
|
||||
|
||||
vb2_core_qbuf
|
||||
if (pb)
|
||||
call_void_bufop(q, copy_timestamp, vb, pb);
|
||||
__copy_timestamp
|
||||
vb->timestamp = timeval_to_ns(&b->timestamp);
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 2. 从0编写一个虚拟的摄像头驱动
|
||||
|
||||
BIN
IMX6ULL/doc_pic/13_V4L2/pic/30_vb2_queue_ops.png
Normal file
BIN
IMX6ULL/doc_pic/13_V4L2/pic/30_vb2_queue_ops.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
BIN
IMX6ULL/doc_pic/13_V4L2/pic/31_vbs_ops_example.png
Normal file
BIN
IMX6ULL/doc_pic/13_V4L2/pic/31_vbs_ops_example.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
BIN
IMX6ULL/doc_pic/13_V4L2/pic/32_call_vbs_ops.png
Normal file
BIN
IMX6ULL/doc_pic/13_V4L2/pic/32_call_vbs_ops.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
BIN
IMX6ULL/doc_pic/13_V4L2/pic/33_vb2_mem_ops_example.png
Normal file
BIN
IMX6ULL/doc_pic/13_V4L2/pic/33_vb2_mem_ops_example.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
BIN
IMX6ULL/doc_pic/13_V4L2/pic/34_vb2_buf_ops_example.png
Normal file
BIN
IMX6ULL/doc_pic/13_V4L2/pic/34_vb2_buf_ops_example.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
@@ -231,10 +231,18 @@ V4L2 capture overlay
|
||||
|
||||
参考资料:
|
||||
|
||||
* https://www.linuxtv.org/downloads/v4l-dvb-apis-new/
|
||||
* https://www.linuxtv.org/downloads/v4l-dvb-apis-old/vidioc-create-bufs.html
|
||||
* https://www.linuxtv.org/downloads/v4l-dvb-apis-old/buffer.html#v4l2-memory
|
||||
* 3种buffer:https://www.linuxtv.org/downloads/v4l-dvb-apis-old/index.html
|
||||
* https://www.linuxtv.org/downloads/v4l-dvb-apis-old/buffer.html#v4l2-memory
|
||||
* https://www.linuxtv.org/downloads/v4l-dvb-apis-old/mmap.html
|
||||
* https://www.linuxtv.org/downloads/v4l-dvb-apis-old/userp.html
|
||||
* https://www.linuxtv.org/downloads/v4l-dvb-apis-old/dmabuf.html
|
||||
* https://www.linuxtv.org/downloads/v4l-dvb-apis-old/vidioc-expbuf.html
|
||||
* 基于Streaming I/O的V4L2设备使用: https://blog.csdn.net/coroutines/article/details/70141086
|
||||
* https://www.linuxtv.org/downloads/v4l-dvb-apis-old/mmap.html
|
||||
* V4l2应用框架-Videobuf2数据结构 https://blog.csdn.net/weixin_42581177/article/details/126582465
|
||||
* user ptr:https://github.com/h4tr3d/v4l2-capture-complex/blob/master/main.cpp
|
||||
|
||||
|
||||
|
||||
@@ -460,3 +468,19 @@ airspy_urb_complete
|
||||
|
||||
|
||||
|
||||
### 4.7 vidioc_prepare_buf
|
||||
|
||||
```c
|
||||
v4l2_ioctl_ops.vidioc_prepare_buf
|
||||
vb2_ioctl_prepare_buf
|
||||
vb2_prepare_buf(vdev->queue, p);
|
||||
vb2_core_prepare_buf(q, b->index, b)
|
||||
call_void_bufop(q, fill_user_buffer, vb, pb);
|
||||
__fill_v4l2_buffer
|
||||
/**
|
||||
* __fill_v4l2_buffer() - fill in a struct v4l2_buffer with information to be
|
||||
* returned to userspace
|
||||
*/
|
||||
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user