韋東山專案視訊之攝像頭驅動1 V4L2框架分析
一、攝像頭驅動 V4L2框架分析
攝像頭驅動是屬於字元裝置驅動程式
V4L2: vidio for linux version 2,我們分析的是linux3.4.2核心。
回顧二期,怎麼寫驅動?
1.構造一個file_operations:.open=drv_open .read=drv_read
2.告訴核心:register_chrdev(主裝置號,名字,&file_operations)
3.入口函式:呼叫register_chrdev
4.出口函式:解除安裝
一般採用register_chrdev的代替方法:分配、設定cdev,cdev_add
而對於複雜的驅動,採用分層的概念。
例如LCD驅動中分為兩層:上層通用的核心層核心已經幫我們做好,即在fbmem.c
1.構造file_operations(open read write 。。)
2.註冊
3.入口、出口
我們做的是硬體相關層,供上層file_operations呼叫
1.分配一個fb_info 結構體
2.設定
3.註冊
4.硬體相關的操作
因此,對於這種複雜的驅動,我們的做法:
1.分配某個結構體
2.設定
3.註冊
4.硬體相關
現在分析V4L2框架:
把usb裝置接到系統前臺,會有列印資訊,根據列印資訊在核心裡找出驅動,用dmsg命令檢視;
grep "Found UVC" * -nR 搜尋 在uvc_driver.c裡,這是個硬體相關的驅動。
分析程式碼,猜測V4L2 框架 肯定也是分為至少兩層 。
app 呼叫 open read write -->呼叫 v4l2_fops 裡的 open read write->呼叫硬體相關層的video_device 裡提供的函式
----------------------------------------------------------------------------------------------------------
核心層:v4l2-dev.c __video_register_device
構造:v4l2_fops(.read = v4l2_read,
.write = v4l2_write,
.open = v4l2_open, 。。。。)
註冊:
vdev->cdev = cdev_alloc(); //1.字元裝置cdev_alloc
vdev->cdev->ops = &v4l2_fops; //2.設定fops
cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); //3.cdev_add
----------------------------------------------------------------------------------------------------------
硬體相關層:如uvc_driver.c Found UVC ->v4l2_device_register(這個不重要)
->video_device_alloc->video_register_device(向核心層註冊)
->v4l2-dev.h->__video_register_device(v4l2-dev.c)
即分配結構體 video_device (裡面的函式供上層v4l2_fops呼叫)
設定 註冊video_register_device
以vivi.c(virtual video driver )虛擬視訊驅動 作為例子
1.分配一個video_device 結構體
2.設定
3.註冊 video_register_device
----------------------------------------------------------------------------------------------------------
硬體相關層
從入口函式開始分析
vivi_init
vivi_create_instance
v4l2_device_register //不是主要的
下面大堆函式是用來設定音量、亮度、增益等屬性,用於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);
dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_CONTRAST, 0, 255, 1, 16);
等等...
vfd = video_device_alloc(); //分配video_device
*vfd = vivi_template; //內容設定為vivi_template
/*最底層的vivi 操作函式*/
static struct video_device vivi_template = {
.name= "vivi",
.fops = &vivi_fops,
.ioctl_ops = &vivi_ioctl_ops,
.release
= video_device_release,
.tvnorms = V4L2_STD_525_60,
.current_norm = V4L2_STD_NTSC_M,
};
vfd->v4l2_dev = &dev->v4l2_dev;
video_register_device(video_device結構體vfd, 型別VFL_TYPE_GRABBER, video_nr); //向上註冊
----------------------------------------------------------------------------------------------------------
核心層
__video_register_device
根據型別VFL_TYPE_GRABBER建立不同的裝置節點
case VFL_TYPE_GRABBER:
name_base = "video";
根據型別VFL_TYPE_GRABBER得到不同的次裝置號
case VFL_TYPE_GRABBER:
minor_offset = 0;
minor_cnt = 64;
vdev->cdev = cdev_alloc();
vdev->cdev->ops = &v4l2_fops;
cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); //3.cdev_add
video_device[vdev->minor] = vdev;//以次裝置號為下標在數組裡面得到一項把vdev存進來
分析vivi.c 的open read write ioctl 過程-
1.open過程 -
APP :open (“/dev/video0”...)-
-
drv : v4l2_fops裡的v4l2_open函式-
vdev = video_devdata(filp)//根據次裝置號從陣列中得到video_device---------
if (vdev->fops->open) //如果有open函式
if (video_is_registered(vdev))
ret = vdev->fops->open(filp);//呼叫open 函式
呼叫vivi.c 裡的v4l2_fh_open
2.read過程
APP :read (“/dev/video0”...)
drv :v4l2_fops裡的v4l2_read 函式
struct video_device *vdev = video_devdata(filp);//根據次裝置號從陣列中得到video_device
if (video_is_registered(vdev))
ret = vdev->fops->read(filp, buf, sz, off);//呼叫read 函式
呼叫vivi.c 裡的vivi_read
2.ioctl 過程(比較複雜)
APP :ioctl(“/dev/video0”...)
drv : v4l2_fops裡的v4l2_ioctl函式
struct video_device *vdev = video_devdata(filp);//根據次裝置號從陣列中得到video_device
if (vdev->fops->unlocked_ioctl)
ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);//呼叫v4l2_ioctl函式
呼叫vivi.c 裡的video_ioctl2
這個video_ioctl2做什麼呢?
video_usercopy(file, cmd, arg, __video_do_ioctl);
video_usercopy:從使用者空間把使用者的命令複製進來,呼叫__video_do_ioctl
__video_do_ioctl
struct video_device *vfd = video_devdata(file);//根據次裝置號從陣列中得到video_device
根據使用者空間APP得到的命令(cmd) 設定某些屬性
switch (cmd)
怎麼設定這些屬性?還得由vivi.c來提供,在vivi.c 裡一開始的vivi_create_instance裡設定
v4l2_ctrl_handler的使用過程
分析__video_do_ioctl
二、怎麼寫v4l2驅動?
1.分配、設定、註冊v4l2_device v4l2_device_register video_register_device
2.分配一個video_device video_device_alloc
.vfd->v4l2_dev
.fops 設定vfd的fops 裡的open、read、write 被上層呼叫
.ioctl_ops 設定屬性被上層呼叫
思考:APP可以通過ioctl來設定(獲得)亮度等資訊,在驅動程式裡,誰來接收、儲存、設定到硬體(提供這些資訊)?
答:在驅動程式中抽象出來一個結構體v4l2_ctrl,每個Ctrl對應其中的一項(音量、亮度等等);
由v4l2_ctrl_handler來管理他們
1.初始化
v4l2_ctrl_handler_init
2.設定
v4l2_ctrl_new_std
v4l2_ctrl_new_custom
這些函式就是建立各個屬性,並且放入v4l2_ctrl_handler的連結串列
3.跟vdev關聯
dev->v4l2_dev.ctrl_handler = hdl;