USB是主機和外圍裝置之間的一種串連。USB最初是為了替代各種各樣的不同的介面的低速匯流排而設計的。(例如:串口,並口,鍵盤串連等)。它以單一類型的匯流排串連各種不同類型的裝置。
USB拓撲機構不是以匯流排方式的。而是一棵由幾個點對點的串連構成的樹。連接線由4根電纜組成(電源,地線,兩個資料線)
USB主控制器(Host Controller)負責詢問每一個USB裝置是否有資料需要發送。也就是說:一個USB裝置在沒有主控制器要求的情況下是不能發送資料的。
USB協議規範定義了一套任何特定類型的裝置都可以遵循的標準。如果一個裝置遵循該裝置,就不需要一個特殊的驅動程式。這些不同的特定類型稱之為類(class).例如:存放裝置,鍵盤,滑鼠,遊戲杆,網路裝置等。對於不符合這些類的其他裝置。則需要對此裝置編寫特定driver.
USB裝置構成:
Linux Kernel提供了USB Core來處理大部分USB的複雜性。寫USB驅動,Sam覺得就是把USB硬體裝置和USB Core之間給溝通起來。
USB協議把一個硬體USB裝置用以下各個定義勾畫出來。
概念一. USB 端點(endpoint)
USB endpoint只能往一個方向傳送資料。從主機到裝置(輸出Endpoint)或從裝置到主機(輸入Endpoint)。一個Endpoint可以看作一個單向的管道。
有四種類型Endpoint,他們的區別在於傳送資料的方式:
控制Endpoint:
用來控制對USB裝置不同部分的訪問。他們通常用於配置裝置,擷取裝置資訊,發送命令到裝置,或者擷取裝置的狀態報表。每個USB裝置都有一個名為:Endpoint0的控制Endpoint。USB Core使用該Endpoint0在插入時進行裝置的配置。
中斷Endpoint:
每當USB主控制器要求裝置傳輸資料時,中斷Endpoint就以一個固定的速率來傳送少量的資料。
USB Keyboard和Mouse通常使用中斷Endpoint。
請注意,中斷Endpoint和中斷不同,它還是無法主動向USB主控制器發送資料。二是需要等待USB主控制器輪詢。
批量Endpoint:
Bulk Endpoint用來傳輸大批量的資料。USB規範不保證資料能在規定時間內傳輸完成。但保證資料完整性。通常印表機,存放裝置和網路裝置使用之。
等時Endpoint:
用來傳輸大批量資料,但資料是否能夠到達,怎無法保證。
通常資料收集裝置會使用之。
Sam覺得,其實一個裝置有多少個以及什麼類型的Endpoint。其實是硬體裝置在製造階段已經定好的。 USB Core只是去讀取了這些資訊,並把這些資訊傳送給USB driver.
Linux Kernel中使用struct usb_host_endpoint來描述USB Endpoint。但其實其中的structusb_endpoint_descriptor才是真正的描述Endpoint的。
struct usb_endpoint_descriptor
{
__u8 bLength;
__u8 bDescriptorType;
__u8bEndpointAddress;
__u8bmAttributes;
__le16wMaxPacketSize;
__u8bInterval;
__u8 bRefresh;
__u8 bSynchAddress;
} __attribute__ ((packed));
bEndpointAddress:
//此Endpoint USB地址。它還包含了Endpoint方向資訊。通過掩碼USB_DIR_OUT和USB_DIR_IN判斷是輸出Endpoint還是輸入Endpoint。
bmAttributes;
Endpoint Type,也可以通過掩碼:USB_ENDPOINT_XFER_ISOC等判斷此Endpoint是中斷,等時,控制還是批量Endpoint。
wMaxPacketSize;
該Endpoint一次可以處理的最大位元組數。雖然driver可以傳送更大的資料,但實際傳送時,還是會分割成這個大小。
bInterval:
如果是中斷Endpoint,它就是Endpoint的間隔設定。也就是說,插斷要求間隔時間。以毫秒為單位。
概念二:介面(Interface)
數個Endpoint被捆綁為一個USB Interface。
一個 USB Interface只對應一個邏輯串連,例如滑鼠,鍵盤或者音頻流。一個USB裝置可以對應多個Interface。例如Sam見過的滑鼠鍵盤一起的裝置,就有2個Interface,一個鍵盤,一個滑鼠。
另外,有些USB擴音器有2個Interface,一個鍵盤,一個音頻流。
注意:每個USB drver只處理一個USB Interface。所以,一個裝置也許會對應多個driver.
所以,USB Core在處理USB裝置插入時,會針對不同的Interface喚醒它認為合適的driver。並以參數的形式把interface傳遞給drver.
Linux Kernel使用struct usb_interface來描述USB Interface。但Interface參數照例是其中的usb_interface_descriptor。
struct usb_interface
{
struct usb_host_interface *altsetting;
struct usb_host_interface *cur_altsetting;
unsigned num_altsetting;
struct usb_interface_assoc_descriptor *intf_assoc;
int minor;
enum usb_interface_condition condition;
unsigned is_active:1;
unsigned needs_remote_wakeup:1;
struct device dev;
struct device *usb_dev;
int pm_usage_cnt;
};
struct usb_host_interface *altsetting;
Interface結構體數組,包含了所有可能用於該Interface的可選配置。
struct usb_host_interface *cur_altsetting;
可選配置數
unsigned num_altsetting;
指向altsetting的指標。當前的Active 設定。
usb_interface_descriptor:Interface描述符
struct usb_interface_descriptor{
__u8bLength;
//描述符的位元組長度。協議裡規定,每個描述符必須以一個位元組打頭來表明描述符的長度。介面描述符的bLength應該是9
__u8bDescriptorType;
//描述符的類型。各種描述符的類型都在ch9.h, * Descriptor types ... USB 2.0 spec table 9.5
__u8bInterfaceNumber;
//介面號。每個配置可以包含多個介面,這個值就是它們的索引值
__u8bAlternateSetting;
__u8bNumEndpoints;
//介面擁有的端點數量。這裡並不包括端點0
__u8bInterfaceClass;
//此Interface所屬Class。例如:HID=0x03
__u8bInterfaceSubClass;
//此值基於bInterfaceClass之上。表明在某個Interface class中的子class。例如:HID中有:USB_INTERFACE_SUBCLASS_BOOT
__u8bInterfaceProtocol;
//同上,HID中就有USB_INTERFACE_PROTOCOL_MOUSE
__u8iInterface; //字串描述符,放一些描述性廠商資訊。
} __attribute__ ((packed));//__attribute__,意思就是告訴編譯器,這個結構的元素都是1位元組對齊的,不要再添加填充位了。
概念三:配置
一個或多個USB Interface被捆綁為配置。一個USB裝置可以有多個配置,並且可以在多個配置之間切換。
配置:struct usb_host_config
USB裝置:usb_device.
綜上所述:
1個USB裝置有一個或多個配置
1個配置有一個或多個Interface
一個Interface有一個或多個設定
Interface沒有或有多個Endpoint
USB URB
Linux Kernel中的USB代碼通過 urb(USB 請求快)與所有的USB裝置通訊。
urb被用來以非同步方式從特定的USB裝置的特定USB Endpoint上接收資料,或往特定的USB裝置的特定USB Endpoint上發送資料。
urb是由USB driver建立的。並分配給特定USB裝置的特定Endpoint。並由USB driver提交給USB Core。
一:建立urb.
urb不能在driver中靜態定義。因為這樣會破壞USB Core對urb的計數機制。所以必須使用:
usb_alloc_urb函數來建立。
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
第一個參數是:等時資料包的數量。