/**************************************************************************** * drivers/usbdev/usbmsc.h * Mass storage class device. Bulk-only with SCSI subclass. * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ #ifndef __DRIVERS_USBDEV_USBMSC_H #define __DRIVERS_USBDEV_USBMSC_H /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* Configuration ************************************************************/ /* If the USB mass storage device is configured as part of a composite device * then both CONFIG_USBDEV_COMPOSITE and CONFIG_USBMSC_COMPOSITE must be * defined. */ #ifndef CONFIG_USBDEV_COMPOSITE # undef CONFIG_USBMSC_COMPOSITE #endif #if defined(CONFIG_USBMSC_COMPOSITE) && !defined(CONFIG_USBMSC_STRBASE) # define CONFIG_USBMSC_STRBASE (0) #endif /* Interface IDs. If the mass storage driver is built as a component of a * composite device, then the interface IDs may need to be offset. */ #ifndef CONFIG_USBMSC_COMPOSITE # undef CONFIG_USBMSC_IFNOBASE # define CONFIG_USBMSC_IFNOBASE 0 #endif #ifndef CONFIG_USBMSC_IFNOBASE # define CONFIG_USBMSC_IFNOBASE 0 #endif /* Number of requests in the write queue */ #ifndef CONFIG_USBMSC_NWRREQS # define CONFIG_USBMSC_NWRREQS 4 #endif /* Number of requests in the read queue */ #ifndef CONFIG_USBMSC_NRDREQS # define CONFIG_USBMSC_NRDREQS 4 #endif /* Logical endpoint numbers / max packet sizes */ #ifndef CONFIG_USBMSC_COMPOSITE # ifndef CONFIG_USBMSC_EPBULKOUT # warning "EPBULKOUT not defined in the configuration" # define CONFIG_USBMSC_EPBULKOUT 2 # endif #endif #ifndef CONFIG_USBMSC_COMPOSITE # ifndef CONFIG_USBMSC_EPBULKIN # warning "EPBULKIN not defined in the configuration" # define CONFIG_USBMSC_EPBULKIN 3 # endif #endif /* Packet and request buffer sizes */ #ifndef CONFIG_USBMSC_COMPOSITE # ifndef CONFIG_USBMSC_EP0MAXPACKET # define CONFIG_USBMSC_EP0MAXPACKET 64 # endif #endif #ifndef CONFIG_USBMSC_BULKINREQLEN # ifdef CONFIG_USBDEV_DUALSPEED # define CONFIG_USBMSC_BULKINREQLEN 512 # else # define CONFIG_USBMSC_BULKINREQLEN 64 # endif #else # ifdef CONFIG_USBDEV_DUALSPEED # if CONFIG_USBMSC_BULKINREQLEN < 512 # warning "Bulk in buffer size smaller than max packet" # undef CONFIG_USBMSC_BULKINREQLEN # define CONFIG_USBMSC_BULKINREQLEN 512 # endif # else # if CONFIG_USBMSC_BULKINREQLEN < 64 # warning "Bulk in buffer size smaller than max packet" # undef CONFIG_USBMSC_BULKINREQLEN # define CONFIG_USBMSC_BULKINREQLEN 64 # endif # endif #endif #ifndef CONFIG_USBMSC_BULKOUTREQLEN # ifdef CONFIG_USBDEV_DUALSPEED # define CONFIG_USBMSC_BULKOUTREQLEN 512 # else # define CONFIG_USBMSC_BULKOUTREQLEN 64 # endif #else # ifdef CONFIG_USBDEV_DUALSPEED # if CONFIG_USBMSC_BULKOUTREQLEN < 512 # warning "Bulk in buffer size smaller than max packet" # undef CONFIG_USBMSC_BULKOUTREQLEN # define CONFIG_USBMSC_BULKOUTREQLEN 512 # endif # else # if CONFIG_USBMSC_BULKOUTREQLEN < 64 # warning "Bulk in buffer size smaller than max packet" # undef CONFIG_USBMSC_BULKOUTREQLEN # define CONFIG_USBMSC_BULKOUTREQLEN 64 # endif # endif #endif /* Vendor and product IDs and strings */ #ifndef CONFIG_USBMSC_COMPOSITE # ifndef CONFIG_USBMSC_VENDORID # warning "CONFIG_USBMSC_VENDORID not defined" # define CONFIG_USBMSC_VENDORID 0x584e # endif # ifndef CONFIG_USBMSC_PRODUCTID # warning "CONFIG_USBMSC_PRODUCTID not defined" # define CONFIG_USBMSC_PRODUCTID 0x5342 # endif # ifndef CONFIG_USBMSC_VERSIONNO # define CONFIG_USBMSC_VERSIONNO (0x0399) # endif # ifndef CONFIG_USBMSC_VENDORSTR # warning "No Vendor string specified" # define CONFIG_USBMSC_VENDORSTR "NuttX" # endif # ifndef CONFIG_USBMSC_PRODUCTSTR # warning "No Product string specified" # define CONFIG_USBMSC_PRODUCTSTR "USBdev Storage" # endif # undef CONFIG_USBMSC_SERIALSTR # define CONFIG_USBMSC_SERIALSTR "0101" #endif #undef CONFIG_USBMSC_CONFIGSTR #define CONFIG_USBMSC_CONFIGSTR "Bulk" /* SCSI daemon */ #ifndef CONFIG_USBMSC_SCSI_PRIO # define CONFIG_USBMSC_SCSI_PRIO 128 #endif #ifndef CONFIG_USBMSC_SCSI_STACKSIZE # define CONFIG_USBMSC_SCSI_STACKSIZE 2048 #endif /* Packet and request buffer sizes */ #ifndef CONFIG_USBMSC_EP0MAXPACKET # define CONFIG_USBMSC_EP0MAXPACKET 64 #endif /* USB Controller */ #ifdef CONFIG_USBDEV_SELFPOWERED # define USBMSC_SELFPOWERED USB_CONFIG_ATTR_SELFPOWER #else # define USBMSC_SELFPOWERED (0) #endif #ifdef CONFIG_USBDEV_REMOTEWAKEUP # define USBMSC_REMOTEWAKEUP USB_CONFIG_ATTR_WAKEUP #else # define USBMSC_REMOTEWAKEUP (0) #endif #ifndef CONFIG_USBDEV_MAXPOWER # define CONFIG_USBDEV_MAXPOWER 100 #endif /* Current state of the worker thread */ #define USBMSC_STATE_NOTSTARTED (0) /* Thread has not yet been started */ #define USBMSC_STATE_STARTED (1) /* Started, but is not yet initialized */ #define USBMSC_STATE_IDLE (2) /* Started and waiting for commands */ #define USBMSC_STATE_CMDPARSE (3) /* Processing a received command */ #define USBMSC_STATE_CMDREAD (4) /* Processing a SCSI read command */ #define USBMSC_STATE_CMDWRITE (5) /* Processing a SCSI write command */ #define USBMSC_STATE_CMDFINISH (6) /* Finish command processing */ #define USBMSC_STATE_CMDSTATUS (7) /* Processing the final status of the command */ #define USBMSC_STATE_TERMINATED (8) /* Thread has exitted */ /* Event communicated to worker thread */ #define USBMSC_EVENT_NOEVENTS (0) /* There are no outstanding events */ #define USBMSC_EVENT_READY (1 << 0) /* Initialization is complete */ #define USBMSC_EVENT_RDCOMPLETE (1 << 1) /* A read has completed there is data to be processed */ #define USBMSC_EVENT_WRCOMPLETE (1 << 2) /* A write has completed and a request is available */ #define USBMSC_EVENT_TERMINATEREQUEST (1 << 3) /* Shutdown requested */ #define USBMSC_EVENT_DISCONNECT (1 << 4) /* USB disconnect received */ #define USBMSC_EVENT_RESET (1 << 5) /* USB storage setup reset received */ #define USBMSC_EVENT_CFGCHANGE (1 << 6) /* USB setup configuration change received */ #define USBMSC_EVENT_IFCHANGE (1 << 7) /* USB setup interface change received */ #define USBMSC_EVENT_ABORTBULKOUT (1 << 8) /* SCSI receive failure */ /* SCSI command flags (passed to usbmsc_setupcmd()) */ #define USBMSC_FLAGS_DIRMASK (0x03) /* Bits 0-1: Data direction */ #define USBMSC_FLAGS_DIRNONE (0x00) /* No data to send */ #define USBMSC_FLAGS_DIRHOST2DEVICE (0x01) /* Host-to-device */ #define USBMSC_FLAGS_DIRDEVICE2HOST (0x02) /* Device-to-host */ #define USBMSC_FLAGS_BLOCKXFR (0x04) /* Bit 2: Command is a block transfer request */ #define USBMSC_FLAGS_LUNNOTNEEDED (0x08) /* Bit 3: Command does not require a valid LUN */ #define USBMSC_FLAGS_UACOKAY (0x10) /* Bit 4: Command OK if unit attention condition */ #define USBMSC_FLAGS_RETAINSENSEDATA (0x20) /* Bit 5: Do not clear sense data */ /* Descriptors **************************************************************/ /* Big enough to hold our biggest descriptor */ #define USBMSC_MXDESCLEN (64) #define USBMSC_MAXSTRLEN (USBMSC_MXDESCLEN-2) /* String language */ #define USBMSC_STR_LANGUAGE (0x0409) /* en-us */ /* Descriptor strings */ #ifndef CONFIG_USBMSC_COMPOSITE # define USBMSC_MANUFACTURERSTRID (1) # define USBMSC_PRODUCTSTRID (2) # define USBMSC_SERIALSTRID (3) # define USBMSC_CONFIGSTRID (4) # define USBMSC_INTERFACESTRID USBMSC_CONFIGSTRID # undef CONFIG_USBMSC_STRBASE # define CONFIG_USBMSC_STRBASE (0) #else # define USBMSC_INTERFACESTRID (CONFIG_USBMSC_STRBASE + 1) #endif #define USBMSC_LASTSTRID USBMSC_INTERFACESTRID #define USBMSC_NSTRIDS (USBMSC_LASTSTRID - CONFIG_USBMSC_STRBASE) /* Configuration Descriptor */ #define USBMSC_INTERFACEID (CONFIG_USBMSC_IFNOBASE + 0) #define USBMSC_ALTINTERFACEID (0) #define USBMSC_CONFIGIDNONE (0) /* Config ID means to return to address mode */ /* Endpoint configuration */ #define USBMSC_MKEPBULKOUT(devDesc) ((devDesc)->epno[USBMSC_EP_BULKOUT_IDX]) #define USBMSC_EPOUTBULK_ATTR (USB_EP_ATTR_XFER_BULK) #define USBMSC_MKEPBULKIN(devDesc) (USB_DIR_IN | (devDesc)->epno[USBMSC_EP_BULKIN_IDX]) #define USBMSC_EPINBULK_ATTR (USB_EP_ATTR_XFER_BULK) #define USBMSC_HSBULKMAXPACKET (512) #define USBMSC_HSBULKMXPKTSHIFT (9) #define USBMSC_HSBULKMXPKTMASK (0x000001ff) #define USBMSC_FSBULKMAXPACKET (64) #define USBMSC_FSBULKMXPKTSHIFT (6) #define USBMSC_FSBULKMXPKTMASK (0x0000003f) /* Configuration descriptor size */ #ifndef CONFIG_USBMSC_COMPOSITE /* The size of the config descriptor: (9 + 9 + 2*7) = 32 */ # define SIZEOF_USBMSC_CFGDESC \ (USB_SIZEOF_CFGDESC + USB_SIZEOF_IFDESC + USBMSC_NENDPOINTS * USB_SIZEOF_EPDESC) #else /* The size of the config descriptor: (9 + 2*7) = 23 */ # define SIZEOF_USBMSC_CFGDESC \ (USB_SIZEOF_IFDESC + USBMSC_NENDPOINTS * USB_SIZEOF_EPDESC) #endif /* Block driver helpers *****************************************************/ #define USBMSC_DRVR_READ(l,b,s,n) \ ((l)->inode->u.i_bops->read((l)->inode,b,s,n)) #define USBMSC_DRVR_WRITE(l,b,s,n) \ ((l)->inode->u.i_bops->write((l)->inode,b,s,n)) #define USBMSC_DRVR_GEOMETRY(l,g) \ ((l)->inode->u.i_bops->geometry((l)->inode,g)) /* Everpresent MIN/MAX macros ***********************************************/ #ifndef MIN # define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif #ifndef MAX # define MAX(a,b) ((a) > (b) ? (a) : (b)) #endif /**************************************************************************** * Public Types ****************************************************************************/ /* Endpoint descriptors */ enum usbmsc_epdesc_e { USBMSC_EPBULKOUT = 0, /* Bulk OUT endpoint descriptor */ USBMSC_EPBULKIN /* Bulk IN endpoint descriptor */ }; /* Container to support a list of requests */ struct usbmsc_req_s { FAR struct usbmsc_req_s *flink; /* Implements a singly linked list */ FAR struct usbdev_req_s *req; /* The contained request */ }; /* This structure describes one LUN: */ struct usbmsc_lun_s { FAR struct inode *inode; /* Inode structure of open'ed block driver */ uint8_t readonly:1; /* Media is read-only */ uint8_t locked:1; /* Media removal is prevented */ uint16_t sectorsize; /* The size of one sector */ uint32_t sd; /* Sense data */ uint32_t sdinfo; /* Sense data information */ uint32_t uad; /* Unit needs attention data */ off_t startsector; /* Sector offset to start of partition */ size_t nsectors; /* Number of sectors in the partition */ }; /* Describes the overall state of one instance of the driver */ struct usbmsc_dev_s { FAR struct usbdev_s *usbdev; /* usbdev driver pointer (Non-null if registered) */ /* SCSI worker kernel thread interface */ pid_t thpid; /* The worker thread task ID */ sem_t thsynch; /* Used to synchronizer terminal events */ sem_t thlock; /* Used to get exclusive access to the state data */ sem_t thwaitsem; /* Used to signal worker thread */ volatile bool thwaiting; /* True: worker thread is waiting for an event */ volatile uint8_t thstate; /* State of the worker thread */ volatile uint16_t theventset; /* Set of pending events signaled to worker thread */ volatile uint8_t thvalue; /* Value passed with the event (must persist) */ /* Storage class configuration and state */ uint8_t nluns:4; /* Number of LUNs */ uint8_t config; /* Configuration number */ /* Endpoints */ FAR struct usbdev_ep_s *epbulkin; /* Bulk IN endpoint structure */ FAR struct usbdev_ep_s *epbulkout; /* Bulk OUT endpoint structure */ FAR struct usbdev_req_s *ctrlreq; /* Control request (for ep0 setup responses) */ /* SCSI command processing */ struct usbmsc_lun_s *lun; /* Currently selected LUN */ struct usbmsc_lun_s *luntab; /* Allocated table of all LUNs */ uint8_t cdb[USBMSC_MAXCDBLEN]; /* Command data (cdb[]) from CBW */ uint8_t phaseerror:1; /* Need to send phase sensing status */ uint8_t shortpacket:1; /* Host transmission stopped unexpectedly */ uint8_t cbwdir:2; /* Direction from CBW. See USBMSC_FLAGS_DIR* definitions */ uint8_t cdblen; /* Length of cdb[] from CBW */ uint8_t cbwlun; /* LUN from the CBW */ uint16_t nsectbytes; /* Bytes buffered in iobuffer[] */ uint16_t nreqbytes; /* Bytes buffered in head write requests */ uint16_t iosize; /* Size of iobuffer[] */ uint32_t cbwlen; /* Length of data from CBW */ uint32_t cbwtag; /* Tag from the CBW */ union { uint32_t xfrlen; /* Read/Write: Sectors remaining to be transferred */ uint32_t alloclen; /* Other device-to-host: Host allocation length */ } u; uint32_t sector; /* Current sector (relative to lun->startsector) */ uint32_t residue; /* Untransferred amount reported in the CSW */ uint8_t *iobuffer; /* Buffer for data transfers */ /* Write request list */ struct sq_queue_s wrreqlist; /* List of empty write request containers */ struct sq_queue_s rdreqlist; /* List of filled read request containers */ struct usbdev_devinfo_s devinfo; /* Pre-allocated write request containers. The write requests will * be linked in a free list (wrreqlist), and used to send requests to * EPBULKIN; Read requests will be queued in the EBULKOUT. */ struct usbmsc_req_s wrreqs[CONFIG_USBMSC_NWRREQS]; struct usbmsc_req_s rdreqs[CONFIG_USBMSC_NRDREQS]; }; /**************************************************************************** * Public Data ****************************************************************************/ #undef EXTERN #if defined(__cplusplus) # define EXTERN extern "C" extern "C" { #else # define EXTERN extern #endif /* String *******************************************************************/ /* Mass storage class vendor/product/serial number strings */ #ifndef CONFIG_USBMSC_COMPOSITE EXTERN const char g_mscvendorstr[]; EXTERN const char g_mscproductstr[]; EXTERN const char g_mscserialstr[]; /* If we are using a composite device, then vendor/product/serial number * strings are provided by the composite device logic. */ #else EXTERN const char g_compvendorstr[]; EXTERN const char g_compproductstr[]; EXTERN const char g_compserialstr[]; #define g_mscvendorstr g_compvendorstr #define g_mscproductstr g_compproductstr #define g_mscserialstr g_compserialstr #endif /* Used to hand-off the state structure when the SCSI worker thread is started */ EXTERN FAR struct usbmsc_dev_s *g_usbmsc_handoff; /**************************************************************************** * Public Function Prototypes ****************************************************************************/ /**************************************************************************** * Name: usbmsc_scsi_lock * * Description: * Get exclusive access to SCSI state data. * ****************************************************************************/ int usbmsc_scsi_lock(FAR struct usbmsc_dev_s *priv); /**************************************************************************** * Name: usbmsc_scsi_unlock * * Description: * Relinquish exclusive access to SCSI state data. * ****************************************************************************/ #define usbmsc_scsi_unlock(priv) nxsem_post(&priv->thlock) /**************************************************************************** * Name: usbmsc_scsi_signal * * Description: * Signal the SCSI worker thread that SCSI events need service. * ****************************************************************************/ void usbmsc_scsi_signal(FAR struct usbmsc_dev_s *priv); /**************************************************************************** * Name: usbmsc_synch_signal * * Description: * ACK controlling tasks request for synchronization. * ****************************************************************************/ #define usbmsc_synch_signal(priv) nxsem_post(&priv->thsynch) /**************************************************************************** * Name: usbmsc_mkstrdesc * * Description: * Construct a string descriptor * ****************************************************************************/ struct usb_strdesc_s; int usbmsc_mkstrdesc(uint8_t id, struct usb_strdesc_s *strdesc); /**************************************************************************** * Name: usbmsc_getdevdesc * * Description: * Return a pointer to the raw device descriptor * ****************************************************************************/ #ifndef CONFIG_USBMSC_COMPOSITE FAR const struct usb_devdesc_s *usbmsc_getdevdesc(void); #endif /**************************************************************************** * Name: usbmsc_copy_epdesc * * Description: * Copies the requested Endpoint Description into the buffer given. * Returns the number of Bytes filled in ( sizeof(struct usb_epdesc_s) ). * ****************************************************************************/ int usbmsc_copy_epdesc(enum usbmsc_epdesc_e epid, FAR struct usb_epdesc_s *epdesc, FAR struct usbdev_devinfo_s *devinfo, bool hispeed); /**************************************************************************** * Name: usbmsc_mkcfgdesc * * Description: * Construct the configuration descriptor * ****************************************************************************/ #ifdef CONFIG_USBDEV_DUALSPEED int16_t usbmsc_mkcfgdesc(FAR uint8_t *buf, FAR struct usbdev_devinfo_s *devinfo, uint8_t speed, uint8_t type); #else int16_t usbmsc_mkcfgdesc(FAR uint8_t *buf, FAR struct usbdev_devinfo_s *devinfo); #endif /**************************************************************************** * Name: usbmsc_getqualdesc * * Description: * Return a pointer to the raw qual descriptor * ****************************************************************************/ #if !defined(CONFIG_USBMSC_COMPOSITE) && defined(CONFIG_USBDEV_DUALSPEED) FAR const struct usb_qualdesc_s *usbmsc_getqualdesc(void); #endif /**************************************************************************** * Name: usbmsc_scsi_main * * Description: * This is the main function of the USB storage worker thread. It loops * until USB-related events occur, then processes those events accordingly * ****************************************************************************/ int usbmsc_scsi_main(int argc, char *argv[]); /**************************************************************************** * Name: usbmsc_setconfig * * Description: * Set the device configuration by allocating and configuring endpoints and * by allocating and queue read and write requests. * ****************************************************************************/ int usbmsc_setconfig(FAR struct usbmsc_dev_s *priv, uint8_t config); /**************************************************************************** * Name: usbmsc_resetconfig * * Description: * Mark the device as not configured and disable all endpoints. * ****************************************************************************/ void usbmsc_resetconfig(FAR struct usbmsc_dev_s *priv); /**************************************************************************** * Name: usbmsc_wrcomplete * * Description: * Handle completion of write request. This function probably executes * in the context of an interrupt handler. * ****************************************************************************/ void usbmsc_wrcomplete(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req); /**************************************************************************** * Name: usbmsc_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 usbmsc_rdcomplete(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req); /**************************************************************************** * Name: usbmsc_deferredresponse * * Description: * Some EP0 setup request cannot be responded to immediately because 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. USBMSC_REQ_MSRESET * * In all cases, the success response 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 usbmsc_deferredresponse(FAR struct usbmsc_dev_s *priv, bool failed); #undef EXTERN #if defined(__cplusplus) } #endif #endif /* __DRIVERS_USBDEV_USBMSC_H */