static int usbhost_hubpwr(FAR struct usbhost_hubpriv_s *priv, FAR struct usbhost_hubport_s *hport, bool on) { FAR struct usb_ctrlreq_s *ctrlreq; uint16_t req; int port; int ret; /* Are we enabling or disabling power? */ if (on) { req = USBHUB_REQ_SETFEATURE; } else { req = USBHUB_REQ_CLEARFEATURE; } /* Enable/disable power to all downstream ports */ ctrlreq = priv->ctrlreq; DEBUGASSERT(ctrlreq); for (port = 1; port <= priv->nports; port++) { ctrlreq->type = USBHUB_REQ_TYPE_PORT; ctrlreq->req = req; usbhost_putle16(ctrlreq->value, USBHUB_PORT_FEAT_POWER); usbhost_putle16(ctrlreq->index, port); usbhost_putle16(ctrlreq->len, 0); ret = DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, NULL); if (ret < 0) { udbg("ERROR: Failed to power %s port %d: %d\n", on ? "UP" : "DOWN", port, ret); return ret; } } return OK; }
int usbhost_enumerate(FAR struct usbhost_hubport_s *hport, FAR struct usbhost_class_s **devclass) { FAR struct usb_ctrlreq_s *ctrlreq = NULL; struct usbhost_id_s id; size_t maxlen; unsigned int cfglen; uint8_t maxpacketsize; uint8_t descsize; uint8_t funcaddr = 0; FAR uint8_t *buffer = NULL; int ret; DEBUGASSERT(hport != NULL && hport->drvr != NULL); /* Allocate descriptor buffers for use in this function. We will need two: * One for the request and one for the data buffer. */ ret = DRVR_ALLOC(hport->drvr, (FAR uint8_t **)&ctrlreq, &maxlen); if (ret < 0) { udbg("DRVR_ALLOC failed: %d\n", ret); return ret; } ret = DRVR_ALLOC(hport->drvr, &buffer, &maxlen); if (ret < 0) { udbg("DRVR_ALLOC failed: %d\n", ret); goto errout; } /* Pick an appropriate packet size for this device * * USB 2.0, Paragraph 5.5.3 "Control Transfer Packet Size Constraints" * * "An endpoint for control transfers specifies the maximum data * payload size that the endpoint can accept from or transmit to * the bus. The allowable maximum control transfer data payload * sizes for full-speed devices is 8, 16, 32, or 64 bytes; for * high-speed devices, it is 64 bytes and for low-speed devices, * it is 8 bytes. This maximum applies to the data payloads of the * Data packets following a Setup..." */ if (hport->speed == USB_SPEED_HIGH) { /* For high-speed, we must use 64 bytes */ maxpacketsize = 64; descsize = USB_SIZEOF_DEVDESC; } else { /* Eight will work for both low- and full-speed */ maxpacketsize = 8; descsize = 8; } /* Configure EP0 with the initial maximum packet size */ DRVR_EP0CONFIGURE(hport->drvr, hport->ep0, 0, hport->speed, maxpacketsize); /* Read first bytes of the device descriptor */ ctrlreq->type = USB_REQ_DIR_IN | USB_REQ_RECIPIENT_DEVICE; ctrlreq->req = USB_REQ_GETDESCRIPTOR; usbhost_putle16(ctrlreq->value, (USB_DESC_TYPE_DEVICE << 8)); usbhost_putle16(ctrlreq->index, 0); usbhost_putle16(ctrlreq->len, descsize); ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq, buffer); if (ret < 0) { udbg("ERROR: Failed to get device descriptor, length=%d: %d\n", descsize, ret); goto errout; } /* Extract the correct max packetsize from the device descriptor */ maxpacketsize = ((struct usb_devdesc_s *)buffer)->mxpacketsize; uvdbg("maxpacksetsize: %d\n", maxpacketsize); /* And reconfigure EP0 with the correct maximum packet size */ DRVR_EP0CONFIGURE(hport->drvr, hport->ep0, 0, hport->speed, maxpacketsize); /* Now read the full device descriptor (if we have not already done so) */ if (descsize < USB_SIZEOF_DEVDESC) { ctrlreq->type = USB_REQ_DIR_IN | USB_REQ_RECIPIENT_DEVICE; ctrlreq->req = USB_REQ_GETDESCRIPTOR; usbhost_putle16(ctrlreq->value, (USB_DESC_TYPE_DEVICE << 8)); usbhost_putle16(ctrlreq->index, 0); usbhost_putle16(ctrlreq->len, USB_SIZEOF_DEVDESC); ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq, buffer); if (ret < 0) { udbg("ERROR: Failed to get device descriptor, length=%d: %d\n", USB_SIZEOF_DEVDESC, ret); goto errout; } } /* Get class identification information from the device descriptor. Most * devices set this to USB_CLASS_PER_INTERFACE (zero) and provide the * identification information in the interface descriptor(s). That allows * a device to support multiple, different classes. */ (void)usbhost_devdesc((struct usb_devdesc_s *)buffer, &id); /* Assign a function address to the device connected to this port */ funcaddr = usbhost_devaddr_create(hport); if (funcaddr < 0) { udbg("ERROR: usbhost_devaddr_create failed: %d\n", ret); goto errout; } /* Set the USB device address */ ctrlreq->type = USB_REQ_DIR_OUT | USB_REQ_RECIPIENT_DEVICE; ctrlreq->req = USB_REQ_SETADDRESS; usbhost_putle16(ctrlreq->value, (uint16_t)funcaddr); usbhost_putle16(ctrlreq->index, 0); usbhost_putle16(ctrlreq->len, 0); ret = DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, NULL); if (ret < 0) { udbg("ERROR: Failed to set address: %d\n"); goto errout; } usleep(2*1000); /* Assign the function address to the port */ DEBUGASSERT(hport->funcaddr == 0 && funcaddr != 0); hport->funcaddr = funcaddr; /* And reconfigure EP0 with the correct address */ DRVR_EP0CONFIGURE(hport->drvr, hport->ep0, hport->funcaddr, hport->speed, maxpacketsize); /* Get the configuration descriptor (only), index == 0. Should not be * hard-coded! More logic is needed in order to handle devices with * multiple configurations. */ ctrlreq->type = USB_REQ_DIR_IN | USB_REQ_RECIPIENT_DEVICE; ctrlreq->req = USB_REQ_GETDESCRIPTOR; usbhost_putle16(ctrlreq->value, (USB_DESC_TYPE_CONFIG << 8)); usbhost_putle16(ctrlreq->index, 0); usbhost_putle16(ctrlreq->len, USB_SIZEOF_CFGDESC); ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq, buffer); if (ret < 0) { udbg("ERROR: Failed to get configuration descriptor, length=%d: %d\n", USB_SIZEOF_CFGDESC, ret); goto errout; } /* Extract the full size of the configuration data */ cfglen = (unsigned int)usbhost_getle16(((struct usb_cfgdesc_s *)buffer)->totallen); uvdbg("sizeof config data: %d\n", cfglen); /* Get all of the configuration descriptor data, index == 0 (Should not be * hard-coded!) */ ctrlreq->type = USB_REQ_DIR_IN | USB_REQ_RECIPIENT_DEVICE; ctrlreq->req = USB_REQ_GETDESCRIPTOR; usbhost_putle16(ctrlreq->value, (USB_DESC_TYPE_CONFIG << 8)); usbhost_putle16(ctrlreq->index, 0); usbhost_putle16(ctrlreq->len, cfglen); ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq, buffer); if (ret < 0) { udbg("ERROR: Failed to get configuration descriptor, length=%d: %d\n", cfglen, ret); goto errout; } /* Select device configuration 1 (Should not be hard-coded!) */ ctrlreq->type = USB_REQ_DIR_OUT | USB_REQ_RECIPIENT_DEVICE; ctrlreq->req = USB_REQ_SETCONFIGURATION; usbhost_putle16(ctrlreq->value, 1); usbhost_putle16(ctrlreq->index, 0); usbhost_putle16(ctrlreq->len, 0); ret = DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, NULL); if (ret < 0) { udbg("ERROR: Failed to set configuration: %d\n", ret); goto errout; } /* Was the class identification information provided in the device * descriptor? Or do we need to find it in the interface descriptor(s)? */ if (id.base == USB_CLASS_PER_INTERFACE) { /* Get the class identification information for this device from the * interface descriptor(s). Hmmm.. More logic is need to handle the * case of multiple interface descriptors. */ ret = usbhost_configdesc(buffer, cfglen, &id); if (ret < 0) { udbg("ERROR: usbhost_configdesc failed: %d\n", ret); goto errout; } } /* Some devices may require some delay before initialization */ usleep(100*1000); /* Parse the configuration descriptor and bind to the class instance for the * device. This needs to be the last thing done because the class driver * will begin configuring the device. */ ret = usbhost_classbind(hport, buffer, cfglen, &id, devclass); if (ret < 0) { udbg("ERROR: usbhost_classbind failed %d\n", ret); } errout: if (ret < 0) { /* Release the device function address on any failure */ usbhost_devaddr_destroy(hport, funcaddr); hport->funcaddr = 0; } /* Release temporary buffers in any event */ if (buffer != NULL) { DRVR_FREE(hport->drvr, buffer); } if (ctrlreq) { DRVR_FREE(hport->drvr, (FAR uint8_t *)ctrlreq); } return ret; }
static void usbhost_hub_event(FAR void *arg) { FAR struct usbhost_class_s *hubclass; FAR struct usbhost_hubport_s *hport; FAR struct usbhost_hubport_s *connport; FAR struct usbhost_hubpriv_s *priv; FAR struct usb_ctrlreq_s *ctrlreq; struct usb_portstatus_s portstatus; irqstate_t flags; uint16_t status; uint16_t change; uint16_t mask; uint16_t feat; uint8_t statuschange; int port; int ret; DEBUGASSERT(arg != NULL); hubclass = (FAR struct usbhost_class_s *)arg; priv = &((FAR struct usbhost_hubclass_s *)hubclass)->hubpriv; /* Has the hub been disconnected? */ if (priv->disconnected) { uvdbg("Disconnected\n"); return; } /* No.. then set up to process the hub event */ DEBUGASSERT(priv->ctrlreq); ctrlreq = priv->ctrlreq; DEBUGASSERT(hubclass->hport); hport = hubclass->hport; statuschange = priv->buffer[0]; uvdbg("StatusChange: %02x\n", statuschange); /* Check for status change on any port */ for (port = 1; port <= priv->nports; port++) { /* Check if port status has changed */ if ((statuschange & (1 << port)) == 0) { continue; } uvdbg("Port %d status change\n", port); /* Port status changed, check what happened */ statuschange &= ~(1 << port); /* Read hub port status */ ctrlreq->type = USB_REQ_DIR_IN | USBHUB_REQ_TYPE_PORT; ctrlreq->req = USBHUB_REQ_GETSTATUS; usbhost_putle16(ctrlreq->value, 0); usbhost_putle16(ctrlreq->index, port); usbhost_putle16(ctrlreq->len, USB_SIZEOF_PORTSTS); ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq, (FAR uint8_t *)&portstatus); if (ret < 0) { udbg("ERROR: Failed to read port %d status: %d\n", port, ret); continue; } status = usbhost_getle16(portstatus.status); change = usbhost_getle16(portstatus.change); /* First, clear all change bits */ mask = 1; feat = USBHUB_PORT_FEAT_CCONNECTION; while (change) { if (change & mask) { ctrlreq->type = USBHUB_REQ_TYPE_PORT; ctrlreq->req = USBHUB_REQ_CLEARFEATURE; usbhost_putle16(ctrlreq->value, feat); usbhost_putle16(ctrlreq->index, port); usbhost_putle16(ctrlreq->len, 0); ret = DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, NULL); if (ret < 0) { udbg("ERROR: Failed to clear port %d change mask %04x: %d\n", port, mask, ret); } change &= (~mask); } mask <<= 1; feat++; } change = usbhost_getle16(portstatus.change); /* Handle connect or disconnect, no power management */ if ((change & USBHUB_PORT_STAT_CCONNECTION) != 0) { uint16_t debouncetime = 0; uint16_t debouncestable = 0; uint16_t connection = 0xffff; uvdbg("Port %d status %04x change %04x\n", port, status, change); /* Debounce */ while (debouncetime < 1500) { ctrlreq->type = USB_REQ_DIR_IN | USBHUB_REQ_TYPE_PORT; ctrlreq->req = USBHUB_REQ_GETSTATUS; usbhost_putle16(ctrlreq->value, 0); usbhost_putle16(ctrlreq->index, port); usbhost_putle16(ctrlreq->len, USB_SIZEOF_PORTSTS); ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq, (FAR uint8_t *)&portstatus); if (ret < 0) { udbg("ERROR: Failed to get port %d status: %d\n", port, ret); break; } status = usbhost_getle16(portstatus.status); change = usbhost_getle16(portstatus.change); if ((change & USBHUB_PORT_STAT_CCONNECTION) == 0 && (status & USBHUB_PORT_STAT_CONNECTION) == connection) { debouncestable += 25; if (debouncestable >= 100) { uvdbg("Port %d debouncestable=%d\n", port, debouncestable); break; } } else { debouncestable = 0; connection = status & USBHUB_PORT_STAT_CONNECTION; } if ((change & USBHUB_PORT_STAT_CCONNECTION) != 0) { ctrlreq->type = USBHUB_REQ_TYPE_PORT; ctrlreq->req = USBHUB_REQ_CLEARFEATURE; usbhost_putle16(ctrlreq->value, USBHUB_PORT_FEAT_CCONNECTION); usbhost_putle16(ctrlreq->index, port); usbhost_putle16(ctrlreq->len, 0); (void)DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, NULL); } debouncetime += 25; usleep(25*1000); } if (ret < 0 || debouncetime >= 1500) { udbg("ERROR: Failed to debounce port %d: %d\n", port, ret); continue; } if ((status & USBHUB_PORT_STAT_CONNECTION) != 0) { /* Device connected to a port on the hub */ uvdbg("Connection on port %d\n", port); ctrlreq->type = USBHUB_REQ_TYPE_PORT; ctrlreq->req = USBHUB_REQ_SETFEATURE; usbhost_putle16(ctrlreq->value, USBHUB_PORT_FEAT_RESET); usbhost_putle16(ctrlreq->index, port); usbhost_putle16(ctrlreq->len, 0); ret = DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, NULL); if (ret < 0) { udbg("ERROR: Failed to reset port %d: %d\n", port, ret); continue; } usleep(100*1000); ctrlreq->type = USB_REQ_DIR_IN | USBHUB_REQ_TYPE_PORT; ctrlreq->req = USBHUB_REQ_GETSTATUS; usbhost_putle16(ctrlreq->value, 0); usbhost_putle16(ctrlreq->index, port); usbhost_putle16(ctrlreq->len, USB_SIZEOF_PORTSTS); ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq, (FAR uint8_t *)&portstatus); if (ret < 0) { udbg("ERROR: Failed to get port %d status: %d\n", port, ret); continue; } status = usbhost_getle16(portstatus.status); change = usbhost_getle16(portstatus.change); uvdbg("port %d status %04x change %04x after reset\n", port, status, change); if ((status & USBHUB_PORT_STAT_RESET) == 0 && (status & USBHUB_PORT_STAT_ENABLE) != 0) { if ((change & USBHUB_PORT_STAT_CRESET) != 0) { ctrlreq->type = USBHUB_REQ_TYPE_PORT; ctrlreq->req = USBHUB_REQ_CLEARFEATURE; usbhost_putle16(ctrlreq->value, USBHUB_PORT_FEAT_CRESET); usbhost_putle16(ctrlreq->index, port); usbhost_putle16(ctrlreq->len, 0); (void)DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, NULL); } connport = &priv->hport[port]; if ((status & USBHUB_PORT_STAT_HIGH_SPEED) != 0) { connport->speed = USB_SPEED_HIGH; } else if ((status & USBHUB_PORT_STAT_LOW_SPEED) != 0) { connport->speed = USB_SPEED_LOW; } else { connport->speed = USB_SPEED_FULL; } /* Activate the hub port by assigning it a control endpoint. */ ret = usbhost_hport_activate(connport); if (ret < 0) { udbg("ERROR: usbhost_hport_activate failed: %d\n", ret); } else { /* Inform waiters that a new device has been connected */ ret = DRVR_CONNECT(connport->drvr, connport, true); if (ret < 0) { udbg("ERROR: DRVR_CONNECT failed: %d\n", ret); usbhost_hport_deactivate(connport); } } } else { udbg("ERROR: Failed to enable port %d\n", port); continue; } } else { /* Device disconnected from a port on the hub. Release port * resources. */ uvdbg("Disconnection on port %d\n", port); /* Free any devices classes connect on this hub port */ connport = &priv->hport[port]; if (connport->devclass != NULL) { CLASS_DISCONNECTED(connport->devclass); connport->devclass = NULL; } /* Free any resources used by the hub port */ usbhost_hport_deactivate(connport); } } else if (change) { udbg("WARNING: status %04x change %04x not handled\n", status, change); } } /* Check for hub status change */ if ((statuschange & 1) != 0) { /* Hub status changed */ udbg("WARNING: Hub status changed, not handled\n"); } /* The preceding sequence of events may take a significant amount of * time and it is possible that the hub may have been removed while this * logic operated. In any event, we will get here after several failures. * But we do not want to schedule another hub event if the hub has been * removed. */ flags = irqsave(); if (!priv->disconnected) { /* Wait for the next hub event */ ret = DRVR_ASYNCH(hport->drvr, priv->intin, (FAR uint8_t *)priv->buffer, INTIN_BUFSIZE, usbhost_callback, hubclass); if (ret < 0) { udbg("ERROR: Failed to queue interrupt endpoint: %d\n", ret); } } irqrestore(flags); }