Linux 驱动设计主要是根据分层分离思想,i2c子系统分为i2cocre、adapter、及device_driver层,其实adapter也是个device,只不过是我们主控芯片的I2C控制接口而已,我们的主控芯片有几个I2C接口就有几个adapter;
i2ccore这一层linux已经帮我们实现,主要的工做是类似platform总线的作用,负责drvier及设备的注册,相比platform多了个adapter的注册管理工作,以及i2c的数据发送接收等等算法,说算法有点夸大,其实就是按照i2c的通讯协议做的收发时序而已;
adapter这一层一般主控芯片厂家会提供,所以也不需自己编写,s3c2440芯片这一层是采用的s3c2410的适配器驱动,大致框架是采用platform虚拟总线,向i2ccore注册adapter驱动;
device_driver这端的驱动就要求自己编写了,这主要是我们外接设备的驱动,比如一些i2c接口的三轴加速度计、at24c0x系列的eeprom等等……这部分代码其实就是实现如何操作他们,比如发什么数据给他们可以启动他们、发什么数据给他们可以读到数据。
按照i2ccore的框架,device_driver 分为两部分内容,一个是device的注册,另一部分是driver的注册,在两边分别完成注册的时候,会分别调用i2ccore的match函数,进行匹配,匹配和platform的匹配方式一致,是通过名字进行匹配,不过这个名字和platform的名字不一样而已;如下列出device及driver注册在i2ccore中的调用过程,从过程中可以看出匹配、绑定及调用driver端的probe函数过程:
注册device:
i2c_new_device device_register device_add bus_probe_device /* 判断device与driver是否匹配 */ device_attach /* 匹配成功,调用bind函数绑定 */ device_bind_driver /* 绑定成功,调用driver端的probe函数 */ driver_deferred_probe_del ( dev ); driver_deferred_probe_trigger();
注册driver:
i2c_register_driver driver_register bus_add_driver driver_attach /* 在总线上的没个设备轮训匹配,实际是调用__driver_attach函数进行匹配 */ bus_for_each_dev ( drv->bus, NULL, drv, __driver_attach ); if ( !dev->driver ) // 如果设备还未绑定driver driver_probe_device ( drv, dev ); ret = really_probe ( dev, drv ); dev->driver = drv; // 设备和driver绑定 ret = drv->probe ( dev ); // 调用driver的probe driver_bound ( dev ); // device和driver绑定
总结下就是编写i2c设备驱动,和platform虚拟总线一致,只不过最后是注册到了i2c总线而已;linux驱动还是采用分离分层的思想设计驱动。