一、相關概念:
1.什么是USB?
USB是Universal Serial Bus的縮寫,中文譯為通用串行總線。
那么與此USB相比,其他還有哪些非串行的總線,以及和此通用的串行總線來說,其他還有哪些相對“不通用”的串行總線呢?
從上述表格中,除了USB接口外,目前已存在的接口,還是很多的,而且各種接口實際上從硬件上也是形狀各異,互相也都有自己的應用領域,而且無法兼容。基于此背景,才有下面的解釋,以說明,為何會出現這么個USB接口。
2. 為何要有USB?
上面已經提到了,在USB出現之前,其實計算機領域中,已經存在眾多的接口,而且不同的應用領域,已有一些相對來說是廣泛使用的各種接口了。
但是,對于計算機等使用的普通用戶來說,由于接口太多,而容易被搞得暈頭轉向。再加上各個接口從硬件形狀和軟件配置也都不一樣,導致不兼容,為了不同的應用,而要配置多種不同的硬件接口,設置對于有些硬件接口來說,還需要手動去配置一些更細節的參數。
關于USB出現之前,計算機領域中的接口太多太繁雜,可以用下面這張,關于PC機箱背后的接口的圖片來說明:
圖 PC機箱后面的眾多接口
所以,總的來說,在USB出現之前,各種接口太多,而且都不太容易使用,互相之間的兼容性也較差,因此,才出現了USB。
而萬能的USB接口出現的話,整個PC機箱背后的接口,就不那么繁雜,顯得清靜多了:
圖 有了USB接口之后的PC機箱背后的接口
USB出現的初的目的,根據USB規范中的解釋,是為了:
(1)將PC和電話能連起來
由于大家都認識到,下一代的應用,肯定是實現計算機設備和通訊設備的完美融合。而且,為了實現移動領域內的人機數據的交互,也需要方便且不貴的連接方案。但是,計算機領域和通訊領域卻是各自為政的發展,沒有考慮互聯性。由此,USB的出現,就是為了解決這一類互聯問題的。
(2)方便用戶使用
以前的一些設備,多數不支持即插即用,而且很多設備還需要懂行的用戶去手動配置,然后才可以正常工作,而USB的出現,使得用戶不用關心設備的細節,不需要去另外再配置什么參數,直接插上就可以用了,而且還支持即插即用,很是方便。
(3)接口擴展性要好
之前的眾多接口,導致不同的應用,需要使用不同的接口,很是繁瑣。USB的出現,支持眾多的應用,都使用統一的USB的接口,方便了用戶,不需要再搞懂各種接口的用途和差異。
總的來說,USB的出現,是希望通過此單個的USB接口,同時支持多種不同的應用,而且用戶用起來也很方便,直接插上就能用了,也方便不同的設備的之間的互聯。
說白了,就相當于在之前眾多的接口之上,設計出一個USB這么個萬能的接口,以后各種外設,都可以用這一種接口即可。
這估計也是USB的名稱中的Universal通用的這一個詞的來歷吧。
3.USB控制器類型:OHCI,UHCI,EHCI,xHCI
【此時可以查看:drivers/usb/host/Kconfig】
OHCI:Open Host Controller Interface,創立者是Compaq,Microsoft和National Semiconductor。
UHCI:Universal Host Controller Interface,創立者是Intel。
兩者之間的相同點是:
不論是OHCI還是UHCI都是對應于USB 1.1的標準的,都是完全符合USB協議標準的。
區別在于:只是各自的實現方式有些略微不同而已。當然對應的具體的性能,也略有差別,具體的差異,和實際的應用有關系。
但是本身OHCI和UHCI的區別在于:雖然都是實現了USB1.1協議規范,但是在功能劃分上,OHCI更多地把要做的事情,用硬件來實現,因此,實現OHCI的USB控制器的軟件驅動的開發工作,相對要容易些,軟件要做的事情,相對較少。
對應地,OHCI更多地應用在擴展卡,尤其是嵌入式領域中,常見的很多開發板中的USB的控制器,很多都是OHCI的。
而UHCI把更多的功能,留給了軟件,相對來說,軟件做的事情,即負擔要重些。但是實現對應的UHCI的硬件的USB控制器,價格上,就相對便宜些。
對應地,UHCI更多地應用在PC機中的主板上的USB控制器。
EHCI:定義了USB 2.0的主機控制器的規范,定義了USB 2.0的主控,需要包括哪些硬件實現,需要實現哪些功能,其也對應著對應的系統軟件,所面對的是哪些接口
xHCI:Extensible Host Controller Interface,同EHCI是針對USB 2.0類似,xHCI是針對的USB 3.0規范。
4.USB的接口(connector)類型
由于USB的產生就是為了支持眾多種應用的,而由于各種應用中,對于硬件接口的大小也有一些限制,比如有些小型設備或者移動式設備中,接口不能太大等,所以而設計出多種類型的接口,用于不同的應用。
在介紹插頭和插座之前,先多解釋一下,基本的叫法。
插頭,plug,對應的也叫公口;
插座,receptacle,對應也叫做母口;
下面就來簡單的介紹一下不同的USB接口類型,即各種不同的插頭插座:
USB的接口類型,根據接口形狀不同,主要可以分為三大類:
普通的硬件直接叫做Type
然后有小型版本的叫Mini迷你的
和更加小的,叫做Micro微小的
其中每一種大類中,又都可以分為兩類
A類(Type A)
B類(Type B)
下面就用表格的形式,詳細對比USB的各種接口,包括對應的插頭和插座
5.USB系統架構
(1)拓撲結構
USB的結構主要包括了 USB 連接、 USB host controller和 USB device 三個部分。 而 USB device 還包括了 hub 和功能設備,什么是 USB controller?在一個 USB 系統中只能有一個 host,其實說白了就是咱們的主機,而 USB 和主機的接口就是 host controller,一個主機可以支持多個 host controller,比如分別屬于不同廠商的。那么 USB host controller 本身是做什么的? controller,控制器,顧名思義,用于控制,控制什么,控制所有的 usb 設備的通信。通常計算機的 cpu 并不是直接和 usb 設備打交道,而是和控制器打交道,他要對設備做什么,他會告訴控制器,而不是直接把指令發給設備, 然后控制器再去負責處理這件事情, 他會去指揮設備執行命令,而 cpu 就不用管剩下的事情,他還是該干嘛干嘛去,控制器替他去完成剩下的事情,事情辦完了再通知 cpu。
一個 usb 控制器和一個 hub 綁定在一起,專業一點說叫集成,而這個 hub 也被稱作 root hub, 其它的 hub 可以連接到她這里,然后可以延伸出去,外接別的設備,當然也可以不用別的 hub,讓 usb 設備直接接到 root hub 上。
USB 連接指的就是連接 device 和 host(或 hub)的四線電纜。
6.USB協議
USB總線是一種輪詢方式的總線。 協議規定所有的數據傳輸都必須由主機發起, host controller初始化所有的數據傳輸,各種設備緊緊圍繞在主機周圍。
USB 通信基本的形式是通過 USB 設備里一個叫 endpoint 的東東,而主機和 endpoint之間的數據傳輸是通過 pipe。 endpoint 就是通信的發送或者接收點,你要發送數據,那你只要把數據發送到正確的端點那里就可以了。端點也是有方向的,從 usb 主機到設備稱為out 端點,從設備到主機稱為 in 端點。嚴格來說,管道的另一端應該是 usb 主機,即前面說的那個 host,usb 協議里邊也是這么說的,協議里邊說pipes 代表著一種能力,怎樣一種能力呢,在主機和設備上的端點之間移動數據。
為什么端點 0 就非要那么的個性那?這還是有內在原因的。管道的通信方式其實有兩種,一種是 stream 的,一種是 message 的, message 管道要求從它那兒過的數據必須具有一定的格式,不是隨便傳的,因為它主要就是用于主機向設備請求信息的,必須得讓設備明白請求的是什么。而 stream 管道就沒這么苛刻,要隨和多了,它對數據沒有特殊的要求。
協議里說, message 管道必須對應兩個相同號碼的端點,一個用來 in,一個用來 out,咱們的缺省管道就是 message 管道,當然,與缺省管道對應的端點 0 就必須是兩個具有同
樣端點號 0 的端點。
USB endpoint 有四種類型,也就分別對應了四種不同的數據傳輸方式。它們是控制傳輸
( Control Transfers),中斷傳輸( Interrupt Data Transfers),批量傳輸(Bulk DataTransfers),等時傳輸(Isochronous Data Transfers)。
控制傳輸:用來控制對 USB 設備不同部分的訪問,通常用于配置設備,獲取設備信息,發送命令到設備,或者獲取設備的狀態報告。總之就是用來傳送控制信息的,每個 USB 設備都會有一個 endpoint 0 的控制端點,內核里的 USB core 使用它在設備插入時進行設備的配置。
中斷傳輸:用來以一個固定的速率傳送少量的數據,USB 鍵盤和 USB 鼠標使用的就是這種方式, USB 的觸摸屏也是,傳輸的數據包含了坐標信息。
批量傳輸:用來傳輸大量的數據,確保沒有數據丟失,并不保證在特定的時間內完成。 U 盤使用的就是批量傳輸,咱們用它備份數據時需要確保數據不能丟,而且也不能指望它能在一個固定的比較快的時間內拷貝完。
等時傳輸:同樣用來傳輸大量的數據,但并不保證數據是否到達,以穩定的速率發送和接收實時的信息,對傳送延遲非常敏感。顯然是用于音頻和視頻一類的設備,這類設備期望能夠有個比較穩定的數據流。
7.USB設備框架
在USB設備的邏輯組織中,包含設備、配置、接口和端點 4 個層次。
每個USB設備都提供了不同級別的配置信息,可以包含一個或多個配置,不同的配置使設備表現出不同的功能組合;
配置代表一個基本的功能,由多個接口組成,接口由多個端點組成,接口是 USB 設備驅動程序控制的對象,一個功能復雜的 USB 設備可以具有多個接口。每個配置中可以有多個接口,而設備接口是端點的匯集( collection)。
接口是設備的接口。設備可以有多個接口,每個接口代表一個功能,每個接口對應著一個驅動。例如:USB揚聲器,可以包含一個音頻接口以及對旋鈕和按鈕的接口。一個配置中的所有接口可以同時有效,并可被不同的驅動程序連接。每個接口可以有備用接口,以提供不同質量的服務參數。
(1)設備通常有一個或多個配置;
(2)配置通常有一個或多個接口;
(3)接口通常有一個或多個設置;
(4)接口有零或多個端點。
分析各個結構體:
結構體分析:
struct usb_device_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__le16 bcdUSB;//USB版本號
__u8 bDeviceClass;//類
__u8 bDeviceSubClass;//子類
__u8 bDeviceProtocol;//協議
__u8 bMaxPacketSize0;//端點0的大包數量
__le16 idVendor;//廠家ID
__le16 idProduct;//產品ID
__le16 bcdDevice;
__u8 iManufacturer;
__u8 iProduct;
__u8 iSerialNumber;
__u8 bNumConfigurations;//配置的個數
} __attribute__ ((packed));
此時查看設備中的USB設備中的屬性,這些屬性的獲得是通過發出命令,獲取該結構體信息;除了設備描述符還有那些描述符呢?
配置描述符:
struct usb_config_descriptor {
__u8 bLength;//該描述符的長度
__u8 bDescriptorType;//類型
__le16 wTotalLength;//該配置下所有信息的總長度
__u8 bNumInterfaces;//接口個數
__u8 bConfigurationValue;
__u8 iConfiguration;
__u8 bmAttributes;//屬性
__u8 bMaxPower;//消耗的電源
} __attribute__ ((packed));
接口描述符:----->邏輯上的設備---->功能
例如:usb聲卡,硬件上只有一個聲卡,但是其邏輯功能既可以錄音又可以播放,故此時需要用兩個接口描述符對其進行描述;驅動寫的是邏輯上的設備
struct usb_interface_descriptor {
__u8 bLength;//長度
__u8 bDescriptorType;
__u8 bInterfaceNumber;//接口個數
__u8 bAlternateSetting;
__u8 bNumEndpoints;//端點個數
__u8 bInterfaceClass;
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
__u8 iInterface;
} __attribute__ ((packed));
端點描述符:
struct usb_endpoint_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bEndpointAddress;//端點地址
__u8 bmAttributes;//屬性,端點類型:輸入/輸出/批量/控制等
__le16 wMaxPacketSize;//大包的大小
__u8 bInterval;//端點被查詢的頻繁程度
/* NOTE: these two are _only_ in audio endpoints. */
/* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
__u8 bRefresh;
__u8 bSynchAddress;
} __attribute__ ((packed));
8.usb設備的鏈接
將 usb 設備連接在 hub 的某個端口上, hub 檢測到有設備連接了進來,就覺得有必要為設備做點什么。它會為設備分配一個 struct usb_device (usb_alloc_dev )結構的對象并初始化,并調用設備模型提供的接口將設備添加到 usb 總線的設備列表里,然后 usb 總線會遍歷驅動列表里的每個驅動,調用自己的 match 函數看它們和你的設備或接口是否匹配。
1.由電腦上插接usb設備引出相應概念
(1)右下角彈出“發現xxx設備”;
(2)若未安裝驅動程序,則自行安裝驅動;
匹配圖:
問題1.既然還沒有“相應設備的驅動程序”為何能知道“phone”?
答:windows中已經有了USB的總線驅動程序,接入USB設備后,是“總線驅動程序”指導設備識別“phone”并提示安裝“設備驅動程序”;“usb總線驅動”負責識別USB設備,給USB設備找到對應的驅動程序;
問題2.USB設備種類非常多,為什么一接入電腦,就能識別出來?
答:PC和USB都得遵守一些規范;
比如:USB設備接入電腦后,PC機會發出“你是什么”?
USB設備就必須回答“我是xxx”.并且回答的格式也得一致;
USB總線驅動程序會發出某些命令想獲取設備信息(描述符)。
USB設備必須返回“描述符”給PC.
引出USB驅動框架:
APP:
設備驅動:--->usb設備
(1)知道收發數據的具體含義
usb驅動:
USB總線驅動:--->usb主機控制器
(1)識別設備
(1.1)分配編號
(1.2)并告訴USB設備(set address)
(1.3)發出命令獲取描述符
(2)找到并安裝對應的USB設備驅動
(3)提供USB讀寫函數
USB主機控制器
UHCI OHCI EHCI
硬件:
USB設備
使用USB總線控制器程序的函數把包發送給USB主機控制器,由USB主機控制器產生信號發送給USB設備;
UHCI:intel,低速usb1.1(1.5mbps)/全速usb2.0(12mbps)(全速/高速)
OHCI:microsoft,低速/全速
EHCI:高速(480mbps)
當插入一個USB設備時,主機控制器會因為d+/d-出現電平變化,主機控制器會產生一個中斷——hub_irq();
問題3.PC機上有非常多的USB設備,怎么分辨他們?
答:USB接口只有四根線:5v ,gnd , d+ , d-
每個USB設備接入PC時,USB總線驅動程序都會給它分配一個編號,接在USB總線上的每一個USB設備都有自己的編號(地址),pc機想訪問某個USB設備時,發出的命令都含有對應的編號。
問題4.USB設備剛接入PC時,還沒有編號,那么PC怎么把“分配的編號”告訴它?
答:新接入的USB設備的默認編號是0,在未分配新編號前,PC使用0和他進行通信。
問題5.為什么一接入USB設備,pc機就能發現它?
答:PC的USB口內部,D-和D+接有15K的下拉電阻,未接入USB設備時為低電平。
USB設備的USB口內部,D+或D-接有1.5K的上拉電阻,它一接入PC就會把PC的USB口的D+/D-拉高,從而從硬件的角度通知PC有usb設備接入。
2其他概念:
(1)usb是主從結構的:所有usb的傳輸都是從USB主機這方發起,USB設備沒有主動通知USB主機的能力;
例如:usb鼠標滑動一下立刻產生數據,但是他沒有能力主動通知PC來讀取數據,只能被動地等待pc機讀取,PC連續不斷地進行查詢;
(2)USB傳輸類型
a.U盤:批量傳輸,可靠,但是時間沒有保證;
b.USB鼠標:中斷傳輸,實時,可靠;
c.USB攝像頭:實時傳輸,實時,數據不可靠;
d.usb設備識別:控制傳輸,可靠,時間有保證;
3.USB傳輸的對象:端點(endpoint)
“讀U盤”:把數據寫到U盤的端點1;
“寫U盤”:從U盤的端點2讀出數據;
除了端點0外,每一個端點只支持一個方向數據傳輸;
端點0用于控制傳輸(識別USB設備);既能輸出也能輸入;
4.每一個端點都有傳輸類型,傳輸方向
5.程序中,術語中所說的輸入IN/輸出OUT都是基于USB主機的立場而言的;
比如:鼠標:鼠標------數據------>PC機 輸入端點
6. 既然USB總線驅動程序已經有了USB的讀寫函數,那么應用程序能否跨過USB設備驅動,直接通過這些讀寫函數直接訪問對應的USB設備?
答:可以,通過libusb
當USB設備插入主機,則USB主機控制器會出現電平變化,則此時就會出現一個中斷,參考:/driver/usb/core/hub.c
int usb_hub_init(void)
khubd_task = kthread_run(hub_thread, NULL, "khubd");
static int hub_thread(void *__unused)
hub_events();
hub_port_connect_change(hub, i,portstatus, portchange);
udev = usb_alloc_dev(hdev, hdev->bus, port1);
dev->dev.bus = &usb_bus_type;
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
};
static int usb_device_match(struct device *dev, struct device_driver *drv)
intf = to_usb_interface(dev);
id = usb_match_id(intf, usb_drv->id_table);
id = usb_match_dynamic_id(intf, usb_drv);
choose_devnum(udev);
devnum = find_next_zero_bit(bus->devmap.devicemap, 128,
bus->devnum_next);
devnum = find_next_zero_bit(bus->devmap.devicemap,128, 1);
bus->devnum_next = (devnum >= 127 ? 1 : devnum + 1);
status = hub_port_init(hub, udev, port1, i);
retval = hub_set_address(udev, devnum);
retval = usb_get_device_descriptor(udev, 8);//獲取設備描述符的前八 //個成員
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
status = usb_new_device(udev);
wait_event_freezable(khubd_wait,!list_empty(&hub_event_list) ||
kthread_should_stop());
9.USB的設備驅動
USB設備驅動指的是從主機角度來觀察,怎樣訪問被插入的USB設備,而不是指USB設備內部本身運行的固件程序,usbfs ——“USB device filesystem”,動態跟蹤總線上插入和移除的設備,通過它可以查看系統中 USB 設備的信息,包括拓撲、帶寬、設備描述符信息、產品 ID、字符串描述符、配置描述符、接口描述符、端點描述符等。
在上面已經分析過當USB設備插入時,usb主機會對該USB進行相應的端點分配及設備描述符識別,此時相當于設備信息已經被提供了。在編寫USB驅動時主要是實現struct usb_driver的分配,填充及注冊等;
1 struct usb_driver {
2 const char *name; /* 驅動名稱 */
3 int (*probe) (struct usb_interface *intf,
4 const struct usb_device_id *id); /*探測函數*/
5 void (*disconnect) (struct usb_interface *intf); /*斷開函數*/
6 int (*ioctl) (struct usb_interface *intf, unsigned int code,
7 void *buf); /* I/O 控制函數 */
8 int (*suspend) (struct usb_interface *intf, pm_message_t message);/*掛起函數*/
9 int (*resume) (struct usb_interface *intf); /* 恢復函數 */
10 int (*reset_resume)(struct usb_interface *intf);
11 void (*pre_reset) (struct usb_interface *intf);
12 void (*post_reset) (struct usb_interface *intf);
13 const struct usb_device_id *id_table;/* usb_device_id 表指針 */
14 struct usb_dynids dynids;
15 struct usbdrv_wrap drvwrap;
16 unsigned int no_dynamic_id:1;
17 unsigned int supports_autosuspend:1;
18 unsigned int soft_unbind:1;
19 };
在編寫新的 USB 設備驅動時,主要應該完成的工作是 probe()和 disconnect()函數,即探測和斷開函數,它們分別在設備被插入和拔出的時候被調用,用于初始化和釋放軟硬件資源。對usb_driver 的注冊和注銷通過這兩個函數完成:
(1)int usb_register(struct usb_driver *new_driver);
功能:注冊usb驅動
參數:struct usb_driver :struct usb_driver 結構體指針
返回值:成功: 0 失敗:錯誤碼的絕對值
(2)void usb_deregister(struct usb_driver *driver);
功能:注銷usb驅動
參數:struct usb_driver :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(vendor, product);
該宏根據制造商 ID 和產品 ID 生成一個 usb_device_id 結構體的實例,在數組中增加該元素將意味著該驅動可支持匹配制造商 ID、產品 ID 的設備。
USB_DEVICE_VER(vendor, product, lo, hi);
該宏根據制造商 ID、產品 ID、產品版本的小值和大值生成一個 usb_device_id 結構體的實例,在數組中增加該元素將意味著該驅動可支持匹配制造商 ID、產品 ID 和 lo~hi 范圍內版本的設備。
USB_DEVICE_INFO(class, subclass, protocol);
該宏用于創建一個匹配設備指定類型的 usb_device_id 結構體實例。
USB_INTERFACE_INFO(class, subclass, protocol)
該宏用于創建一個匹配接口指定類型的 usb device id 結構體實例。
當 USB 核心檢測到某個設備的屬性和某個驅動程序的 usb_device_id 結構體所攜帶的信息一致時,這個驅動程序的 probe()函數就被執行。拔掉設備或者卸掉驅動模塊后, USB 核心就執行disconnect()函數來響應這個動作。
上述 usb_driver 結構體中的函數是 USB 設備驅動中 USB 相關的部分,而 USB 只是一個總線,真正的 USB 設備驅動的主體工作仍然是 USB 設備本身所屬類型的驅動,如字符設備、 tty 設備、塊設備、輸入設備等。因此 USB 設備驅動包含其作為總線上掛在設備的驅動和本身所屬設備類型的驅動兩部分。
與 platform_driver 類似, usb_driver 起到了“牽線”的作用,即在 probe()里注冊相應的字符、tty 等設備,在 disconnect()注銷相應的字符、 tty 等設備,而原先對設備的注冊和注銷一般直接發生在模塊加載和卸載函數中。
盡管 USB 本身所屬設備驅動的結構與其不掛在 USB 總線上時完全相同,但是在訪問方式上卻發生了很大的變化,例如,對于 USB 接口的字符設備而言,盡管仍然是 write()、 read()、 ioctl()這些函數,但是在這些函數中,貫穿始終的是稱為 URB 的 USB 請求塊。
usb_driver 本身只是起到了找到 USB 設備、管理 USB 設備連接和斷開的作用。
程序編寫流程:
1.定義struct usb_driver
2.設置struct usb_driver
3.注冊
4.硬件相關的操作