V4L2框架分析
NO_1. 术语讲解
V4L2 即: video for Linux version 2
uvc: usb video class
NO_2. 硬件相关的
uvc_driver.c
static int __init uvc_init(void)
ret = usb_register(&uvc_driver.driver);
struct uvc_driver uvc_driver = {
.driver = {
.name = "uvcvideo",
.probe = uvc_probe,
.disconnect = uvc_disconnect,
.suspend = uvc_suspend,
.resume = uvc_resume,
.reset_resume = uvc_reset_resume,
.id_table = uvc_ids,
.supports_autosuspend = 1,
},
};
static int uvc_probe(struct usb_interface *intf,const struct usb_device_id *id)
v4l2_device_register(&intf->dev, &dev->vdev);
uvc_register_chains(dev);
ret = uvc_register_terms(dev, chain);
ret = uvc_register_video(dev, stream);
vdev = video_device_alloc();
vdev->fops = &uvc_fops;
ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
总结1:
struct video_device *vdev;
vdev = video_device_alloc();
vdev->fops = &uvc_fops;
ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
总结2:
1.0 通过 video_register_device 函数的搜索可知
video_register_device --> __video_register_device 最终调用的是 v4l2-dev.c 的 __video_register_device 函数,
可见,v4l2-dev.c 是核心层函数;
2.0
int __video_register_device(struct video_device *vdev, int type, int nr,int warn_if_nr_in_use, struct module *owner)
vdev->cdev = cdev_alloc();
vdev->cdev->ops = &v4l2_fops;
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
NO_3. 虚拟视频驱动 vivid.core.c 分析;
1. 分配一个 video_device 结构体;
2. 设置这个结构体;
3. 注册;
vivid_init
ret = vivid_create_instance(i);
ret = v4l2_device_register(NULL, &dev->v4l2_dev);
struct video_device *vfd;
struct vivid_dev *dev;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
vfd = &dev->vid_cap_dev;
1.
strlcpy(vfd->name, "vivid-vid-cap", sizeof(vfd->name));
vfd->fops = &vivid_fops;
vfd->ioctl_ops = &vivid_ioctl_ops;
vfd->release = video_device_release_empty;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->queue = &dev->vb_vid_cap_q;
vfd->tvnorms = tvnorms_cap;
2.
vfd->v4l2_dev = &dev->v4l2_dev;
3. 设置 "ctrl属性" (用于APP的 ioctrl )
ret = vivid_create_controls(dev, ccs_cap == -1, ccs_out == -1, no_error_inj,
in_type_counter[TV] || in_type_counter[SVID] ||
out_type_counter[SVID],
in_type_counter[HDMI] || out_type_counter[HDMI]);
dev->brightness = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_cap_nr[inst]);
__video_register_device(struct video_device *vdev, int type, int nr,int warn_if_nr_in_use, struct module *owner)
vdev->cdev = cdev_alloc();
vdev->cdev->ops = &v4l2_fops;
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
video_device[vdev->minor] = vdev;
NO_4. vivid.core.c 的 open,read,write,ioctl 过程;
1. open
app: open("/dev/videox",....);
.................................................
drv: vdev->cdev->ops = &v4l2_fops;--> .open = v4l2_open,
struct video_device *vdev;
vdev = video_devdata(filp);
return video_device[iminor(file_inode(file))];
ret = vdev->fops->open(filp);
2. read
app: read....
..................................................
drv: vdev->cdev->ops = &v4l2_fops;--> .read = v4l2_read,
struct video_device *vdev = video_devdata(filp);
return video_device[iminor(file_inode(file))];
ret = vdev->fops->read(filp, buf, sz, off);
3. write
类似:通过 vdev->cdev->ops = &v4l2_fops;--> .write 从而调用 vivid-core.c 驱动的 vivid_fops -> v4l2_fh_write;
4. ioctl
app:ioctl....
...................................................
drv:vdev->cdev->ops = &v4l2_fops;--> .unlocked_ioctl = v4l2_ioctl,
struct video_device *vdev = video_devdata(filp);
ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);即调用 vivid-core.c 的 vivid_fops -> .unlocked_ioctl = video_ioctl2,
video_ioctl2
return video_usercopy(file, cmd, arg, __video_do_ioctl);
__video_do_ioctl
struct video_device *vfd = video_devdata(file);
if (v4l2_is_known_ioctl(cmd)) {
info = &v4l2_ioctls[_IOC_NR(cmd)];
}
v4l2_ctrl_handler 的使用过程;
app --> drv: __video_do_ioctl
struct video_device *vfd = video_devdata(file);
NO_4. 如何写 V4L2 驱动?
1. 分配/设置/注册 v4l2_device
通过 v4l2_device_register 注册得到一个 v4l2_dev 结构体:
这个不是主要的,跟入代码可以看出,这是用于初始化一些东西,比如自旋锁,引用计数等;辅助作用而已;
2. 分配 video_device ,用 video_device_alloc 函数分配,最终调用的是 kzalloc ;vivid是通过以下方式来分配的;
struct video_device *vfd;
struct vivid_dev *dev;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
vfd = &dev->vid_cap_dev;
3. 设置;
3.1 vfd->v4l2_dev = &dev->v4l2_dev;
3.2 vfd->fops = &vivid_fops;
3.3 vfd->ioctl_ops = &vivid_ioctl_ops;
3.4 APP 可以通过 ioctl 来设置亮度等信息,那么驱动程序里,谁来接收/存储/设置到硬件?
APP 可以通过 ioctl 来获得亮度等信息,那么驱动程序里,谁来提供这些信息?
属性: v4l2_ctrl
管理: v4l2_ctrl_handler:
a. v4l2_ctrl_handler_init
b. 用 v4l2_ctrl_new_std and v4l2_ctrl_new_custom 填充 v4l2_ctrl_hander 的属性;
即创建 v4l2_ctrl 并放入链表 v4l2_ctrl_hander(查看该结构体可知这是一个链表);
c.如何使用呢?
2. 怎么写 v4l2 的驱动:以 vivid 为参考例子
a. 分配 video_device 结构体 :
my_vivid_device = video_device_alloc();
b. 设置
my_vivid_device->release = my_vivid_reless;
my_vivid_device->fops = &my_vivid_fops;
my_vivid_device->ioctl_ops = &my_vivid_ioctl_ops;
填充 static const struct v4l2_file_operations my_vivid_fops =
{
.unlocked_ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops my_vivid_ioctl_ops =
{
.owner = THIS_MODULE,
.vidioc_querycap = my_vivid_vidioc_querycap,
.vidioc_enum_fmt_vid_cap = my_vivid_vidioc_enum_fmt_vid,
.vidioc_g_fmt_vid_cap = my_vivid_vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = my_vivid_vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = my_vivid_vidioc_s_fmt_vid_cap,
.vidioc_reqbufs = vb2_ioctl_reqbufs,
.vidioc_querybuf = vb2_ioctl_querybuf,
.vidioc_qbuf = vb2_ioctl_qbuf,
.vidioc_dqbuf
};
c. 注册
video_register_device(my_vivid_device, VFL_TYPE_GRABBER, -1);