Linux那些事儿 之 戏说USB(27)设备的生命线(十)
state??????? USB_STATE_ADDRESS
speed???? taken
ep0???????? ep0.urb_list,描述符長度/類型,wMaxPacketSize
接下來設備的目標當然就是Configured了。
要進入Configured狀態,你得去配置設備,當然不能是盲目的去配置,要知道設備是可能有多個配置的,所以你要有選擇有目的有步驟有計劃的去配置,要做這樣一個四有新人,就要先去獲得設備的設備描述符,message.c中的usb_get_device_descriptor()就是core里專門干這個的。
int usb_get_device_descriptor(struct usb_device *dev, unsigned int size)
{struct usb_device_descriptor *desc;int ret;if (size > sizeof(*desc))return -EINVAL;desc = kmalloc(sizeof(*desc), GFP_NOIO);if (!desc)return -ENOMEM;ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, size);if (ret >= 0)memcpy(&dev->descriptor, desc, size);kfree(desc);return ret;
}
這個函數比較的精悍,先是準備了一個struct usb_device_descriptor結構體,然后就用它去調用message.c里的usb_get_descriptor()獲得設備描述符,獲得之后再把得到的描述符復制到設備struct usb_device結構體的descriptor成員里。因此,這個函數成功與否的關鍵就在usb_get_descriptor()。其實對于寫驅動的來說,眼里是只有usb_get_descriptor()沒有usb_get_device_descriptor()的,不管你想獲得哪種描述符都是要通過usb_get_descriptor(),而usb_get_device_descriptor()是專屬內核用的接口。
int usb_get_descriptor(struct usb_device *dev, unsigned char type,unsigned char index, void *buf, int size)
{int i;int result;memset(buf, 0, size); /* Make sure we parse really received data */for (i = 0; i < 3; ++i) {/* retry on length 0 or error; some devices are flakey */result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,(type << 8) + index, 0, buf, size,USB_CTRL_GET_TIMEOUT);if (result <= 0 && result != -ETIMEDOUT)continue;if (result > 1 && ((u8 *)buf)[1] != type) {result = -ENODATA;continue;}break;}return result;
}參數type就是用來區分不同的描述符的,協議里說了,GET_DESCRIPTOR請求主要就是適用于三種描述符,設備描述符,配置描述符和字符串描述符。參數index是要獲得的描述符的序號,如果希望得到的這種描述符設備里可以有多個,你需要指定獲得其中的哪個,比如配置描述符就可以有多個,不過對于設備描述符來說,是只有一個的,所以這里的index應該為0。參數buf和size就是描述你用來放置獲得的描述符的緩沖區的。
這個函數的內容挺單調的,主要就是調用了一個usb_control_msg(),你如果到現在還覺得usb_control_msg()只是個熟悉的陌生人,那俺也就太失敗了。這里要說的第一個問題是它的一堆參數,這就需要認真了解一下spec 9.4.3里的這張表
GET_DESCRIPTOR請求的數據傳輸方向很明顯是device-to-host的,而且還是協議里規定所有設備都要支持的標準請求,也不是針對端點或者接口什么的,而是針對設備的,所以bRequestType只能為0x80,就是上面表里的10000000B,也等于12行的USB_DIR_IN。wValue的高位字節表示描述符的類型,低位字節表示描述符的序號,所以就有13行的(type << 8) + index。wIndex對于字符串描述符應該設置為使用語言的ID,對于其它的描述符應該設置為0,所以也有了13行中間的那個0。至于wLength,就是描述符的長度,對于設備描述符,一般來說你都會指定為USB_DT_DEVICE_SIZE吧。
USB_CTRL_GET_TIMEOUT是定義在include/linux/usb.h里的一個宏,值為5000,表示有5s的超時時間。
#define USB_CTRL_GET_TIMEOUT 5000
#define USB_CTRL_SET_TIMEOUT 5000
第二個問題就是為什么會有3次循環。這個又要歸咎于一些不守規矩的廠商了,搞出的設備古里古怪的,比如一些usb讀卡器,一次請求還不定能成功,但是設備描述符拿不到接下來就沒法子走了,所以這里多試幾次,再不成功,就成鬼了。至于15到17行之間的代碼都是判斷是不是成功得到請求的描述符的,這個版本的內核這里的判斷還比較混亂,就不多說了,你只要知道((u8 *)buf)[1] != type是用來判斷獲得描述符是不是請求的類型就可以了。
現在設備描述符已經有了,但是只有設備描述符是遠遠不夠的,你從設備描述符里只能知道它一共支持幾個配置,具體每個配置是何方神圣,是公的還是母的都不知道,你要配置一個設備總得知道這些吧。所以接下來就要獲得各個配置的配置描述符,并且拿結果去充實struct usb_device的config、rawdescriptors等相關元素。core內部并不直接調用上面的usb_get_descriptor()去完成這個任務,而是調用config.c里的usb_get_configuration(),為什么?core總是需要做更多的事情,不然就不叫core了。
drivers/usb/core/config.c
int usb_get_configuration(struct usb_device *dev)
{struct device *ddev = &dev->dev;int ncfg = dev->descriptor.bNumConfigurations;int result = 0;unsigned int cfgno, length;unsigned char *bigbuffer;struct usb_config_descriptor *desc;cfgno = 0;result = -ENOMEM;if (ncfg > USB_MAXCONFIG) {dev_warn(ddev, "too many configurations: %d, ""using maximum allowed: %d\n", ncfg, USB_MAXCONFIG);dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;}if (ncfg < 1) {dev_err(ddev, "no configurations\n");return -EINVAL;}length = ncfg * sizeof(struct usb_host_config);dev->config = kzalloc(length, GFP_KERNEL);if (!dev->config)goto err2;length = ncfg * sizeof(char *);dev->rawdescriptors = kzalloc(length, GFP_KERNEL);if (!dev->rawdescriptors)goto err2;desc = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);if (!desc)goto err2;result = 0;for (; cfgno < ncfg; cfgno++) {/* We grab just the first descriptor so we know how long* the whole configuration is */result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,desc, USB_DT_CONFIG_SIZE);if (result < 0) {dev_err(ddev, "unable to read config index %d ""descriptor/%s: %d\n", cfgno, "start", result);if (result != -EPIPE)goto err;dev_err(ddev, "chopping to %d config(s)\n", cfgno);dev->descriptor.bNumConfigurations = cfgno;break;} else if (result < 4) {dev_err(ddev, "config index %d descriptor too short ""(expected %i, got %i)\n", cfgno,USB_DT_CONFIG_SIZE, result);result = -EINVAL;goto err;}length = max((int) le16_to_cpu(desc->wTotalLength),USB_DT_CONFIG_SIZE);/* Now that we know the length, get the whole thing */bigbuffer = kmalloc(length, GFP_KERNEL);if (!bigbuffer) {result = -ENOMEM;goto err;}if (dev->quirks & USB_QUIRK_DELAY_INIT)msleep(100);result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,bigbuffer, length);if (result < 0) {dev_err(ddev, "unable to read config index %d ""descriptor/%s\n", cfgno, "all");kfree(bigbuffer);goto err;}if (result < length) {dev_warn(ddev, "config index %d descriptor too short ""(expected %i, got %i)\n", cfgno, length, result);length = result;}dev->rawdescriptors[cfgno] = bigbuffer;result = usb_parse_configuration(dev, cfgno,&dev->config[cfgno], bigbuffer, length);if (result < 0) {++cfgno;goto err;}}result = 0;err:kfree(desc);dev->descriptor.bNumConfigurations = cfgno;
err2:if (result == -ENOMEM)dev_err(ddev, "out of memory\n");return result;
}說代碼前先說點理論,不然要被這么生猛的代碼給嚇倒了。不管過多少河拐幾道彎,要想得到配置描述符,最終都不可避免的要向設備發送GET_DESCRIPTOR請求,這就需要以USB_DT_CONFIG為參數調用usb_get_descriptor函數,也就需要知道該為獲得的描述符準備多大的一個緩沖區,本來這個長度應該很明確的為USB_DT_CONFIG_SIZE,它表示的就是配置描述符的大小,但是實際上不是這么回事兒,USB_DT_CONFIG_SIZE只表示配置描述符本身的大小,并不表示GET_DESCRIPTOR請求返回結果的大小。因為向設備發送GET_DESCRIPTOR請求時,設備并不單單返回一個配置描述符了事,而是一股腦兒的將這個配置下面的所有接口描述符,端點描述,還有class-或vendor-specific描述符都返回了給你。那么這個總長度如何得到那?在神秘的配置描述符里有這樣一個神秘的字段wTotalLength,它里面記錄的就是這個總長度,那么問題就簡單了,可以首先發送USB_DT_CONFIG_SIZE個字節的請求過去,獲得這個配置描述符的內容,從而獲得那個總長度,然后以這個長度再請求一次,這樣就可以獲得一個配置下面所有的描述符內容了。上面的usb_get_configuration()采用的就是這個處理方法。
4行,獲得設備配置描述符的數目。
12行,這些檢驗又來了,在光天化日之下莫明其妙的受到戴大蓋帽的盤問很不爽是吧,但這就是他們的規矩他們的工作,不然你讓他們做什么。USB_MAXCONFIG是config.c理定義的
#define USB_MAXCONFIG 8 /* Arbitrary limit */限制了一個設備最多只能支持8種配置擁有8個配置描述符,如果超出了這個限制,15行就強制它為這個最大值,你一個設備要想在linux里混就得守這里的規矩,自由民主只是相對的。不過如果設備里沒有任何一個配置描述符,什么配置都沒有,就想裸身蒙混過關,那是不可能的,18行這關就過不去。
24行,struct usb_device里的config表示的是設備擁有的所有配置,你設備有多少個配置就為它準備多大的空間。
29行,rawdescriptors還認識吧,這是個字符指針數組里的每一項都指向一個使用GET_DESCRIPTOR請求去獲取配置描述符時所得到的結果。
33行,準備一個大小為USB_DT_CONFIG_SIZE的緩沖區,第一次發送GET_DESCRIPTOR請求要用的。
38行,剩下的主要就是這個for循環了,獲取每一個配置的那些描述符。
41行,誠如上面所說的,首先發送USB_DT_CONFIG_SIZE個字節請求,獲得配置描述符的內容。然后對返回的結果進行檢驗,知道為什么51行會判斷結果是不是小于4么?答案盡在配置描述符中,里面的3,4字節就是wTotalLength,只要得到前4個字節,就已經完成任務能夠獲得總長度了。
62行,既然總長度已經有了,那么這里就為接下來的GET_DESCRIPTOR請求準備一個大點的緩沖區。71行,現在可以獲得這個配置相關的所有描述符了。然后是對返回結果的檢驗,再然后就是將得到的那一堆數據的地址賦給rawdescriptors數組里的指針。
87行,你將會遇到另一個超級變態的函數,它將對前面GET_DESCRIPTOR請求獲得的那堆數據做處理。
總結
以上是生活随笔為你收集整理的Linux那些事儿 之 戏说USB(27)设备的生命线(十)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux那些事儿 之 戏说USB(26
- 下一篇: Linux那些事儿 之 戏说USB(28