赞
踩
本文是基于mini2440开发板Linux版本号是linux-2.6.32.2的学习笔记
在hub_events函数中,如果确实检测到端口状态有变化,这个变化有可能是物理上,也有可能是逻辑上的。那么调用hub_port_connect_change函数。这个变化主要有三种:
一个是连接有变化;
二个是端口本身重新使能,即所谓的enable,这种情况通常就是为了对付电磁干扰的;
第三种情况就是在复位一个设备的时候发现其描述符变了,这通常对应的是硬件本身有了升级;
看一下hub_port_connect_change函数的执行。
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; dev_dbg (hub_dev, "port %d, status %04x, change %04x, %s\n", port1, portstatus, portchange, portspeed (portstatus)); //设置指示灯,HUB_LED_AUTO表示自动设置hub自己根据端口的状态来设置 if (hub->has_indicators) { set_port_led(hub, port1, HUB_LED_AUTO); hub->indicator[port1-1] = INDICATOR_AUTO; } #ifdef CONFIG_USB_OTG /* during HNP, don't repeat the debounce */ if (hdev->bus->is_b_host) portchange &= ~(USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE); #endif /* Try to resuscitate an existing device */ udev = hdev->children[port1-1]; //原来有设备 if ((portstatus & USB_PORT_STAT_CONNECTION) && udev && udev->state != USB_STATE_NOTATTACHED) { usb_lock_device(udev); //设备是连接状态,端口也是enable的,清除标志位 if (portstatus & USB_PORT_STAT_ENABLE) { status = 0; /* Nothing to do */ #ifdef CONFIG_USB_SUSPEND //设备是连接的,但是同时是suspend状态,唤醒 } else if (udev->state == USB_STATE_SUSPENDED && udev->persist_enabled) { /* For a suspended device, treat this as a * remote wakeup event. */ status = remote_wakeup(udev); #endif } else { status = -ENODEV; /* Don't resuscitate */ } usb_unlock_device(udev); if (status == 0) { clear_bit(port1, hub->change_bits); return; } } /* Disconnect any existing devices under this port */ //原来有设备现在没有,调用usb_disconnect函数,并且把相应的change_bits清零 if (udev) usb_disconnect(&hdev->children[port1-1]); clear_bit(port1, hub->change_bits); //如果端口是连接状态并且使能了 if (portchange & (USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE)) { //延时一段时间在获取端口的状态,防抖 status = hub_port_debounce(hub, port1); if (status < 0) { if (printk_ratelimit()) dev_err(hub_dev, "connect-debounce failed, " "port %d disabled\n", port1); portstatus &= ~USB_PORT_STAT_CONNECTION; } else { portstatus = status; } } /* Return now if debouncing failed or nothing is connected */ //如果经历了上面的步骤,端口不在是连接状态,就要把端口关闭 if (!(portstatus & USB_PORT_STAT_CONNECTION)) { /* maybe switch power back on (e.g. root hub was reset) */ //如果该端口的电源被关闭了,那么就打开电源 if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2 && !(portstatus & (1 << USB_PORT_FEAT_POWER))) set_port_feature(hdev, port1, USB_PORT_FEAT_POWER); //如果端口是使能状态,先disable端口 if (portstatus & USB_PORT_STAT_ENABLE) goto done; return; } //SET_CONFIG_TRIES = 4,老的策略试两次,新的策略试两次 for (i = 0; i < SET_CONFIG_TRIES; i++) { /* reallocate for each attempt, since references * to the previous one can escape in various ways */ //为hub这个端口所接的设备而申请内存 udev = usb_alloc_dev(hdev, hdev->bus, port1); if (!udev) { dev_err (hub_dev, "couldn't allocate port %d usb_device\n", port1); goto done; } //将设备的状态设置为Powered usb_set_device_state(udev, USB_STATE_POWERED); //给USB设备的电流赋值 udev->bus_mA = hub->mA_per_port; //usb设备连接的的hub的层级上加1 udev->level = hdev->level + 1; //如果usb设备连接的的hub是一个无线hub,返回1,否则返回0 udev->wusb = hub_is_wusb(hub); /* * USB 3.0 devices are reset automatically before the connect * port status change appears, and the root hub port status * shows the correct speed. We also get port change * notifications for USB 3.0 devices from the USB 3.0 portion of * an external USB 3.0 hub, but this isn't handled correctly yet * FIXME. */ //此时还不知道设备支持的速度,于是将设备的speed成员暂时先设置为USB_SPEED_UNKNOWN if (!(hcd->driver->flags & HCD_USB3)) udev->speed = USB_SPEED_UNKNOWN; else if ((hdev->parent == NULL) && (portstatus & (1 << USB_PORT_FEAT_SUPERSPEED))) udev->speed = USB_SPEED_SUPER; else udev->speed = USB_SPEED_UNKNOWN; /* * xHCI needs to issue an address device command later * in the hub_port_init sequence for SS/HS/FS/LS devices. */ //hub为设备在总上选择地址 if (!(hcd->driver->flags & HCD_USB3)) { /* set the address */ choose_address(udev); if (udev->devnum <= 0) { status = -ENOTCONN; /* Don't retry */ goto loop; } } /* reset (non-USB 3.0 devices) and get descriptor */ //端口初始化 status = hub_port_init(hub, udev, port1, i); if (status < 0) goto loop; /* consecutive bus-powered hubs aren't reliable; they can * violate the voltage drop budget. if the new child has * a "powered" LED, users should notice we didn't enable it * (without reading syslog), even without per-port LEDs * on the parent. */ //接在当前Hub端口的设备是另一个Hub //udev->bus_mA: hub那边能够提供的每个端口的电流 //如果它小于等于100mA,说明设备供电是存在问题的,电力不足 if (udev->descriptor.bDeviceClass == USB_CLASS_HUB && udev->bus_mA <= 100) { u16 devstat; //获取这个设备的状态,保存在devstat里面 status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstat); if (status < 2) { dev_dbg(&udev->dev, "get status %d ?\n", status); goto loop_disable; } le16_to_cpus(&devstat); //判断接入的这个Hub是不是也得靠总线供电,如果是的话,跳到loop_disable执行,关闭这个端口,结束这次循环 if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) { dev_err(&udev->dev, "can't connect bus-powered hub " "to this port\n"); if (hub->has_indicators) { hub->indicator[port1-1] = INDICATOR_AMBER_BLINK; schedule_delayed_work (&hub->leds, 0); } status = -ENOTCONN; /* Don't retry */ goto loop_disable; } } /* check for devices running slower than they could */ //一个设备如果能够进行高速传输,那么它就应该在设备描述符里的bcdUSB这一项写上0200H //如果一个设备可以高速传输却处在全速传输的速度上 //highspeed_hubs:表示设备所在的hub可以进行高速传输 if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200 && udev->speed == USB_SPEED_FULL && highspeed_hubs != 0) check_highspeed (hub, udev, port1); /* Store the parent's children[] pointer. At this point * udev becomes globally accessible, although presumably * no one will look at it until hdev is unlocked. */ status = 0; /* We mustn't add new devices if the parent hub has * been disconnected; we would race with the * recursively_mark_NOTATTACHED() routine. */ spin_lock_irq(&device_state_lock); //如果说hub已经被撤掉,标记状态错误 if (hdev->state == USB_STATE_NOTATTACHED) status = -ENOTCONN; //否则把udev赋值给hdev->children数组中的对应元素 else hdev->children[port1-1] = udev; spin_unlock_irq(&device_state_lock); /* Run it through the hoops (find a driver, etc) */ //如果status为0,调用usb_new_device. if (!status) { status = usb_new_device(udev); //如果不为0说明出错了,那么就把hdev->children相应的那位设置为空 if (status) { spin_lock_irq(&device_state_lock); hdev->children[port1-1] = NULL; spin_unlock_irq(&device_state_lock); } } //如果中间出错,调用loop_disable if (status) goto loop_disable; status = hub_power_remaining(hub); if (status) dev_dbg(hub_dev, "%dmA power budget left\n", status); return; loop_disable: hub_port_disable(hub, port1, 1); loop: usb_ep0_reinit(udev); release_address(udev); usb_put_dev(udev); if ((status == -ENOTCONN) || (status == -ENOTSUPP)) break; } if (hub->hdev->parent || !hcd->driver->port_handed_over || !(hcd->driver->port_handed_over)(hcd, port1)) dev_err(hub_dev, "unable to enumerate USB device on port %d\n", port1); done: hub_port_disable(hub, port1, 1); if (hcd->driver->relinquish_port && !hub->hdev->parent) hcd->driver->relinquish_port(hcd, port1); }
这上面做的事情主要是:
原来睡眠的设备要唤醒;
原来存在的设备现在不存在的设备,要disconnect,要清除端口;
原来不存在的设备现在有了,延时一下在检测,防抖;
申请一个usb设备,并初始化;
为usb设备选择一个地址;
端口初始化,调用hub_port_init函数;
hub_port_init函数的解释如下:
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); int i, j, retval; //delay记录延时,因为usb设备的reset工作不可能是瞬间的,通常会有一点点延时 unsigned delay = HUB_SHORT_RESET_TIME; //定义一个oldspeed用来记录设备在没有reset之前的速度 enum usb_device_speed oldspeed = udev->speed; char *speed, *type; int devnum = udev->devnum; /* root hub ports have a slightly longer reset period * (from USB 2.0 spec, section 7.1.7.5) */ //root hub需要50ms的延时 if (!hdev->parent) { delay = HUB_ROOT_RESET_TIME; if (port1 == hdev->bus->otg_port) hdev->bus->b_hnp_enable = 0; } /* Some low speed devices have problems with the quick delay, so */ /* be a bit pessimistic with those devices. RHbug #23670 */ //某些低速设备要求有比较高的延时才能完成好它们的reset if (oldspeed == USB_SPEED_LOW) delay = HUB_LONG_RESET_TIME; mutex_lock(&usb_address0_mutex); //如果是USB3.0并且配置过了的设备,不支持reset if ((hcd->driver->flags & HCD_USB3) && udev->config) { /* FIXME this will need special handling by the xHCI driver. */ dev_dbg(&udev->dev, "xHCI reset of configured device " "not supported yet.\n"); retval = -EINVAL; goto fail; //USB 3.0 设置在初始化设置阶段不用reset } else if (!udev->config && oldspeed == USB_SPEED_SUPER) { /* Don't reset USB 3.0 devices during an initial setup */ usb_set_device_state(udev, USB_STATE_DEFAULT); } else { /* Reset the device; full speed may morph to high speed */ /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */ retval = hub_port_reset(hub, port1, udev, delay); if (retval < 0) /* error or disconnect */ goto fail; /* success, speed is known */ } //将retval临时设置为ENODEV retval = -ENODEV; //如果oldspeed不是USB_SPEED_UNKNOWN,并且也不等于刚刚设置的这个speed,那么说明reset之前设备已经在工作 //必须结束函数,并且disable这个端口 if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) { dev_dbg(&udev->dev, "device reset changed speed!\n"); goto fail; } //令oldspeed等于现在这个udev->speed oldspeed = udev->speed; /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ... * it's fixed size except for full speed devices. * For Wireless USB devices, ep0 max packet is always 512 (tho * reported as 0xff in the device descriptor). WUSB1.0[4.8.1]. */ switch (udev->speed) { //无线设备,端点0的最大包size就是512 case USB_SPEED_SUPER: case USB_SPEED_VARIABLE: /* fixed at 512 */ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512); break; //高速设备,端点0的最大包size就是64 case USB_SPEED_HIGH: /* fixed at 64 */ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64); break; //全速设备,端点0的最大包size就是64 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; //低速设备,端点0的最大包size就是8 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; } if (udev->speed != USB_SPEED_SUPER) dev_info(&udev->dev, "%s %s speed %sUSB device using %s and address %d\n", (udev->config) ? "reset" : "new", speed, type, udev->bus->controller->driver->name, devnum); /* Set up TT records, if needed */ if (hdev->tt) { udev->tt = hdev->tt; udev->ttport = hdev->ttport; } else if (udev->speed != USB_SPEED_HIGH && hdev->speed == USB_SPEED_HIGH) { udev->tt = &hub->tt; udev->ttport = port1; } /* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way? * Because device hardware and firmware is sometimes buggy in * this area, and this is how Linux has done it for ages. * Change it cautiously. * * NOTE: If USE_NEW_SCHEME() is true we will start by issuing * a 64-byte GET_DESCRIPTOR request. This is what Windows does, * so it may help with some non-standards-compliant devices. * Otherwise we start with SET_ADDRESS and then try to read the * first 8 bytes of the device descriptor to get the ep0 maxpacket * value. */ //GET_DESCRIPTOR_TRIES = 2 for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) { /* * An xHCI controller cannot send any packets to a device until * a set address command successfully completes. */ //,先进行两次新的策略,如果不行就再进行两次旧的策略,获得设备的描述符 //使用新策略,一次获取64字节的设备描述符 if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3)) { struct usb_device_descriptor *buf; int r = 0; #define GET_DESCRIPTOR_BUFSIZE 64 //申请一个64字节的buffer buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO); if (!buf) { retval = -ENOMEM; continue; } /* Retry on all errors; some devices are flakey. * 255 is for WUSB devices, we actually need to use * 512 (WUSB1.0[4.8.1]). */ //发送3次,获取设备描述符 for (j = 0; j < 3; ++j) { buf->bMaxPacketSize0 = 0; r = 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); switch (buf->bMaxPacketSize0) { case 8: case 16: case 32: case 64: case 255: if (buf->bDescriptorType == USB_DT_DEVICE) { r = 0; break; } /* FALL THROUGH */ default: if (r == 0) r = -EPROTO; break; } if (r == 0) break; } //用udev->descriptor.bMaxPacketSize0来记录这个临时获得的值,释放buf udev->descriptor.bMaxPacketSize0 = buf->bMaxPacketSize0; kfree(buf); //把设备reset retval = hub_port_reset(hub, port1, udev, delay); if (retval < 0) /* error or disconnect */ goto fail; if (oldspeed != udev->speed) { dev_dbg(&udev->dev, "device reset changed speed!\n"); retval = -ENODEV; goto fail; } if (r) { dev_err(&udev->dev, "device descriptor read/64, error %d\n", r); retval = -EMSGSIZE; continue; } #undef GET_DESCRIPTOR_BUFSIZE } /* * If device is WUSB, we already assigned an * unauthorized address in the Connect Ack sequence; * authorization will assign the final address. */ //无线device不用在这里设置地址 if (udev->wusb == 0) { for (j = 0; j < SET_ADDRESS_TRIES; ++j) { //将devnum这个地址设置给device retval = hub_set_address(udev, devnum); if (retval >= 0) break; msleep(200); } if (retval < 0) { dev_err(&udev->dev, "device not accepting address %d, error %d\n", devnum, retval); goto fail; } if (udev->speed == USB_SPEED_SUPER) { devnum = udev->devnum; dev_info(&udev->dev, "%s SuperSpeed USB device using %s and address %d\n", (udev->config) ? "reset" : "new", udev->bus->controller->driver->name, devnum); } /* cope with hardware quirkiness: * - let SET_ADDRESS settle, some device hardware wants it * - read ep0 maxpacket even for high and low speed, */ //先sleep 10ms msleep(10); //如果还是新策略,可以break出这个循环了。 if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3)) break; } //如果是使用老的策略 //先获得设备描述符的前8个字节,然后从中得知udev->descriptor的bMaxPacketSize0, //然后再次调用usb_get_device_descriptor完整的获得一次设备描述符 retval = usb_get_device_descriptor(udev, 8); //设备描述符不能小于8字节 if (retval < 8) { dev_err(&udev->dev, "device descriptor read/8, error %d\n", retval); if (retval >= 0) retval = -EMSGSIZE; } else { //否则就是获取设备描述符成功了,break出去 retval = 0; break; } } if (retval) goto fail; if (udev->descriptor.bMaxPacketSize0 == 0xff || udev->speed == USB_SPEED_SUPER) i = 512; else i = udev->descriptor.bMaxPacketSize0; if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) { if (udev->speed != USB_SPEED_FULL || !(i == 8 || i == 16 || i == 32 || i == 64)) { dev_err(&udev->dev, "ep0 maxpacket = %d\n", i); retval = -EMSGSIZE; goto fail; } dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i); udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i); usb_ep0_reinit(udev); } //获取完整的device的描述符 retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE); if (retval < (signed)sizeof(udev->descriptor)) { dev_err(&udev->dev, "device descriptor read/all, error %d\n", retval); if (retval >= 0) retval = -ENOMSG; goto fail; } retval = 0; fail: if (retval) { hub_port_disable(hub, port1, 0); update_address(udev, devnum); /* for disconnect processing */ } mutex_unlock(&usb_address0_mutex); return retval; }
这个函数的主要作用是:
设置设备的额状态成USB_STATE_DEFAULT;
reset usb端口;
获取设备的运行速度;
设置设备的地址,这个地址是前面调用choose_address分配的;
获取完整的设备描述符;
最后就是注册usb设备了。usb_new_device函数的过程在前面有详细的介绍过。
int usb_new_device(struct usb_device *udev) { int err; /* Increment the parent's count of unsuspended children */ //如果该设备不是Root Hub,则调用usb_autoresume_device(),唤醒它的父设备 if (udev->parent) usb_autoresume_device(udev->parent); //usb_detect_quirks会为在黑名单中找得到的设备的struct usb_device结构体中的quirks赋值 usb_detect_quirks(udev); /* Determine quirks */ //获得配置描述符 err = usb_configure_device(udev); /* detect & probe dev/intfs */ if (err < 0) goto fail; //一些打印信息 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 */ //minor=((dev->bus->busnum-1)*128)+(dev->devnum-1); //USB_DEVICE_MAJOR = 189 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); if (err) { dev_err(&udev->dev, "can't device_add, error %d\n", err); goto fail; } (void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev); return err; fail: usb_set_device_state(udev, USB_STATE_NOTATTACHED); usb_stop_pm(udev); return err; }
usb_get_configuration是获取包括设备描述符,配置描述符,接口描述符,端口描述符等所有的描述符。
err = device_add(&udev->dev)添加设备,这个设备在usb_alloc_dev函数中已经做了部分初始化。
dev->dev.bus = &usb_bus_type;
dev->dev.type = &usb_device_type;
dev->dev.groups = usb_device_groups;
文章参考:https://blog.csdn.net/fudan_abc/article/category/325189
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。