一、USB協(xié)議基礎(chǔ)知識(shí)??
前序:USB概念概述
USB1.0版本速度1.5Mbps(低速USB) USB1.1版本速度12Mbps(全速USB)? USB2.0版本速度480Mbps(高速USB)。
USB 分為主從兩大體系,一般而言, PC 中的 USB 系統(tǒng)就是作主,而一般的 USB 鼠標(biāo), U 盤則是典型的 USB 從系統(tǒng)。
USB主控制器這一塊,我們至少要開發(fā)出 USB 的主控制器與從控制器,鼠標(biāo)是低速設(shè)備,所需的是最簡(jiǎn)單的一類從控制器。主控制器則復(fù)雜得多,因?yàn)樘^于復(fù)雜了,所以就形成了一些標(biāo)準(zhǔn)。在一個(gè)復(fù)雜的系統(tǒng)中,標(biāo)準(zhǔn)的好處就是可以讓開發(fā)者把精力集中在自己負(fù)責(zé)的一塊中來,只需要向外界提供最標(biāo)準(zhǔn)的接口,而免于陷于技術(shù)的汪洋大海中。
USB 主控制器主要有 1.1 時(shí)代的 OHCI 和 UHCI , 2.0 時(shí)代的 EHCI ,這些標(biāo)準(zhǔn)規(guī)定了主控制器的功能和接口(寄存器的序列及功能),對(duì)我們驅(qū)動(dòng)工程師而言,這樣的好處就是只要你的驅(qū)動(dòng)符合標(biāo)某一標(biāo)準(zhǔn),你就能輕而易舉的驅(qū)動(dòng)所有這個(gè)標(biāo)準(zhǔn)的主控制器。要想把主控制器驅(qū)動(dòng)起來,本來是一件很難的事情,估計(jì)全球的 IT 工程師沒幾個(gè)能有這樣的水平,但有了標(biāo)準(zhǔn),我們就可以輕松的占有這幾個(gè)高水平的 IT 工程師的勞動(dòng)成果。
主控制器和驅(qū)動(dòng)有了,我們還需要 USB 協(xié)議棧,這就是整個(gè) USB 系統(tǒng)的軟件部分的核心(有的資料中直接把其稱為 USB 核心), USB 協(xié)議棧一方面向使用 USB 總線的設(shè)備驅(qū)動(dòng)提供操作 USB 總線的 API ,另一方面則管理上層驅(qū)動(dòng)傳下來的的數(shù)據(jù)流,按 USB 主控制器的要求放在控制器驅(qū)動(dòng)規(guī)定的位置, USB 主控制器會(huì)調(diào)度這些數(shù)據(jù)。
我們這里用到了調(diào)度這個(gè)詞, USB 主控制器的調(diào)度其實(shí)和火車的調(diào)度 CPU 的調(diào)度有相似之處,物理上的通路只有一條,但 USB 中規(guī)定的邏輯上的通路卻有許多條,有時(shí)一個(gè)設(shè)備就會(huì)占用幾條邏輯通道,而 USB 系統(tǒng)中又會(huì)有多個(gè)設(shè)備同時(shí)運(yùn)行。這就好像是只有一條鐵路線,但來來往往的火車卻有許多, USB 主控制器的作用就是調(diào)度這些火車,而 USB 協(xié)議棧的作用則向上層的 USB 設(shè)備驅(qū)動(dòng)提供不同的車次。
有了以上的這些模塊,才能為 USB 鼠標(biāo)設(shè)計(jì)驅(qū)動(dòng),這一點(diǎn)上 ps/2 鼠標(biāo)的驅(qū)動(dòng)和 USB 鼠標(biāo)的驅(qū)動(dòng)結(jié)構(gòu)基本一樣,只不過我們的數(shù)據(jù)通路是 USB 總線。
USB 系統(tǒng)甚至把設(shè)備驅(qū)動(dòng)都給標(biāo)準(zhǔn)化了,只要是支持 USB 的主機(jī),就可以支持任何一個(gè)廠商的 USB 鼠標(biāo),任何一個(gè)廠商的 U 盤,只要是被 USB 系統(tǒng)包函的設(shè)備,只要這些設(shè)備支持相應(yīng)的標(biāo)準(zhǔn),就無需重新設(shè)計(jì)驅(qū)動(dòng)而直接使用。
下是簡(jiǎn)單的列出了 USB 設(shè)備類型,理想的情況 USB 系統(tǒng)要對(duì)這些設(shè)備作完整的支持,設(shè)備也必須符合 USB 規(guī)范中的要求。
1 - audio :表示一個(gè)音頻設(shè) ? 備。
2 - communication?? device :通訊設(shè)備,如電話, moden 等等。
3 - HID :人機(jī)交互設(shè)備,如鍵盤,鼠標(biāo)等。
6 - image 圖象設(shè)備,如掃描儀,攝像頭等,有時(shí)數(shù)碼相 ?? 機(jī)也可歸到這一類。
7 -打印機(jī)類。如單向,雙向打印機(jī)等。
8 - mass?? storage 海量存儲(chǔ)類。所有帶有一定存儲(chǔ)功能的都可以歸到這一類。如數(shù)碼相機(jī)大多數(shù)都?xì)w這一類。
9 - hub 類。
11 - chip?? card/smart?? card 。
13 -- Content Security
14 -- Video? ( Interface )
15 -- Personal Healthcare
220 -- Diagnostic Device
224 -- Wireless Controller? ( Interface )
239 -- Miscellaneous
254 -- Application Specific? ( Interface )
255 - vendor?? specific. 廠家的自定義類,主要用于一些特殊的設(shè)備。如接口轉(zhuǎn)接卡等。
隨著 USB 技術(shù)的發(fā)展, USB 系統(tǒng)中的一些不足也逐漸被承認(rèn), OTG 就是這種情況下的主要產(chǎn)物。
現(xiàn)在市面上有些設(shè)備(比如一些 MP4 )即能插上電腦當(dāng) U 盤使,也能被 U 盤插上讀取 U 盤。這樣的設(shè)備在 USB 系統(tǒng)中是作主還是作從呢?
這就是 OTG(On-The-Go), 即可以作主也可以作從,傳說中的雌雄同體。這主要是為嵌入式設(shè)備準(zhǔn)備的,因?yàn)?USB 是一種主從系統(tǒng),不能支持點(diǎn)對(duì)點(diǎn)平等的傳輸數(shù)據(jù), OTG 正是在這種需求下產(chǎn)生的, OTG 不僅支持控制器的主從切換,在一定層度上,也支持相同設(shè)備之間的數(shù)據(jù)交換。?
1、USB的傳輸線結(jié)構(gòu)
一條USB的傳輸線分別由地線、電源線、D+、D-四條線構(gòu)成,D+和D-是差分輸入線(抗干擾),它使用的是3.3V的電壓,而電源線和地線可向設(shè)備提供5V電壓,最大電流為500MA。OTG 的做法就是增來一個(gè) ID pin 來判斷設(shè)備是接入設(shè)備的是主還是從。vbus 主要是供電, D+/D- 則是用來傳輸數(shù)據(jù),就是我們前面所講的主設(shè)備和從設(shè)備間唯一的一條鐵路。
信號(hào)線名稱
顏色
1
Vbus
紅
2
D-
白
3
D+
綠
4
GNU
黑
shell?(金屬殼)
屏敝層
2、USB可以熱插拔的硬件原理
USB主機(jī)是如何檢測(cè)到設(shè)備的插入的呢?首先,在USB集線器的每個(gè)下游端口的D+和D-上,分別接了一個(gè)15K歐姆的下拉電阻到地。這樣,在集線器的端口懸空時(shí),就被這兩個(gè)下拉電阻拉到了低電平。而在USB設(shè)備端,在D+或者D-上接了1.5K歐姆上拉電阻。對(duì)于全速和高速設(shè)備,上拉電阻是接在D+上;而低速設(shè)備則是上拉電阻接在D-上。這樣,當(dāng)設(shè)備插入到集線器時(shí),由1.5K的上拉電阻和15K的下拉電阻分壓,結(jié)果就將差分?jǐn)?shù)據(jù)線中的一條拉高了。集線器檢測(cè)到這個(gè)狀態(tài)后,它就報(bào)告給USB主控制器(或者通過它上一層的集線器報(bào)告給USB主控制器),這樣就檢測(cè)到設(shè)備的插入了。USB高速設(shè)備先是被識(shí)別為全速設(shè)備,然后通過HOST和DEVICE兩者之間的確認(rèn),再切換到高速模式的。在高速模式下,是電流傳輸模式,這時(shí)將D+上的上拉電阻斷開。
3、USB主機(jī)控制器
USB主機(jī)控制器屬于南橋芯片的一部分,通過PCI總線和處理器通信。USB主機(jī)控制器分為UHCI(英特爾提出)、OHCI(康柏和微軟提出)、?EHCI。其中OHCI驅(qū)動(dòng)程序用來為非PC系統(tǒng)上以及帶有SiS和ALi芯片組的PC主辦上的USB芯片提供支持。UHCI驅(qū)動(dòng)程序多用來為大多數(shù)其他PC主板(包括Intel和Via)上的USB芯片提供支持。ENCI兼容OHCI和UHCI。UHCI的硬件線路比OHCI簡(jiǎn)單,所以成本較低,但需要較復(fù)雜的驅(qū)動(dòng)程序,CPU負(fù)荷稍重。主機(jī)控制器驅(qū)動(dòng)程序完成的功能主要包括:解析和維護(hù)URB,根據(jù)不同的端點(diǎn)進(jìn)行分類緩存URB;負(fù)責(zé)不同USB傳輸類型的調(diào)度工作;負(fù)責(zé)USB數(shù)據(jù)的實(shí)際傳輸工作;實(shí)現(xiàn)虛擬跟HUB的功能。
4、USB設(shè)備的構(gòu)成
USB設(shè)備的構(gòu)成包括了配置,接口和端點(diǎn)。
1.?設(shè)備通常具有一個(gè)或者更多個(gè)配置
2.?配置經(jīng)常具有一個(gè)或者更多個(gè)接口
3.?接口通常具有一個(gè)或者更多個(gè)設(shè)置
4.?接口沒有或者具有一個(gè)以上的端點(diǎn)
需要注意的是,驅(qū)動(dòng)是綁定到USB接口上,而不是整個(gè)設(shè)備。
5、主控制怎么正確訪問各種不同的USB設(shè)備
每一個(gè)USB設(shè)備接入PC時(shí),USB總線驅(qū)動(dòng)程序都會(huì)使用默認(rèn)的地址0(僅未分配地址的設(shè)備可以使用)跟USB設(shè)備通信,然后給它分配一個(gè)編號(hào),接在USB總線上的每一個(gè)USB設(shè)備都有自己的編號(hào)(地址),PC機(jī)想訪問某個(gè)USB設(shè)備時(shí),發(fā)出的命令都含有對(duì)應(yīng)的編號(hào)(地址)就可以了。
USB總線驅(qū)動(dòng)程序獲取USB設(shè)置信息。USB設(shè)備里都會(huì)有一個(gè)叫?EEPROM的東東,它就是用來存儲(chǔ)設(shè)備本身信息的。它與Flash雖說都是要電擦除的,但它可以按字節(jié)擦除,F(xiàn)lash只能一次擦除一個(gè)?block。
6、usb-firmware簡(jiǎn)易框架
usb firmware主要工作是滿足usb 協(xié)議所定義的標(biāo)準(zhǔn)請(qǐng)求(usb協(xié)議第9章第4節(jié)),不同的firmware因?yàn)橛布煌僮饔兴煌康亩际峭瓿芍骺刂破鲗?duì)設(shè)備的標(biāo)準(zhǔn)請(qǐng)求,大致框圖如下:
7、USB傳輸事務(wù)
USB通信最基本的形式是通過一個(gè)名為端點(diǎn)(endpoint)的東西。它是真實(shí)存在的。
端點(diǎn)只能往一個(gè)方向傳送數(shù)據(jù)(端點(diǎn)0除外,端點(diǎn)0使用message管道,它既可以IN又可以O(shè)UT),或者IN,或者OUT。除了端點(diǎn)0,低速設(shè)備只能有2個(gè)端點(diǎn),高速設(shè)備也只能有15個(gè)IN端點(diǎn)和15個(gè)OUT端點(diǎn)。
主機(jī)和端點(diǎn)之間的數(shù)據(jù)傳輸是通過管道。
端點(diǎn)只有在device上才有,協(xié)議說端點(diǎn)代表在主機(jī)和設(shè)備端點(diǎn)之間移動(dòng)數(shù)據(jù)的能力。
USB通信都是由host端發(fā)起的。
首先明確一點(diǎn)USB協(xié)議規(guī)定所有的數(shù)據(jù)傳輸都必須由主機(jī)發(fā)起。所以這個(gè)傳輸?shù)囊话愀袷剑毫钆瓢?表明傳輸?shù)念愋?,數(shù)據(jù)包(實(shí)際傳輸?shù)臄?shù)據(jù)),握手包(數(shù)據(jù)的正確性)。首先是由主機(jī)控制器發(fā)出令牌包,然后主機(jī)/設(shè)備發(fā)送數(shù)據(jù)包,甚至可以沒有,最后設(shè)備/主機(jī)發(fā)送握手包,這么一個(gè)過程就叫做一個(gè)USB傳輸事務(wù)。一個(gè)USB傳輸事務(wù)就實(shí)現(xiàn)了一次從主機(jī)和設(shè)備間的通訊。USB的事務(wù)有:OUT、IN、SETUP事務(wù)。
令牌包:可分為OUT包、IN包、SetUp包和幀起始包,OUT包就是說明接下來的數(shù)據(jù)包的方向時(shí)從主機(jī)到設(shè)備。?
數(shù)據(jù)包:里面包含的就是我們實(shí)際要傳輸?shù)臇|東了??。
握手包:發(fā)送方發(fā)送了數(shù)據(jù),接受方收沒收到是不是該吱個(gè)聲呀。
一個(gè)數(shù)據(jù)包里面包含有很多的域,里面包含了很多信息,一般有同步的域,數(shù)據(jù)包的核心信息的域,數(shù)據(jù)校驗(yàn)的域。
令牌包:SYNC + PID + ADDR + ENDP + CRC5 :(同步) + (IN/OUT/SetUp) + (設(shè)備地址)+(設(shè)備端點(diǎn)) + (校驗(yàn))
數(shù)據(jù)包:分為DATA0包和DATA1包,當(dāng)USB發(fā)送數(shù)據(jù)的時(shí)候,當(dāng)一次發(fā)送的數(shù)據(jù)長(zhǎng)度大于相應(yīng)端點(diǎn)的容量時(shí),就需要把數(shù)據(jù)包分為好幾個(gè)包,分批發(fā)送,DATA0包和DATA1包交替發(fā)送,即如果第一個(gè)數(shù)據(jù)包是?DATA0,那第二個(gè)數(shù)據(jù)包就是DATA1。
SYNC + PID + DATA0/1 + CRC5:(同步) + (DATA0/1) + (數(shù)據(jù)) + (校驗(yàn))。
但也有例外情況,在同步傳輸中(四類傳輸類型中之一),所有的數(shù)據(jù)包都是為DATA0,格式如下:?SYNC + PID + 0~1023字節(jié)?+ CRC16:(同步) + (DATA0) + (數(shù)據(jù)) + (校驗(yàn))。
握手包:SYNC+PID:(同步)+(HandShake)
8、USB協(xié)議的四種傳輸類型
因?yàn)閡sb支持的設(shè)備實(shí)在是太多,而且不同的設(shè)備對(duì)于傳輸數(shù)據(jù)各有各的要求和這就導(dǎo)致了我們需要不同的傳輸方式。USB支持4種傳輸方式:控制傳輸;批量傳輸;中斷傳輸;實(shí)(等)時(shí)傳輸。
控制傳輸:首先發(fā)送?Setup?傳輸事務(wù),然后IN/OUT傳輸事務(wù),最后是?STATUS transaction,向主機(jī)匯報(bào)前面SETUP?和?IN/OUT階段的結(jié)果??刂苽鬏斨饕糜谙蛟O(shè)備發(fā)送配置信息、獲取設(shè)備信息、發(fā)送命令道設(shè)備,或者獲取設(shè)備的狀態(tài)報(bào)告??刂苽鬏斠话惆l(fā)送的數(shù)據(jù)量較小,當(dāng)USB設(shè)備插入時(shí),USB核心使用端點(diǎn)0對(duì)設(shè)備進(jìn)行配置,另外,端口0與其他端點(diǎn)不一樣,端點(diǎn)0可以雙向傳輸。
批量傳輸:由OUT事務(wù)和IN事務(wù)構(gòu)成,用于大容量數(shù)據(jù)傳輸,沒有固定的傳輸速率,也不占用帶寬,當(dāng)總線忙時(shí),USB會(huì)優(yōu)先進(jìn)行其他類型的數(shù)據(jù)傳輸,而暫時(shí)停止批量轉(zhuǎn)輸。批量傳輸通常用在數(shù)據(jù)量大、對(duì)數(shù)據(jù)實(shí)時(shí)性要求不高的場(chǎng)合,例如USB打印機(jī)、掃描儀、大容量存儲(chǔ)設(shè)備、U盤等。
中斷傳輸:由OUT事務(wù)和IN事務(wù)構(gòu)成,中斷傳輸就是中斷端點(diǎn)以一個(gè)固定的速度來傳輸較少的數(shù)據(jù),USB鍵盤和鼠標(biāo)就是使用這個(gè)傳輸方式。這里說的中斷和硬件上下文中的中斷不一樣,它不是設(shè)備主動(dòng)發(fā)送一個(gè)中斷請(qǐng)求,而是主機(jī)控制器在保證不大于某個(gè)時(shí)間間隔內(nèi)安排一次傳輸。中斷傳輸對(duì)時(shí)間要求比較嚴(yán)格,所以可以用中斷傳輸來不斷地檢測(cè)某個(gè)設(shè)備,當(dāng)條件滿足后再使用批量傳輸傳輸大量的數(shù)據(jù)。
等時(shí)傳輸:由OUT事務(wù)和IN事務(wù)構(gòu)成,有兩個(gè)特殊地方,第一,在同步傳輸?shù)腎N和OUT事務(wù)中是沒有握手階段;第二,在數(shù)據(jù)包階段所有的數(shù)據(jù)包都為DATA0 。等時(shí)傳輸同樣可以傳輸大批量數(shù)據(jù),但是對(duì)數(shù)據(jù)是否到達(dá)沒有保證,它對(duì)實(shí)時(shí)性的要求很高,例如音頻、視頻等設(shè)備(USB攝像頭,USB話筒)。
這4種傳輸方式由4個(gè)事務(wù)組成:
IN事務(wù):IN事務(wù)為host輸入服務(wù),當(dāng)host需要從設(shè)備獲得數(shù)據(jù)的時(shí)候,就需要IN事務(wù)。
OUT事務(wù):OUT事務(wù)為host輸出服務(wù),當(dāng)host需要輸出數(shù)據(jù)到設(shè)備的時(shí)候,就需要OUT事務(wù)。
SETUP事務(wù):SETUP事務(wù)為host控制服務(wù),當(dāng)host希望傳輸一些USB規(guī)范的默認(rèn)操作的時(shí)候就需要使用setup事務(wù)。
SOF事務(wù):這個(gè)用于幀同步。
然后這4種事務(wù)又由3類包(token包,handshake包,data包)組成,每類又分幾種:
in包:in包用于指明當(dāng)前的事務(wù)為in類型的。
out包:?out包用于指明當(dāng)前事務(wù)為out類型的。
setup包:?setup包指明當(dāng)前事務(wù)為setup類型的。
sof包:?sof包指明當(dāng)前事務(wù)為setup類型的。
ack包:ack握手包指明當(dāng)前的事務(wù)的數(shù)據(jù)包傳輸是成功的。
nak包:nak握手包指明當(dāng)前設(shè)備忙,不能處理數(shù)據(jù)包,請(qǐng)主機(jī)稍后再次發(fā)送。
stall包:stall握手包指明當(dāng)前設(shè)備不能接受或者傳輸數(shù)據(jù),表示一個(gè)嚴(yán)重的錯(cuò)誤。
data0包:該數(shù)據(jù)包的類型為0。
data1包:該數(shù)據(jù)包的類型為1。
下圖是一個(gè)USB鼠標(biāo)插入Linux系統(tǒng)時(shí)完整的枚舉過程,一共發(fā)生了11次傳輸,每次傳輸包括幾個(gè)事務(wù),每個(gè)事務(wù)又包括幾個(gè)包,每個(gè)包包括幾個(gè)域。
這里有一個(gè)概念需要注意,這里的中斷傳輸與硬件中斷那個(gè)中斷是不一樣的,這個(gè)中斷傳輸實(shí)際是靠USB host control輪詢usb device來實(shí)現(xiàn)的,而USB host control對(duì)于CPU則是基于中斷的機(jī)制。
拿USB鼠標(biāo)為例,USB host control對(duì)USB鼠標(biāo)不斷請(qǐng)求,這個(gè)請(qǐng)求的間隔是很短的,在USB spec Table 9-13端點(diǎn)描述符中的bInterval域中指定的,當(dāng)鼠標(biāo)發(fā)生過了事件之后,鼠標(biāo)會(huì)發(fā)送數(shù)據(jù)回host,這時(shí)USB host control中斷通知CPU,于是usb_mouse_irq被調(diào)用,在usb_mouse_irq里,就可以讀取鼠標(biāo)發(fā)回來的數(shù)據(jù),當(dāng)讀完之后,驅(qū)動(dòng)再次調(diào)用usb_submit_urb發(fā)出請(qǐng)求,就這么一直重復(fù)下去,一個(gè)usb鼠標(biāo)的驅(qū)動(dòng)也就完成了。
下面是USB鼠標(biāo)中斷傳輸圖,可以看到USB host control向usb device發(fā)送了IN包,沒有數(shù)據(jù)的時(shí)候device回復(fù)的是NAK,有數(shù)據(jù)的時(shí)候才向host control發(fā)送DATA包。
9、USB設(shè)備被識(shí)別的過程
當(dāng)USB設(shè)備插上主機(jī)時(shí),主機(jī)就通過一系列的動(dòng)作來對(duì)設(shè)備進(jìn)行枚舉配置。
1、接入態(tài)(Attached):設(shè)備接入主機(jī)后,主機(jī)通過檢測(cè)信號(hào)線上的電平變化來發(fā)現(xiàn)設(shè)備的接入;
2、供電態(tài)(Powered):就是給設(shè)備供電,分為設(shè)備接入時(shí)的默認(rèn)供電值,配置階段后的供電值(按數(shù)據(jù)中要求的最大值,可通過編程設(shè)置)
3、缺省態(tài)(Default):USB在被配置之前,通過缺省地址0與主機(jī)進(jìn)行通信;
4、地址態(tài)(Address):經(jīng)過了配置,USB設(shè)備被復(fù)位后,就可以按主機(jī)分配給它的唯一地址來與主機(jī)通信,這種狀態(tài)就是地址態(tài);
5、配置態(tài)(Configured):通過各種標(biāo)準(zhǔn)的USB請(qǐng)求命令來獲取設(shè)備的各種信息,并對(duì)設(shè)備的某此信息進(jìn)行改變或設(shè)置。
6、掛起態(tài)(Suspended):總線供電設(shè)備在3ms內(nèi)沒有總線動(dòng)作,即USB總線處于空閑狀態(tài)的話,該設(shè)備就要自動(dòng)進(jìn)入掛起狀態(tài),在進(jìn)入掛起狀態(tài)后,總的電流功耗不超過280UA。
10、標(biāo)準(zhǔn)的USB設(shè)備請(qǐng)求命令
USB設(shè)備請(qǐng)求命令是在控制傳輸?shù)牡谝粋€(gè)階段:setup事務(wù)傳輸?shù)臄?shù)據(jù)傳輸階段發(fā)送給設(shè)備的。
標(biāo)準(zhǔn)USB設(shè)備請(qǐng)求命令共有11個(gè),大小都是8個(gè)字節(jié),具有相同的結(jié)構(gòu),由5 個(gè)字段構(gòu)成。通過標(biāo)準(zhǔn)USB準(zhǔn)設(shè)備請(qǐng)求,我們可以獲取存儲(chǔ)在設(shè)備EEPROM里面的信息;知道設(shè)備有哪些的設(shè)置或功能;獲得設(shè)備的運(yùn)行狀態(tài);改變?cè)O(shè)備的配置等。
標(biāo)準(zhǔn)USB準(zhǔn)設(shè)備請(qǐng)求?=?bmRequestType(1)?+?bRequest(2)?+?wvalue(2)?+?wIndex(2)?+?wLength(2)
bmRequestType:
[7 bit]= 0主機(jī)到設(shè)備;?1設(shè)備到主機(jī)?
[6-5 bit]= 00標(biāo)準(zhǔn)請(qǐng)求命令;?01類請(qǐng)求命令;?10用戶定義命令;?11保留
[4-0 bit]= 00000?接收者為設(shè)備;?00001?接收者為接口;?00010?接收者為端點(diǎn);?00011?接收者為其他接收者;?其他?其他值保留
bRequest:
0)?0 GET_STATUS:用來返回特定接收者的狀態(tài)
1)?1 CLEAR_FEATURE:用來清除或禁止接收者的某些特性
2)?3 SET_FEATURE:用來啟用或激活命令接收者的某些特性
3)?5 SET_ADDRESS:用來給設(shè)備分配地址
4)?6 GET_DEscriptOR:用于主機(jī)獲取設(shè)備的特定描述符
5)?7 SET_DEscriptOR:修改設(shè)備中有關(guān)的描述符,或者增加新的描述符
6)?8 GET_CONFIGURATION:用于主機(jī)獲取設(shè)備當(dāng)前設(shè)備的配置值、
7)?9 SET_CONFIGURATION:用于主機(jī)指示設(shè)備采用的要求的配置
8)?10 GET_INTERFACE:用于獲取當(dāng)前某個(gè)接口描述符編號(hào)
9)?11 SET_INTERFACE:用于主機(jī)要求設(shè)備用某個(gè)描述符來描述接口
10)?12 SYNCH_FRAME:用于設(shè)備設(shè)置和報(bào)告一個(gè)端點(diǎn)的同步
wvalue:?這個(gè)字段是?request?的參數(shù),request?不同,wValue就不同。
wIndex:wIndex,也是request?的參數(shù),bRequestType指明?request?針對(duì)的是設(shè)備上的某個(gè)接口或端點(diǎn)的時(shí)候,wIndex?就用來指明是哪個(gè)接口或端點(diǎn)。
wLength:控制傳輸中?DATA transaction?階段的長(zhǎng)度。
二、Linux?USB系統(tǒng)架構(gòu)
這個(gè)是USB系統(tǒng)的拓?fù)鋱D,4個(gè)部分構(gòu)成:USB主機(jī)控制器,根集線器,集線器,設(shè)備。其中Root Hub與USB主機(jī)控制器是綁定在一起的。
在機(jī)箱的尾部面板上,物理上存在一,二或四個(gè)USB端口。端口可以用來連接一個(gè)普通設(shè)備或者一個(gè)hub,hub是一個(gè)USB設(shè)備,可以用來擴(kuò)展連接USB設(shè)備的端口數(shù)量。最大連接USB設(shè)備數(shù)量是減去連在總線上的hub數(shù)量(如果有50個(gè)hub,那么最多77(=127-50)個(gè)設(shè)備能夠連接),剩下的就是能夠連接USB設(shè)備的數(shù)量。Hub總是高速的,如果一個(gè)hub是自供電的,那么任何設(shè)備都能夠附著到上面。但是如果hub是總線供電的,那么僅僅低供電(最大100mA)設(shè)備能夠附著到上面,一個(gè)總線供電的hub不應(yīng)該連接到另一個(gè)總線供電的hub-你應(yīng)該在總線供電和自供電間交替.
通常情況下主機(jī)控制器的物理端口由一個(gè)虛擬的root hub臉管理。這個(gè)hub是有主機(jī)控制器(host controller)的設(shè)備驅(qū)動(dòng)虛擬的,用來統(tǒng)一管理總線拓?fù)?,因此USB子系統(tǒng)的驅(qū)動(dòng)能夠用同樣的方法管理每個(gè)端口。
USB通信都是由host端發(fā)起的。USB設(shè)備驅(qū)動(dòng)程序分配并初始化一個(gè)URB發(fā)給USB Core,USB Core改一改,發(fā)給USB主機(jī)控制器驅(qū)動(dòng),USB主機(jī)控制器驅(qū)動(dòng)把它解析成包,在總線上進(jìn)行傳送。
USB Core是由內(nèi)核實(shí)現(xiàn)的,其實(shí)也就是把host control driver里的功能更集中的向上抽象了一層,它是用來對(duì)最上層的USB設(shè)備驅(qū)動(dòng)屏蔽掉host control的不同。
USB通信最基本的形式是通過一個(gè)名為端點(diǎn)(endpoint)的東西。它是真實(shí)存在的。端點(diǎn)只能往一個(gè)方向傳送數(shù)據(jù)(端點(diǎn)0除外,端點(diǎn)0使用message管道,它既可以IN又可以O(shè)UT),或者IN,或者OUT(前面已經(jīng)介紹過)。除了端點(diǎn)0,低速設(shè)備只能有2個(gè)端點(diǎn),高速設(shè)備也只能有15個(gè)IN端點(diǎn)和15個(gè)OUT端點(diǎn)。主機(jī)和端點(diǎn)之間的數(shù)據(jù)傳輸是通過管道。端點(diǎn)只有在device上才有,協(xié)議說端點(diǎn)代表在主機(jī)和設(shè)備端點(diǎn)之間移動(dòng)數(shù)據(jù)的能力。
Linux系統(tǒng)下的usb部分分為四個(gè)部門或者叫做四大家族,他們是host控制器驅(qū)動(dòng)、hub驅(qū)動(dòng)、usb core、設(shè)備類驅(qū)動(dòng),他們共同配合著完成了對(duì)usb設(shè)備的訪問操作。
枚舉和設(shè)備描述符
每當(dāng)一個(gè)USB設(shè)備附著到總線上,它將會(huì)被USB子系統(tǒng)枚舉.也就是分配唯一的設(shè)備號(hào)(1-127)然后讀取設(shè)備描述符.描述符是一個(gè)包含關(guān)于設(shè)備的信息和屬性的數(shù)據(jù)結(jié)構(gòu).USB標(biāo)準(zhǔn)定義了一個(gè)描述符層次結(jié)構(gòu),下圖所示:
?
標(biāo)準(zhǔn)描述符
設(shè)備描述符:?描述USB設(shè)備的大概信息,其中包括適用于設(shè)備的全局信息,所有設(shè)備的配置。一個(gè)USB設(shè)備只有一個(gè)設(shè)備描述符。
配置描述符:?描述了特定的設(shè)備配置信息。一個(gè)USB設(shè)備可以有一或多個(gè)配置描述符。每個(gè)配置有一個(gè)或多個(gè)接口(interface),并且每個(gè)接口有零或多個(gè)端點(diǎn)(endpoint)。一個(gè)端點(diǎn)在一個(gè)單獨(dú)的配置下,是不和其他的接口共享的,但是一個(gè)單獨(dú)的接口對(duì)于同一個(gè)端點(diǎn)能夠有幾種可選的配置。端點(diǎn)可以沒有限制的在一部分不同的配置下的接口間共享。配置僅僅能夠通過標(biāo)準(zhǔn)的控制傳輸set_configuration來激活。不同的配置能夠用來全局配置信息,例如供電消耗。
接口描述符:?描述了一個(gè)配置內(nèi)的特定接口。一個(gè)配置提供一個(gè)或多個(gè)接口,每個(gè)接口帶有零個(gè)或多個(gè)端點(diǎn)描述符描述了在配置內(nèi)的唯一配置。一個(gè)可以包含可選的配置的接口使得配置好的端點(diǎn)和/或他們的特性能夠多種多樣。默認(rèn)的接口設(shè)置總是設(shè)置為零。可替換的設(shè)置能夠在標(biāo)準(zhǔn)控制傳輸?shù)膕et_interface來選擇一個(gè)。例如一個(gè)多功能設(shè)備帶有話筒的攝像頭,可以有三種可用的配置來改變分配在總線上的帶寬。
Camera activatedMicrophone activatedCamera and microphone activated
端點(diǎn)描述符:?包含主機(jī)用來決定每個(gè)端點(diǎn)帶寬的信息。一個(gè)端點(diǎn)象征一個(gè)USB設(shè)備的邏輯數(shù)據(jù)源或接收端(logic data source or sink)。端點(diǎn)零是用來所有的控制傳輸并且該端點(diǎn)沒有設(shè)備描述符。USB spec交替使用pipe和endpoint術(shù)語。
字符串描述符:?是可選項(xiàng),提供了unicode編碼的額外的可讀信息。他們可以是廠商和設(shè)備名稱或序列號(hào)。
設(shè)備類型
標(biāo)準(zhǔn)的設(shè)備和接口描述符包含有關(guān)分類的內(nèi)容:class,?sub-class和protocol。這些字段主機(jī)可以用來設(shè)備或接口和驅(qū)動(dòng)聯(lián)系。依賴于分類說明是如何指定的?對(duì)于class字段和接口描述符的合法字段是由USB Device Working Group來定義的。
在Class Specification中將設(shè)備或接口分組歸類并指定特性,這樣就使得主機(jī)開發(fā)軟件能夠基于這個(gè)類別進(jìn)行管理多種多樣的實(shí)現(xiàn)。這樣的主機(jī)軟件通過設(shè)備中的描述信息將操作方法綁定到指定的設(shè)備。一個(gè)類別規(guī)格作為所有的該類別的設(shè)備或接口的最小操作框架服務(wù)。(PS:也就是說,所有該類別的設(shè)備或接口,都是以類別規(guī)格定義為接口框架。)
人機(jī)接口設(shè)備
HID分類,主要是包含人們控制計(jì)算機(jī)系統(tǒng)的設(shè)備。典型的HID分類設(shè)備包含:?
鍵盤和鼠標(biāo)設(shè)備例如:標(biāo)準(zhǔn)的鼠標(biāo)設(shè)備,追蹤球,游戲手柄。
前端面板控制?? 例如:旋鈕,開關(guān),按鍵,滾動(dòng)器。
可能在電話設(shè)備,遠(yuǎn)端控制VCR,游戲或模擬設(shè)備上存在控制器。
再了解一下USB驅(qū)動(dòng)框架:
USB總線和USB設(shè)備使用軟件進(jìn)行抽象描述起來是非常復(fù)雜的,一方面是協(xié)議使然,一方面也是因?yàn)樗鼈兪褂锰珡V泛了,抽象時(shí)考慮很太多情況。幸運(yùn)的是,內(nèi)核開發(fā)者們抽象出來的內(nèi)核USB 子系統(tǒng)把很多復(fù)雜性都隱藏了。
針對(duì)上面這幅圖,為了理解什么是USB子系統(tǒng),我們要做以下說明:
a) USB 驅(qū)動(dòng)都是夸kernel子系統(tǒng)的,因?yàn)樽罱KUSB設(shè)備是要通過BLCOCK 或CHAR設(shè)備的方式呈現(xiàn)給我們的,所以USB Driver之上還有一層。
b) USB driver利用USB Core提供的API來簡(jiǎn)單優(yōu)雅的完成驅(qū)動(dòng)工作,這里USB Core抽象了復(fù)雜的USB協(xié)議。
c) 主機(jī)控制器驅(qū)動(dòng)位于USB軟件的最下層,提供主機(jī)控制器硬件的抽象,隱藏硬件的細(xì)節(jié),在主機(jī)控制器之下是物理的USB及所有與之連接的USB設(shè)備。主機(jī)控制器驅(qū)動(dòng)只和USB Core進(jìn)行關(guān)聯(lián),USB Core將用戶的請(qǐng)求映射到相關(guān)的主機(jī)控制器驅(qū)動(dòng),從而使用戶無需去訪問主機(jī)控制器。
d) USB Core和USB主機(jī)控制器驅(qū)動(dòng)就構(gòu)成了我們的USB子系統(tǒng),USB Core負(fù)責(zé)實(shí)現(xiàn)一些核心的功能,例如協(xié)議之類,提供一個(gè)用于訪問和控制USB硬件的接口,使設(shè)備驅(qū)動(dòng)不用去考慮系統(tǒng)當(dāng)前使用哪種主機(jī)控制器。自從有了USB子系統(tǒng),寫USB驅(qū)動(dòng)的時(shí)候,只需要調(diào)用USB Core export的接口,就幾乎能完成所有工作。
e) USB總線將USB設(shè)備和USB驅(qū)動(dòng)關(guān)聯(lián)起來。
USB子系統(tǒng)初始化
usb初始化函數(shù)定義在內(nèi)核源碼(2.6.37)drivers/usb/core/usb.c:
/* * Init */static int __init usb_init(void){ int retval; if (nousb) { pr_info("%s: USB support disabled ", usbcore_name); return 0; } retval = usb_debugfs_init(); if (retval) goto out; retval =bus_register(&usb_bus_type); if (retval) goto bus_register_failed; retval =bus_register_notifier(&usb_bus_type, &usb_bus_nb); if (retval) goto bus_notifier_failed; retval = usb_major_init(); if (retval) goto major_init_failed; retval =usb_register(&usbfs_driver); if (retval) goto driver_register_failed; retval = usb_devio_init(); if (retval) goto usb_devio_init_failed; retval = usbfs_init(); if (retval) goto fs_init_failed; retval =usb_hub_init(); if (retval) goto hub_init_failed; retval =usb_register_device_driver(&usb_generic_driver, THIS_MODULE); if (!retval) goto out; usb_hub_cleanup();hub_init_failed: usbfs_cleanup();fs_init_failed: usb_devio_cleanup();usb_devio_init_failed: usb_deregister(&usbfs_driver);driver_register_failed: usb_major_cleanup();major_init_failed: bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);bus_notifier_failed: bus_unregister(&usb_bus_type);bus_register_failed: usb_debugfs_cleanup();out: return retval;}subsys_initcall(usb_init);
usb_debugfs_init():
DebugFS,顧名思義,是一種用于內(nèi)核調(diào)試的虛擬文件系統(tǒng),內(nèi)核開發(fā)者通過debugfs和用戶空間交換數(shù)據(jù)。類似的虛擬文件系統(tǒng)還有procfs和sysfs等,這幾種虛擬文件系統(tǒng)都并不實(shí)際存儲(chǔ)在硬盤上,而是Linux內(nèi)核運(yùn)行起來后,執(zhí)行mount -t debugfs none /media/mmcblk0p2/?才建立起來。在/media/mmcblk0p2/目錄下創(chuàng)建usb目錄并在下面創(chuàng)建devices文件。
當(dāng)我們執(zhí)行cat devices會(huì)調(diào)用usbfs_devices_fops->read(usb_device_read)函數(shù)去搜尋usb_bus_list鏈表下的usb設(shè)備信息,也就是所有總線下的設(shè)備。
bus_register:
是將usb總線注冊(cè)到系統(tǒng)中,總線可是linux設(shè)備模型中的領(lǐng)導(dǎo)者,不管是多大的領(lǐng)導(dǎo),也是領(lǐng)導(dǎo),如PCI、USB、I2C,即使他們?cè)谖锢砩嫌袕膶訇P(guān)系,但是在模型的世界里,都是總線,擁有一樣的待遇,所以任何一個(gè)子系統(tǒng)只要管理自己的設(shè)備和驅(qū)動(dòng),就需要向內(nèi)核注冊(cè)一個(gè)總線,注冊(cè)報(bào)到。
bus_register_notifier:
大多數(shù)內(nèi)核子系統(tǒng)都是相互獨(dú)立的,因此某個(gè)子系統(tǒng)可能對(duì)其它子系統(tǒng)產(chǎn)生的事件感興趣。為了滿足這個(gè)需求,也即是讓某個(gè)子系統(tǒng)在發(fā)生某個(gè)事件時(shí)通知其它的子系統(tǒng),Linux內(nèi)核提供了通知鏈的機(jī)制。通知鏈表只能夠在內(nèi)核的子系統(tǒng)之間使用,而不能夠在內(nèi)核與用戶空間之間進(jìn)行事件的通知。
通知鏈表是一個(gè)函數(shù)鏈表,鏈表上的每一個(gè)節(jié)點(diǎn)都注冊(cè)了一個(gè)函數(shù)。當(dāng)某個(gè)事情發(fā)生時(shí),鏈表上所有節(jié)點(diǎn)對(duì)應(yīng)的函數(shù)就會(huì)被執(zhí)行。所以對(duì)于通知鏈表來說有一個(gè)通知方與一個(gè)接收方。在通知這個(gè)事件時(shí)所運(yùn)行的函數(shù)由被通知方?jīng)Q定,實(shí)際上也即是被通知方注冊(cè)了某個(gè)函數(shù),在發(fā)生某個(gè)事件時(shí)這些函數(shù)就得到執(zhí)行。其實(shí)和系統(tǒng)調(diào)用signal的思想差不多。
bus_register->BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier),已經(jīng)初始化了usb_bus_type->p->bus_notifier通過blocking_notifier_chain_register函數(shù)注冊(cè)到通知鏈表。
那什么時(shí)候usb總線收到通知呢?
當(dāng)總線發(fā)現(xiàn)新的設(shè)備調(diào)用device_add->blocking_notifier_call_chain(&dev->bus->p->bus_notifier,?BUS_NOTIFY_ADD_DEVICE, dev)
當(dāng)總線卸載設(shè)備時(shí)調(diào)用device_del->blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_DEL_DEVICE, dev);
則調(diào)用usb_bus_nb的回調(diào)成員函數(shù)notifier_call(usb_bus_notify),函數(shù)定義如下:
/* * Notifications of device and interface registration */static int usb_bus_notify(struct notifier_block *nb, unsigned long action, void *data){ struct device *dev = data; switch (action) { case BUS_NOTIFY_ADD_DEVICE: if (dev->type == &usb_device_type)//usb 設(shè)備 (void) usb_create_sysfs_dev_files(to_usb_device(dev)); //創(chuàng)建descriptors文件 else if (dev->type == &usb_if_device_type) //usb接口 (void) usb_create_sysfs_intf_files( to_usb_interface(dev));//創(chuàng)建interface文件 break; case BUS_NOTIFY_DEL_DEVICE: if (dev->type == &usb_device_type)//usb設(shè)備 usb_remove_sysfs_dev_files(to_usb_device(dev));//刪除descriptors文件 else if (dev->type == &usb_if_device_type)//usb接口 usb_remove_sysfs_intf_files(to_usb_interface(dev));//刪除interface文件 break; } return 0;}
usb_major_init:注冊(cè)字符設(shè)備,主設(shè)備號(hào)180。
usb_register(&usbfs_driver):
struct usb_driver usbfs_driver = { .name = "usbfs", .probe = driver_probe, .disconnect = driver_disconnect, .suspend = driver_suspend, .resume = driver_resume, };
usb_register->usb_register_driver():
/** * usb_register_driver - register a USB interface driver * @new_driver: USB operations for the interface driver * @owner: module owner of this driver. * @mod_name: module name string * * Registers a USB interface driver with the USB core. The list of * unattached interfaces will be rescanned whenever a new driver is * added, allowing the new driver to attach to any recognized interfaces. * Returns a negative error code on failure and 0 on success. * * NOTE: if you want your driver to use the USB major number, you must call * usb_register_dev() to enable that functionality. This function no longer * takes care of that. */int usb_register_driver(struct usb_driver *new_driver, struct module *owner, const char *mod_name){ int retval = 0; if (usb_disabled()) return -ENODEV; new_driver->drvwrap.for_devices = 0; new_driver->drvwrap.driver.name = (char *) new_driver->name; new_driver->drvwrap.driver.bus = &usb_bus_type; new_driver->drvwrap.driver.probe = usb_probe_interface; new_driver->drvwrap.driver.remove = usb_unbind_interface; new_driver->drvwrap.driver.owner = owner; new_driver->drvwrap.driver.mod_name = mod_name; spin_lock_init(&new_driver->dynids.lock); INIT_LIST_HEAD(&new_driver->dynids.list); retval = driver_register(&new_driver->drvwrap.driver); if (retval) goto out; usbfs_update_special(); retval = usb_create_newid_file(new_driver); if (retval) goto out_newid; retval = usb_create_removeid_file(new_driver); if (retval) goto out_removeid; pr_info("%s: registered new interface driver %s ", usbcore_name, new_driver->name);out: return retval;out_removeid: usb_remove_newid_file(new_driver);out_newid: driver_unregister(&new_driver->drvwrap.driver); printk(KERN_ERR "%s: error %d registering interface " " driver %s ", usbcore_name, retval, new_driver->name); goto out;}EXPORT_SYMBOL_GPL(usb_register_driver);
其余功能如下:
1>?driver_register實(shí)現(xiàn)。后面會(huì)詳細(xì)分析。
2>?usbfs_update_special(): 跟usb文件系統(tǒng)相關(guān),看下面的usbfs_init分析。
3>?usb_create_newid_file():?創(chuàng)建newid屬性文件,在/sys/bus/usb/drivers/usbfs/下面可以看到此文件。根據(jù)傳入的ID值,增加一個(gè)新的動(dòng)態(tài)usb設(shè)備到驅(qū)動(dòng)(這里是usbfs),引起驅(qū)動(dòng)重新探測(cè)所有的設(shè)備。
4>?usb_create_removeid_file():創(chuàng)建removeid屬性文件,在/sys/bus/usb/drivers/usbfs/下面可以看到此文件。根據(jù)傳入的ID值,刪除驅(qū)動(dòng)(這里是usbfs)里的一個(gè)usb設(shè)備。
5> 輸出信息:usbcore: registered new interface driver usbfs
現(xiàn)在分析driver_register功能:
1>?首先判斷,些驅(qū)動(dòng)所屬bus的subsys_private結(jié)構(gòu)有沒有初始化。如果沒有,報(bào)bug信息。
2> 判斷需要注冊(cè)的driver和driver所屬的bus是否都有probe, remove, shutdown函數(shù)。如有,打印kernel warning信息。
3> 判斷此driver已經(jīng)在driver所屬的bus上面注冊(cè)過了。如果注冊(cè)過了,打印錯(cuò)誤信息,并返回。
4> 調(diào)用bus_add_driver來注冊(cè)driver。
5> 調(diào)用driver_add_groups來添加組屬性。
最后對(duì)bus_add_driver進(jìn)行分析。
/** * bus_add_driver - Add a driver to the bus. * @drv: driver. */int bus_add_driver(struct device_driver *drv){ struct bus_type *bus; struct driver_private *priv; int error = 0; bus = bus_get(drv->bus); if (!bus) return -EINVAL; pr_debug("bus: '%s': add driver %s ", bus->name, drv->name); priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { error = -ENOMEM; goto out_put_bus; } klist_init(&priv->klist_devices, NULL, NULL); priv->driver = drv; drv->p = priv; priv->kobj.kset = bus->p->drivers_kset; error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, "%s", drv->name); if (error) goto out_unregister; if (drv->bus->p->drivers_autoprobe) { error = driver_attach(drv); if (error) goto out_unregister; } klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); module_add_driver(drv->owner, drv); error = driver_create_file(drv, &driver_attr_uevent); if (error) { printk(KERN_ERR "%s: uevent attr (%s) failed ", __func__, drv->name); } error = driver_add_attrs(bus, drv); if (error) { /* How the hell do we get out of this pickle? Give up */ printk(KERN_ERR "%s: driver_add_attrs(%s) failed ", __func__, drv->name); } if (!drv->suppress_bind_attrs) { error = add_bind_files(drv); if (error) { /* Ditto */ printk(KERN_ERR "%s: add_bind_files(%s) failed ", __func__, drv->name); } } kobject_uevent(&priv->kobj, KOBJ_ADD); return 0;out_unregister: kobject_put(&priv->kobj); kfree(drv->p); drv->p = NULL;out_put_bus: bus_put(bus); return error;}
其功能是向bus中添加一個(gè)driver。
1> bus_get():bus的計(jì)數(shù)加1;
2> kzalloc,分配driver_private內(nèi)存空間。
3> 初始化此driver的klist_devices鏈表。
4> kobject_init_and_add():在/sys/bus/usb/drivers/下面創(chuàng)建usbfs文件夾。
5> 如果總線支持drivers_autoprobe,調(diào)用driver_attach。(USB 總線支持)
6> driver_create_file: 在/sys/bus/usb/drivers/usbfs下面創(chuàng)建uevent屬性文件。
7> driver_add_attrs():將總線的屬性也加到/sys/bus/usb/drivers/usbfs
8> add_bind_files():在/sys/bus/usb/drivers/usbfs創(chuàng)建bind和unbind屬性文件。
9> kobject_uevent():發(fā)送一個(gè)KOBJ_ADD的事件。
在/sys/bus/usb/drivers/usbfs下面的文件:
bind?????? module???? new_id???? remove_id ? ?uevent???? unbind
usb_devio_init:注冊(cè)字符設(shè)備,主設(shè)備189。
usbfs_init:
int __init usbfs_init(void){ int retval; retval = register_filesystem(&usb_fs_type); if (retval) return retval; usb_register_notify(&usbfs_nb); /* create mount point for usbfs */ usbdir = proc_mkdir("bus/usb", NULL); return 0;}
函數(shù)功能:
1>?register_filesystem注冊(cè)u(píng)sbfs文件系統(tǒng),當(dāng)應(yīng)用程序執(zhí)行mount命令的時(shí)候,掛載文件系統(tǒng)到相應(yīng)的目錄。
2>?usb_register_notify函數(shù)注冊(cè)到內(nèi)核通知鏈表,當(dāng)收到其他子系統(tǒng)通知,調(diào)用notifier_call回調(diào)函數(shù)usbfs_notify:
static int usbfs_notify(struct notifier_block *self, unsigned long action, void *dev){ switch (action) { case USB_DEVICE_ADD: usbfs_add_device(dev);//在bus號(hào)創(chuàng)建的目錄下,根據(jù)設(shè)備號(hào)創(chuàng)建設(shè)備文件 break; case USB_DEVICE_REMOVE: usbfs_remove_device(dev);//刪除bus號(hào)創(chuàng)建的目錄下的設(shè)備文件 break; case USB_BUS_ADD: usbfs_add_bus(dev);//根據(jù)bus號(hào)創(chuàng)建目錄 break; case USB_BUS_REMOVE: usbfs_remove_bus(dev);//刪除bus號(hào)創(chuàng)建的目錄 } usbfs_update_special();//更新文件系統(tǒng)節(jié)點(diǎn) usbfs_conn_disc_event(); return NOTIFY_OK;}
static BLOCKING_NOTIFIER_HEAD(usb_notifier_list);usb_notifier_list通知鏈表初始化
usb_register_notify->blocking_notifier_chain_register(&usb_notifier_list, nb):向usb_notifier_list通知鏈表注冊(cè)
blocking_notifier_call_chain(&usb_notifier_list,?USB_DEVICE_ADD, udev):通知有usb設(shè)備增加
blocking_notifier_call_chain(&usb_notifier_list,USB_DEVICE_REMOVE, udev):通知有usb設(shè)備移除
blocking_notifier_call_chain(&usb_notifier_list,?USB_BUS_ADD, ubus):通知有usb總線增加
blocking_notifier_call_chain(&usb_notifier_list,?USB_BUS_REMOVE, ubus):通知有usb總線移除
3>?proc_mkdir在/proc/bus/目錄下創(chuàng)建usb目錄。
usb_register_device_driver:
在了解usb_generic_driver驅(qū)動(dòng)前,先分析usb總線的match函數(shù):
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;}
函數(shù)中我們分成兩類判斷:
is_usb_device(),根據(jù)設(shè)備類型dev->type == &usb_device_type 來判斷是否是usb設(shè)備,然后在通過for_devices(usb_register_device_driver函數(shù)注冊(cè)的時(shí)候設(shè)置為1)?判斷驅(qū)動(dòng)是否是usb設(shè)備設(shè)備驅(qū)動(dòng),如果成功,則設(shè)備和設(shè)備驅(qū)動(dòng)匹配,調(diào)用相應(yīng)的驅(qū)動(dòng)的probe函數(shù)(因?yàn)閡sb總線沒有probe成員函數(shù))。
is_usb_interface(),根據(jù)設(shè)備類型dev->type == &usb_if_device_type 來判斷是否是接口,然后在通過for_devices(usb_register函數(shù)注冊(cè)的時(shí)候設(shè)置為0)?判斷驅(qū)動(dòng)是否是接口驅(qū)動(dòng),如果是接口驅(qū)動(dòng)(所以調(diào)用usb_register都是注冊(cè)的接口驅(qū)動(dòng),因?yàn)橐粋€(gè)設(shè)備可以有多個(gè)接口,每個(gè)接口必須獨(dú)立驅(qū)動(dòng)),接著usb_match_id這個(gè)函數(shù)就是用來判斷這個(gè)接口是否在id table中得到了match,一旦得到,就進(jìn)入了具體接口驅(qū)動(dòng)的probe函數(shù)了。。
到這里我們不禁要思索驅(qū)動(dòng)找到了注冊(cè)的地方,那設(shè)備來自哪里?這里也有兩個(gè)函數(shù)要分析:
usb_alloc_dev():dev->dev.type = &usb_device_type,這里就表示了是usb設(shè)備,這個(gè)函數(shù)主要有兩個(gè)地方調(diào)用。一個(gè)就是usb_init->usb_hub_init->hub_thread->hub_events->hub_port_connect_change,這個(gè)會(huì)在下面進(jìn)行詳細(xì)的分析;另外一個(gè)musb_probe->musb_init_controller->usb_add_hcd,DM8168芯片注冊(cè)主控器的時(shí)候用到(或者其他芯片主控器注冊(cè))。
usb_set_configuration():?intf->dev.type = &usb_if_device_type,這里就表示了是接口。
這里我們知道usb_register?和?usb_register_device_driver,一個(gè)是設(shè)備驅(qū)動(dòng)的注冊(cè),一個(gè)是接口驅(qū)動(dòng)的注冊(cè),match的時(shí)候通過for_devices來區(qū)分。接口指的就是一種具體的功能。
上面我們提過每種類型的總線都有一套自己的驅(qū)動(dòng)函數(shù),看來在usb的世界里更特殊一些,usb總線下的設(shè)備驅(qū)動(dòng)有一套,接口驅(qū)動(dòng)也有一套:usb_probe_interface。
不管是設(shè)備還是接口都是掛在總線上的,一個(gè)總線只有一個(gè)match函數(shù),usb_device_match。
在這個(gè)usb的match函數(shù)里,首先是對(duì)usb設(shè)備的match,設(shè)備的match很簡(jiǎn)單的,只要是個(gè)usb設(shè)備就認(rèn)為match了,因?yàn)楝F(xiàn)在進(jìn)來的usb設(shè)備統(tǒng)統(tǒng)都認(rèn)為是usb_generic_driver的,都和他match。上面我們提到過這個(gè),所有的usb設(shè)備首先都會(huì)經(jīng)過篩選這一關(guān),處理之后,才有重生的機(jī)會(huì)。接口就不一樣了,如果進(jìn)來的dev不是設(shè)備,就認(rèn)為是個(gè)接口,然后判斷drv是否為接口驅(qū)動(dòng),如果是,那么就繼續(xù)判斷,這個(gè)判斷機(jī)制就是usb特有的了:Id。每個(gè)接口驅(qū)動(dòng)注冊(cè)的時(shí)候都會(huì)有一個(gè)id?的,加到了id table表中。
看了上面分析,usb match函數(shù)中涉及到的設(shè)備和接口驅(qū)動(dòng)兩條判斷路線,在usb的世界里,真正的驅(qū)動(dòng)是針對(duì)接口的,針對(duì)設(shè)備的其實(shí)是剛開始沒有配置之前,一個(gè)通用的usb設(shè)備驅(qū)動(dòng),用來處理所有的usb設(shè)備,將其進(jìn)入配置態(tài),獲取該配置下的各種接口,并將接口作為一種特殊的usb設(shè)備(接口設(shè)備)添加到設(shè)備模型中。
下面我們分析usb_generic_driver:
struct usb_device_driver usb_generic_driver = { .name = "usb", .probe = generic_probe, .disconnect = generic_disconnect,#ifdef CONFIG_PM .suspend = generic_suspend, .resume = generic_resume,#endif .supports_autosuspend = 1,};
當(dāng)USB設(shè)備(只有設(shè)備先被注冊(cè)之后才會(huì)分析接口,才會(huì)注冊(cè)接口) 被探測(cè)并被注冊(cè)到系統(tǒng)后(用device_add),會(huì)調(diào)用usb_bus_type.mach()(只要是usb設(shè)備,都會(huì)跟usb_generic_driver匹配上),之后會(huì)調(diào)用usb_probe_device(),從而引發(fā)usb_generic_driver的 probe()調(diào)用,也就是generic_probe函數(shù)。
下面將會(huì)對(duì)generic_probe函數(shù)進(jìn)行分析:
static int generic_probe(struct usb_device *udev){ int err, c; if (udev->authorized == 0) dev_err(&udev->dev, "Device is not authorized for usage "); else { c = usb_choose_configuration(udev); if (c >= 0) { err = usb_set_configuration(udev, c); if (err) { dev_err(&udev->dev, "can't set config #%d, error %d ", c, err); /* This need not be fatal. The user can try to * set other configurations. */ } } } usb_notify_add_device(udev); return 0;}
usb_generic_driver中的generic_probe函數(shù),這個(gè)函數(shù)是一個(gè)usb設(shè)備的第一個(gè)匹配的driver。Generic通用,只要是個(gè)usb設(shè)備就得先跟他來一段,usb設(shè)備驅(qū)動(dòng)界的老大。他的probe干啥了呢?很簡(jiǎn)單!找個(gè)合適的配置,配置一下。從此usb設(shè)備就進(jìn)入配置的時(shí)代了。(前期的工作誰做的呢,到這都已經(jīng)設(shè)置完地址了,當(dāng)然是hub了,hub發(fā)現(xiàn)設(shè)備后,會(huì)進(jìn)行前期的枚舉過程,獲得配置,最終調(diào)用device_add將該usb設(shè)備添加到總線上。這個(gè)過程可以專門來一大段,是hub的主要工作,所以需要把hub單獨(dú)作為一個(gè)家族來對(duì)待,人家可是走在第一線的默默無聞的工作者,默默的將設(shè)備枚舉完成后,將這個(gè)設(shè)備添加到usb總線上,多偉大)。
注意:設(shè)備setconfig時(shí)參數(shù)只能為0或者合理的配置值,0就代表不配置,仍然是尋址態(tài)。不過有些設(shè)備就是拿配置0作為配置值得。
usb_choose_configuration從設(shè)備可能的眾多配置(udev->descriptor.bNumConfigurations)選擇一個(gè)合適的配置(struct usb_host_config),并返回該配置的索引值。
//為usb device選擇一個(gè)合適的配置int usb_choose_configuration(struct usb_device *udev){ int i; int num_configs; int insufficient_power = 0; struct usb_host_config *c, *best; best = NULL; //udev->config,其實(shí)是一個(gè)數(shù)組,存放設(shè)備的配置.usb_dev->config[m]-> interface[n]表示第m個(gè)配置的第n個(gè)接口的intercace結(jié)構(gòu).(m,n不是配置序號(hào)和接口序號(hào)). c = udev->config; //config項(xiàng)數(shù) num_configs = udev->descriptor.bNumConfigurations; //遍歷所有配置項(xiàng) for (i = 0; i < num_configs; (i++, c++)) { struct usb_interface_descriptor *desc = NULL; //配置項(xiàng)的接口數(shù)目 //取配置項(xiàng)的第一個(gè)接口 if (c->desc.bNumInterfaces > 0) desc = &c->intf_cache[0]->altsetting->desc; ... ... //電源不足.配置描述符中的電力是所需電力的1/2 if (c->desc.bMaxPower * 2 > udev->bus_mA) { insufficient_power++; continue; } //非標(biāo)準(zhǔn)Ethernet-over-USB協(xié)議 if (i == 0 && num_configs > 1 && desc && (is_rndis(desc) || is_activesync(desc))){ ... ... } //選擇一個(gè)不是USB_CLASS_VENDOR_SPEC的配置 else if (udev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC && (!desc || desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC)) { best = c; break; } /*如果所有剩下的配置是特殊的vendor,選擇第一個(gè)*/ else if (!best) best = c; } ... ... //如果選擇好了配置,返回配置的序號(hào),否則,返回-1 if (best) { i = best->desc.bConfigurationValue; dev_info(&udev->dev, "configuration #%d chosen from %d choice%s ", i, num_configs, plural(num_configs)); } else { i = -1; dev_warn(&udev->dev, "no configuration chosen from %d choice%s ", num_configs, plural(num_configs)); } return i;}
例如:我機(jī)器上的的 usb 驅(qū)動(dòng)加載時(shí),輸出:usb 1-1: configuration #1 chosen from 3 choices
表示:此設(shè)備有3個(gè)配置,而驅(qū)動(dòng)最終選擇了索引號(hào)為1的配置,至于選擇策略是怎樣的,請(qǐng)看usb_choose_configuration()函數(shù)。
generic_probe函數(shù)中的usb_set_configuration函數(shù)里有很重要的動(dòng)作,不是簡(jiǎn)單的設(shè)置個(gè)配置,當(dāng)我們選擇了某一個(gè)配置后,需要將這個(gè)配置的所有接口取出來,初始化接口作為驅(qū)動(dòng)對(duì)應(yīng)的一種”設(shè)備”的參數(shù),如總線類型、設(shè)備類型等,調(diào)用device_add將該接口設(shè)備添加到設(shè)備模型中。
int usb_set_configuration(struct usb_device *dev, int configuration){ ... ... if (cp && configuration == 0) dev_warn(&dev->dev, "config 0 descriptor?? "); /*首先,根據(jù)選擇好的配置號(hào)找到相應(yīng)的配置,在這里要注意了, dev->config[]數(shù)組中的配置并不是按照配置的序號(hào)來存放的,而是按照遍歷到順序來排序的.因?yàn)橛行┰O(shè)備在發(fā)送配置描述符的時(shí)候,并不是按照配置序號(hào)來發(fā)送的,例如,配置2可能在第一次GET_CONFIGURATION就被發(fā)送了,而配置1可能是在第二次GET_CONFIGURATION才能發(fā)送. 取得配置描述信息之后,要對(duì)它進(jìn)行有效性判斷,注意一下本段代碼的最后幾行代碼:usb2.0 spec上規(guī)定,0號(hào)配置是無效配置,但是可能有些廠商的設(shè)備并末按照這一約定,所以在linux中,遇到這種情況只是打印出警告信息,然后嘗試使用這一配置.*/ n = nintf = 0; if (cp) { //接口總數(shù) nintf = cp->desc.bNumInterfaces; //在這里, 注要是為new_interfaces分配空間,要這意的是, new_interfaces是一個(gè)二級(jí)指針,它的最終指向是struct usb_interface結(jié)構(gòu).特別的,如果總電流數(shù)要小于配置所需電流,則打印出警告消息.實(shí)際上,這種情況在usb_choose_configuration()中已經(jīng)進(jìn)行了過濾. new_interfaces = kmalloc(nintf * sizeof(*new_interfaces), GFP_KERNEL); ... ... for (; n < nintf; ++n) { new_interfaces[n] = kzalloc( sizeof(struct usb_interface), GFP_KERNEL); ... ... } //如果總電源小于所需電流,打印警告信息 i = dev->bus_mA - cp->desc.bMaxPower * 2; ... ... } //要對(duì)設(shè)備進(jìn)行配置了,先喚醒它 ret = usb_autoresume_device(dev); if (ret) goto free_interfaces; //不是處于ADDRESS狀態(tài),先清除設(shè)備的狀態(tài) if (dev->state != USB_STATE_ADDRESS) usb_disable_device(dev, 1); /* Skip ep0 */ //確定我們有足夠帶寬提供這個(gè)配置 ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL); ... ... //發(fā)送控制消息,選取配置 ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION, 0, configuration, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); ... ... } //dev->actconfig存放的是當(dāng)前設(shè)備選取的配置 dev->actconfig = cp; ... ... //將狀態(tài)設(shè)為CONFIGURED usb_set_device_state(dev, USB_STATE_CONFIGURED); /*接下來,就要對(duì)設(shè)備進(jìn)行配置了,首先,將設(shè)備喚醒.只有在ADDRESS狀態(tài)才能轉(zhuǎn)入到CONFIG狀態(tài).(SUSPEND狀態(tài)除外). 所以,如果設(shè)備當(dāng)前不是處于ADDRESS狀態(tài),就需要將設(shè)備的狀態(tài)初始化 接著,發(fā)送SET_CONFIGURATION的Control消息給設(shè)備,用來選擇配置最后,將dev->actconfig指向選定的配置,將設(shè)備狀態(tài)設(shè)為CONFIG*/ //遍歷所有的接口 for (i = 0; i < nintf; ++i) { struct usb_interface_cache *intfc; struct usb_interface *intf; struct usb_host_interface *alt; /*之前初始化的new_interfaces在這里終于要派上用場(chǎng)了.初始化各接口,從上面的初始化過程中,我們可以看出: Intf->altsetting,表示接口的各種設(shè)置 Intf->num_altsetting:表示接口的設(shè)置數(shù)目 Intf->intf_assoc:接口的關(guān)聯(lián)接口(定義于minor usb 2.0 spec) Intf->cur_altsetting:接口的當(dāng)前設(shè)置.*/ cp->interface[i] = intf = new_interfaces[i]; intfc = cp->intf_cache[i]; intf->altsetting = intfc->altsetting; intf->num_altsetting = intfc->num_altsetting; //是否關(guān)聯(lián)的接口描述符,定義在minor usb 2.0 spec中 intf->intf_assoc = find_iad(dev, cp, i); kref_get(&intfc->ref); //選擇0號(hào)設(shè)置 alt = usb_altnum_to_altsetting(intf, 0); //如果0號(hào)設(shè)置不存在,選排在第一個(gè)設(shè)置 if (!alt) alt = &intf->altsetting[0]; //當(dāng)前的配置 intf->cur_altsetting = alt; //用來啟用接口,也就是啟用接口中的每一個(gè)endpoint. usb_enable_interface(dev, intf); //注意這個(gè)地方對(duì)intf內(nèi)嵌的struct devcie結(jié)構(gòu)賦值,它的type被賦值為了usb_if_device_type.bus還是usb_bus_type.可能你已經(jīng)反應(yīng)過來了,要和這個(gè)device匹配的設(shè)備是interface的驅(qū)動(dòng). intf->dev.parent = &dev->dev; intf->dev.driver = NULL; intf->dev.bus = &usb_bus_type; intf->dev.type = &usb_if_device_type; intf->dev.dma_mask = dev->dev.dma_mask; device_initialize(&intf->dev);//device 初始化 mark_quiesced(intf); /* device的命名: dev指的是這個(gè)接口所屬的usb_dev,結(jié)合我們之前在UHCI中關(guān)于usb設(shè)備命名方式的描述.可得出它的命令方式如下: USB總線號(hào)-設(shè)備路徑:配置號(hào).接口號(hào). 例如,在我的虛擬機(jī)上:/sys/bus/usb/devices 1-0:1.0 usb1 可以得知,系統(tǒng)只有一個(gè)usb control. 1-0:1.0:表示,第一個(gè)usb control下的root hub的1號(hào)配置的0號(hào)接口. */ sprintf(&intf->dev.bus_id[0], "%d-%s:%d.%d", dev->bus->busnum, dev->devpath, configuration, alt->desc.bInterfaceNumber); } kfree(new_interfaces); if (cp->string == NULL) cp->string = usb_cache_string(dev, cp->desc.iConfiguration); //注冊(cè)每一個(gè)接口? for (i = 0; i < nintf; ++i) { struct usb_interface *intf = cp->interface[i]; dev_dbg(&dev->dev, "adding %s (config #%d, interface %d) ", intf->dev.bus_id, configuration, intf->cur_altsetting->desc.bInterfaceNumber); ret = device_add(&intf->dev);//增加device if (ret != 0) { dev_err(&dev->dev, "device_add(%s) --> %d ", intf->dev.bus_id, ret); continue; } usb_create_sysfs_intf_files(intf); } //使設(shè)備suspend usb_autosuspend_device(dev); return 0;}
最后,注冊(cè)intf內(nèi)嵌的device結(jié)構(gòu).設(shè)備配置完成了,為了省電,可以將設(shè)備置為SUSPEND狀態(tài).
到此為止usb_generic_driver憑借自己的博愛的胸襟將所有設(shè)備的各個(gè)接口添加到了linux的設(shè)備模型中。
usb設(shè)備首先以設(shè)備的身份與usb_generic_driver匹配,成功之后,會(huì)分裂出接口,當(dāng)對(duì)接口調(diào)用device_add()后,會(huì)引起接口和接口驅(qū)動(dòng)的匹配,這個(gè)匹配還是用usb_bus_type.mach()函數(shù)。因?yàn)榻涌诘膁evice->bus=& usb_bus_type, 這跟usb設(shè)備是一樣的,所以,都會(huì)調(diào)用到usb_bus_type.mach(),但設(shè)備和接口的處理流程是不一樣的(前面已經(jīng)分析過)。
usb_hub_init:
int usb_hub_init(void){ if (usb_register(&hub_driver) < 0) { printk(KERN_ERR "%s: can't register hub driver ", usbcore_name); return -1; } khubd_task = kthread_run(hub_thread, NULL, "khubd"); if (!IS_ERR(khubd_task)) return 0; /* Fall through if kernel_thread failed */ usb_deregister(&hub_driver); printk(KERN_ERR "%s: can't start khubd ", usbcore_name); return -1;}
這個(gè)函數(shù)主要有兩個(gè)功能:
在系統(tǒng)初始化的時(shí)候在usb_init函數(shù)中調(diào)用usb_hub_init函數(shù),就進(jìn)入了hub的初始化。
對(duì)于usb_register()可以看作是usb設(shè)備中的接口驅(qū)動(dòng),而usb_register_device_driver()是一個(gè)單純的USB設(shè)備驅(qū)動(dòng)。
在usb_hub_init函數(shù)中完成了注冊(cè)hub驅(qū)動(dòng),并且利用函數(shù)kthread_run創(chuàng)建一個(gè)內(nèi)核線程。該線程用來管理監(jiān)視hub的狀態(tài),所有的情況都通過該線程來報(bào)告。
當(dāng)加載主控器的時(shí)候,在自身的platform驅(qū)動(dòng)的probe函數(shù)里,調(diào)用usb_add_hcd->register_root_hub向usb總線注冊(cè)root hub設(shè)備, usb總線match成功后,由usb_generic_driver驅(qū)動(dòng)的probe函數(shù),配置interface設(shè)備,然后向usb總線注冊(cè)interface, usb總線再一次match, 不過這次是匹配了interface,通過ID值和hub驅(qū)動(dòng)配置,因此調(diào)用hub驅(qū)動(dòng)的probe函數(shù)(hub_probe),hub_probe函數(shù)中調(diào)用hub_configure函數(shù)來配置hub,在這個(gè)函數(shù)中主要是利用函數(shù)usb_alloc_urb函數(shù)來分配一個(gè)urb,利用usb_fill_int_urb來初始化這個(gè)urb結(jié)構(gòu),包括hub的中斷服務(wù)程序hub_irq的,查詢的周期等。
每當(dāng)有設(shè)備連接到USB接口時(shí),USB總線在查詢hub狀態(tài)信息的時(shí)候會(huì)觸發(fā)hub的中斷服務(wù)程序hub_irq,在該函數(shù)中利用kick_khubd將hub結(jié)構(gòu)通過event_list添加到khubd的隊(duì)列hub_event_list,然后喚醒khubd。進(jìn)入hub_events函數(shù),該函數(shù)用來處理khubd事件隊(duì)列,從khubd的hub_event_list中的每個(gè)usb_hub數(shù)據(jù)結(jié)構(gòu)。該函數(shù)中首先判斷hub是否出錯(cuò),然后通過一個(gè)for循環(huán)來檢測(cè)每個(gè)端口的狀態(tài)信息。利用usb_port_status獲取端口信息,如果發(fā)生變化就調(diào)用hub_port_connect_change函數(shù)來配置端口等。
static void hub_events(void){ ... ... while (1) { //如果hub_event_list為空,退出 spin_lock_irq(&hub_event_lock); if (list_empty(&hub_event_list)) { spin_unlock_irq(&hub_event_lock); break; } //取hub_event_list中的后一個(gè)元素,并將其斷鏈 tmp = hub_event_list.next; list_del_init(tmp); //根據(jù)tmp獲取hub hub = list_entry(tmp, struct usb_hub, event_list); //增加hub計(jì)數(shù) kref_get(&hub->kref); //解鎖 spin_unlock_irq(&hub_event_lock); hdev = hub->hdev; hub_dev = hub->intfdev; intf = to_usb_interface(hub_dev); ... ... usb_lock_device(hdev); //如果hub斷開了,繼續(xù)hub_event_list中的下一個(gè) if (unlikely(hub->disconnected)) goto loop; //設(shè)備沒有連接上 if (hdev->state == USB_STATE_NOTATTACHED) { hub->error = -ENODEV; //將下面的子設(shè)備全部disable hub_pre_reset(intf); goto loop; } /* 自動(dòng)恢復(fù) */ ret = usb_autopm_get_interface(intf); if (ret) { dev_dbg(hub_dev, "Can't autoresume: %d ", ret); goto loop; } //hub 暫停 if (hub->quiescing) goto loop_autopm; //hub 有錯(cuò)誤發(fā)生? if (hub->error) { dev_dbg (hub_dev, "resetting for error %d ", hub->error); ret = usb_reset_composite_device(hdev, intf); if (ret) { dev_dbg (hub_dev, "error resetting hub: %d ", ret); goto loop_autopm; } hub->nerrors = 0; hub->error = 0; } /*首先,從hub_event_list摘下第一個(gè)元素,根據(jù)我們之前在接口驅(qū)動(dòng)probe過程的kick_khubd()函數(shù)分析中,有將hub-> event_list添加到hub_event_list.因此,就可以順藤摸瓜找到hub,再根據(jù)hub結(jié)構(gòu),找到接口結(jié)構(gòu)和所屬的usb 設(shè)備結(jié)構(gòu). 然后,進(jìn)行第一個(gè)重要的判斷.如果hub被斷開了,則,斷開hub下面所連接的所有端口,這是在hub_pre_reset()中完成的. 最后,進(jìn)行第二個(gè)重要的判斷,如果hub發(fā)生了錯(cuò)誤,則reset它下面的所有端口,這是在usb_reset_composite_device()中完成的.*/ //在這里,它遍歷hub上的每一個(gè)端口,如果端口的連接會(huì)生了改變(connect_change等于1)的情況,就會(huì)調(diào)用hub_port_connect_change() for (i = 1; i <= hub->descriptor->bNbrPorts; i++) { //檢測(cè)端口是否忙 if (test_bit(i, hub->busy_bits)) continue; //change_bits會(huì)在hub 第一次初始化時(shí)被賦值。而event_bits則在hub_irq中改變 connect_change = test_bit(i, hub->change_bits); //如果都沒有改變,繼續(xù)測(cè)試下一個(gè)端口。 if (!test_and_clear_bit(i, hub->event_bits) && !connect_change && !hub->activating) continue; //Get_Port_Status:取得端口狀態(tài). //會(huì)取得port的改變值和狀態(tài)值 ret = hub_port_status(hub, i, &portstatus, &portchange); if (ret < 0) continue; //在struct usb_dev中,有一個(gè)struct usb_device *children[USB_MAXCHILDREN]的成員,它是表示對(duì)應(yīng)端口序號(hào)上所連接的usb設(shè)備. //如果對(duì)應(yīng)端口沒有在設(shè)備樹上,且端口顯示已經(jīng)連接上 //將connect_change置為1 if (hub->activating && !hdev->children[i-1] && (portstatus & USB_PORT_STAT_CONNECTION)) connect_change = 1; //端口的連接狀態(tài)發(fā)生了改變.需要發(fā)送Clear_Feature if (portchange & USB_PORT_STAT_C_CONNECTION) { clear_port_feature(hdev, i, USB_PORT_FEAT_C_CONNECTION); connect_change = 1; } //端口的狀態(tài)從enable 變?yōu)榱薲isable if (portchange & USB_PORT_STAT_C_ENABLE) { if (!connect_change) dev_dbg (hub_dev, "port %d enable change, " "status %08x ", i, portstatus); clear_port_feature(hdev, i, USB_PORT_FEAT_C_ENABLE); //端口已經(jīng)被停止了,且端口已經(jīng)被連在設(shè)備樹中. //需要重啟一下此端口 if (!(portstatus & USB_PORT_STAT_ENABLE) && !connect_change && hdev->children[i-1]) { dev_err (hub_dev, "port %i " "disabled by hub (EMI?), " "re-enabling... ", i); connect_change = 1; } } //Resume完成 if (portchange & USB_PORT_STAT_C_SUSPEND) { clear_port_feature(hdev, i, USB_PORT_FEAT_C_SUSPEND); //如果端口連接了設(shè)備,就將設(shè)備喚醒 if (hdev->children[i-1]) { ret = remote_wakeup(hdev-> children[i-1]); if (ret < 0) connect_change = 1; } //如果端口沒有連接設(shè)備,就將端口禁用 else { ret = -ENODEV; hub_port_disable(hub, i, 1); } dev_dbg (hub_dev, "resume on port %d, status %d ", i, ret); } //有過流保護(hù),需要對(duì)hub power on if (portchange & USB_PORT_STAT_C_OVERCURRENT) { dev_err (hub_dev, "over-current change on port %d ", i); clear_port_feature(hdev, i, USB_PORT_FEAT_C_OVER_CURRENT); hub_power_on(hub); } //Reset狀態(tài)已經(jīng)完成了 if (portchange & USB_PORT_STAT_C_RESET) { dev_dbg (hub_dev, "reset change on port %d ", i); clear_port_feature(hdev, i, USB_PORT_FEAT_C_RESET); } /*什么情況下, hub_port_connect_change才會(huì)被設(shè)為1. 1:端口在hub->change_bits中被置位.搜索整個(gè)代碼樹,發(fā)生在設(shè)置hub->change_bits的地方,只有在hub_port_logical_disconnect()中手動(dòng)將端口禁用,會(huì)將對(duì)應(yīng)位置1. 2:hub上沒有這個(gè)設(shè)備樹上沒有這個(gè)端口上的設(shè)備.但顯示端口已經(jīng)連上了設(shè)備 3:hub這個(gè)端口上的連接發(fā)生了改變,從端口有設(shè)備連接變?yōu)闊o設(shè)備連接,或者從無設(shè)備連接變?yōu)橛性O(shè)備連接. 4:hub的端口變?yōu)榱薲isable,此時(shí)這個(gè)端口上連接了設(shè)備,但被顯示該端口已經(jīng)變禁用,需要將connect_change設(shè)為1. 5:端口狀態(tài)從SUSPEND變成了RESUME,遠(yuǎn)程喚醒端口上的設(shè)備失敗,就需要將connect_change設(shè)為1. 另外hub_port_connect_change()函數(shù)我們放在后面再來討論*/ if (connect_change) hub_port_connect_change(hub, i, portstatus, portchange); } //對(duì)HUB的處理 //如果hub狀態(tài)末變化,不需要做任何處理 if (test_and_clear_bit(0, hub->event_bits) == 0) ; /* do nothing */ //Get_hub_status 失敗? else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0) dev_err (hub_dev, "get_hub_status failed "); else { //這里是對(duì)應(yīng)hub 狀態(tài)發(fā)生了改變,且Get_hub_status正常返回的情況 //如果hub的本地電源供電發(fā)生了改變 if (hubchange & HUB_CHANGE_LOCAL_POWER) { dev_dbg (hub_dev, "power change "); clear_hub_feature(hdev, C_HUB_LOCAL_POWER); //如果是本地電源供電 if (hubstatus & HUB_STATUS_LOCAL_POWER) /* FIXME: Is this always true? */ hub->limited_power = 1; //如果本電源不供電 else hub->limited_power = 0; } //如果hub 發(fā)生過電源保護(hù),需要對(duì)hub power on if (hubchange & HUB_CHANGE_OVERCURRENT) { dev_dbg (hub_dev, "overcurrent change "); msleep(500); /* Cool down */ clear_hub_feature(hdev, C_HUB_OVER_CURRENT); hub_power_on(hub); } } hub->activating = 0; /* If this is a root hub, tell the HCD it's okay to * re-enable port-change interrupts now. */ if (!hdev->parent && !hub->busy_bits[0]) usb_enable_root_hub_irq(hdev->bus); loop_autopm: /* Allow autosuspend if we're not going to run again */ if (list_empty(&hub->event_list)) usb_autopm_enable(intf);loop: usb_unlock_device(hdev); kref_put(&hub->kref, hub_release); } /* end while (1) */}
hub_port_connect_change()函數(shù)分析:
static void hub_port_connect_change(struct usb_hub *hub, int port1, u16 portstatus, u16 portchange){ ... ... //hub led if (hub->has_indicators) { set_port_led(hub, port1, HUB_LED_AUTO); hub->indicator[port1-1] = INDICATOR_AUTO; } //忽略掉CONFIG_USB_OTG的處理#ifdef CONFIG_USB_OTG /* during HNP, don't repeat the debounce */ if (hdev->bus->is_b_host) portchange &= ~(USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE);#endif //嘗試喚醒一個(gè)存在的設(shè)備 udev = hdev->children[port1-1]; if ((portstatus & USB_PORT_STAT_CONNECTION) && udev && udev->state != USB_STATE_NOTATTACHED) { usb_lock_device(udev); if (portstatus & USB_PORT_STAT_ENABLE) { status = 0; /* Nothing to do */#ifdef CONFIG_USB_SUSPEND } else if (udev->state == USB_STATE_SUSPENDED && udev->persist_enabled) { /* For a suspended device, treat this as a * remote wakeup event. */ status = usb_remote_wakeup(udev);#endif } else { status = -ENODEV; /* Don't resuscitate */ } usb_unlock_device(udev); if (status == 0) { clear_bit(port1, hub->change_bits); return; } } //如果對(duì)應(yīng)端口已經(jīng)有設(shè)備連接,先將其斷開 if (udev) usb_disconnect(&hdev->children[port1-1]); //接下來,將hub->change_bits的對(duì)應(yīng)位清掉,該位是在函數(shù)hub_port_logical_disconnect()中被置的,在這里將其清除,免得下次在進(jìn)入hub_events()的時(shí)候,再次檢測(cè)到這個(gè)位發(fā)生改變. clear_bit(port1, hub->change_bits); //如果發(fā)生物理斷開或者連接狀態(tài)改變,我們可能忘記移除設(shè)備 if (!(portstatus & USB_PORT_STAT_CONNECTION) || (portchange & USB_PORT_STAT_C_CONNECTION)) clear_bit(port1, hub->removed_bits); //連接發(fā)生改變 //連接反彈的處理,實(shí)際上就是除抖動(dòng) if (portchange & (USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE)) { //如果該端口的連接發(fā)生改變(從有連接到無接接,或者從無連接到有連接),就有一個(gè)除抖動(dòng)的過程,usb2.0 spec上規(guī)定,除抖動(dòng)的時(shí)間為100ms. //在函數(shù)里,定義的測(cè)試時(shí)間是1500ms.如果在這個(gè)時(shí)間內(nèi),端口還末處于穩(wěn)定狀態(tài),就會(huì)返回-ETIMEDOUT //如果已經(jīng)處于穩(wěn)定狀態(tài)了,就會(huì)返回穩(wěn)定狀態(tài)下的portstatus status = hub_port_debounce(hub, port1); if (status < 0) { if (printk_ratelimit()) dev_err(hub_dev, "connect-debounce failed, " "port %d disabled ", port1); portstatus &= ~USB_PORT_STAT_CONNECTION; } else { portstatus = status; } } //如果接口上沒有連接了,可以直接退出了 if (!(portstatus & USB_PORT_STAT_CONNECTION) || test_bit(port1, hub->removed_bits)) { /*經(jīng)過去抖后,端口穩(wěn)定的處于斷開連接狀態(tài).說明端口已經(jīng)沒有設(shè)備了.然后,再判斷hub是否有電源開關(guān)((wHubCharacteristics & HUB_CHAR_LPSM) < 2),portstatus 的 USB_PORT_FEAT_POWER位是否被設(shè)置,如果沒有被設(shè)置,則說明該端口斷電了. 如果hub有電源開關(guān),且端口沒有上電,則需要發(fā)送POWER的Set_Feature來為之上電*/ if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2 && !(portstatus & USB_PORT_STAT_POWER)) set_port_feature(hdev, port1, USB_PORT_FEAT_POWER); //如果端口依然處理enable狀態(tài),就會(huì)跳轉(zhuǎn)到標(biāo)號(hào)done處,就端口disalbe. if (portstatus & USB_PORT_STAT_ENABLE) goto done; return; } /*如果端口隱定處于連接狀態(tài),那就需要連接端口下的設(shè)備了.首先看到的是一個(gè)for循環(huán),是用來配置設(shè)備的兩種方式.我們知道,在配置設(shè)備的時(shí)候,首先要去取設(shè)備的描述符,這個(gè)過程是在ep0上完成的.而這個(gè)ep0支持的最大傳輸出數(shù)據(jù)又是在設(shè)備描述符的bMaxPacketSize0中所 定義的.因此就對(duì)應(yīng)有兩種處理方式: 第一種是傳輸8個(gè)字節(jié),取得描述符的前面一部份,從而就可以取得bMaxPacketSize0.此后再reset設(shè)備,再根據(jù)這個(gè)bMaxPacketSize0的長(zhǎng)度去取它的設(shè)備描述符. 第二種是一次傳輸64字節(jié),取得設(shè)備描述符的bMaxPacketSize0字段*/ for (i = 0; i < SET_CONFIG_TRIES; i++) { //為探測(cè)到的usb設(shè)備(包括普通hub,u盤等)分配并初始化udev // 在為root hub分配struct usb_dev的時(shí)候,它的第一個(gè)參數(shù),也就是它的父結(jié)點(diǎn)是為NULL. /*我們來觀察一下它在sysfs中的命名方式 在沒有插入U(xiǎn)盤之前:/sys/bus/usb/devices 1-0:1.0 usb1 插入U(xiǎn)盤之后: 1-0:1.0 1-1 1-1:1.0 usb1 增加的兩個(gè)目是: 1-1和1-1:1.0 1-1:1.0 :只有這樣的目錄,表示該U盤只有一個(gè)接口,當(dāng)前選取的是第0號(hào)設(shè)置項(xiàng).*/ udev = usb_alloc_dev(hdev, hdev->bus, port1); if (!udev) { dev_err (hub_dev, "couldn't allocate port %d usb_device ", port1); goto done; } //置為USB_STATE_POWERED狀態(tài) usb_set_device_state(udev, USB_STATE_POWERED); udev->bus_mA = hub->mA_per_port; udev->level = hdev->level + 1; udev->wusb = hub_is_wusb(hub); /* * USB 3.0 devices are reset automatically before the connect * port status change appears, and the root hub port status * shows the correct speed. We also get port change * notifications for USB 3.0 devices from the USB 3.0 portion of * an external USB 3.0 hub, but this isn't handled correctly yet * FIXME. */ if (!(hcd->driver->flags & HCD_USB3)) udev->speed = USB_SPEED_UNKNOWN; else if ((hdev->parent == NULL) && (portstatus & USB_PORT_STAT_SUPER_SPEED)) udev->speed = USB_SPEED_SUPER; else udev->speed = USB_SPEED_UNKNOWN; /*為設(shè)備指定一個(gè)地址,是到所屬的usb bus的bus->devmap中找到?jīng)]有使用的那一位,先進(jìn)行兩次新的策略(i=0和=1時(shí)),如果不行就再進(jìn)行兩次舊的策略(i=2和i=3時(shí)).所有這一切只有一個(gè)目的,就是為了獲得設(shè)備的描述符, 設(shè)置了udev->tt、udev->ttport和udev->ep 0.desc.wMaxPacketSize,設(shè)置udev->status= USB_STATE_ADDRESS。*/ choose_address(udev); if (udev->devnum <= 0) { status = -ENOTCONN; /* Don't retry */ goto loop; } //hub_port_init()對(duì)這個(gè)usb_dev結(jié)構(gòu)進(jìn)行一系的初始化,在這個(gè)函數(shù)中會(huì)處理:Get_Description,Set_address.等操作 status = hub_port_init(hub, udev, port1, i); if (status < 0) goto loop; usb_detect_quirks(udev); if (udev->quirks & USB_QUIRK_DELAY_INIT) msleep(1000); /* consecutive bus-powered hubs aren't reliable; they can * violate the voltage drop budget. if the new child has * a "powered" LED, users should notice we didn't enable it * (without reading syslog), even without per-port LEDs * on the parent. */ if (udev->descriptor.bDeviceClass == USB_CLASS_HUB && udev->bus_mA <= 100) { u16 devstat; status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstat); if (status < 2) { dev_dbg(&udev->dev, "get status %d ? ", status); goto loop_disable; } le16_to_cpus(&devstat); if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) { dev_err(&udev->dev, "can't connect bus-powered hub " "to this port "); if (hub->has_indicators) { hub->indicator[port1-1] = INDICATOR_AMBER_BLINK; schedule_delayed_work (&hub->leds, 0); } status = -ENOTCONN; /* Don't retry */ goto loop_disable; } } /* check for devices running slower than they could */ if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200 && udev->speed == USB_SPEED_FULL && highspeed_hubs != 0) check_highspeed (hub, udev, port1); /* Store the parent's children[] pointer. At this point * udev becomes globally accessible, although presumably * no one will look at it until hdev is unlocked. */ status = 0; // 將分配的struct usb_dev結(jié)構(gòu)跟他的父結(jié)構(gòu)關(guān)聯(lián)起來,也就是說添加到它的父結(jié)構(gòu)的usb_dev-> children[]數(shù)組. spin_lock_irq(&device_state_lock); if (hdev->state == USB_STATE_NOTATTACHED) status = -ENOTCONN; else hdev->children[port1-1] = udev; spin_unlock_irq(&device_state_lock); if (!status) { /*usb_configure_device(): 得到設(shè)備的描述符(包括設(shè)備描述符、配置描述符、接口描述符等),分析以上描述符信息,提取出配置、接口等,并賦值給udev結(jié)構(gòu)里相應(yīng)的字段?! ?device_add() :將usb設(shè)備注冊(cè)到系統(tǒng)里,這個(gè)動(dòng)作將觸發(fā)驅(qū)動(dòng)的匹配,由于這是個(gè)usb設(shè)備,所以萬能usb驅(qū)動(dòng)usb_generic_driver會(huì)匹配上,從而generic_probe會(huì)得到執(zhí)行,從上面可以看出來,這一次hub_events()調(diào)用是由于主控制器初始化調(diào)用了 hub_probe,從而引發(fā)hub_events調(diào)用。那root hub初始化完成以后hub_events會(huì)如何觸發(fā)呢?答案是通過中斷!而這個(gè)中斷的服務(wù)函數(shù)就是hub_irq,也即是說,凡是真正的有端口變化事件發(fā)生,hub_irq就會(huì)被調(diào)用,而hub_irq()最終會(huì)調(diào)用kick_khubd(), 觸發(fā)hub的event_list,于是再次調(diào)用hub_events().*/ status = usb_new_device(udev); if (status) { spin_lock_irq(&device_state_lock); hdev->children[port1-1] = NULL; spin_unlock_irq(&device_state_lock); } } if (status) goto loop_disable; status = hub_power_remaining(hub); if (status) dev_dbg(hub_dev, "%dmA power budget left ", status); return;loop_disable: hub_port_disable(hub, port1, 1);loop: usb_ep0_reinit(udev); release_address(udev); hub_free_dev(udev); usb_put_dev(udev); if ((status == -ENOTCONN) || (status == -ENOTSUPP)) break; } if (hub->hdev->parent || !hcd->driver->port_handed_over || !(hcd->driver->port_handed_over)(hcd, port1)) dev_err(hub_dev, "unable to enumerate USB device on port %d ", port1);// Done標(biāo)號(hào)是對(duì)應(yīng)上述處理失敗的處理,它禁用掉該端口(因?yàn)樵摱丝跊]有連接設(shè)備或者是端口上的設(shè)備配置失敗),如果是root hub,且USB控制器器驅(qū)動(dòng)中又定義了relinquish_port.調(diào)用它.done: hub_port_disable(hub, port1, 1); if (hcd->driver->relinquish_port && !hub->hdev->parent) hcd->driver->relinquish_port(hcd, port1);}
參考了很多大神的分析,非常感謝!
?
評(píng)論
查看更多