驱动的抽象层其实类似一个管理层,管理多种资源,使资源之间更加有序,高效的工作。使软件的复用性和鲁棒性更强。其核心思想是面向对象的编程思想,创建抽象的概念,增加抽象层之间的逻辑给更高层,屏蔽底层的“杂乱无章”。
理清芯片之间的关系和逻辑
硬件上8541e通过mipi和i2c连接到9288,9288通过gmsl信号线连接到96705编串器上,96705和0144是通过dvp接口连接,包含i2c。软件上平台8541e通过i2c访问配置9288,通过9288的映射寄存器来访问96705和0144,所以我们软件实现的目标是在8541e上获取0144的图像数据,9288和96705是一种通信的编串和解串方式,不影响数据格式。
所以8541e看到的是关闭打开摄像头,读取摄像头id,设置摄像头格式,帧率等接口。不需要关心中间环节。这也是驱动抽象层给上层看到的接口。对于软件的底层,我们可以把serdes抽象成桥,把传感器抽象成sensor。不管什么桥,不管什么sensor都适用这套方法。
创建对象结构体
三个抽象对象:
- 面向平台的camera操作方法和数据
- 面向brg的操作方法和数据
- 面向sensor的操作方法和数据
1和3的区别在于比如1的power on会包含2和3的power on。
灵活使用链表管理
链表用于管理已创建的数据对象,把相同的数据结构连起来,方便管理。比如在sensor的power中会用到brg的i2c_rw_0144的方法,就可以遍历brg的链表,查找链表中名字为“9288”的brg,当然如果注册了多个brg,可以按照名字区分,也可以按照id来区分。
static LIST_HEAD(brg_list);
list_add_tail(&brg->list, &brg_list);
static inline struct serdbrg_device * sensor2brg(void)
{
struct serdbrg_device *ambrg;
list_for_each_entry(ambrg, &brg_list, list) {
if (ambrg->id == 0)
return ambrg;
}
printk("Can't find serdbrg in list\n");
return NULL;
}
创建逻辑层,抽象接口
当需要创建一个brg的设备驱动时候,我们需要调用一个注册的接口:
int _register_serdes_brg(struct serdbrg_device *brg, struct serdbrg_ops *ops)
当需要创建一个sensor的设备驱动时候,我们需要调用一个注册的接口:
int _register_serdes_brg_device(struct sensor_brg_device *sensor, struct sensor_brg_ops *ops)
调用这两个接口之前,要完成对象结构体的创建,就是传送到函数的参数。
这两个函数所做的逻辑主要是:
- 获取设备id信息,不成功则退出注册,并提示用户失败。成功进入下一步。
- 加入设备到链表。
- 创建文件系统接口。
- 提供文件系统接口访问逻辑,主要是camera抽象函数的实现,比如:
Camera的stream on方法,上层访问文件系统接口,传入参数POWERON,然后底层调用brg和sensor的方法,输出图像:
gbrg->ops->brg_power_on(gbrg,1) ;
gbrg->ops->brg_hw_init_pre(gbrg);
gbrg->ops->brg_set_clk_si(gbrg,0);
gsensor->ops->init_device(gsensor);
gbrg->ops->brg_vout_config(gbrg,1);
- 文件系统接口可以使用字符设备ioctl的方法也可以使用sys文件属性的方法,都可以实现。
总结
虽然本例程没有对多个sensor和brg做测试,但是原理上是可以很容易的兼容多个设备,通过id或者名字进行选择操作方法。