platform平台总线

目录

–——————-platform平台总线简介———————-

文章索引:
1.何为平台总线
2.平台总线下管理的2员大将
3.platform平台总线工作原理2---体系怎么建立起来,怎么工作的
4platform平台总线的工作原理3
5.总线设备驱动程序和平台设备在注册之后怎样来匹配的问题

1.何为平台总线
1》相对于usb,pci,iic等物理总线来说,platform总线是虚拟的,抽象出来的
《2》回顾裸机中讲的,cpu与外部通信的两种方式,地址总线式链接(32的cpu就有0-4G直接集成在cpu内部,以地址指针方式直接访问,没有具体的总线链接就用虚拟的platform平台总线来控制内部的外设)和专用接口式(iic,pci,usb等这些外部总线链接的)链接。平台总线对应地址总线式链接设备,也就是soc内部集成的各种内部外设

soc

32位的cpu有4G地址空间,即cpu可以操作0~4G地址空间,每个soc内部的外设对应的寄存器等都可以通过0~4g的地址来通过地址指针的形式直接访问,所以这些设备就称为地址总线连接的,但是soc外部的外设,如USB设备,通过usb协议来根cpu连接,他们最大的区别,地址总线链接的时候每个外设都被映射了一个地址,所以可以直接通过地址指针的方式访问该设备,但是usb,iic总线链接的设备等就得通过对应的协议来通信

外部专用接口链接就是具体真实的物理总线链接的设备,而扩展到cpu内部的虚拟地址的设备是不对应具体的总线,所以发明了platform虚拟总线,那类设备就统一用platform虚拟总线来挂接,所有集成在soc内部的设备都是platform总线

《3》思考:为什么要有平台总线?进一步思考:为什么要有总线的概念
因为大多数设备都是集成在soc内部,和cpu直接连接,将其直接扩展到内部地址空间,因此他们本身就不该就有总线的概念,本身就不属于总线的链接方式,但是如果一部分设备设计得有总线,一部分没总线就太乱了,所以除了iic,pci,spi等的设备就归类到平台总线来便于管理

学驱动目地就是安装上一个设备,能够把他驱动起来,能够用你的程序来控制它,这就是我们的目标
2.平台总线下管理的2员大将
1》platform工作体系都定义在drivers/base/platform.c中
《2》两个结构体:platfirm_device和platform_dirver平台总线设备结构体和驱动结构体,就是具体设备抽象出来的
structplatform_device {
constchar *name; //这个非常重要,设备名(这就是设备的唯一标识,身份证),将来用于匹配,每个设备的名字是不能一样的
int id; //index索引,可能用于记录顺序等
bool id_auto;
structdevice dev; //这个就是内核描述设备的基类
××××××××××××××××××××××××××××××××××××××××××××××
u32 num_resources; //设备使用到resourse的个数,什么是资源(内核中有限的都叫地址,中断号,io地址等,因为内核中要统一管理资源,所以下面有resource接口体,譬如,一个中断号,一段地址就是一个资源等)
structresource *resource;
××××××××××××××××××××××××××××××××××××××××××××××这两个变量加起来表示了一个资源数组,有首地址和元素个数就能知道数组的大小了,为什么要分开而不是直接写一个数组呢?这就是c高级里面数组元素的缺陷就是必须事先指定元素个数,所以你并不知道要多少个,这就容易造成资源的浪费,所以在内核里可以使用kzalloc来分配,再去指向它就可以了
conststruct platform_device_id *id_entry;
//平台设备的id(设备id表:主要用于好多个型号不一样的统一系列产品)
/*MFD cell pointer */
structmfd_cell *mfd_cell;

/*arch specific additions */
structpdev_archdata archdata; //自留地,自己根据特定情况使用
};

structplatform_driver {
int(*probe)(struct platform_device *);
int(*remove)(struct platform_device *);
void(*shutdown)(struct platform_device *);
int(*suspend)(struct platform_device *, pm_message_t state);
int(*resume)(struct platform_device *);
structdevice_driver driver; //所有驱动的id
conststruct platform_device_id *id_table;//设备id表,表明这个驱动支持哪些同类型的设备
};

structdevice_driver {
constchar *name; //这个名
structbus_type *bus;

structmodule *owner;
constchar *mod_name; /* used for built-in modules */

boolsuppress_bind_attrs; /* disables bind/unbind via sysfs */

conststruct of_device_id *of_match_table;
conststruct acpi_device_id *acpi_match_table;

int(*probe) (struct device*dev);//驱动探测函数,驱动去找设备:通过对设备进行初始化,看有没有反映,有反应说明可以连接上,重点实现probe和remove函数
int(*remove) (struct device *dev); //删除一个设备在当前驱动中卸载一个设备的支持
void(*shutdown) (struct device *dev); //关闭设备,如唱歌,只是关闭唱歌功能,还存在呢,只是暂时部工作
int(*suspend) (struct device *dev, pm_message_tstate);//挂起就是暂时终止,如电源管理进入待机,有待机就有唤醒
int(*resume) (struct device *dev); //这就是恢复函数,恢复待机状态
conststruct attribute_group **groups;

conststruct dev_pm_ops *pm;

structdriver_private *p;
};
《3》两个接口函数:platform_device_register和platform_driver_register

××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

3.platform平台总线工作原理2—体系怎么建立起来,怎么工作的
1》平台总线体系的工作流程
a.第一步:系统启动时在bus系统中注册platform
操作系统内核有一套管理总线的机制内核里肯定有一个总线子系统来管理总线,在内核起来就起来,而且是在platform注册之前,知道就行,不用去分析源代码
bus总线起来后就会注册platform总线了,这样platform就能工作了
b.第二步:内核移植的人员负责提供platform_device(dts描述?)
譬如说在写led驱动时,那么在内核移植时就需要提供platform_device结构体变量,那么我怎么来找到这些设备呢?内核移植就是把硬件的相关信息用软件写出来嘛,这些都是在系统移植的时候做
c.第三步:写驱动的人要负责提供platform_dirver
d.第四步:当两个都被注册了之后,他会调用platform的match函数,如果判断是匹配成功,则会认为找到了驱动,执行probe函数来完成驱动的安装,他就是这样的一个思路来给每一个设备找到具体的驱动
4.代码分析platform本身注册
《1》每种总线(不光是platform,usv,iic)都会有一个match方法,match方法来对总线下的device和driver
staticint platform_match(struct device *dev, struct device_driver *drv)
//形式参数为platform_device platform_driver里的两个元素
{
structplatform_device *pdev = to_platform_device(dev);
structplatform_driver *pdrv = to_platform_driver(drv);

/*Attempt an OF style match first */
if(of_driver_match_device(dev, drv))
return1;

/*Then try ACPI style match */
if(acpi_driver_match_device(dev, drv))
return1;

/*Then try to match against the id table */
if(pdrv->id_table)
returnplatform_match_id(pdrv->id_table,pdev) != NULL;

/*fall-back to driver name match */
return(strcmp(pdev->name, drv->name) == 0);
}
staticconst struct platform_device_id *platform_match_id(
conststruct platform_device_id *id,
structplatform_device *pdev)
{
while(id->name[0]) {
if(strcmp(pdev->name, id->name) == 0) {
pdev->id_entry= id;
returnid;
}
id++;
}
returnNULL;
}




《2》platform_match函数就是平台总线的匹配方法,该函数的工作方法是:如果有id_table就说明驱动可能支持多个设备,所以这时候要去对比id_table中所有的name,只要找到一个相同的就匹配上了不再找了,如果找完id_table都还没找到就说明没匹配上rugosa没有id_table或者没匹配上就直接对比device和driver的name,如果匹配上就匹配上了,如果还没匹配上就匹配失败




[rk_keys.c函数里]
staticconst struct of_device_id rk_key_match[] = { //用于描述驱动设备的compatible属性值,和dts里对应的硬件设备匹配
{.compatible = "rockchip,key", .data = NULL},
{},
};


taticstruct platform_driver keys_device_driver = {
.probe =keys_probe, //重点关注2:将来会调用probe函数
.remove =keys_remove,
.driver ={
.name ="rk-keypad", //重点关注1:这个driver的name必须和device的name相同
.owner =THIS_MODULE,
.of_match_table= rk_key_match,
#ifdefCONFIG_PM
.pm =&keys_pm_ops,
#endif
}

structdevice_driver {
constchar *name;
structbus_type *bus;

structmodule *owner;
constchar *mod_name; /* used for built-in modules */

boolsuppress_bind_attrs; /* disables bind/unbind via sysfs */

conststruct of_device_id *of_match_table;
/*
*Struct used for matching a device
*/
structof_device_id
{
char name[32];
char type[32];
char compatible[128];
constvoid *data;
};

×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

4.platform平台总线的工作原理3
4.1以.c为例分析platform设备和驱动的注册过程
《1》platform_dirver_register
《2》platform_device_register
4.2:resource怎么玩
4.3:match函数的调用轨迹
4.4probe函数的功能和意义



驱动工程师是需要关注二三步,一四步被内核自动处理了
b.第二步:内核移植的人员负责提供platform_device(dts描述?)
譬如说在写led驱动时,那么在内核移植时就需要提供platform_device结构体变量,那么我怎么来找到这些设备呢?内核移植就是把硬件的相关信息用软件写出来嘛,这些都是在系统移植的时候做
c.第三步:写驱动的人要负责提供platform_dirver
5.总线设备驱动程序和平台设备在注册之后怎样来匹配的问题×××××××××××就是解决了我的关于你写完一个驱动程序之后如何能找到你dts中描述的设备,因为现在的开发板的platform_device在内核移植的时候就已经被写好了,我只需要写驱动的驱动,关于在驱动中通过什么方式来指明我要匹配的设备就通过如下方法,但首先你得知道内核移植时对硬件设备的代码描述,即可以通过dts获取,或者通过对应的platform_device注册的时候去找到×××××××××××××××××
怎么知道是哪一个dts通过编译时的参数

各种总线match匹配函数

2012-11-1315:28 9498人阅读评论(3)收藏举报
分类:
usbdriver(5)
版权声明:本文为博主原创文章,未经博主允许不得转载。
目录(?)[+]
       当向Linux系统总线添加设备或驱动时,总是会调用各总线对应的match匹配函数来判断驱动和设备是否匹配,这些match函数之间都存在一定的差异,本文先对常用的match匹配函数进行讲解,以后会陆续添加新的内容。 
一.驱动和设备匹配过程常用数据结构

1.of_device_id

structof_device_id
{
charname[32];
chartype[32];
char compatible[128];
#ifdef__KERNEL__
void*data;
#else
kernel_ulong_tdata;
#endif
};
2.platform_device_id

structplatform_device_id {
charname[PLATFORM_NAME_SIZE];
kernel_ulong_tdriver_data   __attribute__((aligned(sizeof(kernel_ulong_t))));
};
二.平台设备、驱动匹配platform_match

         向系统添加平台驱动或添加设备时会调用平台总线platform_bus_type中的platform_match函数来匹配平台驱动和平台设备。
staticint platform_match(struct device *dev, struct device_driver *drv)
{
structplatform_device *pdev = to_platform_device(dev);
structplatform_driver *pdrv = to_platform_driver(drv);
/*通过驱动里定义了of_device_id项,则通过这一项来比对;*
if(of_driver_match_device(dev, drv))
return1;
/*如果在平台驱动中定义了id_table项,则通过对比id_table来判断*/
if(pdrv->id_table)
returnplatform_match_id(pdrv->id_table, pdev) != NULL;
/*通过对比平台设备名字和平台驱动名字来判断*/
return(strcmp(pdev->name, drv->name) == 0);
}
          由platform_match可以看出,驱动和设备是否匹配可以通过三种方式来进行判断,首先是通过of_device_id结构:
staticinline int of_driver_match_device(struct device *dev, conststruct device_driver *drv)
{
returnof_match_device(drv->of_match_table, dev) != NULL;
}
structof_device_id *of_match_device(const struct of_device_id*matches, const struct device *dev)
{
if((!matches) || (!dev->of_node))
returnNULL;
returnof_match_node(matches, dev->of_node);
}
conststruct of_device_id *of_match_node(const struct of_device_id*matches, const struct device_node *node)
{
if(!matches)
returnNULL;
while(matches->name[0] || matches->type[0] ||matches->compatible[0]) {
intmatch = 1;
if(matches->name[0])
match&= node->name && !strcmp(matches->name,node->name);
if(matches->type[0])
match&= node->type && !strcmp(matches->type,node->type);
if(matches->compatible[0])
match&= of_device_is_compatible(node, matches->compatible);
if(match)
returnmatches;
matches++;
}
returnNULL;
}
        如果driver中定义了of_device_id,则通过driver中的of_device_id和device中的device_node内容进行匹配判断,匹配工作由of_match_node来完成,该函数会遍历of_device_id列表,查找是否有成员与device_node相匹配,具体由matches的name,type和compatioble来进行对比,如果找到则返回相应的表项,否则返回null.如果没有定义of_device_id,device_node或不能找到对应的匹配项,则通过第二种方式platform_device_id来进行对比匹配,通过platform_match_id来完成:
staticconst struct platform_device_id *platform_match_id( conststruct platform_device_id *id, struct platform_device *pdev)
{
while(id->name[0]) {
if(strcmp(pdev->name, id->name) == 0) {
pdev->id_entry= id;
returnid;
}
id++;
}
returnNULL;
}
       platform_match_id函数遍历platfrom_device_id列表,通过比对平台设备与id的name来确定是否有匹配项,如果找到匹配的,则返回对应的id项,否则返回null。如果没有定义platform_device_id或没有找到匹配项,则通过第三种方式进行匹配,第三种方式通过比对平台设备和平台驱动的名字,如果相等,则匹配成功,否则失败。
三.i2c设备、驱动匹配i2c_device_match

        当向i2c总线添加驱动或设备时会调用i2c_device_match来进行匹配判断,i2c_device_match函数定义如下所示:
staticint i2c_device_match(struct device *dev, struct device_driver *drv)
{
structi2c_client*client = i2c_verify_client(dev);
structi2c_driver * driver;
if(!client)
return0;
/*通过of_device_id匹配*/
if(of_driver_match_device(dev, drv))
return1;
driver= to_i2c_driver(drv);
/*如果I2C驱动中定义了id_table,则通过id_table进行匹配;*/
if(driver->id_table)
returni2c_match_id(driver->id_table, client) != NULL;
return0;
}
如i2c_device_match所示,i2c通过两种方式进行匹配设备和驱动,一种是of_device_id,另一种是i2c_device_id,i2c_device_id数据结构和platform_device_id一样。I2C里的两种匹配方式和之前的platform判断方式都是一样,这里就不展开。
四.usb设备、驱动匹配usb_device_match

当向usb总线上注册驱动或添加设备时,就会调用usb_match_device进行驱动和设备配对,函数如下:
staticint usb_device_match(struct device *dev, struct device_driver *drv)
{
if(is_usb_device(dev)) {
if(!is_usb_device_driver(drv))
return0;
return1;
}else if (is_usb_interface(dev)) {
structusb_interface *intf;
structusb_driver *usb_drv;
conststruct usb_device_id *id;
if(is_usb_device_driver(drv))
return0;
intf= to_usb_interface(dev);
usb_drv= to_usb_driver(drv);
id= usb_match_id(intf, usb_drv->id_table);
if(id)
return1;
id= usb_match_dynamic_id(intf, usb_drv);
if(id)
return1;
}
return0;
}

       从函数可以看出,match分成两部分,一部分用于匹配usb设备,另一部分用于匹配usb接口,对于usb设备,在初始化时会设置成usb_device_type,而usb接口,则会设成usb_if_device_type。而函数中的is_usb_device和is_usb_interface就是通过这两个属性来判别的,如果为判定为设备,则进入到设备分支,否则进入到接口分支继续判断。
       usb设备驱动通过usb_register_device_driver接口来注册到系统,而usb接口驱动则通过usb_register来注册到系统,驱动工程师的工作基本上集中在接口驱动上,所以通常是通过usb_register来注册usb驱动的。 
       不管是设备驱动usb_device_driver,还是接口驱动usb_driver数据结构中都包含了structusbdrv_wrap项,其定义如下:
structusbdrv_wrap {
structdevice_driver driver;
intfor_devices;
}
        数据结构中的for_devices用来表示该驱动是设备驱动还是接口驱动,如果为设备驱动,则在用usb_register_device_driver注册时,会将该变量for_devices设置成1,而接口驱动则设为0.
       usb_device_match中的is_usb_device_driver函数就是通过获取上而结构中的for_devices来进行判断是设备还是接口驱动的,函数定义如下:
staticinline int is_usb_device_driver(struct device_driver *drv)
{
returncontainer_of(drv, struct usbdrv_wrap, driver)->for_devices;
}
       当进入is_usb_device分支后,再通过is_usb_device_driver来判断是否为设备驱动,如果是则返回1,表示匹配成功,它接受所有usb设备。
       当进入到接口分支后,也会先用is_usb_device_driver来进行判断,如果不是设备驱动则继续判断,否则退出;然后再通过usb_match_id函数来判断设备和驱动中的usb_device_id是否匹配,usb_match_id定义如下:
conststruct usb_device_id *usb_match_id(struct usb_interface*interface,  const struct usb_device_id *id)
{
if(id == NULL)
returnNULL;
for(; id->idVendor || id->idProduct || id->bDeviceClass||  id->bInterfaceClass || id->driver_info; id++) {
if(usb_match_one_id(interface, id))
returnid;
}
returnNULL;
}
        遍历接口驱动中的usb_device_id列表项,只要usb_device_id结构中的idVendor,idProduct,DeviceClass,binterfaceClass,driver_info项有效就调用usb_match_one_id进行判断,如找到匹配项则函数返回1,否则返回0。
intusb_match_one_id(struct usb_interface *interface,const structusb_device_id *id)
{
structusb_host_interface *intf;
structusb_device *dev;
if(id == NULL)
return0;
intf= interface->cur_altsetting;
dev= interface_to_usbdev(interface);
if(!usb_match_device(dev, id))
return0;
if(dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC && 
 !(id->match_flags& USB_DEVICE_ID_MATCH_VENDOR) &&
(id->match_flags& (USB_DEVICE_ID_MATCH_INT_CLASS| USB_DEVICE_ID_MATCH_INT_SUBCLASS| USB_DEVICE_ID_MATCH_INT_PROTOCOL)))
return0;
if((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
 (id->bInterfaceClass!= intf->desc.bInterfaceClass))
return0;
if((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) && 
(id->bInterfaceSubClass!= intf->desc.bInterfaceSubClass))
return0;
if((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) && 
 (id->bInterfaceProtocol!= intf->desc.bInterfaceProtocol))
return0;
return1;
}
usb_match_one_id和函数中的usb_match_device都是围绕着usb_device_id进行匹配的,该结构定义如下:
structusb_device_id {
/*which fields to match against? */
__u16match_flags;
/*Used for product specific matches; range is inclusive */
__u16idVendor;
__u16idProduct;
__u16bcdDevice_lo;
__u16bcdDevice_hi;
/*Used for device class matches */
__u8bDeviceClass;
__u8bDeviceSubClass;
__u8bDeviceProtocol;
/*Used for interface class matches */
__u8bInterfaceClass;
__u8bInterfaceSubClass;
__u8bInterfaceProtocol;
/*not matched against */
kernel_ulong_tdriver_info;
};
         match_flags用来规定驱动匹配时的具体项,如match_flags包含USB_DEVICE_ID_MATCH_VENDOR,则是通过驱动中的usb_device_id和设备dev中的idVendor来判断。


×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值