Linux那些事儿 之 戏说USB(31)驱动的生命线(三)
生活随笔
收集整理的這篇文章主要介紹了
Linux那些事儿 之 戏说USB(31)驱动的生命线(三)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
準(zhǔn)備工作該做的都做了,別嫌太麻煩,什么事情都要經(jīng)過這么一個階段,大家都明白。現(xiàn)在看看第二階段的重頭戲,看看設(shè)備是怎么從Address進(jìn)入Configured的。1501行,如果已經(jīng)在Configured狀態(tài)了,就得做些清理工作,退回到Address狀態(tài)。都清理些什么怎么去清理?別著急,要想學(xué)會,得仔細(xì)研究下message.c里的usb_disable_device函數(shù)。
先說下第二部分的工作,9行,actconfig表示的是設(shè)備當(dāng)前激活的配置,只有它不為空時才有接下來清理的必要。
15~29這個for循環(huán)就是將這個配置的每個接口從設(shè)備模型的體系中刪除掉,將它們和對應(yīng)的接口驅(qū)動分開,沒有驅(qū)動了,這些接口也就喪失了能力,當(dāng)然也就什么作用都發(fā)揮不了了,這也是名字里那個disable的真正含意所在。
34~44行,將actconfig的interface數(shù)組置為空,然后再將actconfig置為空,這里你可能會有的一個問題是,為什么只是置為空,既然要清理actconfig,為什么不直接將它占用的內(nèi)存給釋放掉?這個問題問的好,說明你足夠細(xì)心,不過你應(yīng)該注意到actconfig只是一個指針,一個地址,你應(yīng)該首先弄清楚這個地址里保存的是什么東西再決定是不是將它給釋放掉,那這個指針指向哪兒?它指向設(shè)備struct usb_device結(jié)構(gòu)的config數(shù)組里的其中一項,當(dāng)前被激活的是哪一個配置,它就指向config數(shù)組里的哪一項,你這里只是不想讓設(shè)備當(dāng)前激活任何一個配置而已,沒必要將actconfig指向的那個配置給釋放掉吧,前面在設(shè)備生命線那里走了那么久,歷盡千辛萬苦才將設(shè)備各個配置的內(nèi)容給拿過來放到config數(shù)組里,你這里如果給釋放掉,對得起誰啊,豈不要哭死。
那這么說的話另一個問題就出來了,既然actconfig指向了config里的一項,那為什么要把那個配置的interface數(shù)組給置為空,這不是修改了那個配置的內(nèi)容,從而也修改了config數(shù)組的內(nèi)容么?你先別著急,俺幫你回憶一下,在設(shè)備生命線那里取配置描述符的,解析返回的那堆數(shù)據(jù)時,只是把每個配置里的cache數(shù)組,也就是intf_cache數(shù)組給初始化了,并沒有為interface數(shù)組充實任何的內(nèi)容,這里做清理工作的目的就是要恢復(fù)原狀,當(dāng)然要將它置為空了,那么配置的interface數(shù)組又在哪里被充實了那? usb_set_configuration函數(shù)里第二個階段之后不是還有個第三個階段么,就在那里,你那時激活了哪個配置,就為哪個配置的interface數(shù)組動手術(shù),填點(diǎn)東西。
45行,如果這個設(shè)備此時確實是在Configured狀態(tài),就讓它回到Address。
現(xiàn)在回頭來說說第一部分的清理工作。這個部分主要就是為每個端點(diǎn)調(diào)用了usb_disable_endpoint函數(shù),將掛在它們上面的urb給取消掉。為什么要這么做?你想想,能調(diào)用到usb_disable_device這個函數(shù),一般來說設(shè)備的狀態(tài)要發(fā)生變化了,設(shè)備的狀態(tài)都改變了,那設(shè)備的那些端點(diǎn)的狀態(tài)要不要改變?還有掛在它們上面的那些urb需不需要給取消掉?這些都是很顯然的事情,就拿現(xiàn)在讓設(shè)備從Configured回到Address來說吧,在Address的時候,你只能通過缺省管道也就是端點(diǎn)0對應(yīng)的管道與設(shè)備進(jìn)行通信的,但是在Configured的時候,設(shè)備的所有端點(diǎn)都是能夠使用的,它們上面可能已經(jīng)掛了一些urb正在處理或者將要處理,那么你這時讓設(shè)備要從Configured變到Address,是不是應(yīng)該先將這些urb給取消掉?
還有個問題是參數(shù)skip_ep0是嘛意思?這里for循環(huán)的i是從skip_ep0開始算起,也就是說skip_ep0為1的話,就不需要對端點(diǎn)0調(diào)用usb_disable_endpoint函數(shù)了,按常理來說,設(shè)備狀態(tài)改變了,是需要把每個端點(diǎn)上面的urb給取消掉的,這里面當(dāng)然也要包括端點(diǎn)0,但是寫代碼的哥們兒這里搞出個skip_ep0自然有他們的玄機(jī),驀然回首一下,usb_set_configuration()調(diào)用這個函數(shù)的時候參數(shù)skip_ep0的值是什么?是1,因為這時候是從Configured回到Address,這個過程中,其它端點(diǎn)是從能夠使用變成了不能使用,但端點(diǎn)0卻是一直都很強(qiáng)勢,雖說是設(shè)備發(fā)生了狀態(tài)的變化,但在這兩個狀態(tài)里它都是要正常使用的,所以就沒必要disable它了。
什么時候需要disable端點(diǎn)0?目前版本的內(nèi)核里俺只發(fā)現(xiàn)了兩種情況,一是設(shè)備要斷開的時候,一是設(shè)備從Default進(jìn)化到Address的時候,雖說不管是Default還是Address,端點(diǎn)0都是需要能夠正常使用的,但因為地址發(fā)生改變了,毫無疑問,你需要將掛在它上面的urb清除掉。俺當(dāng)時講設(shè)備生命線的時候,在設(shè)置完設(shè)備地址,設(shè)備進(jìn)入Address后,第二種情況的這個步驟給有意無意的飄過了,主要是當(dāng)時也不影響理解,現(xiàn)在既然遇到了,就把它給補(bǔ)上吧。
不要怪俺說得比較粗略,只是都在前面說過了,你既然已經(jīng)看到這里了,只要用過那么一點(diǎn)點(diǎn)心就會明白這里是什么意思。
最后23行調(diào)用了一個usb_hcd_endpoint_disable函數(shù),主要的工作還得它來做,不過這已經(jīng)深入HCD的腹地了,就不多說了,還是飄回usb_disable_device()吧。在為每個端點(diǎn)都調(diào)用了usb_disable_endpoint()之后,還有一個小步驟要做,就是將設(shè)備struct usb_device結(jié)構(gòu)體的toggle數(shù)組置為0。至于toggle數(shù)組干嗎的,為啥要被初始化為0,你還是驀然回首到設(shè)備那節(jié)去看吧。俺要接著飄回usb_set_configuration()了。
150行,又一次與熟悉的陌生人usb_control_msg()相遇了,每當(dāng)我們需要向設(shè)備發(fā)送請求的時候它就會適時的出現(xiàn),我們每個人是不是也都希望在自己的生活里有這么一個角色?
usb_control_msg這次出現(xiàn)的目的當(dāng)然是為了SET_CONFIGURATION請求,這里只說一下它的那堆參數(shù),看一下spec 9.4.7的那張表
SET_CONFIGURATION請求不需要DATA transaction,而且還是協(xié)議里規(guī)定所有設(shè)備都要支持的標(biāo)準(zhǔn)請求,也不是針對端點(diǎn)或者接口什么的,而是針對設(shè)備的,所以bRequestType只能為0x80,就是上面表里的00000000B,也就是151行的第一個0,wValue表示配置的bConfigurationValue,就是151行的configuration。
167行,將激活的那個配置的地址賦給actconfig。如果cp為空,重新設(shè)置設(shè)備的狀態(tài)為Address,并將之前準(zhǔn)備的那些struct usb_interface結(jié)構(gòu)體和new_interfaces釋放掉,然后返回。掃一下前面的代碼,cp有三種可能為空,一是參數(shù)configuration為-1,一是參數(shù)configuration為0,且從設(shè)備的config數(shù)組里拿出來的就為空,一是SET_CONFIGURATION 請求出了問題。不管怎么說,走到170行,cp還是空的,你就要準(zhǔn)備返回了。
177行,事情在這里發(fā)展達(dá)到了高潮的頂端,設(shè)置設(shè)備的狀態(tài)為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);}
}經(jīng)過研究我們可以發(fā)現(xiàn),usb_disable_device函數(shù)的清理工作主要有兩部分,一是將設(shè)備里所有端點(diǎn)給disable掉,一是將設(shè)備當(dāng)前配置使用的每個接口都從系統(tǒng)里給unregister掉,也就是將接口和它對應(yīng)的驅(qū)動給分開。
先說下第二部分的工作,9行,actconfig表示的是設(shè)備當(dāng)前激活的配置,只有它不為空時才有接下來清理的必要。
15~29這個for循環(huán)就是將這個配置的每個接口從設(shè)備模型的體系中刪除掉,將它們和對應(yīng)的接口驅(qū)動分開,沒有驅(qū)動了,這些接口也就喪失了能力,當(dāng)然也就什么作用都發(fā)揮不了了,這也是名字里那個disable的真正含意所在。
34~44行,將actconfig的interface數(shù)組置為空,然后再將actconfig置為空,這里你可能會有的一個問題是,為什么只是置為空,既然要清理actconfig,為什么不直接將它占用的內(nèi)存給釋放掉?這個問題問的好,說明你足夠細(xì)心,不過你應(yīng)該注意到actconfig只是一個指針,一個地址,你應(yīng)該首先弄清楚這個地址里保存的是什么東西再決定是不是將它給釋放掉,那這個指針指向哪兒?它指向設(shè)備struct usb_device結(jié)構(gòu)的config數(shù)組里的其中一項,當(dāng)前被激活的是哪一個配置,它就指向config數(shù)組里的哪一項,你這里只是不想讓設(shè)備當(dāng)前激活任何一個配置而已,沒必要將actconfig指向的那個配置給釋放掉吧,前面在設(shè)備生命線那里走了那么久,歷盡千辛萬苦才將設(shè)備各個配置的內(nèi)容給拿過來放到config數(shù)組里,你這里如果給釋放掉,對得起誰啊,豈不要哭死。
那這么說的話另一個問題就出來了,既然actconfig指向了config里的一項,那為什么要把那個配置的interface數(shù)組給置為空,這不是修改了那個配置的內(nèi)容,從而也修改了config數(shù)組的內(nèi)容么?你先別著急,俺幫你回憶一下,在設(shè)備生命線那里取配置描述符的,解析返回的那堆數(shù)據(jù)時,只是把每個配置里的cache數(shù)組,也就是intf_cache數(shù)組給初始化了,并沒有為interface數(shù)組充實任何的內(nèi)容,這里做清理工作的目的就是要恢復(fù)原狀,當(dāng)然要將它置為空了,那么配置的interface數(shù)組又在哪里被充實了那? usb_set_configuration函數(shù)里第二個階段之后不是還有個第三個階段么,就在那里,你那時激活了哪個配置,就為哪個配置的interface數(shù)組動手術(shù),填點(diǎn)東西。
45行,如果這個設(shè)備此時確實是在Configured狀態(tài),就讓它回到Address。
現(xiàn)在回頭來說說第一部分的清理工作。這個部分主要就是為每個端點(diǎn)調(diào)用了usb_disable_endpoint函數(shù),將掛在它們上面的urb給取消掉。為什么要這么做?你想想,能調(diào)用到usb_disable_device這個函數(shù),一般來說設(shè)備的狀態(tài)要發(fā)生變化了,設(shè)備的狀態(tài)都改變了,那設(shè)備的那些端點(diǎn)的狀態(tài)要不要改變?還有掛在它們上面的那些urb需不需要給取消掉?這些都是很顯然的事情,就拿現(xiàn)在讓設(shè)備從Configured回到Address來說吧,在Address的時候,你只能通過缺省管道也就是端點(diǎn)0對應(yīng)的管道與設(shè)備進(jìn)行通信的,但是在Configured的時候,設(shè)備的所有端點(diǎn)都是能夠使用的,它們上面可能已經(jīng)掛了一些urb正在處理或者將要處理,那么你這時讓設(shè)備要從Configured變到Address,是不是應(yīng)該先將這些urb給取消掉?
還有個問題是參數(shù)skip_ep0是嘛意思?這里for循環(huán)的i是從skip_ep0開始算起,也就是說skip_ep0為1的話,就不需要對端點(diǎn)0調(diào)用usb_disable_endpoint函數(shù)了,按常理來說,設(shè)備狀態(tài)改變了,是需要把每個端點(diǎn)上面的urb給取消掉的,這里面當(dāng)然也要包括端點(diǎn)0,但是寫代碼的哥們兒這里搞出個skip_ep0自然有他們的玄機(jī),驀然回首一下,usb_set_configuration()調(diào)用這個函數(shù)的時候參數(shù)skip_ep0的值是什么?是1,因為這時候是從Configured回到Address,這個過程中,其它端點(diǎn)是從能夠使用變成了不能使用,但端點(diǎn)0卻是一直都很強(qiáng)勢,雖說是設(shè)備發(fā)生了狀態(tài)的變化,但在這兩個狀態(tài)里它都是要正常使用的,所以就沒必要disable它了。
什么時候需要disable端點(diǎn)0?目前版本的內(nèi)核里俺只發(fā)現(xiàn)了兩種情況,一是設(shè)備要斷開的時候,一是設(shè)備從Default進(jìn)化到Address的時候,雖說不管是Default還是Address,端點(diǎn)0都是需要能夠正常使用的,但因為地址發(fā)生改變了,毫無疑問,你需要將掛在它上面的urb清除掉。俺當(dāng)時講設(shè)備生命線的時候,在設(shè)置完設(shè)備地址,設(shè)備進(jìn)入Address后,第二種情況的這個步驟給有意無意的飄過了,主要是當(dāng)時也不影響理解,現(xiàn)在既然遇到了,就把它給補(bǔ)上吧。
在設(shè)備生命線的那個過程中,設(shè)置完設(shè)備地址,讓設(shè)備進(jìn)入Address狀態(tài)后,立馬就調(diào)用了hub.c里一個名叫usb_ep0_reinit的函數(shù)
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);
}這個函數(shù)里只對端點(diǎn)0調(diào)用了usb_disable_endpoint(),但是端點(diǎn)0接下來還是要使用的,不然你就取不到設(shè)備那些描述符了,所以接著重新將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);}
}這個函數(shù)先獲得端點(diǎn)號和端點(diǎn)的方向,然后從ep_in或ep_out兩個數(shù)組里取出端點(diǎn)的struct usb_host_endpoint結(jié)構(gòu)體,并將數(shù)組里的對應(yīng)項置為空,要注意的是這里同樣不是釋放掉數(shù)組里對應(yīng)項的內(nèi)存而是置為空。這兩個數(shù)組里的ep_in[0]和ep_out[0]是早就被賦值了,至于剩下的那些項是在什么時候被賦值的,又是指向了什么東西,就是usb_set_configuration函數(shù)第三個階段的事了。
不要怪俺說得比較粗略,只是都在前面說過了,你既然已經(jīng)看到這里了,只要用過那么一點(diǎn)點(diǎn)心就會明白這里是什么意思。
最后23行調(diào)用了一個usb_hcd_endpoint_disable函數(shù),主要的工作還得它來做,不過這已經(jīng)深入HCD的腹地了,就不多說了,還是飄回usb_disable_device()吧。在為每個端點(diǎn)都調(diào)用了usb_disable_endpoint()之后,還有一個小步驟要做,就是將設(shè)備struct usb_device結(jié)構(gòu)體的toggle數(shù)組置為0。至于toggle數(shù)組干嗎的,為啥要被初始化為0,你還是驀然回首到設(shè)備那節(jié)去看吧。俺要接著飄回usb_set_configuration()了。
150行,又一次與熟悉的陌生人usb_control_msg()相遇了,每當(dāng)我們需要向設(shè)備發(fā)送請求的時候它就會適時的出現(xiàn),我們每個人是不是也都希望在自己的生活里有這么一個角色?
usb_control_msg這次出現(xiàn)的目的當(dāng)然是為了SET_CONFIGURATION請求,這里只說一下它的那堆參數(shù),看一下spec 9.4.7的那張表
SET_CONFIGURATION請求不需要DATA transaction,而且還是協(xié)議里規(guī)定所有設(shè)備都要支持的標(biāo)準(zhǔn)請求,也不是針對端點(diǎn)或者接口什么的,而是針對設(shè)備的,所以bRequestType只能為0x80,就是上面表里的00000000B,也就是151行的第一個0,wValue表示配置的bConfigurationValue,就是151行的configuration。
167行,將激活的那個配置的地址賦給actconfig。如果cp為空,重新設(shè)置設(shè)備的狀態(tài)為Address,并將之前準(zhǔn)備的那些struct usb_interface結(jié)構(gòu)體和new_interfaces釋放掉,然后返回。掃一下前面的代碼,cp有三種可能為空,一是參數(shù)configuration為-1,一是參數(shù)configuration為0,且從設(shè)備的config數(shù)組里拿出來的就為空,一是SET_CONFIGURATION 請求出了問題。不管怎么說,走到170行,cp還是空的,你就要準(zhǔn)備返回了。
177行,事情在這里發(fā)展達(dá)到了高潮的頂端,設(shè)置設(shè)備的狀態(tài)為Configured。
總結(jié)
以上是生活随笔為你收集整理的Linux那些事儿 之 戏说USB(31)驱动的生命线(三)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux那些事儿 之 戏说USB(30
- 下一篇: Linux那些事儿 之 戏说USB(32