當向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
strut 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;
};
https://blog.csdn.net/fanqipin/article/details/8153053
match_flags用來規定驅動匹配時的具體項,如match_flags包含USB_DEVICE_ID_MATCH_VENDOR,則是通過驅動中的usb_device_id和設備dev中的idVendor來判斷。