发布:[03-1]_回顾与编写驱动框架,[03-2]_虚拟摄像头驱动编程_ioctl代码,[03-3]_虚拟摄像头驱动编程_buffer代码

This commit is contained in:
weidongshan
2023-09-04 18:55:59 +08:00
parent 58a385c73e
commit 8b94fc7f9f
11 changed files with 922 additions and 8 deletions

View File

@@ -508,7 +508,3 @@ APP ioctl VIDIOC_DQBUF
## 2. 从0编写一个虚拟的摄像头驱动

View File

@@ -0,0 +1,102 @@
# 从0编写一个虚拟摄像头驱动 #
参考资料:
* 深入理解linux内核v4l2框架之videobuf2https://blog.csdn.net/yyjsword/article/details/9243717
* V4L2框架-videobuf2https://blog.csdn.net/u013904227/article/details/81054611
## 1. 回顾与编写程序框架
本节视频对应的代码:
```shell
doc_and_source_for_drivers\
IMX6ULL\source\
13_V4L2\
06_virtual_driver\
01_virtual_driver_framework
```
参考《1.3.2 videobuffer2的3个ops》里面完整的注册流程。
完整的注册流程(参考`drivers\media\usb\airspy\airspy.c`)
```shell
static struct video_device airspy_template = {
.name = "AirSpy SDR",
.release = video_device_release_empty,
.fops = &airspy_fops,
.ioctl_ops = &airspy_ioctl_ops,
};
/* 构造一个vb2_queue */
/* Init videobuf2 queue structure */
s->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
s->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
s->vb_queue.drv_priv = s;
s->vb_queue.buf_struct_size = sizeof(struct airspy_frame_buf);
s->vb_queue.ops = &airspy_vb2_ops; // vb2_ops, 硬件相关的操作函数
s->vb_queue.mem_ops = &vb2_vmalloc_memops; // vb2_mem_ops, 辅助结构体,用于mem ops(alloc、mmap)
s->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
ret = vb2_queue_init(&s->vb_queue);
q->buf_ops = &v4l2_buf_ops; // vb2_buf_ops, 用于APP和驱动传递参数
// 分配/设置video_device结构体
s->vdev = airspy_template;
s->vdev.queue = &s->vb_queue; // 指向前面构造的vb2_queue
// 初始化一个v4l2_device结构体(起辅助作用)
/* Register the v4l2_device structure */
s->v4l2_dev.release = airspy_video_release;
ret = v4l2_device_register(&intf->dev, &s->v4l2_dev);
// video_device和4l2_device建立联系
s->vdev.v4l2_dev = &s->v4l2_dev;
// 注册video_device结构体
ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1);
__video_register_device
// 根据次设备号把video_device结构体放入数组
video_device[vdev->minor] = vdev;
// 注册字符设备驱动程序
vdev->cdev->ops = &v4l2_fops;
vdev->cdev->owner = owner;
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
```
## 2. 编写硬件相关代码
本节视频对应的代码:
```shell
doc_and_source_for_drivers\
IMX6ULL\source\
13_V4L2\
06_virtual_driver\
02_virtual_driver_hardware
```
### 2.1 ioctl相关的代码
### 2.2 buffer相关的代码
### 2.3 数据传输
## 3. 上机测试

View File

@@ -0,0 +1,128 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-vmalloc.h>
static const struct v4l2_file_operations virtual_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open,
.release = vb2_fop_release,
.read = vb2_fop_read,
.poll = vb2_fop_poll,
.mmap = vb2_fop_mmap,
.unlocked_ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops virtual_ioctl_ops = {
.vidioc_querycap = airspy_querycap,
.vidioc_enum_fmt_sdr_cap = airspy_enum_fmt_sdr_cap,
.vidioc_g_fmt_sdr_cap = airspy_g_fmt_sdr_cap,
.vidioc_s_fmt_sdr_cap = airspy_s_fmt_sdr_cap,
.vidioc_try_fmt_sdr_cap = airspy_try_fmt_sdr_cap,
.vidioc_reqbufs = vb2_ioctl_reqbufs,
.vidioc_create_bufs = vb2_ioctl_create_bufs,
.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
.vidioc_querybuf = vb2_ioctl_querybuf,
.vidioc_qbuf = vb2_ioctl_qbuf,
.vidioc_dqbuf = vb2_ioctl_dqbuf,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_g_tuner = airspy_g_tuner,
.vidioc_s_tuner = airspy_s_tuner,
.vidioc_g_frequency = airspy_g_frequency,
.vidioc_s_frequency = airspy_s_frequency,
.vidioc_enum_freq_bands = airspy_enum_freq_bands,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
.vidioc_log_status = v4l2_ctrl_log_status,
};
static struct video_device g_vdev = {
.name = "100ask_virtual_video",
.release = video_device_release_empty,
.fops = &virtual_fops,
.ioctl_ops = &virtual_ioctl_ops,
};
static struct v4l2_device g_v4l2_dev;
static struct vb2_queue g_vb_queue;
static const struct vb2_ops virtul_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,
};
static int virtual_video_drv_init(void)
{
int ret;
/* 分配/设置/注册video_device */
/* 设置
* 1. 函数调用(比如ioctl)
* 2. 队列/buffer的管理
*/
g_vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
g_vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
g_vb_queue.drv_priv = NULL;
g_vb_queue.buf_struct_size = sizeof(struct airspy_frame_buf);
g_vb_queue.ops = &virtul_vb2_ops;
g_vb_queue.mem_ops = &vb2_vmalloc_memops;
g_vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
ret = vb2_queue_init(&s->vb_queue);
if (ret) {
printk("Could not initialize vb2 queue\n");
return -1;;
}
g_vdev.queue = &g_vb_queue;
g_vdev.queue->lock = &s->vb_queue_lock;
/* Register the v4l2_device structure(辅助作用) */
g_v4l2_dev.release = airspy_video_release;
ret = v4l2_device_register(NULL, &g_v4l2_dev);
if (ret) {
printk("Failed to register v4l2-device (%d)\n", ret);
return -1;
}
g_vdev.v4l2_dev = &g_v4l2_dev;
g_vdev.lock = &s->v4l2_lock;
ret = video_register_device(&g_vdev, VFL_TYPE_SDR, -1);
if (ret) {
printk("Failed to register as video device (%d)\n", ret);
return -1;
}
return 0;
}
static void virtual_video_drv_exit(void)
{
/* 反注册/释放video_device */
v4l2_device_unregister(&g_v4l2_dev);
video_unregister_device(&g_vdev);
}
module_init(virtual_video_drv_init);
module_exit(virtual_video_drv_exit);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,227 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-vmalloc.h>
static struct list_head g_queued_bufs;
/* intermediate buffers with raw data from the USB device */
struct virtual_frame_buf {
/* common v4l buffer stuff -- must be first */
struct vb2_v4l2_buffer vb;
struct list_head list;
};
static int virtual_querycap(struct file *file, void *fh,
struct v4l2_capability *cap)
{
strlcpy(cap->driver, "100ask_virtual_video", sizeof(cap->driver));
strlcpy(cap->card, "no-card", sizeof(cap->card));
cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE ;
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
static int virtual_enum_fmt_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
if (f->index > 0)
return -EINVAL;
strlcpy(f->description, "100ask motion jpeg", sizeof(f->description));
f->pixelformat = V4L2_PIX_FMT_MJPEG;
return 0;
}
static int virtual_s_fmt_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
/* 分辨用户传入的参数是否可用
* 如果不可用, 给APP提供最接近的、硬件支持的参数
*/
if (f.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (f.fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
return -EINVAL;
f.fmt.pix.width = 800;
f.fmt.pix.height = 600;
return 0;
}
static int virtual_enum_framesizes(struct file *file, void *fh,
struct v4l2_frmsizeenum *fsize)
{
if (fsize->index > 0)
return -EINVAL;
fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
fsize->discrete.width = 800;
fsize->discrete.height = 600;
return 0;
}
static const struct v4l2_file_operations virtual_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open,
.release = vb2_fop_release,
.read = vb2_fop_read,
.poll = vb2_fop_poll,
.mmap = vb2_fop_mmap,
.unlocked_ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops virtual_ioctl_ops = {
.vidioc_querycap = virtual_querycap,
.vidioc_enum_fmt_vid_cap = virtual_enum_fmt_cap,
.vidioc_s_fmt_vid_cap = virtual_s_fmt_cap,
.vidioc_enum_framesizes = virtual_enum_framesizes,
.vidioc_reqbufs = vb2_ioctl_reqbufs,
.vidioc_create_bufs = vb2_ioctl_create_bufs,
.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
.vidioc_querybuf = vb2_ioctl_querybuf,
.vidioc_qbuf = vb2_ioctl_qbuf,
.vidioc_dqbuf = vb2_ioctl_dqbuf,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
};
static struct video_device g_vdev = {
.name = "100ask_virtual_video",
.release = video_device_release_empty,
.fops = &virtual_fops,
.ioctl_ops = &virtual_ioctl_ops,
};
static struct v4l2_device g_v4l2_dev;
static struct vb2_queue g_vb_queue;
/* Videobuf2 operations */
static int virtual_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers,
unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[])
{
/* 假装:至少需要8个buffer, 每个buffer只有1个plane */
/* Need at least 8 buffers */
if (vq->num_buffers + *nbuffers < 8)
*nbuffers = 8 - vq->num_buffers;
*nplanes = 1;
sizes[0] = PAGE_ALIGN(800*600*2);
return 0;
}
static void virtual_buf_queue(struct vb2_buffer *vb)
{
/* 把这个buffer告诉硬件相关的驱动程序 */
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct virtual_frame_buf *buf =
container_of(vbuf, struct virtual_frame_buf, vb);
unsigned long flags;
//spin_lock_irqsave(&s->queued_bufs_lock, flags);
list_add_tail(&buf->list, &g_queued_bufs);
//spin_unlock_irqrestore(&s->queued_bufs_lock, flags);
}
static int virtual_start_streaming(struct vb2_queue *vq, unsigned int count)
{
/* 启动硬件传输 */
return 0;
}
static void virtual_stop_streaming(struct vb2_queue *vq)
{
/* 停止硬件传输 */
}
static const struct vb2_ops virtul_vb2_ops = {
.queue_setup = virtual_queue_setup,
.buf_queue = virtual_buf_queue,
.start_streaming = virtual_start_streaming,
.stop_streaming = virtual_stop_streaming,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
};
static int virtual_video_drv_init(void)
{
int ret;
/* 分配/设置/注册video_device */
/* 设置
* 1. 函数调用(比如ioctl)
* 2. 队列/buffer的管理
*/
g_vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
g_vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
g_vb_queue.drv_priv = NULL;
g_vb_queue.buf_struct_size = sizeof(struct virtual_frame_buf); /* 分配vb时, 分配的空间大小为buf_struct_size */
g_vb_queue.ops = &virtul_vb2_ops;
g_vb_queue.mem_ops = &vb2_vmalloc_memops;
g_vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
ret = vb2_queue_init(&s->vb_queue);
if (ret) {
printk("Could not initialize vb2 queue\n");
return -1;;
}
g_vdev.queue = &g_vb_queue;
g_vdev.queue->lock = &s->vb_queue_lock;
/* Register the v4l2_device structure(辅助作用) */
g_v4l2_dev.release = airspy_video_release;
ret = v4l2_device_register(NULL, &g_v4l2_dev);
if (ret) {
printk("Failed to register v4l2-device (%d)\n", ret);
return -1;
}
g_vdev.v4l2_dev = &g_v4l2_dev;
g_vdev.lock = &s->v4l2_lock;
ret = video_register_device(&g_vdev, VFL_TYPE_SDR, -1);
if (ret) {
printk("Failed to register as video device (%d)\n", ret);
return -1;
}
INIT_LIST_HEAD(&g_queued_bufs);
return 0;
}
static void virtual_video_drv_exit(void)
{
/* 反注册/释放video_device */
v4l2_device_unregister(&g_v4l2_dev);
video_unregister_device(&g_vdev);
}
module_init(virtual_video_drv_init);
module_exit(virtual_video_drv_exit);
MODULE_LICENSE("GPL");

View File

@@ -809,6 +809,14 @@ git clone https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git
[02-5]_videobuffer2情景分析
```
* 2023.09.04 发布"摄像头驱动"
```shell
[03-1]_回顾与编写驱动框架
[03-2]_虚拟摄像头驱动编程_ioctl代码
[03-3]_虚拟摄像头驱动编程_buffer代码
```

View File

@@ -508,7 +508,3 @@ APP ioctl VIDIOC_DQBUF
## 2. 从0编写一个虚拟的摄像头驱动

View File

@@ -0,0 +1,102 @@
# 从0编写一个虚拟摄像头驱动 #
参考资料:
* 深入理解linux内核v4l2框架之videobuf2https://blog.csdn.net/yyjsword/article/details/9243717
* V4L2框架-videobuf2https://blog.csdn.net/u013904227/article/details/81054611
## 1. 回顾与编写程序框架
本节视频对应的代码:
```shell
doc_and_source_for_drivers\
IMX6ULL\source\
13_V4L2\
06_virtual_driver\
01_virtual_driver_framework
```
参考《1.3.2 videobuffer2的3个ops》里面完整的注册流程。
完整的注册流程(参考`drivers\media\usb\airspy\airspy.c`)
```shell
static struct video_device airspy_template = {
.name = "AirSpy SDR",
.release = video_device_release_empty,
.fops = &airspy_fops,
.ioctl_ops = &airspy_ioctl_ops,
};
/* 构造一个vb2_queue */
/* Init videobuf2 queue structure */
s->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
s->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
s->vb_queue.drv_priv = s;
s->vb_queue.buf_struct_size = sizeof(struct airspy_frame_buf);
s->vb_queue.ops = &airspy_vb2_ops; // vb2_ops, 硬件相关的操作函数
s->vb_queue.mem_ops = &vb2_vmalloc_memops; // vb2_mem_ops, 辅助结构体,用于mem ops(alloc、mmap)
s->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
ret = vb2_queue_init(&s->vb_queue);
q->buf_ops = &v4l2_buf_ops; // vb2_buf_ops, 用于APP和驱动传递参数
// 分配/设置video_device结构体
s->vdev = airspy_template;
s->vdev.queue = &s->vb_queue; // 指向前面构造的vb2_queue
// 初始化一个v4l2_device结构体(起辅助作用)
/* Register the v4l2_device structure */
s->v4l2_dev.release = airspy_video_release;
ret = v4l2_device_register(&intf->dev, &s->v4l2_dev);
// video_device和4l2_device建立联系
s->vdev.v4l2_dev = &s->v4l2_dev;
// 注册video_device结构体
ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1);
__video_register_device
// 根据次设备号把video_device结构体放入数组
video_device[vdev->minor] = vdev;
// 注册字符设备驱动程序
vdev->cdev->ops = &v4l2_fops;
vdev->cdev->owner = owner;
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
```
## 2. 编写硬件相关代码
本节视频对应的代码:
```shell
doc_and_source_for_drivers\
IMX6ULL\source\
13_V4L2\
06_virtual_driver\
02_virtual_driver_hardware
```
### 2.1 ioctl相关的代码
### 2.2 buffer相关的代码
### 2.3 数据传输
## 3. 上机测试

View File

@@ -0,0 +1,128 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-vmalloc.h>
static const struct v4l2_file_operations virtual_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open,
.release = vb2_fop_release,
.read = vb2_fop_read,
.poll = vb2_fop_poll,
.mmap = vb2_fop_mmap,
.unlocked_ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops virtual_ioctl_ops = {
.vidioc_querycap = airspy_querycap,
.vidioc_enum_fmt_sdr_cap = airspy_enum_fmt_sdr_cap,
.vidioc_g_fmt_sdr_cap = airspy_g_fmt_sdr_cap,
.vidioc_s_fmt_sdr_cap = airspy_s_fmt_sdr_cap,
.vidioc_try_fmt_sdr_cap = airspy_try_fmt_sdr_cap,
.vidioc_reqbufs = vb2_ioctl_reqbufs,
.vidioc_create_bufs = vb2_ioctl_create_bufs,
.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
.vidioc_querybuf = vb2_ioctl_querybuf,
.vidioc_qbuf = vb2_ioctl_qbuf,
.vidioc_dqbuf = vb2_ioctl_dqbuf,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_g_tuner = airspy_g_tuner,
.vidioc_s_tuner = airspy_s_tuner,
.vidioc_g_frequency = airspy_g_frequency,
.vidioc_s_frequency = airspy_s_frequency,
.vidioc_enum_freq_bands = airspy_enum_freq_bands,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
.vidioc_log_status = v4l2_ctrl_log_status,
};
static struct video_device g_vdev = {
.name = "100ask_virtual_video",
.release = video_device_release_empty,
.fops = &virtual_fops,
.ioctl_ops = &virtual_ioctl_ops,
};
static struct v4l2_device g_v4l2_dev;
static struct vb2_queue g_vb_queue;
static const struct vb2_ops virtul_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,
};
static int virtual_video_drv_init(void)
{
int ret;
/* 分配/设置/注册video_device */
/* 设置
* 1. 函数调用(比如ioctl)
* 2. 队列/buffer的管理
*/
g_vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
g_vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
g_vb_queue.drv_priv = NULL;
g_vb_queue.buf_struct_size = sizeof(struct airspy_frame_buf);
g_vb_queue.ops = &virtul_vb2_ops;
g_vb_queue.mem_ops = &vb2_vmalloc_memops;
g_vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
ret = vb2_queue_init(&s->vb_queue);
if (ret) {
printk("Could not initialize vb2 queue\n");
return -1;;
}
g_vdev.queue = &g_vb_queue;
g_vdev.queue->lock = &s->vb_queue_lock;
/* Register the v4l2_device structure(辅助作用) */
g_v4l2_dev.release = airspy_video_release;
ret = v4l2_device_register(NULL, &g_v4l2_dev);
if (ret) {
printk("Failed to register v4l2-device (%d)\n", ret);
return -1;
}
g_vdev.v4l2_dev = &g_v4l2_dev;
g_vdev.lock = &s->v4l2_lock;
ret = video_register_device(&g_vdev, VFL_TYPE_SDR, -1);
if (ret) {
printk("Failed to register as video device (%d)\n", ret);
return -1;
}
return 0;
}
static void virtual_video_drv_exit(void)
{
/* 反注册/释放video_device */
v4l2_device_unregister(&g_v4l2_dev);
video_unregister_device(&g_vdev);
}
module_init(virtual_video_drv_init);
module_exit(virtual_video_drv_exit);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,227 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-vmalloc.h>
static struct list_head g_queued_bufs;
/* intermediate buffers with raw data from the USB device */
struct virtual_frame_buf {
/* common v4l buffer stuff -- must be first */
struct vb2_v4l2_buffer vb;
struct list_head list;
};
static int virtual_querycap(struct file *file, void *fh,
struct v4l2_capability *cap)
{
strlcpy(cap->driver, "100ask_virtual_video", sizeof(cap->driver));
strlcpy(cap->card, "no-card", sizeof(cap->card));
cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE ;
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
static int virtual_enum_fmt_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
if (f->index > 0)
return -EINVAL;
strlcpy(f->description, "100ask motion jpeg", sizeof(f->description));
f->pixelformat = V4L2_PIX_FMT_MJPEG;
return 0;
}
static int virtual_s_fmt_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
/* 分辨用户传入的参数是否可用
* 如果不可用, 给APP提供最接近的、硬件支持的参数
*/
if (f.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (f.fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
return -EINVAL;
f.fmt.pix.width = 800;
f.fmt.pix.height = 600;
return 0;
}
static int virtual_enum_framesizes(struct file *file, void *fh,
struct v4l2_frmsizeenum *fsize)
{
if (fsize->index > 0)
return -EINVAL;
fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
fsize->discrete.width = 800;
fsize->discrete.height = 600;
return 0;
}
static const struct v4l2_file_operations virtual_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open,
.release = vb2_fop_release,
.read = vb2_fop_read,
.poll = vb2_fop_poll,
.mmap = vb2_fop_mmap,
.unlocked_ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops virtual_ioctl_ops = {
.vidioc_querycap = virtual_querycap,
.vidioc_enum_fmt_vid_cap = virtual_enum_fmt_cap,
.vidioc_s_fmt_vid_cap = virtual_s_fmt_cap,
.vidioc_enum_framesizes = virtual_enum_framesizes,
.vidioc_reqbufs = vb2_ioctl_reqbufs,
.vidioc_create_bufs = vb2_ioctl_create_bufs,
.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
.vidioc_querybuf = vb2_ioctl_querybuf,
.vidioc_qbuf = vb2_ioctl_qbuf,
.vidioc_dqbuf = vb2_ioctl_dqbuf,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
};
static struct video_device g_vdev = {
.name = "100ask_virtual_video",
.release = video_device_release_empty,
.fops = &virtual_fops,
.ioctl_ops = &virtual_ioctl_ops,
};
static struct v4l2_device g_v4l2_dev;
static struct vb2_queue g_vb_queue;
/* Videobuf2 operations */
static int virtual_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers,
unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[])
{
/* 假装:至少需要8个buffer, 每个buffer只有1个plane */
/* Need at least 8 buffers */
if (vq->num_buffers + *nbuffers < 8)
*nbuffers = 8 - vq->num_buffers;
*nplanes = 1;
sizes[0] = PAGE_ALIGN(800*600*2);
return 0;
}
static void virtual_buf_queue(struct vb2_buffer *vb)
{
/* 把这个buffer告诉硬件相关的驱动程序 */
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct virtual_frame_buf *buf =
container_of(vbuf, struct virtual_frame_buf, vb);
unsigned long flags;
//spin_lock_irqsave(&s->queued_bufs_lock, flags);
list_add_tail(&buf->list, &g_queued_bufs);
//spin_unlock_irqrestore(&s->queued_bufs_lock, flags);
}
static int virtual_start_streaming(struct vb2_queue *vq, unsigned int count)
{
/* 启动硬件传输 */
return 0;
}
static void virtual_stop_streaming(struct vb2_queue *vq)
{
/* 停止硬件传输 */
}
static const struct vb2_ops virtul_vb2_ops = {
.queue_setup = virtual_queue_setup,
.buf_queue = virtual_buf_queue,
.start_streaming = virtual_start_streaming,
.stop_streaming = virtual_stop_streaming,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
};
static int virtual_video_drv_init(void)
{
int ret;
/* 分配/设置/注册video_device */
/* 设置
* 1. 函数调用(比如ioctl)
* 2. 队列/buffer的管理
*/
g_vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
g_vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
g_vb_queue.drv_priv = NULL;
g_vb_queue.buf_struct_size = sizeof(struct virtual_frame_buf); /* 分配vb时, 分配的空间大小为buf_struct_size */
g_vb_queue.ops = &virtul_vb2_ops;
g_vb_queue.mem_ops = &vb2_vmalloc_memops;
g_vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
ret = vb2_queue_init(&s->vb_queue);
if (ret) {
printk("Could not initialize vb2 queue\n");
return -1;;
}
g_vdev.queue = &g_vb_queue;
g_vdev.queue->lock = &s->vb_queue_lock;
/* Register the v4l2_device structure(辅助作用) */
g_v4l2_dev.release = airspy_video_release;
ret = v4l2_device_register(NULL, &g_v4l2_dev);
if (ret) {
printk("Failed to register v4l2-device (%d)\n", ret);
return -1;
}
g_vdev.v4l2_dev = &g_v4l2_dev;
g_vdev.lock = &s->v4l2_lock;
ret = video_register_device(&g_vdev, VFL_TYPE_SDR, -1);
if (ret) {
printk("Failed to register as video device (%d)\n", ret);
return -1;
}
INIT_LIST_HEAD(&g_queued_bufs);
return 0;
}
static void virtual_video_drv_exit(void)
{
/* 反注册/释放video_device */
v4l2_device_unregister(&g_v4l2_dev);
video_unregister_device(&g_vdev);
}
module_init(virtual_video_drv_init);
module_exit(virtual_video_drv_exit);
MODULE_LICENSE("GPL");