简介
因为在qcom平台上和linux原生都是用的v4l2框架作为camera的驱动框架,所以本着学习记录的笔记,做了如下文档记录。 该文档是学习《卫东山老师视频教程第三期》的个人学习笔记,非常感谢老师的资料。该记录仅供学习交流,如有侵犯到大家利益,还望海涵,请联系博主删除。
注册video_device
代码演示
首先是驱动程序的入口、出口以及license,然后第一步就要分配,设置和注册一个video_device结构(基于内核版本:linux-3.13.1)。
#include <linux/module.h> #include <linux/delay.h> #include <linux/errno.h> #include <linux/fs.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/mm.h> #include <linux/ioport.h> #include <linux/init.h> #include <linux/sched.h> #include <linux/pci.h> #include <linux/random.h> #include <linux/version.h> #include <linux/mutex.h> #include <linux/videodev2.h> #include <linux/dma-mapping.h> #include <linux/interrupt.h> #include <linux/kthread.h> #include <linux/highmem.h> #include <linux/freezer.h> #include <media/videobuf-vmalloc.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #define DRIVER_NAME "myvivi_video" static struct video_device *myvivi_device; struct v4l2_device myvivi_v4l2_dev; static void myvivi_release(struct video_device *vdev) { } static const struct v4l2_file_operations myvivi_fops = { .owner = THIS_MODULE, }; static int myvivi_init(void) { int error; /* 1. 分配一个video_device结构体 */ myvivi_device = video_device_alloc(); /* 2. 设置 */ strlcpy(myvivi_v4l2_dev.name, DRIVER_NAME, sizeof(myvivi_v4l2_dev.name)); error = v4l2_device_register(NULL, &myvivi_v4l2_dev); myvivi_device->v4l2_dev = &myvivi_v4l2_dev; myvivi_device->release = myvivi_release; myvivi_device->fops = &myvivi_fops; /* 2.1 */ error = video_register_device(myvivi_device, VFL_TYPE_GRABBER, -1); return error; } static void myvivi_exit(void) { video_unregister_device(myvivi_device); video_device_release(myvivi_device); } module_init(myvivi_init); module_exit(myvivi_exit); MODULE_LICENSE("GPL");
代码讲解
首先是分配一个video_device_alloc来分配一个v4l2_device结构,接着填充设置这个结构的v4l2_dev参数、操作函数fops以及释放函数release, 在代码中为了简洁,所有fops和release函数都设置为了空函数。 接着注册了该v4l2设备,注意这里的video_register_device函数,第一个参数表示我们需要注册的video_device 结构,VFL_TYPE_GRABBER表示是图像采集设备 ,最后的-1参数表示,注册时候使用第一个空闲的设备名,如果/dev/video0已经被使用了,那么这次注册就是作为/dev/video1.如果参数不是-1,比如是5,那么注 册的设备文件为:/dev/video5. 最后是驱动的出口,对应的操作也就是注销掉之前注册的v4l2设备,然后销毁该设备结构。 如此,一个最简单的v4l2设备就完成了。
注意
我使用的内核版本是linux-3.13.1,在该版本中,必须填充了v4l2_device结构的v4l2_dev参数、fops操作函数以及释放函数release之后, 调用video_register_device函数进行注册才能注册成功,否则驱动程序,会报出空指针或者参数之类的错误。 同时在加载该驱动之前,需要保证内核中依赖的v4l2驱动模块已经是加载好了的,v4l2一般对应有如下几个模块: videodev.ko videobuf-vmalloc.ko videobuf-core.ko v4l2-common.ko 驱动依赖哪些模块,可以使用sudo modinfo myvivi.ko 来查看到。
效果演示
最后insmod加载该驱动模块之后,会生成video*的设备节点,效果如下:
接着安装工具:sudo apt-get install xawtv 然后使用该工具后会提示说:
Failed to query video capabilities: Inappropriate ioctl for device libv4l2: error getting capabilities: Inappropriate ioctl for device Failed to query video capabilities: Inappropriate ioctl for device libv4l2: error getting capabilities: Inappropriate ioctl for device vid-open-auto: failed to open a capture device at /dev/video0 vid-open: could not find a suitable videodev no video grabber device available
表示为video设备
在前面使用xawtv的时候,提示说没有找到适合的video设备,所以接着这一步就是将我们之前写的v4l2注册驱动表示为video设备。
代码讲解
添加代码如下:
............... static const struct v4l2_file_operations myvivi_fops = { .owner = THIS_MODULE, .ioctl = video_ioctl2, /* V4L2 ioctl handler */ }; static int myvivi_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap){ strcpy(cap->driver, "myvivi"); strcpy(cap->card, "myvivi"); cap->version = 0x0001; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; /*表明是个视频捕捉设备和通过ioctl来访问*/ return 0; } static const struct v4l2_ioctl_ops myvivi_ioctl_ops = { // 表示它是一个摄像头设备 .vidioc_querycap = myvivi_vidioc_querycap, }; static int myvivi_init(void) { int error; /* 1. 分配一个video_device结构体 */ myvivi_device = video_device_alloc(); /* 2. 设置 */ strlcpy(myvivi_v4l2_dev.name, DRIVER_NAME, sizeof(myvivi_v4l2_dev.name)); error = v4l2_device_register(NULL, &myvivi_v4l2_dev); myvivi_device->v4l2_dev = &myvivi_v4l2_dev; myvivi_device->release = myvivi_release; myvivi_device->fops = &myvivi_fops; myvivi_device->ioctl_ops = &myvivi_ioctl_ops; .............. } ..............
新加入初始化了一个myvivi_ioctl_ops操作函数结构,在其中填充了函数myvivi_vidioc_querycap,在应用程序中,会调用该函数来确实该驱动设备类型。 在该函数中V4L2_CAP_VIDEO_CAPTURE表示该驱动设备是一个视频获取设备,V4L2_CAP_STREAMING表示是通过ioctl来进行获取数据。 同时在调用myvivi_vidioc_querycap函数的时候还需要调用到myvivi_fops中的ioctl函数,所以还需要填充该ioctl。这里使用v4l2提供的ioctl标准操作 函数video_ioctl2。
效果演示
接着继续用xawtv工具来测试该驱动。效果如下:
这里标明xawtv找到了/dev/video0是一个video设备。
设置支持的video格式
前面一步中,xawtv已经识别到了该设备驱动是video设备,接着这一步在驱动中设置该驱动支持的video格式。
代码讲解
static struct v4l2_format myvivi_format; /* 列举支持哪种格式 */ static int myvivi_vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f){ if (f->index >= 1) /*我们这里设置为只支持一种格式,所以设置f->index如果大于等于1,就返回错误*/ return -EINVAL; strcpy(f->description, "4:2:2, packed, YUYV"); f->pixelformat = V4L2_PIX_FMT_YUYV; return 0; } /* 返回当前所使用的格式 */ static int myvivi_vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f){ memcpy(f, &myvivi_format, sizeof(myvivi_format)); return (0); } /* 测试驱动程序是否支持某种格式 */ static int myvivi_vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f){ unsigned int maxw, maxh; if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV) return -EINVAL; /*该设备支持的最大宽度和高度*/ maxw = 1024; maxh = 768; /* 调整format的最大最小width, height,和对齐方式 * 计算bytesperline, sizeimage */ v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, &f->fmt.pix.height, 32, maxh, 0, 0); f->fmt.pix.bytesperline = (f->fmt.pix.width * 16) >> 3; /*设置颜色深度为16*/ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; /*设置图像size*/ return 0; } static int myvivi_vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f){ int ret = myvivi_vidioc_try_fmt_vid_cap(file, NULL, f); if (ret < 0) return ret; memcpy(&myvivi_format, f, sizeof(myvivi_format)); return ret; } static const struct v4l2_ioctl_ops myvivi_ioctl_ops = { // 表示它是一个摄像头设备 .vidioc_querycap = myvivi_vidioc_querycap, /* 用于列举、获得、测试、设置摄像头的数据的格式 */ .vidioc_enum_fmt_vid_cap = myvivi_vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = myvivi_vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = myvivi_vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = myvivi_vidioc_s_fmt_vid_cap, };
如上所示,一共增加了4个函数。 myvivi_vidioc_enum_fmt_vid_cap:用来设置支持的格式,这里设置为只支持一种格式 V4L2_PIX_FMT_YUYV。 myvivi_vidioc_g_fmt_vid_cap:用来获得支持的video格式信息,格式信息保存在结构myvivi_format中。 myvivi_vidioc_try_fmt_vid_cap:用来测试传入的某个video格式是否被该设备驱动支持,同时设置了该格式下的video的width、height和image size等信息。 myvivi_vidioc_s_fmt_vid_cap:首先用myvivi_vidioc_try_fmt_vid_cap,判断传入的某种格式是否支持,如果支持就保存到myvivi_format结构中。
效果演示
同样是用xawtv工具来演示下效果,不过这里看不出太明显。
这里可以看到,xawtv有读取到了一些格式相关信息。
时间: 2024-10-07 06:00:15