?? hub.c
字號:
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 * 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; } retval = clear_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND); if (retval < 0 && retval != -EPIPE) dev_dbg(&udev->dev, "can't clear suspend; %d\n", retval); /* 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); 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) * caller already locked the hub */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);#ifdef CONFIG_USB_OTG /* during HNP, don't repeat the debounce */ if (hdev->bus->is_b_host) portchange &= ~USB_PORT_STAT_C_CONNECTION;#endif 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 */ udev = usb_alloc_dev(hdev, hdev->bus, port); if (!udev) { dev_err (hub_dev, "couldn't allocate port %d usb_device\n", port+1); goto done; } usb_set_device_state(udev, USB_STATE_POWERED); udev->speed = USB_SPEED_UNKNOWN; /* set the address */ choose_address(udev); if (udev->devnum <= 0) { status = -ENOTCONN; /* Don't retry */ goto loop; } /* reset and get descriptor */ status = hub_port_init(hdev, udev, port); 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. */ if (udev->descriptor.bDeviceClass == USB_CLASS_HUB && hub->power_budget) { u16 devstat; status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstat); if (status < 0) { dev_dbg(&udev->dev, "get status %d ?\n", status); goto loop; } cpu_to_le16s(&devstat); 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[port] = INDICATOR_AMBER_BLINK; schedule_work (&hub->leds); } status = -ENOTCONN; /* Don't retry */ goto loop; } } /* check for devices running slower than they could */ if (udev->descriptor.bcdUSB >= 0x0200 && udev->speed == USB_SPEED_FULL && highspeed_hubs != 0) check_highspeed (hub, udev, port); /* 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. */ down (&udev->serialize); 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); if (hdev->state == USB_STATE_NOTATTACHED) status = -ENOTCONN; else hdev->children[port] = udev; spin_unlock_irq(&device_state_lock); /* Run it through the hoops (find a driver, etc) */ if (!status) { status = usb_new_device(udev); if (status) { spin_lock_irq(&device_state_lock); hdev->children[port] = NULL; spin_unlock_irq(&device_state_lock); } } up (&udev->serialize); if (status) goto loop; status = hub_power_remaining(hub); if (status) dev_dbg(hub_dev, "%dmA power budget left\n", 2 * status); return;loop: hub_port_disable(hdev, port); usb_disable_endpoint(udev, 0 + USB_DIR_IN); usb_disable_endpoint(udev, 0 + USB_DIR_OUT); release_address(udev); usb_put_dev(udev); if (status == -ENOTCONN) break; } done: hub_port_disable(hdev, port);}static void hub_events(void){ struct list_head *tmp; struct usb_device *hdev; struct usb_hub *hub; struct device *hub_dev; u16 hubstatus; u16 hubchange; u16 portstatus; u16 portchange; int i, ret; int connect_change; /* * We restart the list every time 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) { /* Grab the first entry at the beginning of the list */ spin_lock_irq(&hub_event_lock); if (list_empty(&hub_event_list)) { spin_unlock_irq(&hub_event_lock); break; } tmp = hub_event_list.next; list_del_init(tmp); hub = list_entry(tmp, struct usb_hub, event_list); hdev = hub->hdev; hub_dev = &hub->intf->dev; usb_get_dev(hdev); spin_unlock_irq(&hub_event_lock); /* Lock the device, then check to see if we were * disconnected while waiting for the lock to succeed. */ if (locktree(hdev) < 0) break; if (hdev->state != USB_STATE_CONFIGURED || !hdev->actconfig || hub != usb_get_intfdata( hdev->actconfig->interface[0])) goto loop; if (hub->error) { dev_dbg (hub_dev, "resetting for error %d\n", hub->error);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -