日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

Linux那些事儿 之 戏说USB(大结局)还是那个match

發布時間:2023/11/27 生活经验 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux那些事儿 之 戏说USB(大结局)还是那个match 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
從上次在幾米的向左走向右走遇到usb總線的那個match函數usb_device_match()開始到現在,遇到了設備,遇到了設備驅動,遇到了接口,也遇到了接口驅動,期間還多次遇到usb_device_match()。
每個設備也都有一條共同之路,與hub初戀,失身于usb_generic_driver,嫁給了接口驅動,被usb總線保養。設備沒有真正自由過,剛開始時在Default狀態動彈不得,稍后步入Address,無論外頭風光多好,都得與usb_generic_driver長廂廝守,沒得選擇,終于達到了Configured,又必須為自己的接口殫精竭慮,以便usb_device_match()能夠為它們找一個好人家。
不管怎么說,在這里我們會再次與usb_device_match()相遇,看看它怎么在接口和驅動之間搭起那座橋。
static int usb_device_match(struct device *dev, struct device_driver *drv)
{/* devices and interfaces are handled separately */if (is_usb_device(dev)) {/* interface drivers never match devices */if (!is_usb_device_driver(drv))return 0;/* TODO: Add real matching code */return 1;} else if (is_usb_interface(dev)) {struct usb_interface *intf;struct usb_driver *usb_drv;const struct usb_device_id *id;/* device drivers never match interfaces */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;
}
設備那條路已經走過了,現在走走13行接口那條路。19行,接口驅動的for_devices在usb_register _driver()里被初始化為0,所以這個把門兒的會痛痛快快的放行,繼續往下走,22行,遇到一對兒似曾相識的宏to_usb_interface和to_usb_driver,之所以說似曾相識,是因為早先已經遇到過一對兒to_usb_device和to_usb_device_driver。這兩對兒一對兒用于接口和接口驅動,一對兒用于設備和設備驅動,意思都很直白,還是看看include/linux/usb.h里的定義
#define	to_usb_interface(d) container_of(d, struct usb_interface, dev)
#define	to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver)
再往下走,就是兩個函數usb_match_id和usb_match_dynamic_id,它們都是用來完成實際的匹配工作的,只不過前一個是從驅動的id_table里找,看接口是不是被驅動所支持,后一個是從驅動的動態id鏈表dynids里找。驅動的id表分id_table和dynids兩種。顯然25~31這幾行的意思就是將id_table放在一個比較高的優先級的位置,從它里面找不到接口了才再從動態id鏈表里找。
當時講到struct usb_driver結構的時候并沒有詳細講它里面表示動態id的那個結構體struct usb_dynids,但是做人要厚道,不能太CCTV,所以現在補充一下,這個結構的定義在include/linux/usb.h里
struct usb_dynids {spinlock_t lock;struct list_head list;
};
它只有兩個字段,一把鎖,一個鏈表,都是在usb_register _driver()里面初始化的,這個list是驅動動態id鏈表的頭兒,它里面的每個節點是用另外一個結構struct usb_dynid來表示
struct usb_dynid {struct list_head node;struct usb_device_id id;
};
這里面就出現了一個struct usb_device_id結構體,也就是設備的id,每次添加一個動態id,就會向驅動的動態id鏈表里添加一個struct usb_dynid結構體。你現在應該可以想像到usb_match_id和usb_match_dynamic_id這兩個函數除了查找的地方不一樣,其它應該是沒什么差別的。所以接下來咱們只深入探討一下usb_match_id函數,至于usb_match_dynamic_id(),如果你實在無聊暫時找不到人生目標也可以去看看。它們都在driver.c里定義
const struct usb_device_id *usb_match_id(struct usb_interface *interface,const struct usb_device_id *id)
{/* proc_connectinfo in devio.c may call us with id == NULL. */if (id == NULL)return NULL;/* It is important to check that id->driver_info is nonzero,since an entry that is all zeroes except for a nonzeroid->driver_info is the way to create an entry thatindicates that the driver want to examine everydevice and interface. */for (; id->idVendor || id->idProduct || id->bDeviceClass ||id->bInterfaceClass || id->driver_info; id++) {if (usb_match_one_id(interface, id))return id;}return NULL;
}
5行,參數id指向的是驅動的那個設備花名冊,即id_table,如果它為空,那肯定就是不可能會匹配成功了。
13行,你可能會問為什么這里不詳細介紹一下struct usb_device_id結構,主要是《我是U盤里》已經說得非常之詳細和有趣了,俺這里實在沒必要耗費時間和口舌去說它,另一方面,它里面的那些元素都相當的暴露和直白,我相信依你的智商一眼就能明白個八九不離十。
那么這個for循環就是輪詢設備花名冊里的每個設備,如果符合了條件id->idVendor || id->bDeviceClass || id->bInterfaceClass || id->driver_info,就調用函數usb_match_one_id做深層次的匹配。本來,在動態id出現之前這個地方是沒有usb_match_one_id這么一個函數的,所有的匹配都在這個for循環里直接做了,但是動態id出現之后,同時出現了前面提到的usb_match_dynamic_id函數,要在動態id鏈表里做同樣的匹配,這就要避免代碼重復,于是就將那些重復的代碼提出來,組成了usb_match_one_id函數。
for循環的條件里可能出現的一種情況是,id的其它字段都為空,只有driver_info字段有實實在在的內容,這種情況下匹配是肯定成功的,不信的話等會兒你可以看usb_match_one_id(),這種驅動對usb接口來說是比較隨便的那種,不管啥接口都能和她對得上眼,為什么會出現這種情況?咱們已經知道,匹配成功后,接著就會調用驅動自己的probe函數,驅動在它里面還會對接口做進一步的檢查,如果真出現了這里所說的情況,意思也就是驅動將所有的檢查接口,和接口培養感情的步驟都攬在自己的probe函數里了,它會在那個時候將driver_info的內容取出來,然后想怎么處理就怎么處理,本來么,id里邊兒的driver_info就是給驅動保存數據用的。
還是看看usb_match_one_id()究竟是怎么匹配的吧,定義也在driver.c里
int usb_match_one_id(struct usb_interface *interface,const struct usb_device_id *id)
{struct usb_host_interface *intf;struct usb_device *dev;/* proc_connectinfo in devio.c may call us with id == NULL. */if (id == NULL)return 0;intf = interface->cur_altsetting;dev = interface_to_usbdev(interface);if (!usb_match_device(dev, id))return 0;return usb_match_one_id_intf(dev, intf, id);
}
8行,這個id指向的就是驅動id_table里的某一項了。
11行,獲得接口采用的設置,設置里可是有接口描述符的,要匹配接口和驅動,接口描述符里的信息是必不可少的。
12行,從接口的struct usb_interface結構體獲得usb設備的struct usb_device結構體,interface_to_usbdev的定義在include/linux/usb.h里
#define	to_usb_device(d) container_of(d, struct usb_device, dev)static inline struct usb_device *interface_to_usbdev(struct usb_interface *intf)
{return to_usb_device(intf->dev.parent);
}
usb設備和它里面的接口是怎么關聯起來的呢?就是上面的那個parent,接口的parent早在usb_generic_driver的generic_probe函數向設備模型提交設備里的每個接口的時候就被初始化好了,而且指定為接口所在的那個usb設備。這么一回顧,interface_to_usbdev的意思就很明顯了。
14行,這里又冒出來個usb_match_device(),接口和驅動之間的感情還真不是那么好培養的,一層一層的。不過既然存在就是有來頭的,它也不會毫無根據的出現,這里雖說是在接口和接口驅動之間匹配,但是接口的parent也是必須要符合條件的,這即合情也合理啊,你好不容易鼓足了勇氣向一個走在大街上一見鐘情的mm表白,你覺得mm的第一反應是什么?依照行規,很可能就是:你爸是干嗎的?是大款么?是當官的么?你要說不,那就別等第二反應了。所以說接口要想得到驅動的芳心,自己的parent符合驅動的條件也是很重要的,usb_match_device()就是專門來匹配接口parent的。同樣在driver.c里定義
int usb_match_device(struct usb_device *dev, const struct usb_device_id *id)
{if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&id->idVendor != le16_to_cpu(dev->descriptor.idVendor))return 0;if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&id->idProduct != le16_to_cpu(dev->descriptor.idProduct))return 0;/* No need to test id->bcdDevice_lo != 0, since 0 is nevergreater than any unsigned number. */if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&(id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice)))return 0;if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&(id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice)))return 0;if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&(id->bDeviceClass != dev->descriptor.bDeviceClass))return 0;if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&(id->bDeviceSubClass != dev->descriptor.bDeviceSubClass))return 0;if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&(id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))return 0;return 1;
}
這個函數采用了排比的修辭手法,美觀的同時也增加了可讀性。這一個個的if條件里都有一部分是將id 的match_flags和一個宏相與,所以弄明白match_flags的意思就很關鍵。罷了罷了,本來說不再浪費口舌在id里的那些字段上了,不過為了減少你驀然回首的次數,這里再說一下這個match_flags。
驅動的花名冊里每個設備都對應了一個struct usb_device_id結構體,這個結構體里有很多字段,都是驅動設定好的條條框框,接口必須完全滿足里面的條件才能夠被驅動所接受,所以說匹配的過程就是檢查接口是否滿足這些條件的過程。
當然你可以每次都按照id的內容一個一個的比較下去,但是經常來說,一個驅動往往只是想設定其中的某幾項,并不要求struct usb_device_id結構里的所有那些條件都要滿足。match_flags就是為了方便各種各樣的需求而生的,驅動可以將自己的條件組合起來,match_flags的每一位對應一個條件,驅動care哪個條件了,就將那一位置1,否則就置0。當然,內核里對每個驅動可能會care的條件都定義成了宏,供驅動去組合,它們都在include/linux/mod_devicetable.h里定義
#define USB_DEVICE_ID_MATCH_VENDOR		0x0001
#define USB_DEVICE_ID_MATCH_PRODUCT		0x0002
#define USB_DEVICE_ID_MATCH_DEV_LO		0x0004
#define USB_DEVICE_ID_MATCH_DEV_HI		0x0008
#define USB_DEVICE_ID_MATCH_DEV_CLASS		0x0010
#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS	0x0020
#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL	0x0040
#define USB_DEVICE_ID_MATCH_INT_CLASS		0x0080
#define USB_DEVICE_ID_MATCH_INT_SUBCLASS	0x0100
#define USB_DEVICE_ID_MATCH_INT_PROTOCOL	0x0200
你用自己的火眼金睛很容易的就能看出來這些數字分別表示了一個u16整數,也就是match_flags中的某一位。驅動比較在意哪個方面,就可以將match_flags里對應的位置1,在和接口匹配的時候自動就會去比較驅動設置的那個條件是否滿足。那整個usb_match_device()函數就沒什么說的了,就是從match_flags那里得到驅動都在意哪些條件,然后將設備保存在自己描述符里的自身信息與id里的相應條件進行比較,有一項比較不成功就說明匹配失敗,如果一項符合了就接著看下一項,接口parent都滿足條件了,就返回1,表示匹配成功了。

還是回到usb_match_one_id()繼續往下看,假設你運氣還不錯,parent滿足了驅動的所有條件,那就調用函數usb_match_one_id_intf

int usb_match_one_id_intf(struct usb_device *dev,struct usb_host_interface *intf,const struct usb_device_id *id)
{/* The interface class, subclass, protocol and number should never be* checked for a match if the device class is Vendor Specific,* unless the match record specifies the Vendor ID. */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 |USB_DEVICE_ID_MATCH_INT_NUMBER)))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;if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER) &&(id->bInterfaceNumber != intf->desc.bInterfaceNumber))return 0;return 1;
}
8行。這行的意思是,如果接口的parent,usb設備是屬于廠商定義的class,也就是不屬于storage等等標準的class,就不再檢查接口的class,subclass和protocol了,除非match_flags里指定了條件USB_DEVICE_ID_MATCH_VENDOR。16行之后的三個if也不用多說,前面是檢查接口parent的,這里就是檢查接口本身是不是滿足驅動的條件的。

當上面各個函數進行的所有檢查都完全匹配時,usb總線的match函數usb_device_match就會返回1表示匹配成功,之后接著就會去調用驅動的probe函數做更深入的處理,什么樣的處理?這是每個驅動才知道的事情,反正到此為止,core的任務是已經圓滿完成了,咱們的故事也就該結束了。

這個core的故事,從match開始,到match結束,它雖說不會遍及core的邊邊角角所有部分,但應該也有那么十之七八。在match的兩端是設備和設備的驅動,是接口和接口的驅動,這個故事里遇到的人,遇到的事,早就安排在那里了,由不得我們去選擇。在人生的路口上,早已經安排了那些人,那些事,決定你向左走還是向右走。既然如此,那就隨便走好了,想那么多干什么呢?

總結

以上是生活随笔為你收集整理的Linux那些事儿 之 戏说USB(大结局)还是那个match的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。