📄 hub.c
字号:
static int hub_hub_status(struct usb_hub *hub, u16 *status, u16 *change){ int ret; ret = get_hub_status(hub->hdev, &hub->status->hub); if (ret < 0) dev_err (hub->intfdev, "%s failed (err = %d)\n", __FUNCTION__, ret); else { *status = le16_to_cpu(hub->status->hub.wHubStatus); *change = le16_to_cpu(hub->status->hub.wHubChange); ret = 0; } return ret;}static int hub_port_disable(struct usb_hub *hub, int port1, int set_state){ struct usb_device *hdev = hub->hdev; int ret; if (hdev->children[port1-1] && set_state) { usb_set_device_state(hdev->children[port1-1], USB_STATE_NOTATTACHED); } ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); if (ret) dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n", port1, ret); return ret;}static int hub_configure(struct usb_hub *hub, struct usb_endpoint_descriptor *endpoint){ struct usb_device *hdev = hub->hdev; struct device *hub_dev = hub->intfdev; u16 hubstatus, hubchange; u16 wHubCharacteristics; unsigned int pipe; int maxp, ret; char *message; hub->buffer = usb_buffer_alloc(hdev, sizeof(*hub->buffer), GFP_KERNEL, &hub->buffer_dma); if (!hub->buffer) { message = "can't allocate hub irq buffer"; ret = -ENOMEM; goto fail; } hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL); if (!hub->status) { message = "can't kmalloc hub status buffer"; ret = -ENOMEM; goto fail; } hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL); if (!hub->descriptor) { message = "can't kmalloc hub descriptor"; ret = -ENOMEM; goto fail; } /* Request the entire hub descriptor. * hub->descriptor can handle USB_MAXCHILDREN ports, * but the hub can/will return fewer bytes here. */ ret = get_hub_descriptor(hdev, hub->descriptor, sizeof(*hub->descriptor)); if (ret < 0) { message = "can't read hub descriptor"; goto fail; } else if (hub->descriptor->bNbrPorts > USB_MAXCHILDREN) { message = "hub has too many ports!"; ret = -ENODEV; goto fail; } hdev->maxchild = hub->descriptor->bNbrPorts; dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild, (hdev->maxchild == 1) ? "" : "s"); wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); if (wHubCharacteristics & HUB_CHAR_COMPOUND) { int i; char portstr [USB_MAXCHILDREN + 1]; for (i = 0; i < hdev->maxchild; i++) portstr[i] = hub->descriptor->DeviceRemovable [((i + 1) / 8)] & (1 << ((i + 1) % 8)) ? 'F' : 'R'; portstr[hdev->maxchild] = 0; dev_dbg(hub_dev, "compound device; port removable status: %s\n", portstr); } else dev_dbg(hub_dev, "standalone hub\n"); switch (wHubCharacteristics & HUB_CHAR_LPSM) { case 0x00: dev_dbg(hub_dev, "ganged power switching\n"); break; case 0x01: dev_dbg(hub_dev, "individual port power switching\n"); break; case 0x02: case 0x03: dev_dbg(hub_dev, "no power switching (usb 1.0)\n"); break; } switch (wHubCharacteristics & HUB_CHAR_OCPM) { case 0x00: dev_dbg(hub_dev, "global over-current protection\n"); break; case 0x08: dev_dbg(hub_dev, "individual port over-current protection\n"); break; case 0x10: case 0x18: dev_dbg(hub_dev, "no over-current protection\n"); break; } spin_lock_init (&hub->tt.lock); INIT_LIST_HEAD (&hub->tt.clear_list); INIT_WORK (&hub->tt.kevent, hub_tt_kevent, hub); switch (hdev->descriptor.bDeviceProtocol) { case 0: break; case 1: dev_dbg(hub_dev, "Single TT\n"); hub->tt.hub = hdev; break; case 2: ret = usb_set_interface(hdev, 0, 1); if (ret == 0) { dev_dbg(hub_dev, "TT per port\n"); hub->tt.multi = 1; } else dev_err(hub_dev, "Using single TT (err %d)\n", ret); hub->tt.hub = hdev; break; default: dev_dbg(hub_dev, "Unrecognized hub protocol %d\n", hdev->descriptor.bDeviceProtocol); break; } /* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */ switch (wHubCharacteristics & HUB_CHAR_TTTT) { case HUB_TTTT_8_BITS: if (hdev->descriptor.bDeviceProtocol != 0) { hub->tt.think_time = 666; dev_dbg(hub_dev, "TT requires at most %d " "FS bit times (%d ns)\n", 8, hub->tt.think_time); } break; case HUB_TTTT_16_BITS: hub->tt.think_time = 666 * 2; dev_dbg(hub_dev, "TT requires at most %d " "FS bit times (%d ns)\n", 16, hub->tt.think_time); break; case HUB_TTTT_24_BITS: hub->tt.think_time = 666 * 3; dev_dbg(hub_dev, "TT requires at most %d " "FS bit times (%d ns)\n", 24, hub->tt.think_time); break; case HUB_TTTT_32_BITS: hub->tt.think_time = 666 * 4; dev_dbg(hub_dev, "TT requires at most %d " "FS bit times (%d ns)\n", 32, hub->tt.think_time); break; } /* probe() zeroes hub->indicator[] */ if (wHubCharacteristics & HUB_CHAR_PORTIND) { hub->has_indicators = 1; dev_dbg(hub_dev, "Port indicators are supported\n"); } dev_dbg(hub_dev, "power on to power good time: %dms\n", hub->descriptor->bPwrOn2PwrGood * 2); /* power budgeting mostly matters with bus-powered hubs, * and battery-powered root hubs (may provide just 8 mA). */ ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus); if (ret < 0) { message = "can't get hub status"; goto fail; } le16_to_cpus(&hubstatus); if (hdev == hdev->bus->root_hub) { struct usb_hcd *hcd = container_of(hdev->bus, struct usb_hcd, self); hub->power_budget = min(500u, hcd->power_budget) / 2; } else if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) { dev_dbg(hub_dev, "hub controller current requirement: %dmA\n", hub->descriptor->bHubContrCurrent); hub->power_budget = (501 - hub->descriptor->bHubContrCurrent) / 2; } if (hub->power_budget) dev_dbg(hub_dev, "%dmA bus power budget for children\n", hub->power_budget * 2); ret = hub_hub_status(hub, &hubstatus, &hubchange); if (ret < 0) { message = "can't get hub status"; goto fail; } /* local power status reports aren't always correct */ if (hdev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_SELFPOWER) dev_dbg(hub_dev, "local power source is %s\n", (hubstatus & HUB_STATUS_LOCAL_POWER) ? "lost (inactive)" : "good"); if ((wHubCharacteristics & HUB_CHAR_OCPM) == 0) dev_dbg(hub_dev, "%sover-current condition exists\n", (hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no "); /* set up the interrupt endpoint */ pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress); maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe)); if (maxp > sizeof(*hub->buffer)) maxp = sizeof(*hub->buffer); hub->urb = usb_alloc_urb(0, GFP_KERNEL); if (!hub->urb) { message = "couldn't allocate interrupt urb"; ret = -ENOMEM; goto fail; } usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq, hub, endpoint->bInterval); hub->urb->transfer_dma = hub->buffer_dma; hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* maybe cycle the hub leds */ if (hub->has_indicators && blinkenlights) hub->indicator [0] = INDICATOR_CYCLE; hub_power_on(hub); hub_activate(hub); return 0;fail: dev_err (hub_dev, "config failed, %s (err %d)\n", message, ret); /* hub_disconnect() frees urb and descriptor */ return ret;}static unsigned highspeed_hubs;/* Called after the hub driver is unbound from a hub with children */static void hub_remove_children_work(void *__hub){ struct usb_hub *hub = __hub; struct usb_device *hdev = hub->hdev; int i; kfree(hub); usb_lock_device(hdev); for (i = 0; i < hdev->maxchild; ++i) { if (hdev->children[i]) usb_disconnect(&hdev->children[i]); } usb_unlock_device(hdev); usb_put_dev(hdev);}static void hub_disconnect(struct usb_interface *intf){ struct usb_hub *hub = usb_get_intfdata (intf); struct usb_device *hdev; int n, port1; usb_set_intfdata (intf, NULL); hdev = hub->hdev; if (hdev->speed == USB_SPEED_HIGH) highspeed_hubs--; hub_quiesce(hub); usb_free_urb(hub->urb); hub->urb = NULL; spin_lock_irq(&hub_event_lock); list_del_init(&hub->event_list); spin_unlock_irq(&hub_event_lock); kfree(hub->descriptor); hub->descriptor = NULL; kfree(hub->status); hub->status = NULL; if (hub->buffer) { usb_buffer_free(hdev, sizeof(*hub->buffer), hub->buffer, hub->buffer_dma); hub->buffer = NULL; } /* If there are any children then this is an unbind only, not a * physical disconnection. The active ports must be disabled * and later on we must call usb_disconnect(). We can't call * it now because we may not hold the hub's device lock. */ n = 0; for (port1 = 1; port1 <= hdev->maxchild; ++port1) { if (hdev->children[port1 - 1]) { ++n; hub_port_disable(hub, port1, 1); } } if (n == 0) kfree(hub); else { /* Reuse the hub->leds work_struct for our own purposes */ INIT_WORK(&hub->leds, hub_remove_children_work, hub); schedule_work(&hub->leds); usb_get_dev(hdev); }}static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id){ struct usb_host_interface *desc; struct usb_endpoint_descriptor *endpoint; struct usb_device *hdev; struct usb_hub *hub; desc = intf->cur_altsetting; hdev = interface_to_usbdev(intf); /* Some hubs have a subclass of 1, which AFAICT according to the */ /* specs is not defined, but it works */ if ((desc->desc.bInterfaceSubClass != 0) && (desc->desc.bInterfaceSubClass != 1)) {descriptor_error: dev_err (&intf->dev, "bad descriptor, ignoring hub\n"); return -EIO; } /* Multiple endpoints? What kind of mutant ninja-hub is this? */ if (desc->desc.bNumEndpoints != 1) goto descriptor_error; endpoint = &desc->endpoint[0].desc; /* Output endpoint? Curiouser and curiouser.. */ if (!(endpoint->bEndpointAddress & USB_DIR_IN)) goto descriptor_error; /* If it's not an interrupt endpoint, we'd better punt! */ if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) goto descriptor_error; /* We found a hub */ dev_info (&intf->dev, "USB hub found\n"); hub = kzalloc(sizeof(*hub), GFP_KERNEL); if (!hub) { dev_dbg (&intf->dev, "couldn't kmalloc hub struct\n"); return -ENOMEM; } INIT_LIST_HEAD(&hub->event_list); hub->intfdev = &intf->dev; hub->hdev = hdev; INIT_WORK(&hub->leds, led_work, hub); usb_set_intfdata (intf, hub); if (hdev->speed == USB_SPEED_HIGH) highspeed_hubs++; if (hub_configure(hub, endpoint) >= 0) return 0; hub_disconnect (intf); return -ENODEV;}static inthub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data){ struct usb_device *hdev = interface_to_usbdev (intf); /* assert ifno == 0 (part of hub spec) */ switch (code) { case USBDEVFS_HUB_PORTINFO: { struct usbdevfs_hub_portinfo *info = user_data; int i; spin_lock_irq(&device_state_lock); if (hdev->devnum <= 0) info->nports = 0; else { info->nports = hdev->maxchild; for (i = 0; i < info->nports; i++) { if (hdev->children[i] == NULL) info->port[i] = 0; else info->port[i] = hdev->children[i]->devnum; } } spin_unlock_irq(&device_state_lock); return info->nports + 1; } default: return -ENOSYS; }}/* caller has locked the hub device */static void hub_pre_reset(struct usb_hub *hub){ struct usb_device *hdev = hub->hdev; int i; for (i = 0; i < hdev->maxchild; ++i) { if (hdev->children[i]) usb_disconnect(&hdev->children[i]); } hub_quiesce(hub);}/* caller has locked the hub device */static void hub_post_reset(struct usb_hub *hub){ hub_activate(hub); hub_power_on(hub);}/* grab device/port lock, returning index of that port (zero based). * protects the upstream link used by this device from concurrent * tree operations like suspend, resume, reset, and disconnect, which * apply to everything downstream of a given port. */static int locktree(struct usb_device *udev){ int t; struct usb_device *hdev; if (!udev) return -ENODEV; /* root hub is always the first lock in the series */ hdev = udev->parent; if (!hdev) { usb_lock_device(udev); return 0; } /* on the path from root to us, lock everything from * top down, dropping parent locks when not needed */ t = locktree(hdev);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -