Linux下设备与驱动match过程
2018年06月13日 11:01:27
阅读数:17
在之前的学习过程中,我们知道了Linux 设备驱动总线架构,抽象硬件上设备都是挂载在总线BUS上的,所以,定义了各种总线结构体。这里用platform_bus_type为例
[plain] view plain copy
- <code class="language-plain">struct bus_type platform_bus_type = {
- <span> </span>.name<span> </span>= "platform",
- <span> </span>.dev_groups<span> </span>= platform_dev_groups,
- <span> </span>.match<span> </span>= <span style="background-color:rgb(51,255,51);">platform_match</span>,
- <span> </span>.uevent<span> </span>= platform_uevent,
- <span> </span>.pm<span> </span>= &platform_dev_pm_ops,
- };
- </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函数具体是根据什么条件,将驱动和设备正确匹配成功的。
-
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);
-
/* When driver_override is set, only bind to the matching driver */
-
if (pdev->driver_override)
-
return !strcmp(pdev->driver_override, drv->name);
-
/* Attempt an OF style match first */
-
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>
-
return 1;
-
/* Then try ACPI style match */
-
if (<span style="background-color:rgb(51,255,51);">acpi_driver_match_device</span>(dev, drv))//通过acpi_
-
return 1;
-
/* Then try to match against the id table */
-
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);/*通过对比平台设备名字和平台驱动名字来判断*/}
一般都是匹配第一个条件
-
static inline int of_driver_match_device(struct device *dev,
-
const struct device_driver *drv)
-
{
-
return <span style="font-size:14px;background-color:rgb(51,255,51);">of_match_device(drv->of_match_table, dev)</span> != NULL;
-
}
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整个字符串)。
-
static const struct of_device_id spmi_match_table[] = {
-
<span style="background-color:rgb(51,255,51);"> { .compatible = "qcom,qpnp-haptic", },</span>
-
{ },
-
};
在dts文件当中也是定义的
-
pm660_haptics: qcom,haptic@c000 {
-
<span style="background-color:rgb(51,255,51);">compatible = "qcom,qpnp-haptic";</span>
-
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来判断。