camera驱动开机加载流程

camera驱动
图片不好上传,可以跟着源码去看,MT6735下面的

也可以查看有道云链接:点击打开链接

一、模块加载函数
模块加载函数位于kd_sensorlist.c文件中,kd_sensorlist.c相当于虚拟驱动,hal层会通过它来调用真正的驱动,路径:
drivers/misc/mediatek/imgsensor/src/mt6735m/kd_sensorlist.c



根据驱动程序的基本架构,先执行模块加载函数 CAMERA_ HW_i2C_init(),可以拆分为三部分:

1)、首先,调用platform_driver_register()和platform_device_register()函数进行了平台驱动和设备的注册,如图:



2)、接下来就是创建proc虚拟文件,如下图:


补充,proc虚拟文件:
创建一个proc虚拟文件,应用层通过读写该文件,即可实现与内核的交互。
创建proc文件夹:
调用以下函数,即可实现proc文件夹的创建:
struct proc_dir_entry * proc_mkdir (const char *name, struct proc_dir_entry *parent);
name:要创建的文件夹名称。
parent:要创建节点的父节点。也就是要在A文件夹之下创建B文件夹,需要将A文件夹的proc_dir_entry传入;如果是在/proc目录下创建文件夹,parent为NULL。

创建proc文件:
创建方法是调用以下函数:
static inline struct proc_dir_entry * proc_create (const char *name, mode_t mode, struct proc_dir_entry *parent, const struct file_operations *proc_fops);
name:要创建的文件名。
mode:文件的访问权限,以UGO的模式表示。
parent与proc_mkdir中的parent类似。也是父文件夹的proc_dir_entry对象。
proc_fops就是该文件的操作函数了。







3)、最后是调动atomic_set函数设置一些原子变量的值为0;

模块加载函数到这里就执行完毕了;其中第一步platform平台设备和驱动的注册是一个重要的步骤,注册后会自动调用match函数根据name变量进行匹配,匹配成功则自动加载platform_driver结构体中包含的probe函数,在camera模块中有前摄和后摄两个设备,设备和驱动都是分别注册,对应各自的probe函数。


二、platform平台设备的probe函数





根据上图可以明显的看出这两个probe函数有差别,CAMERA_HW_probe()比CAMERA_HW_probe2()函数多了一个mtkcam_gpio_init()函数,这个函数的作用是对引脚进行初始化,定义在:drivers/misc/mediatek/imgsensor/src/mt6735m/camera_project/miki8735m1_9706/camera_hw/kd_camera_hw.c

再看probe函数,两个probe函数都调用了i2c_add_driver函数在i2c总线中添加i2c驱动,在添加i2c驱动后,调用match()函数进行匹配,匹配成功则调用i2c_driver结构体里定义的probe函数。


三、i2c设备驱动的probe函数

这里补充说一点:i2c在添加注册设备的时候有两种方式,一种是静态注册,还有一种是动态注册;
静态注册 如图中所示:
采用i2c_register_board_info()函数,将所需要的I2C从设备加入一个名为__i2c_board_list双向循环链表;系统在成功加载I2C主设备adapt后,这张链表里所有I2C从设备逐一地完成 i2c_client的注册:
i2c_register_adapter() -> i2c_scan_static_board_info() -> i2c_new_device() -> device_register()


动态注册
i2c_register_driver() -> __process_new_driver() -> i2c_do_add_adapter() - > i2c_detect() -> i2c_new_device() -> device_register()
对遍历得到的每一个device执行__process_new_driver() ,通过 i2c_detect() 检测是否有合适的设备挂在总线上,若有,则调用i2c_new_device()注册设备

如下图所示,在CAMERA_HW_I2C_init()函数中,CONFIG_MTK_LEGACY这个宏是否定义决定是否调用i2c_register_board_info()这个函数,经检查CONFIG_MTK_LEGACY没有被定义,所以i2c设备是动态注册;


通过log的打印也可以看出是动态注册,调用了如图所示三个函数:

回到probe函数,i2c_driver结构体里定义了probe函数,名称如下:



四、字符设备驱动的注册和文件操作结构体

在CAMERA_HW_i2c_probe函数和CAMERA_HW_i2c_probe2函数中调用RegisterCAMERA_HWCharDrv / RegisterCAMERA_HWCharDrv2函数注册字符设备驱动。


并且创建了一个类和设备节点:
内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。

每个字符设备驱动都有对应的文件操作结构体,文件操作结构体对应上层的调用,如果没有它,上层无法与驱动进行通信,在camera模块中定义了g_stCAMERA_HW_fops0和g_stCAMERA_HW_fops两个文件操作结构体,下图可见,每个文件操作结构体都包含open、release、ioctl三个函数;



4.1 file operation中的open函数

结合log的情况,最先执行的是CAMERA_HW_Open函数,整个函数只是对g_CamDrvOpenCnt进行了原子操作。



4.2 file operation中的ioctl函数

CAMERA_HW_Ioctl函数是重点:
上层跟驱动进行通讯主要是通过ioctl发送cmd命令(上层和驱动“协商”好的一些指令),然后进行数据传输 ,ioctl函数里面有个switch结构,根据用户空间传达指令的不同进行不同的操作;
cmd命令的定义:/drivers/misc/mediatek/imgsensor/inc/kd_imgsensor.h


1、最先传达的指令是KDIMGSENSORIOC_X_SET_MCLK_PLL,对应kdSetSensorMclk函数,该函数的功能是对摄像头时钟进行一些设置






2、然后执行的是指令是KDIMGSENSORIOC_X_SET_DRIVER,对应kdSetDriver函数,很关键的函数,函数原型如下,在这里详细分析:


先看函数功能:
首先,通过kdGetSensorInitFuncList()函数将pSensorList指针指向kdSensorList(驱动列表)这个数组的首地址,然后在for循环中通过分解上层传下来的函数参数pDrvIndex来判断前摄和后摄,根据前后摄选择对应的i2c总线,并且通过pSensorList[drvIdx[i]].SensorInit(&g_pInvokeSensorFunc[i]);将g_pInvokeSensorFunc这个数组指针指向具体的驱动程序,通过这一步之后才算真正的将上层和驱动连接起来,最后使用memcpy函数将指定的drvname放入g_invokeSensorNameStr数组。

1)、函数参数pDrvIndex是一个数组,有2个元素,数组的每一个元素的前16位表示前后摄的下标(目前取值为1和2;1表示后摄,2表示前摄),后16位表示的是sensor的索引(在当前驱动的取值为0,1;0表后摄,1表前摄),将pDrvIndex数组的前16位的值赋给g_invokeSocketIdx数组,用来区分前摄还是后摄,将pDrvIndex数组的后16位的值赋给drvIdx数组,用来表示sensor的序列号;

2)、ACDK_KD_SENSOR_INIT_FUNCTION_STRUCT结构体:
定义在:drivers/misc/mediatek/imgsensor/src/mt6735m/kd_sensorlist.h
在这个结构体中定义了CameraName、CameraID和SensorInit函数;


3)、pSensorList[drvIdx[i]].SensorInit(&g_pInvokeSensorFunc[i]);
这行代码的意思是调用驱动列表中某个具体驱动的SensorInit函数,将g_pInvo keSensorFunc这个数组指针指向sensor_func这个结构体变量;

ST55A_MIPI_RAW_SensorInit和SP250A_MIPI_SensorInit这两个函数定义在具体的驱动模块中,
路径分别为:
drivers/misc/mediatek/imgsensor/src/mt6735m/st55a_mipi_raw_9706/st55amipiraw_Sensor.c
drivers/misc/mediatek/imgsensor/src/mt6735m/sp250amipi_raw_9706/sp250amipi_Sensor.c

将g_pInvokeSensorFunc指针指向sensor_func这个结构体变量,以后通过g_pInvokeSensorFunc指针可以操作sensor_func里面的函数


sensor_func结构体变量变量的定义如下:









路径:drivers/misc/mediatek/imgsensor/inc/kd_imgsensor_define.h


4)、kdGetSensorInitFuncList函数,该函数将pSensorList这个指针指向kdSensorList这个数组的首地址


5)注意:在for循环里面进行前后摄判断的时候,因为每次传参的时候pDrvIndex[]数组里面只有一个有效的元素,所以每次for循环最多只能完成一次循环,在第二次循环的时候直接continue跳出去;


3、KDIMGSENSORIOC_X_SET_DRIVER指令执行完毕之后,会接着执行KDIMGSENSORIOC_T_CHECK_IS_ALIVE指令,调用adopt_CAMERA_HW_CheckIsAlive()函数,这个函数的主要功能是给camera上下电,并且读取camera的ID。
true:表示上电; false:表示下电;


读取ID:

这里调用SensorFeatureControl()函数实际上是调用kd_MultiSensorFeatureControl()函数,原因如下两图:


kd_MultiSensorFeatureControl()函数里又调用了SensorFeatureControl()函数(即调用了驱动模块中的feature_control()函数)
并且传递了SENSOR_FEATURE_CHECK_SENSOR_ID这个参数,feature_ c ontrol()函数根据所传的参数去调用get_imgsensor_id()这个函数,最终会调用iReadRegI2C这个函数去读取ID,iReadRegI2C定义在kd_sensorlist.c文件里面,调用流程如下:
get_imgsensor_id() -> return_sensor_id() -> read_cmos_sensor() -> iReadRegI2C()[读取寄存器里面的数值];

4、之后会继续执行KDIMGSENSORIOC_X_GETINFO和KDIMGSENSORIOC_X _ GETINFO2指令,调用adopt_CAMERA_HW_G etIn fo()和 adopt_CAMERA _HW_GetInfo2()函数,这两个函数并不会因为前摄和后摄而分别调用,前摄和后摄都会调用这两个函数,最终会调用到模块驱动程序里面的get_info()函数,通过传递给get_info()函数的参数来选择执行的内容,根据log可以看出来大致的加载流程:
最开始,依次加载了后摄(ST55A)和前摄(SP250A)的图像预览,通过iReadRegI2C()和iWriteRegI2C()函数对寄存器读取和写入数据,这里说的依次添加是添加了后摄的图像预览之后再去从ioctl函数开始添加前摄的图像预览。


然后,又从ioctl开始,开始依次加载前后摄的各项参数,下面的图片是从log中截取的部分,具体参数代表的意义现在还没有弄清楚,以后再仔细思考。






5、继续ioctl函数,执行KDIMGSENSORIOC_X_SET_CURRENT_SENSOR指令,调用kdSetCurrentSensorIdx()函数


6、执行ioctl函数中adopt_CAMERA_HW_Open()函数,最终调用模块驱动中的open()函数,这个函数的功能是初始化摄像头驱动的寄存器,其中调用了sensor_init()函数:


write_coms_sensor()函数调用了iWriteRegI2C()函数,iWriteRegI2C()函数和iReadRegI2C()函数一样定义在kd_sensorlist.c文件里面;

7、最后调用close函数。

猜你喜欢

转载自blog.csdn.net/qq_40658985/article/details/80526938
今日推荐