mtk-usb代码分析之枚举过程
基于mt6750T,Android 7.0,kernel 3.18.35,本文主要简述了USB的枚举过程,主要是从host的角度来看。
一.USB的拓扑结构
简单来说,USB由host和device两部分组成,hub和function device统称为device,最多支持128个设备。host和root hub是紧密联系的。
二.USB设备的枚举过程
- 1.host和hub已经初始化完,device并未插入hub的port,此时device处于UNATTACHED状态。
- 2.host通过status change ep(属于Interrupt类型)对hub进行轮询,当设备插入port,hub的状态发生改变,此时hub向host返回状态变更信息,此时device处于ATTACHED状态。
- 3.host查询hub的状态变更并确认变更信息
- 4.host已经确认有device插入,等待至少100ms等待device插好并且port口power保持稳定,此时device处于POWERED状态。
- 5.host对hub的port进行reset操作,port开始使能。device进入DEFAULT状态并且能够从port获取不超过100mA的电流,此时device的所有register和state都进行复位。
- 6.host给device分配一个特殊的地址(地址0,的确很特殊啊),此时device处于ADDRESSED状态。
- 7.device被分配为地址0,可以通过默认的控制管道(ep0)对device进行操作。host获取device的设备描述符,确认ep0最大的数据包长度。
- 8.host获取device的配置信息(配置描述符,接口描述符和端点描述符),从0~n-1,n是device的配置数。然后选取configuration和interface进行配置。此时设备处于CONFIGURED状态。
三.USB的状态变更图
四.代码分析
以下代码分析从bus->host controller->hub->device的顺序进行分析,代码分析以流程为主,细节这里就不列出来了。
4.1 Bus
//usb/core/usb.c static int __init usb_init(void) { ......
//注册usb总线 retval = bus_register(&usb_bus_type); ......
//注册hub driver并创建workqueue “usb_hub_wq” retval = usb_hub_init(); ......
//注册usb设备驱动usb_generic_driver retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE); ...... }
- usb_bus_type的match函数。usb里面区分设备和接口的概念,接口对应功能,一个usb设备可能包含多个接口,比如我们的android手机属于usb设备,插入usb可以选择usb模式,MTP、PTP、ADB等等,这些模式对应接口的概念。
- 设备包含一个或多个配置
- 配置包含一个或多个接口
- 接口包含零个或多个端点
struct bus_type usb_bus_type = { .name = "usb", .match = usb_device_match, .uevent = usb_uevent, };
/*
* 将usb设备及设备驱动,usb接口及接口驱动区分对待
*/ static int usb_device_match(struct device *dev, struct device_driver *drv) { //属于usb设备? if (is_usb_device(dev)) { //不是usb设备驱动则直接返回 if (!is_usb_device_driver(drv)) return 0; return 1;
//属于usb接口? } else if (is_usb_interface(dev)) { struct usb_interface *intf; struct usb_driver *usb_drv; const struct usb_device_id *id; //为usb设备类型则直接返回 if (is_usb_device_driver(drv)) return 0; intf = to_usb_interface(dev); usb_drv = to_usb_driver(drv); //接口和driver的id_table进行匹配 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; }
- usb_hub_init分析,hub_driver的函数等到hub这节再进行分析
int usb_hub_init(void) { //注册hub驱动 if (usb_register(&hub_driver) < 0) { printk(KERN_ERR "%s: can't register hub driver\n", usbcore_name); return -1; } //创建workqueue hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0); ...... }
- usb_register
#define usb_register(driver) \ usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME) int usb_register_driver(struct usb_driver *new_driver, struct module *owner, const char *mod_name) { //for_devices为0表明这是interface driver,属于接口范畴 new_driver->drvwrap.for_devices = 0; new_driver->drvwrap.driver.name = new_driver->name; new_driver->drvwrap.driver.bus = &usb_bus_type; //接口匹配probe函数,最终执行interface driver的probe 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); }
- usb_register_device_driver
int usb_register_device_driver(struct usb_device_driver *new_udriver, struct module *owner) { //for_devices为1表明这是device driver,属于设备范畴 new_udriver->drvwrap.for_devices = 1; new_udriver->drvwrap.driver.name = new_udriver->name; new_udriver->drvwrap.driver.bus = &usb_bus_type; //之前的match函数匹配上则执行drvwrap.driver.probe函数
//usb_probe_device最终调用driver的probe
new_udriver->drvwrap.driver.probe = usb_probe_device; new_udriver->drvwrap.driver.remove = usb_unbind_device; new_udriver->drvwrap.driver.owner = owner; //驱动注册 retval = driver_register(&new_udriver->drvwrap.driver); }
4.2 Host Controller
- host controller主要围绕usb_create_hcd&usb_add_hcd 展开
//usb/core/hcd.c struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, struct device *dev, const char *bus_name) { //直接甩锅给其他函数,主要就是hcd的分配空间及初始化,不细究了 return usb_create_shared_hcd(driver, dev, bus_name, NULL); }
//usb/core/hcd.c /* * 完成hcd的初始化并进行注册 */ int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags) { //从phy_bind_list中获取phy device,并进行初始化 usb_get_phy_dev(hcd->self.controller, 0); usb_phy_init(phy); //初始化buffer pools,分两种情况:DMA memory和非DMA memory //DMA memory则调用dma_pool_create,后续调用dma_poll_alloc //非DMA则后续直接调用kmalloc hcd_buffer_create(hcd); //注册usb host controller,一个host controller对应一条总线 usb_register_bus(&hcd->self); //申请root hub设备 rhdev = usb_alloc_dev(NULL, &hcd->self, 0); //hcd的reset if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) { goto err_hcd_driver_setup; } //申请hcd的中断处理函数,即hcd->driver->irq usb_hcd_request_irqs(hcd, irqnum, irqflags); //hcd start hcd->driver->start(hcd); //注册root hub设备 register_root_hub(hcd); }
- usb_alloc_dev
struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1) { //申请设备内存空间 dev = kzalloc(sizeof(*dev), GFP_KERNEL); //调用usb_hcd->driver->alloc_dev if (usb_hcd->driver->alloc_dev && parent && !usb_hcd->driver->alloc_dev(usb_hcd, dev)) { usb_put_hcd(bus_to_hcd(bus)); kfree(dev); return NULL; } //设备隶属总线usb_bus_type,属于usb设备类型而非接口类型 device_initialize(&dev->dev); dev->dev.bus = &usb_bus_type; dev->dev.type = &usb_device_type; dev->dev.groups = usb_device_groups; //设备状态标记为ATTACHED dev->state = USB_STATE_ATTACHED; //初始化并使能ep0 INIT_LIST_HEAD(&dev->ep0.urb_list); dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE; dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT; usb_enable_endpoint(dev, &dev->ep0, false); //设置portnum dev->portnum = port1; }
- register_root_hub
static int register_root_hub(struct usb_hcd *hcd) { //root hub的address设置为1 const int devnum = 1; usb_dev->devnum = devnum; //下一个注册在该总线上的设备address从2开始 usb_dev->bus->devnum_next = devnum + 1; //标记设备状态为ADDRESSED usb_set_device_state(usb_dev, USB_STATE_ADDRESS); //ep0最大包长度 usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64); //获取设备描述符,18个byte usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE); //注册root hub设备 usb_new_device (usb_dev);
}
- usb_new_device
int usb_new_device(struct usb_device *udev) { //获取描述符(包括配置,接口,端点描述符) usb_enumerate_device(udev); /* Read descriptors */ //注册设备,加入统一设备模型 device_add(&udev->dev); }
- usb_enumerate_device
static int usb_enumerate_device(struct usb_device *udev) { //获取描述符(包括配置,接口,端点描述符),解析描述符的过程这里就不贴了^_^ usb_get_configuration(udev); //获取产品序列,制造序列和序列号 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); //otg相关,偷个懒,暂时放着不考虑 usb_enumerate_device_otg(udev); }
4.3 Hub
- 上面已经注册了root hub设备,此时属于usb设备的范畴,首先执行drvwrap_driver_probe函数(usb_probe_device),之后调用usb_generic_driver->probe函数(generic_probe)
static int generic_probe(struct usb_device *udev) { //前面添加root hub,通过usb_new_device已经获取到了配置描述符,这里选择配置描述符,大多数设备只有一种配置 usb_choose_configuration(udev); //创建接口设备,设置配置 usb_set_configuration(udev, c); usb_notify_add_device(udev); return 0; }
- usb_set_configuration
int usb_set_configuration(struct usb_device *dev, int configuration) { for (i = 0; i < nintf; ++i) {
//使用setting num为0的altsetting,这里setting num和数组下标并不存在一一对应关系 alt = usb_altnum_to_altsetting(intf, 0);
//没有num为0则使用第一个setting if (!alt) alt = &intf->altsetting[0];
//使能接口,里面调用使能端点 usb_enable_interface(dev, intf, true); //表明属于接口设备类型 intf->dev.bus = &usb_bus_type; intf->dev.type = &usb_if_device_type; intf->dev.groups = usb_interface_groups; device_initialize(&intf->dev); }
//控制传输,设置配置 ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION, 0, configuration, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
//usb设备状态为CONFIGURED usb_set_device_state(dev, USB_STATE_CONFIGURED); for (i = 0; i < nintf; ++i) {
//添加接口设备并创建端点设备 ret = device_add(&intf->dev); create_intf_ep_devs(intf); } }
- 注册了接口设备,那必然要与总线上的接口驱动相match,与驱动的id_table或者动态id匹配。hub设备顾名思义就是与hub_driver匹配,执行hub_driver->probe函数
- 整个以上过程可以用下面图展示
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) { //判断端点是否属于中断类型,前面提到host通过中断端点进行轮询hub if (!usb_endpoint_is_int_in(endpoint)) goto descriptor_error; //初始化hub_event任务,hub_event处理设备插入hub端口时的事件 INIT_WORK(&hub->events, hub_event); //hub配置 hub_configure(hub, endpoint); }
- hub_configure
static int hub_configure(struct usb_hub *hub, struct usb_endpoint_descriptor *endpoint) { //获取hub描述符,之后对hub描述符进行分析,分析bNbrPorts,wHubCharacteristics,bDeviceProtocol这些字段 ret = get_hub_descriptor(hdev, hub->descriptor); //获取USB设备状态,这里属于标准请求,主要是D0 bit的设备是否self powered ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus); //获取hub的状态,属于hub类型请求,由两个2byte的字段组成,wHubStatus和wHubChange,跟供电状态和是否过流相关 ret = hub_hub_status(hub, &hubstatus, &hubchange); //创建pipe pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress); maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe)); //创建中断urb,urb完成函数hub_irq,当hub有状态发生变化时就调用kick_hub_wq将hub->events加入workqueue,这种针对热插拔时USB枚举的情况 hub->urb = usb_alloc_urb(0, GFP_KERNEL); usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq, hub, endpoint->bInterval); //创建端口设备,有多少个端口就创建多少个端口设备 usb_hub_create_port_device(hub, i + 1); //hub初始化,分为三部曲 hub_activate(hub, HUB_INIT); }
- hub初始化三部曲
static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) { //init第一部曲,hub上电 if (type == HUB_INIT) { unsigned delay = hub_power_on_good_delay(hub); hub_power_on(hub, false); INIT_DELAYED_WORK(&hub->init_work, hub_init_func2); queue_delayed_work(system_power_efficient_wq, &hub->init_work, msecs_to_jiffies(delay)); return; } //初始化第二部曲,检测端口状态变化 init2: for (port1 = 1; port1 <= hdev->maxchild; ++port1) { status = hub_port_status(hub, port1, &portstatus, &portchange); set_bit(port1, hub->change_bits); } if (need_debounce_delay) { delay = HUB_DEBOUNCE_STABLE; if (type == HUB_INIT2) { INIT_DELAYED_WORK(&hub->init_work, hub_init_func3); queue_delayed_work(system_power_efficient_wq, &hub->init_work, msecs_to_jiffies(delay)); device_unlock(hub->intfdev); return; /* Continues at init3: below */ } else { msleep(delay); } } //初始化第三部曲,hub->events加入workqueue,进行端口状态变化的后续处理,这种针对hub初始化时候USB设备的枚举过程 init3: status = usb_submit_urb(hub->urb, GFP_NOIO); kick_hub_wq(hub); }
- hub_event
static void hub_event(struct work_struct *work) { for (i = 1; i <= hdev->maxchild; i++) { //hub端口状态发生改变,调用port_event函数,里面主要为usb设备的枚举过程 port_event(hub, i); } }
4.4 Device
- port_event
static void port_event(struct usb_hub *hub, int port1) __must_hold(&port_dev->status_lock) { //主要调用下面函数 hub_port_connect_change(hub, port1, portstatus, portchange); }
- hub_port_connect_change
static void hub_port_connect_change(struct usb_hub *hub, int port1, u16 portstatus, u16 portchange) __must_hold(&port_dev->status_lock) { //继续直接甩锅 hub_port_connect(hub, port1, portstatus, portchange); }
- 下面开始正戏了
static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, u16 portchange) { //创建usb设备,此时设备处于ATTACHED状态 udev = usb_alloc_dev(hdev, hdev->bus, port1); //设备处于POWERED状态 usb_set_device_state(udev, USB_STATE_POWERED); //选择devicemap中第一个不为0的bit数作为设备number choose_devnum(udev); //复位,分配地址,获取设备描述符 status = hub_port_init(hub, udev, port1, i);
//注册usb设备,之后的流程参考前面root hub,设置configuration&interface
status = usb_new_device(udev);
}
- hub_port_init
static int hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, int retry_counter) { //更新udev->devnum为0,最终调用hcd->driver->reset_device函数,复位后设备处于DEFAULT状态,默认状态下的设备地址为0 hub_port_reset(hub, port1, udev, delay, false); //根据设备类型,设置ep0最大传输包长度,高速和低速确定,但是全速不确定,需要从设备描述符中获取 switch (udev->speed) { case USB_SPEED_SUPER: case USB_SPEED_WIRELESS: /* 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 */ /* to determine the ep0 maxpacket size, try to read * the device descriptor to get bMaxPacketSize0 and * then correct our initial guess. */ 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; } //获取设备描述符,获取长度为64byte,来确定ep0的最大传输包长度 usb_control_msg(udev, usb_rcvaddr0pipe(), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, USB_DT_DEVICE << 8, 0, buf, GET_DESCRIPTOR_BUFSIZE, initial_descriptor_timeout); //参考choose_devnum定义的devnum,分配设备地址,此时设备处于ADDRESSED状态 hub_set_address(udev, devnum); //先获取前8个字节,第一个byte即是该描述符的长度 usb_get_device_descriptor(udev, 8); //再获取一次,得到完整的设备描述符 usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE); }