好~ 现在万事俱备,只欠uhci硬件的注册了
现在谈一下uhci硬件的组成,uhci的硬件分为两个大的部分,主机控制器和根集线器,如下图
当提交uhci硬件的注册到pci总线后,经过一轮匹配,终于找到了uhci,进入到uhci_pci_driver->probe这个函数下
usb_hcd_pci_probe在/drivers/usb/host/uhci-hcd.c中
int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { struct hc_driver *driver; struct usb_hcd *hcd; int retval; //判断内核参数是否启用USB if (usb_disabled()) return -ENODEV; //判断匹配表是否存在,该表为uhci_pci_driver->id_table中的内容 if (!id) return -EINVAL; //取得匹配表中的私有结构,为uhci_driver driver = (struct hc_driver *)id->driver_data; //私有结构不存在则返回错误 if (!driver) return -EINVAL; //使能pci设备 if (pci_enable_device(dev) < 0) return -ENODEV; //设置电源为正常状态 dev->current_state = PCI_D0; //检测是否有中断号 if (!dev->irq) { dev_err(&dev->dev,"Found HC with no IRQ. Check BIOS/PCI %s setup!\n",pci_name(dev)); retval = -ENODEV; goto err1; } //创建usb_hcd结构 hcd = usb_create_hcd(driver, &dev->dev, pci_name(dev)); //检测创建是否成功 if (!hcd) { retval = -ENOMEM; goto err1; } //检测是否需要映射I/O内存空间,EHCI和OHCI这两个主机控制器有自己的内存空间,访问的时候需要映射到计算机的内存空间中才能对设备进行操作,而UHCI没有自己的内存空间,对UHCI的操作是对IO端口的访问,所以UHCI需要分配IO端口空间 if (driver->flags & HCD_MEMORY) { /* EHCI, OHCI */ //这两个设备需要映射I/O内存空间 hcd->rsrc_start = pci_resource_start(dev, 0); hcd->rsrc_len = pci_resource_len(dev, 0); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,driver->description)) { dev_dbg(&dev->dev, "controller already in use\n"); retval = -EBUSY; goto err2; } hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); if (hcd->regs == NULL) { dev_dbg(&dev->dev, "error mapping memory\n"); retval = -EFAULT; goto err3; } } else { /* UHCI */ //UHCI需要映射I/O端口 int region; for (region = 0; region < PCI_ROM_RESOURCE; region++) { //获取PCI I/O区域region号资源标记并判断是否为IO接口资源 if (!(pci_resource_flags(dev, region) & IORESOURCE_IO)) continue; //获取PCI I/O区域refion的首地址 hcd->rsrc_start = pci_resource_start(dev, region); //计算资源长度 hcd->rsrc_len = pci_resource_len(dev, region); //申请映射I/O端口 if (request_region(hcd->rsrc_start, hcd->rsrc_len,driver->description)) break; } //没有IO接口资源则返回出错 if (region == PCI_ROM_RESOURCE) { dev_dbg(&dev->dev, "no i/o regions available\n"); retval = -EBUSY; goto err1; } } //开始竞争总线 pci_set_master(dev); //PCI层初始化完毕,进入Host Controller的初始化 retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED); if (retval != 0) goto err4; return retval; }
|
现在看看usb_create_hcd,这个函数创建每个主机控制器必备的usb_hcd结构
usb_create_hcd在/drivers/usb/core/hcd.c中
struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, struct device *dev, char *bus_name) { struct usb_hcd *hcd; //申请空间,hcd_priv_size为动态的附加结构长度, 附加在usb_hcd的尾部 // 这段空间用于主机控制器的私有结构,uhci的私有结构为uhci_hcd,用于描述uhci的属性 hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL); if (!hcd) { dev_dbg (dev, "hcd alloc failed\n"); return NULL; } //连接usb_hcd到pci_device dev_set_drvdata(dev, hcd); //初始化引用计数器 kref_init(&hcd->kref); //初始化usb总线 usb_bus_init(&hcd->self); //连接pci_device到usb_hcd的usb_bus上 hcd->self.controller = dev; //设置usb_bus名称 hcd->self.bus_name = bus_name; //设置是否使用dma hcd->self.uses_dma = (dev->dma_mask != NULL); //hcd->rh_timer的注释为drives root-hub polling //字面意思是一个用于记时执行某函数的结构,例如5ms后执行指定函数 init_timer(&hcd->rh_timer); hcd->rh_timer.function = rh_timer_func; hcd->rh_timer.data = (unsigned long) hcd; #ifdef CONFIG_PM INIT_WORK(&hcd->wakeup_work, hcd_resume_work); #endif //连接hc_driver到usb_hcd上 hcd->driver = driver; //设置设备名称 hcd->product_desc = (driver->product_desc) ? driver->product_desc : "USB Host Controller"; return hcd; }
|
分配并初始化好的usb_hcd结构如下,其中bus_name和uses_dma因为我还没看pci,所以这两个值不清楚,不过在uhci中dma是肯定会用到的
为什么会有个白框呢?还记得hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);
这句么,这个白框就是所申请的hcd_priv_size大小的空间,里面是什么我们还不知道,所以是一片白的,等用到的时候我再把它画出来
hcd准备好之后,就要进入更深一个层次的初始化了, usb_add_hcd负责这部分的工作
usb_add_hcd在/drivers/usb/core/hcd.c中
int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags) { int retval; struct usb_device *rhdev; dev_info(hcd->self.controller, "%s\n", hcd->product_desc); //设置默认的批准标志,为无线设备设0,否则设1 hcd->authorized_default = hcd->wireless? 0 : 1; //设置主机控制器的状态为硬件可用 set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); /* HC is in reset state, but accessible. Now do the one-time init, * bottom up so that hcds can customize the root hubs before khubd * starts talking to them. (Note, bus id is assigned early too.) */ //初始化缓冲池 if ((retval = hcd_buffer_create(hcd)) != 0) { dev_dbg(hcd->self.controller, "pool alloc failed\n"); return retval; } //注册USB host controller if ((retval = usb_register_bus(&hcd->self)) < 0) goto err_register_bus; //分配一个设备描述符用于根设备 if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) { dev_err(hcd->self.controller, "unable to allocate root hub\n"); retval = -ENOMEM; goto err_allocate_root_hub; } //设置最高速度 rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH : USB_SPEED_FULL; //连接根集线设备到usb_bus上 hcd->self.root_hub = rhdev; /* wakeup flag init defaults to "everything works" for root hubs, * but drivers can override it in reset() if needed, along with * recording the overall controller's system wakeup capability. */ //打开ROOT HUB的WAKEUP device_init_wakeup(&rhdev->dev, 1); /* "reset" is misnamed; its role is now one-time init. the controller * should already have been reset (and boot firmware kicked off etc). */ //复位设备 if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) { dev_err(hcd->self.controller, "can't setup\n"); goto err_hcd_driver_setup; } /* NOTE: root hub and controller capabilities may not be the same */ if (device_can_wakeup(hcd->self.controller) && device_can_wakeup(&hcd->self.root_hub->dev)) dev_dbg(hcd->self.controller, "supports USB remote wakeup\n"); /* enable irqs just before we start the controller */ //判断是否有中断处理函数 if (hcd->driver->irq) { /* IRQF_DISABLED doesn't work as advertised when used together * with IRQF_SHARED. As usb_hcd_irq() will always disable * interrupts we can remove it here. */ irqflags &= ~IRQF_DISABLED; //设置中断描述串 snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d", hcd->driver->description, hcd->self.busnum); //安装中断处理函数 if ((retval = request_irq(irqnum, &usb_hcd_irq, irqflags,hcd->irq_descr, hcd)) != 0) { dev_err(hcd->self.controller,"request interrupt %d failed\n", irqnum); goto err_request_irq; } hcd->irq = irqnum; dev_info(hcd->self.controller, "irq %d, %s 0x%08llx\n", irqnum, (hcd->driver->flags & HCD_MEMORY) ?"io mem" : "io base", (unsigned long long)hcd->rsrc_start); } else { hcd->irq = -1; if (hcd->rsrc_start) dev_info(hcd->self.controller, "%s 0x%08llx\n", (hcd->driver->flags & HCD_MEMORY) ? "io mem" : "io base", (unsigned long long)hcd->rsrc_start); } //启动设备 if ((retval = hcd->driver->start(hcd)) < 0) { dev_err(hcd->self.controller, "startup error %d\n", retval); goto err_hcd_driver_start; } /* starting here, usbcore will pay attention to this root hub */ rhdev->bus_mA = min(500u, hcd->power_budget); //注册根集线器 if ((retval = register_root_hub(hcd)) != 0) goto err_register_root_hub; //添加设备属性文件 retval = sysfs_create_group(&rhdev->dev.kobj, &usb_bus_attr_group); if (retval < 0) { printk(KERN_ERR "Cannot register USB bus sysfs attributes: %d\n", retval); goto error_create_attr_group; } if (hcd->uses_new_polling && hcd->poll_rh) //查询设备状态 usb_hcd_poll_rh_status(hcd); return retval; }
|
首先是hcd_buffer_create,这个函数用于初始化主机控制器的dma内存池,为以后的dma内存分配做准备
hcd_buffer_create在/drivers/usb/core/buffer.c中
int hcd_buffer_create(struct usb_hcd *hcd) { char name[16]; int i, size; if (!hcd->self.controller->dma_mask && !(hcd->driver->flags & HCD_LOCAL_MEM)) return 0; //创建4个不同大小的缓冲池,为不同大小的dma请求分配合适的dma内存 for (i = 0; i < HCD_BUFFER_POOLS; i++) { size = pool_max[i]; if (!size) continue; snprintf(name, sizeof name, "buffer-%d", size); //创建DMA内存池,此时还没有真正分配空间 hcd->pool[i] = dma_pool_create(name, hcd->self.controller,size, size, 0); if (!hcd->pool [i]) { hcd_buffer_destroy(hcd); return -ENOMEM; } } return 0; }
|
usb_register_bus用于注册uhci自己的总线,管理连接到uhci的所有设备
static int usb_register_bus(struct usb_bus *bus) { int result = -E2BIG; int busnum; mutex_lock(&usb_bus_list_lock); //在usbmap位图中寻找未占用的位 busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1); //大于限制则返回错误 if (busnum >= USB_MAXBUS) { printk (KERN_ERR "%s: too many buses\n", usbcore_name); goto error_find_busnum; } //置相应位为已经使用 set_bit (busnum, busmap.busmap); //设置uhci总线的地址号 bus->busnum = busnum; //创建一个usb_host%目录,用于表达该设备为主机控制器 bus->dev = device_create_drvdata(usb_host_class, bus->controller, MKDEV(0, 0), bus,"usb_host%d", busnum); result = PTR_ERR(bus->dev); if (IS_ERR(bus->dev)) goto error_create_class_dev; /* Add it to the local list of buses */ //添加到usb_bus_list的链表中 list_add (&bus->bus_list, &usb_bus_list); mutex_unlock(&usb_bus_list_lock); //新型的总线通知机制,我还没研究,放着等我弄懂先吧 usb_notify_add_bus(bus); dev_info (bus->controller, "new USB bus registered, assigned bus " "number %d\n", bus->busnum); return 0; }
|
这个函数执行完后的结构图如下
然后到usb_alloc_dev,这个函数负责分配一个usb设备数据结构,在这里,这个usb数据结构用于描述主机控制器的根集线器,这个根集线器也是usb设备的一种
usb_alloc_dev在/drivers/usb/core/usb.c中
struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1) { struct usb_device *dev; //取得设备所连接的主机控制器结构 struct usb_hcd *usb_hcd = container_of(bus, struct usb_hcd, self); unsigned root_hub = 0; //分配usb_device结构 dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return NULL; //增加主机控制器的设备计数器 if (!usb_get_hcd(bus_to_hcd(bus))) { kfree(dev); return NULL; } //初始化设备 device_initialize(&dev->dev); //设置所属总线类型,这里是重点,为什么匹配的时候不是匹配pci总线或者其它总线,因为在这里设置了这个设备属于usb总线 dev->dev.bus = &usb_bus_type; //设置设备自身类型,这里也是重点,这里设置了设备的类型,是设备还是接口,对于usb驱动来说分两类,设备驱动和接口驱动, usb_generic_driver为usb设备驱动,hub为usb接口驱动 dev->dev.type = &usb_device_type; //设置属性组,也就是目录下的描述文件 dev->dev.groups = usb_device_groups; //设置是否使用dma dev->dev.dma_mask = bus->controller->dma_mask; set_dev_node(&dev->dev, dev_to_node(bus->controller)); //设置设备状态为连接 dev->state = USB_STATE_ATTACHED; atomic_set(&dev->urbnum, 0) INIT_LIST_HEAD(&dev->ep0.urb_list); //初始化端点0 dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE; dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT; /* ep0 maxpacket comes later, from device descriptor */ usb_enable_endpoint(dev, &dev->ep0); //设置URB发送允许位 dev->can_submit = 1; /* Save readable and stable topology id, distinguishing devices * by location for diagnostics, tools, driver model, etc. The * string is a path along hub ports, from the root. Each device's * dev->devpath will be stable until USB is re-cabled, and hubs * are often labeled with these port numbers. The bus_id isn't * as stable: bus->busnum changes easily from modprobe order, * cardbus or pci hotplugging, and so on. */ //检测是否为根集线器,分配不同的名字 if (unlikely(!parent)) { //设置设备的层次为0 dev->devpath[0] = '0'; //设置父设备,这是根集线器,没有父设备,所以为NULL dev->dev.parent = bus->controller; sprintf(&dev->dev.bus_id[0], "usb%d", bus->busnum); root_hub = 1; } else { /* match any labeling on the hubs; it's one-based */ if (parent->devpath[0] == '0') snprintf(dev->devpath, sizeof dev->devpath,"%d", port1); else snprintf(dev->devpath, sizeof dev->devpath,"%s.%d", parent->devpath, port1); dev->dev.parent = &parent->dev; sprintf(&dev->dev.bus_id[0], "%d-%s",bus->busnum, dev->devpath); /* hub driver sets up TT records */ } //设置usb设备的节点号 dev->portnum = port1; //设置usb设备的usb总线 dev->bus = bus; //设置上层hub dev->parent = parent; INIT_LIST_HEAD(&dev->filelist); //这部分为电源管理机制,我们不关心这部分内容 #ifdef CONFIG_PM mutex_init(&dev->pm_mutex); INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work); dev->autosuspend_delay = usb_autosuspend_delay * HZ; dev->connect_time = jiffies; dev->active_duration = -jiffies; #endif //检测是否为根集线器,这里为根集线器 if (root_hub) /* Root hub always ok [and always wired] */ //设置设备的批准标志为1 dev->authorized = 1; else { dev->authorized = usb_hcd->authorized_default; dev->wusb = usb_bus_is_wusb(bus)? 1 : 0; } return dev; }
|
执行完后的结构图如下
现在到hcd->driver->reset,这个函数为uhci_init
uhci_init在/drivers/usb/host/uhci-hcd.c中
static int uhci_init(struct usb_hcd *hcd) { //这里解开白框的秘密了,原来是一个uhci_hcd结构,如果大家仔细的话,其实这个结构很早就出现了,在uhci_driver中,hcd_priv_size = sizeof(struct uhci_hcd), hcd_priv_size的值正是uhci_hcd的大小 struct uhci_hcd *uhci = hcd_to_uhci(hcd); int port; //取得IO端口资源区长度 unsigned io_size = (unsigned) hcd->rsrc_len; //取得IO端口资源区起始地址 uhci->io_addr = (unsigned long) hcd->rsrc_start; /* The UHCI spec says devices must have 2 ports, and goes on to say * they may have more but gives no way to determine how many there * are. However according to the UHCI spec, Bit 7 of the port * status and control register is always set to 1. So we try to * use this to our advantage. Another common failure mode when * a nonexistent register is addressed is to return all ones, so * we test for that also. */ //判断根集线器有几个端口 for (port = 0; port < (io_size - USBPORTSC1) / 2; port++) { unsigned int portstatus; //inw - 读IO端口 portstatus = inw(uhci->io_addr + USBPORTSC1 + (port * 2)); //第7位不为1或者全为1则说明端口寄存器,判断完毕 if (!(portstatus & 0x0080) || portstatus == 0xffff) break; } if (debug) dev_info(uhci_dev(uhci), "detected %d ports\n", port); /* Anything greater than 7 is weird so we'll ignore it. */ //多于7个端口则强制设置为2个 if (port > UHCI_RH_MAXCHILD) { dev_info(uhci_dev(uhci), "port count misdetected? ""forcing to 2 ports\n"); port = 2; } //记录端口数 uhci->rh_numports = port; /* Kick BIOS off this hardware and reset if the controller * isn't already safely quiescent. */ //判断UHCI是否需要重置 check_and_reset_hc(uhci); return 0; }
|
阅读(2167) | 评论(0) | 转发(0) |