1. 云栖社区>
  2. PHP教程>
  3. 正文

USB主机控制器驱动——OHCI分析

作者:用户 来源:互联网 时间:2017-12-01 16:01:28

驱动usb控制器主机分析OHCI

USB主机控制器驱动——OHCI分析 - 摘要: 本文讲的是USB主机控制器驱动——OHCI分析,本文以 2440-ohci 驱动为例,简单分析 USB 主机控制器驱动 根 Hub 的注册过程,以及 USB设备的枚举过程,并不涉及USB协议,单纯分析驱动框架流程。无论是hub还是普通的usb设备,它们注册到 usb_bus_type 都

本文以 2440-ohci 驱动为例,简单分析 USB 主机控制器驱动 根 Hub 的注册过程,以及 USB设备的枚举过程,并不涉及USB协议,单纯分析驱动框架流程。无论是hub还是普通的usb设备,它们注册到 usb_bus_type 都会经历两次 Match ,因为第一次注册进来时,是将整个设备作为一个 device 注册,然后在通用的 devices 驱动程序 usb_generic_driver
的 generic_probe 函数中,将该设备的所有接口进行设置并将这些接口注册到 usb_bus_type 。如果是Hub设备的接口,则会调用 hub_probe,如果是其他设备则调用 xx_probe 函数。如果是 Hub 的话,usb主机会监测hub端口变化,如果有变化会分配一个usb_devices 注册到 usb_bus_type 重复前边的步骤。
USB主机控制器驱动——OHCI分析

首先,整个驱动框架的开始,是基于 platform 平台总线的。

struct platform_device s3c_device_usb = {.name  = "s3c2410-ohci",.id  = -1,.num_resources  = ARRAY_SIZE(s3c_usb_resource),.resource  = s3c_usb_resource,.dev      = {.dma_mask = &s3c_device_usb_dmamask,.coherent_dma_mask = 0xffffffffUL}};
static struct platform_driver ohci_hcd_s3c2410_driver = {.probe= ohci_hcd_s3c2410_drv_probe,.remove= ohci_hcd_s3c2410_drv_remove,.shutdown= usb_hcd_platform_shutdown,/*.suspend= ohci_hcd_s3c2410_drv_suspend, *//*.resume= ohci_hcd_s3c2410_drv_resume, */.driver= {.owner= THIS_MODULE,.name= "s3c2410-ohci",},};
platform 平台总线模型,这里定义了 platform_device 和 platform_driver ,后面将这俩注册到 platform_bus_type 时,就会根据它们的名字来匹配,显然,它们的名字都是 “s3c2410-ohci” ,匹配成功后,便会调用到 ohci_hcd_s3c2410_drv_probe 函数。在看 probe
函数之前,我们先看看设备侧提供的信息。

static struct resource s3c_usb_resource[] = {[0] = {.start = S3C_PA_USBHOST,.end   = S3C_PA_USBHOST + 0x100 - 1,.flags = IORESOURCE_MEM,},[1] = {.start = IRQ_USBH,.end   = IRQ_USBH,.flags = IORESOURCE_IRQ,}};
resource 中指定了 2440 主机控制器的寄存器范围,以及中断。

int usb_simtec_init(void){s3c_device_usb.dev.platform_data = &usb_simtec_info;}static struct s3c2410_hcd_info usb_simtec_info = {.port[0]= {.flags= S3C_HCDFLG_USED},.port[1]= {.flags= S3C_HCDFLG_USED},.power_control= usb_simtec_powercontrol,.enable_oc= usb_simtec_enableoc,};
这里,指定了一些额外的信息,保存在 dev.platform_data 中,后边我们再来看他们是干什么用的。下面来看 probe 函数。

static int ohci_hcd_s3c2410_drv_probe(struct platform_device *pdev){return usb_hcd_s3c2410_probe(&ohci_s3c2410_hc_driver, pdev);}
static int usb_hcd_s3c2410_probe (const struct hc_driver *driver,  struct platform_device *dev){struct usb_hcd *hcd = NULL;int retval;/* 设置GPG4输出1 mini2440 jz2440好像均不需要 */s3c2410_usb_set_power(dev->dev.platform_data, 1, 1);s3c2410_usb_set_power(dev->dev.platform_data, 2, 1);/* 创建usb_hcd 绑定 usb_driver等 */hcd = usb_create_hcd(driver, &dev->dev, "s3c24xx");/* 主机控制寄存器 起始地址 结束地址 */hcd->rsrc_start = dev->resource[0].start;hcd->rsrc_len   = dev->resource[0].end - dev->resource[0].start + 1;/* 申请IO空间 */if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {...}/* 获得usb-host 时钟 */clk = clk_get(&dev->dev, "usb-host");/* 获得 usb-bus-host 时钟 */usb_clk = clk_get(&dev->dev, "usb-bus-host");/* 使能时钟 使能过流检查 */s3c2410_start_hc(dev, hcd);/* Ioremap */hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);ohci_hcd_init(hcd_to_ohci(hcd));retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED);return 0;}
前边第一个 probe 函数仅仅是一个中转,usb_hcd_s3c2410_probe 它才是真正的 probe 函数,主要工作就是分配一个 usb_hcd 结构、设置然后 usb_add_hcd 。

struct usb_hcd {/* * housekeeping */struct usb_busself;/* hcd is-a bus */struct krefkref;/* reference counter */const char*product_desc;/* product/vendor string */charirq_descr[24];/* driver + bus # */struct timer_listrh_timer;/* drives root-hub polling */struct urb*status_urb;/* the current status urb */#ifdef CONFIG_PMstruct work_structwakeup_work;/* for remote wakeup */#endif/* * hardware info/state */const struct hc_driver*driver;/* hw-specific hooks *//* Flags that need to be manipulated atomically */unsigned longflags;#define HCD_FLAG_HW_ACCESSIBLE0x00000001#define HCD_FLAG_SAW_IRQ0x00000002unsignedrh_registered:1;/* is root hub registered? *//* The next flag is a stopgap, to be removed when all the HCDs * support the new root-hub polling mechanism. */unsigneduses_new_polling:1;unsignedpoll_rh:1;/* poll for rh status? */unsignedpoll_pending:1;/* status has changed? */unsignedwireless:1;/* Wireless USB HCD */unsignedauthorized_default:1;unsignedhas_tt:1;/* Integrated TT in root hub */intirq;/* irq allocated */void __iomem*regs;/* device memory/io */u64rsrc_start;/* memory/io resource start */u64rsrc_len;/* memory/io resource length */unsignedpower_budget;/* in mA, 0 = no limit */#define HCD_BUFFER_POOLS4struct dma_pool*pool [HCD_BUFFER_POOLS];intstate;#define__ACTIVE0x01#define__SUSPEND0x04#define__TRANSIENT0x80#defineHC_STATE_HALT0#defineHC_STATE_RUNNING(__ACTIVE)#defineHC_STATE_QUIESCING(__SUSPEND|__TRANSIENT|__ACTIVE)#defineHC_STATE_RESUMING(__SUSPEND|__TRANSIENT)#defineHC_STATE_SUSPENDED(__SUSPEND)#defineHC_IS_RUNNING(state) ((state) & __ACTIVE)#defineHC_IS_SUSPENDED(state) ((state) & __SUSPEND)/* more shared queuing code would be good; it should support * smarter scheduling, handle transaction translators, etc; * input size of periodic table to an interrupt scheduler. * (ohci 32, uhci 1024, ehci 256/512/1024). *//* The HC driver's private data is stored at the end of * this structure. */unsigned long hcd_priv[0]__attribute__ ((aligned(sizeof(unsigned long))));};
usb_hcd —— USB Host Controller Driver,同时,一个主机控制器驱动对应一条 usb_bus 。

struct usb_bus {struct device *controller;/* host/master side hardware */int busnum;/* Bus number (in order of reg) */const char *bus_name;/* stable id (PCI slot_name etc) */u8 uses_dma;/* Does the host controller use DMA? */u8 otg_port;/* 0, or number of OTG/HNP port */unsigned is_b_host:1;/* true during some HNP roleswitches */unsigned b_hnp_enable:1;/* OTG: did A-Host enable HNP? */int devnum_next;/* Next open device number in * round-robin allocation */struct usb_devmap devmap;/* device address allocation map */struct usb_device *root_hub;/* Root hub */struct list_head bus_list;/* list of busses */int bandwidth_allocated;/* on this bus: how much of the time * reserved for periodic (intr/iso) * requests is used, on average? * Units: microseconds/frame. * Limits: Full/low speed reserve 90%, * while high speed reserves 80%. */int bandwidth_int_reqs;/* number of Interrupt requests */int bandwidth_isoc_reqs;/* number of Isoc. requests */#ifdef CONFIG_USB_DEVICEFSstruct dentry *usbfs_dentry;/* usbfs dentry entry for the bus */#endif#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)struct mon_bus *mon_bus;/* non-null when associated */int monitored;/* non-zero when monitored */#endif};

hcd的分配过程

struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,struct device *dev, const char *bus_name){struct usb_hcd *hcd;/* 分配一个 usb_hcd + driver->hcd_priv_size 空间 */hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);/* dev->p->driver_data = hcd; */dev_set_drvdata(dev, hcd);kref_init(&hcd->kref);/* 初始化 usb_bus ,一个主机控制器对应一个 usb_bus */usb_bus_init(&hcd->self);/* 设置 usb_bus */hcd->self.controller = dev;hcd->self.bus_name = bus_name;hcd->self.uses_dma = (dev->dma_mask != NULL);/* 初始化 根Hub poll定时器 */init_timer(&hcd->rh_timer);hcd->rh_timer.function = rh_timer_func;hcd->rh_timer.data = (unsigned long) hcd;#ifdef CONFIG_PMINIT_WORK(&hcd->wakeup_work, hcd_resume_work);#endif/* 绑定 hc_driver */hcd->driver = driver;hcd->product_desc = (driver->product_desc) ? driver->product_desc :"USB Host Controller";return hcd;}
static void usb_bus_init (struct usb_bus *bus){memset (&bus->devmap, 0, sizeof(struct usb_devmap));bus->devnum_next = 1;bus->root_hub = NULL;bus->busnum = -1;bus->bandwidth_allocated = 0;bus->bandwidth_int_reqs  = 0;bus->bandwidth_isoc_reqs = 0;INIT_LIST_HEAD (&bus->bus_list);}

整个Probe函数里干了那些事:

1、创建一个 usb_hcd

2、usb_bus_init ,初始化 usb_hcd 对应的 usb_bus ,bus->devmap 清零,根 Hub 指向 NULL等

3、设置 usb_hcd.usb_bus

3.1 hcd.usb_bus.controller = s3c_device_usb.dev (最开始创建的平台device)

3.2 hcd.usb_bus.name = “s3c24xx”

4、设置 usb_hcd.rh_timer

5、设置 usb_hcd.driver = ohci_s3c2410_hc_driver

6、根据 resource 资源,设置usb_hcd.rsrc_start、usb_hcd.rsrc_len

7、使能时钟

8、ioremap 、申请 io 空间

9、usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED)

int usb_add_hcd(struct usb_hcd *hcd,unsigned int irqnum, unsigned long irqflags){int retval;struct usb_device *rhdev;/* 无线USB。 */hcd->authorized_default = hcd->wireless? 0 : 1;set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);/* hcd->pool[i] = dma_pool_create(name, hcd->self.controller,size, size, 0); */if ((retval = hcd_buffer_create(hcd)) != 0) {...}/* * busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1); * bus->busnum = busnum; * list_add (&bus->bus_list, &usb_bus_list); */if ((retval = usb_register_bus(&hcd->self)) < 0)goto err_register_bus;/* 根 Hub */if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) {...}rhdev->speed = USB_SPEED_FULL;hcd->self.root_hub = rhdev;/* dev->power.can_wakeup = dev->power.should_wakeup = 1 */device_init_wakeup(&rhdev->dev, 1);if (hcd->driver->irq) {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) {...}hcd->irq = irqnum;}hcd->driver->start(hcd));/* 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 (hcd->uses_new_polling && hcd->poll_rh)usb_hcd_poll_rh_status(hcd);return retval;}

usb_hcd_add 干了哪些事:

1、hcd_buffer_create(hcd)

2、usb_register_bus(&hcd->self) ,将 usb_hcd.usb_bus 注册到全局链表 usb_bus_list

3、为根 hub 分配一个 usb_device 结构(内核中,所有的真实的usb设备(Hub,鼠标...)都用usb_device结构来描述)

4、注册根 Hub 的 usb_device 结构到 usb_bus_type

弄了半天,神神秘秘的USB主机控制器也只不过是分配了一个 usb_hcd 结构体,为它的 根hub 分配了一个usb_device 结构体,注册到 usb_bus_type 罢了,后边是 根Hub 的注册和设备枚举过程了。

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);device_initialize(&dev->dev);/* usb_bus_type */dev->dev.bus = &usb_bus_type;/* 属性文件 */dev->dev.type = &usb_device_type;dev->dev.groups = usb_device_groups;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);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, false);dev->can_submit = 1;/* 如果是根Hub */if (unlikely(!parent)) {...} else {...}<span style="white-space:pre"></span>...}return dev;}
注意一下几点:

1、dev->dev.bus = &usb_bus_type 这里出现了一条“总线模型”中的总线,注意和 usb_bus 完全没关系。相当于hub 、鼠标等 usb 设备是注册到 usb_bus_type 的,前面我们说的控制器的驱动是注册到 platform_bus_type 的。

2、dev->dev.type = &usb_device_type ;后边Match函数中会用到

3、dev->state = USB_STATE_ATTACHED; 根Hub是和控制器连在一起的,必然已经连接上了

ATTACHED :表示设备已经连接到 hub 接口上了。

Powered :表示加电状态

Default :表示默认状态,在powered状态之后,设备必须受到一个复位信号并成功复位后,才能使用默认地址回应主机发过来的设备描述符的请求。

Address:表示主机分配了一个唯一的地址给设备。

Configured :表示设备已经被主机配置过了,此时,主机可以使用设备提供的所有功能。

Supended :表示挂起状态,设备在指定的时间内没有传输,就要进入挂起状态。

4、dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT ,我们说一个 usb 设备有多个配置,每个配置又有多个接口,每个接口有多个 端点。但是端点 0 比较特殊,它是整个 usb 设备共享的,因此它的表述符直接在 usb_device中。

5、dev->bus = bus ,根 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;/* 设备地址 */usb_dev->devnum = devnum;usb_dev->bus->devnum_next = devnum + 1;memset (&usb_dev->bus->devmap.devicemap, 0,sizeof usb_dev->bus->devmap.devicemap);set_bit (devnum, usb_dev->bus->devmap.devicemap);usb_set_device_state(usb_dev, USB_STATE_ADDRESS);mutex_lock(&usb_bus_list_lock);usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);/* 获得设备描述符 */retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);/* 进一步设置,然后 add_device */retval = usb_new_device (usb_dev);return retval;}
1、usb_dev->devnum = 1 ;根 Hub 的地址为1 ,usb_dev->bus->devmap.devicemap ,表示哪些设备地址被占用了,以及这个 hub 一共支持多少设备。

2、usb_set_device_state(usb_dev, USB_STATE_ADDRESS); 变更状态

3、usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE); 获得设备描述符,保存在usb_dev.descriptor 中。

4、usb_new_device 进一步设置(获得配置、端点描述符等),将 usb_device 注册到 usb_bus_type 。

int usb_get_device_descriptor(struct usb_device *dev, unsigned int size){struct usb_device_descriptor *desc;int ret;if (size > sizeof(*desc))return -EINVAL;desc = kmalloc(sizeof(*desc), GFP_NOIO);if (!desc)return -ENOMEM;ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, size);if (ret >= 0)memcpy(&dev->descriptor, desc, size);kfree(desc);return ret;}


int usb_get_descriptor(struct usb_device *dev, unsigned char type,       unsigned char index, void *buf, int size){int i;int result;memset(buf, 0, size);/* Make sure we parse really received data */for (i = 0; i < 3; ++i) {/* retry on length 0 or error; some devices are flakey */result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,(type << 8) + index, 0, buf, size,USB_CTRL_GET_TIMEOUT);if (result <= 0 && result != -ETIMEDOUT)continue;if (result > 1 && ((u8 *)buf)[1] != type) {result = -ENODATA;continue;}break;}return result;}
注意,这里是将整个根Hub作为一个 device 注册到 usb_bus_type ,后边还会将Hub的接口注册进去

int usb_new_device(struct usb_device *udev){int err;/* Increment the parent's count of unsuspended children */if (udev->parent)usb_autoresume_device(udev->parent);usb_detect_quirks(udev);/* Determine quirks */err = usb_configure_device(udev);/* detect & probe dev/intfs *//* export the usbdev device-node for libusb */udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));/* Tell the world! */announce_device(udev);/* Register the device.  The device driver is responsible * for configuring the device and invoking the add-device * notifier chain (used by usbfs and possibly others). */err = device_add(&udev->dev);(void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);return err;}
static int usb_configure_device(struct usb_device *udev){usb_get_configuration(udev);}


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 (ncfg > USB_MAXCONFIG) {dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;}length = ncfg * sizeof(struct usb_host_config);dev->config = kzalloc(length, GFP_KERNEL);length = ncfg * sizeof(char *);dev->rawdescriptors = kzalloc(length, GFP_KERNEL);buffer = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);desc = (struct usb_config_descriptor *)buffer;result = 0;for (; cfgno < ncfg; cfgno++) {/* 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);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);result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, length);dev->rawdescriptors[cfgno] = bigbuffer;/* 解析配置描述符 */result = usb_parse_configuration(&dev->dev, cfgno, &dev->config[cfgno], bigbuffer, length);}result = 0;return result;}


也就是说,在将 usb_device 注册到 usb_bus_type 时,它所有的描述符信息都已经获取到了。

整个控制器驱动一路走下来,最后 注册了一个根 Hub 的 usb_device 到 usb_bus_type 。有必要看一下usb_bus_type ,它的 match 函数,将注册进来的 device 和 接口分开处理。第一次注册的我们说是 device ,那么看看对应的 driver 是啥。

struct bus_type usb_bus_type = {.name ="usb",.match =usb_device_match,.uevent =usb_uevent,};
static int usb_device_match(struct device *dev, struct device_driver *drv){/* return dev->type == &usb_device_type; */if (is_usb_device(dev)) {/* return container_of(drv, struct usbdrv_wrap, driver)->for_devices; */if (!is_usb_device_driver(drv))return 0;/* TODO: Add real matching code */return 1;} else if (is_usb_interface(dev)) {....}return 0;}

如果设备的 dev->type == &usb_device_type ,且 driver.for_devices == 1 ,直接匹配成功。匹配成功之后便会调用driver侧的 probe 函数了。

struct usb_device_driver usb_generic_driver = {.name ="usb",.probe = generic_probe,.disconnect = generic_disconnect,#ifdefCONFIG_PM.suspend = generic_suspend,.resume = generic_resume,#endif.supports_autosuspend = 1,};
static int generic_probe(struct usb_device *udev){int err, c;c = usb_choose_configuration(udev);err = usb_set_configuration(udev, c);/* USB device state == configured ... usable */usb_notify_add_device(udev);return 0;}
int usb_set_configuration(struct usb_device *dev, int configuration){int i, ret;struct usb_host_config *cp = NULL;struct usb_interface **new_interfaces = NULL;int n, nintf;if (dev->authorized == 0 || configuration == -1)configuration = 0;else {for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {if (dev->config[i].desc.bConfigurationValue ==configuration) {cp = &dev->config[i];break;}}}n = nintf = 0;if (cp) {/* new_interfaces 是个指针数组,首先为它分配空间 */nintf = cp->desc.bNumInterfaces;new_interfaces = kmalloc(nintf * sizeof(*new_interfaces), GFP_KERNEL);/* 为它指向的接口分配空间 */for (; n < nintf; ++n) {new_interfaces = kzalloc(sizeof(struct usb_interface), GFP_KERNEL);}i = dev->bus_mA - cp->desc.bMaxPower * 2;}/* Wake up the device so we can send it the Set-Config request */ret = usb_autoresume_device(dev);if (cp)ret = usb_hcd_check_bandwidth(dev, cp, NULL);elseret = usb_hcd_check_bandwidth(dev, NULL, NULL);/* if it's already configured, clear out old state first. * getting rid of old interfaces means unbinding their drivers. */if (dev->state != USB_STATE_ADDRESS)usb_disable_device(dev, 1);/* Skip ep0 *//* Get rid of pending async Set-Config requests for this device */cancel_async_set_config(dev);/* 设置配置 */ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),      USB_REQ_SET_CONFIGURATION, 0, configuration, 0,      NULL, 0, USB_CTRL_SET_TIMEOUT);dev->actconfig = cp;/* 变更状态 */usb_set_device_state(dev, USB_STATE_CONFIGURED);/* 设置这个配置的所有接口 */for (i = 0; i < nintf; ++i) {struct usb_interface_cache *intfc;struct usb_interface *intf;struct usb_host_interface *alt;cp->interface[i] = intf = new_interfaces[i];intfc = cp->intf_cache[i];intf->altsetting = intfc->altsetting;intf->num_altsetting = intfc->num_altsetting;intf->intf_assoc = find_iad(dev, cp, i);kref_get(&intfc->ref);alt = usb_altnum_to_altsetting(intf, 0);if (!alt)alt = &intf->altsetting[0];intf->cur_altsetting = alt;usb_enable_interface(dev, intf, true);intf->dev.parent = &dev->dev;intf->dev.driver = NULL;intf->dev.bus = &usb_bus_type;/* 注意这个,match时会区分 device 和 接口 */intf->dev.type = &usb_if_device_type;intf->dev.groups = usb_interface_groups;intf->dev.dma_mask = dev->dev.dma_mask;INIT_WORK(&intf->reset_ws, __usb_queue_reset_device);device_initialize(&intf->dev);mark_quiesced(intf);dev_set_name(&intf->dev, "%d-%s:%d.%d",dev->bus->busnum, dev->devpath,configuration, alt->desc.bInterfaceNumber);}kfree(new_interfaces);if (cp->string == NULL &&!(dev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS))cp->string = usb_cache_string(dev, cp->desc.iConfiguration);for (i = 0; i < nintf; ++i) {struct usb_interface *intf = cp->interface[i];/* 注册到 usb_bus_type */ret = device_add(&intf->dev);create_intf_ep_devs(intf);}usb_autosuspend_device(dev);return 0;}
static int usb_device_match(struct device *dev, struct device_driver *drv){/* devices and interfaces are handled separately */if (is_usb_device(dev)) {<span style="white-space:pre"></span>...} else if (is_usb_interface(dev)) {struct usb_interface *intf;struct usb_driver *usb_drv;const struct usb_device_id *id;/* device drivers never match interfaces */if (is_usb_device_driver(drv))return 0;intf = to_usb_interface(dev);usb_drv = to_usb_driver(drv);id = usb_match_id(intf, usb_drv->id_table);if (id)return 1;id = usb_match_dynamic_id(intf, usb_drv);if (id)return 1;}return 0;}
第一次注册进来的是 devies,在通用的driver 的 probe 函数,将该设备的所有接口信息都读取出来并设置再注册到 usb_bus_type 中,也就是说在match函数中,会走下边这个分支,根据 dirver 的 id_table 来匹配,根 Hub 的接口自然是与
hub_driver 进行匹配。

static struct usb_driver hub_driver = {.name ="hub",.probe =hub_probe,.disconnect =hub_disconnect,.suspend =hub_suspend,.resume =hub_resume,.reset_resume =hub_reset_resume,.pre_reset =hub_pre_reset,.post_reset =hub_post_reset,.ioctl =hub_ioctl,.id_table =hub_id_table,.supports_autosuspend =1,};
int usb_hub_init(void){if (usb_register(&hub_driver) < 0) {...}/* 创建内核线程,子进程将从 hub_thread 开始,名字叫 khubd  */khubd_task = kthread_run(hub_thread, NULL, "khubd");}
分析过设备模型的都知道,匹配成功后调用的是usb_driver.driver.probe函数,然而这里并没有,而且usb_bus_type中也没有 probe 函数,那么有可能是在driver的注册过程中动了哪些手脚。

static inline int usb_register(struct usb_driver *driver){return usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME);}
int usb_register_driver(struct usb_driver *new_driver, struct module *owner,const char *mod_name){int retval = 0;if (usb_disabled())return -ENODEV;new_driver->drvwrap.for_devices = 0;new_driver->drvwrap.driver.name = (char *) new_driver->name;new_driver->drvwrap.driver.bus = &usb_bus_type;new_driver->drvwrap.driver.probe = usb_probe_interface;new_driver->drvwrap.driver.remove = usb_unbind_interface;new_driver->drvwrap.driver.owner = owner;new_driver->drvwrap.driver.mod_name = mod_name;spin_lock_init(&new_driver->dynids.lock);INIT_LIST_HEAD(&new_driver->dynids.list);retval = driver_register(&new_driver->drvwrap.driver);if (!retval) {pr_info("%s: registered new interface driver %s/n",usbcore_name, new_driver->name);usbfs_update_special();usb_create_newid_file(new_driver);} else {printk(KERN_ERR "%s: error %d registering interface ""driver %s/n",usbcore_name, retval, new_driver->name);}return retval;}
这里注册到 usb_bus_type 的是 usb_driver.drvwrap.driver ,那么匹配成功后调用的自然是 usb_probe_interface

static int usb_probe_interface(struct device *dev){struct usb_driver *driver = to_usb_driver(dev->driver);struct usb_interface *intf = to_usb_interface(dev);struct usb_device *udev = interface_to_usbdev(intf);const struct usb_device_id *id;int error = -ENODEV;dev_dbg(dev, "%s/n", __func__);intf->needs_binding = 0;id = usb_match_id(intf, driver->id_table);if (!id)id = usb_match_dynamic_id(intf, driver);if (id) {dev_dbg(dev, "%s - got id/n", __func__);error = usb_autoresume_device(udev);mark_active(intf);intf->condition = USB_INTERFACE_BINDING;atomic_set(&intf->pm_usage_cnt, !driver->supports_autosuspend);if (intf->needs_altsetting0) {error = usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0);intf->needs_altsetting0 = 0;}<span style="white-space:pre"></span>/* 看这里 */error = driver->probe(intf, id);intf->condition = USB_INTERFACE_BOUND;usb_autosuspend_device(udev);}return error;}
获取到 usb_device 的接口 usb_interface 以及 usb_device_id ,然后 driver->probe(intf, id) 调用到 usb_driver.probe 函数。传递进来的参数非常重要~,尤其是第一个 ---接口。再看 probe 函数之前,还有一点需要先看一下。

int usb_hub_init(void){if (usb_register(&hub_driver) < 0) {...}/* 创建内核线程,子进程将从 hub_thread 开始,名字叫 khubd  */khubd_task = kthread_run(hub_thread, NULL, "khubd");}
static int hub_thread(void *__unused){/* khubd needs to be freezable to avoid intefering with USB-PERSIST * port handover.  Otherwise it might see that a full-speed device * was gone before the EHCI controller had handed its port over to * the companion full-speed controller. */set_freezable();do {hub_events();/* wait_event_interruptible(khubd_wait, ... */wait_event_freezable(khubd_wait,!list_empty(&hub_event_list) ||kthread_should_stop());} while (!kthread_should_stop() || !list_empty(&hub_event_list));pr_debug("%s: khubd exiting/n", usbcore_name);return 0;}
这个内核线程里干两件事,第一,hub_events(),第二休眠,等待唤醒。

static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id){struct usb_host_interface *desc;struct usb_endpoint_descriptor *endpoint;struct usb_device *hdev;struct usb_hub *hub;desc = intf->cur_altsetting;hdev = interface_to_usbdev(intf);/* Hub 的子类就是0,即desc->desc 这个interface 描述符里边的bInterfaceSubClass就应该是0 */if ((desc->desc.bInterfaceSubClass != 0) &&    (desc->desc.bInterfaceSubClass != 1)) {...}/* spec 规定了Hub 只有一个端点(除去端点0)也就是中断端点 */if (desc->desc.bNumEndpoints != 1)goto descriptor_error;endpoint = &desc->endpoint[0].desc;/* 判断这个端点是否是中断端点 */if (!usb_endpoint_is_int_in(endpoint))goto descriptor_error;/* 分配一个usb_hub */hub = kzalloc(sizeof(*hub), GFP_KERNEL);kref_init(&hub->kref);INIT_LIST_HEAD(&hub->event_list);hub->intfdev = &intf->dev;//hub  devicehub->hdev = hdev;//hub usb_deviceINIT_DELAYED_WORK(&hub->leds, led_work);INIT_DELAYED_WORK(&hub->init_work, NULL);usb_get_intf(intf);/* intf->dev = hub */usb_set_intfdata (intf, hub);intf->needs_remote_wakeup = 1;if (hdev->speed == USB_SPEED_HIGH)highspeed_hubs++;if (hub_configure(hub, endpoint) >= 0)return 0;}
static int hub_configure(struct usb_hub *hub, struct usb_endpoint_descriptor *endpoint){struct usb_hcd *hcd;struct usb_device *hdev = hub->hdev;struct device *hub_dev = hub->intfdev;u16 hubstatus, hubchange;u16 wHubCharacteristics;unsigned int pipe;int maxp, ret;char *message = "out of memory";/* 内存分配 */hub->buffer = usb_buffer_alloc(hdev, sizeof(*hub->buffer), GFP_KERNEL,&hub->buffer_dma);hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL);mutex_init(&hub->status_mutex);hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);/* 获得 hub 描述符 */ret = get_hub_descriptor(hdev, hub->descriptor,sizeof(*hub->descriptor));}/* hub 支持的最大下行端口 */hdev->maxchild = hub->descriptor->bNbrPorts;hub->port_owners = kzalloc(hdev->maxchild * sizeof(void *), GFP_KERNEL);wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);if (wHubCharacteristics & HUB_CHAR_COMPOUND) {/* 复合设备 */} elsedev_dbg(hub_dev, "standalone hub/n");switch (wHubCharacteristics & HUB_CHAR_LPSM) {/* 电源切换方式 */}switch (wHubCharacteristics & HUB_CHAR_OCPM) {/* 过流保护模式 */}spin_lock_init (&hub->tt.lock);INIT_LIST_HEAD (&hub->tt.clear_list);INIT_WORK(&hub->tt.clear_work, hub_tt_work);switch (hdev->descriptor.bDeviceProtocol) {/* 低速全速设备掠过 */}/* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */switch (wHubCharacteristics & HUB_CHAR_TTTT) {/* 指定 hub->tt.think_time = 666 * n; n根据设备速度不同而不同*/}/* 是否支持 hub 上的指示灯 */if (wHubCharacteristics & HUB_CHAR_PORTIND) {hub->has_indicators = 1;dev_dbg(hub_dev, "Port indicators are supported/n");}/* 请求设备状态 */ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus);le16_to_cpus(&hubstatus);/* hub 的端口电流 */if (hdev == hdev->bus->root_hub) {//根Hub...}/* Update the HCD's internal representation of this hub before khubd * starts getting port status changes for devices under the hub. */hcd = bus_to_hcd(hdev->bus);if (hcd->driver->update_hub_device) {ret = hcd->driver->update_hub_device(hcd, hdev,&hub->tt, GFP_KERNEL);}ret = hub_hub_status(hub, &hubstatus, &hubchange);/* 获得主机和 Hub 端点的管道 */pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));if (maxp > sizeof(*hub->buffer))maxp = sizeof(*hub->buffer);/* 分配一个urb */hub->urb = usb_alloc_urb(0, GFP_KERNEL);/* 填充Urb */usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,hub, endpoint->bInterval);hub->urb->transfer_dma = hub->buffer_dma;hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;/* maybe cycle the hub leds */if (hub->has_indicators && blinkenlights)hub->indicator [0] = INDICATOR_CYCLE;//tatus = usb_submit_urb(hub->urb, GFP_NOIO);//kick_khubd(hub) -> wake_up(&khubd_wait);hub_activate(hub, HUB_INIT);return 0;}

填充一个 urb ,检测 hub 端口状态,如果有状态发生改变,则会调用 hub_irq

static void hub_irq(struct urb *urb){struct usb_hub *hub = urb->context;int status = urb->status;unsigned i;unsigned long bits;switch (status) {..../* let khubd handle things */case 0:/* we got data:  port status changed */bits = 0;for (i = 0; i < urb->actual_length; ++i)bits |= ((unsigned long) ((*hub->buffer)[i]))<< (i*8);hub->event_bits[0] = bits;break;}hub->nerrors = 0;/* Something happened, let khubd figure it out */kick_khubd(hub);resubmit:if (hub->quiescing)return;if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0&& status != -ENODEV && status != -EPERM)dev_err (hub->intfdev, "resubmit --> %d/n", status);}

如果哪一位端口发生了变化,标记在 Hub->event_bits[0]中,然后唤醒线程,hub_event 被调用。再次提交 urb

static void hub_events(void){struct list_head *tmp;struct usb_device *hdev;struct usb_interface *intf;struct usb_hub *hub;struct device *hub_dev;u16 hubstatus;u16 hubchange;u16 portstatus;u16 portchange;int i, ret;int connect_change;while (1) {/* Grab the first entry at the beginning of the list */spin_lock_irq(&hub_event_lock);tmp = hub_event_list.next;list_del_init(tmp);hub = list_entry(tmp, struct usb_hub, event_list);kref_get(&hub->kref);spin_unlock_irq(&hub_event_lock);hdev = hub->hdev;hub_dev = hub->intfdev;intf = to_usb_interface(hub_dev);/* Lock the device, then check to see if we were * disconnected while waiting for the lock to succeed. */usb_lock_device(hdev);/* Autoresume */ret = usb_autopm_get_interface(intf);/* deal with port status changes */for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {if (test_bit(i, hub->busy_bits))continue;connect_change = test_bit(i, hub->change_bits);/* 第i位为0返回0 否则返回1,如果端口发生变化,返回1 */if (!test_and_clear_bit(i, hub->event_bits) &&!connect_change)continue;/* 程序运行到这,说明第i个端口发生了变化 */ret = hub_port_status(hub, i, &portstatus, &portchange);if (portchange & USB_PORT_STAT_C_CONNECTION) {clear_port_feature(hdev, i, USB_PORT_FEAT_C_CONNECTION);connect_change = 1;}if (portchange & USB_PORT_STAT_C_ENABLE) {...}if (portchange & USB_PORT_STAT_C_SUSPEND) {...}if (portchange & USB_PORT_STAT_C_OVERCURRENT) {...}if (portchange & USB_PORT_STAT_C_RESET) {...}if (connect_change)hub_port_connect_change(hub, i, portstatus, portchange);} /* end for i */...}
static void hub_port_connect_change(struct usb_hub *hub, int port1,u16 portstatus, u16 portchange){struct usb_device *hdev = hub->hdev;struct device *hub_dev = hub->intfdev;struct usb_hcd *hcd = bus_to_hcd(hdev->bus);unsigned wHubCharacteristics =le16_to_cpu(hub->descriptor->wHubCharacteristics);struct usb_device *udev;int status, i;for (i = 0; i < SET_CONFIG_TRIES; i++) {/* 分配一个 usb_device */udev = usb_alloc_dev(hdev, hdev->bus, port1);/* 设置它的状态为 加电的 */usb_set_device_state(udev, USB_STATE_POWERED); udev->bus_mA = hub->mA_per_port;udev->level = hdev->level + 1;udev->wusb = hub_is_wusb(hub);udev->speed = USB_SPEED_UNKNOWN;/* 分配一个地址 devnum = find_next_zero_bit(bus->devmap.devicemap, 128, bus->devnum_next); */choose_address(udev);/* 复位 获取描述符 */status = hub_port_init(hub, udev, port1, i);status = 0;/* Run it through the hoops (find a driver, etc) */if (!status) {status = usb_new_device(udev);}status = hub_power_remaining(hub);return;}
static int hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,int retry_counter){static DEFINE_MUTEX(usb_address0_mutex);struct usb_device*hdev = hub->hdev;struct usb_hcd*hcd = bus_to_hcd(hdev->bus);inti, j, retval;unsigneddelay = HUB_SHORT_RESET_TIME;enum usb_device_speedoldspeed = udev->speed;char *speed, *type;intdevnum = udev->devnum;mutex_lock(&usb_address0_mutex);retval = hub_port_reset(hub, port1, udev, delay);oldspeed = udev->speed;/* 根据传输速度设置端点0的最大包大小 */switch (udev->speed) {case USB_SPEED_SUPER:case USB_SPEED_VARIABLE:/* fixed at 512 */udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);break;case USB_SPEED_HIGH:/* fixed at 64 */udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);break;case USB_SPEED_FULL:/* 8, 16, 32, or 64 */udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);break;case USB_SPEED_LOW:/* fixed at 8 */udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);break;default:goto fail;} type = "";switch (udev->speed) {case USB_SPEED_LOW:speed = "low";break;case USB_SPEED_FULL:speed = "full";break;case USB_SPEED_HIGH:speed = "high";break;case USB_SPEED_SUPER:speed = "super";break;case USB_SPEED_VARIABLE:speed = "variable";type = "Wireless ";break;default: speed = "?";break;}for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3)) {...}if (udev->wusb == 0) {for (j = 0; j < SET_ADDRESS_TRIES; ++j) {/* 将分配的地址告诉设备 ,并设置设备状态为 USB_STATE_ADDRESS */retval = hub_set_address(udev, devnum);msleep(200);}if (udev->speed == USB_SPEED_SUPER) {devnum = udev->devnum;}msleep(10);if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3))break;  }/* 不知道一次能获取多少,但至少能获取8 */retval = usb_get_device_descriptor(udev, 8);if (retval < 8) {...} else {retval = 0;break;}}if (udev->descriptor.bMaxPacketSize0 == 0xff ||udev->speed == USB_SPEED_SUPER)i = 512;elsei = udev->descriptor.bMaxPacketSize0;if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);usb_ep0_reinit(udev);}/* 重新获取全部描述符 */retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);retval = 0;mutex_unlock(&usb_address0_mutex);return retval;}


在 Hub_event 中,会调用 对于状态变化的端口调用 hub_port_status 来检测Hub端口的具体状态,然后调用 hub_port_connect_change

hub_port_connect_change

udev = usb_alloc_dev(hdev, hdev->bus, port1);

dev->dev.bus = &usb_bus_type;

choose_address(udev); // 给新设备分配编号(地址)

hub_port_init // usb 1-1: new full speed USB device using s3c2410-ohci and address 3

hub_set_address // 把编号(地址)告诉USB设备

usb_get_device_descriptor(udev, 8); // 获取设备描述符

retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);

usb_new_device(udev)

err = usb_get_configuration(udev); // 把所有的描述符都读出来,并解析

usb_parse_configuration

device_add // 把device放入usb_bus_type的dev链表,

// 从usb_bus_type的driver链表里取出usb_driver,

// 把usb_interface和usb_driver的id_table比较

// 如果能匹配,调用usb_driver的probe

显然,从 usb_alloc_dev 开始,重复了如同根 Hub 作为一个 device 注册进内核时的流程。如此循环下去,就能枚举注册所有的 usb 设备。

以上是云栖社区小编为您精心准备的的内容,在云栖社区的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索驱动 , usb , 控制器 , 主机 , 分析 OHCI ,以便于您获取更多的相关知识。

弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率

40+云计算产品,6个月免费体验

现在注册,免费体验40+云产品,及域名优惠!

云服务器9.9元/月,大学必备