Linux那些事儿 之 戏说USB(31)驱动的生命线(三)
生活随笔
收集整理的這篇文章主要介紹了
Linux那些事儿 之 戏说USB(31)驱动的生命线(三)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
準備工作該做的都做了,別嫌太麻煩,什么事情都要經過這么一個階段,大家都明白。現在看看第二階段的重頭戲,看看設備是怎么從Address進入Configured的。1501行,如果已經在Configured狀態了,就得做些清理工作,退回到Address狀態。都清理些什么怎么去清理?別著急,要想學會,得仔細研究下message.c里的usb_disable_device函數。
先說下第二部分的工作,9行,actconfig表示的是設備當前激活的配置,只有它不為空時才有接下來清理的必要。
15~29這個for循環就是將這個配置的每個接口從設備模型的體系中刪除掉,將它們和對應的接口驅動分開,沒有驅動了,這些接口也就喪失了能力,當然也就什么作用都發揮不了了,這也是名字里那個disable的真正含意所在。
34~44行,將actconfig的interface數組置為空,然后再將actconfig置為空,這里你可能會有的一個問題是,為什么只是置為空,既然要清理actconfig,為什么不直接將它占用的內存給釋放掉?這個問題問的好,說明你足夠細心,不過你應該注意到actconfig只是一個指針,一個地址,你應該首先弄清楚這個地址里保存的是什么東西再決定是不是將它給釋放掉,那這個指針指向哪兒?它指向設備struct usb_device結構的config數組里的其中一項,當前被激活的是哪一個配置,它就指向config數組里的哪一項,你這里只是不想讓設備當前激活任何一個配置而已,沒必要將actconfig指向的那個配置給釋放掉吧,前面在設備生命線那里走了那么久,歷盡千辛萬苦才將設備各個配置的內容給拿過來放到config數組里,你這里如果給釋放掉,對得起誰啊,豈不要哭死。
那這么說的話另一個問題就出來了,既然actconfig指向了config里的一項,那為什么要把那個配置的interface數組給置為空,這不是修改了那個配置的內容,從而也修改了config數組的內容么?你先別著急,俺幫你回憶一下,在設備生命線那里取配置描述符的,解析返回的那堆數據時,只是把每個配置里的cache數組,也就是intf_cache數組給初始化了,并沒有為interface數組充實任何的內容,這里做清理工作的目的就是要恢復原狀,當然要將它置為空了,那么配置的interface數組又在哪里被充實了那? usb_set_configuration函數里第二個階段之后不是還有個第三個階段么,就在那里,你那時激活了哪個配置,就為哪個配置的interface數組動手術,填點東西。
45行,如果這個設備此時確實是在Configured狀態,就讓它回到Address。
現在回頭來說說第一部分的清理工作。這個部分主要就是為每個端點調用了usb_disable_endpoint函數,將掛在它們上面的urb給取消掉。為什么要這么做?你想想,能調用到usb_disable_device這個函數,一般來說設備的狀態要發生變化了,設備的狀態都改變了,那設備的那些端點的狀態要不要改變?還有掛在它們上面的那些urb需不需要給取消掉?這些都是很顯然的事情,就拿現在讓設備從Configured回到Address來說吧,在Address的時候,你只能通過缺省管道也就是端點0對應的管道與設備進行通信的,但是在Configured的時候,設備的所有端點都是能夠使用的,它們上面可能已經掛了一些urb正在處理或者將要處理,那么你這時讓設備要從Configured變到Address,是不是應該先將這些urb給取消掉?
還有個問題是參數skip_ep0是嘛意思?這里for循環的i是從skip_ep0開始算起,也就是說skip_ep0為1的話,就不需要對端點0調用usb_disable_endpoint函數了,按常理來說,設備狀態改變了,是需要把每個端點上面的urb給取消掉的,這里面當然也要包括端點0,但是寫代碼的哥們兒這里搞出個skip_ep0自然有他們的玄機,驀然回首一下,usb_set_configuration()調用這個函數的時候參數skip_ep0的值是什么?是1,因為這時候是從Configured回到Address,這個過程中,其它端點是從能夠使用變成了不能使用,但端點0卻是一直都很強勢,雖說是設備發生了狀態的變化,但在這兩個狀態里它都是要正常使用的,所以就沒必要disable它了。
什么時候需要disable端點0?目前版本的內核里俺只發現了兩種情況,一是設備要斷開的時候,一是設備從Default進化到Address的時候,雖說不管是Default還是Address,端點0都是需要能夠正常使用的,但因為地址發生改變了,毫無疑問,你需要將掛在它上面的urb清除掉。俺當時講設備生命線的時候,在設置完設備地址,設備進入Address后,第二種情況的這個步驟給有意無意的飄過了,主要是當時也不影響理解,現在既然遇到了,就把它給補上吧。
不要怪俺說得比較粗略,只是都在前面說過了,你既然已經看到這里了,只要用過那么一點點心就會明白這里是什么意思。
最后23行調用了一個usb_hcd_endpoint_disable函數,主要的工作還得它來做,不過這已經深入HCD的腹地了,就不多說了,還是飄回usb_disable_device()吧。在為每個端點都調用了usb_disable_endpoint()之后,還有一個小步驟要做,就是將設備struct usb_device結構體的toggle數組置為0。至于toggle數組干嗎的,為啥要被初始化為0,你還是驀然回首到設備那節去看吧。俺要接著飄回usb_set_configuration()了。
150行,又一次與熟悉的陌生人usb_control_msg()相遇了,每當我們需要向設備發送請求的時候它就會適時的出現,我們每個人是不是也都希望在自己的生活里有這么一個角色?
usb_control_msg這次出現的目的當然是為了SET_CONFIGURATION請求,這里只說一下它的那堆參數,看一下spec 9.4.7的那張表
SET_CONFIGURATION請求不需要DATA transaction,而且還是協議里規定所有設備都要支持的標準請求,也不是針對端點或者接口什么的,而是針對設備的,所以bRequestType只能為0x80,就是上面表里的00000000B,也就是151行的第一個0,wValue表示配置的bConfigurationValue,就是151行的configuration。
167行,將激活的那個配置的地址賦給actconfig。如果cp為空,重新設置設備的狀態為Address,并將之前準備的那些struct usb_interface結構體和new_interfaces釋放掉,然后返回。掃一下前面的代碼,cp有三種可能為空,一是參數configuration為-1,一是參數configuration為0,且從設備的config數組里拿出來的就為空,一是SET_CONFIGURATION 請求出了問題。不管怎么說,走到170行,cp還是空的,你就要準備返回了。
177行,事情在這里發展達到了高潮的頂端,設置設備的狀態為Configured。
void usb_disable_device(struct usb_device *dev, int skip_ep0)
{int i;struct usb_hcd *hcd = bus_to_hcd(dev->bus);/* getting rid of interfaces will disconnect* any drivers bound to them (a key side effect)*/if (dev->actconfig) {/** FIXME: In order to avoid self-deadlock involving the* bandwidth_mutex, we have to mark all the interfaces* before unregistering any of them.*/for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++)dev->actconfig->interface[i]->unregistering = 1;for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {struct usb_interface *interface;/* remove this interface if it has been registered */interface = dev->actconfig->interface[i];if (!device_is_registered(&interface->dev))continue;dev_dbg(&dev->dev, "unregistering interface %s\n",dev_name(&interface->dev));remove_intf_ep_devs(interface);device_del(&interface->dev);}/* Now that the interfaces are unbound, nobody should* try to access them.*/for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {put_device(&dev->actconfig->interface[i]->dev);dev->actconfig->interface[i] = NULL;}if (dev->usb2_hw_lpm_enabled == 1)usb_set_usb2_hardware_lpm(dev, 0);usb_unlocked_disable_lpm(dev);usb_disable_ltm(dev);dev->actconfig = NULL;if (dev->state == USB_STATE_CONFIGURED)usb_set_device_state(dev, USB_STATE_ADDRESS);}dev_dbg(&dev->dev, "%s nuking %s URBs\n", __func__,skip_ep0 ? "non-ep0" : "all");if (hcd->driver->check_bandwidth) {/* First pass: Cancel URBs, leave endpoint pointers intact. */for (i = skip_ep0; i < 16; ++i) {usb_disable_endpoint(dev, i, false);usb_disable_endpoint(dev, i + USB_DIR_IN, false);}/* Remove endpoints from the host controller internal state */mutex_lock(hcd->bandwidth_mutex);usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);mutex_unlock(hcd->bandwidth_mutex);/* Second pass: remove endpoint pointers */}for (i = skip_ep0; i < 16; ++i) {usb_disable_endpoint(dev, i, true);usb_disable_endpoint(dev, i + USB_DIR_IN, true);}
}經過研究我們可以發現,usb_disable_device函數的清理工作主要有兩部分,一是將設備里所有端點給disable掉,一是將設備當前配置使用的每個接口都從系統里給unregister掉,也就是將接口和它對應的驅動給分開。
先說下第二部分的工作,9行,actconfig表示的是設備當前激活的配置,只有它不為空時才有接下來清理的必要。
15~29這個for循環就是將這個配置的每個接口從設備模型的體系中刪除掉,將它們和對應的接口驅動分開,沒有驅動了,這些接口也就喪失了能力,當然也就什么作用都發揮不了了,這也是名字里那個disable的真正含意所在。
34~44行,將actconfig的interface數組置為空,然后再將actconfig置為空,這里你可能會有的一個問題是,為什么只是置為空,既然要清理actconfig,為什么不直接將它占用的內存給釋放掉?這個問題問的好,說明你足夠細心,不過你應該注意到actconfig只是一個指針,一個地址,你應該首先弄清楚這個地址里保存的是什么東西再決定是不是將它給釋放掉,那這個指針指向哪兒?它指向設備struct usb_device結構的config數組里的其中一項,當前被激活的是哪一個配置,它就指向config數組里的哪一項,你這里只是不想讓設備當前激活任何一個配置而已,沒必要將actconfig指向的那個配置給釋放掉吧,前面在設備生命線那里走了那么久,歷盡千辛萬苦才將設備各個配置的內容給拿過來放到config數組里,你這里如果給釋放掉,對得起誰啊,豈不要哭死。
那這么說的話另一個問題就出來了,既然actconfig指向了config里的一項,那為什么要把那個配置的interface數組給置為空,這不是修改了那個配置的內容,從而也修改了config數組的內容么?你先別著急,俺幫你回憶一下,在設備生命線那里取配置描述符的,解析返回的那堆數據時,只是把每個配置里的cache數組,也就是intf_cache數組給初始化了,并沒有為interface數組充實任何的內容,這里做清理工作的目的就是要恢復原狀,當然要將它置為空了,那么配置的interface數組又在哪里被充實了那? usb_set_configuration函數里第二個階段之后不是還有個第三個階段么,就在那里,你那時激活了哪個配置,就為哪個配置的interface數組動手術,填點東西。
45行,如果這個設備此時確實是在Configured狀態,就讓它回到Address。
現在回頭來說說第一部分的清理工作。這個部分主要就是為每個端點調用了usb_disable_endpoint函數,將掛在它們上面的urb給取消掉。為什么要這么做?你想想,能調用到usb_disable_device這個函數,一般來說設備的狀態要發生變化了,設備的狀態都改變了,那設備的那些端點的狀態要不要改變?還有掛在它們上面的那些urb需不需要給取消掉?這些都是很顯然的事情,就拿現在讓設備從Configured回到Address來說吧,在Address的時候,你只能通過缺省管道也就是端點0對應的管道與設備進行通信的,但是在Configured的時候,設備的所有端點都是能夠使用的,它們上面可能已經掛了一些urb正在處理或者將要處理,那么你這時讓設備要從Configured變到Address,是不是應該先將這些urb給取消掉?
還有個問題是參數skip_ep0是嘛意思?這里for循環的i是從skip_ep0開始算起,也就是說skip_ep0為1的話,就不需要對端點0調用usb_disable_endpoint函數了,按常理來說,設備狀態改變了,是需要把每個端點上面的urb給取消掉的,這里面當然也要包括端點0,但是寫代碼的哥們兒這里搞出個skip_ep0自然有他們的玄機,驀然回首一下,usb_set_configuration()調用這個函數的時候參數skip_ep0的值是什么?是1,因為這時候是從Configured回到Address,這個過程中,其它端點是從能夠使用變成了不能使用,但端點0卻是一直都很強勢,雖說是設備發生了狀態的變化,但在這兩個狀態里它都是要正常使用的,所以就沒必要disable它了。
什么時候需要disable端點0?目前版本的內核里俺只發現了兩種情況,一是設備要斷開的時候,一是設備從Default進化到Address的時候,雖說不管是Default還是Address,端點0都是需要能夠正常使用的,但因為地址發生改變了,毫無疑問,你需要將掛在它上面的urb清除掉。俺當時講設備生命線的時候,在設置完設備地址,設備進入Address后,第二種情況的這個步驟給有意無意的飄過了,主要是當時也不影響理解,現在既然遇到了,就把它給補上吧。
在設備生命線的那個過程中,設置完設備地址,讓設備進入Address狀態后,立馬就調用了hub.c里一個名叫usb_ep0_reinit的函數
drivers/usb/core/hub.c
void usb_ep0_reinit(struct usb_device *udev)
{usb_disable_endpoint(udev, 0 + USB_DIR_IN, true);usb_disable_endpoint(udev, 0 + USB_DIR_OUT, true);usb_enable_endpoint(udev, &udev->ep0, true);
}這個函數里只對端點0調用了usb_disable_endpoint(),但是端點0接下來還是要使用的,不然你就取不到設備那些描述符了,所以接著重新將ep0使能。多說無益,還是到usb_disable_endpoint()里面去看看吧。
drivers/usb/core/message.c
void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr,bool reset_hardware)
{unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;struct usb_host_endpoint *ep;if (!dev)return;if (usb_endpoint_out(epaddr)) {ep = dev->ep_out[epnum];if (reset_hardware)dev->ep_out[epnum] = NULL;} else {ep = dev->ep_in[epnum];if (reset_hardware)dev->ep_in[epnum] = NULL;}if (ep) {ep->enabled = 0;usb_hcd_flush_endpoint(dev, ep);if (reset_hardware)usb_hcd_disable_endpoint(dev, ep);}
}這個函數先獲得端點號和端點的方向,然后從ep_in或ep_out兩個數組里取出端點的struct usb_host_endpoint結構體,并將數組里的對應項置為空,要注意的是這里同樣不是釋放掉數組里對應項的內存而是置為空。這兩個數組里的ep_in[0]和ep_out[0]是早就被賦值了,至于剩下的那些項是在什么時候被賦值的,又是指向了什么東西,就是usb_set_configuration函數第三個階段的事了。
不要怪俺說得比較粗略,只是都在前面說過了,你既然已經看到這里了,只要用過那么一點點心就會明白這里是什么意思。
最后23行調用了一個usb_hcd_endpoint_disable函數,主要的工作還得它來做,不過這已經深入HCD的腹地了,就不多說了,還是飄回usb_disable_device()吧。在為每個端點都調用了usb_disable_endpoint()之后,還有一個小步驟要做,就是將設備struct usb_device結構體的toggle數組置為0。至于toggle數組干嗎的,為啥要被初始化為0,你還是驀然回首到設備那節去看吧。俺要接著飄回usb_set_configuration()了。
150行,又一次與熟悉的陌生人usb_control_msg()相遇了,每當我們需要向設備發送請求的時候它就會適時的出現,我們每個人是不是也都希望在自己的生活里有這么一個角色?
usb_control_msg這次出現的目的當然是為了SET_CONFIGURATION請求,這里只說一下它的那堆參數,看一下spec 9.4.7的那張表
SET_CONFIGURATION請求不需要DATA transaction,而且還是協議里規定所有設備都要支持的標準請求,也不是針對端點或者接口什么的,而是針對設備的,所以bRequestType只能為0x80,就是上面表里的00000000B,也就是151行的第一個0,wValue表示配置的bConfigurationValue,就是151行的configuration。
167行,將激活的那個配置的地址賦給actconfig。如果cp為空,重新設置設備的狀態為Address,并將之前準備的那些struct usb_interface結構體和new_interfaces釋放掉,然后返回。掃一下前面的代碼,cp有三種可能為空,一是參數configuration為-1,一是參數configuration為0,且從設備的config數組里拿出來的就為空,一是SET_CONFIGURATION 請求出了問題。不管怎么說,走到170行,cp還是空的,你就要準備返回了。
177行,事情在這里發展達到了高潮的頂端,設置設備的狀態為Configured。
總結
以上是生活随笔為你收集整理的Linux那些事儿 之 戏说USB(31)驱动的生命线(三)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux那些事儿 之 戏说USB(30
- 下一篇: Linux那些事儿 之 戏说USB(32