of_match_device

Linux下设备与驱动match过程

2018年06月13日 11:01:27

阅读数:17

在之前的学习过程中,我们知道了Linux 设备驱动总线架构,抽象硬件上设备都是挂载在总线BUS上的,所以,定义了各种总线结构体。这里用platform_bus_type为例

 

[plain] view plain copy

  1. <code class="language-plain">struct bus_type platform_bus_type = {  
  2. <span>  </span>.name<span>      </span>= "platform",  
  3. <span>  </span>.dev_groups<span>    </span>= platform_dev_groups,  
  4. <span>  </span>.match<span>     </span>= <span style="background-color:rgb(51,255,51);">platform_match</span>,  
  5. <span>  </span>.uevent<span>        </span>= platform_uevent,  
  6. <span>  </span>.pm<span>        </span>= &platform_dev_pm_ops,  
  7. };  
  8. </code>  

由之前的文章可以找到driver 和device 在注册时,Linux系统会调用bus->match函数,对新register的设备和驱动进匹配,如果匹配成功,再回去走driver->probe()函数。

可以参考文章linux设备驱动程序注册过程详解(一)

 

108 static inline int driver_match_device(struct device_driver *drv,
109                       struct device *dev)
110 {
111     return drv->bus->match ? drv->bus->match(dev, drv) : 1;
112 }

match 过程

调用的驱动注册总线的下得match函数(函数指针,callback)

下面来看看match函数具体是根据什么条件,将驱动和设备正确匹配成功的。

 
  1. static int platform_match(struct device *dev, struct device_driver *drv)

  2. {

  3. struct platform_device *pdev = to_platform_device(dev);

  4. struct platform_driver *pdrv = to_platform_driver(drv);

  5.  
  6. /* When driver_override is set, only bind to the matching driver */

  7. if (pdev->driver_override)

  8. return !strcmp(pdev->driver_override, drv->name);

  9.  
  10. /* Attempt an OF style match first */

  11. if (<span style="background-color:rgb(51,255,51);">of_driver_match_device</span>(dev, drv))//<span style="background-color:rgb(255,255,255);">/*通过驱动里定义了of_device_id项,则通过这一项来比对;*</span>

  12. return 1;

  13.  
  14. /* Then try ACPI style match */

  15. if (<span style="background-color:rgb(51,255,51);">acpi_driver_match_device</span>(dev, drv))//通过acpi_

  16. return 1;

  17.  
  18. /* Then try to match against the id table */

  19. if (pdrv->id_table)

<span style="background-color:rgb(255,255,255);">/*如果在平台驱动中定义了id_table项,则通过对比id_table来判断*/</span>
       return <span style="background-color:rgb(51,255,51);">platform_match_id</span>(pdrv->id_table, pdev) != NULL;

/* fall-back to driver name match */return (strcmp(pdev->name, drv->name) == 0);/*通过对比平台设备名字和平台驱动名字来判断*/}

一般都是匹配第一个条件

 
  1. static inline int of_driver_match_device(struct device *dev,

  2. const struct device_driver *drv)

  3. {

  4. return <span style="font-size:14px;background-color:rgb(51,255,51);">of_match_device(drv->of_match_table, dev)</span> != NULL;

  5. }

of_driver_match_device这个函数最终调用到__of_match_node()函数,在这个函数里,通过把device_driver的of_match_table(of_device_id结构体的数组)和device里的of_node(device_node结构体)进行匹配,匹配方式是分别比较两者的name、type、和compatible字符串,三者要同时相同(一般name、和type为空,只比较compatible字符串,比较compatible时,是比较整个字符串,不管字符串里的逗号的,直接compare整个字符串)。

 
  1. static const struct of_device_id spmi_match_table[] = {

  2. <span style="background-color:rgb(51,255,51);"> { .compatible = "qcom,qpnp-haptic", },</span>

  3. { },

  4. };

在dts文件当中也是定义的

 
  1. pm660_haptics: qcom,haptic@c000 {

  2. <span style="background-color:rgb(51,255,51);">compatible = "qcom,qpnp-haptic";</span>

  3. reg = <0xc000 0x100>;//device address

这里要求compatible属性必须要求一致,否则,就不能匹配成功。

 

2.一个驱动可以匹配多个设备

之前有说过驱动和设备的对应关系是一对一和一对多的,也就是驱动可以匹配多个设备。

也就有了主设备号代表驱动,从设备号对应具体设备。

这里也是根据of_device_id 的table数组表,可以添加多个驱动。

参考文章:https://blog.csdn.net/ruanjianruanjianruan/article/details/61622053

 

  当向linux系统总线添加设备或驱动时,总是会调用各总线对应的match匹配函数来判断驱动和设备是否匹配,这些match函数之间都存在一定的差异,本文先对常用的match匹配函数进行讲解,以后会陆续添加新的内容。 

一. 驱动和设备匹配过程常用数据结构

1. of_device_id

struct of_device_id
{

charname[32];
char type[32];
char compatible[128];

#ifdef __KERNEL__

void*data;

#else

kernel_ulong_t data;

#endif

};

2. platform_device_id

struct platform_device_id {

char name[PLATFORM_NAME_SIZE];

kernel_ulong_t driver_data   __attribute__((aligned(sizeof(kernel_ulong_t))));

};

二. 平台设备、驱动匹配platform_match

          向系统添加平台驱动或添加设备时会调用平台总线platform_bus_type中的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);

/*通过驱动里定义了of_device_id项,则通过这一项来比对;*

if (of_driver_match_device(dev, drv))

return 1;

/*如果在平台驱动中定义了id_table项,则通过对比id_table来判断*/

if (pdrv->id_table)

return platform_match_id(pdrv->id_table, pdev) != NULL;

/*通过对比平台设备名字和平台驱动名字来判断*/

return (strcmp(pdev->name, drv->name) == 0);

}

           由platform_match可以看出,驱动和设备是否匹配可以通过三种方式来进行判断,首先是通过of_device_id结构:

static inline int of_driver_match_device(struct device *dev, const struct device_driver *drv)
{

return of_match_device(drv->of_match_table, dev) != NULL;

}

struct of_device_id *of_match_device(const struct of_device_id *matches, const struct device *dev)
{

if ((!matches) || (!dev->of_node))

return NULL;

return of_match_node(matches, dev->of_node);

}

const struct of_device_id *of_match_node(const struct of_device_id *matches, const struct device_node *node)

{

if (!matches)

return NULL;

while (matches->name[0] || matches->type[0] || matches->compatible[0]) {

int match = 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)
return matches;

matches++;

}

return NULL;

}

         如果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来完成:

static const struct platform_device_id *platform_match_id( const struct platform_device_id *id, struct platform_device *pdev)
{

while (id->name[0]) {

if (strcmp(pdev->name, id->name) == 0) {

pdev->id_entry = id;

 

return id;

}

id++;

}

return NULL;

 

}

        platform_match_id函数遍历platfrom_device_id列表,通过比对平台设备与id的name来确定是否有匹配项,如果找到匹配的,则返回对应的id项,否则返回null。如果没有定义platform_device_id或没有找到匹配项,则通过第三种方式进行匹配,第三种方式通过比对平台设备和平台驱动的名字,如果相等,则匹配成功,否则失败。

三. i2c设备、驱动匹配i2c_device_match

         当向i2c总线添加驱动或设备时会调用i2c_device_match来进行匹配判断,i2c_device_match函数定义如下所示:

static int i2c_device_match(struct device *dev, struct device_driver *drv)

{

struct i2c_client*client = i2c_verify_client(dev);

struct i2c_driver * driver;

if (!client)

return 0;

/* 通过of_device_id匹配 */

if (of_driver_match_device(dev, drv))

return 1;

driver = to_i2c_driver(drv);

/*如果I2C 驱动中定义了id_table,则通过id_table进行匹配;*/

if (driver->id_table)

return i2c_match_id(driver->id_table, client) != NULL;

return 0;

}

如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进行驱动和设备配对,函数如下:

static int usb_device_match(struct device *dev, struct device_driver *drv)

{

if (is_usb_device(dev)) {

if (!is_usb_device_driver(drv))

return 0;

return 1;

} else if (is_usb_interface(dev)) {

struct usb_interface *intf;

struct usb_driver *usb_drv;

const struct usb_device_id *id;

if (is_usb_device_driver(drv))

return 0;

intf = to_usb_interface(dev);

usb_drv = to_usb_driver(drv);

id = usb_match_id(intf, usb_drv->id_table);

if (id)

return 1;

id = usb_match_dynamic_id(intf, usb_drv);

if (id)

return 1;

}

return 0;

}

        从函数可以看出,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数据结构中都包含了struct usbdrv_wrap项,其定义如下:

struct usbdrv_wrap {

struct device_driver driver;

int for_devices;

}

         数据结构中的for_devices用来表示该驱动是设备驱动还是接口驱动,如果为设备驱动,则在用usb_register_device_driver注册时,会将该变量for_devices设置成1,而接口驱动则设为0.

        usb_device_match中的is_usb_device_driver函数就是通过获取上而结构中的for_devices来进行判断是设备还是接口驱动的,函数定义如下:

static inline int is_usb_device_driver(struct device_driver *drv)

{

return container_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定义如下:

const struct usb_device_id *usb_match_id(struct usb_interface *interface,  const struct usb_device_id *id)
{

if (id == NULL)

return NULL;

for (; id->idVendor || id->idProduct || id->bDeviceClass ||  id->bInterfaceClass || id->driver_info; id++) {

if (usb_match_one_id(interface, id))

return id;

}

return NULL;

}

         遍历接口驱动中的usb_device_id列表项,只要usb_device_id结构中的idVendor,idProduct,DeviceClass,binterfaceClass,driver_info项有效就调用usb_match_one_id进行判断,如找到匹配项则函数返回1,否则返回0 。

int usb_match_one_id(struct usb_interface *interface,const struct usb_device_id *id)
{

struct usb_host_interface *intf;

struct usb_device *dev;

if (id == NULL)

return 0;

intf = interface->cur_altsetting;

dev = interface_to_usbdev(interface);

if (!usb_match_device(dev, id))

return 0;

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)))

return 0;

if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&

 (id->bInterfaceClass != intf->desc.bInterfaceClass))

return 0;

if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) && 

(id->bInterfaceSubClass != intf->desc.bInterfaceSubClass))

return 0;

if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) && 

 (id->bInterfaceProtocol != intf->desc.bInterfaceProtocol))

return 0;

return 1;

}

usb_match_one_id和函数中的usb_match_device都是围绕着usb_device_id进行匹配的,该结构定义如下:

struct usb_device_id {

/* which fields to match against? */

__u16 match_flags;

/* Used for product specific matches; range is inclusive */

__u16 idVendor;

__u16 idProduct;

__u16 bcdDevice_lo;

__u16 bcdDevice_hi;

/* Used for device class matches */

__u8 bDeviceClass;

__u8 bDeviceSubClass;

__u8 bDeviceProtocol;

/* Used for interface class matches */

__u8 bInterfaceClass;

__u8 bInterfaceSubClass;

__u8 bInterfaceProtocol;

/* not matched against */

kernel_ulong_tdriver_info;

};

          match_flags用来规定驱动匹配时的具体项,如match_flags包含USB_DEVICE_ID_MATCH_VENDOR,则是通过驱动中的usb_device_id和设备dev中的idVendor来判断。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值