{ \ { } }; MODULE_DEVICE_TABLE(i2c, mpu3050_ids); static const struct of_device_id mpu3050_of_match[] = { { .compatible = \ { }, }; MODULE_DEVICE_TABLE(of, mpu3050_of_match); static struct i2c_driver mpu3050_i2c_driver = { .driver = { .name = \ .owner = THIS_MODULE, .pm = &mpu3050_pm, .of_match_table = mpu3050_of_match, }, .probe = mpu3050_probe, .remove = mpu3050_remove, .id_table = mpu3050_ids, }; module_i2c_driver(mpu3050_i2c_driver); 可以看到,实际驱动中喜欢将电源管理集成在i2c_driver的driver成员中。
UNIVERSAL_DEV_PM_OPS这个名字很犀利,貌似是“宇宙终极驱动电源管理大法”的样子:
#define UNIVERSAL_DEV_PM_OPS(name, suspend_fn, resume_fn, idle_fn) \\ const struct dev_pm_ops name = { \\ SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \\ SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \\ } #define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \\ .suspend = suspend_fn, \\ .resume = resume_fn, \\ .freeze = suspend_fn, \\ .thaw = resume_fn, \\ .poweroff = suspend_fn, \\ .restore = resume_fn, #define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \\ .runtime_suspend = suspend_fn, \\ .runtime_resume = resume_fn, \\ .runtime_idle = idle_fn, 结合MPU3050的驱动将其完整展开可以得到:
static const struct dev_pm_ops mpu3050_pm = { .suspend = mpu3050_suspend, .resume = mpu3050_resume, .freeze = mpu3050_suspend, .thaw = mpu3050_resume, .poweroff = mpu3050_suspend, .restore = mpu3050_resume, .runtime_suspend = mpu3050_suspend, .runtime_resume = mpu3050_resume, .runtime_idle = NULL, } 对电源管理有兴趣的可以去查阅pm.h,其中对电源管理有详尽的说明了,这里不做分析。可以看到,在电源管理中,有很多成员实际上是一样的,在现实驱动中这样的情况也经常出现,所以会有“终极电源管理大法”宏的出现了。
of_match_table是OpenFirmware相关,在3.0(具体版本本人不清楚)kernel后对arm平台引入了Device Tree,可通过dts配置文件代替大量板级代码,有兴趣可自行查阅。
上边说过,i2c_driver的多样化最多,从mpu3050的驱动注册中也可以发现,其注重实现的为probe与电源管理,其中probe最为重要(好像是废话,哪个驱动这个都是最重要的-。-)。因为主要是从驱动的角度看待I2C子系统,所以这里不详尽分析mpu3050的代码,只以其为例说明I2C驱动大体框架。在mpu3050的probe主要对此传感器进行上电、工作模式初始化、注册INPUT子系统接口、关联中断处理程序(在中断处理线程中上报三轴参数)等工作。
关于I2C设备驱动的小总结
I2C设备驱动通常只是需要挂载在I2C总线(即依附于I2C子系统),I2C子系统对于设备驱动来说只是一个载体、基石。许多设备的主要核心是建立在其他子系统上,如重力传感器、三轴传感器、触摸屏等通常主要工作集中在INPUT子系统中,而相机模块、FM模块、GPS模块大多主要依附于V4L2子系统。这也能通过I2C设计理念证明,I2C的产生正是为了节省外围电路复杂度,让CPU使用有限的IO口挂载更多的外部模块。假设CPU的扩展IO口足够多,我想I2C也没什么必要存在了,毕竟直接操作IO口驱动设备比I2C来的更简单。
I2C adapter的注册
如上表所示,对于I2C adapter的注册有两种途径:i2c_add_adapter 或
i2c_add_numbered_adapter,两者的区别是后者在注册时已经指定了此I2C适配器的总线号,而前者的总线号将由系统自动分配。
其各自的声明格式为:
int i2c_add_adapter(struct i2c_adapter *adapter) int i2c_add_numbered_adapter(struct i2c_adapter *adap) 在i2c_add_numberd_adapter使用前必须制定adap->nr,如果给-1,说明还是叫系统去自动生成总线号的。
使用场景
之所以区分开两种I2C adapter的注册方式,是因为他们的使用场景有所不同。
? i2c_add_adapter的使用经常是用来注册那些可插拔设备,如USB PCI设备等。主板上的其他模块与其没有直接联系,说白了就是现有模块不在乎新加入的I2C适配器的总线号是多少,因为他们不需
要。反而这个可插拔设备上的一些模块会需要其注册成功的适配器指针。回看一开始就分析的i2c_client,会发现不同场景的设备与其匹配的适配器有着这样的对应关系:
? 1. i2c_register_board_info需要指定已有的busnum,而i2c_add_numbered_adapter注册前已经指定总线号; 2. i2c_new_device需要指定adapter指针,而i2c_add_adapter注册成功后恰好这个指针就有了。 想象这样一个场景:新设备插入后,对应的驱动程序通过i2c_add_adapter注册自己的I2C适配器,然后根据与小弟们的协定将其是适配器指针存放在某处,相当于对小弟们(依附在其上的I2C设备)说:“看见没?你们注册你们自己的设备的时候就通过这个就能找到我,就能跟我混了!”然后驱动程序继续,当执行到对自己的I2C设备注册时候,小弟们去约定地点找老大留下的记号,发现有效信息后,一拥而上:“看!老大在那!!!” ? i2c_add_numbered_adapter用来注册CPU自带的I2C适配器,或是集成在主板上的I2C适配器。主板上的其他I2C从设备(client)在注册时候需要这个总线号。 通过简短的代码分析看一看他们的区别究竟如何,以及为什么静态注册的i2c_client必须要在adapter注册前(此处会精简部分代码,只留重要部分):
int i2c_add_adapter(struct i2c_adapter *adapter) { int id, res = 0; res = idr_get_new_above(&i2c_adapter_idr, adapter, __i2c_first_dynamic_bus_num, &id); //动态获取总线号 adapter->nr = id; return i2c_register_adapter(adapter); //注册adapter } int i2c_add_numbered_adapter(struct i2c_adapter *adap) { int id; int status; if (adap->nr == -1) /* -1 means dynamically assign bus id */ return i2c_add_adapter(adap); status = i2c_register_adapter(adap); return status; } 可见,最终他们都是通过i2c_register_adapter注册适配器:
static int i2c_register_adapter(struct i2c_adapter *adap) { int res = 0; /* Can't register until after driver model init */ //时序检查 if (unlikely(WARN_ON(!i2c_bus_type.p))) { res = -EAGAIN; goto out_list; } /* Sanity checks */ if (unlikely(adap->name[0] == '\\0')) { //防御型代码,检查适配器名称 pr_err(\ \ return -EINVAL; } if (unlikely(!adap->algo)) { //适配器是否已经完成了通信方法的实现 pr_err(\ \ return -EINVAL; } rt_mutex_init(&adap->bus_lock); mutex_init(&adap->userspace_clients_lock); INIT_LIST_HEAD(&adap->userspace_clients); /* Set default timeout to 1 second if not already set */ if (adap->timeout == 0) adap->timeout = HZ; dev_set_name(&adap->dev, \ adap->dev.bus = &i2c_bus_type; adap->dev.type = &i2c_adapter_type; res = device_register(&adap->dev); //注册设备节点 if (res) goto out_list; /* create pre-declared device nodes */ //创建预-声明的I2C设备节点