// 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 "util.h" // dprintf #include "config.h" // CONFIG_USB_HUB #include "usb-hub.h" // struct usb_hub_descriptor #include "usb.h" // struct usb_s static int get_hub_desc(struct usb_hub_descriptor *desc, u32 endp) { struct usb_ctrlrequest req; req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE; req.bRequest = USB_REQ_GET_DESCRIPTOR; req.wValue = USB_DT_HUB<<8; req.wIndex = 0; req.wLength = sizeof(*desc); return send_default_control(endp, &req, desc); } static int set_port_feature(int port, int feature, u32 endp) { 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; req.wLength = 0; return send_default_control(endp, &req, NULL); } static int clear_port_feature(int port, int feature, u32 endp) { 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; req.wLength = 0; return send_default_control(endp, &req, NULL); } static int get_port_status(int port, struct usb_port_status *sts, u32 endp) { 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; req.wLength = sizeof(*sts); return send_default_control(endp, &req, sts); } // Configure a usb hub and then find devices connected to it. int usb_hub_init(u32 endp) { if (!CONFIG_USB_HUB) return 0; struct usb_hub_descriptor desc; int ret = get_hub_desc(&desc, endp); if (ret) return ret; // Turn on power to all ports. int i; for (i=1; i<=desc.bNbrPorts; i++) { ret = set_port_feature(i, USB_PORT_FEAT_POWER, endp); if (ret) goto fail; } // Wait for port detection. msleep(desc.bPwrOn2PwrGood * 2 + USB_TIME_SIGATT); // XXX - should poll for ports becoming active sooner and then // possibly wait USB_TIME_ATTDB. // Detect down stream devices. struct usb_s *cntl = endp2cntl(endp); int totalcount = 0; for (i=1; i<=desc.bNbrPorts; i++) { struct usb_port_status sts; ret = get_port_status(i, &sts, endp); if (ret) goto fail; if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION)) // XXX - power down port? continue; // Reset port. ret = set_port_feature(i, USB_PORT_FEAT_RESET, endp); if (ret) goto fail; // Wait for reset to complete. u64 end = calc_future_tsc(USB_TIME_DRST * 2); for (;;) { ret = get_port_status(i, &sts, endp); if (ret) goto fail; if (!(sts.wPortStatus & USB_PORT_STAT_RESET)) break; if (check_time(end)) { // Timeout. warn_timeout(); goto fail; } yield(); } if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION)) // Device no longer present. XXX - power down port? continue; // XXX - should try to parallelize configuration. int count = configure_usb_device( cntl, !!(sts.wPortStatus & USB_PORT_STAT_LOW_SPEED)); if (! count) { // Shutdown port ret = clear_port_feature(i, USB_PORT_FEAT_ENABLE, endp); if (ret) goto fail; } totalcount += count; } return totalcount; fail: dprintf(1, "Failure on hub setup\n"); return 0; }