Linux那些事儿 之 戏说USB(19)设备的生命线(二)
給張表吧,集中列了下到目前為止,設備結構體里成員的狀況。里面的taken只是表示賦過值了,好像期末考試前你去突擊自習,這時一個ppmm走到你旁邊,用一個美妙的聲音問你“Is this seat taken?”,你怎么回答?當然是“No, No, please.”對頭,taken就是這個意思。
| devnum | taken |
| devpath[16] | taken |
| state | USB_STATE_POWERED |
| speed | USB_SPEED_UNKNOWN |
| parent | 設備連接的那個hub |
| bus | 設備連接的那條總線 |
| ep0 | ep0.urb_list,描述符長度/類型 |
| dev | dev.bus,dev.type,dev.dma_mask,,dev.parent,dev.bus_id |
| ep_in[16] | ep_in[0] |
| ep_out[16] | ep_out[0] |
| bus_mA | hub->mA_per_port |
| portnum | 設備連接在hub上的那個端口 |
| level | hdev->level + 1 |
| filelist | taken |
| pm_mutex | taken |
| autosuspend | taken |
| autosuspend_delay | 2 * HZ |
你的設備現在已經處在了Powered狀態。前面講過的,設備要想從Powered狀態發展到下一個狀態Default,必須收到一個復位信號并成功復位。那hub接下來的動作就很明顯了,復位設備,復位成功后,設備就會進入Default狀態。
設備復位順利的話也就那么幾十毫秒的功夫,不順利的話,它會多嘗試幾次。不過如果試了幾次都復位不成,那就不用試了,這條設備的生命線就算提前玩完兒了。
現在就算設備成功復位了,大步邁進了Default狀態,同時,hub也會獲得設備真正的速度,低速、全速也好,高速也罷,總算是浮出水面了,speed也終于知道了自己的真正身份,不用再是UNKNOWN了。那根據這個速度,咱們能知道些什么?起碼能夠知道端點0一次能夠處理的最大數據長度啊,協議里說,對于高速設備,這個值為為64字節,對于低速設備為8字節,而對于全速設備可能為8,16,32,64其中的一個。所以hub還要通過一個蜿蜒曲折的過程去獲得這個確定的值。
hub也辛苦的蠻久了,設備也該進入Address狀態了。
只要hub使用core里定義的一個函數usb_control_msg,發送SET_ADDRESS請求給設備,設備就興高采烈的邁進Address了。那么設備的這個address是什么,就是上面的devnum啊。
那現在咱就來說說這個usb_control_msg函數,它在drivers/usb/core/message.c里定義
int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,__u8 requesttype, __u16 value, __u16 index, void *data,__u16 size, int timeout)
{struct usb_ctrlrequest *dr;int ret;dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);if (!dr)return -ENOMEM;dr->bRequestType = requesttype;dr->bRequest = request;dr->wValue = cpu_to_le16(value);dr->wIndex = cpu_to_le16(index);dr->wLength = cpu_to_le16(size);ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout);kfree(dr);return ret;
}這個函數主要目的是創建一個控制urb,并把它發送給usb設備,然后等待它完成。urb是什么?忘了么,前面提到過的,你要想和你的usb通信,就得創建一個urb,并且為它賦好值,交給usb core,它會找到合適的host controller,從而進行具體的數據傳輸。
8行,為一個struct usb_ctrlrequest結構體申請了內存。它在include/uapi/linux/usb/ch9.h文件里定義
struct usb_ctrlrequest {__u8 bRequestType;__u8 bRequest;__le16 wValue;__le16 wIndex;__le16 wLength;
} __attribute__ ((packed));這個結構完全對應于spec里的Table 9-2,描述了主機通過控制傳輸發送給設備的請求(Device Requests)。主機向設備請求些信息必須得按照協議里規定好的格式,不然設備就會不明白主機是嘛意思。
這個結構描述的request都在Setup包里發送,Setup包是前面某處說到的Token PID類型中的一種,為了你好理解,這里細說一下控制傳輸底層的packet情況。控制傳輸最少要有兩個階段的transaction,SETUP和STATUS,SETUP和STATUS中間的那個DATA階段是可有可無的。Transaction這個詞兒在很多地方都有,在這里你稱它為事務也好,會話也罷,我還是直呼它的原名transaction,可以理解為主機和設備之間形成的一次完整的交流,比如2004中華小姐環球大賽總決賽上評委蔡瀾和陜西選手姚佳雯之間的對話:
要老公還是要錢?要錢。
這就可以算是一次transaction,usb的transaction要比上面的對話復雜,起碼要過過腦子,它可以包括一個Token包、一個Data包和一個Handshake包。
Token、Data和Handshake都屬于四種PID類型中的,前面說到時提到的一個包里的那些部分,如SYNC、PID、地址域、DATA、CRC,并不是所有PID類型的包都會全部包括的。Token包只包括SYNC、PID、地址域、CRC,并沒有DATA字段,它的名字起的很形象,就是用來標記所在transaction里接下來動作的,對于Out和Setup Token包,里面的地址域指明了接下來要接收Data包的端點,對于In Token包,地址域指明了接下來哪個端點要發送Data包。還有,只有主機才有權利發送Token包,協議里就這么規定的。
與Token包相比,Data包里沒了地址域,多了Data字段,這個Data字段對于低速設備最大為8字節,對于全速設備最大為1023字節,對于高速設備最大為1024字節。里里外外看過去,它就是躲在Token后邊兒用來傳輸數據的。Handshake包的成分就非常的簡單了,簡直和那位姚佳雯的回答一樣簡單,除了SYNC,它就只包含了一個PID,通過PID取不同的值來報告一個transaction的狀態,比如數據已經成功接收了等。
控制傳輸的SETUP transaction一般來說也有三個階段,就是主機向設備發送Setup Token包、然后發送Data0包,如果一切順利,設備回應ACK Handshake包表示OK,為什么加上一般?如果中間的那個Data0包由于某種不可知因素被損壞了,設備就什么都不會回應,這時就成倆階段了。SETUP transaction之后,接下來如果控制傳輸有DATA transaction的話,那就Data0、Data1這樣交叉的發送數據包,前面說過這是為了實現data toggle。最后是STATUS transaction,向主機匯報前面SETUP和DATA階段的結果,比如表示主機下達的命令已經完成了,或者主機下達的命令沒有完成,或者設備正忙著那沒功夫去理會主機的那些命令。
這樣經過SETUP、DATA、STATUS這三個transaction階段,一個完整的控制傳輸完成了。主機接下來可以規劃下一次的控制傳輸。
現在對隱藏在控制傳輸背后的是是非非摸了個底兒,群眾的眼睛是雪亮的,咱們現在應該可以看出之前說requests都在Setup包里發送是有問題的,因為Setup包本身并沒有數據字段,嚴格來說它們應該都是在SETUP transaction階段里Setup包后的Data0包里發送的。
bRequestType,它的bit7就表示了控制傳輸中DATA transaction階段的方向,當然,如果有DATA階段的話。bit5~6表示request的類型,是標準的,class-specific的還是vendor-specific的。bit0~4表示了這個請求針對的是設備,接口,還是端點。內核為它們專門量身定做了一批掩碼,也在ch9.h文件里,
/** USB directions** This bit flag is used in endpoint descriptors' bEndpointAddress field.* It's also one of three fields in control requests bRequestType.*/
#define USB_DIR_OUT 0 /* to device */
#define USB_DIR_IN 0x80 /* to host *//** 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)/** USB recipients, the third of three bRequestType fields*/
#define USB_RECIP_MASK 0x1f
#define USB_RECIP_DEVICE 0x00
#define USB_RECIP_INTERFACE 0x01
#define USB_RECIP_ENDPOINT 0x02
#define USB_RECIP_OTHER 0x03
/* From Wireless USB 1.0 */
#define USB_RECIP_PORT 0x04
#define USB_RECIP_RPIPE 0x05bRequest,表示具體是哪個request。
wValue,這個字段是request的參數,request不同,wValue就不同。
wIndex,也是request的參數,bRequestType指明request針對的是設備上的某個接口或端點的時候,wIndex就用來指明是哪個接口或端點。
wLength,控制傳輸中DATA transaction階段的長度,方向已經在bRequestType那兒指明了。如果這個值為0,就表示沒有DATA transaction階段,bRequestType的方向位也就無效了。
和 struct usb_ctrlrequest 的約會暫時就到這里,回到usb_control_msg函數里。很明顯要進行控制傳輸,得首先創建一個struct usb_ctrlrequest結構體,填上請求的內容。12到16行就是來使用傳遞過來的參數初始化這個結構體的。對于剛開始提到的SET_ADDRESS來說,bRequest的值就是USB_REQ_SET_ADDRESS,標準請求之一,ch9.h里定義有。因為SET_ADDRESS請求并不需要DATA階段,所以wLength為0,而且這個請求是針對設備的,所以wIndex也為0。這么一來,bRequestType的值也只能為0了。因為是設置設備地址的,總得把要設置的地址發給設備,不然設備會比咱們還一頭霧水不知道主機是嘛個意思,所以請求的參數wValue就是之前hub已經你的設備指定好的devnum。其實SET_ADDRESS請求各個部分的值spec 9.4.6里都有規定,就和我這里說的一樣,不信你去看看。
接下來先看20行,走到這兒就表示成也好敗也好,總之這次通信已經完成了,那么struct usb_ctrlrequest結構體也就沒用了,沒用的東西要好不猶豫的精簡掉。
回頭看18行,這是引領咱們往深處走了,不過不怕,路有多遠,咱們看下去的決心就有多遠。
總結
以上是生活随笔為你收集整理的Linux那些事儿 之 戏说USB(19)设备的生命线(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux那些事儿 之 戏说USB(18
- 下一篇: Linux那些事儿 之 戏说USB(20