static inline int usbhost_classbind(FAR struct usbhost_hubport_s *hport, const uint8_t *configdesc, int desclen, struct usbhost_id_s *id, FAR struct usbhost_class_s **usbclass) { FAR struct usbhost_class_s *devclass; FAR const struct usbhost_registry_s *reg; int ret = -EINVAL; /* Is there is a class implementation registered to support this device. */ reg = usbhost_findclass(id); uvdbg("usbhost_findclass: %p\n", reg); if (reg != NULL) { /* Yes.. there is a class for this device. Get an instance of * its interface. */ ret = -ENOMEM; devclass = CLASS_CREATE(reg, hport, id); uvdbg("CLASS_CREATE: %p\n", devclass); if (devclass != NULL) { /* Then bind the newly instantiated class instance */ ret = CLASS_CONNECT(devclass, configdesc, desclen); if (ret < 0) { /* On failures, call the class disconnect method which * should then free the allocated devclass instance. */ udbg("CLASS_CONNECT failed: %d\n", ret); CLASS_DISCONNECTED(devclass); } else { *usbclass = devclass; } } } uvdbg("Returning: %d\n", ret); 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); }
static void usbhost_disconnect_event(FAR void *arg) { FAR struct usbhost_class_s *hubclass = (FAR struct usbhost_class_s *)arg; FAR struct usbhost_hubpriv_s *priv; FAR struct usbhost_hubport_s *hport; FAR struct usbhost_hubport_s *child; irqstate_t flags; int port; uvdbg("Disconnecting\n"); DEBUGASSERT(hubclass != NULL && hubclass->hport != NULL); priv = &((FAR struct usbhost_hubclass_s *)hubclass)->hubpriv; hport = hubclass->hport; uvdbg("Destroying hub on port %d\n", hport->port); /* Set an indication to any users of the device that the device is no * longer available. */ flags = irqsave(); /* Cancel any pending transfers on the interrupt IN pipe */ DRVR_CANCEL(hport->drvr, priv->intin); /* Cancel any pending port status change events */ work_cancel(LPWORK, &priv->work); /* Disable power to all downstream ports */ (void)usbhost_hubpwr(priv, hport, false); /* Free the allocated control request */ DRVR_FREE(hport->drvr, (FAR uint8_t *)priv->ctrlreq); /* Free buffer for status change (INT) endpoint */ DRVR_IOFREE(hport->drvr, priv->buffer); /* Destroy the interrupt IN endpoint */ DRVR_EPFREE(hport->drvr, priv->intin); /* Release per-port resources */ for (port = 0; port < USBHUB_MAX_PORTS; port++) { /* Free any devices classes connect on this hub port */ child = &priv->hport[port]; if (child->devclass != NULL) { CLASS_DISCONNECTED(child->devclass); child->devclass = NULL; } /* Free any resources used by the hub port */ usbhost_hport_deactivate(child); } /* Deactivate the parent hub port (unless it is the root hub port) */ usbhost_hport_deactivate(hport); /* Destroy the semaphores */ sem_destroy(&priv->exclsem); /* Disconnect the USB host device */ DRVR_DISCONNECT(hport->drvr, hport); /* Free the class instance */ kmm_free(hubclass); hport->devclass = NULL; irqrestore(flags); }