日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

Linux那些事儿 之 戏说USB(19)设备的生命线(二)

發布時間:2023/11/27 生活经验 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux那些事儿 之 戏说USB(19)设备的生命线(二) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
現在設備的struct usb_device結構體已經準備好了,只是還不怎么飽滿,hub接下來就會給它做做整容手術,往里邊兒塞點什么,充實一些內容,比如:將設備的狀態設置為Powered,也就是加電狀態;因為此時還不知道設備支持的速度,于是將設備的speed成員暫時先設置為USB_SPEED_UNKNOWN;設備的級別level當然會被設置為hub的level加上1了;還有為設備能夠從hub那里獲得的電流賦值;為了保證通信暢通,hub還會為設備在總上選擇一個獨一無二的地址。

給張表吧,集中列了下到目前為止,設備結構體里成員的狀況。里面的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		0x05
bRequest,表示具體是哪個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)设备的生命线(二)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。