Linux驅動之USB總線驅動程序框架簡析

通用串行總線(USB)是主機和外圍設備之間的一種鏈接。USB總線規範有1.1版和2.0版,固然如今已經有了3.0版本。USB1.1支持兩種傳輸速度:低速爲1.5Mbps,高速爲12Mbps。USB2.0的傳輸速度能夠高達480Mbps。USB2.0向下兼容USB1.1,能夠將USB1.1的設備鏈接到USB2.0控制器上,也能夠把USB2.0的設備鏈接到USB1.1控制器上。S3C2440的USB主機控制器支持USB1.1總線規範。windows

USB總線的拓撲結構以下圖所示:USB主機控制器(USB Host Controller)經過根集線器(Root Hub)與其餘USB設備相連。集線器也屬於USB設備,經過它能夠在一個USB接口上擴展出多個接口。除根集線器外,最多能夠層疊5個根集線器。一條USB主線上最多能夠外接127個設備,固然包括根集線器和其餘集線器。整個結構圖是一個星型結構,一條USB總線上全部設備共享一條通往主機的數據通道,同一時刻只能有一個設備與主機通信框架

經過USB主機控制器來管理外接的USB設備。USB控制器實際上由USB控制器硬件+USB控制器軟件組成的。USB主機控制器分爲3種:UHCI、OHCI、EHCI。UHCI與OHCI支持USB1.1協議;EHCI支持USB2.0協議。UHCI,它的硬件比較簡單,因此軟件相對就比較複雜;而OHCI,它的硬件具有更多性能,相反的軟件作的事情就比較少。S3C2440的USB主機控制器支持OHCI主機控制器。HCI的英文全稱爲(Host Controller Interface)。函數

根據以上知識能夠知道其實USB驅動程序能夠分爲兩類:USB主機控制器驅動程序(Host Controller Drivers)、USB設備驅動程序(USB device drivers)。它們在內核中的層次以下圖所示性能

USB主機控制器驅動程序提供訪問USB設備的接口,它只是一個數據通道,至於這些數據有什麼做用,這要靠上層的USB設備驅動程序來解釋。USB設備驅動程序使用下層的驅動提供的數據接口來訪問USB設備,不須要關心具體的傳輸細節。在Linux2.6中,USB主機控制器驅動程序已經幫咱們寫好了,下面就簡單分析一下USB主機控制器驅動程序。spa

一、在分析USB主機控制器驅動程序以前先來看幾個問題:翻譯

當把你的安卓手機經過USB接口接到電腦上的USB口時,會有以下現象code

一、右下角彈出「發現andrido phone」。orm

二、跳出一個對話框,提示你安裝驅動程序。對象

那麼問題便來了blog

問題一、既然尚未「驅動程序」,爲什麼能知道是「andrido phone」
答1:windows裏已經有了USB的總線設備驅動程序,接入USB設備後,是「總線驅動程序」知道你是「andrido phone」,提示你安裝的是「設備驅動程序」,USB總線驅動程序負責:識別USB設備,給USB設備找到對應的驅動程序

問題二、USB設備種類很是多,爲何一接入電腦就能識別出來?
答2:PC和USB設備都得遵照一些規範。
好比:USB設備接入電腦後,PC會發出「你是什麼?」USB設備必須回答「我是XXX」,而且回答的語言必須是中文;
USB總線驅動程序會發出某些命令獲取設備信息(描述符),USB設備必須返回「描述符」給PC。

問題三、PC機上接有很是多的USB設備,怎麼分辨它們?
答3:接在USB總線上的每一個USB設備都有本身的編號(地址),每個USB設備接入PC時,USB總線驅動程序都會給它分配一個編號,PC機想訪問某個USB設備時,要先發出的命令都含有對方的編號(地址)

問題四、USB設備剛接入PC時,尚未編號,那麼PC怎麼把「分配的編號」告訴它?
答4:新接入的USB設備的默認編號是0,在未分配新的編號前,PC使用0編號和它通信。

問題五、爲何一接入USB設備,PC機就能發現它(又沒有中斷)
答5:USB接口只有4條線:5V、GND、D-、D+。PC的USB口內部D-和D+接有15K的下拉電阻,未接USB設備時爲低電平;USB設備的USB口內部,D-或D+接有1.5k的上拉電阻:它一接入就會把PC USB口D-(全速)或D+(高速)拉高,從硬件的角度通知PC有新設備接入。

從以上問答能夠看出來USB總線驅動程序的做用能夠總結爲:(後面會再詳細說明)

一、識別設備
二、找到並安裝對應的USB設備驅動
三、提供USB讀寫函數(不瞭解數據含義)

 

 二、接着再提出一些USB的概念

一、USB是主從結構的
全部的USB傳輸都是從USB主機這方發起,USB設備沒有主動通知USB主機的能力
例子:USB鼠標滑動一下,馬上產生數據,可是它沒有能力通知PC讀數據,只能被動的等待PC機被讀

二、USB的傳輸類型:
a、控制傳輸:可靠,時間有保證,好比USB設備的識別過程
b、批量傳輸:可靠,時間沒有保證,好比:U盤
c、中斷傳輸(仍是查詢方式):可靠,實時,好比:USB鼠標
d、實時傳輸:數據不可靠,實時,好比:USB攝像頭

三、USB傳輸的對象:端點(endpoint)
咱們說「讀U盤」、「寫U盤」能夠細化爲:把數據寫到U盤的端點1,把數據從U盤的端點2裏讀出數據
除了端點0以外,每個端點只支持一個方向的數據傳輸
端點0用於控制傳輸,既能輸出也能輸入

四、每個端點都只有一個傳輸類型,傳輸方向

五、術語裏、程序裏說的輸入(IN)、輸出(OUT)「都是」基於USB主機的立場說的。
好比鼠標的數據是從鼠標傳入PC機的,對應的端點稱爲「輸入端點」

 

三、經過前面的描述對於USB已經有了大體的瞭解了,接着經過程序分析來更深刻的瞭解USB設備的識別過程:

把一個USB鼠標接到開發板上會彈出以下信息:

usb 1-1: new full speed USB device using s3c2410-ohci and address 3 usb 1-1: configuration #1 chosen from 1 choice scsi1 : SCSI emulation for USB Mass Storage devices 拔掉 usb 1-1: USB disconnect, address 3 再接上 usb 1-1: USB disconnect, address 3usb 1-1: new full speed USB device using s3c2410-ohci and address 4 usb 1-1: configuration #1 chosen from 1 choice scsi2 : SCSI emulation for USB Mass Storage devices

其中address3,address4就是USB核心程序給USB設備分配的地址。查找「USB device using 」關鍵字,在drivers/usb/core/hub.c的2186行找到了這句話:"%s %s speed %sUSB device using %s and address %d\n"。它位於hub_port_init函數下,層層查找能夠發現最終是hub_irq函數致使了hub_port_init被調用。hub_irq不是一個真正的中斷處理程序,它只是集線器USB設備的數據接收完成後的回調函數,其實drivers/usb/core/hub.c也不過是一個USB設備驅動程序而已。

drivers/usb/core/hub.c程序先放到一邊,待會再來分析它。先嚐試找一下USB控制器的驅動程序,在drivers\usb\host\Ohci-s3c2410.c找到了這個驅動程序, 它以平臺設備驅動程序爲框架,drivers\usb\host\Ohci-s3c2410.c屬於driver層

static struct platform_driver ohci_hcd_s3c2410_driver = { .probe = ohci_hcd_s3c2410_drv_probe,//一旦匹配devices則被調用
    .remove        = ohci_hcd_s3c2410_drv_remove, .shutdown = usb_hcd_platform_shutdown, /*.suspend = ohci_hcd_s3c2410_drv_suspend, */
    /*.resume = ohci_hcd_s3c2410_drv_resume, */ .driver = { .owner = THIS_MODULE, .name = "s3c2410-ohci", }, };

接着搜索「s3c2410-ohci」,找到了devices層,位於arch\arm\plat-s3c24xx\Devs.c

struct platform_device s3c_device_usb = { .name = "s3c2410-ohci", .id = -1, .num_resources = ARRAY_SIZE(s3c_usb_resource), .resource = s3c_usb_resource, .dev = { .dma_mask = &s3c_device_usb_dmamask, .coherent_dma_mask = 0xffffffffUL } };

到這裏咱們尚未找到插上USB設備後第一個運行的函數,咱們猜想這個函數一定是一箇中斷,USB控制器接收到數據後將會產生一箇中斷給MCU,而後MCU再進行處理。如今咱們就是要找到這一個中斷函數,在drivers/usb/core/hub.c中找到了usb_add_hcd函數,它位於usb_hcd_s3c2410_probe函數下,這個函數一旦有USB控制器設備匹配就會被調用。

static int usb_hcd_s3c2410_probe (const struct hc_driver *driver, struct platform_device *dev) { ... ... retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED);//註冊OHCI控制器中斷
    if (retval != 0) goto err_ioremap; ... ... }

繼續往下看usb_add_hcd函數,它位於drivers\usb\core\hcd.c下

int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags) { ... ... if ((retval = request_irq(irqnum, &usb_hcd_irq, irqflags, hcd->irq_descr, hcd)) != 0) {//中斷USB中斷
            dev_err(hcd->self.controller, "request interrupt %d failed\n", irqnum); goto err_request_irq; } hcd->irq = irqnum; ... ... } 

在這裏看到了調用註冊中斷函數,而且它的中斷處理回調函數爲usb_hcd_irq,它一樣位於drivers\usb\core\hcd.c下

irqreturn_t usb_hcd_irq (int irq, void *__hcd) { struct usb_hcd        *hcd = __hcd; int            start = hcd->state; if (unlikely(start == HC_STATE_HALT ||
        !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) return IRQ_NONE; if (hcd->driver->irq (hcd) == IRQ_NONE) return IRQ_NONE; set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);//設置中斷標誌

    if (unlikely(hcd->state == HC_STATE_HALT)) usb_hc_died (hcd); return IRQ_HANDLED; }

這樣,經過usb_hcd_irq 函數的做用,最終將會調用到drivers/usb/core/hub.c下的hub_irq函數。以前說過drivers/usb/core/hub.c其實也是一個USB設備驅動程序,它實際上是一個根集線器驅動程序。這裏直接列出匹配USB設備驅動程序的過程。

 

hub_irq kick_khubd hub_thread hub_events hub_port_connect_change usb_alloc_dev(hdev, hdev->bus, port1); dev->dev.type = &usb_device_type; choose_address(udev); //給新設備分配編號(地址)
 hub_port_init //usb 1-1: new full speed USB device using s3c2410-ohci and address 3
                        hub_set_address  //把編號(地址)告訴USB設備
                        usb_get_device_descriptor(udev, 8);//獲取設備描述符
 usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE); usb_new_device(udev); usb_get_configuration(udev);//把全部的描述符都讀出來並解析
 usb_parse_configuration device_add//把device放入usb_bus_type的dev鏈表, //從usb_bus_type的driver鏈表中取出usb_driver //把usb_interface和usb_driver的id_table比較 //若是能匹配,調用usb_driver的probe函數

 

大體總結一下:

一、當接上USB鼠標後,USB主機控制器檢查到D-或D+接口變高知道了有設備接入。

二、USB主機控制器把新分配的編號告訴USB設備,這時候仍是以0地址通信的。

三、USB主機控制器以0地址從USB設備得到設備描述符的前8個字節,從這8個字節能夠知道後面全部設備描述符總共的大小

四、知道了剩下設備描述符的大小,就能夠將剩餘的設備描述符都讀出來

五、接着把device放入usb_bus_type的dev鏈表

六、從usb_bus_type的driver鏈表中取出usb_driver

七、把usb_interface和usb_driver的id_table比較(對於USB鼠標最終比較的是接口描述符下的bInterfaceClass;bInterfaceSubClass;bInterfaceProtocol;三個信息,中文翻譯爲設備類型,設備子類型,設備協議)

八、若是能匹配,調用usb_driver的probe函數

 從上面總結能夠看出,要寫USB設備的驅動程序就須要對usb_driver結構體進行初始。

 

USB各個描述符的關係以下,具體描述符結構將在下一節講解

 

 以上就是USB總線驅動程序的框架,USB設備是一個很是複雜的東西,這裏只是簡單的描述,並未深刻理解。目的是爲了編寫USB設備驅動程序。

相關文章
相關標籤/搜索