linux设备驱动之USB主机控制器驱动分析(二)

首先来看一下这个函数要做什么事情:
我们在前面说过,int128,int64,int32……int4,int2,int1这样8个QH.我们在后面看到.会将 uhci->frame的物理地址存放到UHCI控制器的Frame List Base Address Register中.所以现在要做的事情就是将这些QH与uhci->frame[ ]关联起来.必须要按照相应的时间间隔将QH插入到uhci->frame[]中.例如,例如如果frame[]的n存放int128的QH,那么 下一个int128的QH就必须要放到n+128的位置.很明显,对于int1是可以随便放的,也可放可不放.因为int1链接在所有的间隔的QH后面. 同时int1又可以单独存放到frame[]中.
另外,从skelqh[2]~skelqh[9]分别表示int128~int1.对于skelqh[0]和skelqh[1]是不需要用到 的,而且8- (int) __ffs(frame | UHCI_NUMFRAMES)不可能大于9.所以,将用到skelqh[0]和skelqh[1]的地方.用间隔1ms的skelqh[9]代替.
经过这个函数这后,uhci->frame[]中的各个QH都按照对应的间隔存放到一起了.
 
接着看下面的configure_hc()函数:
static void configure_hc(struct uhci_hcd *uhci)
{
     /* Set the frame length to the default: 1 ms exactly */
     outb(USBSOF_DEFAULT, uhci->io_addr + USBSOF);
 
     /* Store the frame list base address */
     //将frame指针地址写入基地址寄存器
     outl(uhci->frame_dma_handle, uhci->io_addr + USBFLBASEADD);
 
     /* Set the current frame number */
     //当前的frame number
     outw(uhci->frame_number & UHCI_MAX_SOF_NUMBER,
              uhci->io_addr + USBFRNUM);
 
     /* Mark controller as not halted before we enable interrupts */
     uhci_to_hcd(uhci)->state = HC_STATE_SUSPENDED;
     mb();
 
     /* Enable PIRQ */
     pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
              USBLEGSUP_DEFAULT);
}
这个函数比较简单.首先将uhci->frame[ ]的物理地址写到FRBASEADD寄存器中.再将起始帧号写入到FRNUM寄存器.再将状态置为HC_STATE_SUSPENDED.最后到USBLEGSUP中启用PIRQ.这样UHCI就可以产生中断了.
到这里,UHCI已经初始化完成了.现在到了启用它的时候了.
 
返回到uhci_start().流程转入到uhci_start().代码如下:
static void start_rh(struct uhci_hcd *uhci)
{
     //将UHCI的状态置为HC_STATE_RUNNING
     uhci_to_hcd(uhci)->state = HC_STATE_RUNNING;
     uhci->is_stopped = 0;
 
     /* Mark it configured and running with a 64-byte max packet.
      * All interrupts are enabled, even though RESUME won't do anything.
      */
      //启用UHCI,设置CF位,表示已经配置好了,指定最大的包长为64 byte
     outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, uhci->io_addr + USBCMD);
     //启用各种中断.包括传输超时或者CRC检验错误,RESUME状态中断.传输完成时产生中断
     //短包中断
     outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP,
              uhci->io_addr + USBINTR);
     mb();
     //然后将UHCI的状态置为UHCI_RH_RUNNING状态.
     uhci->rh_state = UHCI_RH_RUNNING;
     uhci_to_hcd(uhci)->poll_rh = 1;
}
对照代码中的注释和UHCI spec,理解这段代码比较容易.在这里要特别提示一下,什么叫短包中断.
在发送包的时候,如果一次不能够打包完.那就需要将包截短成小包. 一个个传输传输出去.另外,最后一个包可能传输数据会小于允许包大小的最大值.这个的包叫短包.我们在后面的中断处理函数中会有对于短包的处理.到时再详细分析它的处理.
到这里.UHCI就开始调度了.不过这时候.整个调度系统中就只含有一个term_td.然而这个td的初始化如下(在uhci_start[ ]函数中):
uhci_fill_td(uhci->term_td, 0, uhci_explen(0) |
              (0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
也就是它的status为空,也就是说,这个TD是一个INACTIVE的.实际这个UHCI是空负荷运行.
 
运行到这里,hcd->start()运行完了.流程会返回到usb_add_hcd()到.的重要操作只剩余register_root_hub().probe过程也要接近尾声了.
 
2.4:register_root_hub()的操作
这个函数主要是对UHCI的root hub进行处理.代码如下:
static int register_root_hub(struct usb_hcd *hcd)
{
     struct device *parent_dev = hcd->self.controller;
     struct usb_device *usb_dev = hcd->self.root_hub;
     const int devnum = 1;
     int retval;
 
     //root hub的devnum为1.下一个设备号从2开始.devnum也即设备地址
     usb_dev->devnum = devnum;
     usb_dev->bus->devnum_next = devnum + 1;
 
     //usb_bus->devmap是一个位图.表示设备号的分配情况
     memset (&usb_dev->bus->devmap.devicemap, 0,
              sizeof usb_dev->bus->devmap.devicemap);
     //将root hub占用位置1
     set_bit (devnum, usb_dev->bus->devmap.devicemap);
     //设置成Address  状态
     usb_set_device_state(usb_dev, USB_STATE_ADDRESS);
 
     mutex_lock(&usb_bus_list_lock);
 
     //端点0的最大发送或者接收值
     usb_dev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);
     //取得root hub的设备描述符
     retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
     if (retval != sizeof usb_dev->descriptor) {
         mutex_unlock(&usb_bus_list_lock);
         dev_dbg (parent_dev, "can't read %s device descriptor %d\n",
                   usb_dev->dev.bus_id, retval);
         return (retval < 0) ? retval : -EMSGSIZE;
     }
 
     //进一步初始化这个设备
     retval = usb_new_device (usb_dev);
     if (retval) {
         dev_err (parent_dev, "can't register root hub for %s, %d\n",
                   usb_dev->dev.bus_id, retval);
     }
     mutex_unlock(&usb_bus_list_lock);
 
     if (retval == 0) {
         spin_lock_irq (&hcd_root_hub_lock);
         //root hub注册成功,将rh_registered设为1
         hcd->rh_registered = 1;
         spin_unlock_irq (&hcd_root_hub_lock);
 
         /* Did the HC die before the root hub was registered? */
         //如果hcd被人为置为了HALT
         if (hcd->state == HC_STATE_HALT)
              usb_hc_died (hcd); /* This time clean up */
     }
 
     return retval;
}
对于代码中较简单部份,结合注释应该就能看懂了.详细分析一下里面涉及到的几个子函数.
第一个是usb_set_device_state().
在分析代码之前,先来看一下USB设备的状态机.在USB2.0的spec上.有一副这样的图:
【转】linux设备驱动之USB主机控制器驱动分析(续) - 楚江北 - 空谈误己,实干兴业
 
上图表示USB设备的各种状态的转变.
1:如果设备末连接,对应状态为USB_STATE_NOTATTACHED. 这个状态在spec上末表示.是linux中自定义的.实际上它就是表示Attached的一个对立状态.
2:如果设备连上了,但是没有打开电源,处于Attached状态.USB检测到一个设备的时候,会将它初始化这个状态(道理很简单,因为要连上才能检测到 ^_^).可以查看下usb_alloc_dev()函数对状态的初始化.在代码,这个状态对应为:  USB_STATE_ATTACHED.
3:如果在上个状态中打开了设备此时打开了电源,设备处于Rowered.在代码中对应USB_STATE_POWERED.
4:如果在上一个状态中,设备被重置,也即初始化,就会转入Default.代码中对应USB_STATE_DEFAULT.
5:如果在上一个状态中,USB为设备分配了地址,就会转入到Address.代码中对应USB_STATE_ADDRESS.
6:如果在上一个状态中,USB完成了设备的配置.就会转入Configured.代码中对应USB_STATE_CONFIGURED.
7:上面除NoAttached和Attached外的所有状态,如果设备被挂起,就会转入Suspended.代码中对应USB_STATE_SUSPENDED.
特别说明:UHCI本身带有root hub功能.hub是一个特殊的USB设备.它的设备地址被固定为1.
 
对应到上面的代码中:
指定root hub的devnum之后,就将其状态设为Address.这个devnum也即设备的地址.设备状态函数为usb_set_device_state().代码如下:
void usb_set_device_state(struct usb_device *udev,
         enum usb_device_state new_state)
{
     unsigned long flags;
 
     spin_lock_irqsave(&device_state_lock, flags);
     //如果设备末连接.不做任何处理
     if (udev->state == USB_STATE_NOTATTACHED)
         ;    /* do nothing */
     else if (new_state != USB_STATE_NOTATTACHED) {
 
         /* root hub wakeup capabilities are managed out-of-band
          * and may involve silicon errata ... ignore them here.
          */
          //如果不是root hub
         if (udev->parent) {
              if (udev->state == USB_STATE_SUSPENDED
                       || new_state == USB_STATE_SUSPENDED)
                   ;    /* No change to wakeup settings */
              else if (new_state == USB_STATE_CONFIGURED)
                   device_init_wakeup(&udev->dev,
                       (udev->actconfig->desc.bmAttributes
                        & USB_CONFIG_ATT_WAKEUP));
              else
                   device_init_wakeup(&udev->dev, 0);
         }
         //如果是从Suspended转到其它状态或者是转到Suspended状态
         //更新active_duration计数
         if (udev->state == USB_STATE_SUSPENDED &&
              new_state != USB_STATE_SUSPENDED)
              udev->active_duration -= jiffies;
         else if (new_state == USB_STATE_SUSPENDED &&
                   udev->state != USB_STATE_SUSPENDED)
              udev->active_duration += jiffies;
 
         //设置状态
         udev->state = new_state;
     }
     else
         //这里是多余的吧?
         recursively_mark_NOTATTACHED(udev);
     spin_unlock_irqrestore(&device_state_lock, flags);
}
这段代码没有什么好多讲的.就是设置状态而已.对于不是root hub的情况,还涉及到了电源管理的情况,在这里不做分析.
 
设置完设备状态之后,调用usb_get_device_descriptor()来取得设备描述符.这个函数涉及到数据的传输实现.在接下来的章节中再做详细分析.在这里只需知道,完成之后会将设备描述符存放在usb_dev->descriptor.
获取到设备描述符之后,就可以获得设备的详细信息了.具体的详细可考阅USB2.0 spec.在这些信息里会包括设备的配置项数目.因此在接下来的操作中,就会将设备所支持的所有配置取出来.这是在usb_new_device()中完成的.代码如下所示:
int usb_new_device(struct usb_device *udev)
{
     int err;
 
     //一些设备的fixup
     usb_detect_quirks(udev);         /* Determine quirks */
     //取得配置描述符
     err = usb_configure_device(udev);    /* detect & probe dev/intfs */
     if (err < 0)
         goto fail;
     /* export the usbdev device-node for libusb */
     //指定设备的设备号
     udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
              (((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
 
     /* Increment the parent's count of unsuspended children */
     if (udev->parent)
         usb_autoresume_device(udev->parent);
 
     /* Register the device.  The device driver is responsible
      * for adding the device files to sysfs and for configuring
      * the device.
      */
      //注册usb_dev中内嵌的struct device
     err = device_add(&udev->dev);
     if (err) {
         dev_err(&udev->dev, "can't device_add, error %d\n", err);
         goto fail;
     }
 
     /* Tell the world! */
     //输出一些该设备的信息
     announce_device(udev);
     return err;
 
fail:
     usb_set_device_state(udev, USB_STATE_NOTATTACHED);
     return err;
}
这个代码的逻辑比较清淅.首先是usb_detect_quirks()函数,为个函数较简单,不打算进行详细分析,只是简单提一下.有些设备 可能在设计存在一些问题.比如说,有的设备在Reset的时候会出现问题,或者在取string描述符的时候对buffer长度有要求.这样的设备都会在 linux内核中形成一个链表,即usb_quirk_list.然后将设备的厂商ID,版本等信息与usb_quirk_list上的设备匹配.如果匹 配到了,就在usb_dev添上相应的标识,不允许设备进行限制的功能.或者是设备驱动根据修改信息调整相关的操作.
然后是usb_configure_device()函数.这个函数比较重要,跟踪进去分析一下 :
static int usb_configure_device(struct usb_device *udev)
{
     int err;
 
     //取得设备的配置
     if (udev->config == NULL) {
         err = usb_get_configuration(udev);
         if (err < 0) {
              dev_err(&udev->dev, "can't read configurations, error %d\n",
                   err);
              goto fail;
         }
     }
     //如果是无线设备
     if (udev->wusb == 1 && udev->authorized == 0) {
         udev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);
         udev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);
         udev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL);
     }
     else {
         //usb_cache_string:会将其关的字串存进一个缓冲,用户空间如果要取设备信息的话
         //只要从缓存区取就可以了
         /* read the standard strings and cache them if present */
         udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
         udev->manufacturer = usb_cache_string(udev,
                                  udev->descriptor.iManufacturer);
         udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
     }
     //OTG: On-The-GO.表示设备有主机控制器的功能
     err = usb_configure_device_otg(udev);
fail:
     return err;
}
首先解释一下CONFIG_USB_OTG的配置选项.一般来说,系统中只能有一个主机控制器.但有时候设备也可以带一个host control的功能.举个例子,数码相机.它接在PC上,做为一般的USB设备使用.它也可以连接在打印上直接打印,这时就会做会一个HC使用.
关于OTG的选择编译代码,这里不做深入研究,忽略掉.
其次要解释的是关于usb_cache_string()的操作.这个函数在取字串的时候还会将字符信息保存到一个缓存区.这样一些读USB信息的工具,就只要从指定的缓存区里取值就可以了.
重点放在usb_get_configuration()函数上.这个函数很烦锁.在分析之前.先来了解一下相关的部份.
USB设备有时候会用做多种用途.比如上面的一个例如.数码相机中的USB,可以用做视频存储,也可以当做U盘来使用.那做为驱动程序.它必须 要知道设备有多少种功能.在USB2.0 spec中,用配置表示功能.也就是说,对于上在的例子来说,数码相机的USB设备至少应该有两个配置,一个是视频存储的配置,另外的是U盘的配置.由驱 动程序来决定应用哪种配置来使用对应的功能.
接口是USB提供的单元组件.因此,有可能一个配置要使用到多个接口,也有可能一个接口也被多个配置使用的情况,不过不使用接口的配置是不存在的.
根据USB的spec有关设备的检测过程中描述,USB控制器先取得设备描述符,这个描述符里包含了配置的个数.然后再以长度9做为参数去取设 备配置描述符头部,这个描述符里包含了描述符的实际长际.最后再以实际长度做参数去取完整的配置描述符.取得的配置描述符不仅包含配置描述符信息,还包括 了接口信息和接口所使用的端口信息.
将代码中的有关数据结构如下所示 :
【转】linux设备驱动之USB主机控制器驱动分析(续) - 楚江北 - 空谈误己,实干兴业
 
大概说一下,usb_dev中的config数组对应每一项配置.config数组的数据结构为struct usb_host_config.这个数据结构中又包含Inft_cache[ ]数组,这个数组用来表示存放接口信息.由于一个接口可能属于同一配置的不同设置,用接口描述符的bAlternateSetting字段来区别接口所属 的接口描述符.所以在inft_cache[]对应的usb_host_cache中又有一个扩展项来存放每一个接口描述符.
 
以注释的方式列出usb_get_configuration(),就不做详细分析了,结合上面的说明和代码中的注释来分析这段代码应该没什么问题了.如下:
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;
     }
 
     //dev->config所占内存大小.总共有ncfg个配置项
     length = ncfg * sizeof(struct usb_host_config);
//为dev->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;
     //从设备中依次取出各配置.
     for (; cfgno < ncfg; cfgno++) {
 
         //这里有两次取CONFIG的过程.第一次是9为size取得配置的长度.然后
         //再以特定长度做为size去取完整的config
        
         /* We grab just the first descriptor so we know how long
          * the whole configuration is */
         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;
         }
         //取config长度
         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;
         }
 
         //取完整的config,并将其存放在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中存放了取得的CONFIG
         dev->rawdescriptors[cfgno] = bigbuffer;
         //解析取得的config信息
         result = usb_parse_configuration(&dev->dev, cfgno,
             &dev->config[cfgno], bigbuffer, length);
         if (result < 0) {
              ++cfgno;
              goto err;
         }
     }
     result = 0;
 
err:
     kfree(buffer);
out_not_authorized:
     dev->descriptor.bNumConfigurations = cfgno;
err2:
     if (result == -ENOMEM)
         dev_err(ddev, "out of memory\n");
     return result;
}
对每个配置都会调用usb_parse_configuration()对它进行解析.代码如下:
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;
 
     //配置描述符信息.这个信息在后面还会修正的
     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;
     }
     //CONFIG序号
     cfgno = config->desc.bConfigurationValue;
     //完整的配置信息除了标准头部处,还会带上接口和端口描述符信息
     //bLength: 描述符长度
     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 = 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;
         }
 
         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;
         }
 
         //如果后面跟的是INTERFACE的描述符
         if (header->bDescriptorType == USB_DT_INTERFACE) {
              struct usb_interface_descriptor *d;
              int inum;
 
              d = (struct usb_interface_descriptor *) header;
              //如果长度太短,不合法.继续下一个interface config
              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 */
               //nalts[ ]是相同端口出现次数的统计
               //如果在inums[ ]中已经包含这个接口了.
              for (i = 0; i < n; ++i) {
                   if (inums[i] == inum)
                       break;
              }
 
              //如果已经在inums[ ]了,增加nalts[]相应项的统计计数
              if (i < n) {
                   if (nalts[i] < 255)
                       ++nalts[i];
              }
              //否则将序号设置进inums[ ]中,nalts[]相应项为1.因为还是第一次出现
              else if (n < USB_MAXINTERFACES) {
                   inums[n] = inum;
                   nalts[n] = 1;
                   ++n;
              }
 
         }
         //minor usb only
         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是有效的interface config数据部份的长度
     size = buffer2 - buffer;
     config->desc.wTotalLength = cpu_to_le16(buffer2 - buffer0);
 
     //n是inums[ ]数组的有效项数,也即端口个数.
     //更新n,使表示实际的端口个数
     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 */
     //检查inums[ ]是否准确.如果有异常,打印出警告信息
     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 */
     //每一个接口号对应intf_cache[ ]一项.然nals[ ]表示该接口号的个数
     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->extar:config的扩展部份,即interface config的那部份
     config->extra = buffer;
     //找到一下个USB_DT_INTERFACE项.返回跳过去的数据长度和描述符项
     i = find_next_descriptor(buffer, size, USB_DT_INTERFACE,
         USB_DT_INTERFACE, &n);
     config->extralen = i;
     //到现在为止,config->extra返回的是下一个配置描述符起始地址
     //config->extralen下一个配置描述符地址和config->extra的偏移值
     if (n > 0)
         dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
             n, plural(n), "configuration");
     //现在interface config有效的起点位置,size有效大小
     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 */
     //检查config->inft)cache[]中是否有异常
     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;
}
usb_parse_interface()代码如下:
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;
 
     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;
     //config->intf_cache保存着端点的相关信息
     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;
     //如果合法的话,那alt就是指向一个空的接点描述符
     memcpy(&alt->desc, d, USB_DT_INTERFACE_SIZE);
 
     /* Skip over any Class Specific or Vendor Specific descriptors;
      * find the first endpoint or interface descriptor */
      //下一个endpoint descriptors的地址
     alt->extra = buffer;
     i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
         USB_DT_INTERFACE, &n);
     //alt->extar+alt->extralen表示下一个描述符地址
     alt->extralen = i;
     if (n > 0)
         dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
             n, plural(n), "interface");
     //下个intreface desp的地址
     buffer += i;
     size -= i;
 
     //接口中端点描述符的个数.  注意在这里将alt->desc.bNumEndpoints清0了
     /* Allocate space for the right(?) number of endpoints */
     num_ep = num_ep_orig = alt->desc.bNumEndpoints;
     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()代码如下:
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;
 
     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;
     //不可能会超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 */
     //注意在前面调用函数中已经将ifp->desc.bNumEndpoints清0了,以后每处理
     //一个端点描述符,都会将这个成员值+1
     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;
         if (le16_to_cpu(endpoint->desc.wMaxPacketSize) > 8)
              endpoint->desc.wMaxPacketSize = cpu_to_le16(8);
     }
 
     /* 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;
}
 
到这里,root hub对应的配置,接口,端点信息都可以在usb_dev中找到了.UHCI的初始化工作就全部完成了.在之后的分析中,会经常涉及到具体的信息传输过 程.在前面的代码中遇到也一笔代过了.为了以后的分析方便,在下一节里,对每个类型的传输过程做一个全面的分析.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值