學到這里不容易,先說一段故事吧。
二兄弟住一大樓的第80層,某深夜回家忘看通知(內容今夜停電)。
兄弟倆背着沉重的大背包,在樓底下商量一下,決定一鼓作氣,爬樓梯回家。兩人抖擻精神,開始爬樓。爬到20樓的時候,開始覺得背包很重了。兩人商量,決定把背包存在20樓,到時候再回過頭來取。卸下了背包,兩個人覺得很輕松,說說笑笑地繼續往上爬。
爬到40樓的時候,兩人已經很累了,就開始互相抱怨指責。哥哥說:你為什么不看通知啊?弟弟說:我忘了看通知這件事,你怎么不提醒我昵?兩個人就這樣吵吵鬧鬧,一路吵到60層。
到了這時候,兩人實在疲憊不堪,終於懶得吵了,覺得還是應該安安靜靜地繼續爬樓。當他們終於爬完了最后20層,來到了家門口的時候,兩個人互相一看,不約而同想起了一件事:鑰匙忘在20樓了,在背包里。
其實,這說的就是人的一生。(故事來源 於丹 《論生與死》)
在struct usb_host_interface結構體,我們把struct usb_host_endpoint *endpoint成員忽略了,下面就看這個結構體。
struct usb_host_endpoint {
struct usb_endpoint_descriptor desc;
struct list_head urb_list;
void *hcpriv;
struct ep_device *ep_dev; /* For sysfs info */
unsigned char *extra; /* Extra descriptors */
int extralen;
};
按照我們做事的原則:避重就輕。先不分析usb_interface_descriptor結構體成員,咱們先分析其他簡單的。
urb_list表示端點要處理的urb隊列。urb是什么?urb可是usb通信的主角,它包含了執行urb傳輸所需要的所有信息,若要進行usb通信,就得創建一個urb,並且為它賦好值(專業點就是初始化),交給咱們的usb core,它會找到合適的host controller,從而進行具體的數據傳輸。設備中的每個端點都可以處理一個urb隊列,可以理解為urb是內核里對usb傳輸數據的封裝(或者抽象)。基於urb特殊性,會有專題來分析它。
hcpriv,這是提供給HCD(host controller driver)用的。比如等時端點會在里邊兒放一個ehci_iso_stream,什么意思?鄭板橋告訴我們要難得糊塗,以后會明白的。
ep_dev,這個字段是供sysfs用的。好奇的話可以去/sys下看一看
[root@localhost ep_00]# pwd
/sys/bus/usb/devices/usb1/ep_00
[root@localhost ep_00]# ls
bEndpointAddress bmAttributes direction subsystem wMaxPacketSize
bInterval dev interval type
bLength device power uevent
[root@localhost ep_00]#
ep_00端點目錄下的這些文件從哪兒來的?就是在usb_create_ep_files函數里使用ep_dev創建的。
extra和 extralen,有關一些額外擴展的描述符的,和struct usb_host_interface里差不多,只是這里的是針對端點的,如果你請求從設備里獲得描述符信息,它們會跟在標准的端點描述符后面返回給你。
再回頭看看desc的結構體struct usb_endpoint_descriptor定義
/* USB_DT_ENDPOINT: Endpoint descriptor */
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));
這個結構與spec Table 9.13是一一對應的,0號端點仍然保持着它特殊的地位,它沒有自己的端點描述符。
bLength,描述符的字節長度。數一下,前邊兒有7個字節,后邊兒又多了兩個字節__u8 bRefresh; __u8 bSynchAddress;那是針對音頻設備擴展的,不用管它
bDescriptorType,描述符類型,這里對於端點就是USB_DT_ENDPOINT,0x05,緊接在usb_endpoint_descriptor結構體定義后面。還記得對於接口描述符類型是什么嗎?對,值為USB_DT_INTERFACE,也就是0x04。
bEndpointAddress,這個字段描述的信息挺多的,比如這個端點是輸入端點還是輸出端點,這個端點的地址,以及這個端點的端點號。它的bits 0~3表示的就是端點號,你使用0x0f和它相與就可以得到端點號。不過,開發內核的同志想的都很周到,定義好了一個掩碼USB_ENDPOINT_NUMBER_MASK,它的值就是0x0f,當然,這是為了讓咱們更容易去讀他們的代碼,也為了以后的擴展。另外,它的bit 8表示方向,輸入還是輸出,同樣有掩碼USB_ENDPOINT_DIR_MASK,值為0x80,將它和bEndpointAddress相與,並結合USB_DIR_IN和USB_DIR_OUT作判斷就可以得到端點的方向。使用下面兩個定義值表示端點傳輸方向:
#define USB_DIR_OUT 0 /* to device */
#define USB_DIR_IN 0x80 /* to host */
bmAttributes,屬性,總共8位,其中bit1和bit0 共同稱為Transfer Type,即傳輸類型, 00 表示控制,01 表示等時,10 表示批量,11 表示中斷。前面的端點號還有端點方向都有配對兒的掩碼,這里當然也有,就在struct usb_endpoint_descriptor定義的下面。
/*
* USB types, the second of three bRequestType fields
*/
#define USB_TYPE_MASK (0x03 << 5)
#define USB_TYPE_STANDARD (0x00 << 5)
#define USB_TYPE_CLASS (0x01 << 5)
#define USB_TYPE_VENDOR (0x02 << 5)
#define USB_TYPE_RESERVED (0x03 << 5)
wMaxPacketSize,端點一次可以處理的最大字節數。如果你發送的數據量大於端點的這個值,也會分成多次一次一次來傳輸。友情提醒一下,這個字段還是有點門道的,對不同的傳輸類型也有不同的要求,以后可以再說。
bInterval, USB是輪詢式的總線,這個值表達了端點一種美好的期待,希望主機輪詢自己的時間間隔,但實際上批准不批准是host的事。不同的傳輸類型bInterval也有不同的意義,以后碰到各個實際的傳輸類型也可以再說。
