Chinaunix首页 | 论坛 | 博客
  • 博客访问: 657425
  • 博文数量: 151
  • 博客积分: 3498
  • 博客等级: 中校
  • 技术积分: 1570
  • 用 户 组: 普通用户
  • 注册时间: 2005-02-28 18:10
文章分类

全部博文(151)

文章存档

2014年(12)

2013年(17)

2012年(17)

2011年(5)

2010年(12)

2009年(2)

2007年(26)

2006年(22)

2005年(38)

分类: LINUX

2005-12-16 22:46:28

usb 设备的初始化过程,USB设备是如何探测的!

我们用UHCI类型的控制器来分析控制器的初始化,过程是这样的:
    PCI设备枚举------> UHCI控制器初始化------------>登记usb_bus---------->登记root hub
                              |                        |                       |
                              |                        |                       |   
                              |                        |                       |
                             /                      /                     /
                         中断处理函数        添加到USB 总线链表            取得配置信息    
    
1。USB控制器是连接在PCI总线上的,是一个PCI设备,所以在PCI总线初始化过程中也会受到枚举。
   UHCI的代码在DRIVER/USB/UHCI.C文件中;UCHI作为pci_driver 的结构被搜索出来。
  probe函数为:
  static int __devinit uhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
    int i;

    /* Search for the IO base address.. */
    for (i = 0; i < 6; i++) {
        unsigned int io_addr = pci_resource_start(dev, i);
        unsigned int io_size = pci_resource_len(dev, i);

        /* IO address? */
        if (!(pci_resource_flags(dev, i) & IORESOURCE_IO))
            continue;

        return alloc_uhci(dev, io_addr, io_size);
    }

    return -ENODEV;
}

2。USB控制器作为PCI设备在PCI总线层次上的初始化已经完成了,下面就是USB总线控制器本身的初始化,即USB总线的初始化了。
   * 分配UHCI所连接的USB总线,并注册它!
   * 探测ROOT HUB的端口数,其IO地址空间的前16个地址用于总线控制器本身,其余的用于ROOT HUB;
    ROOT HUB的每个端口(PORT)占用两个地址,端口在2 和8之间,每个端口的BIT7总是1。
   * 分配ROOT HUB,也是一个USB DEVICE;
   * 注册UHCI的中断请求函数;
   * 为ROOT HUB 动态的分配一个设备号;
   * ROOT HUB的总线控制器的连接是固定的,不需要通过另一个HUB的报告,所以直接就开始其枚举过程。

static int alloc_uhci(struct pci_dev *dev, unsigned int io_addr, unsigned int io_size)
{
    struct uhci *uhci;
    int retval;
    char buf[8], *bufp = buf;
    int i, port;
    struct usb_bus *bus;
    dma_addr_t dma_handle;
   
    uhci = kmalloc(sizeof(*uhci), GFP_KERNEL);
    if (!uhci) {
        err("couldn't allocate uhci structure");
        retval = -ENOMEM;
        goto err_alloc_uhci;
    }

    uhci->dev = dev;
    uhci->irq = dev->irq;
    uhci->io_addr = io_addr;
    uhci->io_size = io_size;
    pci_set_drvdata(dev, uhci);   
        //分配UHCI所连接的USB总线,并注册它!
    bus = usb_alloc_bus(&uhci_device_operations);
    if (!bus) {
        err("unable to allocate bus");
        goto err_alloc_bus;
    }

    uhci->bus = bus;
    bus->bus_name = dev->slot_name;
    bus->hcpriv = uhci;

    usb_register_bus(uhci->bus);

        //探测ROOT HUB的端口数,其IO地址空间的前16个地址用于总线控制器本身,其余的用于ROOT HUB;
        //ROOT HUB的每个端口(PORT)占用两个地址,端口在2 和8之间,每个端口的BIT7总是1。
    /* Initialize the root hub */

    /* UHCI specs says devices must have 2 ports, but goes on to say */
    /*  they may have more but give no way to determine how many they */
    /*  have. However, according to the UHCI spec, Bit 7 is always set */
    /*  to 1. So we try to use this to our advantage */
    for (port = 0; port < (uhci->io_size - 0x10) / 2; port++) {
        unsigned int portstatus;

        portstatus = inw(uhci->io_addr + 0x10 + (port * 2));
        if (!(portstatus & 0x0080))
            break;
    }
    if (debug)
        info("detected %d ports", port);

    /* This is experimental so anything less than 2 or greater than 8 is */
    /*  something weird and we'll ignore it */
    if (port < 2 || port > 8) {
        info("port count misdetected? forcing to 2 ports");
        port = 2;
    }

    uhci->rh.numports = port;
       //分配ROOT HUB,也是一个USB DEVICE;
    uhci->bus->root_hub = uhci->rh.dev = usb_alloc_dev(NULL, uhci->bus);
    if (!uhci->rh.dev) {
        err("unable to allocate root hub");
        goto err_alloc_root_hub;
    }

    start_hc(uhci);
       //注册UHCI的中断请求函数;
    if (request_irq(dev->irq, uhci_interrupt, SA_SHIRQ, "usb-uhci", uhci))
        goto err_request_irq;

    /* disable legacy emulation */
    pci_write_config_word(uhci->dev, USBLEGSUP, USBLEGSUP_DEFAULT);

       //为ROOT HUB 动态的分配一个设备号;
    usb_connect(uhci->rh.dev);

      //ROOT HUB的总线控制器的连接是固定的,不需要通过另一个HUB的报告,所以直接就开始其枚举过程。
      //枚举过程要做的事情如下:1.分配地址;2.读入usb_device_descriptor; 3.读入配置描述符结构;4.选择或者改变设备的配置;
    if (usb_new_device(uhci->rh.dev) != 0) {
        err("unable to start root hub");
        retval = -ENOMEM;
        goto err_start_root_hub;
    }

    return 0;
}

3.枚举ROOT HUB 并做相关的工作
   *  取得descriptor 结构
   *  查找处理这个设备的驱动       
   *  留给用户空间程序加载模块;

int usb_new_device(struct usb_device *dev)
{
   
    err = usb_set_address(dev);
   
    wait_ms(10);    /* Let the SET_ADDRESS settle */

       //取得descriptor 结构
    err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8);   
    err = usb_get_device_descriptor(dev);

    err = usb_get_configuration(dev);   
    /* now that the basic setup is over, add a /proc/bus/usb entry */
    usbdevfs_add_device(dev);

        //查找处理这个设备的驱动       
    /* find drivers willing to handle this device */
    usb_find_drivers(dev);

        //留给用户空间程序加载模块;
    /* userspace may load modules and/or configure further */
    call_policy ("add", dev);

    return 0;
}

4.取得设备的配置信息
  * 读入配置描述结构
  * 分析配置

int usb_get_configuration(struct usb_device *dev)
{
   
    desc = (struct usb_config_descriptor *)buffer;

    for (cfgno = 0; cfgno < dev->descriptor.bNumConfigurations; cfgno++) {
        /* We grab the first 8 bytes so we know how long the whole */
        /*  configuration is */
                //读入配置描述结构
        result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 8);
            /* Get the full buffer */
        length = le16_to_cpu(desc->wTotalLength);
        bigbuffer = kmalloc(length, GFP_KERNEL);
       
        /* Now that we know the length, get the whole thing */
        result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, length);       
        dev->rawdescriptors[cfgno] = bigbuffer;

                //分析配置
        result = usb_parse_configuration(&dev->config[cfgno], bigbuffer);
       
    }

}


5.分析设备的配置信息
  * 找到其中一种类型的描述符,则我们对该描述进行处理
  * 分析usb_interface;
int usb_parse_configuration(struct usb_config_descriptor *config, char *buffer)
{
   
    struct usb_descriptor_header *header;

    memcpy(config, buffer, USB_DT_CONFIG_SIZE);
    le16_to_cpus(&config->wTotalLength);
    size = config->wTotalLength;

   
    config->interface = (struct usb_interface *)
        kmalloc(config->bNumInterfaces *
        sizeof(struct usb_interface), GFP_KERNEL);
   
    memset(config->interface, 0,
           config->bNumInterfaces * sizeof(struct usb_interface));


    for (i = 0; i < config->bNumInterfaces; i++) {
        int numskipped, len;
        char *begin;

        /* Skip over the rest of the Class Specific or Vendor */
        /*  Specific descriptors */
        begin = buffer;
        numskipped = 0;
               //找到其中一种类型的描述符,则我们对该描述进行处理;
        while (size >= sizeof(struct usb_descriptor_header)) {
            header = (struct usb_descriptor_header *)buffer;

            /* If we find another "proper" descriptor then we're done  */
            if ((header->bDescriptorType == USB_DT_ENDPOINT) ||
                (header->bDescriptorType == USB_DT_INTERFACE) ||
                (header->bDescriptorType == USB_DT_CONFIG) ||
                (header->bDescriptorType == USB_DT_DEVICE))
                break;

            dbg("skipping descriptor 0x%X", header->bDescriptorType);
            numskipped++;

            buffer += header->bLength;
            size -= header->bLength;
        }
        if (numskipped)
            dbg("skipped %d class/vendor specific endpoint descriptors", numskipped);
       
        //分析usb_interface;
        retval = usb_parse_interface(config->interface + i, buffer, size);
        if (retval < 0)
            return retval;
        buffer += retval;
        size -= retval;
    }

    return size;
}

6.分析usb_interface
  * 分析endpoint 描述符结构
  * 为usb_interface_descriptor 分配空间;

static int usb_parse_interface(struct usb_interface *interface, unsigned char *buffer, int size)
{
    int i, len, numskipped, retval, parsed = 0;
    struct usb_descriptor_header *header;
    struct usb_interface_descriptor *ifp;
    unsigned char *begin;

    interface->act_altsetting = 0;
    interface->num_altsetting = 0;
    interface->max_altsetting = USB_ALTSETTINGALLOC;

    interface->altsetting = kmalloc(sizeof(struct usb_interface_descriptor) * interface->max_altsetting, GFP_KERNEL);
   
    if (!interface->altsetting) {
        err("couldn't kmalloc interface->altsetting");
        return -1;
    }

    while (size > 0) {
        if (interface->num_altsetting >= interface->max_altsetting) {
            void *ptr;
            int oldmas;

            oldmas = interface->max_altsetting;
            interface->max_altsetting += USB_ALTSETTINGALLOC;
            if (interface->max_altsetting > USB_MAXALTSETTING) {
                warn("too many alternate settings (max %d)",
                    USB_MAXALTSETTING);
                return -1;
            }

            ptr = interface->altsetting;
            interface->altsetting = kmalloc(sizeof(struct usb_interface_descriptor) * interface->max_altsetting, GFP_KERNEL);
            if (!interface->altsetting) {
                err("couldn't kmalloc interface->altsetting");
                interface->altsetting = ptr;
                return -1;
            }
            memcpy(interface->altsetting, ptr, sizeof(struct usb_interface_descriptor) * oldmas);

            kfree(ptr);
        }

        ifp = interface->altsetting + interface->num_altsetting;
        interface->num_altsetting++;

        memcpy(ifp, buffer, USB_DT_INTERFACE_SIZE);

        /* Skip over the interface */
        buffer += ifp->bLength;
        parsed += ifp->bLength;
        size -= ifp->bLength;

        begin = buffer;
        numskipped = 0;

               //跳过非标准的interface ,endpoint ,etc;
        /* Skip over any interface, class or vendor descriptors */
        while (size >= sizeof(struct usb_descriptor_header)) {
            header = (struct usb_descriptor_header *)buffer;

            if (header->bLength < 2) {
                err("invalid descriptor length of %d", header->bLength);
                return -1;
            }

            /* If we find another "proper" descriptor then we're done  */
            if ((header->bDescriptorType == USB_DT_INTERFACE) ||
                (header->bDescriptorType == USB_DT_ENDPOINT) ||
                (header->bDescriptorType == USB_DT_CONFIG) ||
                (header->bDescriptorType == USB_DT_DEVICE))
                break;

            numskipped++;

            buffer += header->bLength;
            parsed += header->bLength;
            size -= header->bLength;
        }

        if (numskipped)
            dbg("skipped %d class/vendor specific interface descriptors", numskipped);

        ifp->endpoint = (struct usb_endpoint_descriptor *)
            kmalloc(ifp->bNumEndpoints *
            sizeof(struct usb_endpoint_descriptor), GFP_KERNEL);

            //分析endpoint 描述符结构,把每一个endpoint 分配空间,并且读出配置信息!
        for (i = 0; i < ifp->bNumEndpoints; i++) {
            header = (struct usb_descriptor_header *)buffer;
       
            retval = usb_parse_endpoint(ifp->endpoint + i, buffer, size);
            if (retval < 0)
                return retval;

            buffer += retval;
            parsed += retval;
            size -= retval;
        }

               //检查一下是否buffer中还有其他的INTERFACE结构,否则就返回了;
        /* We check to see if it's an alternate to this one */
        ifp = (struct usb_interface_descriptor *)buffer;
        if (size < USB_DT_INTERFACE_SIZE ||ifp->bDescriptorType != USB_DT_INTERFACE ||!ifp->bAlternateSetting)
                return parsed;

       
    }

    return parsed;
}

7。总线驱动的安装(启动)
  * 通过call_policy, 构筑一个命令行"/sbin/hotplug usb"及必要的环境信息和创建一个内核线程,让该线程升级为进程,调用工具软件/sbin/hotplug装入USB HUB的驱动模块。
  * 通过usb_init()安装,在driver/usb/usb.c文件中
 
8。装载集中器的驱动过程及khubd
   在driver/usb/hub.c文件中,
    *注册HUB的驱动,struct usb_driver hub_driver;
    *启动一个内核线程,用于探测HUB的状态改变(插拔设备);

int usb_hub_init(void)
{
    int pid;

    if (usb_register(&hub_driver) < 0) {
        err("Unable to register USB hub driver");
        return -1;
    }

    pid = kernel_thread(usb_hub_thread, NULL,
        CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
    if (pid >= 0) {
        khubd_pid = pid;

        return 0;
    }

    /* Fall through if kernel_thread failed */
    usb_deregister(&hub_driver);
    err("failed to start usb_hub_thread");

    return -1;
}

在函数hub_probe()->usb_hub_configure()注册HUB的complete_handler函数,在complete_handler中把将该HUB加入hub_event_list链表,该链表提供给usb_hub_thread()->usb_hub_events()处理。

通过usb_hub_events()探测HUB的端口状态改变,即可知道USB DEVICE的插入和拔除 。则可进行一系列的操作了。。。。

9。问题
    (1) 注册HUB的驱动,struct usb_driver hub_driver(driver/usb/hub.c)但是没有file_operation 成员,在hub_probe中也没有注册相关的可被文件操作支持的其他设备类型;那么HUB的读、写操作是如何实现的呢?
    (2) 在usb_hub_events()(driver/usb/hub.c)处理中,hub_event_list链表被删除了,第一次提交URB触发hub_irq完成添加,那么后来又是么添加进去的?

阅读(4449) | 评论(2) | 转发(1) |
0

上一篇:双链表的作用kernel:2.4.20

下一篇:小记

给主人留下些什么吧!~~

chinaunix网友2009-04-28 23:06:11

irq是什么,是中断服务用的。提交urb包后,设备就会有反应的,而这种反应会给以中断的形式上报给cpu,cpu根据你注册的中断服务程序hub_irq来进行响应的。