【RK3399】【Android7.1】camsys驱动加载流程分析
驱动的加载过程
(1) camsys driver⼊⼝函数
根据 ./kernel/drivers/media/video/rk_camsys ⽬录下的Makefile可以了解到,该⽬录源码最终会⽣成camsys_drv.o模块,设备端加载camsys_drv.o这个模块时,会调⽤这个模块中module_init注册的⼊⼝函数camsys_platform_init,具体代码如下:
module_init(camsys_platform_init);
static int __init camsys_platform_init(void)
{
printk("CamSys driver version: v%d.%d.%d, "
"CamSys head file version: v%d.%d.%d\n",
(CAMSYS_DRIVER_VERSION&0xff0000)>>16,
(CAMSYS_DRIVER_VERSION&0xff00)>>8,
CAMSYS_DRIVER_VERSION&0xff,
(CAMSYS_HEAD_VERSION&0xff0000)>>16,
(CAMSYS_HEAD_VERSION&0xff00)>>8,
CAMSYS_HEAD_VERSION&0xff);
spin_lock_init(&camsys_devs.lock);
INIT_LIST_HEAD(&camsys_devs.devs);
platform_driver_register(&camsys_platform_driver);
return0;
}
由上述代码我们可以知道在camsys_platform_init中实现了⾃旋锁的初始化,链表头的初始化,以及平台设备驱动的注册等。
(2) platform_driver结构体定义
camsys_platform_driver结构体的定义如下:
static struct platform_driver camsys_platform_driver ={
.driver ={
.name = CAMSYS_PLATFORM_DRV_NAME,
.of_match_table =of_match_ptr(cif_of_match),
},
.probe = camsys_platform_probe,
.remove =(camsys_platform_remove),
};
有上述代码可知,camsys_drv.o模块加载的时候,主要是注册了⼀个platform_driver结构体。在Linux
内核中,platform总线在加载设备或者驱动的时候,都会有⼀个探测过程,探测是否有匹配的驱动或者设备,匹配成功则会执⾏其中的 .probe 函数,其中会优先通过
.of_match_table 匹配,如果匹配不成功,再使⽤ .name 进⾏匹配。
需要的 .name 定义如下:
#define CAMSYS_PLATFORM_DRV_NAME "RockChip-CamSys"
需要的 .of_match_table 定义如下:
static const struct of_device_id cif_of_match[]={
{patible ="rockchip,isp"},
{/* sentinel */}
};
MODULE_DEVICE_TABLE(of, cif_of_match);
有上述定义可知,驱动中的 .of_match_table 需要和DTS⾥⾯的 patible = “rockchip,isp” 进⾏匹配,匹配成功了会调
⽤platform_driver中的probe函数。
(3) DTS设置
相应的dts⽂件存放路径:
./kernel/arch/arm64/boot/dts/rockchip/rk3399-android.dtsi
dts⽂件⾥的设置如下:
isp0: isp@ff910000 {
compatible ="rockchip,rk3399-isp","rockchip,isp";
......
status ="disabled";
};
isp0: isp@ff920000 {
compatible ="rockchip,rk3399-isp","rockchip,isp";
......
status ="disabled";
};
如果需要 .of_match_table 能和DTS匹配上,需要将status的状态从disabled改成okay。即:
isp0: isp@ff910000 {
compatible ="rockchip,rk3399-isp","rockchip,isp";
......
status ="okay";
};
isp0: isp@ff920000 {
compatible ="rockchip,rk3399-isp","rockchip,isp";
......
status ="okay";
};
(4) 执⾏probe函数
platform设备与驱动⼀旦匹配成功,将会执⾏驱动的probe函数,camsys驱动的probe函数定义如下,省略掉了部分⾮核⼼的代码:
static int camsys_platform_probe(struct platform_device *pdev)
{
......
/* 获取dts⽂件中的compatible的节点并打印出来 */
err =of_property_read_string(dev->of_node,"compatible",&compatible);
if(err <0){
camsys_err("get compatible failed!");
八年级数学上册期中试卷}else{
/* compatible = “rockchip,rk3399-isp” */
camsys_trace(1,"compatible is %s\n", compatible);
}祝福短信 中秋
/* 通过节点中包含的IC类型来判断具体是哪款IC */退税流程怎么操作
if(strstr(compatible,"rk3368"))
CHIP_TYPE =3368;
else if(strstr(compatible,"rk3288"))
CHIP_TYPE =3288;
else if(strstr(compatible,"rk3366"))
CHIP_TYPE =3366;
else if(strstr(compatible,"rk3399"))
CHIP_TYPE =3399;
/* 配置选择的IC,即填充camsys_soc_priv_s结构体的.soc_cfg函数 */
camsys_soc_init(CHIP_TYPE);
/*********************************************************
* 根据设备节点dev的reg属性值,填充资源结构体r。第⼆个参数指明了使⽤
* 根据设备节点dev的reg属性值,填充资源结构体r。第⼆个参数指明了使⽤
安全横幅标语* reg属性中第⼏个属性值,⼀般设置为0,表⽰第⼀个。
*********************************************************/
err =of_address_to_resource(dev->of_node,0,®ister_res);
/* map irqs,从设备节点dev中读取第0个irq号 */
irq_id =irq_of_parse_and_map(dev->of_node,0);
/**********************************************************
* 为camsys_dev结构体分配内存并初始化,使⽤devm_kzalloc分配的内存在
* 注销这个驱动时会⾃动释放为camsys_dev结构体分配的内存。
**********************************************************/
camsys_dev =(camsys_dev_t *)devm_kzalloc(&pdev->dev,
sizeof(camsys_dev_t),
GFP_KERNEL);
/
* spin_lock_init(&camsys_dev->lock); */
/* 初始化互斥锁 */
mutex_init(&camsys_dev->extdevs.mut);
INIT_LIST_HEAD(&camsys_dev->extdevs.list);
INIT_LIST_HEAD(&camsys_dev->extdevs.active);
INIT_LIST_HEAD(&camsys_dev->list);
/* IRQ init,将上⾯获取的irq_id保存在结构体irq的成员变量irq_id中 */
camsys_dev->irq.irq_id = irq_id;
/* 初始化⾃旋锁⽤于中断 */
spin_lock_init(&camsys_dev->irq.lock);
/* 初始化链表头 */
INIT_LIST_HEAD(&camsys_dev->irq.irq_pool);
INIT_LIST_HEAD(&camsys_dev-&slist);
/* get soc operation */
/***********************************************************
* camsys_soc_get中的操作⽐较简单,就是如果判断全局变量camsys_soc_p
* 不为空则返回全局变量camsys_soc_p。这个全局变量camsys_soc_p在上⾯
* 的函数camsys_soc_init()中分配并初始化。
***********************************************************/
沈阳什么时候解封camsys_dev->soc =(void*)camsys_soc_get();
/* Register mem init */
/* 为camsys_meminfo_t结构体分配内存并初始化,该结构体主要⽤于设备内存地址映射 */ meminfo =kzalloc(sizeof(camsys_meminfo_t), GFP_KERNEL);
/* 通过平台资源register_res映射出内存虚拟地址 */
meminfo->vir_base =
(unsigned long)devm_ioremap_resource(dev,®ister_res);
/* 将上⾯获取的vir_base保存在结构体camsys_dev的成员变量rk_isp_base中 */
camsys_dev->rk_isp_base = meminfo->vir_base;
/* CAMSYS_REGISTER_MEM_NAME是宏,对应字符串"CamSys_RegMem" */
strlcpy(meminfo->name, CAMSYS_REGISTER_MEM_NAME,sizeof(meminfo->name)); /* 获取meminfo的物理地址以及长度 */
meminfo->phy_base = register_res.start;
meminfo->size = d - register_res.start +1;
/* 将链表meminfo->list插⼊camsys_dev-&slist链表头之后 */
list_add_tail(&meminfo->list,&camsys_dev-&slist);
/* I2c mem init */
/******************************************************************
* __get_free_page()分配连续的物理地址,⽤于整页分配。它返回值是⼀个虚拟地址。 * 它们与真实的物理地址只有⼀个固定的偏移,因此存在较简单的转换关系。
******************************************************************/
i2cmem =__get_free_page(GFP_KERNEL);
i2cmem =__get_free_page(GFP_KERNEL);
/****************************************************************
* 随着linux的长时间运⾏,空闲页⾯会越来越少,为了防⽌linux内核进⼊请求页⾯
* 的僵局中,Linux内核采⽤页⾯回收算法(PFRA)从⽤户进程和内核⾼速缓存中回收
* 内存页框,并根据需要把要回收页框的内容交换到磁盘上的交换区。
* 调⽤SetPageReserved函数可以使页⾯不被交换。
****************************************************************/
SetPageReserved(virt_to_page(i2cmem));
/* 再次为camsys_meminfo_t结构体分配内存并初始化,该结构体主要⽤于I2C内存地址映射 */ meminfo =kzalloc(sizeof(camsys_meminfo_t), GFP_KERNEL);
/* CAMSYS_I2C_MEM_NAME是宏,对应字符串"CamSys_I2cMem" */
strlcpy(meminfo->name, CAMSYS_I2C_MEM_NAME,sizeof(meminfo->name));
meminfo->vir_base = i2cmem;
/**************************************************************
* __get_free_page()分配的物理地址是以 PAGE_OFFSET(3G) 为起点进⾏计算,
* 所以__get_free_page() 所分配的物理页⾯被映射到了 PAGE_OFFSET 开始的
* 虚拟地址中,所以其返回值实际是虚拟地址,需要通过virt_to_phys()转换为
* 物理地址,通过virt_to_phys函数让虚拟地址减去3G内存即得到物理地址。
**************************************************************/
meminfo->phy_base =virt_to_phys((void*)i2cmem);
/* 因为分配的内存⼤⼩为⼀页,所以这⾥meminfo->size的长度为⼀页 */
老人生日祝福语meminfo->size = PAGE_SIZE;
/* 将新的链表meminfo->list插⼊camsys_dev-&slist链表头之后 */
list_add_tail(&meminfo->list,&camsys_dev-&slist);
/* 不了解这段代码块有什么意义 */
{
unsigned int*tmpp;
tmpp =(unsigned int*)meminfo->vir_base;
*tmpp =0xfa561243;
}
/* Special init */
{
if(camsys_mipiphy_probe_cb(pdev, camsys_dev)<0){
camsys_err("Mipi phy probe failed!");
}
}
/* 因为使⽤的是mipi接⼝,这段条件编译并没有起到作⽤ */
#if (defined(CONFIG_CAMSYS_MRV))
camsys_mrv_probe_cb(pdev, camsys_dev);
#elif (defined(CONFIG_CAMSYS_CIF))
camsys_cif_probe_cb(pdev, camsys_dev);
#else
camsys_err("camsys driver haven't been complie");
#endif
camsys_trace(1,"%s memory:",dev_name(&pdev->dev));
/* 遍历camsys_dev-&slist,对meminfo类型的数据进⾏处理 */
list_for_each_entry(meminfo,&camsys_dev-&slist, list){
if(strcmp(meminfo->name, CAMSYS_I2C_MEM_NAME)==0){
camsys_dev->devmems.i2cmem = meminfo;
camsys_trace(1,
" I2c memory (phy: 0x%lx vir: 0x%lx size: 0x%x)",
meminfo->phy_base,
meminfo->vir_base,
meminfo->size);
}
if(strcmp(meminfo->name, CAMSYS_REGISTER_MEM_NAME)==0){
camsys_dev-&istermem = meminfo;
camsys_dev-&istermem = meminfo;
camsys_trace(1,
" Register memory (phy: 0x%lx vir: 0x%lx size: 0x%x)",
meminfo->phy_base,
meminfo->vir_base,
meminfo->size);
}
}
camsys_dev->phy_cb = camsys_phy_ops;
camsys_dev->pdev = pdev;
/******************************************************
* 把camsys_dev赋值给pdev->driver_data,pdev是平台总线设备,
* 对于整个驱动是可见的,所以可以通过platform_get_drvdata来
* 获取data。
*******************************************************/
platform_set_drvdata(pdev,(void*)camsys_dev);
/
* Camsys_devs list add */
spin_lock(&camsys_devs.lock);
list_add_tail(&camsys_dev->list,&camsys_devs.devs);
spin_unlock(&camsys_devs.lock);
/* 该函数实际上只是初始化了g_ext_fsh_devs.dev_list链表头 */
camsys_init_ext_fsh_module();
camsys_trace(1,"Probe %s device success ",dev_name(&pdev->dev));
return0;
}
在这个probe函数中通过DTS获取了⼀些平台资源,⽐如确定IC类型(RK3399),地址以及中断irq;分配了⼀个camsys_dev结构体,其对应着⼀个具体的摄像头设备,对其进⾏设置;初始化需要⽤到的⾃旋锁和互斥锁。分配了两个meminfo内存并获取它们的物理地址与虚拟地址以及长度,最后将设置
好的camsys_dev保存在pdev使其全局可见,因为之后我们需要操作到这个结构体。
(5) probe函数中重要的函数解析
在probe函数中有⼀个⽐较重要的函数camsys_mipiphy_probe_cb,其定义如下:
int camsys_mipiphy_probe_cb(
struct platform_device *pdev, camsys_dev_t *camsys_dev)
{
......
/* 从dts⽂件中读取32位的属性值,并保存在mipiphy_cnt变量中 */
err =of_property_read_u32(dev->of_node,
"rockchip,isp,mipiphy",&mipiphy_cnt);
if(err <0){
camsys_err("get property(rockchip,isp,mipiphy) failed!");
goto fail;
}else{
camsys_trace(2,"%s have %d mipi phy\n",
dev_name(&pdev->dev), mipiphy_cnt);
}
/* 给mipiphy分配内存并初始化 */
mipiphy =kzalloc(sizeof(camsys_phyinfo_t)*mipiphy_cnt, GFP_KERNEL);
camsys_dev->mipiphy = mipiphy;
/* str是⼀个具有32个元素的数组,在这⾥将每个元素置0 */
memset(str,0x00,sizeof(str));
for(i =0; i < mipiphy_cnt; i++){
meminfo =NULL;
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论