http://www.cnblogs.com/cslunatic/p/3726053.html
Linux usb子系統(一):子系統架構

一、USB協議基礎知識  

前序:USB概念概述

  USB1.0版本速度1.5Mbps(低速USB USB1.1版本速度12Mbps(全速USB  USB2.0版本速度480Mbps(高速USB)。

  USB 分為主從兩大體系,一般而言, PC 中的 USB 系統就是作主,而一般的 USB 滑鼠, U 盤則是典型的 USB 從系統。

  USB主控制器這一塊,我們至少要開發出 USB 的主控制器與從控制器,滑鼠是低速設備,所需的是最簡單的一類從控制器。主控制器則複雜得多,因為太過於複雜了,所以就形成了一些標準。在一個複雜的系統中,標準的好處就是可以讓開發者把精力集中在自己負責的一塊中來,只需要向外界提供最標準的介面,而免於陷於技術的汪洋大海中。

  USB 主控制器主要有 1.1 時代的 OHCI UHCI 2.0 時代的 EHCI ,這些標準規定了主控制器的功能和介面(寄存器的序列及功能),對我們驅動工程師而言,這樣的好處就是只要你的驅動符合標某一標準,你就能輕而易舉的驅動所有這個標準的主控制器。要想把主控制器驅動起來,本來是一件很難的事情,估計全球的 IT 工程師沒幾個能有這樣的水準,但有了標準,我們就可以輕鬆的佔有這幾個高水準的 IT 工程師的勞動成果。

  主控制器和驅動有了,我們還需要 USB 協定棧,這就是整個 USB 系統的軟體部分的kernel(有的資料中直接把其稱為 USB kernel), USB 協定棧一方面向使用 USB 匯流排的設備驅動提供操作 USB 匯流排的 API ,另一方面則管理上層驅動傳下來的的資料流程,按 USB 主控制器的要求放在控制器驅動規定的位置, USB 主控制器會調度這些資料。

  我們這裡用到了調度這個詞, USB 主控制器的調度其實和火車的調度 CPU 的調度有相似之處,物理上的通路只有一條,但 USB 中規定的邏輯上的通路卻有許多條,有時一個設備就會佔用幾條邏輯通道,而 USB 系統中又會有多個設備同時運行。這就好像是只有一條鐵路線,但來來往往的火車卻有許多, USB 主控制器的作用就是調度這些火車,而 USB 協議棧的作用則向上層的 USB 設備驅動提供不同的車次。

  有了以上的這些模組,才能為 USB 滑鼠設計驅動,這一點上 ps/2 滑鼠的驅動和 USB 滑鼠的驅動結構基本一樣,只不過我們的資料通路是 USB 匯流排。

  USB 系統甚至把設備驅動都給標準化了,只要是支援 USB 的主機,就可以支援任何一個廠商的 USB 滑鼠,任何一個廠商的 U 盤,只要是被 USB 系統包函的設備,只要這些設備支援相應的標準,就無需重新設計驅動而直接使用。

  下是簡單的列出了 USB 設備類型,理想的情況 USB 系統要對這些設備作完整的支援,設備也必須符合 USB 規範中的要求。

1 audio :表示一個音頻設備。

 

2 communication   device :通訊設備,如電話, moden 等等。

 

3 HID :人機交互設備,如鍵盤,滑鼠等。

 

6 image 圖像設備,如掃描器,攝影頭等,有時數碼相    機也可歸到這一類。

 

7 -印表機類。如單向,雙向印表機等。

 

8 mass   storage 海量存儲類。所有帶有一定存儲功能的都可以歸到這一類。如數碼相機大多數都歸這一類。

 

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. 廠家的自定義類,主要用於一些特殊的設備。如介面轉接卡等。

 

  隨著 USB 技術的發展, USB 系統中的一些不足也逐漸被承認, OTG 就是這種情況下的主要產物。

  現在市面上有些設備(比如一些 MP4 )即能插上電腦當 U盤使,也能被 U盤插上讀取 U盤。這樣的設備在 USB 系統中是作主還是作從呢?

  這就是 OTG(On-The-Go), 即可以作主也可以作從,傳說中的雌雄同體。這主要是為嵌入式設備準備的,因為 USB 是一種主從系統,不能支援點對點平等的傳輸資料, OTG 正是在這種需求下產生的, OTG 不僅支持控制器的主從切換,在一定層度上,也支援相同設備之間的資料交換。 

1USB的傳輸線結構

        一條USB的傳輸線分別由地線、電源線、D+D-四條線構成,D+D-是差分輸入線(抗干擾),它使用的是3.3V的電壓,而電源線和地線可向設備提供5V電壓,最大電流為500MAOTG 的做法就是增來一個 ID pin 來判斷設備是接入設備的是主還是從。vbus 主要是供電, D+/D- 則是用來傳輸資料,就是我們前面所講的主設備和從設備間唯一的一條鐵路。

 

信號線名稱

顏色

1

Vbus

2

D-

3

D+

4

GNU

shell (金屬殼)

屏敝層

 

2USB可以熱插拔的硬體原理

  USB主機是如何檢測到設備的插入的呢?首先,在USB集線器的每個下游埠的D+D-上,分別接了一個15K歐姆的下拉電阻到地。這樣,在集線器的埠懸空時,就被這兩個下拉電阻拉到了低電平。而在USB設備端,在D+或者D-上接了1.5K歐姆上拉電阻。對於全速和高速設備,上拉電阻是接在D+上;而低速設備則是上拉電阻接在D-上。這樣,當設備插入到集線器時,由1.5K的上拉電阻和15K的下拉電阻分壓,結果就將差分數據線中的一條拉高了。集線器檢測到這個狀態後,它就報告給USB主控制器(或者通過它上一層的集線器報告給USB主控制器),這樣就檢測到設備的插入了。USB高速設備先是被識別為全速設備,然後通過HOSTDEVICE兩者之間的確認,再切換到高速模式的。在高速模式下,是電流傳輸模式,這時將D+上的上拉電阻斷開。

3USB主機控制器

  USB主機控制器屬於南橋晶片的一部分,通過PCI匯流排和處理器通信。USB主機控制器分為UHCI(英代爾提出)、OHCI(康柏和微軟提出)、 EHCI。其中OHCI驅動程式用來為非PC系統上以及帶有SiSALi晶片組的PC主辦上的USB晶片提供支援。UHCI驅動程式多用來為大多數其他PC主板(包括IntelVia)上的USB晶片提供支援。ENCI相容OHCIUHCIUHCI的硬體線路比OHCI簡單,所以成本較低,但需要較複雜的驅動程式,CPU負荷稍重。主機控制器驅動程式完成的功能主要包括:解析和維護URB,根據不同的端點進行分類緩存URB;負責不同USB傳輸類型的調度工作;負責USB資料的實際傳輸工作;實現虛擬跟HUB的功能。

4USB設備的構成

  USB設備的構成包括了配置,介面和端點。

 

  1. 設備通常具有一個或者更多個配置

  2. 配置經常具有一個或者更多個介面

  3. 介面通常具有一個或者更多個設置

  4. 介面沒有或者具有一個以上的端點

  需要注意的是,驅動是綁定到USB介面上,而不是整個設備。

5、主控制怎麼正確訪問各種不同的USB設備

  每一個USB設備接入PC時,USB匯流排驅動程式都會使用默認的位址0(僅未分配位址的設備可以使用)USB設備通信,然後給它分配一個編號,接在USB匯流排上的每一個USB設備都有自己的編號(位址)PC機想訪問某個USB設備時,發出的命令都含有對應的編號(位址)就可以了。

  USB匯流排驅動程式獲取USB設置資訊。USB設備裡都會有一個叫 EEPROM的東東,它就是用來存儲設備本身資訊的。它與Flash雖說都是要電擦除的,但它可以按位元組擦除,Flash只能一次擦除一個 block

6usb-firmware簡易框架

 

  usb firmware主要工作是滿足usb 協議所定義的標準請求(usb協定第9章第4節),不同的firmware因為硬體不同而操作有所不同,但目的都是完成主控制器對設備的標準請求,大致框圖如下:

 

 

 

7USB傳輸事務

  USB通信最基本的形式是通過一個名為端點(endpoint)的東西。它是真實存在的。

  端點只能往一個方向傳送資料(端點0除外,端點0使用message管道,它既可以IN又可以OUT),或者IN,或者OUT。除了端點0,低速設備只能有2個端點,高速設備也只能有15IN端點和15OUT端點。

  主機和端點之間的資料傳輸是通過管道。

  端點只有在device上才有,協定說端點代表在主機和設備端點之間移動資料的能力。

  USB通信都是由host端發起的。

 

  首先明確一點USB協議規定所有的資料傳輸都必須由主機發起。所以這個傳輸的一般格式:權杖包(表明傳輸的類型)資料包(實際傳輸的資料)握手包(資料的正確性)首先是由主機控制器發出權杖包然後主機/設備發送資料包,甚至可以沒有,最後設備/主機發送握手包,這麼一個過程就叫做一個USB傳輸事務。一個USB傳輸事務就實現了一次從主機和設備間的通訊。USB的事務有:OUTINSETUP事務。

  權杖包:可分為OUT包、IN包、SetUp包和幀起始包OUT包就是說明接下來的資料包的方向時從主機到設備。 

  數據包:裡面包含的就是我們實際要傳輸的東東了  

  握手包:發送方發送了資料,接受方收沒收到是不是該吱個聲呀。

  一個資料包裡面包含有很多的域,裡面包含了很多資訊,一般有同步的域,資料包的kernel資訊的域,資料校驗的域。

  權杖包:SYNC + PID + ADDR + ENDP + CRC5 :(同步) + (IN/OUT/SetUp) + (設備位址)+(設備端點) + (校驗)

  數據包:分為DATA0包和DATA1包,當USB發送資料的時候,當一次發送的資料長度大於相應端點的容量時,就需要把資料包分為好幾個包,分批發送,DATA0包和DATA1包交替發送,即如果第一個資料包是 DATA0,那第二個資料包就是DATA1

      SYNC + PID + DATA0/1 + CRC5:(同步) + (DATA0/1) + (資料) + (校驗)。

      但也有例外情況,在同步傳輸中(四類傳輸類型中之一),所有的資料包都是為DATA0,格式如下: SYNC + PID + 0~1023位元組 + CRC16:(同步) + (DATA0) + (資料) + (校驗)。

      握手包:SYNC+PID:(同步)+HandShake

8USB協議的四種傳輸類型

  因為usb支援的設備實在是太多,而且不同的設備對於傳輸資料各有各的要求和這就導致了我們需要不同的傳輸方式。USB支援4種傳輸方式:控制傳輸;批量傳輸;中斷傳輸;實()時傳輸

  控制傳輸:首先發送 Setup 傳輸事務,然後IN/OUT傳輸事務,最後是 STATUS transaction,向主機彙報前面SETUP  IN/OUT階段的結果。控制傳輸主要用於向設備發送配置資訊、獲取設備資訊、發送命令道設備,或者獲取設備的狀態報告。控制傳輸一般發送的資料量較小,當USB設備插入時,USBkernel使用端點0對設備進行配置,另外,埠0與其他端點不一樣,端點0可以雙向傳輸。

  批量傳輸:由OUT事務和IN事務構成,用於大容量資料傳輸,沒有固定的傳輸速率,也不佔用帶寬,當匯流排忙時,USB會優先進行其他類型的資料傳輸,而暫時停止批量轉輸。批量傳輸通常用在資料量大、對資料即時性要求不高的場合,例如USB印表機、掃描器、大容量存儲設備、U盤等。

  中斷傳輸:由OUT事務和IN事務構成,中斷傳輸就是中斷端點以一個固定的速度來傳輸較少的資料,USB鍵盤和滑鼠就是使用這個傳輸方式。這裡說的中斷和硬體上下文中的中斷不一樣,它不是設備主動發送一個中斷請求,而是主機控制器在保證不大於某個時間間隔內安排一次傳輸。中斷傳輸對時間要求比較嚴格,所以可以用中斷傳輸來不斷地檢測某個設備,當條件滿足後再使用批量傳輸傳輸大量的資料。

  等時傳輸:由OUT事務和IN事務構成,有兩個特殊地方,第一,在同步傳輸的INOUT事務中是沒有握手階段;第二,在資料包階段所有的資料包都為DATA0 等時傳輸同樣可以傳輸大批量資料,但是對資料是否到達沒有保證,它對即時性的要求很高,例如音頻、視頻等設備(USB攝影頭,USB話筒)。

  這4種傳輸方式由4個事務組成:

  IN事務:IN事務為host輸入服務,當host需要從設備獲得資料的時候,就需要IN事務。

  OUT事務:OUT事務為host輸出服務,當host需要輸出資料到設備的時候,就需要OUT事務。

  SETUP事務:SETUP事務為host控制服務,當host希望傳輸一些USB規範的默認操作的時候就需要使用setup事務。

  SOF事務:這個用於幀同步。

  然後這4種事務又由3類包(token包,handshake包,data)組成,每類又分幾種:

 

  in包:in包用於指明當前的事務為in類型的。

  out: out包用於指明當前事務為out類型的。

  setup: setup包指明當前事務為setup類型的。

  sofsof包指明當前事務為setup類型的。

 

  ack包:ack握手包指明當前的事務的資料包傳輸是成功的。

  nak包:nak握手包指明當前設備忙,不能處理資料包,請主機稍後再次發送。

  stall包:stall握手包指明當前設備不能接受或者傳輸資料,表示一個嚴重的錯誤。

 

  data0包:該資料包的類型為0

  data1包:該資料包的類型為1

  下圖是一個USB滑鼠插入Linux系統時完整的列舉過程,一共發生了11次傳輸,每次傳輸包括幾個事務,每個事務又包括幾個包,每個包包括幾個域。

 

  這裡有一個概念需要注意,這裡的中斷傳輸與硬體中斷那個中斷是不一樣的,這個中斷傳輸實際是靠USB host control輪詢usb device來實現的,而USB host control對於CPU則是基於中斷的機制。

  拿USB滑鼠為例,USB host controlUSB滑鼠不斷請求,這個請求的間隔是很短的,在USB spec Table 9-13端點描述符中的bInterval域中指定的,當滑鼠發生過了事件之後,滑鼠會發送資料回host,這時USB host control中斷通知CPU,於是usb_mouse_irq被呼叫,在usb_mouse_irq裡,就可以讀取滑鼠發回來的資料,當讀完之後,驅動再次呼叫usb_submit_urb發出請求,就這麼一直重複下去,一個usb滑鼠的驅動也就完成了。

  下面是USB滑鼠中斷傳輸圖,可以看到USB host controlusb device發送了IN包,沒有資料的時候device回復的是NAK,有資料的時候才向host control發送DATA包。

 

9USB設備被識別的過程

  當USB設備插上主機時,主機就通過一系列的動作來對設備進行列舉配置。

  1接入態Attached):設備接入主機後,主機通過檢測信號線上的電平變化來發現設備的接入;

  2供電態Powered):就是給設備供電,分為設備接入時的默認供電值,配置階段後的供電值(按資料中要求的最大值,可通過編程設置)

  3缺省態Default):USB在被配置之前,通過缺省位址0與主機進行通信;

  4地址態Address):經過了配置,USB設備被重定後,就可以按主機分配給它的唯一位址來與主機通信,這種狀態就是位址態;

  5配置態Configured):通過各種標準的USB請求命令來獲取設備的各種資訊,並對設備的某此資訊進行改變或設置。

  6掛起態Suspended):匯流排供電設備在3ms內沒有匯流排動作,即USB匯流排處於空閒狀態的話,該設備就要自動進入掛起狀態,在進入掛起狀態後,總的電流功耗不超過280UA

10、標準的USB設備請求命令

  USB設備請求命令是在控制傳輸的第一個階段:setup事務傳輸的資料傳輸階段發送給設備的。

  標準USB設備請求命令共有11個,大小都是8個位元組,具有相同的結構,由5 個欄位構成。通過標準USB准設備請求,我們可以獲取存儲在設備EEPROM裡面的資訊;知道設備有哪些的設置或功能獲得設備的運行狀態改變設備的配置等。

  標準USB准設備請求 = bmRequestType(1) + bRequest(2) wvalue(2) wIndex(2) wLength(2)

  bmRequestType

    [7 bit]= 0主機到設備; 1設備到主機 

    [6-5 bit]= 00標準請求命令; 01類請求命令; 10用戶定義命令; 11保留

    [4-0 bit]= 00000 接收者為設備; 00001 接收者為介面; 00010 接收者為端點; 00011 接收者為其他接收者; 其他 其他值保留

  bRequest:

    0 0 GET_STATUS:用來返回特定接收者的狀態

    1 1 CLEAR_FEATURE:用來清除或禁止接收者的某些特性

    2 3 SET_FEATURE:用來啟用或啟動命令接收者的某些特性

    3 5 SET_ADDRESS:用來給設備分配位址

    4 6 GET_DEscriptOR:用於主機獲取設備的特定描述符

    5 7 SET_DEscriptOR:修改設備中有關的描述符,或者增加新的描述符

    6 8 GET_CONFIGURATION:用於主機獲取設備當前設備的配置值、

    7 9 SET_CONFIGURATION:用於主機指示設備採用的要求的配置

    8 10 GET_INTERFACE:用於獲取當前某個介面描述符編號

    9 11 SET_INTERFACE:用於主機要求設備用某個描述符來描述介面

    10 12 SYNCH_FRAME:用於設備設置和報告一個端點的同步

  wvalue這個欄位是 request 的參數,request 不同,wValue就不同。

  wIndexwIndex,也是request 的參數,bRequestType指明 request 針對的是設備上的某個介面或端點的時候,wIndex 就用來指明是哪個介面或端點。

  wLength控制傳輸中 DATA transaction 階段的長度。

Linux USB系統架構

這個是USB系統的拓撲圖,4個部分構成:USB主機控制器,根集線器,集線器,設備。其中Root HubUSB主機控制器是綁定在一起的。

 

  在機箱的尾部面板上,物理上存在一,二或四個USB埠。埠可以用來連接一個普通設備或者一個hubhub是一個USB設備,可以用來擴展連接USB設備的埠數量。最大連接USB設備數量是減去連在匯流排上的hub數量(如果有50hub,那麼最多77(=127-50)個設備能夠連接),剩下的就是能夠連接USB設備的數量。Hub總是高速的,如果一個hub是自供電的,那麼任何設備都能夠附著到上面。但是如果hub是匯流排供電的,那麼僅僅低供電(最大100mA)設備能夠附著到上面,一個匯流排供電的hub不應該連接到另一個匯流排供電的hub-你應該在匯流排供電和自供電間交替.

  通常情況下主機控制器的物理埠由一個虛擬的root hub臉管理。這個hub是有主機控制器(host controller)的設備驅動虛擬的,用來統一管理匯流排拓撲,因此USB子系統的驅動能夠用同樣的方法管理每個埠。

  USB通信都是由host端發起的。USB設備驅動程式分配並初始化一個URB發給USB Core,USB Core改一改,發給USB主機控制器驅動,USB主機控制器驅動把它解析成包,在匯流排上進行傳送。

  USB Core是由kernel實現的,其實也就是把host control driver裡的功能更集中的向上抽象了一層,它是用來對最上層的USB設備驅動遮罩掉host control的不同。

  USB通信最基本的形式是通過一個名為端點(endpoint)的東西。它是真實存在的。端點只能往一個方向傳送資料(端點0除外,端點0使用message管道,它既可以IN又可以OUT),或者IN,或者OUT(前面已經介紹過)。除了端點0,低速設備只能有2個端點,高速設備也只能有15IN端點和15OUT端點。主機和端點之間的資料傳輸是通過管道。端點只有在device上才有,協定說端點代表在主機和設備端點之間移動資料的能力。

   Linux系統下的usb部分分為四個部門或者叫做四大家族,他們是host控制器驅動、hub驅動、usb core、設備類驅動,他們共同配合著完成了對usb設備的訪問操作。

列舉和設備描述符

  每當一個USB設備附著到匯流排上,它將會被USB子系統列舉.也就是分配唯一的設備號(1-127)然後讀取設備描述符.描述符是一個包含關於設備的資訊和屬性的資料結構.USB標準定義了一個描述符層次結構,下圖所示:

 

 標準描述符

  設備描述符:描述USB設備的大概資訊,其中包括適用於設備的全局資訊,所有設備的配置。一個USB設備只有一個設備描述符。

  配置描述符:描述了特定的設備配置資訊。一個USB設備可以有一或多個配置描述符。每個配置有一個或多個介面(interface),並且每個介面有零或多個端點(endpoint)。一個端點在一個單獨的配置下,是不和其他的介面共用的,但是一個單獨的介面對於同一個端點能夠有幾種可選的配置。端點可以沒有限制的在一部分不同的配置下的介面間共用。配置僅僅能夠通過標準的控制傳輸set_configuration來啟動。不同的配置能夠用來全局配置資訊,例如供電消耗。

  介面描述符:描述了一個配置內的特定介面。一個配置提供一個或多個介面,每個介面帶有零個或多個端點描述符描述了在配置內的唯一配置。一個可以包含可選的配置的介面使得配置好的端點和/或他們的特性能夠多種多樣。默認的介面設置總是設置為零。可替換的設置能夠在標準控制傳輸的set_interface來選擇一個。例如一個多功能設備帶有話筒的攝影頭,可以有三種可用的配置來改變分配在匯流排上的帶寬。

Camera activated

Microphone activated

Camera and microphone activated

  端點描述符:包含主機用來決定每個端點帶寬的資訊。一個端點象徵一個USB設備的邏輯資料源或接收端(logic data source or sink)。端點零是用來所有的控制傳輸並且該端點沒有設備描述符。USB spec交替使用pipeendpoint術語。

  字串描述符:是可選項,提供了unicode編碼的額外的可讀資訊。他們可以是廠商和設備名稱或序列號。

設備類型

  標準的設備和介面描述符包含有關分類的內容:class, sub-classprotocol。這些欄位主機可以用來設備或介面和驅動聯繫。依賴於分類說明是如何指定的?對於class欄位和介面描述符的合法欄位是由USB Device Working Group來定義的。

  在Class Specification中將設備或介面分組歸類並指定特性,這樣就使得主機開發軟體能夠基於這個類別進行管理多種多樣的實現。這樣的主機軟體通過設備中的描述資訊將操作方法綁定到指定的設備。一個類別規格作為所有的該類別的設備或介面的最小操作框架服務。(PS:也就是說,所有該類別的設備或介面,都是以類別規格定義為介面框架。)

人機周邊設備

  HID分類,主要是包含人們控制電腦系統的設備。典型的HID分類設備包含

  鍵盤和滑鼠設備例如:標準的滑鼠設備,追蹤球,遊戲手柄。

  前端面板控制   例如:旋鈕,開關,按鍵,滾動器。

  可能在電話設備,遠端控制VCR,遊戲或類比設備上存在控制器。

再瞭解一下USB驅動框架:

  

  USB匯流排和USB設備使用軟體進行抽象描述起來是非常複雜的,一方面是協定使然,一方面也是因為它們使用太廣泛了,抽象時考慮很太多情況。幸運的是,kernel開發者們抽象出來的kernelUSB 子系統把很多複雜性都隱藏了。

針對上面這幅圖,為了理解什麼是USB子系統,我們要做以下說明:
  a) USB 驅動都是跨kernel子系統的,因為最終USB設備是要通過BLCOCK CHAR設備的方式呈現給我們的,所以USB Driver之上還有一層。
  b) USB driver利用USB Core提供的API來簡單優雅的完成驅動工作,這裡USB Core抽象了複雜的USB協議。
  c) 主機控制器驅動位於USB軟體的最下層,提供主機控制器硬體的抽象,隱藏硬體的細節,在主機控制器之下是物理的USB及所有與之連接的USB設備。主機控制器驅動只和USB Core進行關聯,USB Core將用戶的請求映射到相關的主機控制器驅動,從而使用戶無需去訪問主機控制器。
  d) USB CoreUSB主機控制器驅動就構成了我們的USB子系統,USB Core負責實現一些kernel的功能,例如協議之類,提供一個用於訪問和控制USB硬體的介面,使設備驅動不用去考慮系統當前使用哪種主機控制器。自從有了USB子系統,寫USB驅動的時候,只需要呼叫USB Core export的介面,就幾乎能完成所有工作。
  e) USB匯流排將USB設備和USB驅動關聯起來。

USB子系統初始化

  usb初始化函數定義在kernel原始碼(2.6.37drivers/usb/core/usb.c:

 

/*

* Init

*/

staticint __init usb_init(void)

{

   int retval;

   if (nousb) {

       pr_info("%s: USB support disabled\n", usbcore_name);

       return0;

   }

 

   retval = usb_debugfs_init();

   if (retval)

       gotoout;

 

   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)

       gotoout;

 

   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,顧名思義,是一種用於kernel除錯的虛擬檔系統,kernel開發者通過debugfs和用戶空間交換資料。類似的虛擬檔系統還有procfssysfs等,這幾種虛擬檔系統都並不實際存儲在硬碟上,而是Linuxkernel運行起來後,執行mount -t debugfs none /media/mmcblk0p2/ 才建立起來。在/media/mmcblk0p2/目錄下建立usb目錄並在下面建立devices檔。

  當我們執行cat devices會呼叫usbfs_devices_fops->read(usb_device_read)函數去搜尋usb_bus_list鏈表下的usb設備資訊,也就是所有匯流排下的設備。

bus_register

  是將usb匯流排註冊到系統中,匯流排可是linux設備模型中的領導者,不管是多大的領導,也是領導,如PCIUSBI2C,即使他們在物理上有從屬關係,但是在模型的世界裡,都是匯流排,擁有一樣的待遇,所以任何一個子系統只要管理自己的設備和驅動,就需要向kernel註冊一個匯流排,註冊報到。

bus_register_notifier

  大多數kernel子系統都是相互獨立的,因此某個子系統可能對其他子系統產生的事件感興趣。為了滿足這個需求,也即是讓某個子系統在發生某個事件時通知其他的子系統,Linuxkernel提供了通知鏈的機制。通知鏈表只能夠在kernel的子系統之間使用,而不能夠在kernel與用戶空間之間進行事件的通知。

  通知鏈表是一個函數鏈表,鏈表上的每一個節點都註冊了一個函數。當某個事情發生時,鏈表上所有節點對應的函數就會被執行。所以對於通知鏈表來說有一個通知方與一個接收方。在通知這個事件時所運行的函數由被通知方決定,實際上也即是被通知方註冊了某個函數,在發生某個事件時這些函數就得到執行。其實和系統呼叫signal的思想差不多。

  bus_register->BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier),已經初始化了usb_bus_type->p->bus_notifier通過blocking_notifier_chain_register函數註冊到通知鏈表。

  那什麼時候usb匯流排收到通知呢?

  當匯流排發現新的設備呼叫device_add->blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev)

  當匯流排卸載設備時呼叫device_del->blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_DEL_DEVICE, dev);

  則呼叫usb_bus_nb的回呼(callback)成員函數notifier_call(usb_bus_notify),函數定義如下:

 

/*

* Notifications of device and interface registration

*/

staticint 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 設備  

           (void) usb_create_sysfs_dev_files(to_usb_device(dev)); //建立descriptors文件

       elseif (dev->type == &usb_if_device_type) //usb介面

           (void) usb_create_sysfs_intf_files(

                   to_usb_interface(dev));//建立interface文件

       break;

 

   case BUS_NOTIFY_DEL_DEVICE:

       if (dev->type == &usb_device_type)//usb設備

           usb_remove_sysfs_dev_files(to_usb_device(dev));//刪除descriptors

       elseif (dev->type == &usb_if_device_type)//usb介面

           usb_remove_sysfs_intf_files(to_usb_interface(dev));//刪除interface

       break;

   }

   return0;

}

 

 usb_major_init註冊字元設備,主設備號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,

           constchar *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)

       gotoout;

 

   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\n",

           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\n",

           usbcore_name, retval, new_driver->name);

   gotoout;

}

EXPORT_SYMBOL_GPL(usb_register_driver);

 

其餘功能如下:

  1> driver_register實現。後面會詳細分析。

  2> usbfs_update_special(): usb檔系統相關,看下面的usbfs_init分析。

  3> usb_create_newid_file(): 建立newid屬性檔,在/sys/bus/usb/drivers/usbfs/下面可以看到此檔。根據傳入的ID值,增加一個新的動態usb設備到驅動(這裡是usbfs),引起驅動重新探測所有的設備。

  4> usb_create_removeid_file():建立removeid屬性檔,在/sys/bus/usb/drivers/usbfs/下面可以看到此檔。根據傳入的ID值,刪除驅動(這裡是usbfs)裡的一個usb設備。

  5> 輸出資訊:usbcore: registered new interface driver usbfs

現在分析driver_register功能:

  1> 首先判斷,些驅動所屬bussubsys_private結構有沒有初始化。如果沒有,報bug資訊。

  2> 判斷需要註冊的driverdriver所屬的bus是否都有probe, remove, shutdown函數。如有,列印kernel warning資訊。

  3> 判斷此driver已經在driver所屬的bus上面註冊過了。如果註冊過了,列印錯誤資訊,並返回。

  4> 呼叫bus_add_driver來註冊driver

  5> 呼叫driver_add_groups來添加組屬性。

最後對bus_add_driver進行分析。

 

/**

* 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\n", 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\n",

         __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\n",

           __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\n",

               __func__, drv->name);

       }

   }

 

   kobject_uevent(&priv->kobj, KOBJ_ADD);

   return0;

 

out_unregister:

   kobject_put(&priv->kobj);

   kfree(drv->p);

   drv->p = NULL;

out_put_bus:

   bus_put(bus);

   return error;

}

 

其功能是向bus中添加一個driver

  1> bus_get()bus的計數加1;

  2> kzalloc,分配driver_private記憶體空間。

  3> 初始化此driverklist_devices鏈表。

  4> kobject_init_and_add():在/sys/bus/usb/drivers/下面建立usbfs文件夾。

  5> 如果匯流排支援drivers_autoprobe,呼叫driver_attach。(USB 匯流排支援)

  6> driver_create_file/sys/bus/usb/drivers/usbfs下面建立uevent屬性檔。

  7> driver_add_attrs():將匯流排的屬性也加到/sys/bus/usb/drivers/usbfs

  8> add_bind_files():在/sys/bus/usb/drivers/usbfs建立bindunbind屬性檔。

  9> kobject_uevent():發送一個KOBJ_ADD的事件。

/sys/bus/usb/drivers/usbfs下面的文件:

bind       module     new_id     remove_id    uevent     unbind

usb_devio_init註冊字元設備,主設備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);

 

   return0;

}

 

函數功能:

  1> register_filesystem註冊usbfs檔系統,當應用程式執行mount命令的時候,掛載檔系統到相應的目錄。

  2> usb_register_notify函數註冊到kernel通知鏈表,當收到其他子系統通知,呼叫notifier_call回呼(callback)函數usbfs_notify

 

staticint usbfs_notify(struct notifier_block *self, unsigned long action, void *dev)

{

   switch (action) {

   case USB_DEVICE_ADD:

       usbfs_add_device(dev);//bus號建立的目錄下,根據設備號建立設備檔

       break;

   case USB_DEVICE_REMOVE:

       usbfs_remove_device(dev);//刪除bus號建立的目錄下的設備檔

       break;

   case USB_BUS_ADD:

       usbfs_add_bus(dev);//根據bus號建立目錄

       break;

   case USB_BUS_REMOVE:

       usbfs_remove_bus(dev);//刪除bus號建立的目錄

   }

 

   usbfs_update_special();//更新檔系統節點

   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通知鏈表註冊

  blocking_notifier_call_chain(&usb_notifier_list, USB_DEVICE_ADD, udev):通知有usb設備增加
  blocking_notifier_call_chain(&usb_notifier_list,USB_DEVICE_REMOVE, udev):通知有usb設備移除
  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/目錄下建立usb目錄。

usb_register_device_driver

在瞭解usb_generic_driver驅動前,先分析usb匯流排的match函數:

 

staticint 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))

           return0;

 

       /* TODO: Add real matching code */

       return1;

 

   } elseif (is_usb_interface(dev)) {

       struct usb_interface *intf;

       struct usb_driver *usb_drv;

       conststruct usb_device_id *id;

 

       /* device drivers never match interfaces */

       if (is_usb_device_driver(drv))

           return0;

 

       intf = to_usb_interface(dev);

       usb_drv = to_usb_driver(drv);

 

       id = usb_match_id(intf, usb_drv->id_table);

       if (id)

           return1;

 

       id = usb_match_dynamic_id(intf, usb_drv);

       if (id)

           return1;

   }

 

   return0;

}

 

  函數中我們分成兩類判斷:

  is_usb_device(),根據設備類型dev->type == &usb_device_type 來判斷是否是usb設備,然後在通過for_devicesusb_register_device_driver函數註冊的時候設置為1 判斷驅動是否是usb設備設備驅動,如果成功,則設備和設備驅動匹配,呼叫相應的驅動的probe函數(因為usb匯流排沒有probe成員函數)。

  is_usb_interface(),根據設備類型dev->type == &usb_if_device_type 來判斷是否是介面,然後在通過for_devicesusb_register函數註冊的時候設置為0 判斷驅動是否是介面驅動,如果是介面驅動(所以呼叫usb_register都是註冊的介面驅動,因為一個設備可以有多個介面,每個介面必須獨立驅動),接著usb_match_id這個函數就是用來判斷這個介面是否在id table中得到了match,一旦得到,就進入了具體介面驅動的probe函數了。。

  到這裡我們不禁要思索驅動找到了註冊的地方,那設備來自哪里?這裡也有兩個函數要分析:

  usb_alloc_dev()dev->dev.type = &usb_device_type,這裡就表示了是usb設備,這個函數主要有兩個地方呼叫。一個就是usb_init->usb_hub_init->hub_thread->hub_events->hub_port_connect_change,這個會在下面進行詳細的分析;另外一個musb_probe->musb_init_controller->usb_add_hcdDM8168晶片註冊主控器的時候用到(或者其他晶片主控器註冊)。

  usb_set_configuration(): intf->dev.type = &usb_if_device_type,這裡就表示了是介面。

  這裡我們知道usb_register  usb_register_device_driver,一個是設備驅動的註冊,一個是介面驅動的註冊,match的時候通過for_devices來區分。介面指的就是一種具體的功能。

      上面我們提過每種類型的匯流排都有一套自己的驅動函數,看來在usb的世界裡更特殊一些,usb匯流排下的設備驅動有一套,介面驅動也有一套:usb_probe_interface

      不管是設備還是介面都是掛在匯流排上的,一個匯流排只有一個match函數,usb_device_match

  在這個usbmatch函數裡,首先是對usb設備的match,設備的match很簡單的,只要是個usb設備就認為match了,因為現在進來的usb設備統統都認為是usb_generic_driver的,都和他match。上面我們提到過這個,所有的usb設備首先都會經過篩選這一關,處理之後,才有重生的機會。介面就不一樣了,如果進來的dev不是設備,就認為是個介面,然後判斷drv是否為介面驅動,如果是,那麼就繼續判斷,這個判斷機制就是usb特有的了:Id。每個介面驅動註冊的時候都會有一個id 的,加到了id table表中。

  看了上面分析,usb match函數中涉及到的設備和介面驅動兩條判斷路線,在usb的世界裡,真正的驅動是針對介面的,針對設備的其實是剛開始沒有配置之前,一個通用的usb設備驅動,用來處理所有的usb設備,將其進入配置態,獲取該配置下的各種介面,並將介面作為一種特殊的usb設備(周邊設備)添加到設備模型中。

  下面我們分析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,

};

 

  當USB設備(只有設備先被註冊之後才會分析介面,才會註冊介面) 被探測並被註冊到系統後(device_add),會呼叫usb_bus_type.mach()(只要是usb設備,都會跟usb_generic_driver匹配上),之後會呼叫usb_probe_device(),從而引發usb_generic_driver probe()呼叫,也就是generic_probe函數。

  下面將會對generic_probe函數進行分析:

 

staticint generic_probe(struct usb_device *udev)

{

   int err, c;

 

   if (udev->authorized == 0)

       dev_err(&udev->dev, "Device is not authorized for usage\n");

   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\n",

                   c, err);

               /* This need not be fatal. The user can try to

                 * set other configurations. */

           }

       }

   }

   usb_notify_add_device(udev);

   return0;

}

 

  usb_generic_driver中的generic_probe函數,這個函數是一個usb設備的第一個匹配的driverGeneric通用,只要是個usb設備就得先跟他來一段,usb設備驅動界的老大。他的probe幹啥了呢?很簡單!找個合適的配置,配置一下。從此usb設備就進入配置的時代了。(前期的工作誰做的呢,到這都已經設置完位址了,當然是hub了,hub發現設備後,會進行前期的列舉過程,獲得配置,最終呼叫device_add將該usb設備添加到匯流排上。這個過程可以專門來一大段,是hub的主要工作,所以需要把hub單獨作為一個家族來對待,人家可是走在第一線的默默無聞的工作者,默默的將設備列舉完成後,將這個設備添加到usb匯流排上,多偉大)

  注意:設備setconfig時參數只能為0或者合理的配置值,0就代表不配置,仍然是定址態。不過有些設備就是拿配置0作為配置值得。

  usb_choose_configuration從設備可能的眾多配置(udev->descriptor.bNumConfigurations)選擇一個合適的配置(struct usb_host_config),並返回該配置的索引值。

 

//usb device選擇一個合適的配置

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,其實是一個陣列,存放設備的配置.usb_dev->config[m]-> interface[n]表示第m個配置的第n個介面的intercace結構.(m,n不是配置序號和介面序號).

   c = udev->config;

   //config項數

   num_configs = udev->descriptor.bNumConfigurations;

   //遍曆所有配置項

   for (i = 0; i < num_configs; (i++, c++)) {

       struct usb_interface_descriptor *desc = NULL;

 

       //配置項的介面數目

       //取配置項的第一個介面

       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;

       }

  

         //非標準Ethernet-over-USB協定

   if (i == 0 && num_configs > 1 && desc &&

           (is_rndis(desc) || is_activesync(desc))){

        

      ... ...

 

     }

       //選擇一個不是USB_CLASS_VENDOR_SPEC的配置

     elseif (udev->descriptor.bDeviceClass !=

                       USB_CLASS_VENDOR_SPEC &&

               (!desc || desc->bInterfaceClass !=

                       USB_CLASS_VENDOR_SPEC)) {

           best = c;

           break;

       }

 

       /*如果所有剩下的配置是特殊的vendor,選擇第一個*/

       elseif (!best)

           best = c;

   }

 

   ... ...

 

   //如果選擇好了配置,返回配置的序號,否則,返回-1

   if (best) {

       i = best->desc.bConfigurationValue;

       dev_info(&udev->dev,

           "configuration #%d chosen from %d choice%s\n",

           i, num_configs, plural(num_configs));

   } else {

       i = -1;

       dev_warn(&udev->dev,

           "no configuration chosen from %d choice%s\n",

           num_configs, plural(num_configs));

   }

 

   return i;

}  

 

  例如:我機器上的的 usb 驅動載入時,輸出:usb 1-1: configuration #1 chosen from 3 choices

  表示:此設備有3個配置,而驅動最終選擇了索引號為1的配置,至於選擇策略是怎樣的,請看usb_choose_configuration()函數。 

  generic_probe函數中的usb_set_configuration函數裡有很重要的動作,不是簡單的設置個配置,當我們選擇了某一個配置後,需要將這個配置的所有介面取出來,初始化介面作為驅動對應的一種設備的參數,如匯流排類型、設備類型等,呼叫device_add將該周邊設備添加到設備模型中。

 

int usb_set_configuration(struct usb_device *dev, int configuration)

{

   ... ...

 

   if (cp && configuration == 0)

       dev_warn(&dev->dev, "config 0 descriptor??\n");

 

  /*首先,根據選擇好的配置號找到相應的配置,在這裡要注意了, dev->config[]陣列中的配置並不是按照配置的序號來存放的,而是按照遍曆到順序來排序的.因為有些設備在發送配置描述符的時候,並不是按照配置序號來發送的,例如,配置2可能在第一次GET_CONFIGURATION就被發送了,而配置1可能是在第二次GET_CONFIGURATION才能發送.

  取得配置描述資訊之後,要對它進行有效性判斷,注意一下本段代碼的最後幾行代碼:usb2.0 spec上規定,0號配置是無效配置,但是可能有些廠商的設備並末按照這一約定,所以在linux,遇到這種情況只是列印出警告資訊,然後嘗試使用這一配置.*/

   n = nintf = 0;

   if (cp) {

       //介面總數

       nintf = cp->desc.bNumInterfaces;

       //在這裡, 注要是為new_interfaces分配空間,要這意的是, new_interfaces是一個二級指標,它的最終指向是struct usb_interface結構.特別的,如果總電流數要小於配置所需電流,則列印出警告消息.實際上,這種情況在usb_choose_configuration()中已經進行了過濾.

      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;

       ... ...

   }

 

   //要對設備進行配置了,先喚醒它

   ret = usb_autoresume_device(dev);

   if (ret)

       goto free_interfaces;

 

     //不是處於ADDRESS狀態,先清除設備的狀態

   if (dev->state != USB_STATE_ADDRESS)

       usb_disable_device(dev, 1); /* Skip ep0 */

 

   //確定我們有足夠帶寬提供這個配置

   ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL);

   ... ...

 

   //發送控制消息,選取配置

   ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),

                 USB_REQ_SET_CONFIGURATION, 0, configuration, 0,

                 NULL, 0, USB_CTRL_SET_TIMEOUT);

     ... ...

   }

 

   //dev->actconfig存放的是當前設備選取的配置

   dev->actconfig = cp;

     ... ...

   //將狀態設為CONFIGURED

   usb_set_device_state(dev, USB_STATE_CONFIGURED);

   /*接下來,就要對設備進行配置了,首先,將設備喚醒.只有在ADDRESS狀態才能轉入到CONFIG狀態.(SUSPEND狀態除外). 所以,如果設備當前不是處於ADDRESS狀態,就需要將設備的狀態初始化

   接著,發送SET_CONFIGURATIONControl消息給設備,用來選擇配置最後,dev->actconfig指向選定的配置,將設備狀態設為CONFIG*/

 

     //遍曆所有的介面

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

       struct usb_interface_cache *intfc;

       struct usb_interface *intf;

       struct usb_host_interface *alt;

      /*之前初始化的new_interfaces在這裡終於要派上用場了.初始化各介面,從上面的初始化過程中,我們可以看出:

    Intf->altsetting,表示介面的各種設置

    Intf->num_altsetting:表示介面的設置數目

    Intf->intf_assoc:介面的關聯介面(定義於minor usb 2.0 spec)

    Intf->cur_altsetting:介面的當前設置.*/

       cp->interface[i] = intf = new_interfaces[i];

       intfc = cp->intf_cache[i];

       intf->altsetting = intfc->altsetting;

       intf->num_altsetting = intfc->num_altsetting;

       //是否關聯的介面描述符,定義在minor usb 2.0 spec

       intf->intf_assoc = find_iad(dev, cp, i);

       kref_get(&intfc->ref);

 

       //選擇0號設置

       alt = usb_altnum_to_altsetting(intf, 0);

 

       //如果0號設置不存在,選排在第一個設置

       if (!alt)

            alt = &intf->altsetting[0];

 

       //當前的配置

       intf->cur_altsetting = alt;

    //用來啟用介面,也就是啟用介面中的每一個endpoint.

       usb_enable_interface(dev, intf);

       //注意這個地方對intf內嵌的struct devcie結構賦值,它的type被賦值為了usb_if_device_type.bus還是usb_bus_type.可能你已經反應過來了,要和這個device匹配的設備是interface的驅動.

       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指的是這個介面所屬的usb_dev,結合我們之前在UHCI中關於usb設備命名方式的描述.可得出它的命令方式如下:

      USB匯流排號-設備路徑:配置號.介面號.

      例如,在我的虛擬機上:/sys/bus/usb/devices

      1-0:1.0 usb1

      可以得知,系統只有一個usb control.

      1-0:1.0:表示,第一個usb control下的root hub1號配置的0號介面.

       */

       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);

 

   //註冊每一個介面?

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

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

 

       dev_dbg(&dev->dev,

           "adding %s (config #%d, interface %d)\n",

           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\n",

               intf->dev.bus_id, ret);

           continue;

       }

       usb_create_sysfs_intf_files(intf);

   }

 

   //使設備suspend

   usb_autosuspend_device(dev);

 

   return0;

}

 

  最後,註冊intf內嵌的device結構.設備配置完成了,為了省電,可以將設備置為SUSPEND狀態.

  到此為止usb_generic_driver憑藉自己的博愛的胸襟將所有設備的各個介面添加到了linux的設備模型中。

  usb設備首先以設備的身份與usb_generic_driver匹配,成功之後,會分裂出介面,當對介面呼叫device_add()後,會引起介面和介面驅動的匹配,這個匹配還是用usb_bus_type.mach()函數。因為介面的device->bus=& usb_bus_type, 這跟usb設備是一樣的,所以,都會呼叫到usb_bus_type.mach(),但設備和介面的處理流程是不一樣的(前面已經分析過)。

usb_hub_init

 

int usb_hub_init(void)

{

   if (usb_register(&hub_driver) < 0) {

       printk(KERN_ERR "%s: can't register hub driver\n",

           usbcore_name);

       return -1;

   }

 

   khubd_task = kthread_run(hub_thread, NULL, "khubd");

   if (!IS_ERR(khubd_task))

       return0;

 

   /* Fall through if kernel_thread failed */

   usb_deregister(&hub_driver);

   printk(KERN_ERR "%s: can't start khubd\n", usbcore_name);

 

   return -1;

}

 

這個函數主要有兩個功能:

  在系統初始化的時候在usb_init函數中呼叫usb_hub_init函數,就進入了hub的初始化。

      對於usb_register()可以看作是usb設備中的介面驅動,而usb_register_device_driver()是一個單純的USB設備驅動。

  在usb_hub_init函數中完成了註冊hub驅動,並且利用函數kthread_run建立一個kernel線程。該線程用來管理監視hub的狀態,所有的情況都通過該線程來報告。

  當載入主控器的時候,在自身的platform驅動的probe函數裡,呼叫usb_add_hcd->register_root_hubusb匯流排註冊root hub設備, usb匯流排match成功後,由usb_generic_driver驅動的probe函數,配置interface設備,然後向usb匯流排註冊interface usb匯流排再一次match不過這次是匹配了interface,通過ID值和hub驅動配置,因此呼叫hub驅動的probe函數(hub_probe)hub_probe函數中呼叫hub_configure函數來配置hub,在這個函數中主要是利用函數usb_alloc_urb函數來分配一個urb,利用usb_fill_int_urb來初始化這個urb結構,包括hub的中斷服務程式hub_irq的,查詢的週期等。

  每當有設備連接到USB介面時,USB匯流排在查詢hub狀態資訊的時候會觸發hub的中斷服務程式hub_irq,在該函數中利用kick_khubdhub結構通過event_list添加到khubd的佇列hub_event_list,然後喚醒khubd。進入hub_events函數,該函數用來處理khubd事件佇列,從khubdhub_event_list中的每個usb_hub資料結構。該函數中首先判斷hub是否出錯,然後通過一個for迴圈來檢測每個埠的狀態資訊。利用usb_port_status獲取埠資訊,如果發生變化就呼叫hub_port_connect_change函數來配置埠等。 

 

staticvoid 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中的後一個元素,並將其斷鏈

       tmp = hub_event_list.next;

       list_del_init(tmp);

 

    //根據tmp獲取hub

       hub = list_entry(tmp, struct usb_hub, event_list);

       //增加hub計數

       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斷開了,繼續hub_event_list中的下一個

       if (unlikely(hub->disconnected))

           goto loop;

 

       //設備沒有連接上

       if (hdev->state == USB_STATE_NOTATTACHED) {

           hub->error = -ENODEV;

           //將下面的子設備全部disable

           hub_pre_reset(intf);

           goto loop;

       }

 

       /* 自動恢復 */

       ret = usb_autopm_get_interface(intf);

       if (ret) {

           dev_dbg(hub_dev, "Can't autoresume: %d\n", ret);

            goto loop;

       }

 

       //hub 暫停

       if (hub->quiescing)

           goto loop_autopm;

 

       //hub 有錯誤發生?

       if (hub->error) {

           dev_dbg (hub_dev, "resetting for error %d\n",

               hub->error);

 

           ret = usb_reset_composite_device(hdev, intf);

           if (ret) {

               dev_dbg (hub_dev,

                   "error resetting hub: %d\n", ret);

               goto loop_autopm;

           }

 

           hub->nerrors = 0;

           hub->error = 0;

       }

    /*首先,hub_event_list摘下第一個元素,根據我們之前在介面驅動probe過程的kick_khubd()函數分析中,有將hub-> event_list添加到hub_event_list.因此,就可以順藤摸瓜找到hub,再根據hub結構,找到介面結構和所屬的usb 設備結構.

         然後,進行第一個重要的判斷.如果hub被斷開了,,斷開hub下面所連接的所有埠,這是在hub_pre_reset()中完成的.

         最後,進行第二個重要的判斷,如果hub發生了錯誤,reset它下面的所有埠,這是在usb_reset_composite_device()中完成的.*/

 

       //在這裡,它遍曆hub上的每一個埠,如果埠的連接會生了改變(connect_change等於1)的情況,就會呼叫hub_port_connect_change()

       for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {

      //檢測埠是否忙

           if (test_bit(i, hub->busy_bits))

               continue;

           //change_bits會在hub 第一次初始化時被賦值。而event_bits則在hub_irq中改變

           connect_change = test_bit(i, hub->change_bits);

           //如果都沒有改變,繼續測試下一個埠。

           if (!test_and_clear_bit(i, hub->event_bits) &&

                   !connect_change && !hub->activating)

               continue;

 

       //Get_Port_Status:取得埠狀態.

       //會取得port的改變值和狀態值

           ret = hub_port_status(hub, i,

                   &portstatus, &portchange);

           if (ret < 0)

               continue;

 

       //struct usb_dev,有一個struct usb_device *children[USB_MAXCHILDREN]的成員,它是表示對應埠序號上所連接的usb設備.

           //如果對應埠沒有在設備樹上,且埠顯示已經連接上

           //connect_change置為1

           if (hub->activating && !hdev->children[i-1] &&

                   (portstatus &

                       USB_PORT_STAT_CONNECTION))

               connect_change = 1;

           //埠的連接狀態發生了改變.需要發送Clear_Feature

           if (portchange & USB_PORT_STAT_C_CONNECTION) {

               clear_port_feature(hdev, i,

                   USB_PORT_FEAT_C_CONNECTION);

               connect_change = 1;

           }

 

           //埠的狀態從enable 變為了disable

           if (portchange & USB_PORT_STAT_C_ENABLE) {

               if (!connect_change)

                    dev_dbg (hub_dev,

                       "port %d enable change, "

                       "status %08x\n",

                       i, portstatus);

               clear_port_feature(hdev, i,

                   USB_PORT_FEAT_C_ENABLE);

 

                //埠已經被停止了,且埠已經被連在設備樹中.

                 //需要重啟一下此埠

               if (!(portstatus & USB_PORT_STAT_ENABLE)

                   && !connect_change

                   && hdev->children[i-1]) {

                   dev_err (hub_dev,

                       "port %i "

                       "disabled by hub (EMI?), "

                       "re-enabling...\n",

                       i);

                   connect_change = 1;

               }

           }

 

           //Resume完成  

           if (portchange & USB_PORT_STAT_C_SUSPEND) {

               clear_port_feature(hdev, i,

                   USB_PORT_FEAT_C_SUSPEND);

               //如果埠連接了設備,就將設備喚醒

               if (hdev->children[i-1]) {

                   ret = remote_wakeup(hdev->

                            children[i-1]);

                   if (ret < 0)

                       connect_change = 1;

               }

               //如果埠沒有連接設備,就將埠禁用

               else {

                   ret = -ENODEV;

                   hub_port_disable(hub, i, 1);

               }

               dev_dbg (hub_dev,

                   "resume on port %d, status %d\n",

                   i, ret);

           }

 

           //有過流保護,需要對hub power on

           if (portchange & USB_PORT_STAT_C_OVERCURRENT) {

               dev_err (hub_dev,

                   "over-current change on port %d\n",

                   i);

               clear_port_feature(hdev, i,

                   USB_PORT_FEAT_C_OVER_CURRENT);

               hub_power_on(hub);

          }

 

           //Reset狀態已經完成了

           if (portchange & USB_PORT_STAT_C_RESET) {

               dev_dbg (hub_dev,

                   "reset change on port %d\n",

                   i);

               clear_port_feature(hdev, i,

                   USB_PORT_FEAT_C_RESET);

           }

      

           /*什麼情況下, hub_port_connect_change才會被設為1.

       1:埠在hub->change_bits中被置位.搜索整個代碼樹,發生在設置hub->change_bits的地方,只有在hub_port_logical_disconnect()中手動將埠禁用,會將對應位置1.

           2:hub上沒有這個設備樹上沒有這個埠上的設備.但顯示埠已經連上了設備

           3:hub這個埠上的連接發生了改變,從埠有設備連接變為無設備連接,或者從無設備連接變為有設備連接.

           4:hub的埠變為了disable,此時這個埠上連接了設備,但被顯示該埠已經變禁用,需要將connect_change設為1.

           5:埠狀態從SUSPEND變成了RESUME,遠端喚醒埠上的設備失敗,就需要將connect_change設為1.

           另外hub_port_connect_change()函數我們放在後面再來討論*/

           if (connect_change)

               hub_port_connect_change(hub, i,

                       portstatus, portchange);

       }

 

         //HUB的處理

       //如果hub狀態末變化,不需要做任何處理

       if (test_and_clear_bit(0, hub->event_bits) == 0)

           ;   /* do nothing */

       //Get_hub_status 失敗?

       elseif (hub_hub_status(hub, &hubstatus, &hubchange) < 0)

           dev_err (hub_dev, "get_hub_status failed\n");

       else {

           //這裡是對應hub 狀態發生了改變,Get_hub_status正常返回的情況

           //如果hub的本地電源供電發生了改變

           if (hubchange & HUB_CHANGE_LOCAL_POWER) {

               dev_dbg (hub_dev, "power change\n");

               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 發生過電源保護,需要對hub power on

          if (hubchange & HUB_CHANGE_OVERCURRENT) {

               dev_dbg (hub_dev, "overcurrent change\n");

               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()函數分析: 

 

staticvoid 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

 

   //嘗試喚醒一個存在的設備

   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

       } elseif (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;

        }

   }

 

   //如果對應埠已經有設備連接,先將其斷開

   if (udev)

       usb_disconnect(&hdev->children[port1-1]);

   //接下來,hub->change_bits的對應位清掉,該位元是在函數hub_port_logical_disconnect()中被置的,在這裡將其清除,免得下次在進入hub_events()的時候,再次檢測到這個位發生改變.  

   clear_bit(port1, hub->change_bits);

 

   //如果發生物理斷開或者連接狀態改變,我們可能忘記移除設備

   if (!(portstatus & USB_PORT_STAT_CONNECTION) ||

           (portchange & USB_PORT_STAT_C_CONNECTION))

       clear_bit(port1, hub->removed_bits);

 

   //連接發生改變

   //連接反彈的處理,實際上就是除抖動

   if (portchange & (USB_PORT_STAT_C_CONNECTION |

               USB_PORT_STAT_C_ENABLE)) {

       //如果該埠的連接發生改變(從有連接到無接接,或者從無連接到有連接),就有一個除抖動的過程,usb2.0 spec上規定,除抖動的時間為100ms.

       //在函數裡,定義的測試時間是1500ms.如果在這個時間內,埠還末處於穩定狀態,就會返回-ETIMEDOUT

       //如果已經處於穩定狀態了,就會返回穩定狀態下的portstatus

       status = hub_port_debounce(hub, port1);

       if (status < 0) {

           if (printk_ratelimit())

               dev_err(hub_dev, "connect-debounce failed, "

                       "port %d disabled\n", port1);

           portstatus &= ~USB_PORT_STAT_CONNECTION;

       } else {

           portstatus = status;

       }

   }

   //如果介面上沒有連接了,可以直接退出了

   if (!(portstatus & USB_PORT_STAT_CONNECTION) ||

           test_bit(port1, hub->removed_bits)) {

 

       /*經過去抖後,埠穩定的處於斷開連接狀態.說明埠已經沒有設備了.然後,再判斷hub是否有電源開關((wHubCharacteristics & HUB_CHAR_LPSM) < 2),portstatus USB_PORT_FEAT_POWER位是否被設置,如果沒有被設置,則說明該埠斷電了.
         如果hub有電源開關,且埠沒有上電,則需要發送POWERSet_Feature來為之上電*/

       if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2

               && !(portstatus & USB_PORT_STAT_POWER))

           set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);

     //如果埠依然處理enable狀態,就會跳轉到標號done,就埠disalbe.

       if (portstatus & USB_PORT_STAT_ENABLE)

             goto done;

       return;

   }

   /*如果埠隱定處於連接狀態,那就需要連接埠下的設備了.首先看到的是一個for迴圈,是用來配置設備的兩種方式.我們知道,在配置設備的時候,首先要去取設備的描述符,這個過程是在ep0上完成的.而這個ep0支援的最大傳輸出資料又是在設備描述符的bMaxPacketSize0中所     定義的.因此就對應有兩種處理方式:

     第一種是傳輸8個位元組,取得描述符的前面一部份,從而就可以取得bMaxPacketSize0.此後再reset設備,再根據這個bMaxPacketSize0的長度去取它的設備描述符.

     第二種是一次傳輸64位元組,取得設備描述符的bMaxPacketSize0欄位*/

   for (i = 0; i < SET_CONFIG_TRIES; i++) {

 

     //為探測到的usb設備(包括普通hubu盤等)分配並初始化udev

       // 在為root hub分配struct usb_dev的時候,它的第一個參數,也就是它的父結點是為NULL.

       /*我們來觀察一下它在sysfs中的命名方式

         在沒有插入U盤之前:/sys/bus/usb/devices

         1-0:1.0 usb1

         插入U盤之後:

         1-0:1.0 1-1 1-1:1.0 usb1

         增加的兩個目是:

         1-11-1:1.0

         1-1:1.0 :只有這樣的目錄,表示該U盤只有一個介面,當前選取的是第0號設置項.*/

       udev = usb_alloc_dev(hdev, hdev->bus, port1);

       if (!udev) {

           dev_err (hub_dev,

               "couldn't allocate port %d usb_device\n",

               port1);

           goto done;

       }

       //置為USB_STATE_POWERED狀態

       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;

       elseif ((hdev->parent == NULL) &&

               (portstatus & USB_PORT_STAT_SUPER_SPEED))

           udev->speed = USB_SPEED_SUPER;

       else

           udev->speed = USB_SPEED_UNKNOWN;

 

       /*為設備指定一個位址,是到所屬的usb busbus->devmap中找到沒有使用的那一位,先進行兩次新的策略(i=0=1),如果不行就再進行兩次舊的策略(i=2i=3).所有這一切只有一個目的,就是為了獲得設備的描述符,
       設置了udev->ttudev->ttportudev->ep   0.desc.wMaxPacketSize,設置udev->status= USB_STATE_ADDRESS*/

       choose_address(udev);

       if (udev->devnum <= 0) {

           status = -ENOTCONN;   /* Don't retry */

            goto loop;

       }

 

       //hub_port_init()對這個usb_dev結構進行一系的初始化,在這個函數中會處理: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 ?\n", 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\n");

               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結構跟他的父結構關聯起來,也就是說添加到它的父結構的usb_dev-> children[]陣列.

       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(): 得到設備的描述符(包括設備描述符、配置描述符、介面描述符等),分析以上描述符資訊,提取出配置、介面等,並賦值給udev結構裡相應的欄位。

         device_add() :usb設備註冊到系統裡,這個動作將觸發驅動的匹配,由於這是個usb設備,所以萬能usb驅動usb_generic_driver會匹配上,從而generic_probe會得到執行,從上面可以看出來,這一次hub_events()呼叫是由於主控制器初始化呼叫了
           hub_probe,從而引發hub_events呼叫。那root hub初始化完成以後hub_events會如何觸發呢?答案是通過中斷!而這個中斷的服務函數就是hub_irq,也即是說,凡是真正的有埠變化事件發生,hub_irq就會被呼叫,hub_irq()最終會呼叫kick_khubd()             觸發hubevent_list,於是再次呼叫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\n", 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\n",

               port1);

// Done標號是對應上述處理失敗的處理,它禁用掉該埠(因為該埠沒有連接設備或者是埠上的設備配置失敗),如果是root hub,USB控制器器驅動中又定義了relinquish_port.呼叫它.

done:

   hub_port_disable(hub, port1, 1);

   if (hcd->driver->relinquish_port && !hub->hdev->parent)

       hcd->driver->relinquish_port(hcd, port1);

}

 

 

arrow
arrow
    全站熱搜

    立你斯 發表在 痞客邦 留言(0) 人氣()