Linux内核USB3.0驱动框架分析--USB主机控制器hcd驱动分析
一,概述
usb主机控制器驱动一般以platform的形式将驱动注册进内核,,因此我们需要从前面一篇文章的框图说起。主要分析下图中橙色部分的内容。
二,usb主机控制器相关函数
2.1 usb_create_hcd
我们来看一下usb_create_hcd函数,该函数定义在drivers/usb/core/hcd.c:
struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, // 参数1为ohci_s3c2410_hc_driver,参数2为s3c_device_ohci ->dev,参数3位s3c24xxstruct device *dev, const char *bus_name)
{return __usb_create_hcd(driver, dev, dev, bus_name, NULL);
}
这里调用了__usb_create_hcd函数:
struct usb_hcd *__usb_create_hcd(const struct hc_driver *driver, struct device *sysdev, struct device *dev, const char *bus_name,struct usb_hcd *primary_hcd)
{struct usb_hcd *hcd;hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL); // 动态申请内存空间,额外申请hcd私有数据大小的内存timer_setup(&hcd->rh_timer, rh_timer_func, 0); // 初始化定时器,设置定时器超时函数hcd->driver = driver; // 绑定hc_driverreturn hcd;
}
总结一下函数执行流程:
首先分配一个hcd的内存空间,包含私有数据空间;
设置hcd->rh_timer设置定时器超时函数rh_timer_func,主机控制器以轮询的方式查找端口变化状态;
设置 hcd->driver = &_hc_driver等 ;
hc_driver是usb主机控制器的驱动函数,实现了通过主机控制器硬件向外通信的方法。
2.2 rh_timer_func
定时器回调函数定义在drivers/usb/core/hcd.c文件中:
/* timer callback */
static void rh_timer_func (struct timer_list *t)
{struct usb_hcd *_hcd = from_timer(_hcd, t, rh_timer);usb_hcd_poll_rh_status(_hcd);
}
2.3 usc_add_hcd
用usb_hcd结构体定义好usb_hdc设备后,用usb_add_hcd函数向linux内核注册usb主机控制器驱动,函数定义在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;retval = usb_phy_roothub_set_mode(hcd->phy_roothub,PHY_MODE_USB_HOST);retval = usb_register_bus(&hcd->self);if (retval < 0)goto err_register_bus;rhdev = usb_alloc_dev(NULL, &hcd->self, 0);if (rhdev == NULL) {dev_err(hcd->self.sysdev, "unable to allocate root hub\n");retval = -ENOMEM;goto err_allocate_root_hub;}mutex_lock(&usb_port_peer_mutex);hcd->self.root_hub = rhdev;mutex_unlock(&usb_port_peer_mutex);/* initialize tasklets */init_giveback_urb_bh(&hcd->high_prio_bh);init_giveback_urb_bh(&hcd->low_prio_bh);/* starting here, usbcore will pay attention to this root hub */retval = register_root_hub(hcd);if (hcd->uses_new_polling && HCD_POLL_RH(hcd))usb_hcd_poll_rh_status(hcd); // 定时器轮询root hub端口状态return retval;
}
该函数代码比较长,这里挑重点说:
usb_register_bus(&hcd->self) ,将 hcd->usb_bus 注册到全局链表usb_bus_list;
usb_alloc_dev为根hub分配一个usb_device 结构(内核中,所有的真实的usb设备;比如hub,鼠标等都用usb_device结构来描述);
register_root_hub注册根hub设备到usb_bus_type;
usb_hcd_poll_rh_status定时器函数轮询hub端口状态;
usb主机控制器也只不过是分配了一个usb_hcd结构体,为它的根hub分配了一个usb_device 结构体,注册到usb_bus_type罢了,后边是根hub的注册和设备枚举过程了。
2.4 usb_alloc_dev
usb_alloc_dev函数定义在drivers/usb/core/hcd.c:
struct usb_device *usb_alloc_dev(struct usb_device *parent,struct usb_bus *bus, unsigned port1)
{struct usb_device *dev;dev = kzalloc(sizeof(*dev), GFP_KERNEL);device_initialize(&dev->dev);dev->dev.bus = &usb_bus_type;dev->dev.type = &usb_device_type;dev->dev.groups = usb_device_groups;dev->state = USB_STATE_ATTACHED;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;/* 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 name isn't* as stable: bus->busnum changes easily from modprobe order,* cardbus or pci hotplugging, and so on.*/if (unlikely(!parent)) {dev->devpath[0] = '0';dev->route = 0;dev->dev.parent = bus->controller;device_set_of_node_from_dev(&dev->dev, bus->sysdev);dev_set_name(&dev->dev, "usb%d", bus->busnum);root_hub = 1;}return dev;
}
该函数主要做了以下事情:
这里实际上就是为根hub,动态分配一个usb_device;
初始化设备基类dev->dev:dev->dev.bus = &usb_bus_type,设置总线类型dev->dev.type = &usb_device_type,设置设备类型,用于和usb接口区分;等等;
初始化dev其它成员;dev->state = USB_STATE_ATTACHED,设备状态设置位已连接;usb_enable_endpoint函数使能usb端点0,用于usb通信,usb主机控制器会通过该端点和root hub通信获取设备/配置描述信息;一个 usb 设备有多个配置,每个配置又有多个接口,每个接口有多个端点。但是端点0比较特殊,它是整个usb设备共享的,端点0是直接存储在usb_device->ep0字段中。等等;
2.5 register_root_hub
register_root_hub函数定义在drivers/usb/core/hcd.c,顾名思义,就是注册root_hub设备,先是对自身初始化,然后获取设备描述符,最后把自己当普通usb设备给注册了。
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; // 根hub设备const int devnum = 1;int retval;usb_dev->devnum = devnum; //根hub设备地址为1usb_dev->bus->devnum_next = devnum + 1;set_bit (devnum, usb_dev->bus->devmap.devicemap);usb_set_device_state(usb_dev, USB_STATE_ADDRESS); // 变更状态mutex_lock(&usb_bus_idr_lock);usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE); // 获取设备描述符retval = usb_new_device (usb_dev);if (retval) {dev_err (parent_dev, "can't register root hub for %s, %d\n",dev_name(&usb_dev->dev), retval);} else {spin_lock_irq (&hcd_root_hub_lock);hcd->rh_registered = 1;spin_unlock_irq (&hcd_root_hub_lock);/* Did the HC die before the root hub was registered? */if (HCD_DEAD(hcd))usb_hc_died (hcd); /* This time clean up */}mutex_unlock(&usb_bus_idr_lock);return retval;
}
这里我们先介绍usb_get_device_descriptor函数,该函数用于获取根hub设备的描述符;
int usb_get_device_descriptor(struct usb_device *dev, unsigned int size)
{struct usb_device_descriptor *desc;desc = kmalloc(sizeof(*desc), GFP_NOIO);ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, size); // 获取usb设备描述符,request = 0x06,requestType = 0x80、value = 0x100return ret;
}
2.6 usb_new_device
接下来我们再来看看usb_new_device。
int usb_new_device(struct usb_device *udev)
{err = usb_enumerate_device(udev); /* Read descriptors */dev_dbg(&udev->dev, "udev %d, busnum %d, minor = %d\n",udev->devnum, udev->bus->busnum,(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));/* 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);if (udev->serial)add_device_randomness(udev->serial, strlen(udev->serial));if (udev->product)add_device_randomness(udev->product, strlen(udev->product));if (udev->manufacturer)add_device_randomness(udev->manufacturer,strlen(udev->manufacturer));err = device_add(&udev->dev);return err;
}
usb_new_device函数用于注册usb根hub设备,先是枚举调用usb_enumerate_device获取设备配置描述符信息(这里枚举获取的pid、vid等用于设备驱动匹配的id使用):
static int usb_enumerate_device(struct usb_device *udev)
{int err;struct usb_hcd *hcd = bus_to_hcd(udev->bus);if (udev->config == NULL) {err = usb_get_configuration(udev); // 获取配置描述符,这里根据设备描述符dev->descriptor.bNumConfiguratios中配置数量,来一一发送USC_DT_CONFIG(value=0x200)命令获取配置描述符,初始化dev->config[num]成员}/* read the standard strings and cache them if present */udev->product = usb_cache_string(udev, udev->descriptor.iProduct); // 从设备描述符获取产品名信息udev->manufacturer = usb_cache_string(udev, // 从设备描述符获取厂商信息 udev->descriptor.iManufacturer);udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber); // 从设备描述符获取产品串号信息err = usb_enumerate_device_otg(udev);return 0;
}
usb_get_configuration,大致步骤也是调用usb_control_msg函数通过usb主机控制器向root hub发送控制报文,获取配置描述符信息。
然后调用device_add将设备加入到设备链表中,在执行device_add后,将执行总线的匹配函数usb_device_match:
device_addbus_probe_devicdevice_attachbus_for_each_drv__device_attachdriver_match_devicedrv->bus->match(dev, drv)usb_device_match
注册根hub设备后,由于在usb子系统初始化函数中注册了usb通用设备驱动usb_generic_driver,这样就会执行usb_generic_driver的probe函数,即generic_probe。这里就和前面一幅图的流程一样了。
2.7 usb_hcd_poll_rh_status
/** Root Hub interrupt transfers are polled using a timer if the* driver requests it; otherwise the driver is responsible for* calling usb_hcd_poll_rh_status() when an event occurs.** Completions are called in_interrupt(), but they may or may not* be in_irq().*/
void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
{struct urb *urb;length = hcd->driver->hub_status_data(hcd, buffer);if (length > 0) {/* try to complete the status urb */spin_lock_irqsave(&hcd_root_hub_lock, flags);urb = hcd->status_urb;if (urb) {clear_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);hcd->status_urb = NULL;urb->actual_length = length;memcpy(urb->transfer_buffer, buffer, length);usb_hcd_unlink_urb_from_ep(hcd, urb);usb_hcd_giveback_urb(hcd, urb, 0);} else {length = 0;set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);}spin_unlock_irqrestore(&hcd_root_hub_lock, flags);}/* The USB 2.0 spec says 256 ms. This is close enough and won't* exceed that limit if HZ is 100. The math is more clunky than* maybe expected, this is to make sure that all timers for USB devices* fire at the same time to give the CPU a break in between */if (hcd->uses_new_polling ? HCD_POLL_RH(hcd) :(length == 0 && hcd->status_urb != NULL))mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4)); // 修改定时器超时时间,并重新激活定时器
}
usb_hcd_poll_rh_status 调用主机控制器的hub_status_data函数获取端口状态。如果端口的状态有变化,那么length > 0,把获取到的端口状态的数组拷贝到urb->transfer_buffer中,就是前面的hub->buffer中,同时调用usb_hcd_giveback_urb函数。
2.8 usb_hcd_giveback_urb
void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
{ if (!hcd_giveback_urb_in_bh(hcd) && !is_root_hub(urb->dev)) {__usb_hcd_giveback_urb(urb);return;}}
static void __usb_hcd_giveback_urb(struct urb *urb)
{urb->complete(urb);}
usb_hcd_giveback_urb函数中调用urb->complete (urb),而urb->complete = hub_irq,这样就返回到了hub中 ;
三,usb驱动的probe匹配过程
前面我们分析到调用usb_new_device来进行配置使usb设备可以正常工作,我们现在分析一下具体过程。主要是找到对应的客户驱动程序和该USB设备挂钩。
usb_new_device中调用device_add,将设备添加到设备层次结构中。
大概调用流程:device_add -> bus_probe_device -> device_initial_probe -> __device_attach -> bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver);
3.1 1. usb_device_match
我们分析一下就是usb_device_match,这个函数只是做一些粗略的匹配, 如果匹配成功则返回1。这个函数只是做一些粗略的匹配, 如果匹配成功则返回1, 然后由driver_probe_device来做进一步的匹配, 如果匹配失败则返回0, 并且driver_probe_device也不会在执行. 这个函数的调用保证了dev, drv 要么都是设备级别的( 即dev 代表usb 设备,drv 代表usb 设备驱动), 要么都是接口级别的( 即dev 代表usb 设备的一个interface,drv 代表usb 接口驱动).
static int usb_device_match(struct device *dev, struct device_driver *drv)
{/* devices and interfaces are handled separately */if (is_usb_device(dev)) {/* interface drivers never match devices */ //是匹配USB设备的驱动,USB接口的驱动不能匹配if (!is_usb_device_driver(drv))return 0;/* TODO: Add real matching code */return 1;} else if (is_usb_interface(dev)) { //如果是USB接口struct usb_interface *intf;struct usb_driver *usb_drv;const struct usb_device_id *id;/* device drivers never match interfaces *///usb接口在注册driver时将for_devices设置为0,for_devices =1,表示设备驱动,for_devices = 0,表示接口驱动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); //匹配id tableif (id)return 1;id = usb_match_dynamic_id(intf, usb_drv); //匹配动态id tableif (id)return 1;}return 0;
}
3.2 driver_probe_device
driver_probe_device主要是调用really_probe -> (drv->probe)
对于usb 来说这个函数的调用有2 种分支, 1: dev,drv 代表的是设备级别的, 2 dev,drv 代表的是接口级别的. 其他情况组合在usb_device_match 中被过滤掉了.
分支1: dev,drv 代表的是设备级别:
此时的drv 肯定是usb_generic_driver. 因为在当前的usb 系统中只有这个driver 是代表整个设备的驱动, 它是在usb_init 中被注册的, 而我们通常写的usb 驱动都是代表一个interface 的.
因此, 此时的drv->probe 将调用generic_probe().再到usb_set_configuration
int usb_set_configuration(struct usb_device *dev, int configuration)
{for(I = 0; I < nintf; i++) {struct usb_interface *intf = cp->interface[i];device_add(&intf->dev); //又会进入匹配
}
该函数比较重要, 但我们只关心probe 过程因此省掉了很多东西. 它为当前配置下的每个interface 调用device_add() 函数, 根据前面的分析可知, 这个过程将会走到接下来我们要分析的分支2.
分支2: dev,drv 代表的是interface 级别:
此时的dev 代表着一个interface, 而drv 就代表了我们自己的usb 驱动. 但是我们应当看到drv是device_driver 类型, 而我们写的usb 驱动的类型一般是usb_driver, 因此这里的probe 和我们自己写的probe 显然不是同一个. 实际上这里的drv 是我们的驱动对象里内嵌的一个子对象( 因为linux 下所以的驱动都必须用device_driver 来代表,). 那这个子对象的probe 函数是在哪里赋值的呢?
这就要看usb_register宏了,实际里面是调用usb_register_driver
int usb_register_driver(struct usb_driver *new_driver, struct module *owner,const char *mod_name)
{new_driver->drvwrap.for_devices = 0;new_driver->drvwrap.driver.name = new_driver->name;new_driver->drvwrap.driver.bus = &usb_bus_type;new_driver->drvwrap.driver.probe = usb_probe_interface; //这里是probe函数new_driver->drvwrap.driver.remove = usb_unbind_interface; new_driver->drvwrap.driver.owner = owner;new_driver->drvwrap.driver.mod_name = mod_name;new_driver->drvwrap.driver.dev_groups = new_driver->dev_groups;spin_lock_init(&new_driver->dynids.lock);INIT_LIST_HEAD(&new_driver->dynids.list);retval = driver_register(&new_driver->drvwrap.driver);if (retval)goto out;retval = usb_create_newid_files(new_driver);if (retval)goto out_newid;pr_info("%s: registered new interface driver %s\n",usbcore_name, new_driver->name); //一般log会打印这个
}
跟踪这个函数我们可以看到这里的probe 函数实际上是usb_probe_interface( 所有的usb interface 驱动都是一样的).
static int usb_probe_interface(struct device *dev)
{struct driver = to_usb_driver(dev->driver); //dev->driver 在really_probe 中设置.error = driver->probe(intf, id); // 这个就是我们自己写的probe 函数了.
}
driver->probe(intf, id); 这就调用到我们自己写的代码里面了,
3.3 流程图
大概流程图是一样的: