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;
}這個函數的內容挺單調的,主要就是調用了一個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
現在設備描述符已經有了,但是只有設備描述符是遠遠不夠的,你從設備描述符里只能知道它一共支持幾個配置,具體每個配置是何方神圣,是公的還是母的都不知道,你要配置一個設備總得知道這些吧。所以接下來就要獲得各個配置的配置描述符,并且拿結果去充實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;
}4行,獲得設備配置描述符的數目。
12行,這些檢驗又來了,在光天化日之下莫明其妙的受到戴大蓋帽的盤問很不爽是吧,但這就是他們的規矩他們的工作,不然你讓他們做什么。USB_MAXCONFIG是config.c理定義的
#define USB_MAXCONFIG			8	/* Arbitrary limit */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
