天天看點

linux usb驅動 probe,linux USB裝置驅動之2:usb裝置的probe全過程

本文将詳細講述2.6.22下的一個USB裝置插上linux系統的PC後是如何一步一步調到我們的usb裝置驅動的probe函數的,我們知道我們的USB驅動的probe函數中的一個參數是interface結構,是以一般來說,一個USB裝置中的任何一個接口都應該有對應的一個驅動程式,當然也有例外(如cdc-acm).

我們知道USB裝置都是通過插入上層HUB的一個Port來連入系統并進而被系統發現的,當USB裝置插入一個HUB時,該HUB的那個port的狀态就會改變,進而系統就會知道這個改變,此時會調用hub_port_connect_change()

static void hub_connect_change(struct usb_hub *hub, int portl, u16 portstatus, u16 portchange)

{

….

usb_new_device(udev);

}

該函數建立一個usb_device的對象udev,并初始化它,接着調用usb_new_device()來擷取這個usb裝置的各種描述符并為每個interface找到對用的driver.

int usb_new_device(struct usb_device *udev)

{

….

err = usb_get_configuration(udev);

….

device_add(&udev->dev);

}

該函數首先調用usb_get_configuration()來擷取裝置的各種描述符(裝置描述符,配置描述符等),接着調用device_add()來把這個USB裝置添加到USB系統中去,也就是在這個過程中系統回去為這個裝置找到相應的驅動.在2.6的早期的一些版本中在分析配置描述符後得到interface的同時把interface作為裝置來調用device_add()的

int device_add(struct device *dev)

{

….

if((error = bus_add_device(dev)))

bus_attach_device(dev);

}

這個函數是個通用的裝置管理函數,它會為每個裝置調用bus_add_device來把這個裝置添加到相應bus的裝置清單中去.接着調用bus_attach_device()來比對對應的驅動程式,對于USB裝置來說第一次調用bus_attach_device()時的參數dev代表的是整個usb裝置(以後usb裝置中的interface也會作為裝置調用這個函數).

int bus_attach_device(struct device *dev)

{

ret = device_attach(dev);

}

這個函數就是用來為裝置找到相應的裝置驅動程式的(通過調用device_attach()實作).

int device_attach(struct device *dev)

{

ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);

}

該函數調用bus_for_each_drv()來從總線上已注冊的所有驅動中找出比對的驅動程式.

int bus_for_each_drv(struct bus_type *bus,

struct device_driver *start,

void *data,

int (*fn)(struct device_driver *, void *))

{

….

while((drv = next_driver(&i)) && !error)

error = fn(drv, data);//傳回0将繼續搜尋,傳回錯誤值将停止搜尋.

}

該函數周遊bus上的所有驅動程式,并為每個驅動調用fn()來檢視是否比對.這裡的fn就是__device_attach.

static int __device_attach(struct device_driver *drv, void *data)

{

struct device *dev = data;

return driver_probe_device(drv, dev);

}

int driver_probe_device(struct device *drv, struct device *dev)

{

if(drv->bus->match && !drv->bus_match(dev, drv))

ret = really_probe(dev, drv);

}

對于usb驅動來說,我們通過usb_registe()r來注冊我們的驅動程式,這個函數會為我們的驅動程式對象(usb_driver)中的bus指定為usb_bus_type:

Struct bus_type usb_bus_type = {

.match = usb_device_match,

….

}

是以對于usb驅動會首先調用usb_device_match().

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

{

if(is_usb_device(dev)) {

….

}

else

{

usb_match_id();

usb_match_dynamic_id();

}

}

這個函數隻是做一些粗略的比對,如果比對成功則傳回1,然後由really_probe來做進一步的比對,如果比對失敗則傳回0,并且really_probe也不會在執行.這個函數的調用保證了dev, drv要麼都是裝置級别的(即dev代表usb裝置,drv代表usb裝置驅動),要麼都是接口級别的(即dev代表usb裝置的一個interface,drv代表usb接口驅動).

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

{

dev->driver = drv;//先指派,以後的probe過程中會用到

else if(drv->probe)

ret = drv->probe(dev);

probe_failed:

dev->drvier = NULL;//probe失敗,重設它

}

對于usb來說這個函數的調用有2種分支, 1: dev,drv代表的是裝置級别的, 2 dev,drv代表的是接口級别的.其他情況組合在usb_device_match中被過濾掉了,

分支1: dev,drv代表的是裝置級别:

此時的drv肯定是usb_generic_driver.因為在目前的usb系統中隻有這個driver是代表整個裝置的驅動,它是在usb_init中被注冊的,而我們通常寫的usb驅動都是代表一個interface的.

struct usb_device_driver usb_generic_driver = {

.probe = generic_probe,

}

是以,此時的drv->probe将調用generic_probe().

static int generic_probe(struct usb_device *udev)

{

c = choose_configuration(dev);

if(c >= 0) {

err = usb_set_configuration(udev, c);//設定配置,并注冊interface.

}

}

該函數為這個usb裝置選擇一個合适的配置,并注冊這個配置下面的interface.

int usb_set_configuration(struct usb_device *dev, int configuration)

{

for(I = 0; I < nintf; i++) {

struct usb_interface *intf = cp->interface[i];

device_add(&intf->dev);

}

}

該函數比較重要,但我們隻關心probe過程是以省掉了很多東西.它為目前配置下的每個interface調用device_add()函數,根據前面的分析可知,這個過程将會走到接下來我們要分析的分支2.

分支2: dev,drv代表的是interface級别:

此時的dev代表着一個interface,而drv就代表了我們自己的usb驅動.但是我們應當看到drv是device_driver類型,而我們寫的usb驅動的類型一般是usb_driver,是以這裡的probe和我們自己寫的probe顯然不是同一個.實際上這裡的drv是我們的驅動對象裡内嵌的一個子對象(因為linux下是以的驅動都必須用device_driver來代表,).那這個子對象的probe函數是在哪裡指派的呢?這就要看usb_register函數了,

跟蹤這個函數我們可以看到這裡的probe函數實際上是usb_probe_interface(所有的usb interface驅動都是一樣的).

static int usb_probe_interface(struct device *dev)

{

struct driver = to_usb_driver(dev->driver);//dev->driver在really_probe中設定.

error = driver->probe(intf, id);//這個就是我們自己寫的probe函數了.

}

driver->probe(intf, id);這就調用到我們自己寫的代碼裡面了,

整個流程大概就是這樣:

linux usb驅動 probe,linux USB裝置驅動之2:usb裝置的probe全過程