一、vivi摄像头驱动基本框架
- 分配一个video_device结构体变量
- 设置这个结构体变量
- 注册这个结构体变量
二、vivi摄像头驱动数据的获取过程
- 请求分配缓冲区
- 查询缓冲区,并为缓冲区分配空间
- 将缓冲区放入队列
- 启动摄像头
- 通过poll机制来对查询是否有数据,如果有数据可以通过定时器或者内核线程来唤醒它
- 将数据从缓冲区中取出,并将这个缓冲区从队列中删除
- 判断摄像头是否继续工作,如果继续工作则重复执行3、5、6步,否则结束
三、vivi摄像头驱动的简单实现步骤
1、自定义一个结构体yl_vivi_dev来表示vivi驱动的核心结构体,具体实现如下:
/* 定义一个结构体用来表示虚拟摄像头设备 */
struct yl_vivi_dev {
struct video_device *video_device; /* vivi驱动遵循V4L2框架的核心结构体 */
struct v4l2_format yl_vivi_format; /* 摄像头数据格式 */
struct videobuf_queue yl_videobuf_queue; /* 缓冲区队列 */
spinlock_t yl_videobuf_queue_slock; /* 用去缓冲区队列构建时使用的自旋锁 */
struct list_head yl_videobuf_list; /* 表示一个本地的队列,用来对缓冲区进行操作的 */
struct timer_list yl_vivi_timer; /* 通过定时器来填充摄像头数据 */
};
static struct yl_vivi_dev *yl_vivi_dev;
2、vivi驱动模块的输入、输出函数接口定义
/* 模块入口函数 */
static int yl_vivi_init(void)
{
int ret;
/* 给yl_vivi_dev的结构体变量分配内存 */
yl_vivi_dev = kzalloc(sizeof(struct yl_vivi_dev), GFP_KERNEL);
if(!yl_vivi_dev)
{
return -ENOMEM;
}
/* 1 分配一个video_device的结构体变量 */
yl_vivi_dev->video_device = video_device_alloc();
if (NULL == yl_vivi_dev->video_device)
{
kfree(yl_vivi_dev);
return -ENOMEM;
}
/* 2 设置这个video_device结构体变量 */
/* 2.1 给video_device结构体变量提供release函数,可以不用具体实现这个函数 */
yl_vivi_dev->video_device->release = yl_vivi_release;
/* 2.2 fops */
yl_vivi_dev->video_device->fops = &yl_vivi_fops;
/* 2.3 ioctl_ops */
yl_vivi_dev->video_device->ioctl_ops = &yl_vivi_ioctl_ops;
/* 3 注册这个video_device结构体变量 */
ret = video_register_device(yl_vivi_dev->video_device, VFL_TYPE_GRABBER, -1);
return ret;
}
/* 模块出口函数 */
static void yl_vivi_exit(void)
{
video_unregister_device(yl_vivi_dev->video_device);
video_device_release(yl_vivi_dev->video_device);
kfree(yl_vivi_dev);
}
主要完成video_device结构体的分配、设置和注册工作,核心是:
/* 2.2 fops */
yl_vivi_dev->video_device->fops = &yl_vivi_fops;
/* 2.3 ioctl_ops */
yl_vivi_dev->video_device->ioctl_ops = &yl_vivi_ioctl_ops;
yl_vivi_fops和yl_vivi_ioctl_ops的具体定义如下所示,这两个结构体是应用程序调用驱动程序时的主要接口:
/* video_device结构体变量的file操作函数 */
static const struct v4l2_file_operations yl_vivi_fops = {
.owner = THIS_MODULE,
.open = yl_vivi_open,
.release = yl_vivi_close,
.mmap = yl_vivi_mmap,
.poll = yl_vivi_poll,
.ioctl = video_ioctl2, /* V4L2 ioctl handler */
};
/* video_device结构体变量的ioctl操作函数 */
static const struct v4l2_ioctl_ops yl_vivi_ioctl_ops = {
/* 查询摄像头设备的基本性能 */
.vidioc_querycap = yl_vivi_vidioc_querycap,
/* 用于列举、获得、测试、设置摄像头的数据的格式 */
.vidioc_enum_fmt_vid_cap = yl_vivi_vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = yl_vivi_vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = yl_vivi_vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = yl_vivi_vidioc_s_fmt_vid_cap,
/* 用于申请、查询、入队列、出队列的摄像头数据的缓冲区的操作 */
.vidioc_reqbufs = yl_vivi_vidioc_reqbufs,
.vidioc_querybuf = yl_vivi_vidioc_querybuf,
.vidioc_qbuf = yl_vivi_vidioc_qbuf,
.vidioc_dqbuf = yl_vivi_vidioc_dqbuf,
/* 用于启动和关闭摄像头设备 */
.vidioc_streamon = yl_vivi_vidioc_streamon,
.vidioc_streamoff = yl_vivi_vidioc_streamoff,
};
下面主要工作就是设置这两个结构体变量的成员函数。
3、查询摄像头的基本性能
/* 查询摄像头设备的基本性能 */
static int yl_vivi_vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
strcpy(cap->driver, "yl_vivi");
strcpy(cap->card, "yl_vivi");
cap->version = 0x0001;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
return 0;
}
4、列举、获得、测试、设置摄像头的数据的格式
/* 列举摄像头支持的数据格式 */
static int yl_vivi_vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
stru