/************************************************************************************
*本文为个人学习记录,如有错误,欢迎指正。
* http://www.cnblogs.com/xiaojiang1025/p/6367061.html
* http://www.cnblogs.com/xiaojiang1025/p/6367910.html
* http://www.cnblogs.com/xiaojiang1025/p/6369065.html
* https://www.cnblogs.com/lifexy/p/7569371.html
* https://www.cnblogs.com/biaohc/p/6667529.html
************************************************************************************/
1.1 platform总线简介
在Linux2.6以后的设备驱动模型中,需关心总线,设备和驱动这三种实体,总线将设备和驱动绑定。Linux内核中的总线主要负责管理挂接在该总线下的设备与驱动,将设备信息与驱动程序分类管理,提高驱动程序的可移植性。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相同地,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
对于依附在USB、PCI、I2C、SPI等物理总线来 这些都不是问题。但是在嵌入式系统里面,在SoC中集成的独立外设控制器,挂接在SoC内存空间的外设等却不依附在此类总线。基于这一背景,Linux发明了一种总线,称为platform。相对于USB、PCI、I2C、SPI等物理总线来说,platform总线是一种虚拟、抽象出来的总线,实际中并不存在这样的总线。
1.2 platform总线核心代码
(1)platform总线相关代码:/kernel/driver/base/platform.c 文件;
(2)相关数据结构体定义:/kernel/include/linux/platform_device.h 文件中。
2. platform总线机制
platform总线在sysfs下的目录为/sys/bus/platform,该目录下有两个子目录和相关的platform属性文件;/platform/devices目录下存放的是platform总线下的所有设备,/platform/drivers目录下存放的是platform总线下的所有驱动程序。
platform总线的驱动与设备的管理与匹配机制如下图所示。
2.1 platform_device对象
在设备树出现之前,设备信息只能使用C语言的方式进行编写,在Linux3.0之后,设备信息就开始同时支持两种编写方式:设备树、C语言。对于ARM平台,使用设备树封装设备信息是将来的趋势,但是由于历史原因,当下的内核中这两种种方式并存。
1)设备树
使用设备树,手动将设备信息写到设备树中之后,内核就可以自动从设备树中提取相应的设备信息并将其封装成相应的platform_device对象,并注册到相应的总线中。从而,我们就不需要对设备信息再进行编码。
2)C语言
使用C语言,我们需要将使用内核提供的结构将设备信息进行手动封装,这种封装又分为两种形式,一种是使用平台文件(静态),将整个板子的所有设备都写在一个文件中并编译进内核。另一种是使用模块(动态),将我们需要的设备信息编译成模块在insmod进内核。
本文主要讨论C语言的方式实现设备信息的填充。
(1)struct platform_device
Linux内核中,使用struct platform_device来描述一个注册在platform总线上的设备。对设备信息进行编码,其实就是创建一个struct platform_device对象,platform_device和其他设备一样,都是device的子类。
struct platform_device
{ const char * name;//设备的名称,是设备和驱动match的方法之一 int id; //表示这个platform_device对象表征了几个设备,当多个设备有共用资源的时候(MFD),里面填充相应的设备数量,如果只是一个,填-1 struct device dev; //父类对象 u32 num_resources;//资源的数量,即resource数组中元素的个数,我们用ARRAY_SIZE()宏来确定数组的大小 struct resource * resource;//资源指针,如果是多个资源就是struct resource[]数组名 const struct platform_device_id *id_entry;//设备和驱动match的方法之一 /* arch specific additions */ struct pdev_archdata archdata; };
struct platform_device的父类struct device。我们通常关心里面的platform_data和release,前者是用来存储私有设备信息的,后者是供当这个设备的最后引用被删除时被内核回调,注意和rmmod没关系。
struct device { struct device *parent; struct device_private *p; struct kobject kobj; const char *init_name; /* initial name of the device */ struct device_type *type; struct mutex mutex; /* mutex to synchronize calls to its driver.*/ struct bus_type *bus; /* type of bus device is on */ struct device_driver *driver; /* which driver has allocated this device */ void *platform_data; /* Platform specific data, device core doesn't touch it */ struct dev_pm_info power; #ifdef CONFIG_NUMA int numa_node; /* NUMA node this device is close to */ #endif u64 *dma_mask; /* dma mask (if dma'able device) */ u64 coherent_dma_mask; struct device_dma_parameters *dma_parms; struct list_head dma_pools; /* dma pools (if dma'ble) */ struct dma_coherent_mem *dma_mem; /* internal for coherent mem override */ struct dev_archdata archdata; #ifdef CONFIG_OF struct device_node *of_node; #endif dev_t devt; /* dev_t, creates the sysfs "dev" */ spinlock_t devres_lock; struct list_head devres_head; struct klist_node knode_class; struct class *class; const struct attribute_group **groups; /* optional groups */ void (*release)(struct device *dev); }; struct device_private { struct klist klist_children; struct klist_node knode_parent; struct klist_node knode_driver; struct klist_node knode_bus; void *driver_data; struct device *device; };