📄 hub.c
字号:
c, err); usb_remove_sysfs_dev_files(udev); device_del(&udev->dev); goto fail; } } /* USB device state == configured ... usable */ /* add a /proc/bus/usb entry */ usbfs_add_device(udev); return 0;fail: usb_set_device_state(udev, USB_STATE_NOTATTACHED); return err;}static int hub_port_status(struct usb_device *hdev, int port, u16 *status, u16 *change){ struct usb_hub *hub = usb_get_intfdata(hdev->actconfig->interface[0]); int ret; if (!hub) return -ENODEV; ret = get_port_status(hdev, port + 1, &hub->status->port); if (ret < 0) dev_err (&hub->intf->dev, "%s failed (err = %d)\n", __FUNCTION__, ret); else { *status = le16_to_cpu(hub->status->port.wPortStatus); *change = le16_to_cpu(hub->status->port.wPortChange); ret = 0; } return ret;}#define PORT_RESET_TRIES 5#define SET_ADDRESS_TRIES 2#define GET_DESCRIPTOR_TRIES 2#define SET_CONFIG_TRIES 2#define HUB_ROOT_RESET_TIME 50 /* times are in msec */#define HUB_SHORT_RESET_TIME 10#define HUB_LONG_RESET_TIME 200#define HUB_RESET_TIMEOUT 500static int hub_port_wait_reset(struct usb_device *hdev, int port, struct usb_device *udev, unsigned int delay){ int delay_time, ret; u16 portstatus; u16 portchange; for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT; delay_time += delay) { /* wait to give the device a chance to reset */ msleep(delay); /* read and decode port status */ ret = hub_port_status(hdev, port, &portstatus, &portchange); if (ret < 0) return ret; /* Device went away? */ if (!(portstatus & USB_PORT_STAT_CONNECTION)) return -ENOTCONN; /* bomb out completely if something weird happened */ if ((portchange & USB_PORT_STAT_C_CONNECTION)) return -EINVAL; /* if we`ve finished resetting, then break out of the loop */ if (!(portstatus & USB_PORT_STAT_RESET) && (portstatus & USB_PORT_STAT_ENABLE)) { if (portstatus & USB_PORT_STAT_HIGH_SPEED) udev->speed = USB_SPEED_HIGH; else if (portstatus & USB_PORT_STAT_LOW_SPEED) udev->speed = USB_SPEED_LOW; else udev->speed = USB_SPEED_FULL; return 0; } /* switch to the long delay after two short delay failures */ if (delay_time >= 2 * HUB_SHORT_RESET_TIME) delay = HUB_LONG_RESET_TIME; dev_dbg (hubdev (hdev), "port %d not reset yet, waiting %dms\n", port + 1, delay); } return -EBUSY;}static int hub_port_reset(struct usb_device *hdev, int port, struct usb_device *udev, unsigned int delay){ int i, status; struct device *hub_dev = hubdev (hdev); /* Reset the port */ for (i = 0; i < PORT_RESET_TRIES; i++) { status = set_port_feature(hdev, port + 1, USB_PORT_FEAT_RESET); if (status) dev_err(hub_dev, "cannot reset port %d (err = %d)\n", port + 1, status); else status = hub_port_wait_reset(hdev, port, udev, delay); /* return on disconnect or reset */ if (status == -ENOTCONN || status == 0) { clear_port_feature(hdev, port + 1, USB_PORT_FEAT_C_RESET); usb_set_device_state(udev, status ? USB_STATE_NOTATTACHED : USB_STATE_DEFAULT); return status; } dev_dbg (hub_dev, "port %d not enabled, trying reset again...\n", port + 1); delay = HUB_LONG_RESET_TIME; } dev_err (hub_dev, "Cannot enable port %i. Maybe the USB cable is bad?\n", port + 1); return status;}static int hub_port_disable(struct usb_device *hdev, int port){ int ret; if (hdev->children[port]) usb_set_device_state(hdev->children[port], USB_STATE_NOTATTACHED); ret = clear_port_feature(hdev, port + 1, USB_PORT_FEAT_ENABLE); if (ret) dev_err(hubdev(hdev), "cannot disable port %d (err = %d)\n", port + 1, ret); return ret;}#ifdef CONFIG_USB_SUSPEND /* no USB_SUSPEND yet! */#else /* !CONFIG_USB_SUSPEND */int usb_suspend_device(struct usb_device *udev, u32 state){ return 0;}int usb_resume_device(struct usb_device *udev){ return 0;}#define hub_suspend NULL#define hub_resume NULL#define remote_wakeup(x) 0#endif /* CONFIG_USB_SUSPEND */EXPORT_SYMBOL(usb_suspend_device);EXPORT_SYMBOL(usb_resume_device);/* USB 2.0 spec, 7.1.7.3 / fig 7-29: * * Between connect detection and reset signaling there must be a delay * of 100ms at least for debounce and power-settling. The corresponding * timer shall restart whenever the downstream port detects a disconnect. * * Apparently there are some bluetooth and irda-dongles and a number of * low-speed devices for which this debounce period may last over a second. * Not covered by the spec - but easy to deal with. * * This implementation uses a 1500ms total debounce timeout; if the * connection isn't stable by then it returns -ETIMEDOUT. It checks * every 25ms for transient disconnects. When the port status has been * unchanged for 100ms it returns the port status. */#define HUB_DEBOUNCE_TIMEOUT 1500#define HUB_DEBOUNCE_STEP 25#define HUB_DEBOUNCE_STABLE 100static int hub_port_debounce(struct usb_device *hdev, int port){ int ret; int total_time, stable_time = 0; u16 portchange, portstatus; unsigned connection = 0xffff; for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) { ret = hub_port_status(hdev, port, &portstatus, &portchange); if (ret < 0) return ret; if (!(portchange & USB_PORT_STAT_C_CONNECTION) && (portstatus & USB_PORT_STAT_CONNECTION) == connection) { stable_time += HUB_DEBOUNCE_STEP; if (stable_time >= HUB_DEBOUNCE_STABLE) break; } else { stable_time = 0; connection = portstatus & USB_PORT_STAT_CONNECTION; } if (portchange & USB_PORT_STAT_C_CONNECTION) { clear_port_feature(hdev, port+1, USB_PORT_FEAT_C_CONNECTION); } if (total_time >= HUB_DEBOUNCE_TIMEOUT) break; msleep(HUB_DEBOUNCE_STEP); } dev_dbg (hubdev (hdev), "debounce: port %d: total %dms stable %dms status 0x%x\n", port + 1, total_time, stable_time, portstatus); if (stable_time < HUB_DEBOUNCE_STABLE) return -ETIMEDOUT; return portstatus;}static int hub_set_address(struct usb_device *udev){ int retval; if (udev->devnum == 0) return -EINVAL; if (udev->state != USB_STATE_DEFAULT && udev->state != USB_STATE_ADDRESS) return -EINVAL; retval = usb_control_msg(udev, (PIPE_CONTROL << 30) /* Address 0 */, USB_REQ_SET_ADDRESS, 0, udev->devnum, 0, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT); if (retval == 0) usb_set_device_state(udev, USB_STATE_ADDRESS); return retval;}/* Reset device, (re)assign address, get device descriptor. * Device connection must be stable, no more debouncing needed. * Returns device in USB_STATE_ADDRESS, except on error. * * If this is called for an already-existing device (as part of * usb_reset_device), the caller must own the device lock. For a * newly detected device that is not accessible through any global * pointers, it's not necessary to lock the device. */static inthub_port_init (struct usb_device *hdev, struct usb_device *udev, int port){ static DECLARE_MUTEX(usb_address0_sem); int i, j, retval; unsigned delay = HUB_SHORT_RESET_TIME; enum usb_device_speed oldspeed = udev->speed; /* root hub ports have a slightly longer reset period * (from USB 2.0 spec, section 7.1.7.5) */ if (!hdev->parent) { delay = HUB_ROOT_RESET_TIME; if (port + 1 == 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 */ if (oldspeed == USB_SPEED_LOW) delay = HUB_LONG_RESET_TIME; down(&usb_address0_sem); /* Reset the device; full speed may morph to high speed */ retval = hub_port_reset(hdev, port, udev, delay); if (retval < 0) /* error or disconnect */ goto fail; /* success, speed is known */ retval = -ENODEV; if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) { dev_dbg(&udev->dev, "device reset changed speed!\n"); goto fail; } /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ... * it's fixed size except for full speed devices. */ switch (udev->speed) { case USB_SPEED_HIGH: /* fixed at 64 */ i = 64; break; case USB_SPEED_FULL: /* 8, 16, 32, or 64 */ /* to determine the ep0 maxpacket size, read the first 8 * bytes from the device descriptor to get bMaxPacketSize0; * then correct our initial (small) guess. */ // FALLTHROUGH case USB_SPEED_LOW: /* fixed at 8 */ i = 8; break; default: goto fail; } udev->epmaxpacketin [0] = i; udev->epmaxpacketout[0] = i; dev_info (&udev->dev, "%s %s speed USB device using address %d\n", (udev->config) ? "reset" : "new", ({ char *speed; switch (udev->speed) { case USB_SPEED_LOW: speed = "low"; break; case USB_SPEED_FULL: speed = "full"; break; case USB_SPEED_HIGH: speed = "high"; break; default: speed = "?"; break; }; speed;}), udev->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) { struct usb_hub *hub; hub = usb_get_intfdata(hdev->actconfig->interface[0]); udev->tt = &hub->tt; udev->ttport = port + 1; } /* 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: Windows gets the descriptor first, seemingly to help * work around device bugs like "can't use addresses with bit 3 * set in certain configurations". Yes, really. */ for (i = 0; i < GET_DESCRIPTOR_TRIES; ++i) { for (j = 0; j < SET_ADDRESS_TRIES; ++j) { retval = hub_set_address(udev); if (retval >= 0) break; msleep(200); } if (retval < 0) { dev_err(&udev->dev, "device not accepting address %d, error %d\n", udev->devnum, retval); goto fail; } /* cope with hardware quirkiness: * - let SET_ADDRESS settle, some device hardware wants it * - read ep0 maxpacket even for high and low speed, */ msleep(10); retval = usb_get_device_descriptor(udev, 8); if (retval >= 8) break; msleep(100); } if (retval != 8) { dev_err(&udev->dev, "device descriptor read/%s, error %d\n", "8", retval); if (retval >= 0) retval = -EMSGSIZE; goto fail; } if (udev->speed == USB_SPEED_FULL && (udev->epmaxpacketin [0] != udev->descriptor.bMaxPacketSize0)) { usb_disable_endpoint(udev, 0 + USB_DIR_IN); usb_disable_endpoint(udev, 0 + USB_DIR_OUT); usb_endpoint_running(udev, 0, 1); usb_endpoint_running(udev, 0, 0); udev->epmaxpacketin [0] = udev->descriptor.bMaxPacketSize0; udev->epmaxpacketout[0] = udev->descriptor.bMaxPacketSize0; } retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE); if (retval < (signed)sizeof(udev->descriptor)) { dev_err(&udev->dev, "device descriptor read/%s, error %d\n", "all", retval); if (retval >= 0) retval = -ENOMSG; goto fail; } retval = 0;fail: up(&usb_address0_sem); return retval;}static voidcheck_highspeed (struct usb_hub *hub, struct usb_device *udev, int port){ struct usb_qualifier_descriptor *qual; int status; qual = kmalloc (sizeof *qual, SLAB_KERNEL); if (qual == 0) return; status = usb_get_descriptor (udev, USB_DT_DEVICE_QUALIFIER, 0, qual, sizeof *qual); if (status == sizeof *qual) { dev_info(&udev->dev, "not running at top speed; " "connect to a high speed hub\n"); /* hub LEDs are probably harder to miss than syslog */ if (hub->has_indicators) { hub->indicator[port] = INDICATOR_GREEN_BLINK; schedule_work (&hub->leds); } } kfree (qual);}static unsignedhub_power_remaining (struct usb_hub *hub){ struct usb_device *hdev = hub->hdev; int remaining; unsigned i; remaining = hub->power_budget; if (!remaining) /* self-powered */ return 0; for (i = 0; i < hdev->maxchild; i++) { struct usb_device *udev = hdev->children[i]; int delta, ceiling; if (!udev) continue; /* 100mA per-port ceiling, or 8mA for OTG ports */ if (i != (udev->bus->otg_port - 1) || hdev->parent) ceiling = 50; else ceiling = 4; if (udev->actconfig) delta = udev->actconfig->desc.bMaxPower; else delta = ceiling; // dev_dbg(&udev->dev, "budgeted %dmA\n", 2 * delta); if (delta > ceiling) dev_warn(&udev->dev, "%dmA over %dmA budget!\n", 2 * (delta - ceiling), 2 * ceiling); remaining -= delta; } if (remaining < 0) { dev_warn(&hub->intf->dev, "%dmA over power budget!\n", -2 * remaining); remaining = 0; } return remaining;}/* Handle physical or logical connection change events. * This routine is called when: * a port connection-change occurs; * a port enable-change occurs (often caused by EMI); * usb_reset_device() encounters changed descriptors (as from * a firmware download) */static void hub_port_connect_change(struct usb_hub *hub, int port, u16 portstatus, u16 portchange){ struct usb_device *hdev = hub->hdev; struct device *hub_dev = &hub->intf->dev; int status, i; dev_dbg (hub_dev, "port %d, status %04x, change %04x, %s\n", port + 1, portstatus, portchange, portspeed (portstatus)); if (hub->has_indicators) { set_port_led(hdev, port + 1, HUB_LED_AUTO); hub->indicator[port] = INDICATOR_AUTO; } /* Disconnect any existing devices under this port */ if (hdev->children[port]) usb_disconnect(&hdev->children[port]); clear_bit(port, hub->change_bits); if (portchange & USB_PORT_STAT_C_CONNECTION) { status = hub_port_debounce(hdev, port); if (status < 0) { dev_err (hub_dev, "connect-debounce failed, port %d disabled\n", port+1); goto done; } portstatus = status; } /* Return now if nothing is connected */ if (!(portstatus & USB_PORT_STAT_CONNECTION)) { /* maybe switch power back on (e.g. root hub was reset) */ if ((hub->descriptor->wHubCharacteristics & HUB_CHAR_LPSM) < 2 && !(portstatus & (1 << USB_PORT_FEAT_POWER))) set_port_feature(hdev, port + 1, USB_PORT_FEAT_POWER); if (portstatus & USB_PORT_STAT_ENABLE) goto done; return; } for (i = 0; i < SET_CONFIG_TRIES; i++) { struct usb_device *udev; /* reallocate for each attempt, since references * to the previous one can escape in various ways */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -