Newer
Older
{
return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0,
data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT);
}
int usb_get_port_status(struct usb_device *dev, int port, void *data)
{
return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port,
data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT);
}
static void usb_hub_power_on(struct usb_hub_device *hub)
{
int i;
struct usb_device *dev;
/* Enable power to the ports */
USB_HUB_PRINTF("enabling power on all ports\n");
for (i = 0; i < dev->maxchild; i++) {
usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER);
USB_HUB_PRINTF("port %d returns %lX\n", i + 1, dev->status);
wait_ms(hub->desc.bPwrOn2PwrGood * 2);
}
}
void usb_hub_reset(void)
{
}
struct usb_hub_device *usb_hub_allocate(void)
{
if (usb_hub_index < USB_MAX_HUB)
printf("ERROR: USB_MAX_HUB (%d) reached\n", USB_MAX_HUB);
return NULL;
}
#define MAX_TRIES 5
static inline char *portspeed(int portstatus)
{
if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED))
return "480 Mb/s";
else if (portstatus & (1 << USB_PORT_FEAT_LOWSPEED))
return "1.5 Mb/s";
else
return "12 Mb/s";
}
static int hub_port_reset(struct usb_device *dev, int port,
unsigned short *portstat)
struct usb_port_status portsts;
unsigned short portstatus, portchange;
USB_HUB_PRINTF("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);
wait_ms(200);
if (usb_get_port_status(dev, port + 1, &portsts) < 0) {
USB_HUB_PRINTF("get_port_status failed status %lX\n",
dev->status);
portstatus = le16_to_cpu(portsts.wPortStatus);
portchange = le16_to_cpu(portsts.wPortChange);
USB_HUB_PRINTF("portstatus %x, change %x, %s\n",
portstatus, portchange,
portspeed(portstatus));
USB_HUB_PRINTF("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))
if (portstatus & USB_PORT_STAT_ENABLE)
if (tries == MAX_TRIES) {
USB_HUB_PRINTF("Cannot enable port %i after %i retries, " \
"disabling port.\n", port + 1, MAX_TRIES);
}
usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_RESET);
*portstat = portstatus;
return 0;
}
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) {
USB_HUB_PRINTF("get_port_status failed\n");
return;
}
portstatus = le16_to_cpu(portsts.wPortStatus);
portchange = le16_to_cpu(portsts.wPortChange);
USB_HUB_PRINTF("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 (((!(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;
}
wait_ms(200);
/* Reset the port */
if (hub_port_reset(dev, port, &portstatus) < 0) {
printf("cannot reset port %i!?\n", port + 1);
return;
}
wait_ms(200);
/* Allocate a new device struct for it */
usb = usb_alloc_new_device();
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;
/* Run it through the hoops (find a driver, etc) */
if (usb_new_device(usb)) {
/* Woops, disable the port */
USB_HUB_PRINTF("hub: disabling port %d\n", port + 1);
usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_ENABLE);
}
}
int usb_hub_configure(struct usb_device *dev)
{
unsigned char buffer[USB_BUFSIZ], *bitmap;
struct usb_hub_descriptor *descriptor;
struct usb_hub_status *hubsts;
int i;
struct usb_hub_device *hub;
/* "allocate" Hub device */
hub = usb_hub_allocate();
if (hub == NULL)
/* 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);
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);
memcpy((unsigned char *)&hub->desc, buffer, descriptor->bLength);
hub->desc.wHubCharacteristics =
le16_to_cpu(descriptor->wHubCharacteristics);
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.DeviceRemovable[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);
USB_HUB_PRINTF("usb_hub_configure: failed to get Status %lX\n",
dev->status);
hubsts = (struct usb_hub_status *)buffer;
USB_HUB_PRINTF("get_hub_status returned status %X, change %X\n",
le16_to_cpu(hubsts->wHubStatus),
le16_to_cpu(hubsts->wHubChange));
(le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_LOCAL_POWER) ? \
"lost (inactive)" : "good");
(le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? \
"" : "no ");
for (i = 0; i < dev->maxchild; i++) {
struct usb_port_status portsts;
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 */
(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);
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
}
} /* end for i all ports */
return 0;
}
int usb_hub_probe(struct usb_device *dev, int ifnum)
{
struct usb_interface_descriptor *iface;
struct usb_endpoint_descriptor *ep;
int ret;
iface = &dev->config.if_desc[ifnum];
/* Is it a hub? */
if (iface->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->bInterfaceSubClass != 0) &&
(iface->bInterfaceSubClass != 1))
return 0;
/* Multiple endpoints? What kind of mutant ninja-hub is this? */
if (iface->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);