platform总线实际上并不对应任何硬件上的总线,有时又称为伪总线。由于设备模型中的驱动和设备关联机制必须要有一条总线才能发挥作用,对于那些没有连接在实际总线上的设备,如果想使用这种机制,就需要将它连接在一条假想的总线上。platform总线就可以起到这个作用,通常,platform总线上的设备都是直接与CPU相连的底层设备。
使用platform总线的好处是可以将驱动与设备分离,驱动所需的平台相关数据则在定义设备时提供,使驱动具有更大的跨平台通用性。
platform总线的相关定义和声明在头文件中。
struct bus_type platform_bus_type = {
}
if(strcmp(pdev->name, id->name) == 0){ pdev->id_entry=id; return id; }
id++;
return NULL;
platform总线的用户态事件钩子函数是platform_uevent,定义如下:
static int platform_uevent(struct device *dev, struct kobj_uevent_env *env){
struct platform_device *pdev = to_platform_device(dev);
add_uevent_var(env,"MODALIAS=%s%s", PLATFORM_MODULE_PREFIX, (pdev->id_entry) ? pdev->id_entry->name : pdev->name);
return 0;
}
struct platform_device{
const char *name;//设备名称
int id;//设备编号,-1表示只有一个
struct device dev;//内嵌的设备对象
u32 num_resources;//资源数组中的元素的个数
struct resource *resource;//指向描述资源的数组
struct platform_device_id *id_entry;//保存与驱动匹配后的ID
}
num_resources和resource用于描述设备需要的全部资源,即端口号,IO内存、中断号等,内存将它们统称为资源。
struct resource{
}
* IORESOURCE_IO :端口号资源
* IORESOURCE_MEM :IO内存资源
* IORESOURCE_IRQ :中断号资源
* IORESOURCE_DMA :DMA资源
int platform_device_register(struct platform_device *pdev);//注册
void platform_device_unregister(struct platform_device *pdev);//注销
通常一个系统中的platform设备的个数和类型总是固定的,因此可以把它们的地址放在一个数组中,然后用下面的接口函数同时注册:
int platform_add_devices(struct platform_device **devs, int num);
struct platform_driver{
int (*probe)(struct platform_device *pdev)
int (*remove)(struct platform_device *pdev);
struct device_driver driver;//内嵌的驱动对象
struct platform_device_id *id_table;//用于匹配的ID数组
.......
}
platform设备通常先于platform驱动而加载,这样探测操作就会只发生在模块的加载过程中,从而可以安全的放入初始化数据段,以节约内存。
注册和注销platform驱动的接口函数如下:
int platform_driver_register(struct platform_driver *drv);
void platform_driver_unregister(struct platform_driver *drv);
struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned num);
int platform_get_irq(struct platform_device *dev, unsigned int num);
关键字:platform总线 伪总线
引用地址:platform总线
1.platform总线基本特征
.name = "platform",
.match = platform_match,
.uevent = platform_uevent,
......
匹配操作是platform_match,定义如下:
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev=to_platform_device(dev);
struct platform_driver *pdrv=to_platform_driver(drv);
if(pdrv->id_table)
return platform_match_id(pdrv->id_table,pdev)!=NULL;
return (strcmp(pdev->name,drv->name)==0);
}
其中调用的platform_match_id函数定义如下:
static const struct platform_device_id *platform_match_id(
struct platform_device_id *id, struct platform_device *pdev)
{
while(id->name[0]){
}
}
显然在匹配时,先将设备的名称与驱动的id_table成员数组中列举的名称逐一比较,若相同则匹配成功,否则再将设备的名字与驱动的名字比较,若相同则匹配成功。
也就是说,platform总线上的设备发送用户态事件时,会增加一个MODALIAS环境变量。
2.platform设备
设备的名称最终会被设为name.id,其中id是设备的编号。如果id的赋值为-1,表示设备的个数只可能是1个,这时设备的名字为name.
resource_size_t start;//资源区域的起始值
resource_size_t end;//资源区域的结束值
const char *name;//申请资源的设备名称
unsigned long flags;//资源的标志
struct resource *parent,*sibling,*child;//树指针
每个struct resource类型的数据描述资源的一段区域,同一类型的所有资源以树的形式组织在一起。资源的标志包含了资源类型的说明,常用类型如下:
平台相关的其他数据则放在platform设备的dev成员的platform_data指针所指向的内存中,。
下面是platform设备的注册和注销函数的原型:
注册platform设备时,首先向platform总线注册相应的设备,然后将设备的端口号和IO内存资源添加到系统的资源树种,注销platform设备则是相反的操作。
3.platform驱动
在platform驱动的成员driver的各种操作函数中,实际上直接回调了platform驱动的各种操作,只是将参数的类型转换了下。因为platform总线本身没有probe和remove操作,所以当platform驱动注册时,若platform总线上已经注册了匹配的设备,就会调用驱动的probe方法。
在platform驱动的probe函数中,可以用下面这个函数获取设备的各种资源:
*dev指向要获取资源的platform设备。
*type资源的类型
*num:资源的索引,表示要获得设备的资源数组中第几个此类型的资源,索引从0开始编号。
获取中断号的接口函数: