USB控制器的初始化和枚举

                                                                ||
                                                                \/
                                                              Go
 
 
USB控制器的初始化和枚举
2005年12月16日 14:46 来源:ChinaUnix博客 作者:windguy   编辑:周荣茂

    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完成添加,那么后来又是么添加进去的?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值