声明:视频来源韦东山老师视频第三期的摄像头一章,如有侵权,请联系作者,我会自行删除
一、摄像图框架属于字符驱动框架,是一个通用的框架:
1. 构造file_operations:
2. 告诉内核,注册register_chrdev(major, file_ops, name) //名字无所谓,不重要
有的书认为这个函数很老了,会用另外三个函数代替这个函数:
2.2 分配cdev
2.3 设置cdev
2.4 cdev_add
3. 入口函数
4. 出口函数
二、对于复杂的字符驱动程序,就会引入分层的概念,例如第二期中的LCD驱动程序
1. fbmen.c //内核已经提供好了
1.1 file_operation
1.2 register_chrdev
1.3 入口、出口
2. 硬件相关 //这个是需要我们做的
2.1 分配fb_info
2.2 设置fb_info
2.3 注册 //所谓的注册就是告诉fbmen.c,当应用程序调用open等函数操作LCD的时候,首先调用fbmen.c中的open等函数
调用open的时候就会调用我们注册的fb_info结构体里面提供的函数或者属性操作硬件,分层的好处我们只需要关注
硬件相关的操作,其他通用的部分部分内核已经做好了。
2.4 硬件相关的操作
总结: 如何写分层的驱动程序?
内核已经写好了软件相关层,我们只需需要去构造硬件相关的一层,所以这样写就可以了
2.4.1 分配某个结构体,具体是什么结构体,具体驱动具体分析
2.4.2 设置它
2.4.3 注册结构体
2.4.4 硬件相关的操作
三、分析摄像头的驱动框架(V4L2)
猜测这个应该也是一个分层形式的驱动框架
book@www.100ask.org:/work/system/linux-3.4.2/drivers$ grep -nR "Found UVC" *
Binary file built-in.o matches
Binary file media/video/uvc/uvc_driver.o matches
media/video/uvc/uvc_driver.c:1848: uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n",
Binary file media/video/uvc/uvcvideo.o matches
Binary file media/video/uvc/built-in.o matches
Binary file media/video/built-in.o matches
Binary file media/built-in.o matches
进入函数uvc_driver:
uvc_driver
uvc_init
usb_register(&uvc_driver.driver)
uvc_probe
v4l2_device_register //这个不是主要的,用来一些初始化
uvc_register_chains chain链
uvc_register_terms term条目
uvc_register_video
video_device_alloc
video_register_device
__video_register_device
vdev->cdev = cdev_alloc();
vdev->cdev->ops = &v4l2_fops;
cdev_add
v4l2_fops将会调用video_device函数中的各种属性
分析一个比较不错的函数vivi.c,作为分析的例子
虚拟视频驱动vivi.c分析
关键点:
分配video_device
设置
注册video_register_device
vivi.c
vivi_init
vivi_create_instance
v4l2_device_register //不重要,只是用来初始化一些东西,例如自锁锁等
分配
video_device_alloc
设置
1.*vfd = vivi_template;
.fops = &vivi_fops, (v4l2_file_operations) // 命名有点智障,把我都搞糊涂了
.ioctl_ops = &vivi_ioctl_ops,
2.vfd->v4l2_dev = &dev->v4l2_dev;
3.设置"ctrl",用于app的ioctl
dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
注册
video_register_device(video_device: vfd, type :VFL_TYPE_GRABBER, number: video_nr)根据不同类型自动创建相应的节点
vdev->cdev = cdev_alloc();
vdev->cdev->ops = &v4l2_fops(file_operations);//忍不住了,再吐槽一次,智障的命名,这个智障调用上面的那个智障
cdev_add
猜测file_operations会调用v4 l2_file_operations
彻底了解一个驱动程序最好的方式及时分析函数里面的open,read,write函数等
open
vdev = video_devdata(filp);
return video_device[iminor(file->f_path.dentry->d_inode)]; //根据此设备号从数组中得到video_device结构体
在下面两句话里将video_device存进数组里(在video_register_device里面可以看见)
if (vdev->cdev == NULL)
video_device[vdev->minor] = vdev;
open:
file_operations v4l2_fops
.open = v4l2_open
vdev = video_devdata(filp);
if (vdev->fops->open) //v4l2_file_operations,从这里就可以看见了file_operations调用了v4l2_file_operations
ret = vdev->fops->open(filp);
read:
write函数都是一样的,差不多
ioctl:
file_operations v4l2_fops
.unlocked_ioctl = v4l2_ioctl,
struct video_device *vdev = video_devdata(filp);
if (vdev->fops->unlocked_ioctl) { //下面是vivi_fops 的确含有ioctl
// static const struct v4l2_file_operations vivi_fops = {
// .owner = THIS_MODULE,
// .open = v4l2_fh_open,
// .release = vivi_close,
// .read = vivi_read,
// .poll = vivi_poll,
// .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
// .mmap = vivi_mmap,
// };
ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
// 实质上就是这个函数video_ioctl2
video_ioctl2
video_usercopy(file, cmd, arg, __video_do_ioctl);//从用户空间复制参数调用函数__video_do_ioctl
__video_do_ioctl
struct video_device *vfd = video_devdata(file); //到这一步的时候已经和read函数一样了
switch (cmd) //根据app传入的参数来设置、获得一些属性。
问题是这些属性是谁提供的呢
dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
总结: 怎么写驱动程序呢?
分配设置注册v4l2_device v4l2_device_register //注意一下,这个命名有点奇葩,这个不是主要的,主要的是video_device
分配一个结构体video_device video_device_alloc
设置这个结构体,就称之为vfd 结构体v4l2_device结构体就是在这里面,这个结构体仅仅是辅助作用(v4l2_device)
看看这个函数:
*vfd = vivi_template;
.ioctl_ops = &vivi_ioctl_ops,
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
........
在和v4l2_fops一个文件里应该会有一个函数调用ioctl_ops,但是我没找到,估计不是我想的这样,不过没事
分离分层的时候,v4l2_file_operations在底层,file_operation在中层,上面是app
ioctl_ops就是底层文件,app可以通过他来设置亮度等之类的属性,那么驱动程序里谁来接收存储接收到这些硬件,提供这些信息呢
这个就是v4l2_ctrl,由v42l_ctrl_handler来管理这些信息
v42l_ctrl_handler
1. 初始化
v4l2_ctrl_handler_init 初始化v42l_ctrl_handler
2. 设置
v4l2_ctrl_new_std
v4l2_ctrl_new_custom 这两个函数的作用就是创建v4l2_ctrl ,清切放入v42l_ctrl_handler链表中
3. 使用
dev->v4l2_dev.ctrl_handler = hdl; //和video_device关联
因为video_device.v42l_dev
v4l2_ctrl_handler的使用过程:
ioctl:
file_operations v4l2_fops
.unlocked_ioctl = v4l2_ioctl,
struct video_device *vdev = video_devdata(filp);
ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
video_ioctl2
video_usercopy(file, cmd, arg, __video_do_ioctl);//从用户空间复制参数调用函数__video_do_ioctl
__video_do_ioctl
struct video_device *vfd = video_devdata(file); //到这一步的时候已经和read函数一样了
switch (cmd) //根据app传入的参数来设置、获得一些属性。
case VIDIOC_QUERYCTRL:
{
struct v4l2_queryctrl *p = arg;
if (vfh && vfh->ctrl_handler)
ret = v4l2_queryctrl(vfh->ctrl_handler, p);
else if (vfd->ctrl_handler)
//搜索ctrl_handler ,lookup references可以看到在__video_register_device中已经有了设置
//if (vdev->ctrl_handler == NULL)
//vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
ret = v4l2_queryctrl(vfd->ctrl_handler, p);
暂时到这儿,后续会补上