camera驅動

在Android4.0升級後,突然發現大量平臺對Camera的支持均非常不好。要麼Camera使用不穩定,要麼各類ioctl設置不能使用,更有甚者,連Camera Device都不能創建。


而我們的產品,基於Camera的正常工作,必須解決此類問題。選擇了其中一款使用CSI接口Camera的平臺:全志A20來做研究和解決。


0. 基礎知識 : 
0.0. 目錄結構:
除了常見的,在linux-3.3/drivers/media/video/ 目錄下有v4l2一些文件外,全志還在另一個目錄下放置了csi以及做支持模組芯片的代碼:linux-3.3/drivers/media/video/sunxi_csi/
其中,csi部分在csi0或者csi1中。 模組對應代碼在device中。
這些代碼,基本組成了全志CSI 接口 Camera的全部驅動代碼。


0.1. CSI:
COMS Sensor Interface:
CSI接口通常從COMS Sensor,Video Encoder和其它視頻輸出設備收集數據。


0.2. 模組芯片:
手頭這塊板子上自帶GT2005模組。所以需要看linux-3.3/drivers/media/video/sunxi_csi/device/gt2005.c


1. 驅動結構:
CSI 模組Driver的基本思路其實挺簡單,就是需要創建主設備號爲81的device. /dev/videoX。並將其open,close,ioctl 與Driver聯繫起來,最終反應到Sensor (gt2005)層面。


我們首先從linux-3.3/drivers/media/video/sunxi_csi/csi0部分開始着手,這個文件夾內的三個文件:sunxi_csi_reg.c ,sunxi_drv_csi.c, sunxi_csi_reg.h 最終會生成sunxi_csi0.ko。 會被以modules形式insmod 進系統。


1.1:CSI0 modules學習:
當sunxi_csi0.ko被insmod時,sunxi_drv_csi.c中的csi_init()被調用。
csi_init() 中註冊了一個driver--csi_driver.
platform_driver_register(&csi_driver);


csi_driver中有偵測函數:.probe = csi_probe,
Sam的理解是:因爲CSI接口一直連接着,所以當此Driver剛被註冊時,csi_probe就被調用。
請注意:csi_probe中作了相當多事情,是sunxi_csi0.ko的重點。


它做了很多準備工作.並通過video_register_device()創建了Device. /dev/videoX. 並將此device的操作與v4l2_fops聯繫起來。
所以,對此Device的open,close, ioctl等, 都使用v4l2_fops中提供的函數指證。


而我們看v4l2_fops中的ioctl. 它最終是 video_device ->fops->ioctl()。
其實就是:csi_template 中的:.fops       = &csi_fops, 的ioctl. 即:video_ioctl2


video_ioctl2,在v4l2-ioctl.c中定義:其實就是:
static long __video_do_ioctl(struct file *file,unsigned int cmd, void *arg)


所以,經過CSI程序的鏈接。最終,對Device /dev/videoX的操作,最終都被歸結到__video_do_ioctl


同時,csi_ioctl_ops也被鏈接到ioctl_ops。請注意,未來要用到這裏。


看到這裏,大家都會疑惑。 這怎麼和Sensor操作練習起來了。這部分只將CSI和V4L2 連起來了。
這就是下一部要說的gt2005.ko了。




1.2: GT2005模塊學習:
同樣道理,insmod gt2005.ko時,gt2005.c中的init_sensor()會被啓動。
在init_sensor()中,只執行了:i2c_add_driver(&sensor_driver); 其中,sensor_driver 同樣會在驅動安裝後立刻啓動。


static int sensor_probe(struct i2c_client *client,const struct i2c_device_id *id)
v4l2_i2c_subdev_init(sd, client, &sensor_ops);
這一句代碼是關鍵。
它將sensor_ops加入v4l2_subdev->ops中去了。而v4l2_subdev類型的sd被加入list.未來會用。(關鍵3)


分析下:sensor_ops.
static const struct v4l2_subdev_ops sensor_ops = {
.core = &sensor_core_ops,
.video = &sensor_video_ops,
};
也就是有:core和video 兩個。一定要注意了,這裏和未來很有關係。(關鍵4)






__video_do_ioctl()最終會通過vfd->ioctl_ops 來做實際動作。而ioctl_ops是誰呢?就是在在sunxi_drv_csi.c中的csi_ioctl_ops






2. ioctl的完整流程:
當用戶open /dev/videoX後,使用ioctl()時,


例1:得到能力集:
rel = ioctl(Handle, VIDIOC_QUERYCAP, &cap);
通過上面的分析, 它通過 __video_do_ioctl 最終調用到:vfd->ioctl_ops->vidioc_querycap.
也就是 csi_ioctl_ops 的 .vidioc_querycap          = vidioc_querycap,


也就是說: 調用ioctl(VIDIOC_QUERYCAP). 會通過__vidoe_do_ioctl() 最終調用到sunxi_drv_sic.c中的vidioc_querycap()
爲什麼沒有進Sensor呢? 是因爲能力集在代碼層面維護。不需要去問Sensor。




例2:得到像素格式
ioctl(Handle, VIDIOC_G_FMT, &Format)
它最終被調用sunxi_drv_csi.c中的vidioc_g_fmt_vid_cap()
因爲格式也是由程序維護,所以沒有訪問Sensor。 


例3:設置像素格式
ioctl(Handle, VIDIOC_S_FMT, &Format);
最終被調用sunxi_drv_csi.c中的vidioc_s_fmt_vid_cap()
注意: 設置像素格式,需要通知Sensor。
所以會被調用:
 v4l2_subdev_call(dev->sd,video,s_mbus_fmt,&ccm_fmt);
這裏的video. 就是表明調用的是關鍵4 那個的sensor_video_ops。
這裏的sd,其實就是關鍵3 那裏的v4l2_subdev類型sd.
所以,又被調用到gt2005.c中的sensor_s_fmt()
此時會向Sensor中寫入數據。sensor_write




例4:
ioctl(Handle, VIDIOC_G_PARM, &Stream_Parm)
會通過__vidoe_do_ioctl()
調用到sunxi_drv_csi.c中vidioc_g_parm() 
這裏會:v4l2_subdev_call(dev->sd,video,g_parm,parms);
在gt2005.c中寫死的。 如果格式大於 800x600. 則15幀。 小於800x600. 則30幀。




例5:
ioctl(Handle, VIDIOC_G_PARM, &Stream_Parm)
會通過__vidoe_do_ioctl()
調用到sunxi_drv_csi.c中vidioc_s_parm() 
這裏會:v4l2_subdev_call(dev->sd,video,s_parm,parms);
在gt2005.c 中。這裏打空。所以其實不能調。


例6:
ioctl(Handle, VIDIOC_G_CTRL, &ctrl)
會通過__vidoe_do_ioctl()
調用到sunxi_drv_csi.c中vidioc_g_ctrl() 
這裏會:v4l2_subdev_call(dev->sd,core,g_ctrl,ctrl);
注意:這裏是core. 所以調用gt2005.c中的sensor_g_ctrl()






例7:
ioctl(Handle, VIDIOC_S_CTRL, &ctrl)
會通過__vidoe_do_ioctl()
調用到sunxi_drv_csi.c中vidioc_s_ctrl() 
這裏會:v4l2_subdev_call(dev->sd,core,s_ctrl,ctrl);
注意:這裏是core. 所以調用gt2005.c中的sensor_s_ctrl()






但有一點需要注意:在A20平臺編程中,大家會發現所有VIDIOC_G_CTRL, VIDIOC_S_CTRL全不可用。Sam查了一下代碼。發現是全志修改Kernel時弄錯了。
錯誤如下:
struct v4l2_control {
__u32     id;
__s32     value;
__u32 user_pt;
};
看起來,是全志一位叫Raymon的工程師非常隨意的修改了v4l2_control結構體。添加了4個字節的user_pt;
但這會造成嚴重後果,首先,大量使用v4l2_control的接口會出現未知問題。
更嚴重的是:ioctl的cmd這一項是計算出來的。v4l2_control的大小會影響到cmd的值。


所以,在A20平臺上,應用程序使用VIDIOC_G_CTRL, VIDIOC_S_CTRL時,
__video_do_ioctl(struct file *file,unsigned int cmd, void *arg)
參數2:cmd這一項和Kernel中算出的cmd值對不上。所以無法進入:
case VIDIOC_G_CTRL:
case VIDIOC_S_CTRL:




如果有全志工程師偶爾看到這篇Blog。 請通知Driver部門。修改這個明顯錯誤。
發佈了101 篇原創文章 · 獲贊 23 · 訪問量 28萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章