免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1615 | 回复: 0
打印 上一主题 下一主题

USB控制器的初始化和枚举 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2005-12-16 22:46 |只看该作者 |倒序浏览
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 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 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  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 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 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 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 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 bNumEndpoints; i++) {
            header = (struct usb_descriptor_header *)buffer;
       
            retval = usb_parse_endpoint(ifp->endpoint + i, buffer, size);
            if (retval 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) {
        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完成添加,那么后来又是么添加进去的?


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/4591/showart_61901.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP