Hub正常工作后,主控制器就會(huì)定時(shí)詢問(wèn)hub是否有中斷產(chǎn)生,當(dāng)hub端口上有一個(gè)設(shè)備插入或拔除,hub就向主控制器發(fā)送urb請(qǐng)求,即把hub端口的變化狀況告訴主控制器,這是通過(guò)urb請(qǐng)求來(lái)完成的,主機(jī)在處理完了這個(gè)urb后,就會(huì)調(diào)用urb所提供的完成函數(shù),來(lái)調(diào)用hub的中斷函數(shù),即hub_irq。
Hub_irq是hub的中斷處理函數(shù),處理程序首先判斷主控制器處理urb的結(jié)果狀態(tài),如果狀態(tài)是OK的,則繼續(xù)處理。
1.掃描hub的所有端口,確定是哪個(gè)端口發(fā)生了變化。端口是用位圖來(lái)表示的,一個(gè)long型數(shù)據(jù)可以表示32個(gè)hub端口(每位表示一個(gè)端口),有多少個(gè)端口,就用多少位表示,而8位用一個(gè)字節(jié)表示,因此,最后的使用的位都是轉(zhuǎn)化成了字節(jié)的的,比如一個(gè)hub有18個(gè)端口,則需要用18位來(lái)表示,但是一個(gè)字節(jié)只有8位,因此,需要用3個(gè)字節(jié)才能表示完。
2.調(diào)用kick_khubd函數(shù),把當(dāng)前hub加入到hub驅(qū)動(dòng)隊(duì)列hub_event_list中,然后喚醒hub守護(hù)進(jìn)程wake_up(khubd_wait),開(kāi)始解析hub發(fā)生的事情了。
3.hub_thread被喚醒后,將得到執(zhí)行,hub_events也將得到執(zhí)行
4.hub_events是分析hub事件的主函數(shù),hub分析的相關(guān)內(nèi)容都在這里執(zhí)行。這個(gè)函數(shù)是一個(gè)大的死循環(huán)。
5.Hub_events處理流程
5.1先從hub_event_list中取出此次處理的hub節(jié)點(diǎn),并把hub節(jié)點(diǎn)從原來(lái)的隊(duì)列中刪除,使之獨(dú)立于任何鏈表,因?yàn)槲覀兲幚硗晔录螅琱ub結(jié)構(gòu)體就要?jiǎng)h除,因此不能保留在任何隊(duì)列中。hub可以是根hub,也可以是接到根hub上的子hub,只是,不論是什么hub,都使用同樣的守護(hù)進(jìn)程了。
5.2.通過(guò)宏轉(zhuǎn)換,找到hub節(jié)點(diǎn)對(duì)以的hub結(jié)構(gòu)體,進(jìn)而得到hub結(jié)構(gòu)體對(duì)應(yīng)的usb結(jié)構(gòu)體,以及hub接口結(jié)構(gòu)體usb_intfdev,得到這三個(gè)結(jié)構(gòu)體是這個(gè)函數(shù)處理的關(guān)鍵。
5.3.鎖住當(dāng)前的hub樹(shù),因?yàn)閔ub只有一個(gè)守護(hù)進(jìn)程,所有的hub都使用這個(gè)守護(hù)進(jìn)程,而守護(hù)進(jìn)程每次只能服務(wù)一個(gè)hub,因此,只要有一個(gè)hub在使用這個(gè)守護(hù)進(jìn)程,就需要鎖住,以防止其他hub的使用。
5.4.按位檢索hub每個(gè)端口是否正在執(zhí)行reset或resume操作,注意是從1開(kāi)始檢索的,位0表示整個(gè)hub的情況,如果當(dāng)前端口正在執(zhí)行reset或resume操作,則跳過(guò)對(duì)這端口的檢查,否則,測(cè)試這個(gè)端口是否有狀態(tài)改變,如果沒(méi)有狀態(tài)改變,也跳過(guò)此端口。
5.5.如果端口有狀態(tài)改變,則判斷端口是發(fā)生了什么狀態(tài)的改變,有以下的狀態(tài)
#define USB_PORT_STAT_C_CONNECTION 0x0001
#define USB_PORT_STAT_C_ENABLE 0x0002
#define USB_PORT_STAT_C_SUSPEND 0x0004
#define USB_PORT_STAT_C_OVERCURRENT 0x0008
#define USB_PORT_STAT_C_RESET 0x0010
5.6.確定有狀態(tài)改變后,就開(kāi)始對(duì)有改變的端口的狀態(tài)進(jìn)行進(jìn)一步的處理,此時(shí)的處理,就要根據(jù)剛剛分析得到的各種狀態(tài),分開(kāi)進(jìn)行處理。
5.7.首先要確定端口是否還有設(shè)備,如果有,則要把設(shè)備刪除,原因是,hub端口上有兩種狀態(tài)發(fā)生轉(zhuǎn)換,1為端口從無(wú)設(shè)備到有設(shè)備的狀態(tài),此時(shí)設(shè)備還在認(rèn)證中,因此端口不應(yīng)該有設(shè)備,2是端口從有到無(wú)設(shè)備的轉(zhuǎn)換,此時(shí)檢測(cè)到還有設(shè)備,表示設(shè)備還沒(méi)有被移除,因此,需要直接把設(shè)備disable掉。
5.8.把設(shè)備disable掉的動(dòng)作,需要進(jìn)行反彈的檢查,一個(gè)設(shè)備至少需要100ms的時(shí)間,才能表示此狀態(tài)是穩(wěn)定的,因此,等100ms后再判斷端口是否是disable了,就可以判斷了
5.9.判斷設(shè)備狀態(tài)為無(wú)設(shè)備插入時(shí),需要檢測(cè)是否是端口電源被disable掉了,如果是,則需要開(kāi)啟端口電源,如果電源是開(kāi)啟的,則表示設(shè)備有誤,結(jié)束判斷,返回上層。
5.10.經(jīng)過(guò)上面的判斷,能執(zhí)行到這里,表示端口是有設(shè)備插入了,此時(shí),就需要要分析端口的狀態(tài)了,對(duì)端口的分析,可以嘗試4次,主要是為了排除各種干擾。
5.11.為即將到來(lái)的設(shè)備分配空間(struct usb_device),對(duì)這個(gè)即將到來(lái)的設(shè)備的設(shè)備結(jié)構(gòu)體設(shè)置狀態(tài)為連接狀態(tài),速度為未知,電源為hub分配給的,這些都是默認(rèn)狀態(tài)。
5.12.準(zhǔn)備為設(shè)備分配地址,此時(shí)是子系統(tǒng)軟件的操作,就是查看總線上128位位圖中,哪位為0的,就選擇出來(lái),查找的方式是從上次記錄的下一位開(kāi)始查找,如果超過(guò)128了還沒(méi)有找到,就接著從0開(kāi)始查找,即如果頻繁的拔插usb設(shè)備,即使只有一個(gè)設(shè)備,則每次插入,設(shè)備地址都會(huì)增加1,直到到達(dá)128后,從0接著開(kāi)始,此時(shí)的設(shè)備地址,還不是設(shè)備的真實(shí)地址,因?yàn)檫€沒(méi)有發(fā)送給設(shè)備。
5.13.復(fù)位設(shè)備,通過(guò)對(duì)設(shè)備的復(fù)位來(lái)達(dá)到使能設(shè)備的目的,此時(shí),如果復(fù)位成功,則設(shè)備的狀態(tài)將成為USB_STATE_DEFAULT狀態(tài),主控制器就可以通過(guò)控制端口獲得設(shè)備的描述符符,通過(guò)對(duì)設(shè)備描述符的解析,可以得到設(shè)備的速度,從而可以根據(jù)速度猜測(cè)控制端口的空間大小。
5.14.得到控制端點(diǎn)的大小后,就開(kāi)始準(zhǔn)備往控制端點(diǎn)發(fā)送urb請(qǐng)求,獲得設(shè)備的真正的設(shè)備描述符。
5.15.獲得設(shè)備描述符時(shí)分兩種模式,新模式和舊模式,每種模式最多試兩次,每次可以最多讀三回端口。對(duì)于新模式,需要分配一個(gè)64字節(jié)的空間,用于接收從設(shè)備返回的設(shè)備描述符。通過(guò)獲得的設(shè)備描述符來(lái)判斷端口的類別是不是確實(shí)是設(shè)備類別的,如果是,則只需要讀取一次端口,就可以判斷是設(shè)備描述符了,把描述符中記錄的端口的空間大小給剛剛申請(qǐng)的空間的對(duì)應(yīng)字節(jié)賦值了。
5.16.復(fù)位設(shè)備,準(zhǔn)備給設(shè)備設(shè)置剛剛系統(tǒng)選擇好,但還沒(méi)有設(shè)置的那個(gè)地址,這個(gè)設(shè)置可以最多嘗試設(shè)置兩次,之間要停留200ms。
5.17.一旦設(shè)置成功,就要把設(shè)備的狀態(tài)轉(zhuǎn)變?yōu)榈刂窢顟B(tài)(USB_STATE_ADDRESS),同時(shí)把控制端口0給disable掉,這樣,這個(gè)端口上的urb鏈表都被清除了。
5.18.如果新的模式能走到這里,就表明新模式成功的獲得了設(shè)備描述符了,因此舊模式就不要試了,而舊模式是分兩次來(lái)獲得設(shè)備的描述符的,第一次是發(fā)送要求獲得8字節(jié)的urb請(qǐng)求,經(jīng)過(guò)這個(gè)請(qǐng)求得到的數(shù)據(jù),就可以知道設(shè)備的端口大小到達(dá)是多大,再按照獲得的端口大小,獲得端口的真實(shí)的設(shè)備描述符。
5.19.接下來(lái),就要開(kāi)始把這個(gè)設(shè)備加入設(shè)備模式中去了(usb_new_device)。
5.20.首先,要獲得設(shè)備的配置描述符,注意,只有設(shè)備才有配置,接口,端口都是沒(méi)有配置的,接口有設(shè)置。設(shè)備有多少個(gè)配置,就要分配多少個(gè)配置的空間,用于接收,而有多少個(gè)配置,已經(jīng)在設(shè)備描述符中獲得了,這時(shí)只需要從設(shè)備中讀取就可以。
5.21.設(shè)置配置描述符指針,同樣有多少個(gè)配置多少個(gè)這樣的指針
5.22.一個(gè)配置描述符有9個(gè)字節(jié),此時(shí)需要分配出這個(gè)空間,用于接收從設(shè)備中讀回的配置描述符。
5.23.循環(huán)讀取設(shè)備中的配置描述符,并把讀回的數(shù)據(jù)放在設(shè)備結(jié)構(gòu)體中的相應(yīng)位置,之后,從配置中獲得接口數(shù),接口設(shè)置,以及接口下面的端點(diǎn),通通都是在這里分析的,好長(zhǎng)的幾個(gè)函數(shù)。
5.24.接著,把設(shè)備添加到設(shè)備模型中去,這部分工作由設(shè)備模型完成,主要包括完成了設(shè)備鏈表的添加,和查找設(shè)備驅(qū)動(dòng)程序的,此處是設(shè)備的驅(qū)動(dòng),usb系統(tǒng)只有一個(gè)設(shè)備驅(qū)動(dòng),而且是系統(tǒng)已經(jīng)做好的,當(dāng)設(shè)備里的接口被添加時(shí),我們的編寫的驅(qū)動(dòng),就是在這里被調(diào)用,用來(lái)判斷驅(qū)動(dòng)是否符合設(shè)備的了。
5.25.掃描設(shè)備的所有配置,選擇最優(yōu)的當(dāng)前配置,再用選中的這個(gè)配置去設(shè)置設(shè)備。
5.26.到此,設(shè)備的配置,設(shè)置,什么都配置妥當(dāng)了,系統(tǒng)就可以正常的使用設(shè)備了。
聯(lián)系客服