/**************************************************************************** * drivers/usbdev/usbdev_storage.c * * Copyright (C) 2008-2010 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Mass storage class device. Bulk-only with SCSI subclass. * * References: * "Universal Serial Bus Mass Storage Class, Specification Overview," * Revision 1.2, USB Implementer's Forum, June 23, 2003. * * "Universal Serial Bus Mass Storage Class, Bulk-Only Transport," * Revision 1.0, USB Implementer's Forum, September 31, 1999. * * "SCSI Primary Commands - 3 (SPC-3)," American National Standard * for Information Technology, May 4, 2005 * * "SCSI Primary Commands - 4 (SPC-4)," American National Standard * for Information Technology, July 19, 2008 * * "SCSI Block Commands -2 (SBC-2)," American National Standard * for Information Technology, November 13, 2004 * * "SCSI Multimedia Commands - 3 (MMC-3)," American National Standard * for Information Technology, November 12, 2001 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name NuttX nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdev_storage.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /**************************************************************************** * Private Types ****************************************************************************/ /* The internal version of the class driver */ struct usbstrg_driver_s { struct usbdevclass_driver_s drvr; FAR struct usbstrg_dev_s *dev; }; /* This is what is allocated */ struct usbstrg_alloc_s { struct usbstrg_dev_s dev; struct usbstrg_driver_s drvr; }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ /* Class Driver Support *****************************************************/ static void usbstrg_ep0incomplete(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req); static struct usbdev_req_s *usbstrg_allocreq(FAR struct usbdev_ep_s *ep, uint16_t len); static void usbstrg_freereq(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req); static int usbstrg_mkstrdesc(uint8_t id, struct usb_strdesc_s *strdesc); #ifdef CONFIG_USBDEV_DUALSPEED static int16_t usbstrg_mkcfgdesc(uint8_t *buf, uint8_t speed, uint8_t type); #else static int16_t usbstrg_mkcfgdesc(uint8_t *buf); #endif /* Class Driver Operations (most at interrupt level) ************************/ static int usbstrg_bind(FAR struct usbdev_s *dev, FAR struct usbdevclass_driver_s *driver); static void usbstrg_unbind(FAR struct usbdev_s *dev); static int usbstrg_setup(FAR struct usbdev_s *dev, FAR const struct usb_ctrlreq_s *ctrl); static void usbstrg_disconnect(FAR struct usbdev_s *dev); /* Initialization/Uninitialization ******************************************/ static void usbstrg_lununinitialize(struct usbstrg_lun_s *lun); /**************************************************************************** * Private Data ****************************************************************************/ /* Driver operations ********************************************************/ static struct usbdevclass_driverops_s g_driverops = { usbstrg_bind, /* bind */ usbstrg_unbind, /* unbind */ usbstrg_setup, /* setup */ usbstrg_disconnect, /* disconnect */ NULL, /* suspend */ NULL /* resume */ }; /* Descriptors **************************************************************/ /* Device descriptor */ static const struct usb_devdesc_s g_devdesc = { USB_SIZEOF_DEVDESC, /* len */ USB_DESC_TYPE_DEVICE, /* type */ {LSBYTE(0x0200), MSBYTE(0x0200)}, /* usb */ USB_CLASS_PER_INTERFACE, /* class */ 0, /* subclass */ 0, /* protocol */ CONFIG_USBSTRG_EP0MAXPACKET, /* maxpacketsize */ { LSBYTE(CONFIG_USBSTRG_VENDORID), /* vendor */ MSBYTE(CONFIG_USBSTRG_VENDORID) }, { LSBYTE(CONFIG_USBSTRG_PRODUCTID), /* product */ MSBYTE(CONFIG_USBSTRG_PRODUCTID) }, { LSBYTE(CONFIG_USBSTRG_VERSIONNO), /* device */ MSBYTE(CONFIG_USBSTRG_VERSIONNO) }, USBSTRG_MANUFACTURERSTRID, /* imfgr */ USBSTRG_PRODUCTSTRID, /* iproduct */ USBSTRG_SERIALSTRID, /* serno */ USBSTRG_NCONFIGS /* nconfigs */ }; /* Configuration descriptor */ static const struct usb_cfgdesc_s g_cfgdesc = { USB_SIZEOF_CFGDESC, /* len */ USB_DESC_TYPE_CONFIG, /* type */ {0, 0}, /* totallen -- to be provided */ USBSTRG_NINTERFACES, /* ninterfaces */ USBSTRG_CONFIGID, /* cfgvalue */ USBSTRG_CONFIGSTRID, /* icfg */ USB_CONFIG_ATTR_ONE|SELFPOWERED|REMOTEWAKEUP, /* attr */ (CONFIG_USBDEV_MAXPOWER + 1) / 2 /* mxpower */ }; /* Single interface descriptor */ static const struct usb_ifdesc_s g_ifdesc = { USB_SIZEOF_IFDESC, /* len */ USB_DESC_TYPE_INTERFACE, /* type */ USBSTRG_INTERFACEID, /* ifno */ USBSTRG_ALTINTERFACEID, /* alt */ USBSTRG_NENDPOINTS, /* neps */ USB_CLASS_MASS_STORAGE, /* class */ SUBSTRG_SUBCLASS_SCSI, /* subclass */ USBSTRG_PROTO_BULKONLY, /* protocol */ USBSTRG_CONFIGSTRID /* iif */ }; /* Endpoint descriptors */ static const struct usb_epdesc_s g_fsepbulkoutdesc = { USB_SIZEOF_EPDESC, /* len */ USB_DESC_TYPE_ENDPOINT, /* type */ USBSTRG_EPOUTBULK_ADDR, /* addr */ USBSTRG_EPOUTBULK_ATTR, /* attr */ { LSBYTE(USBSTRG_FSBULKMAXPACKET), /* maxpacket */ MSBYTE(USBSTRG_FSBULKMAXPACKET) }, 0 /* interval */ }; static const struct usb_epdesc_s g_fsepbulkindesc = { USB_SIZEOF_EPDESC, /* len */ USB_DESC_TYPE_ENDPOINT, /* type */ USBSTRG_EPINBULK_ADDR, /* addr */ USBSTRG_EPINBULK_ATTR, /* attr */ { LSBYTE(USBSTRG_FSBULKMAXPACKET), /* maxpacket */ MSBYTE(USBSTRG_FSBULKMAXPACKET) }, 0 /* interval */ }; #ifdef CONFIG_USBDEV_DUALSPEED static const struct usb_qualdesc_s g_qualdesc = { USB_SIZEOF_QUALDESC, /* len */ USB_DESC_TYPE_DEVICEQUALIFIER, /* type */ {LSBYTE(0x0200), MSBYTE(0x0200) }, /* USB */ USB_CLASS_PER_INTERFACE, /* class */ 0, /* subclass */ 0, /* protocol */ CONFIG_USBSTRG_EP0MAXPACKET, /* mxpacketsize */ USBSTRG_NCONFIGS, /* nconfigs */ 0, /* reserved */ }; static const struct usb_epdesc_s g_hsepbulkoutdesc = { USB_SIZEOF_EPDESC, /* len */ USB_DESC_TYPE_ENDPOINT, /* type */ USBSTRG_EPOUTBULK_ADDR, /* addr */ USBSTRG_EPOUTBULK_ATTR, /* attr */ { LSBYTE(USBSTRG_HSBULKMAXPACKET), /* maxpacket */ MSBYTE(USBSTRG_HSBULKMAXPACKET) }, 0 /* interval */ }; static const struct usb_epdesc_s g_hsepbulkindesc = { USB_SIZEOF_EPDESC, /* len */ USB_DESC_TYPE_ENDPOINT, /* type */ USBSTRG_EPINBULK_ADDR, /* addr */ USBSTRG_EPINBULK_ATTR, /* attr */ { LSBYTE(USBSTRG_HSBULKMAXPACKET), /* maxpacket */ MSBYTE(USBSTRG_HSBULKMAXPACKET) }, 0 /* interval */ }; #endif /**************************************************************************** * Public Data ****************************************************************************/ /* String *******************************************************************/ const char g_vendorstr[] = CONFIG_USBSTRG_VENDORSTR; const char g_productstr[] = CONFIG_USBSTRG_PRODUCTSTR; const char g_serialstr[] = CONFIG_USBSTRG_SERIALSTR; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Class Driver Support ****************************************************************************/ /**************************************************************************** * Name: usbstrg_ep0incomplete * * Description: * Handle completion of EP0 control operations * ****************************************************************************/ static void usbstrg_ep0incomplete(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req) { if (req->result || req->xfrd != req->len) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_REQRESULT), (uint16_t)-req->result); } } /**************************************************************************** * Name: usbstrg_allocreq * * Description: * Allocate a request instance along with its buffer * ****************************************************************************/ static struct usbdev_req_s *usbstrg_allocreq(FAR struct usbdev_ep_s *ep, uint16_t len) { FAR struct usbdev_req_s *req; req = EP_ALLOCREQ(ep); if (req != NULL) { req->len = len; req->buf = EP_ALLOCBUFFER(ep, len); if (!req->buf) { EP_FREEREQ(ep, req); req = NULL; } } return req; } /**************************************************************************** * Name: usbstrg_freereq * * Description: * Free a request instance along with its buffer * ****************************************************************************/ static void usbstrg_freereq(FAR struct usbdev_ep_s *ep, struct usbdev_req_s *req) { if (ep != NULL && req != NULL) { if (req->buf != NULL) { EP_FREEBUFFER(ep, req->buf); } EP_FREEREQ(ep, req); } } /**************************************************************************** * Name: usbstrg_mkstrdesc * * Description: * Construct a string descriptor * ****************************************************************************/ static int usbstrg_mkstrdesc(uint8_t id, struct usb_strdesc_s *strdesc) { const char *str; int len; int ndata; int i; switch (id) { case 0: { /* Descriptor 0 is the language id */ strdesc->len = 4; strdesc->type = USB_DESC_TYPE_STRING; strdesc->data[0] = LSBYTE(USBSTRG_STR_LANGUAGE); strdesc->data[1] = MSBYTE(USBSTRG_STR_LANGUAGE); return 4; } case USBSTRG_MANUFACTURERSTRID: str = g_vendorstr; break; case USBSTRG_PRODUCTSTRID: str = g_productstr; break; case USBSTRG_SERIALSTRID: str = g_serialstr; break; case USBSTRG_CONFIGSTRID: str = CONFIG_USBSTRG_CONFIGSTR; break; default: return -EINVAL; } /* The string is utf16-le. The poor man's utf-8 to utf16-le * conversion below will only handle 7-bit en-us ascii */ len = strlen(str); for (i = 0, ndata = 0; i < len; i++, ndata += 2) { strdesc->data[ndata] = str[i]; strdesc->data[ndata+1] = 0; } strdesc->len = ndata+2; strdesc->type = USB_DESC_TYPE_STRING; return strdesc->len; } /**************************************************************************** * Name: usbstrg_mkcfgdesc * * Description: * Construct the configuration descriptor * ****************************************************************************/ #ifdef CONFIG_USBDEV_DUALSPEED static int16_t usbstrg_mkcfgdesc(uint8_t *buf, uint8_t speed, uint8_t type) #else static int16_t usbstrg_mkcfgdesc(uint8_t *buf) #endif { FAR struct usb_cfgdesc_s *cfgdesc = (struct usb_cfgdesc_s*)buf; #ifdef CONFIG_USBDEV_DUALSPEED FAR const struct usb_epdesc_s *epdesc; bool hispeed = (speed == USB_SPEED_HIGH); uint16_t bulkmxpacket; #endif uint16_t totallen; /* This is the total length of the configuration (not necessarily the * size that we will be sending now. */ totallen = USB_SIZEOF_CFGDESC + USB_SIZEOF_IFDESC + USBSTRG_NENDPOINTS * USB_SIZEOF_EPDESC; /* Configuration descriptor -- Copy the canned descriptor and fill in the * type (we'll also need to update the size below */ memcpy(cfgdesc, &g_cfgdesc, USB_SIZEOF_CFGDESC); buf += USB_SIZEOF_CFGDESC; /* Copy the canned interface descriptor */ memcpy(buf, &g_ifdesc, USB_SIZEOF_IFDESC); buf += USB_SIZEOF_IFDESC; /* Make the two endpoint configurations */ #ifdef CONFIG_USBDEV_DUALSPEED /* Check for switches between high and full speed */ hispeed = (speed == USB_SPEED_HIGH); if (type == USB_DESC_TYPE_OTHERSPEEDCONFIG) { hispeed = !hispeed; } bulkmxpacket = USBSTRG_BULKMAXPACKET(hispeed); epdesc = USBSTRG_EPBULKINDESC(hispeed); memcpy(buf, epdesc, USB_SIZEOF_EPDESC); buf += USB_SIZEOF_EPDESC; epdesc = USBSTRG_EPBULKOUTDESC(hispeed); memcpy(buf, epdesc, USB_SIZEOF_EPDESC); #else memcpy(buf, &g_fsepbulkoutdesc, USB_SIZEOF_EPDESC); buf += USB_SIZEOF_EPDESC; memcpy(buf, &g_fsepbulkindesc, USB_SIZEOF_EPDESC); #endif /* Finally, fill in the total size of the configuration descriptor */ cfgdesc->totallen[0] = LSBYTE(totallen); cfgdesc->totallen[1] = MSBYTE(totallen); return totallen; } /**************************************************************************** * Class Driver Interfaces ****************************************************************************/ /**************************************************************************** * Name: usbstrg_bind * * Description: * Invoked when the driver is bound to a USB device driver * ****************************************************************************/ static int usbstrg_bind(FAR struct usbdev_s *dev, FAR struct usbdevclass_driver_s *driver) { FAR struct usbstrg_dev_s *priv = ((struct usbstrg_driver_s*)driver)->dev; FAR struct usbstrg_req_s *reqcontainer; irqstate_t flags; int ret = OK; int i; usbtrace(TRACE_CLASSBIND, 0); /* Bind the structures */ priv->usbdev = dev; dev->ep0->priv = priv; /* The configured EP0 size should match the reported EP0 size. We could * easily adapt to the reported EP0 size, but then we could not use the * const, canned descriptors. */ DEBUGASSERT(CONFIG_USBSTRG_EP0MAXPACKET == dev->ep0->maxpacket); /* Preallocate control request */ priv->ctrlreq = usbstrg_allocreq(dev->ep0, USBSTRG_MXDESCLEN); if (priv->ctrlreq == NULL) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_ALLOCCTRLREQ), 0); ret = -ENOMEM; goto errout; } priv->ctrlreq->callback = usbstrg_ep0incomplete; /* Pre-allocate all endpoints... the endpoints will not be functional * until the SET CONFIGURATION request is processed in usbstrg_setconfig. * This is done here because there may be calls to malloc and the SET * CONFIGURATION processing probably occurrs within interrupt handling * logic where malloc calls will fail. */ /* Pre-allocate the IN bulk endpoint */ priv->epbulkin = DEV_ALLOCEP(dev, USBSTRG_EPINBULK_ADDR, true, USB_EP_ATTR_XFER_BULK); if (!priv->epbulkin) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_EPBULKINALLOCFAIL), 0); ret = -ENODEV; goto errout; } priv->epbulkin->priv = priv; /* Pre-allocate the OUT bulk endpoint */ priv->epbulkout = DEV_ALLOCEP(dev, USBSTRG_EPOUTBULK_ADDR, false, USB_EP_ATTR_XFER_BULK); if (!priv->epbulkout) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_EPBULKOUTALLOCFAIL), 0); ret = -ENODEV; goto errout; } priv->epbulkout->priv = priv; /* Pre-allocate read requests */ for (i = 0; i < CONFIG_USBSTRG_NRDREQS; i++) { reqcontainer = &priv->rdreqs[i]; reqcontainer->req = usbstrg_allocreq(priv->epbulkout, CONFIG_USBSTRG_BULKOUTREQLEN); if (reqcontainer->req == NULL) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_RDALLOCREQ), (uint16_t)-ret); ret = -ENOMEM; goto errout; } reqcontainer->req->priv = reqcontainer; reqcontainer->req->callback = usbstrg_rdcomplete; } /* Pre-allocate write request containers and put in a free list */ for (i = 0; i < CONFIG_USBSTRG_NWRREQS; i++) { reqcontainer = &priv->wrreqs[i]; reqcontainer->req = usbstrg_allocreq(priv->epbulkin, CONFIG_USBSTRG_BULKINREQLEN); if (reqcontainer->req == NULL) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_WRALLOCREQ), (uint16_t)-ret); ret = -ENOMEM; goto errout; } reqcontainer->req->priv = reqcontainer; reqcontainer->req->callback = usbstrg_wrcomplete; flags = irqsave(); sq_addlast((sq_entry_t*)reqcontainer, &priv->wrreqlist); irqrestore(flags); } /* Report if we are selfpowered */ #ifdef CONFIG_USBDEV_SELFPOWERED DEV_SETSELFPOWERED(dev); #endif /* And pull-up the data line for the soft connect function */ DEV_CONNECT(dev); return OK; errout: usbstrg_unbind(dev); return ret; } /**************************************************************************** * Name: usbstrg_unbind * * Description: * Invoked when the driver is unbound from a USB device driver * ****************************************************************************/ static void usbstrg_unbind(FAR struct usbdev_s *dev) { FAR struct usbstrg_dev_s *priv; FAR struct usbstrg_req_s *reqcontainer; irqstate_t flags; int i; usbtrace(TRACE_CLASSUNBIND, 0); #ifdef CONFIG_DEBUG if (!dev || !dev->ep0) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_UNBINDINVALIDARGS), 0); return; } #endif /* Extract reference to private data */ priv = (FAR struct usbstrg_dev_s *)dev->ep0->priv; #ifdef CONFIG_DEBUG if (!priv) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_EP0NOTBOUND1), 0); return; } #endif /* The worker thread should have already been stopped by the * driver un-initialize logic. */ DEBUGASSERT(priv->thstate == USBSTRG_STATE_TERMINATED); /* Make sure that we are not already unbound */ if (priv != NULL) { /* Make sure that the endpoints have been unconfigured. If * we were terminated gracefully, then the configuration should * already have been reset. If not, then calling usbstrg_resetconfig * should cause the endpoints to immediately terminate all * transfers and return the requests to us (with result == -ESHUTDOWN) */ usbstrg_resetconfig(priv); up_mdelay(50); /* Free the bulk IN endpoint */ if (priv->epbulkin) { DEV_FREEEP(dev, priv->epbulkin); priv->epbulkin = NULL; } /* Free the pre-allocated control request */ if (priv->ctrlreq != NULL) { usbstrg_freereq(dev->ep0, priv->ctrlreq); priv->ctrlreq = NULL; } /* Free pre-allocated read requests (which should all have * been returned to the free list at this time -- we don't check) */ for (i = 0; i < CONFIG_USBSTRG_NRDREQS; i++) { reqcontainer = &priv->rdreqs[i]; if (reqcontainer->req) { usbstrg_freereq(priv->epbulkout, reqcontainer->req); reqcontainer->req = NULL; } } /* Free the bulk OUT endpoint */ if (priv->epbulkout) { DEV_FREEEP(dev, priv->epbulkout); priv->epbulkout = NULL; } /* Free write requests that are not in use (which should be all * of them */ flags = irqsave(); while (!sq_empty(&priv->wrreqlist)) { reqcontainer = (struct usbstrg_req_s *)sq_remfirst(&priv->wrreqlist); if (reqcontainer->req != NULL) { usbstrg_freereq(priv->epbulkin, reqcontainer->req); } } irqrestore(flags); } } /**************************************************************************** * Name: usbstrg_setup * * Description: * Invoked for ep0 control requests. This function probably executes * in the context of an interrupt handler. * ****************************************************************************/ static int usbstrg_setup(FAR struct usbdev_s *dev, FAR const struct usb_ctrlreq_s *ctrl) { FAR struct usbstrg_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 (!dev || !dev->ep0 || !ctrl) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_SETUPINVALIDARGS), 0); return -EIO; } #endif /* Extract reference to private data */ usbtrace(TRACE_CLASSSETUP, ctrl->req); priv = (FAR struct usbstrg_dev_s *)dev->ep0->priv; #ifdef CONFIG_DEBUG if (!priv || !priv->ctrlreq) { usbtrace(TRACE_CLSERROR(USBSTRG_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]) { case USB_DESC_TYPE_DEVICE: { ret = USB_SIZEOF_DEVDESC; memcpy(ctrlreq->buf, &g_devdesc, ret); } break; #ifdef CONFIG_USBDEV_DUALSPEED case USB_DESC_TYPE_DEVICEQUALIFIER: { ret = USB_SIZEOF_QUALDESC; memcpy(ctrlreq->buf, &g_qualdesc, ret); } break; case USB_DESC_TYPE_OTHERSPEEDCONFIG: #endif /* CONFIG_USBDEV_DUALSPEED */ case USB_DESC_TYPE_CONFIG: { #ifdef CONFIG_USBDEV_DUALSPEED ret = usbstrg_mkcfgdesc(ctrlreq->buf, dev->speed, ctrl->value[1]); #else ret = usbstrg_mkcfgdesc(ctrlreq->buf); #endif } break; case USB_DESC_TYPE_STRING: { /* index == language code. */ ret = usbstrg_mkstrdesc(ctrl->value[0], (struct usb_strdesc_s *)ctrlreq->buf); } break; default: { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_GETUNKNOWNDESC), value); } break; } } break; case USB_REQ_SETCONFIGURATION: { if (ctrl->type == 0) { /* Signal the worker thread to instantiate the new configuration */ priv->theventset |= USBSTRG_EVENT_CFGCHANGE; priv->thvalue = value; pthread_cond_signal(&priv->cond); /* Return here... the response will be provided later by the * worker thread. */ return OK; } } 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) { if (priv->config == USBSTRG_CONFIGID && index == USBSTRG_INTERFACEID && value == USBSTRG_ALTINTERFACEID) { /* Signal to instantiate the interface change */ priv->theventset |= USBSTRG_EVENT_IFCHANGE; pthread_cond_signal(&priv->cond); /* 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 == USBSTRG_CONFIGIDNONE) { if (index != USBSTRG_INTERFACEID) { ret = -EDOM; } else { ctrlreq->buf[0] = USBSTRG_ALTINTERFACEID; ret = 1; } } } break; default: usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_UNSUPPORTEDSTDREQ), ctrl->req); break; } } else { /********************************************************************** * Bulk-Only Mass Storage Class Requests **********************************************************************/ /* Verify that we are configured */ if (!priv->config) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_NOTCONFIGURED), 0); return ret; } switch (ctrl->req) { case USBSTRG_REQ_MSRESET: /* Reset mass storage device and interface */ { if (ctrl->type == USBSTRG_TYPE_SETUPOUT && value == 0 && len == 0) { /* Only one interface is supported */ if (index != USBSTRG_CONFIGID) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_MSRESETNDX), index); ret = -EDOM; } else { /* Signal to stop the current operation and reinitialize state */ priv->theventset |= USBSTRG_EVENT_RESET; pthread_cond_signal(&priv->cond); /* Return here... the response will be provided later by the * worker thread. */ return OK; } } } break; case USBSTRG_REQ_GETMAXLUN: /* Return number LUNs supported */ { if (ctrl->type == USBSTRG_TYPE_SETUPIN && value == 0) { /* Only one interface is supported */ if (index != USBSTRG_INTERFACEID) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_GETMAXLUNNDX), index); ret = -EDOM; } else { ctrlreq->buf[0] = priv->nluns - 1; ret = 1; } } } break; default: usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_BADREQUEST), index); break; } } /* 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) { ctrlreq->len = min(len, ret); ctrlreq->flags = USBDEV_REQFLAGS_NULLPKT; ret = EP_SUBMIT(dev->ep0, ctrlreq); if (ret < 0) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_EPRESPQ), (uint16_t)-ret); #if 0 /* Not necessary */ ctrlreq->result = OK; usbstrg_ep0incomplete(dev->ep0, ctrlreq); #endif } } return ret; } /**************************************************************************** * Name: usbstrg_disconnect * * Description: * Invoked after all transfers have been stopped, when the host is * disconnected. This function is probably called from the context of an * interrupt handler. * ****************************************************************************/ static void usbstrg_disconnect(FAR struct usbdev_s *dev) { struct usbstrg_dev_s *priv; irqstate_t flags; usbtrace(TRACE_CLASSDISCONNECT, 0); #ifdef CONFIG_DEBUG if (!dev || !dev->ep0) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_DISCONNECTINVALIDARGS), 0); return; } #endif /* Extract reference to private data */ priv = (FAR struct usbstrg_dev_s *)dev->ep0->priv; #ifdef CONFIG_DEBUG if (!priv) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_EP0NOTBOUND3), 0); return; } #endif /* Reset the configuration */ flags = irqsave(); usbstrg_resetconfig(priv); /* Signal the worker thread */ priv->theventset |= USBSTRG_EVENT_DISCONNECT; pthread_cond_signal(&priv->cond); irqrestore(flags); /* Perform the soft connect function so that we will we can be * re-enumerated. */ DEV_CONNECT(dev); } /**************************************************************************** * Initialization/Un-Initialization ****************************************************************************/ /**************************************************************************** * Name: usbstrg_lununinitialize ****************************************************************************/ static void usbstrg_lununinitialize(struct usbstrg_lun_s *lun) { /* Has a block driver has been bound to the LUN? */ if (lun->inode) { /* Close the block driver */ (void)close_blockdriver(lun->inode); } memset(lun, 0, sizeof(struct usbstrg_lun_s *)); } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Internal Interfaces ****************************************************************************/ /**************************************************************************** * Name: usbstrg_setconfig * * Description: * Set the device configuration by allocating and configuring endpoints and * by allocating and queuing read and write requests. * ****************************************************************************/ int usbstrg_setconfig(FAR struct usbstrg_dev_s *priv, uint8_t config) { FAR struct usbstrg_req_s *privreq; FAR struct usbdev_req_s *req; #ifdef CONFIG_USBDEV_DUALSPEED FAR const struct usb_epdesc_s *epdesc; bool hispeed = (priv->usbdev->speed == USB_SPEED_HIGH); uint16_t bulkmxpacket; #endif int i; int ret = 0; #if CONFIG_DEBUG if (priv == NULL) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_SETCONFIGINVALIDARGS), 0); return -EIO; } #endif if (config == priv->config) { /* Already configured -- Do nothing */ usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_ALREADYCONFIGURED), 0); return OK; } /* Discard the previous configuration data */ usbstrg_resetconfig(priv); /* Was this a request to simply discard the current configuration? */ if (config == USBSTRG_CONFIGIDNONE) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_CONFIGNONE), 0); return OK; } /* We only accept one configuration */ if (config != USBSTRG_CONFIGID) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_CONFIGIDBAD), 0); return -EINVAL; } /* Configure the IN bulk endpoint */ #ifdef CONFIG_USBDEV_DUALSPEED bulkmxpacket = USBSTRG_BULKMAXPACKET(hispeed); epdesc = USBSTRG_EPBULKINDESC(hispeed); ret = EP_CONFIGURE(priv->epbulkin, epdesc, false); #else ret = EP_CONFIGURE(priv->epbulkin, &g_fsepbulkindesc, false); #endif if (ret < 0) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_EPBULKINCONFIGFAIL), 0); goto errout; } priv->epbulkin->priv = priv; /* Configure the OUT bulk endpoint */ #ifdef CONFIG_USBDEV_DUALSPEED epdesc = USBSTRG_EPBULKOUTDESC(hispeed); ret = EP_CONFIGURE(priv->epbulkout, epdesc, true); #else ret = EP_CONFIGURE(priv->epbulkout, &g_fsepbulkoutdesc, true); #endif if (ret < 0) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_EPBULKOUTCONFIGFAIL), 0); goto errout; } priv->epbulkout->priv = priv; /* Queue read requests in the bulk OUT endpoint */ for (i = 0; i < CONFIG_USBSTRG_NRDREQS; i++) { privreq = &priv->rdreqs[i]; req = privreq->req; req->len = CONFIG_USBSTRG_BULKOUTREQLEN; req->priv = privreq; req->callback = usbstrg_rdcomplete; ret = EP_SUBMIT(priv->epbulkout, req); if (ret < 0) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_RDSUBMIT), (uint16_t)-ret); goto errout; } } priv->config = config; return OK; errout: usbstrg_resetconfig(priv); return ret; } /**************************************************************************** * Name: usbstrg_resetconfig * * Description: * Mark the device as not configured and disable all endpoints. * ****************************************************************************/ void usbstrg_resetconfig(FAR struct usbstrg_dev_s *priv) { /* Are we configured? */ if (priv->config != USBSTRG_CONFIGIDNONE) { /* Yes.. but not anymore */ priv->config = USBSTRG_CONFIGIDNONE; /* Disable endpoints. This should force completion of all pending * transfers. */ EP_DISABLE(priv->epbulkin); EP_DISABLE(priv->epbulkout); } } /**************************************************************************** * Name: usbstrg_wrcomplete * * Description: * Handle completion of write request. This function probably executes * in the context of an interrupt handler. * ****************************************************************************/ void usbstrg_wrcomplete(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req) { FAR struct usbstrg_dev_s *priv; FAR struct usbstrg_req_s *privreq; irqstate_t flags; /* Sanity check */ #ifdef CONFIG_DEBUG if (!ep || !ep->priv || !req || !req->priv) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_WRCOMPLETEINVALIDARGS), 0); return; } #endif /* Extract references to private data */ priv = (FAR struct usbstrg_dev_s*)ep->priv; privreq = (FAR struct usbstrg_req_s *)req->priv; /* Return the write request to the free list */ flags = irqsave(); sq_addlast((sq_entry_t*)privreq, &priv->wrreqlist); irqrestore(flags); /* Process the received data unless this is some unusual condition */ switch (req->result) { case OK: /* Normal completion */ usbtrace(TRACE_CLASSWRCOMPLETE, req->xfrd); break; case -ESHUTDOWN: /* Disconnection */ usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_WRSHUTDOWN), 0); break; default: /* Some other error occurred */ usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_WRUNEXPECTED), (uint16_t)-req->result); break; }; /* Inform the worker thread that a write request has been returned */ priv->theventset |= USBSTRG_EVENT_WRCOMPLETE; pthread_cond_signal(&priv->cond); } /**************************************************************************** * Name: usbstrg_rdcomplete * * Description: * Handle completion of read request on the bulk OUT endpoint. This * is handled like the receipt of serial data on the "UART" * ****************************************************************************/ void usbstrg_rdcomplete(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req) { FAR struct usbstrg_dev_s *priv; FAR struct usbstrg_req_s *privreq; irqstate_t flags; int ret; /* Sanity check */ #ifdef CONFIG_DEBUG if (!ep || !ep->priv || !req || !req->priv) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_RDCOMPLETEINVALIDARGS), 0); return; } #endif /* Extract references to private data */ priv = (FAR struct usbstrg_dev_s*)ep->priv; privreq = (FAR struct usbstrg_req_s *)req->priv; /* Process the received data unless this is some unusual condition */ switch (req->result) { case 0: /* Normal completion */ { usbtrace(TRACE_CLASSRDCOMPLETE, req->xfrd); /* Add the filled read request from the rdreqlist */ flags = irqsave(); sq_addlast((sq_entry_t*)privreq, &priv->rdreqlist); irqrestore(flags); /* Signal the worker thread that there is received data to be processed */ priv->theventset |= USBSTRG_EVENT_RDCOMPLETE; pthread_cond_signal(&priv->cond); } break; case -ESHUTDOWN: /* Disconnection */ { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_RDSHUTDOWN), 0); /* Drop the read request... it will be cleaned up later */ } break; default: /* Some other error occurred */ { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_RDUNEXPECTED), (uint16_t)-req->result); /* Return the read request to the bulk out endpoint for re-filling */ req = privreq->req; req->priv = privreq; req->callback = usbstrg_rdcomplete; ret = EP_SUBMIT(priv->epbulkout, req); if (ret != OK) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_RDCOMPLETERDSUBMIT), (uint16_t)-ret); } } break; } } /**************************************************************************** * Name: usbstrg_deferredresponse * * Description: * Some EP0 setup request cannot be responded to immediately becuase they * require some asynchronous action from the SCSI worker thread. This * function is provided for the SCSI thread to make that deferred response. * The specific requests that require this deferred response are: * * 1. USB_REQ_SETCONFIGURATION, * 2. USB_REQ_SETINTERFACE, or * 3. USBSTRG_REQ_MSRESET * * In all cases, the success reponse is a zero-length packet; the failure * response is an EP0 stall. * * Input parameters: * priv - Private state structure for this USB storage instance * stall - true is the action failed and a stall is required * ****************************************************************************/ void usbstrg_deferredresponse(FAR struct usbstrg_dev_s *priv, bool failed) { FAR struct usbdev_s *dev; FAR struct usbdev_req_s *ctrlreq; int ret; #ifdef CONFIG_DEBUG if (!priv || !priv->usbdev || !priv->ctrlreq) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_DEFERREDRESPINVALIDARGS), 0); return; } #endif dev = priv->usbdev; ctrlreq = priv->ctrlreq; /* If no error occurs, respond to the deferred setup command with a null * packet. */ if (!failed) { ctrlreq->len = 0; ctrlreq->flags = USBDEV_REQFLAGS_NULLPKT; ret = EP_SUBMIT(dev->ep0, ctrlreq); if (ret < 0) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_DEFERREDRESPSUBMIT), (uint16_t)-ret); #if 0 /* Not necessary */ ctrlreq->result = OK; usbstrg_ep0incomplete(dev->ep0, ctrlreq); #endif } } else { /* On a failure, the USB driver will stall. */ usbtrace(TRACE_DEVERROR(USBSTRG_TRACEERR_DEFERREDRESPSTALLED), 0); EP_STALL(dev->ep0); } } /**************************************************************************** * User Interfaces ****************************************************************************/ /**************************************************************************** * Name: usbstrg_configure * * Description: * One-time initialization of the USB storage driver. The initialization * sequence is as follows: * * 1. Call usbstrg_configure to perform one-time initialization specifying * the number of luns. * 2. Call usbstrg_bindlun to configure each supported LUN * 3. Call usbstrg_exportluns when all LUNs are configured * * Input Parameters: * nluns - the number of LUNs that will be registered * handle - Location to return a handle that is used in other API calls. * * Returned Value: * 0 on success; a negated errno on failure * ****************************************************************************/ int usbstrg_configure(unsigned int nluns, void **handle) { FAR struct usbstrg_alloc_s *alloc; FAR struct usbstrg_dev_s *priv; FAR struct usbstrg_driver_s *drvr; int ret; #ifdef CONFIG_DEBUG if (nluns > 15) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_TOOMANYLUNS), 0); return -EDOM; } #endif /* Allocate the structures needed */ alloc = (FAR struct usbstrg_alloc_s*)malloc(sizeof(struct usbstrg_alloc_s)); if (!alloc) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_ALLOCDEVSTRUCT), 0); return -ENOMEM; } /* Initialize the USB storage driver structure */ priv = &alloc->dev; memset(priv, 0, sizeof(struct usbstrg_dev_s)); pthread_mutex_init(&priv->mutex, NULL); pthread_cond_init(&priv->cond, NULL); sq_init(&priv->wrreqlist); priv->nluns = nluns; /* Allocate the LUN table */ priv->luntab = (struct usbstrg_lun_s*)malloc(priv->nluns*sizeof(struct usbstrg_lun_s)); if (!priv->luntab) { ret = -ENOMEM; goto errout; } memset(priv->luntab, 0, priv->nluns * sizeof(struct usbstrg_lun_s)); /* Initialize the USB class driver structure */ drvr = &alloc->drvr; #ifdef CONFIG_USBDEV_DUALSPEED drvr->drvr.speed = USB_SPEED_HIGH; #else drvr->drvr.speed = USB_SPEED_FULL; #endif drvr->drvr.ops = &g_driverops; drvr->dev = priv; /* Return the handle and success */ *handle = (FAR void*)alloc; return OK; errout: usbstrg_uninitialize(alloc); return ret; } /**************************************************************************** * Name: usbstrg_bindlun * * Description: * Bind the block driver specified by drvrpath to a USB storage LUN. * * Input Parameters: * handle - The handle returned by a previous call to usbstrg_configure(). * drvrpath - the full path to the block driver * startsector - A sector offset into the block driver to the start of the * partition on drvrpath (0 if no partitions) * nsectors - The number of sectors in the partition (if 0, all sectors * to the end of the media will be exported). * lunno - the LUN to bind to * * Returned Value: * 0 on success; a negated errno on failure. * ****************************************************************************/ int usbstrg_bindlun(FAR void *handle, FAR const char *drvrpath, unsigned int lunno, off_t startsector, size_t nsectors, bool readonly) { FAR struct usbstrg_alloc_s *alloc = (FAR struct usbstrg_alloc_s *)handle; FAR struct usbstrg_dev_s *priv; FAR struct usbstrg_lun_s *lun; FAR struct inode *inode; struct geometry geo; int ret; #ifdef CONFIG_DEBUG if (!alloc || !drvrpath || startsector < 0 || nsectors < 0) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_BINLUNINVALIDARGS1), 0); return -EINVAL; } #endif priv = &alloc->dev; #ifdef CONFIG_DEBUG if (!priv->luntab) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_INTERNALCONFUSION1), 0); return -EIO; } if (lunno > priv->nluns) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_BINDLUNINVALIDARGS2), 0); return -EINVAL; } #endif lun = &priv->luntab[lunno]; #ifdef CONFIG_DEBUG if (lun->inode != NULL) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_LUNALREADYBOUND), 0); return -EBUSY; } #endif /* Open the block driver */ ret = open_blockdriver(drvrpath, 0, &inode); if (ret < 0) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_BLKDRVEOPEN), 0); return ret; } /* Get the drive geometry */ if (!inode || !inode->u.i_bops || !inode->u.i_bops->geometry || inode->u.i_bops->geometry(inode, &geo) != OK || !geo.geo_available) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_NOGEOMETRY), 0); return -ENODEV; } /* Verify that the partition parameters are valid */ if (startsector >= geo.geo_nsectors) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_BINDLUNINVALIDARGS3), 0); return -EDOM; } else if (nsectors == 0) { nsectors = geo.geo_nsectors - startsector; } else if (startsector + nsectors >= geo.geo_nsectors) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_BINDLUNINVALIDARGS4), 0); return -EDOM; } /* Initialize the LUN structure */ memset(lun, 0, sizeof(struct usbstrg_lun_s *)); /* Allocate an I/O buffer big enough to hold one hardware sector. SCSI commands * are processed one at a time so all LUNs may share a single I/O buffer. The * I/O buffer will be allocated so that is it as large as the largest block * device sector size */ if (!priv->iobuffer) { priv->iobuffer = (uint8_t*)malloc(geo.geo_sectorsize); if (!priv->iobuffer) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_ALLOCIOBUFFER), geo.geo_sectorsize); return -ENOMEM; } priv->iosize = geo.geo_sectorsize; } else if (priv->iosize < geo.geo_sectorsize) { void *tmp; tmp = (uint8_t*)realloc(priv->iobuffer, geo.geo_sectorsize); if (!tmp) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_REALLOCIOBUFFER), geo.geo_sectorsize); return -ENOMEM; } priv->iobuffer = (uint8_t*)tmp; priv->iosize = geo.geo_sectorsize; } lun->inode = inode; lun->startsector = startsector; lun->nsectors = nsectors; lun->sectorsize = geo.geo_sectorsize; /* If the driver does not support the write method, then this is read-only */ if (!inode->u.i_bops->write) { lun->readonly = true; } return OK; } /**************************************************************************** * Name: usbstrg_unbindlun * * Description: * Un-bind the block driver for the specified LUN * * Input Parameters: * handle - The handle returned by a previous call to usbstrg_configure(). * lun - the LUN to unbind from * * Returned Value: * 0 on success; a negated errno on failure. * ****************************************************************************/ int usbstrg_unbindlun(FAR void *handle, unsigned int lunno) { FAR struct usbstrg_alloc_s *alloc = (FAR struct usbstrg_alloc_s *)handle; FAR struct usbstrg_dev_s *priv; FAR struct usbstrg_lun_s *lun; int ret; #ifdef CONFIG_DEBUG if (!alloc) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_UNBINDLUNINVALIDARGS1), 0); return -EINVAL; } #endif priv = &alloc->dev; #ifdef CONFIG_DEBUG if (!priv->luntab) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_INTERNALCONFUSION2), 0); return -EIO; } if (lunno > priv->nluns) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_UNBINDLUNINVALIDARGS2), 0); return -EINVAL; } #endif lun = &priv->luntab[lunno]; pthread_mutex_lock(&priv->mutex); #ifdef CONFIG_DEBUG if (lun->inode == NULL) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_LUNNOTBOUND), 0); ret = -EBUSY; } else #endif { /* Close the block driver */ usbstrg_lununinitialize(lun); ret = OK; } pthread_mutex_unlock(&priv->mutex); return ret; } /**************************************************************************** * Name: usbstrg_exportluns * * Description: * After all of the LUNs have been bound, this function may be called * in order to export those LUNs in the USB storage device. * * Input Parameters: * handle - The handle returned by a previous call to usbstrg_configure(). * * Returned Value: * 0 on success; a negated errno on failure * ****************************************************************************/ int usbstrg_exportluns(FAR void *handle) { FAR struct usbstrg_alloc_s *alloc = (FAR struct usbstrg_alloc_s *)handle; FAR struct usbstrg_dev_s *priv; FAR struct usbstrg_driver_s *drvr; irqstate_t flags; #ifdef SDCC pthread_attr_t attr; #endif int ret; #ifdef CONFIG_DEBUG if (!alloc) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_EXPORTLUNSINVALIDARGS), 0); return -ENXIO; } #endif priv = &alloc->dev; drvr = &alloc->drvr; /* Start the worker thread */ pthread_mutex_lock(&priv->mutex); priv->thstate = USBSTRG_STATE_NOTSTARTED; priv->theventset = USBSTRG_EVENT_NOEVENTS; #ifdef SDCC (void)pthread_attr_init(&attr); ret = pthread_create(&priv->thread, &attr, usbstrg_workerthread, (pthread_addr_t)priv); #else ret = pthread_create(&priv->thread, NULL, usbstrg_workerthread, (pthread_addr_t)priv); #endif if (ret != OK) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_THREADCREATE), (uint16_t)-ret); goto errout_with_mutex; } /* Register the USB storage class driver */ ret = usbdev_register(&drvr->drvr); if (ret != OK) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_DEVREGISTER), (uint16_t)-ret); goto errout_with_mutex; } /* Signal to start the thread */ flags = irqsave(); priv->theventset |= USBSTRG_EVENT_READY; pthread_cond_signal(&priv->cond); irqrestore(flags); errout_with_mutex: pthread_mutex_unlock(&priv->mutex); return ret; } /**************************************************************************** * Name: usbstrg_uninitialize * * Description: * Un-initialize the USB storage class driver * * Input Parameters: * handle - The handle returned by a previous call to usbstrg_configure(). * * Returned Value: * None * ****************************************************************************/ void usbstrg_uninitialize(FAR void *handle) { FAR struct usbstrg_alloc_s *alloc = (FAR struct usbstrg_alloc_s *)handle; FAR struct usbstrg_dev_s *priv; irqstate_t flags; #ifdef SDCC pthread_addr_t result1, result2; pthread_attr_t attr; #endif void *value; int ret; int i; #ifdef CONFIG_DEBUG if (!handle) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_UNINITIALIZEINVALIDARGS), 0); return; } #endif priv = &alloc->dev; /* If the thread hasn't already exitted, tell it to exit now */ if (priv->thstate != USBSTRG_STATE_NOTSTARTED) { /* The thread was started.. Is it still running? */ pthread_mutex_lock(&priv->mutex); if (priv->thstate != USBSTRG_STATE_TERMINATED) { /* Yes.. Ask the thread to stop */ flags = irqsave(); priv->theventset |= USBSTRG_EVENT_TERMINATEREQUEST; pthread_cond_signal(&priv->cond); irqrestore(flags); } pthread_mutex_unlock(&priv->mutex); /* Wait for the thread to exit. This is necessary even if the * thread has already exitted in order to collect the join * garbage */ ret = pthread_join(priv->thread, &value); } priv->thread = 0; /* Unregister the driver */ usbdev_unregister(&alloc->drvr.drvr); /* Uninitialize and release the LUNs */ for (i = 0; i < priv->nluns; ++i) { usbstrg_lununinitialize(&priv->luntab[i]); } free(priv->luntab); /* Release the I/O buffer */ if (priv->iobuffer) { free(priv->iobuffer); } /* Uninitialize and release the driver structure */ pthread_mutex_destroy(&priv->mutex); pthread_cond_destroy(&priv->cond); free(priv); }