天天看點

Linux下USB驅動架構分析​

1.1 USB簡介​

1.1.1 USB總線介紹​

USB是連接配接計算機系統與外部裝置的一種序列槽總線标準,也是一種輸入輸出接口的技術規範,被廣泛地應用于個人電腦和移動裝置等資訊通訊産品,USB就是簡寫,中文叫通用串行總線。最早出現在1995年,伴随着奔騰機發展而來。自微軟在Windows 98中加入對USB接口的支援後,USB接口才推廣開來,USB裝置也日漸增多,如數位相機、攝像頭、掃描器、遊戲杆、列印機、鍵盤、滑鼠等等,其中應用最廣的就是攝像頭和U盤了。​

USB包括老舊的USB 1.1标準和時下正流行的USB 2.0标準。傳統的USB 1.1最高傳輸速率為12Mbps,一般廠商将其符合USB 1.1标準的産品稱為“全速USB”。而高速USB 2.0最初推出時最高傳輸速率僅為240Mbps,後來USB2.0推廣組(USB Promoter Group)在1999年10月将該速率提高到480Mbps,比傳統的USB 1.1快40倍。​

USB2.0向下相容USB 1.1,當然USB1.1裝置也“向上相容”USB 2.0,但是無法實作USB2.0的傳輸能力,并自動以低速傳輸。USB 2.0連接配接線的最大長度為5米,但如果用五個USB擴充卡,則最大長度可達30米。​

最新一代是USB 3.1,傳輸速度為10Gbit/s,三段式電壓5V/12V/20V,最大供電100W ,新型Type C插型不再分正反。​

Linux下USB驅動架構分析​
Linux下USB驅動架構分析​

USB采用四線電纜,其中兩根是用來傳送資料的串行通道,另兩根為下遊(Downstream)裝置提供電源,對于高速且需要高帶寬的外設,USB以全速12Mbps的傳輸資料;對于低速外設,USB則以1.5Mbps的傳輸速率來傳輸資料。USB總線會根據外設情況在兩種傳輸模式中自動地動态轉換。USB是基于令牌的總線。類似于令牌環網絡或FDDI基于令牌的總線。USB主要制器廣播令牌,總線上裝置檢測令牌中的位址是否與自身相符,通過接收或發送資料給主機來響應。USB通過支援懸挂/恢複操作來管理USB總線電源。USB系統采用級聯星型拓撲,該拓撲由三個基本部分組成:主機(Host),集線器(Hub)和功能裝置。​

主機,也稱為根,根結或根Hub,它做在主機闆上或作為适配卡安裝在計算機上,主機包含有主要制器和根集線器(Root Hub),控制着USB總線上的資料和控制資訊的流動,每個USB系統隻能有一個根集線器,它連接配接在主要制器上。​

集線器是USB結構中的特定成分,它提供叫做端口(Port)的點将裝置連接配接到USB總線上,同時檢測連接配接在總線上的裝置,并為這些裝置提供電源管理,負責總線的故障檢測和恢複。集線可為總線提供能源,亦可為自身提供能源(從外部得到電源),自身提供能源的裝置可插入總線提供能源的集線器中,但總線提供能源的裝置不能插入自身提供能源的集線器或支援超過四個的下遊端口中,如總線提供能源裝置的需要超過100mA電源時,不能同總線提供電源的集線器連接配接。​

USB介紹: ​​http://www.usb.org/home​​​

  • USB裝置主要具有以下優點:​1. 可以熱插拔。就是使用者在使用外接裝置時,不需要關機再開機等動作,而是在電腦工作時,直接将USB插上使用。​

    2. 攜帶友善。USB裝置大多以“小、輕、薄”見長,對使用者來說,随身攜帶大量資料時,很友善。當然USB硬碟是首要之選了。​

    3. 标準統一。大家常見的是IDE接口的硬碟,序列槽的滑鼠鍵盤,并口的列印機掃描器,可是有了USB之後,這些應用外設統統可以用同樣的标準與個人電腦連接配接,這時就有了USB硬碟、USB滑鼠、USB列印機等等。​

    4. 可以連接配接多個裝置。USB在個人電腦上往往具有多個接口,可以同時連接配接幾個裝置,如果接上一個有四個端口的USB HUB時,就可以再連上;四個USB裝置,以此類推,盡可以連下去,将你家的裝置都同時連在一台個人電腦上而不會有任何問題(注:最高可連接配接至127個裝置)​

  • USB接口定義顔色一般的排列方式是:紅白綠黑從左到右

    定義:

  1. 紅色-USB電源: 标有-VCC、Power、5V、5VSB字樣​
  2. 白色-USB資料線:(負)-DATA-、USBD-、PD-、USBDT-​
  3. 綠色-USB資料線:(正)-DATA+、USBD+、PD+、USBDT+​
  4. 黑色-地線: GND、Ground​

1.1.2 USB接口HID裝置​

HID(Human Interface Device,人機接口裝置)是USB裝置中常用的裝置類型,是直接與人互動的USB裝置,例如鍵盤、滑鼠與遊戲杆等。在USB裝置中,HID裝置的成本較低。另外,HID裝置并不一定要有人機互動功能,隻要符合HID類别規範的裝置都是HID裝置。​

  • HID裝置的特點​

HID裝置交換的資料儲存在稱為報表(Report)的結構内,裝置的固件必須支援HlD報表的格式。主機通過控制和中斷傳輸中的傳送和請求報表來傳送和接收資料,報表的格式非常靈活。每一筆事務可以攜帶小量或中量的資料。低速裝置每一筆事務最大是8B,全速裝置每一筆事務最大是64B,高速裝置每一筆事務最大是1024B。一個報表可以使用多筆事務。裝置可以在未預期的時間傳送資訊給主機,例如鍵盤的按鍵或是滑鼠的移動。是以 主機會定時輪詢裝置,以取得最新的資料。 Ÿ​

HID裝置的最大傳輸速度有限制。主機可以保證低速的中斷端點每10ms内最多1 筆事務,每一秒最多是800B。保證全速端點每lms一筆事務,每一秒最多是64000B。保證高速端點每125 us三筆事務,每一秒最多是24.576MB。HID裝置沒有保證的傳輸速率。如果裝置是設定在10ms的時距,事務之間的時間可能等于或小于10ms。除非裝置是設定在全速時在每個幀傳輸資料,或是在高速時在每個微幀傳輸資料。這是最快的輪詢速率,是以端點可以保證有正确的帶寬可供使用。HID裝置除了傳送資料給主機外,它也會從主機接收資料。隻要能夠符合HlD類别規範的裝置都可以是HID裝置。 ​

裝置除了HlD接口之外,它可能同時還包含有其他的USB接口。例如:影像顯示裝置可能使用HID接口來做亮度、對比度的軟體控制,而使用傳統的影像接口來傳送要顯示的資料。USB擴音器可以使用實時傳輸來播放語音,同時使用HID接口來控制音量、低音等。 ​

HID類别裝置的規範檔案主要是以下兩份: Ÿ ​

  1. Device Class Definition for Human interface Devices Ÿ ​
  2. HID Usage Tables ​

其中前者是HID的基本規範檔案,後者可以是前者的附件,為開發人員提供實際的控制類型的描述。檔案是用來定義讓主機了解以及使用HID資料的數值。這兩份檔案是由 USB Device Working Group制定的,可以在網址http://www.usb.org/developers/hidpage/ #Class _Definition下載下傳。​

滑鼠驅動源碼路徑:\drivers\hid\usbhid\usbmouse.c ​

鍵盤驅動源碼路徑: \drivers/usb/input/usbkbd.c​

USB轉序列槽源碼:/drivers/usb/serial/​

USB藍牙源碼:/drivers/bluetooth/​

USB攝像頭源碼:/drivers/media/video/uvc/​

USB總線定義: \drivers\usb\core\ driver.c​

USB總線核心結構定義檔案:\include\linux\usb.h​

USB注冊: \drivers\usb\core\usb.c​

1.1.1 USB 構造​

1.1.2 USB裝置斷點描述符​

USB 通訊的最基本形式是通過一個稱為端點的東西。一個USB端點隻能向一個方向傳輸資料(從主機到裝置(稱為輸出端點)或者從裝置到主機(稱為輸入端點))。端點可被看作一個單向的管道。​

一個 USB 端點有 4 種不同類型, 分别具有不同的資料傳送方式:​

  • 控制CONTROL ​控制端點被用來控制對 USB 裝置的不同部分通路. 通常用作配置裝置、擷取裝置資訊、發送指令到裝置或擷取裝置狀态報告。這些端點通常較小。每個 USB 裝置都有一個控制端點稱為"端點 0", 被 USB 核心用來在插入時配置裝置。USB協定保證總有足夠的帶寬留給控制端點傳送資料到裝置.​
  • 中斷INTERRUPT ​每當 USB 主機向裝置請求資料時,中斷端點以固定的速率傳送小量的資料。此為USB 鍵盤和滑鼠的主要的資料傳送方法。它還用以傳送資料到 USB 裝置來控制裝置。通常不用來傳送大量資料。USB協定保證總有足夠的帶寬留給中斷端點傳送資料到裝置.​
  • 批量BULK​批量端點用以傳送大量資料。這些端點常比中斷端點大得多. 它們普遍用于不能有任何資料丢失的資料。USB 協定不保證傳輸在特定時間範圍内完成。如果總線上沒有足夠的空間來發送整個BULK包,它被分為多個包進行傳輸。這些端點普遍用于列印機、USB Mass Storage和USB網絡裝置上。​
  • 等時ISOCHRONOUS 等時端點也批量傳送大量資料, 但是這個資料不被保證能送達。這些端點用在可以處理資料丢失的裝置中,并且更多依賴于保持持續的資料流。如音頻和視訊裝置等等。

    控制和批量端點用于異步資料傳送,而中斷和同步端點是周期性的。這意味着這些端點被設定來在固定的時間連續傳送資料,USB 核心為它們保留了相應的帶寬。

    端點在核心中使用結構 struct usb_host_endpoint 來描述,它所包含的真實端點資訊在另一個結構中:struct usb_endpoint_descriptor(端點描述符,包含所有的USB特定資料)。

1.1.3 USB總線的注冊​

在\drivers\usb\core\ driver.c檔案的最後幾行:​

struct bus_type usb_bus_type = {​

.name ="usb",​

.match =usb_device_match, //用來比對USB裝置端和驅動端的裝置ID和類型​

.uevent =usb_uevent,​

};​

1.2 USB驅動端的注冊​

1.2.2 USB驅動的注冊​

#define usb_register(driver) \​

usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)​

功能:注冊USB裝置驅動端。注冊成功後,USB裝置端就可以與驅動端進行比對了。​

涉及到的頭檔案:usb.h​

  • struct usb_driver原型如下:

struct usb_driver {​

const char *name; //USB裝置的名字,名稱可以随便填寫。​

int (*probe) (struct usb_interface *intf, const struct usb_device_id *id);資源探索函數,當usB驅動端與裝置端比對成功的時候調用。​

void (*disconnect) (struct usb_interface *intf); //USB裝置斷開時調用。​

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; //USB裝置的比對ID清單​

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

};​

紅色部分為必填成員。​

  • struct usb_device_id 裝置ID結構原型如下:

struct usb_device_id {​

/* 确定裝置資訊去和結構體中哪幾個字段比對來判斷驅動的适用性,比如是否是HID協定等*/​

__u16match_flags;​

/*用于特定于産品的比對 */​

__u16idVendor; /*USB裝置的制造商ID,須向www.usb.org申請*/​

__u16idProduct; // USB裝置的産品ID,有制造商自定​

__u16bcdDevice_lo; /* USB裝置的産品版本号最低值*/​

__u16bcdDevice_hi; /* 和最高值,以BCD碼來表示。*/​

/* 分别定義裝置的類,子類和協定,他們由 USB 論壇配置設定并定義在 USB 規範中. ​

這些值指定這個裝置的行為, 包括裝置上所有的接口 */ ​

__u8bDeviceClass;​

__u8bDeviceSubClass;​

__u8bDeviceProtocol;​

/* 分别定義單個接口的類,子類和協定,他們由 USB 論壇配置設定并定義在 USB 規範中 */​

__u8bInterfaceClass;​

__u8bInterfaceSubClass;​

__u8bInterfaceProtocol;​

/* 這個值不用來比對驅動的, 驅動用它來在 USB 驅動的探測回調函數中區分不同的裝置 ​

該成員一般來儲存一個結構體指針,存放該裝置特殊的資料​

*/​

kernel_ulong_tdriver_info;​

  • 填充struct usb_device_id結構體中的__u16match_flags成員用到的宏定義:

#define USB_DEVICE_ID_MATCH_VENDOR0x0001 /*供應商廠家ID*/​

#define USB_DEVICE_ID_MATCH_PRODUCT0x0002産品ID*/​

  • 快速填充usb_device_id結構體的相關宏:

USB_DEVICE_ID_MATCH_INT_INFO --根據接口資訊​

USB_DEVICE_ID_MATCH_DEV_INFO --根據裝置的資訊​

USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION --根據裝置制造資訊和版本​

USB_DEVICE_ID_MATCH_DEV_RANGE --根據裝置版本​

USB_DEVICE_ID_MATCH_DEVICE 根據裝置制造資訊​

  • struct usb_device_id結構體填充示例1—(摘自DM9620-USB網卡)

static const struct usb_device_id products[] = {​

{​

USB_DEVICE(0x07aa, 0x9601),/* Corega FEther USB-TXC */​

.driver_info = (unsigned long)&dm9620_info,​

}, {​

USB_DEVICE(0x0a46, 0x9601),/* Davicom USB-100 */​

.driver_info = (unsigned long)&dm9620_info,​

},​

}​

  • struct usb_device_id結構體填充示例2------->(摘自核心自帶的滑鼠驅動)​

static struct usb_device_id usb_mouse_id_table [] = {​

{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,​

USB_INTERFACE_PROTOCOL_MOUSE) },​

{ }/* 結束 */​

};​

  • USB滑鼠USB驅動端結構體填充示例:​

static struct usb_driver usb_mouse_driver = {​

.name= "usbmouse", /*滑鼠的名稱-不是比對使用*/​

.probe= usb_mouse_probe,資源探測函數-比對成功調用*/​

.disconnect= usb_mouse_disconnect,/*當滑鼠斷開連接配接調用*/​

.id_table= usb_mouse_id_table,比對驅動使用*/​

};​

1.2.5 USB裝置的登出

void usb_deregister(struct usb_driver *driver)​

1.2.6 USB注冊架構示例

#include <linux/init.h>​

#include <linux/module.h>​

#include <linux/usb.h>​

//定義USB的IDTAB​

static const struct usb_device_id usbtest[] = {​

{//148f:760b​

USB_DEVICE(0x148f, 0x760b),/*360WIFI的制造商ID和産品ID */​

},​

};​

//USB裝置資訊與驅動端比對成功的時候調用。​

static int test_probe(struct usb_interface *intf,const struct usb_device_id *id) //資源探索函數​

{​

printk("USB 驅動比對成功!\n");​

return 0;​

}​

//USB斷開的時候調用​

static void test_disconnect(struct usb_interface *intf)​

{​

printk("USB 裝置釋放成功!\n"); ​

}​

//定義USB驅動結構體 ​

static struct usb_driver usbtest_driver = {​

.name = "tiny4412_usbtest",​

.id_table = usbtest,​

.probe = test_probe,​

.disconnect = test_disconnect​

};​

static int __init usbtest_init(void)​

{​

//注冊USB裝置驅動​

usb_register(&usbtest_driver);​

return 0;​

}​

module_init(usbtest_init);​

static void __exit usbtest_exit(void)​

{​

//登出USB裝置驅動​

usb_deregister(&usbtest_driver);​

}​

module_exit(usbtest_exit);​

MODULE_AUTHOR("xiaolong");​

MODULE_LICENSE("GPL");​

1.3 USB資料傳輸

USB系統的資訊傳輸就是打成URB結構,然後再過行傳送的,URB的全稱叫USB request block。​

1.3.1 建立urb接口

struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)​

這個函數有兩個參數,一個是iso_packets.僅僅用于ISO傳輸.表示ISO資料包個數,如果用于其它類型的傳輸,此參數為0.另一個是mem_flags.是配置設定記憶體的參數。​

示例:​

struct urb *urb_1;​

urb_1= usb_alloc_urb(0, GFP_KERNEL);​

1.3.2 初始化USB中斷

static inline void usb_fill_int_urb(struct urb *urb,​

struct usb_device *dev,​

unsigned int pipe, //管道端點​

void *transfer_buffer, //資料的緩沖區​

int buffer_length, //傳輸的長度​

usb_complete_t complete_fn, //回調函數指針​

void *context,​

int interval)​

1.3.3 配置設定資料緩沖區位址

void *usb_alloc_coherent(struct usb_device *dev, size_t size, gfp_t mem_flags,​

繼續閱讀