Linux里SPI与I2C创建设备的流程
Soc里怎么知道设备的存在? 我高中的时候就曾想过,Windows怎么知道我电脑上有什么硬件呢?后来刚接触驱动这一块时,被Soc上设备的创建困惑了好一阵子。当然现在看起来已经清晰不少了。Linux下的SPI与I2C算是轻量级框架了,是去熟悉Linux驱动模型一个不错的切入点。当初在学校尽去关心操作系统的实现而无视驱动开发这一块,后来找工作时才发现,满大街招的都是搞驱动的。于是转回去熟悉Linux的设备驱动模型。实习的时候接触了第一个子系统是I2C,当初强行看了四五天,愣是看出一点名堂。可惜后面没有从事相关方面的项目,也没有及时总结,后来都忘的差不多了。
歪题了。因为简历上写了SPI与I2C的内容,今天大致浏览了一下两个子系统的代码。比较在意的是SPI设备与I2C设备的创建。Soc中,SPI控制器与I2C控制器是由platform_device与platform_driver带入系统中来的。其对应的probe与两个子系统交互,从而告知系统自身的存在。如果系统中有四个相同型号的控制器,那么就有4个platform_device和1个platform_driver。注册到子系统这一过程,最主要是把自己加到子系统维护的控制器列表里。SPI里是spi_register_master,I2C里是i2c_register_adapter。然而,这两个函数里还有一个任务:把挂在控制器(总线)上的设备带到系统中来。
这样做其实相当无奈。像PCI,USB这类协议,设备都是即时产生的,你把设备插上去,系统就能知道它的存在。而Soc上没有这么动态,系统上有什么设备必需在平台相关的代码写出来。这件事就在注册控制器的时候做。spi_register_master的最后会遍历一个叫board_list的链表,平台相关文件会把平台上有的SPI设备一相相关信息挂在上面。其中最重要的是bus number。spi_master一个属性就是bus number,这是私下约定的控制器的标识。于是,每新加一个spi_master,就去这个链表上找相符的“设备原型”,并将其实例化,也就是从设备原型提供的信息来构建spi_device。设备就是在这里出现!然后将设备注册进Linux设备驱动模型里,后面与驱动的匹配就是标准的那一套了。但是这些过程都被拆散了,一口气调用了几个函数。
每个spi_device生成的时候都于其所处的spi_master绑定了。也就是说spi_master与spi_device融为一体,得到spi_device的引用就可以用spi_master里的方法进行数据传输了。有spi_register_master而没有spi_register_device,至于后者对应的方法--spi_register_driver--基本上不需做什么事,设备已被加入标准设备驱动模型里,驱动也只需加入其中即可。
I2C框架看起来要更高级一些。SPI经历的那些它都经历了:i2c_register_adapter里将i2c_adapter加入控制器列表(直接加在了i2c_bus上),遍历一些预定义的设备原型链表,bus number匹配则用i2c_new_device生成设备,i2c_device与i2c_adapter的绑定,将i2c_device注册到标准设备驱动模型。I2C高级的地方在于,它有一定的动态性。它用一个很简单的机制来决定i2c_adapter需不需要静态扫描预定义的链表。如果不扫描列表,怎么知道新加入的i2c_adapterr上有哪些设备呢?由i2c_driver去发现!也就是说,每次加入一个i2c_adapter就会挨个让已注册的i2c_adapter在它上面肆虐一把,找找有没有自己支持的设备。其中有一部分2.4里面的老模型流程还留着,一些老流程的公共代码抽出来了,一个新模型的i2c_driver->detect方法来做更精确的设备探测。找到设备便生成i2c_device,并将其注入标准设备驱动模型。
这里有个与SPI不同的假定。SPI里生成设备的机会只有一次,就是在添加spi_master时扫描预定义设备模型链表。以后似乎就没有机会加入设备了。而I2C里生成设备的时机却更灵活:除了添加i2c_adapter时扫描预定义设备模型链表,添加i2c_adpater或i2c_driver时都有可能触发新设备的建立。这像标准设备驱动模型里device与driver的匹配,所有可能的匹配都不会落下。当然,I2C的工作量更大了,不仅新加i2c_adapter时要去遍历驱动,新加i2c_driver时也要去遍历i2c_adapter。这个工作自然是在i2c_register_driver里做。这个遍历又比较蹊跷,spi_master有一个单独的链表连起来(虽然好像没有人会去遍历它),i2c_adapter却挂在i2c_bus上--i2c_adapter和i2c_device都挤在i2c_bus的device链表上。
整体看起来,SPI框架还处于I2C框架的早期水平。不过不能在初始化以外的地方添加SPI设备有点不爽,不能在模块动态创建,这意味着调试SPI设备驱动时每次都得从编内核,重新烧录,重新启动。以前就确实有过这样的经历,当时似乎改了让系统可以动态添加SPI设备,现在是一点也记不得了。内核的I2C框架其实已经有点小复杂了,不过它文档挺好的。以前浏览了一遍,有些东西不好一次看懂,也就没有深究,以后有机会再去文档里找找关于设计方面的内容吧。