发布: [02-3]_videobuffer2缓冲区结构体

This commit is contained in:
weidongshan
2023-08-10 18:28:49 +08:00
parent 7355c13ef5
commit e0fa1d0990
13 changed files with 550 additions and 0 deletions

View File

@@ -3,6 +3,7 @@
参考资料:
* 深入理解linux内核v4l2框架之videobuf2https://blog.csdn.net/yyjsword/article/details/9243717
* V4L2框架-videobuf2https://blog.csdn.net/u013904227/article/details/81054611
## 1. 整体框架
@@ -107,5 +108,41 @@ APP操作buffer的示意图如下
#### 1.3.1 videobuffer2缓冲区结构体
![image-20230810163245420](pic/29_videobuffer2_struct.png)
分配流程:
* 驱动程序初始化时就构造了vb2_queue这是"buffer的队列",一开始里面没有"buffer"
* APP调用ioctl VIDIOC_REQBUFS向驱动申请N个buffer
* 驱动程序分配n(n<=N)个vb2_buffer结构体然后
* 对于普通摄像头还分配一个vb2_plane结构体、vb2_vmalloc_buf结构体最后分配存数据的buffer
* 对于多平面摄像头给每个vb2_buffer分配多个"vb2_plane结构体、vb2_vmalloc_buf结构体、存数据的buffer"
入队列流程:
* APP调用ioctl VIDIOC_QBUF
* 驱动程序根据其index找到vb2_buffer
* 把这个vb2_buffer放入链表vb2_queue.queued_list
硬件驱动接收到数据后比如URB传输完成后
* 从链表vb2_queue.queued_list取出vb2_buffer
* 把硬件数据存入vb2_buffer
* 把vb2_buffer放入链表vb2_queue.done_list
出队列流程:
* APP调用ioctl VIDIOC_DQBUF
* 驱动程序从链表vb2_queue.done_list取出并移除第1个vb2_buffer
* 驱动程序也把这个vb2_buffer从链表vb2_queue.queued_list移除
## 2. 从0编写一个虚拟的摄像头驱动

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View File

@@ -20,6 +20,8 @@ GIT简明教程http://download.100ask.org/tools/Software/git/how_to_use_git.h
## 2. 收到的建议
![image-20230804075519837](pic/28_suggestion.png)
subdev, media control, media framework, vb2 buffer的分配怎么轮转怎么跟硬件打交道然后图像给应用层用
有的摄像头有控制接口iic,spi还有没有控制接口的我们怎么处理
@@ -225,3 +227,236 @@ V4L2 capture overlay
## 4. videobuffer2
参考资料:
* 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
* https://www.linuxtv.org/downloads/v4l-dvb-apis-old/mmap.html
* V4l2应用框架-Videobuf2数据结构 https://blog.csdn.net/weixin_42581177/article/details/126582465
```c
static const struct vb2_ops airspy_vb2_ops = {
.queue_setup = airspy_queue_setup,
.buf_queue = airspy_buf_queue,
.start_streaming = airspy_start_streaming,
.stop_streaming = airspy_stop_streaming,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
};
const struct vb2_mem_ops vb2_vmalloc_memops = {
.alloc = vb2_vmalloc_alloc,
.put = vb2_vmalloc_put,
.get_userptr = vb2_vmalloc_get_userptr,
.put_userptr = vb2_vmalloc_put_userptr,
#ifdef CONFIG_HAS_DMA
.get_dmabuf = vb2_vmalloc_get_dmabuf,
#endif
.map_dmabuf = vb2_vmalloc_map_dmabuf,
.unmap_dmabuf = vb2_vmalloc_unmap_dmabuf,
.attach_dmabuf = vb2_vmalloc_attach_dmabuf,
.detach_dmabuf = vb2_vmalloc_detach_dmabuf,
.vaddr = vb2_vmalloc_vaddr,
.mmap = vb2_vmalloc_mmap,
.num_users = vb2_vmalloc_num_users,
};
s->vb_queue.ops = &airspy_vb2_ops; // call_qop,call_vb_qop
s->vb_queue.mem_ops = &vb2_vmalloc_memops; // call_ptr_memop
int vb2_queue_init(struct vb2_queue *q)
q->buf_ops = &v4l2_buf_ops;
```
### 4.1 APP request buf
```c
ioctl(fd, VIDIOC_REQBUFS, &rb);
--------------------------------
v4l_reqbufs(const struct v4l2_ioctl_ops *ops,
ops->vidioc_reqbufs(file, fh, p);
vb2_ioctl_reqbufs
res = vb2_core_reqbufs(vdev->queue, p->memory, &p->count);
num_buffers = min_t(unsigned int, *count, VB2_MAX_FRAME);
num_buffers = max_t(unsigned int, num_buffers, q->min_buffers_needed);
// 给你机会修改num_buffers,num_planes
ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes, plane_sizes, q->alloc_devs); // airspy_queue_setup
/* Finally, allocate buffers and video memory */
allocated_buffers =
__vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes);
ret = __vb2_buf_mem_alloc(vb);
call_ptr_memop(vb, alloc,
vb2_queue->mem_ops->alloc
vb2_vmalloc_alloc
buf->vaddr = vmalloc_user(buf->size);
ret = call_vb_qop(vb, buf_init, vb);
```
### 4.2 queue buffer
queue buffer关键点
* VB2的链表list_add_tail(&vb->queued_entry, &q->queued_list);
* 驱动自己维护的链表list_add_tail(&buf->list, &s->queued_bufs);
驱动的URB传输完成后
* 从链表queued_bufs取出buffer
* 把URB得到的数据填入buffer
* 调用vb2_buffer_donelist_add_tail(&vb->done_entry, &q->done_list);
dequeue buffer关键点
* 从链表done_entry取出并移除第1个vb
* 把它也从queued_list中移除
```c
ioctl(fd, VIDIOC_QBUF, &buf)
-------------------------------
v4l_qbuf
ops->vidioc_qbuf(file, fh, p);
vb2_ioctl_qbuf
vb2_qbuf(vdev->queue, p);
vb2_queue_or_prepare_buf(q, b, "qbuf");
vb2_core_qbuf(q, b->index, b);
list_add_tail(&vb->queued_entry, &q->queued_list);
vb->state = VB2_BUF_STATE_QUEUED;
/*
* If already streaming, give the buffer to driver for processing.
* If not, the buffer will be given to driver on next streamon.
*/
if (q->start_streaming_called)
__enqueue_in_driver(vb);
/* sync buffers */
for (plane = 0; plane < vb->num_planes; ++plane)
call_void_memop(vb, prepare, vb->planes[plane].mem_priv);
call_void_vb_qop(vb, buf_queue, vb);
airspy_buf_queue
list_add_tail(&buf->list, &s->queued_bufs);
if (pb)
call_void_bufop(q, fill_user_buffer, vb, pb);
```
### 4.3 dequeue buf
```shell
ioctl(fd, VIDIOC_DQBUF, &buf)
----------------------------------
v4l_dqbuf
ops->vidioc_dqbuf(file, fh, p);
vb2_ioctl_dqbuf
vb2_dqbuf(vdev->queue, p, file->f_flags & O_NONBLOCK);
ret = vb2_core_dqbuf(q, NULL, b, nonblocking);
ret = __vb2_get_done_vb(q, &vb, pb, nonblocking);
*vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry);
list_del(&(*vb)->done_entry);
call_void_vb_qop(vb, buf_finish, vb);
call_void_bufop(q, fill_user_buffer, vb, pb);
__vb2_dqbuf(vb);
```
### 4.3 stream on
```c
ioctl(fd, VIDIOC_STREAMON, &type)
------------------------------------
v4l_streamon
ops->vidioc_streamon(file, fh, *(unsigned int *)arg)
vb2_ioctl_streamon
vb2_streamon(vdev->queue, i);
vb2_core_streamon(q, type);
ret = vb2_start_streaming(q);
/*
* If any buffers were queued before streamon,
* we can now pass them to driver for processing.
*/
list_for_each_entry(vb, &q->queued_list, queued_entry)
__enqueue_in_driver(vb);
/* sync buffers */
for (plane = 0; plane < vb->num_planes; ++plane)
call_void_memop(vb, prepare, vb->planes[plane].mem_priv);
call_void_vb_qop(vb, buf_queue, vb);
airspy_buf_queue
/* Tell the driver to start streaming */
q->start_streaming_called = 1;
ret = call_qop(q, start_streaming, q, atomic_read(&q->owned_by_drv_count));
airspy_start_streaming // 分配提交URB
```
### 4.3 stream off
```c
ioctl(fd, VIDIOC_STREAMOFF, &type)
-----------------------------------
v4l_streamoff
ops->vidioc_streamoff(file, fh, *(unsigned int *)arg);
vb2_ioctl_streamoff
vb2_streamoff(vdev->queue, i);
vb2_core_streamoff(q, type);
__vb2_queue_cancel(q);
call_void_qop(q, stop_streaming, q);
airspy_stop_streaming // kill URB
call_void_vb_qop(vb, buf_finish, vb);
```
### 4.4 阻塞与唤醒
#### 4.4.1 阻塞
```shell
memset(fds, 0, sizeof(fds));
fds[0].fd = fd;
fds[0].events = POLLIN;
if (1 == poll(fds, 1, -1))
--------------------------------------
v4l2_poll
res = vdev->fops->poll(filp, poll);
vb2_fop_poll
res = vb2_poll(vdev->queue, file, wait);
vb2_core_poll(q, file, wait);
poll_wait(file, &q->done_wq, wait);
```
#### 4.4.2 唤醒
```shell
airspy_urb_complete
vb2_buffer_done(&fbuf->vb.vb2_buf, VB2_BUF_STATE_DONE);
```
### 4.6 读写buf

View File

@@ -796,6 +796,12 @@ git clone https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git
[02-2]_ioctl调用流程分析
```
* 2023.08.10 发布"摄像头驱动"
```shell
[02-3]_videobuffer2缓冲区结构体
```

View File

@@ -3,6 +3,7 @@
参考资料:
* 深入理解linux内核v4l2框架之videobuf2https://blog.csdn.net/yyjsword/article/details/9243717
* V4L2框架-videobuf2https://blog.csdn.net/u013904227/article/details/81054611
## 1. 整体框架
@@ -107,5 +108,41 @@ APP操作buffer的示意图如下
#### 1.3.1 videobuffer2缓冲区结构体
![image-20230810163245420](pic/29_videobuffer2_struct.png)
分配流程:
* 驱动程序初始化时就构造了vb2_queue这是"buffer的队列",一开始里面没有"buffer"
* APP调用ioctl VIDIOC_REQBUFS向驱动申请N个buffer
* 驱动程序分配n(n<=N)个vb2_buffer结构体然后
* 对于普通摄像头还分配一个vb2_plane结构体、vb2_vmalloc_buf结构体最后分配存数据的buffer
* 对于多平面摄像头给每个vb2_buffer分配多个"vb2_plane结构体、vb2_vmalloc_buf结构体、存数据的buffer"
入队列流程:
* APP调用ioctl VIDIOC_QBUF
* 驱动程序根据其index找到vb2_buffer
* 把这个vb2_buffer放入链表vb2_queue.queued_list
硬件驱动接收到数据后比如URB传输完成后
* 从链表vb2_queue.queued_list取出vb2_buffer
* 把硬件数据存入vb2_buffer
* 把vb2_buffer放入链表vb2_queue.done_list
出队列流程:
* APP调用ioctl VIDIOC_DQBUF
* 驱动程序从链表vb2_queue.done_list取出并移除第1个vb2_buffer
* 驱动程序也把这个vb2_buffer从链表vb2_queue.queued_list移除
## 2. 从0编写一个虚拟的摄像头驱动

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View File

@@ -20,6 +20,8 @@ GIT简明教程http://download.100ask.org/tools/Software/git/how_to_use_git.h
## 2. 收到的建议
![image-20230804075519837](pic/28_suggestion.png)
subdev, media control, media framework, vb2 buffer的分配怎么轮转怎么跟硬件打交道然后图像给应用层用
有的摄像头有控制接口iic,spi还有没有控制接口的我们怎么处理
@@ -225,3 +227,236 @@ V4L2 capture overlay
## 4. videobuffer2
参考资料:
* 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
* https://www.linuxtv.org/downloads/v4l-dvb-apis-old/mmap.html
* V4l2应用框架-Videobuf2数据结构 https://blog.csdn.net/weixin_42581177/article/details/126582465
```c
static const struct vb2_ops airspy_vb2_ops = {
.queue_setup = airspy_queue_setup,
.buf_queue = airspy_buf_queue,
.start_streaming = airspy_start_streaming,
.stop_streaming = airspy_stop_streaming,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
};
const struct vb2_mem_ops vb2_vmalloc_memops = {
.alloc = vb2_vmalloc_alloc,
.put = vb2_vmalloc_put,
.get_userptr = vb2_vmalloc_get_userptr,
.put_userptr = vb2_vmalloc_put_userptr,
#ifdef CONFIG_HAS_DMA
.get_dmabuf = vb2_vmalloc_get_dmabuf,
#endif
.map_dmabuf = vb2_vmalloc_map_dmabuf,
.unmap_dmabuf = vb2_vmalloc_unmap_dmabuf,
.attach_dmabuf = vb2_vmalloc_attach_dmabuf,
.detach_dmabuf = vb2_vmalloc_detach_dmabuf,
.vaddr = vb2_vmalloc_vaddr,
.mmap = vb2_vmalloc_mmap,
.num_users = vb2_vmalloc_num_users,
};
s->vb_queue.ops = &airspy_vb2_ops; // call_qop,call_vb_qop
s->vb_queue.mem_ops = &vb2_vmalloc_memops; // call_ptr_memop
int vb2_queue_init(struct vb2_queue *q)
q->buf_ops = &v4l2_buf_ops;
```
### 4.1 APP request buf
```c
ioctl(fd, VIDIOC_REQBUFS, &rb);
--------------------------------
v4l_reqbufs(const struct v4l2_ioctl_ops *ops,
ops->vidioc_reqbufs(file, fh, p);
vb2_ioctl_reqbufs
res = vb2_core_reqbufs(vdev->queue, p->memory, &p->count);
num_buffers = min_t(unsigned int, *count, VB2_MAX_FRAME);
num_buffers = max_t(unsigned int, num_buffers, q->min_buffers_needed);
// 给你机会修改num_buffers,num_planes
ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes, plane_sizes, q->alloc_devs); // airspy_queue_setup
/* Finally, allocate buffers and video memory */
allocated_buffers =
__vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes);
ret = __vb2_buf_mem_alloc(vb);
call_ptr_memop(vb, alloc,
vb2_queue->mem_ops->alloc
vb2_vmalloc_alloc
buf->vaddr = vmalloc_user(buf->size);
ret = call_vb_qop(vb, buf_init, vb);
```
### 4.2 queue buffer
queue buffer关键点
* VB2的链表list_add_tail(&vb->queued_entry, &q->queued_list);
* 驱动自己维护的链表list_add_tail(&buf->list, &s->queued_bufs);
驱动的URB传输完成后
* 从链表queued_bufs取出buffer
* 把URB得到的数据填入buffer
* 调用vb2_buffer_donelist_add_tail(&vb->done_entry, &q->done_list);
dequeue buffer关键点
* 从链表done_entry取出并移除第1个vb
* 把它也从queued_list中移除
```c
ioctl(fd, VIDIOC_QBUF, &buf)
-------------------------------
v4l_qbuf
ops->vidioc_qbuf(file, fh, p);
vb2_ioctl_qbuf
vb2_qbuf(vdev->queue, p);
vb2_queue_or_prepare_buf(q, b, "qbuf");
vb2_core_qbuf(q, b->index, b);
list_add_tail(&vb->queued_entry, &q->queued_list);
vb->state = VB2_BUF_STATE_QUEUED;
/*
* If already streaming, give the buffer to driver for processing.
* If not, the buffer will be given to driver on next streamon.
*/
if (q->start_streaming_called)
__enqueue_in_driver(vb);
/* sync buffers */
for (plane = 0; plane < vb->num_planes; ++plane)
call_void_memop(vb, prepare, vb->planes[plane].mem_priv);
call_void_vb_qop(vb, buf_queue, vb);
airspy_buf_queue
list_add_tail(&buf->list, &s->queued_bufs);
if (pb)
call_void_bufop(q, fill_user_buffer, vb, pb);
```
### 4.3 dequeue buf
```shell
ioctl(fd, VIDIOC_DQBUF, &buf)
----------------------------------
v4l_dqbuf
ops->vidioc_dqbuf(file, fh, p);
vb2_ioctl_dqbuf
vb2_dqbuf(vdev->queue, p, file->f_flags & O_NONBLOCK);
ret = vb2_core_dqbuf(q, NULL, b, nonblocking);
ret = __vb2_get_done_vb(q, &vb, pb, nonblocking);
*vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry);
list_del(&(*vb)->done_entry);
call_void_vb_qop(vb, buf_finish, vb);
call_void_bufop(q, fill_user_buffer, vb, pb);
__vb2_dqbuf(vb);
```
### 4.3 stream on
```c
ioctl(fd, VIDIOC_STREAMON, &type)
------------------------------------
v4l_streamon
ops->vidioc_streamon(file, fh, *(unsigned int *)arg)
vb2_ioctl_streamon
vb2_streamon(vdev->queue, i);
vb2_core_streamon(q, type);
ret = vb2_start_streaming(q);
/*
* If any buffers were queued before streamon,
* we can now pass them to driver for processing.
*/
list_for_each_entry(vb, &q->queued_list, queued_entry)
__enqueue_in_driver(vb);
/* sync buffers */
for (plane = 0; plane < vb->num_planes; ++plane)
call_void_memop(vb, prepare, vb->planes[plane].mem_priv);
call_void_vb_qop(vb, buf_queue, vb);
airspy_buf_queue
/* Tell the driver to start streaming */
q->start_streaming_called = 1;
ret = call_qop(q, start_streaming, q, atomic_read(&q->owned_by_drv_count));
airspy_start_streaming // 分配提交URB
```
### 4.3 stream off
```c
ioctl(fd, VIDIOC_STREAMOFF, &type)
-----------------------------------
v4l_streamoff
ops->vidioc_streamoff(file, fh, *(unsigned int *)arg);
vb2_ioctl_streamoff
vb2_streamoff(vdev->queue, i);
vb2_core_streamoff(q, type);
__vb2_queue_cancel(q);
call_void_qop(q, stop_streaming, q);
airspy_stop_streaming // kill URB
call_void_vb_qop(vb, buf_finish, vb);
```
### 4.4 阻塞与唤醒
#### 4.4.1 阻塞
```shell
memset(fds, 0, sizeof(fds));
fds[0].fd = fd;
fds[0].events = POLLIN;
if (1 == poll(fds, 1, -1))
--------------------------------------
v4l2_poll
res = vdev->fops->poll(filp, poll);
vb2_fop_poll
res = vb2_poll(vdev->queue, file, wait);
vb2_core_poll(q, file, wait);
poll_wait(file, &q->done_wq, wait);
```
#### 4.4.2 唤醒
```shell
airspy_urb_complete
vb2_buffer_done(&fbuf->vb.vb2_buf, VB2_BUF_STATE_DONE);
```
### 4.6 读写buf