usb_get_configuration的用途为取得并设置所有的描述符
usb_get_configuration在/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 *buffer; unsigned char *bigbuffer; struct usb_config_descriptor *desc; cfgno = 0; if (dev->authorized == 0) /* Not really an error */ goto out_not_authorized; 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; //为配置信息中的配置描述符分配空间 buffer = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL); //分配空间失败跳到出错处理 if (!buffer) goto err2; //将得到的空间按配置描述符结构初始化 desc = (struct usb_config_descriptor *)buffer; result = 0; //历遍所有配置, usb11_rh_dev_descriptor中的配置数为1 for (; cfgno < ncfg; cfgno++) { /* We grab just the first descriptor so we know how long * the whole configuration is */ //取得配置信息的前9字节,保存在buffer,这前9字节也就是配置描述符 result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,buffer, USB_DT_CONFIG_SIZE); if (result < 0) { dev_err(ddev, "unable to read config index %d " "descriptor/%s: %d\n", cfgno, "start", result); 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; } //计算配置信息的长度,小于USB_DT_CONFIG_SIZE则为USB_DT_CONFIG_SIZE 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; } //取得配置信息,保存在bigbuffer 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->dev, cfgno, &dev->config[cfgno], bigbuffer, length); if (result < 0) { ++cfgno; goto err; } } result = 0; }
|
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,buffer, USB_DT_CONFIG_SIZE);
这句代码和取得设备描述符一样,最后走到rh_call_control中,第一个switch的case依然为DeviceRequest | USB_REQ_GET_DESCRIPTOR,而第二个switch就不一样了,这次为配置信息,case为USB_DT_CONFIG << 8
uhci的配置信息为fs_rh_config_descriptor
这个结构如下
static const u8 fs_rh_config_descriptor [] = {
/* one configuration */
0x09, /* __u8 bLength; */
0x02, /* __u8 bDescriptorType; Configuration */
0x19, 0x00, /* __le16 wTotalLength; */
0x01, /* __u8 bNumInterfaces; (1) */
0x01, /* __u8 bConfigurationValue; */
0x00, /* __u8 iConfiguration; */
0xc0, /* __u8 bmAttributes;
Bit 7: must be set,
6: Self-powered,
5: Remote wakeup,
4..0: resvd */
0x00, /* __u8 MaxPower; */
/* USB 1.1:
* USB 2.0, single TT organization (mandatory):
* one interface, protocol 0
*
* USB 2.0, multiple TT organization (optional):
* two interfaces, protocols 1 (like single TT)
* and 2 (multiple TT mode) ... config is
* sometimes settable
* NOT IMPLEMENTED
*/
/* one interface */
0x09, /* __u8 if_bLength; */
0x04, /* __u8 if_bDescriptorType; Interface */
0x00, /* __u8 if_bInterfaceNumber; */
0x00, /* __u8 if_bAlternateSetting; */
0x01, /* __u8 if_bNumEndpoints; */
0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
0x00, /* __u8 if_bInterfaceSubClass; */
0x00, /* __u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
0x00, /* __u8 if_iInterface; */
/* one endpoint (status change endpoint) */
0x07, /* __u8 ep_bLength; */
0x05, /* __u8 ep_bDescriptorType; Endpoint */
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
0x03, /* __u8 ep_bmAttributes; Interrupt */
0x02, 0x00, /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
0xff /* __u8 ep_bInterval; (255ms -- usb 2.0 spec) */
};
包括了一个配置描述符,一个接口描述符和一个端口描述符
注意有2次获取配置信息,这2次是不一样的,第1次是获取配置信息中的头部,也就是配置描述符,在配置描述符中有配置信息的大小,所以第一次主要是取得配置信息的大小,然后根据大小取得空间后,再获取一次配置信息
获取配置信息结束后就要开始分析配置信息中的描述符了
完成这个工作的是usb_parse_configuration
usb_parse_configuration在/drivers/usb/core/config.c中
static int usb_parse_configuration(struct device *ddev, int cfgidx, struct usb_host_config *config, unsigned char *buffer, int size) { unsigned char *buffer0 = buffer; int cfgno; int nintf, nintf_orig; int i, j, n; struct usb_interface_cache *intfc; unsigned char *buffer2; int size2; struct usb_descriptor_header *header; int len, retval; u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES]; unsigned iad_num = 0; //复制配置信息的前9字节(也就是配置描述符) //到dev->config[cfgidx]->desc中 memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE); //检测描述符的类型及长度是否正确 if (config->desc.bDescriptorType != USB_DT_CONFIG || config->desc.bLength < USB_DT_CONFIG_SIZE) { dev_err(ddev, "invalid descriptor for config index %d: " "type = 0x%X, length = %d\n", cfgidx, config->desc.bDescriptorType, config->desc.bLength); return -EINVAL; } //取得配置中的配置值 cfgno = config->desc.bConfigurationValue; //buffer指向接口描述符部分的起始位置 buffer += config->desc.bLength; //计算配置信息除去配置描述符后的大小 size -= config->desc.bLength; //取得该配置的接口数 nintf = nintf_orig = config->desc.bNumInterfaces; //检测接口数目是否超过上限 if (nintf > USB_MAXINTERFACES) { dev_warn(ddev, "config %d has too many interfaces: %d, " "using maximum allowed: %d\n", cfgno, nintf, USB_MAXINTERFACES); nintf = USB_MAXINTERFACES; } /* Go through the descriptors, checking their length and counting the * number of altsettings for each interface */ //n用于保存接口数 n = 0; //历遍接口 for ((buffer2 = buffer, size2 = size) ; size2 > 0 ; (buffer2 += header->bLength, size2 -= header->bLength)) { if (size2 < sizeof(struct usb_descriptor_header)) { dev_warn(ddev, "config %d descriptor has %d excess " "byte%s, ignoring\n",cfgno, size2, plural(size2)); break; } //将buffer2,也就是配置描述符的第10字节起格式化成描述符头结构保存在header中 header = (struct usb_descriptor_header *) buffer2; //检测接口描述符的长度是否超过最大长度或过小 if ((header->bLength > size2) || (header->bLength < 2)) { dev_warn(ddev, "config %d has an invalid descriptor " "of length %d, skipping remainder of the config\n",cfgno, header->bLength); break; } //检测接口描述符的类型是否为接口 if (header->bDescriptorType == USB_DT_INTERFACE) { struct usb_interface_descriptor *d; int inum; //将header,也就是配置描述符的第10字节起格式化成接口描述符结构保存在d中 d = (struct usb_interface_descriptor *) header; //检测接口描述符的长度是否过短 if (d->bLength < USB_DT_INTERFACE_SIZE) { dev_warn(ddev, "config %d has an invalid " "interface descriptor of length %d, " "skipping\n", cfgno, d->bLength); continue; } //取得接口描述符的接口号 inum = d->bInterfaceNumber; //接口描述符数目是否大于配置描述符中规定的数目 if (inum >= nintf_orig) dev_warn(ddev, "config %d has an invalid " "interface number: %d but max is %d\n",cfgno, inum, nintf_orig - 1); /* Have we already encountered this interface? * Count its altsettings */ //inums记录当前接口的接口号 //nalts记录接口号的重复次数 //历遍接口号数组 for (i = 0; i < n; ++i) { //检测接口号是否等于当前接口的接口号 if (inums[i] == inum) break; } //i if (i < n) { if (nalts[i] < 255) ++nalts[i]; } //没有相同接口号,则在交互号数组中占一个对应接口号的位置 else if (n < USB_MAXINTERFACES) { inums[n] = inum; nalts[n] = 1; //接口数目计数器自加1 ++n; } } //检测接口描述符的类型是否为接口联合 else if (header->bDescriptorType == USB_DT_INTERFACE_ASSOCIATION) { if (iad_num == USB_MAXIADS) { dev_warn(ddev, "found more Interface " "Association Descriptors " "than allocated for in " "configuration %d\n", cfgno); } else { config->intf_assoc[iad_num] =(struct usb_interface_assoc_descriptor *)header; iad_num++; } } //检测接口描述符的类型是否为设备或者配置 else if (header->bDescriptorType == USB_DT_DEVICE || header->bDescriptorType == USB_DT_CONFIG) dev_warn(ddev, "config %d contains an unexpected " "descriptor of type 0x%X, skipping\n", cfgno, header->bDescriptorType); } /* for ((buffer2 = buffer, size2 = size); ...) */ //计算除去配置描述符的长度 size = buffer2 - buffer; //计算剩下的配置信息长度 config->desc.wTotalLength = cpu_to_le16(buffer2 - buffer0); if (n != nintf) dev_warn(ddev, "config %d has %d interface%s, different from " "the descriptor's value: %d\n", cfgno, n, plural(n), nintf_orig); else if (n == 0) dev_warn(ddev, "config %d has no interfaces?\n", cfgno); //设置接口数 config->desc.bNumInterfaces = nintf = n; /* Check for missing interface numbers */ //历遍接口 for (i = 0; i < nintf; ++i) { //历遍接口号数组 for (j = 0; j < nintf; ++j) { if (inums[j] == i) break; } //检测是否有占用了接口号数组却没有接口号 if (j >= nintf) dev_warn(ddev, "config %d has no interface number " "%d\n", cfgno, i); } /* Allocate the usb_interface_caches and altsetting arrays */ //历遍接口 for (i = 0; i < nintf; ++i) { //取得交互数 j = nalts[i]; //检测是否超过最大交互数 if (j > USB_MAXALTSETTING) { dev_warn(ddev, "too many alternate settings for " "config %d interface %d: %d, " "using maximum allowed: %d\n", cfgno, inums[i], j, USB_MAXALTSETTING); nalts[i] = j = USB_MAXALTSETTING; } //计算交互数组结构与接口结构一共需要的空间 len = sizeof(*intfc) + sizeof(struct usb_host_interface) * j; //分配接口结构 config->intf_cache[i] = intfc = kzalloc(len, GFP_KERNEL); if (!intfc) return -ENOMEM; kref_init(&intfc->ref); } /* Skip over any Class Specific or Vendor Specific descriptors; * find the first interface descriptor */ //保存接口描述符起始位置到config中 config->extra = buffer; //寻找下一个接口描述符,返回偏移量 i = find_next_descriptor(buffer, size, USB_DT_INTERFACE,USB_DT_INTERFACE, &n); //设置距离下一个接口描述符的偏移量 config->extralen = i; if (n > 0) dev_dbg(ddev, "skipped %d descriptor%s after %s\n",n, plural(n), "configuration"); //移动到下一个描述符的起始位置 buffer += i; //计算描述符的剩余大小 size -= i; /* Parse all the interface/altsetting descriptors */ while (size > 0) { //分析接口描述符 retval = usb_parse_interface(ddev, cfgno, config,buffer, size, inums, nalts); if (retval < 0) return retval; //移动到下一个描述符的起始位置 buffer += retval; //计算配置信息的剩余大小 size -= retval; } /* Check for missing altsettings */ //检测是否有缺失的交互配置 for (i = 0; i < nintf; ++i) { intfc = config->intf_cache[i]; for (j = 0; j < intfc->num_altsetting; ++j) { for (n = 0; n < intfc->num_altsetting; ++n) { if (intfc->altsetting[n].desc.bAlternateSetting == j) break; } if (n >= intfc->num_altsetting) dev_warn(ddev, "config %d interface %d has no ""altsetting %d\n", cfgno, inums[i], j); } } return 0; }
|
啥是交互?~
英文是Alternate,也就是接口描述符中的AlternateSetting,在usb规范中, 接口描述设备的功能,如果一个设备有数个功能,例如usb键盘上有个摄像头,这样就需要2个接口,一个描述键盘,一个描述摄像头,而AlternateSetting和InterfaceNumber的区别就是AlternateSetting是接口的集合,而InterfaceNumber是AlternateSetting的集合
find_next_descriptor用于寻找下一个指定的描述符,并返回偏移,可以指定任意2种类型,只想寻找1种指定类型的话,2个描述符的类型填写为相同就可以了
usb_parse_interface负责分析接口描述符
usb_parse_interface在/drivers/usb/core/config.c中
static int usb_parse_interface(struct device *ddev, int cfgno, struct usb_host_config *config, unsigned char *buffer, int size, u8 inums[], u8 nalts[]) { unsigned char *buffer0 = buffer; struct usb_interface_descriptor *d; int inum, asnum; struct usb_interface_cache *intfc; struct usb_host_interface *alt; int i, n; int len, retval; int num_ep, num_ep_orig; //按usb_interface_descriptor结构格式化buffer d = (struct usb_interface_descriptor *) buffer; //计算端点描述符的起始位置 buffer += d->bLength; //计算减去接口描述符剩下的大小 size -= d->bLength; if (d->bLength < USB_DT_INTERFACE_SIZE) goto skip_to_next_interface_descriptor; /* Which interface entry is this? */ intfc = NULL; //取得接口号 inum = d->bInterfaceNumber; //历遍接口号数组,寻找匹配的接口组结构 for (i = 0; i < config->desc.bNumInterfaces; ++i) { if (inums[i] == inum) { intfc = config->intf_cache[i]; break; } } if (!intfc || intfc->num_altsetting >= nalts[i]) goto skip_to_next_interface_descriptor; /* Check for duplicate altsetting entries */ //取得交互号 asnum = d->bAlternateSetting; //历遍交互组结构,检测当前接口是否已经分析过 for ((i = 0, alt = &intfc->altsetting[0]) ; i < intfc->num_altsetting ; (++i, ++alt)) { if (alt->desc.bAlternateSetting == asnum) { dev_warn(ddev, "Duplicate descriptor for config %d " "interface %d altsetting %d, skipping\n",cfgno, inum, asnum); goto skip_to_next_interface_descriptor; } } //增加接口组结构中的交互计数器 ++intfc->num_altsetting; //拷贝接口描述符到接口组结构中 memcpy(&alt->desc, d, USB_DT_INTERFACE_SIZE); /* Skip over any Class Specific or Vendor Specific descriptors; * find the first endpoint or interface descriptor */ //保存当前描述符的起始位置到接口结构中 alt->extra = buffer; //寻找下一个端点或者接口描述符 i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,USB_DT_INTERFACE, &n); //设置距离下一描述符的偏移 alt->extralen = i; if (n > 0) dev_dbg(ddev, "skipped %d descriptor%s after %s\n", n, plural(n), "interface"); //移动到下一描述符的起始位置 buffer += i; //计算配置信息的剩余大小 size -= i; /* Allocate space for the right(?) number of endpoints */ //取得端点的数目 num_ep = num_ep_orig = alt->desc.bNumEndpoints; //置端点数为0 alt->desc.bNumEndpoints = 0; /* Use as a counter */ //端点数大于最大值则设为最大值 if (num_ep > USB_MAXENDPOINTS) { dev_warn(ddev, "too many endpoints for config %d interface %d " "altsetting %d: %d, using maximum allowed: %d\n", cfgno, inum, asnum, num_ep, USB_MAXENDPOINTS); num_ep = USB_MAXENDPOINTS; } //分配端点结构需要的空间 if (num_ep > 0) { /* Can't allocate 0 bytes */ len = sizeof(struct usb_host_endpoint) * num_ep; alt->endpoint = kzalloc(len, GFP_KERNEL); if (!alt->endpoint) return -ENOMEM; } /* Parse all the endpoint descriptors */ //初始化端点数目计数器 n = 0; while (size > 0) { //描述符为接口则跳过 if (((struct usb_descriptor_header *) buffer)->bDescriptorType == USB_DT_INTERFACE) break; //分析端点描述符 retval = usb_parse_endpoint(ddev, cfgno, inum, asnum, alt,num_ep, buffer, size); if (retval < 0) return retval; ++n; //移动到下一个描述符 buffer += retval; //计算剩余大小 size -= retval; } if (n != num_ep_orig) dev_warn(ddev, "config %d interface %d altsetting %d has %d " "endpoint descriptor%s, different from the interface " "descriptor's value: %d\n", cfgno, inum, asnum, n, plural(n), num_ep_orig); return buffer - buffer0; skip_to_next_interface_descriptor: i = find_next_descriptor(buffer, size, USB_DT_INTERFACE, USB_DT_INTERFACE, NULL); return buffer - buffer0 + i; }
|
usb_parse_endpoint的用途为分析端点描述符
usb_parse_endpoint在/drivers/usb/core/config.c中
static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, int asnum, struct usb_host_interface *ifp, int num_ep, unsigned char *buffer, int size) { unsigned char *buffer0 = buffer; struct usb_endpoint_descriptor *d; struct usb_host_endpoint *endpoint; int n, i, j; //以端点描述符结构格式化buffer d = (struct usb_endpoint_descriptor *) buffer; //移动到端点描述符的尾部 buffer += d->bLength; //计算描述符的剩余大小 size -= d->bLength; //检测端点类型 if (d->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE) n = USB_DT_ENDPOINT_AUDIO_SIZE; else if (d->bLength >= USB_DT_ENDPOINT_SIZE) n = USB_DT_ENDPOINT_SIZE; else { dev_warn(ddev, "config %d interface %d altsetting %d has an " "invalid endpoint descriptor of length %d, skipping\n", cfgno, inum, asnum, d->bLength); goto skip_to_next_endpoint_or_interface_descriptor; } //取得端点号 i = d->bEndpointAddress & ~USB_ENDPOINT_DIR_MASK; //端点号不在1-16中则分析下一个 if (i >= 16 || i == 0) { dev_warn(ddev, "config %d interface %d altsetting %d has an " "invalid endpoint with address 0x%X, skipping\n", cfgno, inum, asnum, d->bEndpointAddress); goto skip_to_next_endpoint_or_interface_descriptor; } /* Only store as many endpoints as we have room for */ if (ifp->desc.bNumEndpoints >= num_ep) goto skip_to_next_endpoint_or_interface_descriptor; //获得接口结构中相应的端点结构的地址 endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints]; //增加端点计数器数 ++ifp->desc.bNumEndpoints; //拷贝端点描述符信息到端点结构中 memcpy(&endpoint->desc, d, n); INIT_LIST_HEAD(&endpoint->urb_list); /* Fix up bInterval values outside the legal range. Use 32 ms if no * proper value can be guessed. */ i = 0; /* i = min, j = max, n = default */ j = 255; //测试端点的传输类型是否为中断 if (usb_endpoint_xfer_int(d)) { i = 1; //判断设备的速度类型 switch (to_usb_device(ddev)->speed) { case USB_SPEED_HIGH: /* Many device manufacturers are using full-speed * bInterval values in high-speed interrupt endpoint * descriptors. Try to fix those and fall back to a * 32 ms default value otherwise. */ n = fls(d->bInterval*8); if (n == 0) n = 9; /* 32 ms = 2^(9-1) uframes */ j = 16; break; default: /* USB_SPEED_FULL or _LOW */ /* For low-speed, 10 ms is the official minimum. * But some "overclocked" devices might want faster * polling so we'll allow it. */ n = 32; break; } } //传输类型为等时 else if (usb_endpoint_xfer_isoc(d)) { i = 1; j = 16; switch (to_usb_device(ddev)->speed) { case USB_SPEED_HIGH: n = 9; /* 32 ms = 2^(9-1) uframes */ break; default: /* USB_SPEED_FULL */ n = 6; /* 32 ms = 2^(6-1) frames */ break; } }
if (d->bInterval < i || d->bInterval > j) { dev_warn(ddev, "config %d interface %d altsetting %d " "endpoint 0x%X has an invalid bInterval %d, " "changing to %d\n", cfgno, inum, asnum, d->bEndpointAddress, d->bInterval, n); endpoint->desc.bInterval = n; } /* Some buggy low-speed devices have Bulk endpoints, which is * explicitly forbidden by the USB spec. In an attempt to make * them usable, we will try treating them as Interrupt endpoints. */ //设备为低速设备并且该端点的传输类型为块传输 if (to_usb_device(ddev)->speed == USB_SPEED_LOW &&usb_endpoint_xfer_bulk(d)) { dev_warn(ddev, "config %d interface %d altsetting %d " "endpoint 0x%X is Bulk; changing to Interrupt\n", cfgno, inum, asnum, d->bEndpointAddress); //设置端点的传输类型为中断 endpoint->desc.bmAttributes = USB_ENDPOINT_XFER_INT; endpoint->desc.bInterval = 1; //最大包的值大于8则设为8 if (le16_to_cpu(endpoint->desc.wMaxPacketSize) > 8) endpoint->desc.wMaxPacketSize = cpu_to_le16(8); } /* * Some buggy high speed devices have bulk endpoints using * maxpacket sizes other than 512. High speed HCDs may not * be able to handle that particular bug, so let's warn... */ //设备为高速设备并且该端点的传输类型为块传输 if (to_usb_device(ddev)->speed == USB_SPEED_HIGH && usb_endpoint_xfer_bulk(d)) { unsigned maxp; maxp = le16_to_cpu(endpoint->desc.wMaxPacketSize) & 0x07ff; if (maxp != 512) dev_warn(ddev, "config %d interface %d altsetting %d " "bulk endpoint 0x%X has invalid maxpacket %d\n", cfgno, inum, asnum, d->bEndpointAddress, maxp); } /* Skip over any Class Specific or Vendor Specific descriptors; * find the next endpoint or interface descriptor */ //保存端点描述符的尾部到端点结构中 endpoint->extra = buffer; //寻找下一个接口或者端点描述符 i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,USB_DT_INTERFACE, &n); //取得距离下一个描述符的偏移 endpoint->extralen = i; if (n > 0) dev_dbg(ddev, "skipped %d descriptor%s after %s\n",n, plural(n), "endpoint"); return buffer - buffer0 + i; skip_to_next_endpoint_or_interface_descriptor: i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, USB_DT_INTERFACE, NULL); return buffer - buffer0 + i; }
|
终于分析完配置信息了.........
现在主机控制器的数据结构如下
精简了一些数据没有画出来,因为全部画出来需要的空间太大了 Orz
阅读(3719) | 评论(0) | 转发(1) |