USB(Universal Serial Bus)全稱通用串口總線,USB爲解決即插即用需求而誕生,支持熱插拔。USB協議版本有USB1.0、USB1.1、USB2.0、USB3.1等,USB2.0目前比較經常使用。因爲USB是主從模式的結構,設備與設備之間、主機與主機之間不能互連,爲解決這個問題,擴大USB的應用範圍,出現了USB OTG,全拼 ON The Go。USB OTG 同一個設備,在不一樣的場合下可行在主機和從機之間切換。css
USB網絡的基本拓撲結構是星型的。一個USB系統由一個或多個USB設備(外設)、一個或多個集線器(hub)和一個主機組成。計算機主機有時又叫做主控制器,在一個USB網絡中只能有一個主機。主控制器內置了一個根集線器,提供主控制器上的初始附屬點。USB是一種高速總線,它鏈接的設備數量最多可達127個。html
主機定時對集線器的狀態進行查詢。當一個新設備接入一個集線器時,這個集線器就會向主機報告狀態改變,主機發出一個命令使端口有效並對其進行從新設置。位於這個端口上的設備進行響應,主機收到關於設備的信息。根據這些信息,主機的操做系統肯定對這個設備使用哪一種驅動程序,接着設備被分配一個惟一標識的地址,主機向它發出內部設置請求。當一個設備從總線上移走時,主機就從其可用資源列表中將這個設備刪除。主機對USB設備的探測和識別叫做總線列舉(bus enumeration)。linux
OHCI:Open Host Controller Interface,爲非PC系統上以及帶有SiS和Ali芯片組的PC主板上的USB芯片提供支持,如擴展卡、嵌入式開發板的USB主控等。USB1.1接口標準,它不只僅針對USB,還支持其餘一些接口,好比支持Apple的火線(IEEE1394)接口。與UHCI相比,OHCI的硬件複雜,硬件作的事情更多,因此實現對應的軟件驅動的任務就相對簡單。編程
UHCI:Universal Host Controller Interface,用來爲大多數PC主板(包括Intel和Via)上的USB芯片提供支持。因爲UHCI的硬件線路比OHCI簡單,因此成本低,但須要複雜的驅動程序,CPU負荷稍重。Intel主導的對USB1.0、1.1的接口標準,與OHCI不兼容。ubuntu
EHCI:Enhanced Host Controller Interface,Intel主導的USB2.0接口標準,兼容於OHCI和UHCI。EHCI僅提供USB2.0的高速功能,而依靠UHCI或OHCI來提供對全速(full-speed)或低速(low-speed)設備的支持。數組
XHCI:eXtensible Host controller Interface,可擴展主控制器接口,由Intel開發,面向USB3.0,同時支持USB2.0及如下的設備。XHCI支持全部種類速度的USB設備(USB3.0 SuperSpeed,USB 2.0 Low-,Full-,and High-speed,USB 1.1 Low- and Full-speed)。網絡
USB1.1支持的數據傳輸率爲12Mbps(full-speed)和1.5Mbps(用於慢速外設,low-speed),USB2.0支持的數據傳輸率可達480Mbps(high-speed),USB3.0支持的傳輸速率達5.0Gbit/s(SupperSpeed)。數據結構
主要的控制器IP(Controller IP)提供商有:app
synopsys-DesignWare,DWC3(Synopsys DesignWare C3 IP block)用在Qualcomm,Exynos和OMAP SOCs,DWC2用在Rockchip SoCs。less
Chipidea-用在Qualcomm的High-Speed OTG上。
ST/Ericsson
Mentor Graphics-Inventra IP,用在TI DaVinci,OMAP2,OMAP3上,AD部分SOCs,Allwinner(全志) SoCs。
linux內核drivers/usb下有控制器支持的代碼:chipidea,dwc2,dwc3等,固然最通用的仍是EHCI/XHCI/UHCI/OHCI。
USB數據傳輸有四種類型:
控制傳輸(Control Transfers),非週期,突發。用於對總線和總線上的設備進行設置,並返回狀態信息,即用於命令和狀態的傳輸。
中斷傳輸(Interrupt Transfers),週期性,低頻率。用來在必定的時間片發送數據,這個時間片的範圍從1ms-255ms。如人機接口設備(HID)中的鼠標、鍵盤、軌跡球等。
同步傳輸(Isochronous Transfers,等時傳輸),週期性。用來發送對實時性要求較高的數據,好比送往一個輸出設備的音頻數據,與塊傳輸(這種傳輸是雙向的)不一樣,同步傳輸是單向的,而且不包含循環冗餘校驗(CRC)字段。
塊傳輸(Bulk Transfers,批量傳輸,大容量數據傳輸),非週期,突發。經過USB設備對數據進行異步發送,大容量數據的通訊,數據能夠佔用任意帶寬,並容忍延遲。如USB打印機、掃描儀、大容量存儲設備等。
數據在USB設備之間以包的方式進行傳輸。一個傳輸由一個或多個包組成,一個包包括1個SYNC(同步)字節,1個PID(包ID),內容(數據、地址等)和1個CRC字段。SYNC字節鎖定接收器時鐘的相位,它至關於RS232幀的起始位。PID指定包的功能,好比它是不是一個數據包或配置包。包ID的高4位是低4位取反的結果,這樣作是爲了增長一種錯誤檢測機制。
USB包由四種類型:令牌、數據、握手和先導包。
令牌包
令牌是個24字節的包,它決定總線上發生的數據傳輸的類型,分爲輸入包、輸出包、設置包和幀起始包(注意這裏的輸入包是用於設置輸入命令的,輸出包是用來設置輸出命令的,而不是放數據的)。
2. 數據包
分爲DATA0包和DATA1包,當USB發送數據的時候,若是一次發送的數據長度大於相應端點的容量時,就須要把數據包分爲好幾個包,分批發送,DATA0包和DATA1包交替發送,即若是第一個數據包是DATA0,那第二個數據包就是DATA1。但也有例外狀況,在同步傳輸中(四類傳輸類型中之一),全部的數據包都是爲DATA0,格式以下:
SYNC + PID + 0~1023字節 + CRC16
3. 握手包
握手包包括 ACK、NAK、STALL以及NYET 四種,其中 ACK 表示確定的應答,成功的數據傳輸。NAK 表示否認的應答,失敗的數據傳輸,要求從新傳輸。STALL表示功能錯誤或端點被設置了STALL屬性。NYET表示還沒有準備好,要求等待。
描述符是一個用來將設備的性能通知主機的數據包,它包含1個設備生產上的標識符、1個產品標識、1個類別類型和設備的內部設置,好比終端和對電源的需求。每一個生產商都有一個惟一的ID,每一個產品依次也有一個惟一的ID。計算機主機上的軟件根據從描述符中獲得的信息判斷一個設備有哪些功能,以及主機如何與設備進行交互。
USB採用樹形拓撲結構,主機側和設備側的USB控制器分別稱爲主機控制器(Host Controller)和USB設備控制器(UDC),每條總線上只有一個主控制器,負責協調主機和設備間的通訊,而設備不能主動向主機發送任何消息。USB驅動能夠從兩個角度去觀察,一個角度是主機側,一個角度是設備側。
主機側USB驅動包括兩類:USB主機控制器驅動和USB設備驅動,前者控制插入其中的USB設備,後者控制USB設備如何與主機通訊。USB核心負責USB驅動管理和協議處理,包括:經過定義一些數據結構、宏和功能函數,向上爲設備驅動提供編程接口,向下爲USB主機控制器驅動提供編程接口;經過全局變量維護整個系統的USB設備信息;完成設備熱插拔控制、總線數據傳輸控制等。
USB驅動程序直接訪問硬件,控制USB設備和主機間的底層通訊,向上提供與硬件相關操做的回調函數。當前Gadget API是UDC驅動程序回調函數的簡單封裝。Gadget驅動程序具體控制USB設備驅動功能的實現。它使用GadgetAPI控制UDC實現上述功能。GadgetAPI把下層的UDC驅動程序和上層的Gadget驅動程序隔離開,使得在linux系統中編寫USB設備側驅動程序時可以把功能的實現和底層通訊分離。
在usb設備的邏輯組織中,包含設備、配置、接口和端點4個層次。
每一個USB設備都提供了不一樣級別的配置信息,能夠包含一個或多個配置,不一樣的配置使設備表現出不一樣的功能組合(在探測/鏈接期間需從其中選定一個),配置由多個接口組成。
在USB協議中,接口由多個端點組成,表明一個基本的功能,是USB設備驅動程序控制的對象,一個功能複雜的USB設備能夠具備多個接口。每一個配置中能夠有多個接口,而設備接口是端點的聚集(collection)。例如USB揚聲器能夠包含一個音頻接口以及對旋鈕和按鈕的接口。一個配置中的全部接口能夠同時有效,並可被不一樣的驅動程序鏈接。每一個接口能夠有備用接口,以提供不一樣質量的服務參數。
端點是USB通訊的最基本形式,每個USB設備接口在主機看來就是一個端點的集合。主機只能經過端點與設備進行通訊,以使用設備的功能。在USB系統中每個端點都有惟一的地址,這是由設備地址和端點號給出的。每一個端點都有必定的屬性,其中包括傳輸方式、總線訪問頻率、帶寬、端點號和數據包的最大容量等。一個USB端點只能在一個方向承載數據,或者從主機到設備(輸出端點),或者從設備到主機(輸入端點),所以端點可看做一個單向的管道。端點0一般爲控制端點,用於設備初始化參數等。只要設備鏈接到USB上而且上電端點0就能夠被訪問。端點1、2等通常用做數據端點,存放主機與設備間往來的數據。
》》設備一般有一個或多個配置;
》》配置一般有一個或多個接口;
》》接口一般有一個或多個設置;
》》接口有0個或多個端點。
這種層次化配置信息在設備中經過一組標準的描述符來描述,以下所示。
》》設備描述符:關於設備的通用信息,如供應商ID、產品ID和修訂ID,支持的設備類、子類和使用的協議以及默認端點的最大包大小等。在linux內核中,USB設備用usb_device結構體來描述,USB設備描述符定義爲usb_device_descriptor結構體。
》》配置描述符:此配置中的接口數、支持的掛起和恢復能力以及功率要求。USB配置在內核中使用usb_host_config結構體描述,USB配置描述符定義爲結構體usb_config_descriptor。
》》接口描述符:接口類、子類和使用的協議,接口備用配置的數據和端點數目。USB接口在內核中使用usb_interface結構體描述,USB接口描述符定義爲結構體usb_interface_descriptor。
》》端點描述符:端點地址、方向和類型,支持的最大包大小,若是是終端類型的端點則還包括輪詢頻率。USB端點使用usb_host_endpoint結構體描述,USB端點描述符定義爲usb_endpoint_descriptor。
》》字符串描述符:在其餘描述符中會爲某些字段提供字符串索引,它們可被用來檢索描述性字符串,能夠以多種語言形式提供。字符串描述符是可選的,有的設備有,有的設備沒有,字符串描述符對應於usb_string_descriptor結構體。
可經過lsusb查看USB設備各種描述符。
Usage: lsusb [options]... List USB devices -v, --verbose Increase verbosity (show descriptors) -s [[bus]:][devnum] Show only devices with specified device and/or bus numbers (in decimal) -d vendor:[product] Show only devices with the specified vendor and product ID numbers (in hexadecimal) -D device Selects which device lsusb will examine -t, --tree Dump the physical USB device hierarchy as a tree -V, --version Show version of program -h, --help Show usage and help
一個FT2232H設備的lsusb顯示(FT2232H包含兩個獨立的usb口):
wang@ubuntu:~$ lsusb -s 001:002 -v Bus 001 Device 002: ID 0403:6010 Future Technology Devices International, Ltd FT2232C Dual USB-UART/FIFO IC Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 0 (Defined at Interface level) bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 64 idVendor 0x0403 Future Technology Devices International, Ltd idProduct 0x6010 FT2232C Dual USB-UART/FIFO IC bcdDevice 7.00 iManufacturer 1 FTDI iProduct 2 Dual RS232-HS iSerial 0 bNumConfigurations 1
Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 55 bNumInterfaces 2 bConfigurationValue 1 iConfiguration 0 bmAttributes 0x80 (Bus Powered) MaxPower 500mA
Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 255 Vendor Specific Class bInterfaceSubClass 255 Vendor Specific Subclass bInterfaceProtocol 255 Vendor Specific Protocol iInterface 2 Dual RS232-HS Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0200 1x 512 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x02 EP 2 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0200 1x 512 bytes bInterval 0 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 1 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 255 Vendor Specific Class bInterfaceSubClass 255 Vendor Specific Subclass bInterfaceProtocol 255 Vendor Specific Protocol iInterface 2 Dual RS232-HS Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x83 EP 3 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0200 1x 512 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x04 EP 4 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0200 1x 512 bytes bInterval 0 Device Qualifier (for other device speed): bLength 10 bDescriptorType 6 bcdUSB 2.00 bDeviceClass 0 (Defined at Interface level) bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 64 bNumConfigurations 1 Device Status: 0x0000 (Bus Powered)
用usb_hcd結構體描述USB主控制器驅動,它包含USB主機控制器的「家務」信息,硬件資源、狀態描述和用於操做主機控制器的hc_driver等。
struct usb_hcd { /* * housekeeping */ struct usb_bus self; /* hcd is-a bus */ struct kref kref; /* reference counter */ const char *product_desc; /* product/vendor string */ int speed; /* Speed for this roothub. * May be different from * hcd->driver->flags & HCD_MASK */ char irq_descr[24]; /* driver + bus # */ struct timer_list rh_timer; /* drives root-hub polling */ struct urb *status_urb; /* the current status urb */ #ifdef CONFIG_PM struct work_struct wakeup_work; /* for remote wakeup */ #endif /* * hardware info/state */ const struct hc_driver *driver; /* hw-specific hooks */ /* * OTG and some Host controllers need software interaction with phys; * other external phys should be software-transparent */ struct usb_phy *usb_phy; struct phy *phy; /* more shared queuing code would be good; it should support * smarter scheduling, handle transaction translators, etc; * input size of periodic table to an interrupt scheduler. * (ohci 32, uhci 1024, ehci 256/512/1024). */ …… /* The HC driver's private data is stored at the end of * this structure. */ unsigned long hcd_priv[0] __attribute__ ((aligned(sizeof(s64)))); };
hc_driver包含用於操做主機控制器的鉤子函數,即「hw-specific hooks」。
struct hc_driver { const char *description; /* "ehci-hcd" etc */ const char *product_desc; /* product/vendor string */ size_t hcd_priv_size; /* size of private data */ /* irq handler */ irqreturn_t (*irq) (struct usb_hcd *hcd); int flags; /* called to init HCD and root hub */ int (*reset) (struct usb_hcd *hcd); int (*start) (struct usb_hcd *hcd); …… /* cleanly make HCD stop writing memory and doing I/O */ void (*stop) (struct usb_hcd *hcd); /* shutdown HCD */ void (*shutdown) (struct usb_hcd *hcd); /* return current frame number */ int (*get_frame_number) (struct usb_hcd *hcd); /* manage i/o requests, device state */ int (*urb_enqueue)(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags); int (*urb_dequeue)(struct usb_hcd *hcd, struct urb *urb, int status); …… /* Allocate endpoint resources and add them to a new schedule */ int (*add_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *); /* Drop an endpoint from a new schedule */ int (*drop_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *); …… int (*check_bandwidth)(struct usb_hcd *, struct usb_device *); /* Reset the device schedule to the last known good schedule, * which was set from a previous successful call to * check_bandwidth(). This reverts any add_endpoint() and * drop_endpoint() calls since that last successful call. * Used for when a check_bandwidth() call fails due to resource * or bandwidth constraints. */ void (*reset_bandwidth)(struct usb_hcd *, struct usb_device *); /* Returns the hardware-chosen device address */ int (*address_device)(struct usb_hcd *, struct usb_device *udev); /* prepares the hardware to send commands to the device */ int (*enable_device)(struct usb_hcd *, struct usb_device *udev); /* Notifies the HCD after a hub descriptor is fetched. * Will block. */ int (*update_hub_device)(struct usb_hcd *, struct usb_device *hdev, struct usb_tt *tt, gfp_t mem_flags); int (*reset_device)(struct usb_hcd *, struct usb_device *); /* Notifies the HCD after a device is connected and its * address is set */ int (*update_device)(struct usb_hcd *, struct usb_device *); int (*set_usb2_hw_lpm)(struct usb_hcd *, struct usb_device *, int); /* USB 3.0 Link Power Management */ /* Returns the USB3 hub-encoded value for the U1/U2 timeout. */ int (*enable_usb3_lpm_timeout)(struct usb_hcd *, struct usb_device *, enum usb3_link_state state); /* The xHCI host controller can still fail the command to * disable the LPM timeouts, so this can return an error code. */ int (*disable_usb3_lpm_timeout)(struct usb_hcd *, struct usb_device *, enum usb3_link_state state); int (*find_raw_port_number)(struct usb_hcd *, int); /* Call for power on/off the port if necessary */ int (*port_power)(struct usb_hcd *hcd, int portnum, bool enable); };
urb_enqueue()函數很是關鍵,上層經過usb_submit_urb()提交1個USB請求後,該函數調用usb_hcd_submit_urb(),並最終調用至usb_hcd的driver成員(hc_driver類型)的urb_enqueue()函數。
在linux中,以下函數建立、增長和移除HCD:
struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, struct device *dev, const char *bus_name); int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags); void usb_remove_hcd(struct usb_hcd *hcd);
在drivers/usb/host中分別實現了不一樣USB協議的主控制器驅動,如UHCI,OHCI,EHCI和XHCI。
從主機角度看,怎樣訪問被插入的USB設備。
USB設備和USB接口在sysfs中均表示爲單獨的USB設備,其目錄命名規則以下:
根集線器-集線器端口號(-集線器端口號-…):配置.接口
tree /sys/bus/usb /sys/bus/usb ├── devices │ ├── 1-0:1.0 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-0:1.0 │ ├── 1-1 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-1 │ ├── 1-1:1.0 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-1/1-1:1.0 │ ├── 1-1:1.1 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-1/1-1:1.1 │ ├── 2-0:1.0 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-0:1.0 │ ├── 2-1 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-1 │ ├── 2-1:1.0 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-1/2-1:1.0 │ ├── 2-2 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2 │ ├── 2-2:1.0 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2/2-2:1.0 │ ├── usb1 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1 │ └── usb2 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2 ├── drivers │ ├── ftdi_sio │ │ ├── 1-1:1.1 -> ../../../../devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-1/1-1:1.1 │ │ ├── bind │ │ ├── module -> ../../../../module/usbserial │ │ ├── uevent │ │ └── unbind │ ├── hub │ │ ├── 1-0:1.0 -> ../../../../devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-0:1.0 │ │ ├── 2-0:1.0 -> ../../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-0:1.0 │ │ ├── 2-2:1.0 -> ../../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2/2-2:1.0 │ │ ├── bind │ │ ├── module -> ../../../../module/usbcore │ │ ├── new_id │ │ ├── remove_id │ │ ├── uevent │ │ └── unbind │ ├── usb │ │ ├── 1-1 -> ../../../../devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-1 │ │ ├── 2-1 -> ../../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-1 │ │ ├── 2-2 -> ../../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2 │ │ ├── bind │ │ ├── uevent │ │ ├── unbind │ │ ├── usb1 -> ../../../../devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1 │ │ └── usb2 -> ../../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2 │ ├── usbfs │ │ ├── 1-1:1.0 -> ../../../../devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-1/1-1:1.0 │ │ ├── bind │ │ ├── module -> ../../../../module/usbcore │ │ ├── new_id │ │ ├── remove_id │ │ ├── uevent │ │ └── unbind │ ├── usbhid │ │ ├── 2-1:1.0 -> ../../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-1/2-1:1.0 │ │ ├── bind │ │ ├── module -> ../../../../module/usbhid │ │ ├── new_id │ │ ├── remove_id │ │ ├── uevent │ │ └── unbind │ ├── usbserial │ │ ├── bind │ │ ├── module -> ../../../../module/usbserial │ │ ├── uevent │ │ └── unbind │ └── usbserial_generic │ ├── bind │ ├── module -> ../../../../module/usbserial │ ├── uevent │ └── unbind ├── drivers_autoprobe ├── drivers_probe └── uevent
linux中用usb_driver描述一個USB設備驅動。
/** * struct usbdrv_wrap - wrapper for driver-model structure * @driver: The driver-model core driver structure. * @for_devices: Non-zero for device drivers, 0 for interface drivers. */ struct usbdrv_wrap { struct device_driver driver; int for_devices; }; /** * struct usb_driver - identifies USB interface driver to usbcore * @name: The driver name should be unique among USB drivers, * and should normally be the same as the module name. * @probe: Called to see if the driver is willing to manage a particular * interface on a device. If it is, probe returns zero and uses * usb_set_intfdata() to associate driver-specific data with the * interface. It may also use usb_set_interface() to specify the * appropriate altsetting. If unwilling to manage the interface, * return -ENODEV, if genuine IO errors occurred, an appropriate * negative errno value. * @disconnect: Called when the interface is no longer accessible, usually * because its device has been (or is being) disconnected or the * driver module is being unloaded. * @unlocked_ioctl: Used for drivers that want to talk to userspace through * the "usbfs" filesystem. This lets devices provide ways to * expose information to user space regardless of where they * do (or don't) show up otherwise in the filesystem. * @suspend: Called when the device is going to be suspended by the * system either from system sleep or runtime suspend context. The * return value will be ignored in system sleep context, so do NOT * try to continue using the device if suspend fails in this case. * Instead, let the resume or reset-resume routine recover from * the failure. * @resume: Called when the device is being resumed by the system. * @reset_resume: Called when the suspended device has been reset instead * of being resumed. * @pre_reset: Called by usb_reset_device() when the device is about to be * reset. This routine must not return until the driver has no active * URBs for the device, and no more URBs may be submitted until the * post_reset method is called. * @post_reset: Called by usb_reset_device() after the device * has been reset * @id_table: USB drivers use ID table to support hotplugging. * Export this with MODULE_DEVICE_TABLE(usb,...). This must be set * or your driver's probe function will never get called. * @dynids: used internally to hold the list of dynamically added device * ids for this driver. * @drvwrap: Driver-model core structure wrapper. * @no_dynamic_id: if set to 1, the USB core will not allow dynamic ids to be * added to this driver by preventing the sysfs file from being created. * @supports_autosuspend: if set to 0, the USB core will not allow autosuspend * for interfaces bound to this driver. * @soft_unbind: if set to 1, the USB core will not kill URBs and disable * endpoints before calling the driver's disconnect method. * @disable_hub_initiated_lpm: if set to 1, the USB core will not allow hubs * to initiate lower power link state transitions when an idle timeout * occurs. Device-initiated USB 3.0 link PM will still be allowed. * * USB interface drivers must provide a name, probe() and disconnect() * methods, and an id_table. Other driver fields are optional. * * The id_table is used in hotplugging. It holds a set of descriptors, * and specialized data may be associated with each entry. That table * is used by both user and kernel mode hotplugging support. * * The probe() and disconnect() methods are called in a context where * they can sleep, but they should avoid abusing the privilege. Most * work to connect to a device should be done when the device is opened, * and undone at the last close. The disconnect code needs to address * concurrency issues with respect to open() and close() methods, as * well as forcing all pending I/O requests to complete (by unlinking * them as necessary, and blocking until the unlinks complete). */ struct usb_driver { const char *name; int (*probe) (struct usb_interface *intf, const struct usb_device_id *id); void (*disconnect) (struct usb_interface *intf); int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code, void *buf); int (*suspend) (struct usb_interface *intf, pm_message_t message); int (*resume) (struct usb_interface *intf); int (*reset_resume)(struct usb_interface *intf); int (*pre_reset)(struct usb_interface *intf); int (*post_reset)(struct usb_interface *intf); const struct usb_device_id *id_table; struct usb_dynids dynids; struct usbdrv_wrap drvwrap; unsigned int no_dynamic_id:1; unsigned int supports_autosuspend:1; unsigned int disable_hub_initiated_lpm:1; unsigned int soft_unbind:1; }; #define to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver)
在編寫新的USB設備驅動時,主要應該完成的工做是probe()和disconnect()函數,即探測和斷開函數,它們分別在設備被插入和拔出的時候被調用,用於初始化和釋放軟硬件資源。對usb_driver的註冊和註銷經過兩個函數完成:
/* * use these in module_init()/module_exit() * and don't forget MODULE_DEVICE_TABLE(usb, ...) */ extern int usb_register_driver(struct usb_driver *, struct module *, const char *); /* use a define to avoid include chaining to get THIS_MODULE & friends */ #define usb_register(driver) \ usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME) extern void usb_deregister(struct usb_driver *);
usb_driver結構體中的id_table成員描述了這個USB驅動所支持的USB設備列表,它指向一個usb_device_id數組,usb_device_id結構體用於包含USB設備的製造商ID、產品ID、產品版本、設備類、接口類等信息及其要匹配標誌成員match_flags(標明要與哪些成員匹配,包含DEV_LO、DEV_HI、DEV_CLASS、DEV_SUBCLASS、DEV_PROTOCOL、INT_CLASS、INT_SUBCLASS、INT_PROTOCOL)。可用以下宏生產相應usb_device_id
/** * USB_DEVICE - macro used to describe a specific usb device * @vend: the 16 bit USB Vendor ID * @prod: the 16 bit USB Product ID * * This macro is used to create a struct usb_device_id that matches a * specific device. */ #define USB_DEVICE(vend, prod) \ .match_flags = USB_DEVICE_ID_MATCH_DEVICE, \ .idVendor = (vend), \ .idProduct = (prod) /** * USB_DEVICE_VER - describe a specific usb device with a version range * @vend: the 16 bit USB Vendor ID * @prod: the 16 bit USB Product ID * @lo: the bcdDevice_lo value * @hi: the bcdDevice_hi value * * This macro is used to create a struct usb_device_id that matches a * specific device, with a version range. */ #define USB_DEVICE_VER(vend, prod, lo, hi) \ .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, \ .idVendor = (vend), \ .idProduct = (prod), \ .bcdDevice_lo = (lo), \ .bcdDevice_hi = (hi)
當USB核心檢測到某個設備的屬性和某個驅動程序的usb_device_id結構體所攜帶的信息一致時,這個驅動程序的probe()函數就被執行了。拔掉設備或者卸載掉驅動模塊後,USB核心就執行disconnect()函數來響應這個動做。
上述usb_driver結構體中的函數是USB設備驅動中與USB相關的部分,而USB只是一個總線,USB設備驅動真正的主體工做仍然是USB設備自己所屬類型的驅動,如字符設備、tty設備、塊設備、輸入設備等。所以USB設備驅動包含其做爲總線上掛接設備的驅動和自己所屬設備類型的驅動兩部分。
usb_driver起到了牽線的做用,即在probe()裏註冊相應的字符、tty設備,在disconnect()註銷相應的字符、tty設備。
usb_driver自己只是完成找到USB設備、管理USB設備鏈接和斷開的做用。
USB設備控制器(UDC)驅動指的是做爲其餘USB主機控制器外設的USB硬件設備上底層硬件控制器的驅動,該硬件和驅動負責將一個USB設備依附於一個USB主控制器上。例如,linux手機鏈接PC時,手機中的底層USB控制器行使USB設備控制器的功能,這時候在底層的是UDC驅動。
代碼位於drivers/usb/gadget。
USB OTG標準在徹底兼容USB2.0標準的基礎上,容許設備既可做爲主機,也可做爲外設操做,OTG新增了主機通令協議(HNP)和對話請求協議(SRP)。
在OTG中,初始主機設備稱爲A設備,外設稱爲B設備。可用電纜的鏈接方式來決定初始角色。兩用設備使用新型Mini-AB插座,從而使Mini-A插頭、Mini-B插頭和Mini-AB插座增添了第5個引腳(ID),以用於標識不一樣的電纜端點。Mini-AB插頭中的ID引腳接地,Mini-B插頭中的ID引腳浮空。當OTG設備檢測到接地的ID引腳時,標識默認的是A設備(主機),而檢測到ID引腳懸空的設備則認爲是B設備(外設)。系統一旦鏈接後,OTG的角色還能夠更換,以採用新的HNP協議。而SRP容許B設備請求A設備打開VBUS電源並啓動一次對話。一次OTG對話可經過A設備提供VBUS電源的時間肯定。
usb_otg的代碼通常在usb的phy端實現,位於drivers/usb/phy或drivers/usb/musb。
USB 請求塊(USB request block,urb)是USB設備驅動中用來描述與USB設備通訊所用的基本載體和核心數據結構,很是相似於網絡設備驅動中的sk_buff結構體,是USB主機與設備通訊的「電波」。
當USB設備接到USB控制器接口時,usb_core就檢測該設備的一些信息,如廠商ID和產品ID等等,並配置設備使其工做,這個過程稱爲BUS枚舉。USB設備從接入HUB到正常工做以前,都屬於設備枚舉階段。所謂設備枚舉,就是讓host控制器認識USB設備,併爲其準備資源,創建好主機與設備間的數據傳遞機制。該階段的工做,是USB通訊協議規定的,因此屬於ISO標準流程。
USB枚舉實例
對2440的USB HOST進行初始化完畢(主要包括對符合OHCI規範的寄存器的初始化—總線復位、中斷使能、清除中斷標誌、電源管理、內存指針寄存器的初始化,各類數據結構的初始化等),等待USB設備的插入,當2440檢測到有設備插入,就要對設備進行枚舉了。枚舉就至關於主機和設備創建鏈接的過程(接頭),Host向Device詢問一些東西,Device將自身的設備類型、如何進行通訊等報告給Host,這樣Host就知道怎麼對Device進行操做了。
枚舉的過程實際上只用到了總線的「控制傳輸(Control Transfer)」。這種傳輸方式一般用於配置/命令/狀態等情形,其中的設置操做setup和狀態操做status過程的數據包具備USB協議定義的數據結構,所以,控制傳輸只能經過消息管道進行。
一個完整的控制傳輸包括三個過程:
創建鏈接。
數據過程(可選) 。
狀態過程。
創建鏈接的過程都是由Host發起,它開始於一個Setup令牌包,後面緊跟一個DATA0包。若是是控制輸入傳輸,數據過程則爲輸入數據,如果控制輸出傳輸,則數據過程是輸出數據。
數據過程的可選型是指設置過程須要指定數據長度,若是指定爲0,則沒有數據過程。狀態過程跟在數據過程以後,狀態過程剛好和數據過程的數據傳輸方向相反,由於此階段主要是用來確認以前兩階段的全部數據都已經正確傳輸了。
下面就結合實例來看看枚舉的詳細過程:
控制2440向U盤發送第一個Setup包,內容是80 06 00 01 00 00 08 00,其中最後的00 08表示獲得DEVICE_DCESCRIPTOR的前8個字節,由於這個包的主要目的是要得到USB Device中端點0的最大包的大小(第8個字節),因此只須要8個字節就能夠了。USB Device返回的設備標識符爲12 01 10 01 00 00 00 40,下面咱們須要把0x40記錄下來,將其放到Endpoint Descriptor數據結構的DWORD0的MPS(bit16~bit32)塊中去。
接下來2440發送第二個Setup包,內容是00 05 01 00 00 00 00 00,這一次的做用是爲USB設備分配地址。若是USB Device接收並接受了此地址設置包,會返回一個長度爲0的數據包。主機接收到長度爲0的狀態包以後就會返回一個ACK給Device,Device再接收到這個ACK以後,就能夠啓用新地址了。這樣Device就獲得了一個惟一的設備地址,做爲主機通訊的惟一表示。
發送第三個Setup包,內容是80 06 00 02 00 00 09 00,此次是爲了獲取配置描述符集合的大小,此位位於讀回數據的第三個字節。U盤返回的數據爲09 02 20 00 01 01 00 80 32,即描述符集合總大小爲0x20。
發送第四個Setup包,內容是80 06 00 02 00 00 09 00,和上次不一樣的僅僅是,此次要讀回來的數據是整個配置描述符區域。U盤返回來的數據是09 02 20 00 01 01 00 80 32 09 04 00 00 02 08 06 50 00 07 05 82 02 40 00 00 07 05 02 02 40 00 00。這時候咱們就能夠知道該設備是什麼類型的設備,支持什麼樣的操做了。
上述這兩個過程也有的程序就是直接讀取0xff個字符大小,固然一樣能夠達到讀回設備描述符集合的目的。
至此,咱們已經獲得了所須要的設備信息,以後就能夠對設備進行配置了。
向設備發送第五個Setup包,數據爲00 09 01 00 00 00 00 00,USB Device返回一個長度爲0的數據包,代表數據正確接收。至此,USB枚舉過程就完成了。
多接口設備
多接口的器件驅動使得一個從USB設備能夠做爲多個設備同時工做,典型的就是GSM和CDMA模塊。3G或4G模塊常使用option.c驅動實現USB多接口功能,option.c就是GSM modems驅動。
此類設備提供的端口及功能,須要配置到設備的配置、接口等描述符中,並實現相應的功能(符合USB協議ISO標準)。
option.c:This driver is named "option" because the most common device it's used for is a PC-Card (with an internal OHCI-USB interface, behind which the GSM interface sits), made by Option Inc.
USB_SERIAL_OPTION: Say Y if you have a GSM or CDMA modem that's connected to USB.
參考: