// Code for handling standard USB hubs. // // Copyright (C) 2010 Kevin O'Connor // // This file may be distributed under the terms of the GNU LGPLv3 license. #include "config.h" // CONFIG_USB_HUB #include "output.h" // dprintf #include "string.h" // memset #include "usb.h" // struct usb_s #include "usb-hub.h" // struct usb_hub_descriptor #include "util.h" // timer_calc static int get_hub_desc(struct usb_pipe *pipe, struct usb_hub_descriptor *desc) { struct usb_ctrlrequest req; req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE; req.bRequest = USB_REQ_GET_DESCRIPTOR; if (pipe->speed == USB_SUPERSPEED) req.wValue = USB_DT_HUB3<<8; else req.wValue = USB_DT_HUB<<8; req.wIndex = 0; req.wLength = sizeof(*desc); return usb_send_default_control(pipe, &req, desc); } static int set_hub_depth(struct usb_pipe *pipe, u16 depth) { struct usb_ctrlrequest req; req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE; req.bRequest = HUB_REQ_SET_HUB_DEPTH; req.wValue = depth; req.wIndex = 0; req.wLength = 0; return usb_send_default_control(pipe, &req, NULL); } static int set_port_feature(struct usbhub_s *hub, int port, int feature) { struct usb_ctrlrequest req; req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER; req.bRequest = USB_REQ_SET_FEATURE; req.wValue = feature; req.wIndex = port + 1; req.wLength = 0; mutex_lock(&hub->lock); int ret = usb_send_default_control(hub->usbdev->defpipe, &req, NULL); mutex_unlock(&hub->lock); return ret; } static int clear_port_feature(struct usbhub_s *hub, int port, int feature) { struct usb_ctrlrequest req; req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER; req.bRequest = USB_REQ_CLEAR_FEATURE; req.wValue = feature; req.wIndex = port + 1; req.wLength = 0; mutex_lock(&hub->lock); int ret = usb_send_default_control(hub->usbdev->defpipe, &req, NULL); mutex_unlock(&hub->lock); return ret; } static int get_port_status(struct usbhub_s *hub, int port, struct usb_port_status *sts) { struct usb_ctrlrequest req; req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_OTHER; req.bRequest = USB_REQ_GET_STATUS; req.wValue = 0; req.wIndex = port + 1; req.wLength = sizeof(*sts); mutex_lock(&hub->lock); int ret = usb_send_default_control(hub->usbdev->defpipe, &req, sts); mutex_unlock(&hub->lock); return ret; } // Check if device attached to port static int usb_hub_detect(struct usbhub_s *hub, u32 port) { struct usb_port_status sts; int ret = get_port_status(hub, port, &sts); if (ret) { dprintf(1, "Failure on hub port %d detect\n", port); return -1; } return (sts.wPortStatus & USB_PORT_STAT_CONNECTION) ? 1 : 0; } // Disable port static void usb_hub_disconnect(struct usbhub_s *hub, u32 port) { int ret = clear_port_feature(hub, port, USB_PORT_FEAT_ENABLE); if (ret) dprintf(1, "Failure on hub port %d disconnect\n", port); } // Reset device on port static int usb_hub_reset(struct usbhub_s *hub, u32 port) { int ret = set_port_feature(hub, port, USB_PORT_FEAT_RESET); if (ret) goto fail; // Wait for reset to complete. struct usb_port_status sts; u32 end = timer_calc(USB_TIME_DRST * 2); for (;;) { ret = get_port_status(hub, port, &sts); if (ret) goto fail; if (!(sts.wPortStatus & USB_PORT_STAT_RESET) && (hub->usbdev->speed != USB_SUPERSPEED || !(sts.wPortStatus & USB_PORT_STAT_LINK_MASK))) break; if (timer_check(end)) { warn_timeout(); goto fail; } msleep(5); } // Reset complete. if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION)) // Device no longer present return -1; if (hub->usbdev->speed == USB_SUPERSPEED) return USB_SUPERSPEED; return ((sts.wPortStatus & USB_PORT_STAT_SPEED_MASK) >> USB_PORT_STAT_SPEED_SHIFT); fail: dprintf(1, "Failure on hub port %d reset\n", port); usb_hub_disconnect(hub, port); return -1; } static struct usbhub_op_s HubOp = { .detect = usb_hub_detect, .reset = usb_hub_reset, .disconnect = usb_hub_disconnect, }; // Configure a usb hub and then find devices connected to it. int usb_hub_setup(struct usbdevice_s *usbdev) { ASSERT32FLAT(); if (!CONFIG_USB_HUB) return -1; struct usb_hub_descriptor desc; int ret = get_hub_desc(usbdev->defpipe, &desc); if (ret) return ret; struct usbhub_s hub; memset(&hub, 0, sizeof(hub)); hub.usbdev = usbdev; hub.cntl = usbdev->defpipe->cntl; hub.portcount = desc.bNbrPorts; hub.op = &HubOp; if (usbdev->speed == USB_SUPERSPEED) { int depth = 0; struct usbdevice_s *parent = usbdev->hub->usbdev; while (parent) { depth++; parent = parent->hub->usbdev; } ret = set_hub_depth(usbdev->defpipe, depth); if (ret) return ret; } // Turn on power to ports. int port; for (port=0; port