static int usb_hub_port_status(struct usb_device *hub, int port, u16 *status, u16 *change) { struct usb_port_status *portsts; int ret = -ENOMEM; portsts = kmalloc(sizeof(*portsts), GFP_KERNEL); if (portsts) { ret = usb_get_port_status(hub, port + 1, portsts); if (ret < 0) err("%s (%d) failed (err = %d)", __FUNCTION__, hub->devnum, ret); else { *status = le16_to_cpu(portsts->wPortStatus); *change = le16_to_cpu(portsts->wPortChange); dbg("port %d, portstatus %x, change %x, %s", port + 1, *status, *change, portspeed(*status)); //info("<usb_hub_port_status>:port %d, portstatus %x, change %x, %s", port + 1, // *status, *change, portspeed(*status)); ret = 0; } kfree(portsts); } return ret; }
void usb_hub_port_connect_change(struct usb_device *dev, int port) { struct usb_device *usb; ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1); unsigned short portstatus; /* Check status */ if (usb_get_port_status(dev, port + 1, portsts) < 0) { USB_HUB_PRINTF("get_port_status failed\n"); return; } portstatus = le16_to_cpu(portsts->wPortStatus); USB_HUB_PRINTF("portstatus %x, change %x, %s\n", portstatus, le16_to_cpu(portsts->wPortChange), portspeed(portstatus)); /* Clear the connection change status */ usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_CONNECTION); /* Disconnect any existing devices under this port */ if (((!(portstatus & USB_PORT_STAT_CONNECTION)) && (!(portstatus & USB_PORT_STAT_ENABLE))) || (dev->children[port])) { USB_HUB_PRINTF("usb_disconnect(&hub->children[port]);\n"); /* Return now if nothing is connected */ if (!(portstatus & USB_PORT_STAT_CONNECTION)) return; } /* Reset the port */ if (hub_port_reset(dev, port, &portstatus) < 0) { printf("cannot reset port %i!?\n", port + 1); return; } /* Allocate a new device struct for it */ usb = usb_alloc_new_device(dev->controller); if (portstatus & USB_PORT_STAT_HIGH_SPEED) usb->speed = USB_SPEED_HIGH; else if (portstatus & USB_PORT_STAT_LOW_SPEED) usb->speed = USB_SPEED_LOW; else usb->speed = USB_SPEED_FULL; dev->children[port] = usb; usb->parent = dev; usb->portnr = port + 1; /* Run it through the hoops (find a driver, etc) */ if (usb_new_device(usb)) { /* Woops, disable the port */ usb_free_device(); dev->children[port] = NULL; USB_HUB_PRINTF("hub: disabling port %d\n", port + 1); usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_ENABLE); } }
static void usb_hub_power_on(struct usb_hub_device *hub) { int i; struct usb_device *dev; unsigned pgood_delay = hub->desc.bPwrOn2PwrGood * 2; ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1); unsigned short portstatus; int ret; dev = hub->pusb_dev; /* * Enable power to the ports: * Here we Power-cycle the ports: aka, * turning them off and turning on again. */ debug("enabling power on all ports\n"); for (i = 0; i < dev->maxchild; i++) { usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_POWER); debug("port %d returns %lX\n", i + 1, dev->status); } /* Wait at least 2*bPwrOn2PwrGood for PP to change */ mdelay(pgood_delay); for (i = 0; i < dev->maxchild; i++) { ret = usb_get_port_status(dev, i + 1, portsts); if (ret < 0) { debug("port %d: get_port_status failed\n", i + 1); return; } /* * Check to confirm the state of Port Power: * xHCI says "After modifying PP, s/w shall read * PP and confirm that it has reached the desired state * before modifying it again, undefined behavior may occur * if this procedure is not followed". * EHCI doesn't say anything like this, but no harm in keeping * this. */ portstatus = le16_to_cpu(portsts->wPortStatus); if (portstatus & (USB_PORT_STAT_POWER << 1)) { debug("port %d: Port power change failed\n", i + 1); return; } } for (i = 0; i < dev->maxchild; i++) { usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER); debug("port %d returns %lX\n", i + 1, dev->status); } /* Wait for power to become stable */ mdelay(max(pgood_delay, CONFIG_USB_HUB_MIN_POWER_ON_DELAY)); }
int hub_port_reset(struct usb_device *dev, int port, unsigned short *portstat) { int tries; struct usb_port_status portsts; unsigned short portstatus, portchange; dev_dbg(&dev->dev, "hub_port_reset: resetting port %d...\n", port); for (tries = 0; tries < MAX_TRIES; tries++) { usb_set_port_feature(dev, port + 1, USB_PORT_FEAT_RESET); mdelay(200); if (usb_get_port_status(dev, port + 1, &portsts) < 0) { dev_dbg(&dev->dev, "get_port_status failed status %lX\n", dev->status); return -1; } portstatus = le16_to_cpu(portsts.wPortStatus); portchange = le16_to_cpu(portsts.wPortChange); dev_dbg(&dev->dev, "portstatus %x, change %x, %s\n", portstatus, portchange, portspeed(portstatus)); dev_dbg(&dev->dev, "STAT_C_CONNECTION = %d STAT_CONNECTION = %d" \ " USB_PORT_STAT_ENABLE %d\n", (portchange & USB_PORT_STAT_C_CONNECTION) ? 1 : 0, (portstatus & USB_PORT_STAT_CONNECTION) ? 1 : 0, (portstatus & USB_PORT_STAT_ENABLE) ? 1 : 0); if ((portchange & USB_PORT_STAT_C_CONNECTION) || !(portstatus & USB_PORT_STAT_CONNECTION)) return -1; if (portstatus & USB_PORT_STAT_ENABLE) break; mdelay(200); } if (tries == MAX_TRIES) { dev_dbg(&dev->dev, "Cannot enable port %i after %i retries, " \ "disabling port.\n", port + 1, MAX_TRIES); dev_dbg(&dev->dev, "Maybe the USB cable is bad?\n"); return -1; } usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_RESET); *portstat = portstatus; return 0; }
/* return: -1 on error, 0 on success, 1 on disconnect. */ static int usb_hub_port_wait_reset(struct usb_device *hub, int port, struct usb_device *dev, unsigned int delay) { int delay_time, ret; struct usb_port_status portsts; unsigned short portchange, portstatus; for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT; delay_time += delay) { /* wait to give the device a chance to reset */ wait_ms(delay); /* read and decode port status */ ret = usb_get_port_status(hub, port + 1, &portsts); if (ret < 0) { err("get_port_status(%d) failed (err = %d)", port + 1, ret); return -1; } portstatus = le16_to_cpu(portsts.wPortStatus); portchange = le16_to_cpu(portsts.wPortChange); dbg("port %d, portstatus %x, change %x, %s", port + 1, portstatus, portchange, portspeed (portstatus)); /* bomb out completely if something weird happened */ if ((portchange & USB_PORT_STAT_C_CONNECTION)) return -1; /* 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) dev->speed = USB_SPEED_HIGH; else if (portstatus & USB_PORT_STAT_LOW_SPEED) dev->speed = USB_SPEED_LOW; else dev->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; dbg("port %d of hub %d not reset yet, waiting %dms", port + 1, hub->devnum, delay); } return -1; }
static int hub_port_wait_reset(struct usb_device *dev, int port, unsigned int delay, unsigned short *portstatus_ret) { int delay_time, ret; ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1); unsigned short portstatus, portchange; for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT; delay_time += delay) { /* wait to give the device a chance to reset */ mdelay(delay); /* read and decode port status */ ret = usb_get_port_status(dev, port + 1, portsts); if (ret < 0) { USB_HUB_PRINTF("get_port_status failed status %lX\n", dev->status); return ret; } portstatus = le16_to_cpu(portsts->wPortStatus); portchange = le16_to_cpu(portsts->wPortChange); /* Device went away? */ if (!(portstatus & USB_PORT_STAT_CONNECTION)) return -ENOTCONN; /* bomb out completely if the connection bounced */ if ((portchange & USB_PORT_STAT_C_CONNECTION)) return -ENOTCONN; /* if we`ve finished resetting, then break out of the loop */ if (!(portstatus & USB_PORT_STAT_RESET) && (portstatus & USB_PORT_STAT_ENABLE)) { *portstatus_ret = portstatus; 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; USB_HUB_PRINTF( "port %d not reset yet, waiting %dms\n", port, delay); } return -1; }
/****************************************************************************** * Detect if a USB device has been plugged or unplugged. */ int usb_detect_change(void) { int i, j; int change = 0; for (j = 0; j < USB_MAX_DEVICE; j++) { for (i = 0; i < usb_dev[j].maxchild; i++) { struct usb_port_status status; if (usb_get_port_status(&usb_dev[j], i + 1, &status) < 0) /* USB request failed */ continue; if (le16_to_cpu(status.wPortChange) & USB_PORT_STAT_C_CONNECTION) change++; } } return change; }
static int usb_hub_configure(struct usb_device *dev) { int i, length; ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, USB_BUFSIZ); unsigned char *bitmap; short hubCharacteristics; struct usb_hub_descriptor *descriptor; struct usb_hub_device *hub; __maybe_unused struct usb_hub_status *hubsts; /* "allocate" Hub device */ hub = usb_hub_allocate(); if (hub == NULL) return -1; hub->pusb_dev = dev; /* Get the the hub descriptor */ if (usb_get_hub_descriptor(dev, buffer, 4) < 0) { debug("usb_hub_configure: failed to get hub " \ "descriptor, giving up %lX\n", dev->status); return -1; } descriptor = (struct usb_hub_descriptor *)buffer; length = min_t(int, descriptor->bLength, sizeof(struct usb_hub_descriptor)); if (usb_get_hub_descriptor(dev, buffer, length) < 0) { debug("usb_hub_configure: failed to get hub " \ "descriptor 2nd giving up %lX\n", dev->status); return -1; } memcpy((unsigned char *)&hub->desc, buffer, length); /* adjust 16bit values */ put_unaligned(le16_to_cpu(get_unaligned( &descriptor->wHubCharacteristics)), &hub->desc.wHubCharacteristics); /* set the bitmap */ bitmap = (unsigned char *)&hub->desc.DeviceRemovable[0]; /* devices not removable by default */ memset(bitmap, 0xff, (USB_MAXCHILDREN+1+7)/8); bitmap = (unsigned char *)&hub->desc.PortPowerCtrlMask[0]; memset(bitmap, 0xff, (USB_MAXCHILDREN+1+7)/8); /* PowerMask = 1B */ for (i = 0; i < ((hub->desc.bNbrPorts + 1 + 7)/8); i++) hub->desc.DeviceRemovable[i] = descriptor->DeviceRemovable[i]; for (i = 0; i < ((hub->desc.bNbrPorts + 1 + 7)/8); i++) hub->desc.PortPowerCtrlMask[i] = descriptor->PortPowerCtrlMask[i]; dev->maxchild = descriptor->bNbrPorts; debug("%d ports detected\n", dev->maxchild); hubCharacteristics = get_unaligned(&hub->desc.wHubCharacteristics); switch (hubCharacteristics & HUB_CHAR_LPSM) { case 0x00: debug("ganged power switching\n"); break; case 0x01: debug("individual port power switching\n"); break; case 0x02: case 0x03: debug("unknown reserved power switching mode\n"); break; } if (hubCharacteristics & HUB_CHAR_COMPOUND) debug("part of a compound device\n"); else debug("standalone hub\n"); switch (hubCharacteristics & HUB_CHAR_OCPM) { case 0x00: debug("global over-current protection\n"); break; case 0x08: debug("individual port over-current protection\n"); break; case 0x10: case 0x18: debug("no over-current protection\n"); break; } debug("power on to power good time: %dms\n", descriptor->bPwrOn2PwrGood * 2); debug("hub controller current requirement: %dmA\n", descriptor->bHubContrCurrent); for (i = 0; i < dev->maxchild; i++) debug("port %d is%s removable\n", i + 1, hub->desc.DeviceRemovable[(i + 1) / 8] & \ (1 << ((i + 1) % 8)) ? " not" : ""); if (sizeof(struct usb_hub_status) > USB_BUFSIZ) { debug("usb_hub_configure: failed to get Status - " \ "too long: %d\n", descriptor->bLength); return -1; } if (usb_get_hub_status(dev, buffer) < 0) { debug("usb_hub_configure: failed to get Status %lX\n", dev->status); return -1; } #ifdef DEBUG hubsts = (struct usb_hub_status *)buffer; #endif debug("get_hub_status returned status %X, change %X\n", le16_to_cpu(hubsts->wHubStatus), le16_to_cpu(hubsts->wHubChange)); debug("local power source is %s\n", (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_LOCAL_POWER) ? \ "lost (inactive)" : "good"); debug("%sover-current condition exists\n", (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? \ "" : "no "); usb_hub_power_on(hub); /* * Reset any devices that may be in a bad state when applying * the power. This is a __weak function. Resetting of the devices * should occur in the board file of the device. */ for (i = 0; i < dev->maxchild; i++) usb_hub_reset_devices(i + 1); for (i = 0; i < dev->maxchild; i++) { ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1); unsigned short portstatus, portchange; int ret; ulong start = get_timer(0); /* * Wait for (whichever finishes first) * - A maximum of 10 seconds * This is a purely observational value driven by connecting * a few broken pen drives and taking the max * 1.5 approach * - connection_change and connection state to report same * state */ do { ret = usb_get_port_status(dev, i + 1, portsts); if (ret < 0) { debug("get_port_status failed\n"); break; } portstatus = le16_to_cpu(portsts->wPortStatus); portchange = le16_to_cpu(portsts->wPortChange); if ((portchange & USB_PORT_STAT_C_CONNECTION) == (portstatus & USB_PORT_STAT_CONNECTION)) break; } while (get_timer(start) < CONFIG_SYS_HZ * 10); if (ret < 0) continue; debug("Port %d Status %X Change %X\n", i + 1, portstatus, portchange); if (portchange & USB_PORT_STAT_C_CONNECTION) { debug("port %d connection change\n", i + 1); usb_hub_port_connect_change(dev, i); } if (portchange & USB_PORT_STAT_C_ENABLE) { debug("port %d enable change, status %x\n", i + 1, portstatus); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_ENABLE); /* * The following hack causes a ghost device problem * to Faraday EHCI */ #ifndef CONFIG_USB_EHCI_FARADAY /* EM interference sometimes causes bad shielded USB * devices to be shutdown by the hub, this hack enables * them again. Works at least with mouse driver */ if (!(portstatus & USB_PORT_STAT_ENABLE) && (portstatus & USB_PORT_STAT_CONNECTION) && ((dev->children[i]))) { debug("already running port %i " \ "disabled by hub (EMI?), " \ "re-enabling...\n", i + 1); usb_hub_port_connect_change(dev, i); } #endif } if (portstatus & USB_PORT_STAT_SUSPEND) { debug("port %d suspend change\n", i + 1); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_SUSPEND); } if (portchange & USB_PORT_STAT_C_OVERCURRENT) { debug("port %d over-current change\n", i + 1); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_OVER_CURRENT); usb_hub_power_on(hub); } if (portchange & USB_PORT_STAT_C_RESET) { debug("port %d reset change\n", i + 1); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_RESET); } } /* end for i all ports */ return 0; }
int hub_port_reset(struct usb_device *dev, int port, unsigned short *portstat) { int tries; ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1); unsigned short portstatus, portchange; debug("hub_port_reset: resetting port %d...\n", port); for (tries = 0; tries < MAX_TRIES; tries++) { usb_set_port_feature(dev, port + 1, USB_PORT_FEAT_RESET); mdelay(200); if (usb_get_port_status(dev, port + 1, portsts) < 0) { debug("get_port_status failed status %lX\n", dev->status); return -1; } portstatus = le16_to_cpu(portsts->wPortStatus); portchange = le16_to_cpu(portsts->wPortChange); debug("portstatus %x, change %x, %s\n", portstatus, portchange, portspeed(portstatus)); debug("STAT_C_CONNECTION = %d STAT_CONNECTION = %d" \ " USB_PORT_STAT_ENABLE %d\n", (portchange & USB_PORT_STAT_C_CONNECTION) ? 1 : 0, (portstatus & USB_PORT_STAT_CONNECTION) ? 1 : 0, (portstatus & USB_PORT_STAT_ENABLE) ? 1 : 0); /* * Perhaps we should check for the following here: * - C_CONNECTION hasn't been set. * - CONNECTION is still set. * * Doing so would ensure that the device is still connected * to the bus, and hasn't been unplugged or replaced while the * USB bus reset was going on. * * However, if we do that, then (at least) a San Disk Ultra * USB 3.0 16GB device fails to reset on (at least) an NVIDIA * Tegra Jetson TK1 board. For some reason, the device appears * to briefly drop off the bus when this second bus reset is * executed, yet if we retry this loop, it'll eventually come * back after another reset or two. */ if (portstatus & USB_PORT_STAT_ENABLE) break; mdelay(200); } if (tries == MAX_TRIES) { debug("Cannot enable port %i after %i retries, " \ "disabling port.\n", port + 1, MAX_TRIES); debug("Maybe the USB cable is bad?\n"); return -1; } usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_RESET); *portstat = portstatus; return 0; }
int usb_hub_port_connect_change(struct usb_device *dev, int port) { ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1); unsigned short portstatus; int ret, speed; /* Check status */ ret = usb_get_port_status(dev, port + 1, portsts); if (ret < 0) { debug("get_port_status failed\n"); return ret; } portstatus = le16_to_cpu(portsts->wPortStatus); debug("portstatus %x, change %x, %s\n", portstatus, le16_to_cpu(portsts->wPortChange), portspeed(portstatus)); /* Clear the connection change status */ usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_CONNECTION); /* Disconnect any existing devices under this port */ if (((!(portstatus & USB_PORT_STAT_CONNECTION)) && (!(portstatus & USB_PORT_STAT_ENABLE))) || usb_device_has_child_on_port(dev, port)) { debug("usb_disconnect(&hub->children[port]);\n"); /* Return now if nothing is connected */ if (!(portstatus & USB_PORT_STAT_CONNECTION)) return -ENOTCONN; } mdelay(200); /* Reset the port */ ret = legacy_hub_port_reset(dev, port, &portstatus); if (ret < 0) { if (ret != -ENXIO) printf("cannot reset port %i!?\n", port + 1); return ret; } mdelay(200); switch (portstatus & USB_PORT_STAT_SPEED_MASK) { case USB_PORT_STAT_SUPER_SPEED: speed = USB_SPEED_SUPER; break; case USB_PORT_STAT_HIGH_SPEED: speed = USB_SPEED_HIGH; break; case USB_PORT_STAT_LOW_SPEED: speed = USB_SPEED_LOW; break; default: speed = USB_SPEED_FULL; break; } #ifdef CONFIG_DM_USB struct udevice *child; ret = usb_scan_device(dev->dev, port + 1, speed, &child); #else struct usb_device *usb; ret = usb_alloc_new_device(dev->controller, &usb); if (ret) { printf("cannot create new device: ret=%d", ret); return ret; } dev->children[port] = usb; usb->speed = speed; usb->parent = dev; usb->portnr = port + 1; /* Run it through the hoops (find a driver, etc) */ ret = usb_new_device(usb); if (ret < 0) { /* Woops, disable the port */ usb_free_device(dev->controller); dev->children[port] = NULL; } #endif if (ret < 0) { debug("hub: disabling port %d\n", port + 1); usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_ENABLE); } return ret; }
static void usb_hub_port_connect_change(struct usb_device *dev, int port) { struct usb_device *usb; struct usb_port_status portsts; unsigned short portstatus, portchange; /* Check status */ if (usb_get_port_status(dev, port + 1, &portsts) < 0) { dev_dbg(&dev->dev, "get_port_status failed\n"); return; } portstatus = le16_to_cpu(portsts.wPortStatus); portchange = le16_to_cpu(portsts.wPortChange); dev_dbg(&dev->dev, "portstatus %x, change %x, %s\n", portstatus, portchange, portspeed(portstatus)); /* Clear the connection change status */ usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_CONNECTION); /* Disconnect any existing devices under this port */ if (dev->children[port] && !(portstatus & USB_PORT_STAT_CONNECTION)) { dev_dbg(&dev->dev, "disconnect detected on port %d\n", port + 1); usb_remove_device(dev->children[port]); if (!dev->parent && dev->host->usbphy) usb_phy_notify_disconnect(dev->host->usbphy, dev->speed); return; } /* Remove disabled but connected devices */ if (dev->children[port] && !(portstatus & USB_PORT_STAT_ENABLE)) usb_remove_device(dev->children[port]); mdelay(200); /* Reset the port */ if (hub_port_reset(dev, port, &portstatus) < 0) { dev_warn(&dev->dev, "cannot reset port %i!?\n", port + 1); return; } mdelay(200); /* Allocate a new device struct for it */ usb = usb_alloc_new_device(); usb->dev.parent = &dev->dev; usb->host = dev->host; if (portstatus & USB_PORT_STAT_HIGH_SPEED) usb->speed = USB_SPEED_HIGH; else if (portstatus & USB_PORT_STAT_LOW_SPEED) usb->speed = USB_SPEED_LOW; else usb->speed = USB_SPEED_FULL; dev->children[port] = usb; usb->parent = dev; usb->portnr = port + 1; /* Run it through the hoops (find a driver, etc) */ if (usb_new_device(usb)) { /* Woops, disable the port */ dev_dbg(&dev->dev, "hub: disabling port %d\n", port + 1); usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_ENABLE); usb_free_device(usb); return; } if (!dev->parent && dev->host->usbphy) usb_phy_notify_connect(dev->host->usbphy, usb->speed); device_detect(&usb->dev); }
int usb_hub_events(void) { USB_LIST_T *tmp; USB_DEV_T *dev; USB_HUB_T *hub; USB_HUB_STATUS_T hubsts; uint16_t hubchange; uint16_t irq_data; int i, ret; //printf("0x%x 0x%x\n", *(uint32_t *)0x40009054, *(uint32_t *)0x40009058); if(list_empty(&_HubEventList)) return 0; /* * We restart the list everytime to avoid a deadlock with * deleting hubs downstream from this one. This should be * safe since we delete the hub from the event list. * Not the most efficient, but avoids deadlocks. */ while(1) { if(list_empty(&_HubEventList)) break; /* Grab the next entry from the beginning of the list */ tmp = _HubEventList.next; hub = list_entry(tmp, USB_HUB_T, event_list); dev = hub->dev; list_del(tmp); INIT_LIST_HEAD(tmp); if(hub->error) { USB_error("hub error %d!!\n", hub->error); } /* added by YCHuang */ if(hub->urb->transfer_buffer_length == 1) irq_data = *(uint8_t *)hub->urb->transfer_buffer; else { /* BIGBIG */ //irq_data = *(uint16_t *)hub->urb->transfer_buffer; /* FIX DEBUG, not verified */ irq_data = (*((uint8_t *)hub->urb->transfer_buffer + 1) << 8) | *(uint8_t *)hub->urb->transfer_buffer; } for(i = 0; i < hub->descriptor.bNbrPorts; i++) { USB_PORT_STATUS_T portsts; uint16_t portstatus, portchange; if(!((irq_data >> (i + 1)) & 0x01)) continue; USB_info("usb_hub_events - hub:%d, get port status...\n", hub->dev->devnum); ret = usb_get_port_status(dev, i + 1, &portsts); if(ret < 0) { USB_error("Error - get_hub %d port %d status failed (err = %d)\n", hub->dev->devnum, i + 1, ret); continue; } portstatus = portsts.wPortStatus; portchange = portsts.wPortChange; USB_debug("port %d, portstatus = %x, portchange = %x\n", i, portstatus, portchange); if(portchange & USB_PORT_STAT_C_CONNECTION) { USB_info("port %d of hub %d connection change\n", i + 1, hub->dev->devnum); usb_hub_port_connect_change(hub, dev, i, &portsts); } else if(portchange & USB_PORT_STAT_C_ENABLE) { USB_info("port %d of hub %d enable change, status %x\n", i + 1, portstatus, hub->dev->devnum); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_ENABLE); /* * EM interference sometimes causes bad shielded USB * devices to be shutdown by the hub, this hack enables * them again. Works at least with mouse driver. */ if(!(portstatus & USB_PORT_STAT_ENABLE) && (portstatus & USB_PORT_STAT_CONNECTION) && (dev->children[i])) { USB_error("Error - already running port %i disabled by hub (EMI?), re-enabling...\n", i + 1); usb_hub_port_connect_change(hub, dev, i, &portsts); } } if(portchange & USB_PORT_STAT_C_SUSPEND) { USB_info("port %d of hub %d suspend change\n", i + 1, hub->dev->devnum); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_SUSPEND); } if(portchange & USB_PORT_STAT_C_OVERCURRENT) { USB_warning("!! port %d of hub %d over-current change\n", i + 1, hub->dev->devnum); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_OVER_CURRENT); usb_hub_power_on(hub); } if(portchange & USB_PORT_STAT_C_RESET) { USB_info("port %d of hub %d reset change\n", i + 1, hub->dev->devnum); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_RESET); } } /* end for i */ /* deal with hub status changes */ if (usb_get_hub_status(dev, &hubsts) < 0) USB_error("Error - get_hub_status failed\n"); else { //hubstatus = USB_SWAP16(hubsts.wHubStatus); hubchange = USB_SWAP16(hubsts.wHubChange); if(hubchange & HUB_CHANGE_LOCAL_POWER) { USB_debug("hub power change\n"); usb_clear_hub_feature(dev, C_HUB_LOCAL_POWER); } if(hubchange & HUB_CHANGE_OVERCURRENT) { USB_error("!!hub overcurrent change\n"); usbh_mdelay(500); /* Cool down */ usb_clear_hub_feature(dev, C_HUB_OVER_CURRENT); usb_hub_power_on(hub); } } } /* end while (1) */ return 1; }
static int usb_hub_port_wait_reset(USB_DEV_T *hub, int port, USB_DEV_T *dev, uint32_t delay) { int delay_time, ret; uint16_t portchange, portstatus; USB_PORT_STATUS_T portsts; for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT; delay_time += delay) { /* wait to give the device a chance to reset */ usbh_mdelay(delay); /* read and decode port status */ ret = usb_get_port_status(hub, port + 1, &portsts); if(ret < 0) { USB_error("Error - get_port_status(%d) failed (err = %d)\n", port + 1, ret); return -1; } portstatus = portsts.wPortStatus; portchange = portsts.wPortChange; USB_info("port %d of hub %d, portstatus %x, change %x, %s\n", port + 1, hub->devnum, portstatus, portchange, portstatus & (1 << USB_PORT_FEAT_LOWSPEED) ? "1.5 Mb/s" : "12 Mb/s"); if((portchange & USB_PORT_STAT_C_CONNECTION) || !(portstatus & USB_PORT_STAT_CONNECTION)) return -1; /* if we have 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) { USB_debug("Device is high speed!\n"); dev->speed = USB_SPEED_HIGH; } else if(portstatus & USB_PORT_STAT_LOW_SPEED) { USB_debug("Device is low speed!\n"); dev->speed = USB_SPEED_LOW; } else { USB_debug("Device is full speed!\n"); dev->speed = USB_SPEED_FULL; } dev->slow = (portstatus & USB_PORT_STAT_LOW_SPEED) ? 1 : 0; 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; USB_info("port %d of hub %d not reset yet, waiting %dms\n", port + 1, hub->devnum, delay); } return -1; }
static int usb_hub_configure(struct usb_device *dev) { int i; ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, USB_BUFSIZ); unsigned char *bitmap; short hubCharacteristics; struct usb_hub_descriptor *descriptor; struct usb_hub_device *hub; #ifdef USB_HUB_DEBUG struct usb_hub_status *hubsts; #endif /* "allocate" Hub device */ hub = usb_hub_allocate(); if (hub == NULL) return -1; hub->pusb_dev = dev; /* Get the the hub descriptor */ if (usb_get_hub_descriptor(dev, buffer, 4) < 0) { USB_HUB_PRINTF("usb_hub_configure: failed to get hub " \ "descriptor, giving up %lX\n", dev->status); return -1; } descriptor = (struct usb_hub_descriptor *)buffer; /* silence compiler warning if USB_BUFSIZ is > 256 [= sizeof(char)] */ i = descriptor->bLength; if (i > USB_BUFSIZ) { USB_HUB_PRINTF("usb_hub_configure: failed to get hub " \ "descriptor - too long: %d\n", descriptor->bLength); return -1; } if (usb_get_hub_descriptor(dev, buffer, descriptor->bLength) < 0) { USB_HUB_PRINTF("usb_hub_configure: failed to get hub " \ "descriptor 2nd giving up %lX\n", dev->status); return -1; } memcpy((unsigned char *)&hub->desc, buffer, descriptor->bLength); /* adjust 16bit values */ put_unaligned(le16_to_cpu(get_unaligned( &descriptor->wHubCharacteristics)), &hub->desc.wHubCharacteristics); /* set the bitmap */ bitmap = (unsigned char *)&hub->desc.DeviceRemovable[0]; /* devices not removable by default */ memset(bitmap, 0xff, (USB_MAXCHILDREN+1+7)/8); bitmap = (unsigned char *)&hub->desc.PortPowerCtrlMask[0]; memset(bitmap, 0xff, (USB_MAXCHILDREN+1+7)/8); /* PowerMask = 1B */ for (i = 0; i < ((hub->desc.bNbrPorts + 1 + 7)/8); i++) hub->desc.DeviceRemovable[i] = descriptor->DeviceRemovable[i]; for (i = 0; i < ((hub->desc.bNbrPorts + 1 + 7)/8); i++) hub->desc.PortPowerCtrlMask[i] = descriptor->PortPowerCtrlMask[i]; dev->maxchild = descriptor->bNbrPorts; USB_HUB_PRINTF("%d ports detected\n", dev->maxchild); hubCharacteristics = get_unaligned(&hub->desc.wHubCharacteristics); switch (hubCharacteristics & HUB_CHAR_LPSM) { case 0x00: USB_HUB_PRINTF("ganged power switching\n"); break; case 0x01: USB_HUB_PRINTF("individual port power switching\n"); break; case 0x02: case 0x03: USB_HUB_PRINTF("unknown reserved power switching mode\n"); break; } if (hubCharacteristics & HUB_CHAR_COMPOUND) USB_HUB_PRINTF("part of a compound device\n"); else USB_HUB_PRINTF("standalone hub\n"); switch (hubCharacteristics & HUB_CHAR_OCPM) { case 0x00: USB_HUB_PRINTF("global over-current protection\n"); break; case 0x08: USB_HUB_PRINTF("individual port over-current protection\n"); break; case 0x10: case 0x18: USB_HUB_PRINTF("no over-current protection\n"); break; } USB_HUB_PRINTF("power on to power good time: %dms\n", descriptor->bPwrOn2PwrGood * 2); USB_HUB_PRINTF("hub controller current requirement: %dmA\n", descriptor->bHubContrCurrent); for (i = 0; i < dev->maxchild; i++) USB_HUB_PRINTF("port %d is%s removable\n", i + 1, hub->desc.DeviceRemovable[(i + 1) / 8] & \ (1 << ((i + 1) % 8)) ? " not" : ""); if (sizeof(struct usb_hub_status) > USB_BUFSIZ) { USB_HUB_PRINTF("usb_hub_configure: failed to get Status - " \ "too long: %d\n", descriptor->bLength); return -1; } if (usb_get_hub_status(dev, buffer) < 0) { USB_HUB_PRINTF("usb_hub_configure: failed to get Status %lX\n", dev->status); return -1; } #ifdef USB_HUB_DEBUG hubsts = (struct usb_hub_status *)buffer; #endif USB_HUB_PRINTF("get_hub_status returned status %X, change %X\n", le16_to_cpu(hubsts->wHubStatus), le16_to_cpu(hubsts->wHubChange)); USB_HUB_PRINTF("local power source is %s\n", (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_LOCAL_POWER) ? \ "lost (inactive)" : "good"); USB_HUB_PRINTF("%sover-current condition exists\n", (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? \ "" : "no "); usb_hub_power_on(hub); for (i = 0; i < dev->maxchild; i++) { ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1); unsigned short portstatus, portchange; int ret; ulong start = get_timer(0); /* * Wait for (whichever finishes first) * - A maximum of 10 seconds * This is a purely observational value driven by connecting * a few broken pen drives and taking the max * 1.5 approach * - connection_change and connection state to report same * state */ do { ret = usb_get_port_status(dev, i + 1, portsts); if (ret < 0) { USB_HUB_PRINTF("get_port_status failed\n"); break; } portstatus = le16_to_cpu(portsts->wPortStatus); portchange = le16_to_cpu(portsts->wPortChange); #ifdef CONFIG_MARVELL /* - USB3.0 issue with Avanta-LP/Armada-375: * If usb device was already connected before detection ('usb start'), * port connect status bit is set (USB_PORT_STAT_CONNECTION), * but port connect status change bit (USB_PORT_STAT_C_CONNECTION) is not set. * - Added WA (aligned with kernel driver): instead of checking port change status, * to be satisfied with checking USB_PORT_STAT_CONNECTION Bit on portstatus only" */ if (((portchange & USB_PORT_STAT_C_CONNECTION) == (portstatus & USB_PORT_STAT_CONNECTION)) || (portstatus & USB_PORT_STAT_CONNECTION)) #else if ((portchange & USB_PORT_STAT_C_CONNECTION) == (portstatus & USB_PORT_STAT_CONNECTION)) #endif break; mdelay(100); } while (get_timer(start) < CONFIG_SYS_HZ * 10); if (ret < 0) continue; USB_HUB_PRINTF("Port %d Status %X Change %X\n", i + 1, portstatus, portchange); #ifdef CONFIG_MARVELL /* - USB3.0 issue with Avanta-LP/Armada-375: * If usb device was already connected before detection ('usb start'), * port connect status bit is set (USB_PORT_STAT_CONNECTION), * but port connect status change bit (USB_PORT_STAT_C_CONNECTION) is not set. * - Added WA (aligned with kernel driver): instead of checking port change status, * to be satisfied with checking USB_PORT_STAT_CONNECTION Bit on portstatus only" */ if ((portchange & USB_PORT_STAT_C_CONNECTION) || (portstatus & USB_PORT_STAT_CONNECTION)) { #else if (portchange & USB_PORT_STAT_C_CONNECTION) { #endif USB_HUB_PRINTF("port %d connection change\n", i + 1); usb_hub_port_connect_change(dev, i); } if (portchange & USB_PORT_STAT_C_ENABLE) { USB_HUB_PRINTF("port %d enable change, status %x\n", i + 1, portstatus); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_ENABLE); /* EM interference sometimes causes bad shielded USB * devices to be shutdown by the hub, this hack enables * them again. Works at least with mouse driver */ if (!(portstatus & USB_PORT_STAT_ENABLE) && (portstatus & USB_PORT_STAT_CONNECTION) && ((dev->children[i]))) { USB_HUB_PRINTF("already running port %i " \ "disabled by hub (EMI?), " \ "re-enabling...\n", i + 1); usb_hub_port_connect_change(dev, i); } } if (portstatus & USB_PORT_STAT_SUSPEND) { USB_HUB_PRINTF("port %d suspend change\n", i + 1); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_SUSPEND); } if (portchange & USB_PORT_STAT_C_OVERCURRENT) { USB_HUB_PRINTF("port %d over-current change\n", i + 1); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_OVER_CURRENT); usb_hub_power_on(hub); } if (portchange & USB_PORT_STAT_C_RESET) { USB_HUB_PRINTF("port %d reset change\n", i + 1); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_RESET); } } /* end for i all ports */ return 0; } int usb_hub_probe(struct usb_device *dev, int ifnum) { struct usb_interface *iface; struct usb_endpoint_descriptor *ep; int ret; iface = &dev->config.if_desc[ifnum]; /* Is it a hub? */ if (iface->desc.bInterfaceClass != USB_CLASS_HUB) return 0; /* Some hubs have a subclass of 1, which AFAICT according to the */ /* specs is not defined, but it works */ if ((iface->desc.bInterfaceSubClass != 0) && (iface->desc.bInterfaceSubClass != 1)) return 0; /* Multiple endpoints? What kind of mutant ninja-hub is this? */ if (iface->desc.bNumEndpoints != 1) return 0; ep = &iface->ep_desc[0]; /* Output endpoint? Curiousier and curiousier.. */ if (!(ep->bEndpointAddress & USB_DIR_IN)) return 0; /* If it's not an interrupt endpoint, we'd better punt! */ if ((ep->bmAttributes & 3) != 3) return 0; /* We found a hub */ USB_HUB_PRINTF("USB hub found\n"); ret = usb_hub_configure(dev); return ret; }
int usb_hub_configure(struct usb_device *dev) { int i; ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, USB_BUFSIZ); unsigned char *bitmap; struct usb_hub_descriptor *descriptor; struct usb_hub_device *hub; #ifdef USB_HUB_DEBUG struct usb_hub_status *hubsts; #endif /* "allocate" Hub device */ hub = usb_hub_allocate(); if (hub == NULL) return -1; hub->pusb_dev = dev; /* Get the the hub descriptor */ if (usb_get_hub_descriptor(dev, buffer, 4) < 0) { USB_HUB_PRINTF("usb_hub_configure: failed to get hub " \ "descriptor, giving up %lX\n", dev->status); return -1; } descriptor = (struct usb_hub_descriptor *)buffer; /* silence compiler warning if USB_BUFSIZ is > 256 [= sizeof(char)] */ i = descriptor->bLength; if (i > USB_BUFSIZ) { USB_HUB_PRINTF("usb_hub_configure: failed to get hub " \ "descriptor - too long: %d\n", descriptor->bLength); return -1; } if (usb_get_hub_descriptor(dev, buffer, descriptor->bLength) < 0) { USB_HUB_PRINTF("usb_hub_configure: failed to get hub " \ "descriptor 2nd giving up %lX\n", dev->status); return -1; } memcpy((unsigned char *)&hub->desc, buffer, descriptor->bLength); /* adjust 16bit values */ hub->desc.wHubCharacteristics = le16_to_cpu(descriptor->wHubCharacteristics); /* set the bitmap */ bitmap = (unsigned char *)&hub->desc.DeviceRemovable[0]; /* devices not removable by default */ memset(bitmap, 0xff, (USB_MAXCHILDREN+1+7)/8); bitmap = (unsigned char *)&hub->desc.PortPowerCtrlMask[0]; memset(bitmap, 0xff, (USB_MAXCHILDREN+1+7)/8); /* PowerMask = 1B */ for (i = 0; i < ((hub->desc.bNbrPorts + 1 + 7)/8); i++) hub->desc.DeviceRemovable[i] = descriptor->DeviceRemovable[i]; for (i = 0; i < ((hub->desc.bNbrPorts + 1 + 7)/8); i++) hub->desc.PortPowerCtrlMask[i] = descriptor->PortPowerCtrlMask[i]; dev->maxchild = descriptor->bNbrPorts; USB_HUB_PRINTF("%d ports detected\n", dev->maxchild); switch (hub->desc.wHubCharacteristics & HUB_CHAR_LPSM) { case 0x00: USB_HUB_PRINTF("ganged power switching\n"); break; case 0x01: USB_HUB_PRINTF("individual port power switching\n"); break; case 0x02: case 0x03: USB_HUB_PRINTF("unknown reserved power switching mode\n"); break; } if (hub->desc.wHubCharacteristics & HUB_CHAR_COMPOUND) USB_HUB_PRINTF("part of a compound device\n"); else USB_HUB_PRINTF("standalone hub\n"); switch (hub->desc.wHubCharacteristics & HUB_CHAR_OCPM) { case 0x00: USB_HUB_PRINTF("global over-current protection\n"); break; case 0x08: USB_HUB_PRINTF("individual port over-current protection\n"); break; case 0x10: case 0x18: USB_HUB_PRINTF("no over-current protection\n"); break; } USB_HUB_PRINTF("power on to power good time: %dms\n", descriptor->bPwrOn2PwrGood * 2); USB_HUB_PRINTF("hub controller current requirement: %dmA\n", descriptor->bHubContrCurrent); for (i = 0; i < dev->maxchild; i++) USB_HUB_PRINTF("port %d is%s removable\n", i + 1, hub->desc.DeviceRemovable[(i + 1) / 8] & \ (1 << ((i + 1) % 8)) ? " not" : ""); if (sizeof(struct usb_hub_status) > USB_BUFSIZ) { USB_HUB_PRINTF("usb_hub_configure: failed to get Status - " \ "too long: %d\n", descriptor->bLength); return -1; } if (usb_get_hub_status(dev, buffer) < 0) { USB_HUB_PRINTF("usb_hub_configure: failed to get Status %lX\n", dev->status); return -1; } #ifdef USB_HUB_DEBUG hubsts = (struct usb_hub_status *)buffer; #endif USB_HUB_PRINTF("get_hub_status returned status %X, change %X\n", le16_to_cpu(hubsts->wHubStatus), le16_to_cpu(hubsts->wHubChange)); USB_HUB_PRINTF("local power source is %s\n", (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_LOCAL_POWER) ? \ "lost (inactive)" : "good"); USB_HUB_PRINTF("%sover-current condition exists\n", (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? \ "" : "no "); usb_hub_power_on(hub); for (i = 0; i < dev->maxchild; i++) { ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1); unsigned short portstatus, portchange; if (usb_get_port_status(dev, i + 1, portsts) < 0) { USB_HUB_PRINTF("get_port_status failed\n"); continue; } portstatus = le16_to_cpu(portsts->wPortStatus); portchange = le16_to_cpu(portsts->wPortChange); USB_HUB_PRINTF("Port %d Status %X Change %X\n", i + 1, portstatus, portchange); if (portchange & USB_PORT_STAT_C_CONNECTION) { USB_HUB_PRINTF("port %d connection change\n", i + 1); usb_hub_port_connect_change(dev, i); } if (portchange & USB_PORT_STAT_C_ENABLE) { USB_HUB_PRINTF("port %d enable change, status %x\n", i + 1, portstatus); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_ENABLE); /* EM interference sometimes causes bad shielded USB * devices to be shutdown by the hub, this hack enables * them again. Works at least with mouse driver */ if (!(portstatus & USB_PORT_STAT_ENABLE) && (portstatus & USB_PORT_STAT_CONNECTION) && ((dev->children[i]))) { USB_HUB_PRINTF("already running port %i " \ "disabled by hub (EMI?), " \ "re-enabling...\n", i + 1); usb_hub_port_connect_change(dev, i); } } if (portstatus & USB_PORT_STAT_SUSPEND) { USB_HUB_PRINTF("port %d suspend change\n", i + 1); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_SUSPEND); } if (portchange & USB_PORT_STAT_C_OVERCURRENT) { USB_HUB_PRINTF("port %d over-current change\n", i + 1); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_OVER_CURRENT); usb_hub_power_on(hub); } if (portchange & USB_PORT_STAT_C_RESET) { USB_HUB_PRINTF("port %d reset change\n", i + 1); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_RESET); } } /* end for i all ports */ return 0; }
static void usb_hub_events(void) { unsigned long flags; struct list_head *tmp; struct usb_device *dev; struct usb_hub *hub; struct usb_hub_status hubsts; unsigned short hubstatus, hubchange; int i, ret; /* * We restart the list everytime to avoid a deadlock with * deleting hubs downstream from this one. This should be * safe since we delete the hub from the event list. * Not the most efficient, but avoids deadlocks. */ while (1) { spin_lock_irqsave(&hub_event_lock, flags); if (list_empty(&hub_event_list)) break; /* Grab the next entry from the beginning of the list */ tmp = hub_event_list.next; hub = list_entry(tmp, struct usb_hub, event_list); dev = hub->dev; list_del(tmp); INIT_LIST_HEAD(tmp); down(&hub->khubd_sem); /* never blocks, we were on list */ spin_unlock_irqrestore(&hub_event_lock, flags); if (hub->error) { dbg("resetting hub %d for error %d", dev->devnum, hub->error); if (usb_hub_reset(hub)) { err("error resetting hub %d - disconnecting", dev->devnum); up(&hub->khubd_sem); usb_hub_disconnect(dev); continue; } hub->nerrors = 0; hub->error = 0; } for (i = 0; i < hub->descriptor->bNbrPorts; i++) { struct usb_port_status portsts; unsigned short portstatus, portchange; ret = usb_get_port_status(dev, i + 1, &portsts); if (ret < 0) { err("get_port_status failed (err = %d)", ret); continue; } portstatus = le16_to_cpu(portsts.wPortStatus); portchange = le16_to_cpu(portsts.wPortChange); if (portchange & USB_PORT_STAT_C_CONNECTION) { dbg("port %d connection change", i + 1); usb_hub_port_connect_change(dev, i, &portsts); } else if (portchange & USB_PORT_STAT_C_ENABLE) { dbg("port %d enable change, status %x", i + 1, portstatus); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_ENABLE); /* * EM interference sometimes causes bad shielded USB devices to * be shutdown by the hub, this hack enables them again. * Works at least with mouse driver. */ if (!(portstatus & USB_PORT_STAT_ENABLE) && (portstatus & USB_PORT_STAT_CONNECTION) && (dev->children[i])) { err("already running port %i disabled by hub (EMI?), re-enabling...", i + 1); usb_hub_port_connect_change(dev, i, &portsts); } } if (portchange & USB_PORT_STAT_C_SUSPEND) { dbg("port %d suspend change", i + 1); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_SUSPEND); } if (portchange & USB_PORT_STAT_C_OVERCURRENT) { err("port %d over-current change", i + 1); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_OVER_CURRENT); usb_hub_power_on(hub); } if (portchange & USB_PORT_STAT_C_RESET) { dbg("port %d reset change", i + 1); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_RESET); } } /* end for i */ /* deal with hub status changes */ if (usb_get_hub_status(dev, &hubsts) < 0) err("get_hub_status failed"); else { hubstatus = le16_to_cpup(&hubsts.wHubStatus); hubchange = le16_to_cpup(&hubsts.wHubChange); if (hubchange & HUB_CHANGE_LOCAL_POWER) { dbg("hub power change"); usb_clear_hub_feature(dev, C_HUB_LOCAL_POWER); } if (hubchange & HUB_CHANGE_OVERCURRENT) { dbg("hub overcurrent change"); wait_ms(500); /* Cool down */ usb_clear_hub_feature(dev, C_HUB_OVER_CURRENT); usb_hub_power_on(hub); } } up(&hub->khubd_sem); } /* end while (1) */ spin_unlock_irqrestore(&hub_event_lock, flags); }
static int usb_hub_configure_port(struct usb_device *dev, int port) { struct usb_port_status portsts; unsigned short portstatus, portchange; int connect_change = 0; if (usb_get_port_status(dev, port + 1, &portsts) < 0) { dev_dbg(&dev->dev, "get_port_status failed\n"); return -EIO; } portstatus = le16_to_cpu(portsts.wPortStatus); portchange = le16_to_cpu(portsts.wPortChange); dev_dbg(&dev->dev, "Port %d Status %X Change %X\n", port + 1, portstatus, portchange); if (portchange & USB_PORT_STAT_C_CONNECTION) { dev_dbg(&dev->dev, "port %d connection change\n", port + 1); connect_change = 1; } if (portchange & USB_PORT_STAT_C_ENABLE) { dev_dbg(&dev->dev, "port %d enable change, status %x\n", port + 1, portstatus); usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_ENABLE); /* EM interference sometimes causes bad shielded USB * devices to be shutdown by the hub, this hack enables * them again. Works at least with mouse driver */ if (!(portstatus & USB_PORT_STAT_ENABLE) && (portstatus & USB_PORT_STAT_CONNECTION) && ((dev->children[port]))) { dev_dbg(&dev->dev, "already running port %i " \ "disabled by hub (EMI?), " \ "re-enabling...\n", port + 1); connect_change = 1; } } if (connect_change) usb_hub_port_connect_change(dev, port); if (portstatus & USB_PORT_STAT_SUSPEND) { dev_dbg(&dev->dev, "port %d suspend change\n", port + 1); usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_SUSPEND); } if (portchange & USB_PORT_STAT_C_OVERCURRENT) { dev_dbg(&dev->dev, "port %d over-current change\n", port + 1); usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_OVER_CURRENT); usb_hub_power_on(dev->hub); } if (portchange & USB_PORT_STAT_C_RESET) { dev_dbg(&dev->dev, "port %d reset change\n", port + 1); usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_RESET); } return 0; }