static int usbhost_waiter(int argc, char *argv[]) { struct usbhost_hubport_s *hport; uvdbg("Running\n"); for (;;) { /* Wait for the device to change state */ DEBUGVERIFY(CONN_WAIT(g_usbconn, &hport)); uvdbg("%s\n", hport->connected ? "connected" : "disconnected"); /* Did we just become connected? */ if (hport->connected) { /* Yes.. enumerate the newly connected device */ (void)CONN_ENUMERATE(g_usbconn, hport); } } /* Keep the compiler from complaining */ return 0; }
int stm32_usbhost_initialize(void) { int pid; int ret; /* First, register all of the class drivers needed to support the drivers * that we care about: */ uvdbg("Register class drivers\n"); ret = usbhost_storageinit(); if (ret != OK) { udbg("Failed to register the mass storage class\n"); } /* Then get an instance of the USB host interface */ uvdbg("Initialize USB host\n"); g_drvr = usbhost_initialize(0); if (g_drvr) { /* Start a thread to handle device connection. */ uvdbg("Start usbhost_waiter\n"); pid = TASK_CREATE("usbhost", CONFIG_USBHOST_DEFPRIO, CONFIG_USBHOST_STACKSIZE, (main_t)usbhost_waiter, (FAR char * const *)NULL); return pid < 0 ? -ENOEXEC : OK; } return -ENODEV; }
static int usbhost_waiter(int argc, char *argv[]) { bool connected = false; int ret; uvdbg("Running\n"); for (;;) { /* Wait for the device to change state */ ret = DRVR_WAIT(g_drvr, connected); DEBUGASSERT(ret == OK); connected = !connected; uvdbg("%s\n", connected ? "connected" : "disconnected"); /* Did we just become connected? */ if (connected) { /* Yes.. enumerate the newly connected device */ (void)DRVR_ENUMERATE(g_drvr); } } /* Keep the compiler from complaining */ return 0; }
int stm32_usbhost_initialize(void) { int pid; int ret; /* First, register all of the class drivers needed to support the drivers * that we care about: */ uvdbg("Register class drivers\n"); #ifdef CONFIG_USBHOST_HUB /* Initialize USB hub class support */ ret = usbhost_hub_initialize(); if (ret < 0) { udbg("ERROR: usbhost_hub_initialize failed: %d\n", ret); } #endif #ifdef CONFIG_USBHOST_MSC /* Register the USB mass storage class class */ ret = usbhost_msc_initialize(); if (ret != OK) { udbg("ERROR: Failed to register the mass storage class: %d\n", ret); } #endif #ifdef CONFIG_USBHOST_CDCACM /* Register the CDC/ACM serial class */ ret = usbhost_cdcacm_initialize(); if (ret != OK) { udbg("ERROR: Failed to register the CDC/ACM serial class: %d\n", ret); } #endif /* Then get an instance of the USB host interface */ uvdbg("Initialize USB host\n"); g_usbconn = stm32_otghshost_initialize(0); if (g_usbconn) { /* Start a thread to handle device connection. */ uvdbg("Start usbhost_waiter\n"); pid = task_create("usbhost", CONFIG_STM32F429IDISCO_USBHOST_PRIO, CONFIG_STM32F429IDISCO_USBHOST_STACKSIZE, (main_t)usbhost_waiter, (FAR char * const *)NULL); return pid < 0 ? -ENOEXEC : OK; } return -ENODEV; }
static inline int usbhost_configdesc(const uint8_t *configdesc, int cfglen, struct usbhost_id_s *id) { FAR struct usb_cfgdesc_s *cfgdesc; FAR struct usb_ifdesc_s *ifdesc; int remaining; DEBUGASSERT(configdesc != NULL && cfglen >= USB_SIZEOF_CFGDESC); /* Verify that we were passed a configuration descriptor */ cfgdesc = (struct usb_cfgdesc_s *)configdesc; uvdbg("cfg len:%d total len:%d\n", cfgdesc->len, cfglen); if (cfgdesc->type != USB_DESC_TYPE_CONFIG) { return -EINVAL; } /* Skip to the next entry descriptor */ configdesc += cfgdesc->len; remaining = cfglen - cfgdesc->len; /* Loop while there are more descriptors to examine */ memset(id, 0, sizeof(FAR struct usb_desc_s)); while (remaining >= sizeof(struct usb_desc_s)) { /* What is the next descriptor? Is it an interface descriptor? */ ifdesc = (struct usb_ifdesc_s *)configdesc; if (ifdesc->type == USB_DESC_TYPE_INTERFACE) { /* Yes, extract the class information from the interface descriptor. * Typically these values are zero meaning that the "real" ID * information resides in the device descriptor. */ DEBUGASSERT(remaining >= sizeof(struct usb_ifdesc_s)); id->base = ifdesc->classid; id->subclass = ifdesc->subclass; id->proto = ifdesc->protocol; uvdbg("class:%d subclass:%d protocol:%d\n", id->base, id->subclass, id->proto); return OK; } /* Increment the address of the next descriptor */ configdesc += ifdesc->len; remaining -= ifdesc->len; } return -ENOENT; }
static int usbhost_disconnected(struct usbhost_class_s *hubclass) { FAR struct usbhost_hubpriv_s *priv; irqstate_t flags; int ret; uvdbg("Disconnected\n"); /* Execute the disconnect action from the worker thread. */ DEBUGASSERT(hubclass != NULL); priv = &((FAR struct usbhost_hubclass_s *)hubclass)->hubpriv; /* Mark the driver disconnected. This will cause the callback to ignore * any subsequent completions of asynchronous transfers. */ flags = irqsave(); priv->disconnected = true; /* Cancel any pending work. There may be pending HUB work associated with * hub interrupt pipe events. That work may be lost by this action. */ (void)work_cancel(LPWORK, &priv->work); /* Schedule the disconnection work */ ret = work_queue(LPWORK, &priv->work, (worker_t)usbhost_disconnect_event, hubclass, 0); irqrestore(flags); return ret; }
static void usbhost_hport_deactivate(FAR struct usbhost_hubport_s *hport) { uvdbg("Deactivating: %s port %d\n", ROOTHUB(hport) ? "Root" : "Hub", hport->port); /* Don't free the control pipe of root hub ports! */ if (!ROOTHUB(hport) && hport->ep0 != NULL) { /* Free the control endpoint */ DRVR_EPFREE(hport->drvr, hport->ep0); hport->ep0 = NULL; } /* Free the function address if one has been assigned */ usbhost_devaddr_destroy(hport, hport->funcaddr); hport->funcaddr = 0; /* If this is a downstream hub port, then there should be no class driver * associated wit it. */ DEBUGASSERT(ROOTHUB(hport) || hport->devclass == NULL); }
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; }
const struct usbhost_registry_s *usbhost_findclass(const struct usbhost_id_s *id) { struct usbhost_registry_s *usbclass; irqstate_t flags; int ndx; DEBUGASSERT(id); uvdbg("Looking for class:%d subclass:%d protocol:%d vid:%04x pid:%04x\n", id->base, id->subclass, id->proto, id->vid, id->pid); /* g_classregistry is a singly-linked list of class ID information added by * calls to usbhost_registerclass(). Since this list is accessed from USB * host controller interrupt handling logic, accesses to this list must be * protected by disabling interrupts. */ flags = irqsave(); /* Examine each register class in the linked list */ for (usbclass = g_classregistry; usbclass; usbclass = usbclass->flink) { /* If the registered class supports more than one ID, subclass, or * protocol, then try each. */ uvdbg("Checking class:%p nids:%d\n", usbclass, usbclass->nids); for (ndx = 0; ndx < usbclass->nids; ndx++) { /* Did we find a matching ID? */ if (usbhost_idmatch(&usbclass->id[ndx], id)) { /* Yes.. restore interrupts and return the class info */ irqrestore(flags); return usbclass; } } } /* Not found... restore interrupts and return NULL */ irqrestore(flags); return NULL; }
void sam_usbhost_vbusdrive(int rhport, bool enable) { pio_pinset_t pinset = 0; uvdbg("RHPort%d: enable=%d\n", rhport+1, enable); /* Pick the PIO configuration associated with the selected root hub port */ switch (rhport) { case SAM_RHPORT1: #ifndef CONFIG_SAMA5_UHPHS_RHPORT1 udbg("ERROR: RHPort1 is not available in this configuration\n"); return; #else pinset = PIO_USBA_VBUS_ENABLE; break; #endif case SAM_RHPORT2: #ifndef CONFIG_SAMA5_UHPHS_RHPORT2 udbg("ERROR: RHPort2 is not available in this configuration\n"); return; #else pinset = PIO_USBB_VBUS_ENABLE; break; #endif case SAM_RHPORT3: #ifndef CONFIG_SAMA5_UHPHS_RHPORT3 udbg("ERROR: RHPort3 is not available in this configuration\n"); return; #else pinset = PIO_USBC_VBUS_ENABLE; break; #endif default: udbg("ERROR: RHPort%d is not supported\n", rhport+1); return; } /* Then enable or disable VBUS power */ if (enable) { /* Enable the Power Switch by driving the enable pin low */ sam_piowrite(pinset, false); } else { /* Disable the Power Switch by driving the enable pin high */ sam_piowrite(pinset, true); } }
static inline FAR struct usbhost_state_s *usbhost_allocclass(void) { FAR struct usbhost_state_s *priv; DEBUGASSERT(!up_interrupt_context()); priv = (FAR struct usbhost_state_s *)kmalloc(sizeof(struct usbhost_state_s)); uvdbg("Allocated: %p\n", priv);; return priv; }
static inline void usbhost_freeclass(FAR struct usbhost_state_s *usbclass) { DEBUGASSERT(usbclass != NULL); /* Free the class instance (perhaps calling sched_kmm_free() in case we are * executing from an interrupt handler. */ uvdbg("Freeing: %p\n", usbclass); kmm_free(usbclass); }
static int usbhost_waiter(struct usbhost_connection_s *dev) #endif { bool connected[SAM_OHCI_NRHPORT] = {false, false, false}; int rhpndx; int ret; uvdbg("%s Waiter Running\n", hcistr); for (;;) { /* Wait for the device to change state */ rhpndx = CONN_WAIT(dev, connected); DEBUGASSERT(rhpndx >= 0 && rhpndx < SAM_OHCI_NRHPORT); connected[rhpndx] = !connected[rhpndx]; uvdbg("%s RHport%d %s\n", hcistr, rhpndx + 1, connected[rhpndx] ? "connected" : "disconnected"); /* Did we just become connected? */ if (connected[rhpndx]) { /* Yes.. enumerate the newly connected device */ ret = CONN_ENUMERATE(dev, rhpndx); if (ret < 0) { uvdbg("%s RHport%d CONN_ENUMERATE failed: %d\n", hcistr, rhpndx+1, ret); connected[rhpndx] = false; } } } /* Keep the compiler from complaining */ return 0; }
static int usbhost_disconnected(struct usbhost_class_s *usbclass) { FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)usbclass; irqstate_t flags; DEBUGASSERT(priv != NULL); /* Set an indication to any users of the device that the device is no * longer available. */ flags = irqsave(); priv->disconnected = true; /* Now check the number of references on the class instance. If it is one, * then we can free the class instance now. Otherwise, we will have to * wait until the holders of the references free them by closing the * block driver. */ ullvdbg("crefs: %d\n", priv->crefs); if (priv->crefs == 1) { /* Destroy the class instance. If we are executing from an interrupt * handler, then defer the destruction to the worker thread. * Otherwise, destroy the instance now. */ if (up_interrupt_context()) { /* Destroy the instance on the worker thread. */ uvdbg("Queuing destruction: worker %p->%p\n", priv->work.worker, usbhost_destroy); DEBUGASSERT(priv->work.worker == NULL); (void)work_queue(HPWORK, &priv->work, usbhost_destroy, priv, 0); } else { /* Do the work now */ usbhost_destroy(priv); } } irqrestore(flags); return OK; }
static void usbhost_destroy(FAR void *arg) { FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)arg; FAR struct usbhost_hubport_s *hport; FAR struct usbhost_driver_s *drvr; DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL); hport = priv->usbclass.hport; DEBUGASSERT(hport->drvr); drvr = hport->drvr; uvdbg("crefs: %d\n", priv->crefs); /* Unregister the driver */ /* Release the device name used by this connection */ usbhost_freedevno(priv); /* Free the endpoints */ /* Free any transfer buffers */ /* Free the function address assigned to this device */ usbhost_devaddr_destroy(hport, hport->funcaddr); hport->funcaddr = 0; /* Destroy the semaphores */ /* Disconnect the USB host device */ DRVR_DISCONNECT(drvr, hport); /* And free the class instance. Hmmm.. this may execute on the worker * thread and the work structure is part of what is getting freed. That * should be okay because once the work contained is removed from the * queue, it should not longer be accessed by the worker thread. */ usbhost_freeclass(priv); }
static bool usbhost_idmatch(const struct usbhost_id_s *classid, const struct usbhost_id_s *devid) { uvdbg("Compare to class:%d subclass:%d protocol:%d vid:%04x pid:%04x\n", classid->base, classid->subclass, classid->proto, classid->vid, classid->pid); /* The base class ID, subclass and protocol have to match up in any event */ if (devid->base == classid->base && devid->subclass == classid->subclass && devid->proto == classid->proto) { /* If this is a vendor-specific class ID, then the VID and PID have to * match as well. */ if (devid->base == USB_CLASS_VENDOR_SPEC) { /* Vendor specific... do the VID and PID also match? */ if (devid->vid == classid->vid && devid->pid == classid->pid) { /* Yes.. then we have a match */ return true; } } else { /* Not vendor specific? Then we have a match */ return true; } } /* No match.. not supported */ return false; }
static int usbhost_hport_activate(FAR struct usbhost_hubport_s *hport) { struct usbhost_epdesc_s epdesc; int ret; uvdbg("Activating port %d\n", hport->port); epdesc.hport = hport; epdesc.addr = 0; epdesc.in = false; epdesc.xfrtype = USB_EP_ATTR_XFER_CONTROL; epdesc.interval = 0; epdesc.mxpacketsize = (hport->speed == USB_SPEED_HIGH) ? 64 : 8; ret = DRVR_EPALLOC(hport->drvr, &epdesc, &hport->ep0); if (ret < 0) { udbg("ERROR: Failed to allocate ep0: %d\n", ret); } return ret; }
static inline int usbhost_devdesc(FAR const struct usb_devdesc_s *devdesc, FAR struct usbhost_id_s *id) { /* Clear the ID info */ memset(id, 0, sizeof(struct usbhost_id_s)); /* Pick off the class ID info */ id->base = devdesc->classid; id->subclass = devdesc->subclass; id->proto = devdesc->protocol; /* Pick off the VID and PID as well (for vendor specfic devices) */ id->vid = usbhost_getle16(devdesc->vendor); id->pid = usbhost_getle16(devdesc->product); uvdbg("class:%d subclass:%04x protocol:%04x vid:%d pid:%d\n", id->base, id->subclass, id->proto, id->vid, id->pid); 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 int composite_setup(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev, FAR const struct usb_ctrlreq_s *ctrl, FAR uint8_t *dataout, size_t outlen) { FAR struct composite_dev_s *priv; FAR struct usbdev_req_s *ctrlreq; uint16_t value; #ifdef CONFIG_DEBUG_USB uint16_t index; #endif uint16_t len; bool dispatched = false; int ret = -EOPNOTSUPP; #ifdef CONFIG_DEBUG if (!driver || !dev || !dev->ep0 || !ctrl) { usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_SETUPINVALIDARGS), 0); return -EIO; } #endif /* Extract a reference to private data */ usbtrace(TRACE_CLASSSETUP, ctrl->req); priv = ((FAR struct composite_driver_s*)driver)->dev; #ifdef CONFIG_DEBUG if (!priv) { usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_EP0NOTBOUND2), 0); return -ENODEV; } #endif ctrlreq = priv->ctrlreq; /* Extract the little-endian 16-bit values to host order */ value = GETUINT16(ctrl->value); #ifdef CONFIG_DEBUG_USB index = GETUINT16(ctrl->index); #endif len = GETUINT16(ctrl->len); uvdbg("type=%02x req=%02x value=%04x index=%04x len=%04x\n", ctrl->type, ctrl->req, value, index, len); if ((ctrl->type & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_STANDARD) { /********************************************************************** * Standard Requests **********************************************************************/ switch (ctrl->req) { case USB_REQ_GETDESCRIPTOR: { /* The value field specifies the descriptor type in the MS byte and the * descriptor index in the LS byte (order is little endian) */ switch (ctrl->value[1]) { case USB_DESC_TYPE_DEVICE: { ret = USB_SIZEOF_DEVDESC; memcpy(ctrlreq->buf, composite_getdevdesc(), ret); } break; #ifdef CONFIG_USBDEV_DUALSPEED case USB_DESC_TYPE_DEVICEQUALIFIER: { ret = USB_SIZEOF_QUALDESC; memcpy(ctrlreq->buf, composite_getqualdesc(), ret); } break; case USB_DESC_TYPE_OTHERSPEEDCONFIG: #endif case USB_DESC_TYPE_CONFIG: { #ifdef CONFIG_USBDEV_DUALSPEED ret = composite_mkcfgdesc(ctrlreq->buf, dev->speed, ctrl->value[1]); #else ret = composite_mkcfgdesc(ctrlreq->buf); #endif } break; case USB_DESC_TYPE_STRING: { /* value == string index. Zero is the language ID. */ uint8_t strid = ctrl->value[0]; FAR struct usb_strdesc_s *buf = (FAR struct usb_strdesc_s *)ctrlreq->buf; if (strid <= COMPOSITE_NSTRIDS) { ret = composite_mkstrdesc(strid, buf); } #if DEV1_NSTRIDS > 0 else if (strid <= DEV1_STRIDBASE + DEV1_NSTRIDS) { ret = DEV1_MKSTRDESC(strid, buf); } #endif #if DEV2_NSTRIDS > 0 else if (strid <= DEV2_STRIDBASE + DEV2_NSTRIDS) { ret = DEV2_MKSTRDESC(strid, buf); } #endif } break; default: { usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_GETUNKNOWNDESC), value); } break; } } break; case USB_REQ_SETCONFIGURATION: { if (ctrl->type == 0) { /* Save the configuration and inform the constituent classes */ ret = CLASS_SETUP(priv->dev1, dev, ctrl, dataout, outlen); dispatched = true; if (ret >= 0) { ret = CLASS_SETUP(priv->dev2, dev, ctrl, dataout, outlen); if (ret >= 0) { priv->config = value; } } } } break; case USB_REQ_GETCONFIGURATION: { if (ctrl->type == USB_DIR_IN) { ctrlreq->buf[0] = priv->config; ret = 1; } } break; case USB_REQ_SETINTERFACE: { if (ctrl->type == USB_REQ_RECIPIENT_INTERFACE && priv->config == COMPOSITE_CONFIGID) { ret = composite_classsetup(priv, dev, ctrl, dataout, outlen); dispatched = true; } } break; case USB_REQ_GETINTERFACE: { if (ctrl->type == (USB_DIR_IN|USB_REQ_RECIPIENT_INTERFACE) && priv->config == COMPOSITE_CONFIGIDNONE) { ret = composite_classsetup(priv, dev, ctrl, dataout, outlen); dispatched = true; } } break; default: usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_UNSUPPORTEDSTDREQ), ctrl->req); break; } } else { uint8_t recipient; /********************************************************************** * Non-Standard Class Requests **********************************************************************/ /* Class implementations should handle there own interface and endpoint * requests. */ recipient = ctrl->type & USB_REQ_RECIPIENT_MASK; if (recipient == USB_REQ_RECIPIENT_INTERFACE || recipient == USB_REQ_RECIPIENT_ENDPOINT) { ret = composite_classsetup(priv, dev, ctrl, dataout, outlen); dispatched = true; } } /* Respond to the setup command if (1) data was returned, and (2) the request was * NOT successfully dispatched to the component class driver. On an error return * value (ret < 0), the USB driver will stall EP0. */ if (ret >= 0 && !dispatched) { /* Setup the request */ ctrlreq->len = MIN(len, ret); ctrlreq->flags = USBDEV_REQFLAGS_NULLPKT; /* And submit the request to the USB controller driver */ ret = EP_SUBMIT(dev->ep0, ctrlreq); if (ret < 0) { usbtrace(TRACE_CLSERROR(USBCOMPOSITE_TRACEERR_EPRESPQ), (uint16_t)-ret); ctrlreq->result = OK; composite_ep0incomplete(dev->ep0, ctrlreq); } } return ret; }
static int usbclass_setup(struct usbdevclass_driver_s *driver, struct usbdev_s *dev, const struct usb_ctrlreq_s *ctrl, uint8_t * dataout, size_t outlen) { struct apbridge_dev_s *priv; struct usbdev_req_s *ctrlreq; uint16_t value; uint16_t index; uint16_t len; int ret = -EOPNOTSUPP; #ifdef CONFIG_DEBUG if (!driver || !dev || !dev->ep0 || !ctrl) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0); return -EIO; } #endif /* Extract reference to private data */ usbtrace(TRACE_CLASSSETUP, ctrl->req); priv = ((struct apbridge_driver_s *)driver)->dev; #ifdef CONFIG_DEBUG if (!priv || !priv->ctrlreq) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EP0NOTBOUND), 0); return -ENODEV; } #endif ctrlreq = priv->ctrlreq; ctrlreq->priv = (void *)USB_REQ; /* Extract the little-endian 16-bit values to host order */ value = GETUINT16(ctrl->value); index = GETUINT16(ctrl->index); len = GETUINT16(ctrl->len); uvdbg("type=%02x req=%02x value=%04x index=%04x len=%04x\n", ctrl->type, ctrl->req, value, index, len); switch (ctrl->type & USB_REQ_TYPE_MASK) { /*********************************************************************** * Standard Requests ***********************************************************************/ case USB_REQ_TYPE_STANDARD: { switch (ctrl->req) { case USB_REQ_GETDESCRIPTOR: { /* The value field specifies the descriptor type in the MS byte and the * descriptor index in the LS byte (order is little endian) */ switch (ctrl->value[1]) { case USB_DESC_TYPE_DEVICE: { ret = USB_SIZEOF_DEVDESC; memcpy(ctrlreq->buf, &g_devdesc, ret); } break; case USB_DESC_TYPE_DEVICEQUALIFIER: { ret = USB_SIZEOF_QUALDESC; memcpy(ctrlreq->buf, &g_qualdesc, ret); } break; case USB_DESC_TYPE_OTHERSPEEDCONFIG: case USB_DESC_TYPE_CONFIG: { ret = usbclass_mkcfgdesc(ctrlreq->buf, dev->speed, ctrl->req); } break; case USB_DESC_TYPE_STRING: { /* index == language code. */ ret = usbclass_mkstrdesc(ctrl->value[0], (struct usb_strdesc_s *) ctrlreq->buf); } break; default: { usbtrace(TRACE_CLSERROR (USBSER_TRACEERR_GETUNKNOWNDESC), value); } break; } } break; case USB_REQ_SETCONFIGURATION: { if (ctrl->type == 0) { ret = usbclass_setconfig(priv, value); } } break; case USB_REQ_GETCONFIGURATION: { if (ctrl->type == USB_DIR_IN) { *(uint8_t *) ctrlreq->buf = priv->config; ret = 1; } } break; case USB_REQ_SETINTERFACE: { if (ctrl->type == USB_REQ_RECIPIENT_INTERFACE) { if (priv->config == APBRIDGE_CONFIGID && index == APBRIDGE_INTERFACEID && value == APBRIDGE_ALTINTERFACEID) { usbclass_resetconfig(priv); usbclass_setconfig(priv, priv->config); ret = 0; } } } break; case USB_REQ_GETINTERFACE: { if (ctrl->type == (USB_DIR_IN | USB_REQ_RECIPIENT_INTERFACE) && priv->config == APBRIDGE_CONFIGIDNONE) { if (index != APBRIDGE_INTERFACEID) { ret = -EDOM; } else { *(uint8_t *) ctrlreq->buf = APBRIDGE_ALTINTERFACEID; ret = 1; } } } break; default: usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDSTDREQ), ctrl->req); break; } } break; /* Put here vendor request */ case USB_REQ_TYPE_VENDOR: { if ((ctrl->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_INTERFACE) { if (ctrl->req == APBRIDGE_RWREQUEST_SVC) { if ((ctrl->type & USB_DIR_IN) != 0) { *(uint32_t *) ctrlreq->buf = 0xdeadbeef; ret = 4; } else { ctrlreq->priv = (void *)GREYBUS_SVC_REQ; ret = len; } } else if (ctrl->req == APBRIDGE_RWREQUEST_LOG) { if ((ctrl->type & USB_DIR_IN) == 0) { } else { #if defined(CONFIG_APB_USB_LOG) ctrlreq->priv = (void *)GREYBUS_LOG; ret = usb_get_log(ctrlreq->buf, len); #else ret = 0; #endif } } else { usbtrace(TRACE_CLSERROR (USBSER_TRACEERR_UNSUPPORTEDCLASSREQ), ctrl->type); } } } break; default: usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDTYPE), ctrl->type); break; } /* Respond to the setup command if data was returned. On an error return * value (ret < 0), the USB driver will stall. */ if (ret >= 0) { ctrlreq->len = min(len, ret); ctrlreq->flags = USBDEV_REQFLAGS_NULLPKT; ret = EP_SUBMIT(dev->ep0, ctrlreq); if (ret < 0) { usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPRESPQ), (uint16_t) - ret); ctrlreq->result = OK; usbclass_ep0incomplete(dev->ep0, ctrlreq); } } return ret; }
static int usbmsc_setup(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev, FAR const struct usb_ctrlreq_s *ctrl, FAR uint8_t *dataout, size_t outlen) { FAR struct usbmsc_dev_s *priv; FAR struct usbdev_req_s *ctrlreq; uint16_t value; uint16_t index; uint16_t len; int ret = -EOPNOTSUPP; #ifdef CONFIG_DEBUG if (!driver || !dev || !dev->ep0 || !ctrl) { usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_SETUPINVALIDARGS), 0); return -EIO; } #endif /* Extract reference to private data */ usbtrace(TRACE_CLASSSETUP, ctrl->req); priv = ((FAR struct usbmsc_driver_s *)driver)->dev; #ifdef CONFIG_DEBUG if (!priv || !priv->ctrlreq) { usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_EP0NOTBOUND2), 0); return -ENODEV; } #endif ctrlreq = priv->ctrlreq; /* Extract the little-endian 16-bit values to host order */ value = GETUINT16(ctrl->value); index = GETUINT16(ctrl->index); len = GETUINT16(ctrl->len); uvdbg("type=%02x req=%02x value=%04x index=%04x len=%04x\n", ctrl->type, ctrl->req, value, index, len); if ((ctrl->type & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_STANDARD) { /********************************************************************** * Standard Requests **********************************************************************/ switch (ctrl->req) { case USB_REQ_GETDESCRIPTOR: { /* The value field specifies the descriptor type in the MS byte and the * descriptor index in the LS byte (order is little endian) */ switch (ctrl->value[1]) { /* If the mass storage device is used in as part of a composite * device, then the device descriptor is is provided by logic * in the composite device implementation. */ #ifndef CONFIG_USBMSC_COMPOSITE case USB_DESC_TYPE_DEVICE: { ret = USB_SIZEOF_DEVDESC; memcpy(ctrlreq->buf, usbmsc_getdevdesc(), ret); } break; #endif /* If the mass storage device is used in as part of a composite device, * then the device qualifier descriptor is provided by logic in the * composite device implementation. */ #if !defined(CONFIG_USBMSC_COMPOSITE) && defined(CONFIG_USBDEV_DUALSPEED) case USB_DESC_TYPE_DEVICEQUALIFIER: { ret = USB_SIZEOF_QUALDESC; memcpy(ctrlreq->buf, usbmsc_getqualdesc(), ret); } break; case USB_DESC_TYPE_OTHERSPEEDCONFIG: #endif /* If the mass storage device is used in as part of a composite device, * then the configuration descriptor is provided by logic in the * composite device implementation. */ #ifndef CONFIG_USBMSC_COMPOSITE case USB_DESC_TYPE_CONFIG: { #ifdef CONFIG_USBDEV_DUALSPEED ret = usbmsc_mkcfgdesc(ctrlreq->buf, dev->speed, ctrl->value[1]); #else ret = usbmsc_mkcfgdesc(ctrlreq->buf); #endif } break; #endif /* If the mass storage device is used in as part of a composite device, * then the language string descriptor is provided by logic in the * composite device implementation. */ #ifndef CONFIG_USBMSC_COMPOSITE case USB_DESC_TYPE_STRING: { /* index == language code. */ ret = usbmsc_mkstrdesc(ctrl->value[0], (struct usb_strdesc_s *)ctrlreq->buf); } break; #endif default: { usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_GETUNKNOWNDESC), value); } break; } } break; case USB_REQ_SETCONFIGURATION: { if (ctrl->type == 0) { /* Signal the worker thread to instantiate the new configuration */ priv->theventset |= USBMSC_EVENT_CFGCHANGE; priv->thvalue = value; usbmsc_scsi_signal(priv); /* Return here... the response will be provided later by the * worker thread. */ return OK; } } break; /* If the mass storage device is used in as part of a composite device, * then the overall composite class configuration is managed by logic * in the composite device implementation. */ #ifndef CONFIG_USBMSC_COMPOSITE case USB_REQ_GETCONFIGURATION: { if (ctrl->type == USB_DIR_IN) { ctrlreq->buf[0] = priv->config; ret = 1; } } break; #endif case USB_REQ_SETINTERFACE: { if (ctrl->type == USB_REQ_RECIPIENT_INTERFACE) { if (priv->config == USBMSC_CONFIGID && index == USBMSC_INTERFACEID && value == USBMSC_ALTINTERFACEID) { /* Signal to instantiate the interface change */ priv->theventset |= USBMSC_EVENT_IFCHANGE; usbmsc_scsi_signal(priv); /* Return here... the response will be provided later by the * worker thread. */ return OK; } } } break; case USB_REQ_GETINTERFACE: { if (ctrl->type == (USB_DIR_IN | USB_REQ_RECIPIENT_INTERFACE) && priv->config == USBMSC_CONFIGIDNONE) { if (index != USBMSC_INTERFACEID) { ret = -EDOM; } else { ctrlreq->buf[0] = USBMSC_ALTINTERFACEID; ret = 1; } } } break; default: usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_UNSUPPORTEDSTDREQ), ctrl->req); break; } } else if ((ctrl->type & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_CLASS) { /********************************************************************** * Bulk-Only Mass Storage Class Requests **********************************************************************/ /* Verify that we are configured */ if (!priv->config) { usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_NOTCONFIGURED), 0); return ret; } switch (ctrl->req) { case USBMSC_REQ_MSRESET: { /* Reset mass storage device and interface */ if (ctrl->type == USBMSC_TYPE_SETUPOUT && value == 0 && len == 0) { /* Only one interface is supported */ if (index != USBMSC_INTERFACEID) { usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_MSRESETNDX), index); ret = -EDOM; } else { /* Signal to stop the current operation and reinitialize state */ priv->theventset |= USBMSC_EVENT_RESET; usbmsc_scsi_signal(priv); /* Return here... the response will be provided later by the * worker thread. */ return OK; } } } break; case USBMSC_REQ_GETMAXLUN: { /* Return number LUNs supported */ if (ctrl->type == USBMSC_TYPE_SETUPIN && value == 0 && len == 1) { /* Only one interface is supported */ if (index != USBMSC_INTERFACEID) { usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_GETMAXLUNNDX), index); ret = -EDOM; } else { ctrlreq->buf[0] = priv->nluns - 1; ret = 1; } } } break; default: usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_BADREQUEST), ctrl->req); break; } } else { usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_UNSUPPORTEDTYPE), ctrl->type); } /* Respond to the setup command if data was returned. On an error return * value (ret < 0), the USB driver will stall EP0. */ if (ret >= 0) { /* Configure the response */ ctrlreq->len = MIN(len, ret); ctrlreq->flags = USBDEV_REQFLAGS_NULLPKT; /* Send the response -- either directly to the USB controller or * indirectly in the case where this class is a member of a composite * device. */ #ifndef CONFIG_USBMSC_COMPOSITE ret = EP_SUBMIT(dev->ep0, ctrlreq); #else ret = composite_ep0submit(driver, dev, ctrlreq); #endif if (ret < 0) { usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_EPRESPQ), (uint16_t)-ret); #if 0 /* Not necessary */ ctrlreq->result = OK; usbmsc_ep0incomplete(dev->ep0, ctrlreq); #endif } } return ret; }
static #endif int usbmsc_exportluns(FAR void *handle) { FAR struct usbmsc_alloc_s *alloc = (FAR struct usbmsc_alloc_s *)handle; FAR struct usbmsc_dev_s *priv; #ifndef CONFIG_USBMSC_COMPOSITE FAR struct usbmsc_driver_s *drvr; #endif irqstate_t flags; int ret = OK; #ifdef CONFIG_DEBUG if (!alloc) { usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_EXPORTLUNSINVALIDARGS), 0); return -ENXIO; } #endif priv = &alloc->dev; #ifndef CONFIG_USBMSC_COMPOSITE drvr = &alloc->drvr; #endif /* Start the worker thread * * REVISIT: g_usbmsc_handoff is a global and, hence, really requires * some protection against re-entrant usage. */ usbmsc_scsi_lock(priv); priv->thstate = USBMSC_STATE_NOTSTARTED; priv->theventset = USBMSC_EVENT_NOEVENTS; g_usbmsc_handoff = priv; uvdbg("Starting SCSI worker thread\n"); priv->thpid = kernel_thread("scsid", CONFIG_USBMSC_SCSI_PRIO, CONFIG_USBMSC_SCSI_STACKSIZE, usbmsc_scsi_main, NULL); if (priv->thpid <= 0) { usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_THREADCREATE), (uint16_t)errno); goto errout_with_lock; } /* Wait for the worker thread to run and initialize */ uvdbg("Waiting for the SCSI worker thread\n"); usbmsc_sync_wait(priv); DEBUGASSERT(g_usbmsc_handoff == NULL); /* Register the USB storage class driver (unless we are part of a composite device) */ #ifndef CONFIG_USBMSC_COMPOSITE ret = usbdev_register(&drvr->drvr); if (ret != OK) { usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_DEVREGISTER), (uint16_t)-ret); goto errout_with_lock; } #endif /* Signal to start the thread */ uvdbg("Signalling for the SCSI worker thread\n"); flags = irqsave(); priv->theventset |= USBMSC_EVENT_READY; usbmsc_scsi_signal(priv); irqrestore(flags); errout_with_lock: usbmsc_scsi_unlock(priv); 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 inline int usbhost_hubdesc(FAR struct usbhost_class_s *hubclass) { FAR struct usbhost_hubpriv_s *priv; FAR struct usbhost_hubport_s *hport; FAR struct usb_ctrlreq_s *ctrlreq; struct usb_hubdesc_s hubdesc; uint16_t hubchar; int ret; uvdbg("Read hub descriptor\n"); DEBUGASSERT(hubclass != NULL); priv = &((FAR struct usbhost_hubclass_s *)hubclass)->hubpriv; DEBUGASSERT(hubclass->hport); hport = hubclass->hport; /* Get the hub descriptor */ ctrlreq = priv->ctrlreq; DEBUGASSERT(ctrlreq); ctrlreq->type = USB_REQ_DIR_IN | USBHUB_REQ_TYPE_HUB; ctrlreq->req = USBHUB_REQ_GETDESCRIPTOR; usbhost_putle16(ctrlreq->value, (USB_DESC_TYPE_HUB << 8)); usbhost_putle16(ctrlreq->index, 0); usbhost_putle16(ctrlreq->len, USB_SIZEOF_HUBDESC); ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq, (FAR uint8_t *)&hubdesc); if (ret < 0) { udbg("ERROR: Failed to read hub descriptor: %d\n", ret); return ret; } priv->nports = hubdesc.nports; hubchar = usbhost_getle16(hubdesc.characteristics); priv->lpsm = (hubchar & USBHUB_CHAR_LPSM_MASK) >> USBHUB_CHAR_LPSM_SHIFT; priv->compounddev = (hubchar & USBHUB_CHAR_COMPOUND) ? true : false; priv->ocmode = (hubchar & USBHUB_CHAR_OCPM_MASK) >> USBHUB_CHAR_OCPM_SHIFT; priv->indicator = (hubchar & USBHUB_CHAR_PORTIND) ? true : false; priv->pwrondelay = (2 * hubdesc.pwrondelay); priv->ctrlcurrent = hubdesc.ctrlcurrent; uvdbg("Hub Descriptor:\n"); uvdbg(" bDescLength: %d\n", hubdesc.len); uvdbg(" bDescriptorType: 0x%02x\n", hubdesc.type); uvdbg(" bNbrPorts: %d\n", hubdesc.nports); uvdbg(" wHubCharacteristics: 0x%04x\n", usbhost_getle16(hubdesc.characteristics)); uvdbg(" lpsm: %d\n", priv->lpsm); uvdbg(" compounddev: %s\n", priv->compounddev ? "TRUE" : "FALSE"); uvdbg(" ocmode: %d\n", priv->ocmode); uvdbg(" indicator: %s\n", priv->indicator ? "TRUE" : "FALSE"); uvdbg(" bPwrOn2PwrGood: %d\n", hubdesc.pwrondelay); uvdbg(" pwrondelay: %d\n", priv->pwrondelay); uvdbg(" bHubContrCurrent: %d\n", hubdesc.ctrlcurrent); uvdbg(" DeviceRemovable: %d\n", hubdesc.devattached); uvdbg(" PortPwrCtrlMask: %d\n", hubdesc.pwrctrlmask); return OK; }
static inline int usbhost_cfgdesc(FAR struct usbhost_class_s *hubclass, FAR const uint8_t *configdesc, int desclen) { FAR struct usbhost_hubpriv_s *priv; FAR struct usbhost_hubport_s *hport; FAR struct usb_cfgdesc_s *cfgdesc; FAR struct usb_desc_s *desc; FAR struct usbhost_epdesc_s intindesc; int remaining; uint8_t found = 0; int ret; DEBUGASSERT(hubclass != NULL); priv = &((FAR struct usbhost_hubclass_s *)hubclass)->hubpriv; DEBUGASSERT(hubclass->hport); hport = hubclass->hport; DEBUGASSERT(configdesc != NULL && desclen >= sizeof(struct usb_cfgdesc_s)); /* Initialize the interrupt IN endpoint information (only to prevent * compiler complaints) */ intindesc.hport = hport; intindesc.addr = 0; intindesc.in = true; intindesc.xfrtype = USB_EP_ATTR_XFER_INT; intindesc.interval = 0; intindesc.mxpacketsize = 0; /* Verify that we were passed a configuration descriptor */ cfgdesc = (FAR struct usb_cfgdesc_s *)configdesc; if (cfgdesc->type != USB_DESC_TYPE_CONFIG) { return -EINVAL; } /* Get the total length of the configuration descriptor (little endian). * It might be a good check to get the number of interfaces here too. */ remaining = (int)usbhost_getle16(cfgdesc->totallen); /* Skip to the next entry descriptor */ configdesc += cfgdesc->len; remaining -= cfgdesc->len; /* Loop where there are more descriptors to examine */ while (remaining >= sizeof(struct usb_desc_s)) { /* What is the next descriptor? */ desc = (FAR struct usb_desc_s *)configdesc; switch (desc->type) { /* Interface descriptor. We really should get the number of endpoints * from this descriptor too. */ case USB_DESC_TYPE_INTERFACE: { FAR struct usb_ifdesc_s *ifdesc = (FAR struct usb_ifdesc_s *)configdesc; uvdbg("Interface descriptor\n"); DEBUGASSERT(remaining >= USB_SIZEOF_IFDESC); /* Save the interface number and mark ONLY the interface found */ priv->ifno = ifdesc->ifno; found = USBHOST_IFFOUND; } break; /* Endpoint descriptor. Here, we expect one interrupt IN endpoints. */ case USB_DESC_TYPE_ENDPOINT: { FAR struct usb_epdesc_s *epdesc = (FAR struct usb_epdesc_s *)configdesc; uvdbg("Endpoint descriptor\n"); DEBUGASSERT(remaining >= USB_SIZEOF_EPDESC); /* Check for an interrupt endpoint. */ if ((epdesc->attr & USB_EP_ATTR_XFERTYPE_MASK) == USB_EP_ATTR_XFER_INT) { /* Yes.. it is a interrupt endpoint. IN or OUT? */ if (USB_ISEPOUT(epdesc->addr)) { /* It is an OUT interrupt endpoint. Ignore */ uvdbg("Interrupt OUT EP addr:%d mxpacketsize:%d\n", (epdesc->addr & USB_EP_ADDR_NUMBER_MASK), usbhost_getle16(epdesc->mxpacketsize)); } else { /* It is an IN interrupt endpoint. */ found |= USBHOST_EPINFOUND; /* Save the interrupt IN endpoint information */ intindesc.addr = epdesc->addr & USB_EP_ADDR_NUMBER_MASK; intindesc.interval = epdesc->interval; intindesc.mxpacketsize = usbhost_getle16(epdesc->mxpacketsize); uvdbg("Interrupt IN EP: addr=%d interval=%d mxpacketsize=%d\n", intindesc.addr, intindesc.interval, intindesc.mxpacketsize); } } } break; /* Other descriptors are just ignored for now */ default: break; } /* If we found everything we need with this interface, then break out * of the loop early. */ if (found == USBHOST_ALLFOUND) { break; } /* Increment the address of the next descriptor */ configdesc += desc->len; remaining -= desc->len; } /* Sanity checking... did we find all of things that we need? */ if (found != USBHOST_ALLFOUND) { ulldbg("ERROR: Found IF=%s EPIN=%s\n", (found & USBHOST_IFFOUND) != 0 ? "YES" : "NO", (found & USBHOST_EPINFOUND) != 0 ? "YES" : "NO"); return -EINVAL; } /* We are good... Allocate the interrupt IN endpoint */ ret = DRVR_EPALLOC(hport->drvr, &intindesc, &priv->intin); if (ret < 0) { udbg("ERROR: Failed to allocate Interrupt IN endpoint: %d\n", ret); (void)DRVR_EPFREE(hport->drvr, priv->intin); return ret; } ullvdbg("Endpoint allocated\n"); return OK; }
static inline int usbhost_devinit(FAR struct usbhost_state_s *priv) { int ret = OK; /* Set aside a transfer buffer for exclusive use by the class driver */ /* Increment the reference count. This will prevent usbhost_destroy() from * being called asynchronously if the device is removed. */ priv->crefs++; DEBUGASSERT(priv->crefs == 2); /* Configure the device */ /* Register the driver */ if (ret >= 0) { char devname[DEV_NAMELEN]; uvdbg("Register block driver\n"); usbhost_mkdevname(priv, devname); // ret = register_blockdriver(devname, &g_bops, 0, priv); } /* Check if we successfully initialized. We now have to be concerned * about asynchronous modification of crefs because the block * driver has been registerd. */ if (ret >= 0) { usbhost_takesem(&priv->exclsem); DEBUGASSERT(priv->crefs >= 2); /* Handle a corner case where (1) open() has been called so the * reference count is > 2, but the device has been disconnected. * In this case, the class instance needs to persist until close() * is called. */ if (priv->crefs <= 2 && priv->disconnected) { /* We don't have to give the semaphore because it will be * destroyed when usb_destroy is called. */ ret = -ENODEV; } else { /* Ready for normal operation as a block device driver */ uvdbg("Successfully initialized\n"); priv->crefs--; usbhost_givesem(&priv->exclsem); } } return ret; }
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); }
static inline int usbhost_cfgdesc(FAR struct usbhost_state_s *priv, FAR const uint8_t *configdesc, int desclen) { FAR struct usbhost_hubport_s *hport; FAR struct usb_cfgdesc_s *cfgdesc; FAR struct usb_desc_s *desc; FAR struct usbhost_epdesc_s bindesc; FAR struct usbhost_epdesc_s boutdesc; int remaining; uint8_t found = 0; int ret; DEBUGASSERT(priv != NULL && priv->usbclass.hport && configdesc != NULL && desclen >= sizeof(struct usb_cfgdesc_s)); hport = priv->usbclass.hport; /* Verify that we were passed a configuration descriptor */ cfgdesc = (FAR struct usb_cfgdesc_s *)configdesc; if (cfgdesc->type != USB_DESC_TYPE_CONFIG) { return -EINVAL; } /* Get the total length of the configuration descriptor (little endian). * It might be a good check to get the number of interfaces here too. */ remaining = (int)usbhost_getle16(cfgdesc->totallen); /* Skip to the next entry descriptor */ configdesc += cfgdesc->len; remaining -= cfgdesc->len; /* Loop where there are more dscriptors to examine */ while (remaining >= sizeof(struct usb_desc_s)) { /* What is the next descriptor? */ desc = (FAR struct usb_desc_s *)configdesc; switch (desc->type) { /* Interface descriptor. We really should get the number of endpoints * from this descriptor too. */ case USB_DESC_TYPE_INTERFACE: { FAR struct usb_ifdesc_s *ifdesc = (FAR struct usb_ifdesc_s *)configdesc; uvdbg("Interface descriptor\n"); DEBUGASSERT(remaining >= USB_SIZEOF_IFDESC); /* Save the interface number and mark ONLY the interface found */ priv->ifno = ifdesc->ifno; found = USBHOST_IFFOUND; } break; /* Endpoint descriptor. Here, we expect two bulk endpoints, an IN * and an OUT. */ case USB_DESC_TYPE_ENDPOINT: { FAR struct usb_epdesc_s *epdesc = (FAR struct usb_epdesc_s *)configdesc; uvdbg("Endpoint descriptor\n"); DEBUGASSERT(remaining >= USB_SIZEOF_EPDESC); /* Check for a bulk endpoint. */ if ((epdesc->attr & USB_EP_ATTR_XFERTYPE_MASK) == USB_EP_ATTR_XFER_BULK) { /* Yes.. it is a bulk endpoint. IN or OUT? */ if (USB_ISEPOUT(epdesc->addr)) { /* It is an OUT bulk endpoint. There should be only one * bulk OUT endpoint. */ if ((found & USBHOST_BOUTFOUND) != 0) { /* Oops.. more than one endpoint. We don't know * what to do with this. */ return -EINVAL; } found |= USBHOST_BOUTFOUND; /* Save the bulk OUT endpoint information */ boutdesc.hport = hport; boutdesc.addr = epdesc->addr & USB_EP_ADDR_NUMBER_MASK; boutdesc.in = false; boutdesc.xfrtype = USB_EP_ATTR_XFER_BULK; boutdesc.interval = epdesc->interval; boutdesc.mxpacketsize = usbhost_getle16(epdesc->mxpacketsize); uvdbg("Bulk OUT EP addr:%d mxpacketsize:%d\n", boutdesc.addr, boutdesc.mxpacketsize); } else { /* It is an IN bulk endpoint. There should be only one * bulk IN endpoint. */ if ((found & USBHOST_BINFOUND) != 0) { /* Oops.. more than one endpoint. We don't know * what to do with this. */ return -EINVAL; } found |= USBHOST_BINFOUND; /* Save the bulk IN endpoint information */ bindesc.hport = hport; bindesc.addr = epdesc->addr & USB_EP_ADDR_NUMBER_MASK; bindesc.in = 1; bindesc.xfrtype = USB_EP_ATTR_XFER_BULK; bindesc.interval = epdesc->interval; bindesc.mxpacketsize = usbhost_getle16(epdesc->mxpacketsize); uvdbg("Bulk IN EP addr:%d mxpacketsize:%d\n", bindesc.addr, bindesc.mxpacketsize); } } } break; /* Other descriptors are just ignored for now */ default: break; } /* If we found everything we need with this interface, then break out * of the loop early. */ if (found == USBHOST_ALLFOUND) { break; } /* Increment the address of the next descriptor */ configdesc += desc->len; remaining -= desc->len; } /* Sanity checking... did we find all of things that we need? */ if (found != USBHOST_ALLFOUND) { ulldbg("ERROR: Found IF:%s BIN:%s BOUT:%s\n", (found & USBHOST_IFFOUND) != 0 ? "YES" : "NO", (found & USBHOST_BINFOUND) != 0 ? "YES" : "NO", (found & USBHOST_BOUTFOUND) != 0 ? "YES" : "NO"); return -EINVAL; } /* We are good... Allocate the endpoints */ ret = DRVR_EPALLOC(hport->drvr, &boutdesc, &priv->epout); if (ret < 0) { udbg("ERROR: Failed to allocate Bulk OUT endpoint\n"); return ret; } ret = DRVR_EPALLOC(hport->drvr, &bindesc, &priv->epin); if (ret < 0) { udbg("ERROR: Failed to allocate Bulk IN endpoint\n"); (void)DRVR_EPFREE(hport->drvr, priv->epout); return ret; } ullvdbg("Endpoints allocated\n"); return OK; }