一、概述
一般在 Linux 设备驱动模型中,我们只需要关心总线、设备、驱动这三个实体。总线会充当红娘对加载于其上的设备与驱动进行配对,对于 Camera 模块也不例外,下面从总线、设备、驱动的角度来分析 Camera 模块驱动的注册、匹配与加载过程。本文以MTK平台为例。
二、驱动加载过程
驱动加载都是以module_init(XXX)开始,如下所示:
module_init(CAMERA_HW_i2C_init);
1、入口函数
/*=======================================================
* CAMERA_HW_i2C_init()
*======================================================*/
static int __init CAMERA_HW_i2C_init(void)
{
PK_DBG("[camerahw_probe] start\n");
#ifndef CONFIG_OF
int ret = 0;
//注册平台总线的设备
ret = platform_device_register(&camerahw_platform_device);
if (ret) {
PK_ERR("[camerahw_probe] platform_device_register fail\n");
return ret;
}
ret = platform_device_register(&camerahw2_platform_device);
if (ret) {
PK_ERR("[camerahw2_probe] platform_device_register fail\n");
return ret;
}
#endif
//注册平台总线的驱动
if (platform_driver_register(&g_stCAMERA_HW_Driver)) {
PK_ERR("failed to register CAMERA_HW driver\n");
return -ENODEV;
}
if (platform_driver_register(&g_stCAMERA_HW_Driver2)) {
PK_ERR("failed to register CAMERA_HW driver\n");
return -ENODEV;
}
/* FIX-ME: linux-3.10 procfs API changed */
//在proc下创建driver/camsensor这个节点,用于前置摄像头进行adb效果调试
proc_create("driver/camsensor", 0, NULL, &fcamera_proc_fops);
//在proc下创建driver/camsensor2这个节点,用于后置摄像头进行adb效果调试
proc_create("driver/camsensor2", 0, NULL, &fcamera_proc_fops2);
proc_create("driver/camsensor3", 0, NULL, &fcamera_proc_fops3);
/* Camera information */
memset(mtk_ccm_name, 0, camera_info_size);
proc_create(PROC_CAMERA_INFO, 0, NULL, &fcamera_proc_fops1);
…………
return 0;
}
CAMERA_HW_i2C_init函数主要做的是首先要进行I2C总线板极设备的注册及初始化工作,然后注册platform总线的driver,通过g_stCAMERA_HW_Driver结构体里面的name来与device进行匹配。同时,在函数里面还在proc目录下创建了driver/camsensor和driver/camsensor2两个节点,这样做主要是方便sensor的IC原厂FAE利用adb进行效果调试的。
2、g_stCAMERA_HW_Driver结构体
static struct platform_driver g_stCAMERA_HW_Driver = {
.probe = CAMERA_HW_probe,//匹配成功则调用
.remove = CAMERA_HW_remove,
.suspend = CAMERA_HW_suspend,//休眠时调用
.resume = CAMERA_HW_resume,//唤醒时调用
.driver = {
.name = "image_sensor",
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = CAMERA_HW_of_ids,
#endif
}
};
g_stCAMERA_HW_Driver结构体中主要有probe、remove、suspend等接口的实现,尤其是probe接口为设备注册的匹配函数,在driver与device匹配后就会调用 .probe = CAMERA_HW_probe进入CAMERA_HW_probe函数。
3、探测函数CAMERA_HW_probe
static int CAMERA_HW_probe(struct platform_device *pdev)
{
#if !defined(CONFIG_MTK_CLKMGR)
Get_ccf_clk(pdev);
#endif
#if !defined(CONFIG_MTK_LEGACY)/*GPIO Pin control*/
mtkcam_gpio_init(pdev);//GPIO管脚通过pinctl进行初始化
#endif
//注册I2C驱动
return i2c_add_driver(&CAMERA_HW_i2c_driver);
}
4、CAMERA_HW_i2c_driver结构体
struct i2c_driver CAMERA_HW_i2c_driver = {
.probe = CAMERA_HW_i2c_probe,
.remove = CAMERA_HW_i2c_remove,
.driver = {
.name = CAMERA_HW_DRVNAME1,
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = CAMERA_HW_i2c_of_ids,
#endif
},
.id_table = CAMERA_HW_i2c_id,
};
sensor驱动最终还是会注册I2C设备,I2C总线的注册其实跟platform总线的注册大致相同,注册完I2C driver后就会进行匹配,匹配成功后系统就会调用CAMERA_HW_i2c_driver 结构体里面的 .probe = CAMERA_HW_i2c_probe。
5、探测函数CAMERA_HW_probe
static int CAMERA_HW_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int i4RetValue = 0;
PK_DBG("[CAMERA_HW] Attach I2C\n");
/* get sensor i2c client */
spin_lock(&kdsensor_drv_lock);
g_pstI2Cclient = client;
/* set I2C clock rate */
g_pstI2Cclient->timing = 100;/* 100k */
g_pstI2Cclient->ext_flag &= ~I2C_POLLING_FLAG; /* No I2C polling busy waiting */
spin_unlock(&kdsensor_drv_lock);
/* Register char driver */
i4RetValue = RegisterCAMERA_HWCharDrv();//注册一个字符设备
if (i4RetValue) {
PK_ERR("[CAMERA_HW] register char device failed!\n");
return i4RetValue;
}
/* spin_lock_init(&g_CamHWLock); */
#if !defined(CONFIG_MTK_LEGACY)
Get_Cam_Regulator();//获取电源
#endif
PK_DBG("[CAMERA_HW] Attached!!\n");
return 0;
}
6、字符设备注册
static inline int RegisterCAMERA_HWCharDrv(void)
{
#if CAMERA_HW_DYNAMIC_ALLOCATE_DEVNO
// 动态分配一个字符设备
if (alloc_chrdev_region(&g_CAMERA_HWdevno, 0, 1, CAMERA_HW_DRVNAME1)) {
PK_DBG("[CAMERA SENSOR] Allocate device no failed\n");
return -EAGAIN;
}
#else
// 静态分配一个字符设备
if (register_chrdev_region(g_CAMERA_HWdevno , 1 , CAMERA_HW_DRVNAME1)) {
PK_DBG("[CAMERA SENSOR] Register device no failed\n");
return -EAGAIN;
}
#endif
/* Allocate driver */
g_pCAMERA_HW_CharDrv = cdev_alloc(); // 申请一个cdev结构体
if (NULL == g_pCAMERA_HW_CharDrv) {
unregister_chrdev_region(g_CAMERA_HWdevno, 1);
PK_DBG("[CAMERA SENSOR] Allocate mem for kobject failed\n");
return -ENOMEM;
}
/* Attatch file operation. */
//关联到file_operation进入字符设备
cdev_init(g_pCAMERA_HW_CharDrv, &g_stCAMERA_HW_fops);
g_pCAMERA_HW_CharDrv->owner = THIS_MODULE;
/* Add to system */
//将我们分配的字符设备,attach上file_operation添加到system
if (cdev_add(g_pCAMERA_HW_CharDrv, g_CAMERA_HWdevno, 1)) {
PK_DBG("[mt6516_IDP] Attatch file operation failed\n");
unregister_chrdev_region(g_CAMERA_HWdevno, 1);
return -EAGAIN;
}
//创建一个sensordrv类
sensor_class = class_create(THIS_MODULE, "sensordrv");
if (IS_ERR(sensor_class)) {
int ret = PTR_ERR(sensor_class);
PK_DBG("Unable to create class, err = %d\n", ret);
return ret;
}
sensor_device = device_create(sensor_class, NULL, g_CAMERA_HWdevno, NULL, CAMERA_HW_DRVNAME1);
return 0;
}
camera的模块驱动为字符驱动,所以RegisterCAMERA_HWCharDrv函数主要是对camera_image进行字符驱动注册,代码开始先判断是否定义了CAMERA_HW_DYNAMIC_ALLOCATE_DEVNO变量来判断动态还是静态分配一个字符设备,然后通过cdev_alloc申请了一个cdev结构体后,通过cdev_init将g_stCAMERA_HW_fops关联到字符设备,g_stCAMERA_HW_fops是HAL层与内核驱动进行交互的渠道。然后就是将我们分配的字符设备,attach上file_operation添加到system,最后在sys/class目录下创建一个sensordrv类。
三、总结
到目前为止,我们大致了解camera驱动模块大致的加载过程,值得注意的是注册字符设备时,关联着结构体g_stCAMERA_HW_fops;下一篇将围绕这个结构体,一起分析HAL层与驱动的交互过程