绑定完请刷新页面
取消
刷新

分享好友

×
取消 复制
LINUX下USB1.1设备学习小记(4)_uhci(5)
2020-06-03 15:03:42
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 (= 0; i < n; ++i) 
   {
    //检测接口号是否等于当前接口的接口号
    if (inums[i] == inum)
     break;
   }
   //i
   if (< n) 
   {
    if (nalts[i] < 255)
     ++nalts[i];
   } 
   //没有相同接口号,则在交互号数组中占一个对应接口号的位置
   else if (< 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 (!= 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 (== 0)
  dev_warn(ddev, "config %d has no interfaces?\n", cfgno);
 //设置接口数
 config->desc.bNumInterfaces = nintf = n;
 /* Check for missing interface numbers */
 //历遍接口
 for (= 0; i < nintf; ++i) 
 {
  //历遍接口号数组
  for (= 0; j < nintf; ++j) 
  {
   if (inums[j] == i)
    break;
  }
  //检测是否有占用了接口号数组却没有接口号
  if (>= nintf)
   dev_warn(ddev, "config %d has no interface number " "%d\n", cfgno, i);
 }
 /* Allocate the usb_interface_caches and altsetting arrays */
 //历遍接口
 for (= 0; i < nintf; ++i) 
 { 
  //取得交互数
  j = nalts[i];
  //检测是否超过大交互数
  if (> 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 (> 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 (= 0; i < nintf; ++i) 
 {
  intfc = config->intf_cache[i];
  for (= 0; j < intfc->num_altsetting; ++j) 
  {
   for (= 0; n < intfc->num_altsetting; ++n) 
   {
    if (intfc->altsetting[n].desc.bAlternateSetting == j)
     break;
   }
   if (>= 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 (= 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 ((= 0, alt = &intfc->altsetting[]) ; 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 (> 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 (!= 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 (>= 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 (== 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 (> 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


文章来源CU社区:LINUX下USB1.1设备学习小记(4)_uhci(5)

分享好友

分享这个小栈给你的朋友们,一起进步吧。

内核源码
创建时间:2020-05-18 13:36:55
内核源码精华帖内容汇总
展开
订阅须知

• 所有用户可根据关注领域订阅专区或所有专区

• 付费订阅:虚拟交易,一经交易不退款;若特殊情况,可3日内客服咨询

• 专区发布评论属默认订阅所评论专区(除付费小栈外)

技术专家

查看更多
  • 飘絮絮絮丶
    专家
戳我,来吐槽~