/****************************************************************************
 * drivers/usbdev/usbmsc.h
 *
 *   Copyright (C) 2008-2013, 2015 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <gnutt@nuttx.org>
 *
 * Mass storage class device.  Bulk-only with SCSI subclass.
 *
 * 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.
 *
 ****************************************************************************/

#ifndef __DRIVERS_USBDEV_USBMSC_H
#define __DRIVERS_USBDEV_USBMSC_H

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>

#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <queue.h>
#include <semaphore.h>

#include <nuttx/fs/fs.h>
#include <nuttx/usb/storage.h>
#include <nuttx/usb/usbdev.h>

/****************************************************************************
 * 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 (4)
#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_EPBULKOUT
#  warning "EPBULKOUT not defined in the configuration"
#  define CONFIG_USBMSC_EPBULKOUT 2
#endif

#ifndef CONFIG_USBMSC_EPBULKIN
#  warning "EPBULKIN not defined in the configuration"
#  define CONFIG_USBMSC_EPBULKIN 3
#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)

#define USBMSC_NCONFIGS               (1) /* Number of configurations supported */

/* Configuration Descriptor */

#define USBMSC_NINTERFACES            (1) /* Number of interfaces in the configuration */
#define USBMSC_INTERFACEID            (CONFIG_USBMSC_IFNOBASE+0)
#define USBMSC_ALTINTERFACEID         (0)

#define USBMSC_CONFIGIDNONE           (0) /* Config ID means to return to address mode */
#define USBMSC_CONFIGID               (1) /* The only supported configuration ID */

/* Interface description */

#define USBMSC_NENDPOINTS             (2) /* Number of endpoints in the interface  */

/* Endpoint configuration */

#define USBMSC_EPOUTBULK_ADDR         (CONFIG_USBMSC_EPBULKOUT)
#define USBMSC_EPOUTBULK_ATTR         (USB_EP_ATTR_XFER_BULK)

#define USBMSC_EPINBULK_ADDR          (USB_DIR_IN|CONFIG_USBMSC_EPBULKIN)
#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)

/* Macros for dual speed vs. full speed only operation */

#ifdef CONFIG_USBDEV_DUALSPEED
#  define USBMSC_EPBULKINDESC(hs)  \
   usbmsc_getepdesc((hs) ? USBMSC_EPHSBULKIN : USBMSC_EPFSBULKIN)
#  define USBMSC_EPBULKOUTDESC(hs) \
   usbmsc_getepdesc((hs) ? USBMSC_EPHSBULKOUT : USBMSC_EPFSBULKOUT)
#  define USBMSC_BULKMAXPACKET(hs) \
   ((hs) ? USBMSC_HSBULKMAXPACKET : USBMSC_FSBULKMAXPACKET)
#  define USBMSC_BULKMXPKTSHIFT(d) \
   (((d)->speed==USB_SPEED_HIGH) ? USBMSC_HSBULKMXPKTSHIFT : USBMSC_FSBULKMXPKTSHIFT)
#  define USBMSC_BULKMXPKTMASK(d) \
   (((d)->speed==USB_SPEED_HIGH) ? USBMSC_HSBULKMXPKTMASK : USBMSC_FSBULKMXPKTMASK)
#else
#  define USBMSC_EPBULKINDESC(d)    usbmsc_getepdesc(USBMSC_EPFSBULKIN)
#  define USBMSC_EPBULKOUTDESC(d)   usbmsc_getepdesc(USBMSC_EPFSBULKOUT)
#  define USBMSC_BULKMAXPACKET(hs)  USBMSC_FSBULKMAXPACKET
#  define USBMSC_BULKMXPKTSHIFT(d)  USBMSC_FSBULKMXPKTSHIFT
#  define USBMSC_BULKMXPKTMASK(d)   USBMSC_FSBULKMXPKTMASK
#endif

/* Configuration descriptor size */

#ifndef CONFIG_USBMSC_COMPOSITE

/* Number of individual descriptors in the configuration descriptor:
 * (1) Configuration descriptor + (1) interface descriptor + (2) interface
 * descriptors.
 */

#  define USBMSC_CFGGROUP_SIZE      (4)

/* 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

/* Number of individual descriptors in the configuration descriptor:
 * (1) interface descriptor + (2) interface descriptors.
 */

#  define USBMSC_CFGGROUP_SIZE     (3)

/* 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_EPFSBULKOUT = 0, /* Full speed bulk OUT endpoint descriptor */
  USBMSC_EPFSBULKIN       /* Full speed bulk IN endpoint descriptor */
#ifdef CONFIG_USBDEV_DUALSPEED
  ,
  USBMSC_EPHSBULKOUT,     /* High speed bulk OUT endpoint descriptor */
  USBMSC_EPHSBULKIN       /* High speed bulk IN endpoint descriptor */
#endif
};

/* 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
{
  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 */

  /* 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.
 *
 ****************************************************************************/

void 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) sem_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) sem_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_getepdesc
 *
 * 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_getepdesc
 *
 * Description:
 *   Return a pointer to the raw endpoint descriptor (used for configuring endpoints)
 *
 ************************************************************************************/

struct usb_epdesc_s;
FAR const struct usb_epdesc_s *usbmsc_getepdesc(enum usbmsc_epdesc_e epid);

/************************************************************************************
 * Name: usbmsc_mkcfgdesc
 *
 * Description:
 *   Construct the configuration descriptor
 *
 ************************************************************************************/

#ifdef CONFIG_USBDEV_DUALSPEED
int16_t usbmsc_mkcfgdesc(FAR uint8_t *buf, uint8_t speed, uint8_t type);
#else
int16_t usbmsc_mkcfgdesc(FAR uint8_t *buf);
#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 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. USBMSC_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 usbmsc_deferredresponse(FAR struct usbmsc_dev_s *priv, bool failed);

#undef EXTERN
#if defined(__cplusplus)
}
#endif

#endif /* #define __DRIVERS_USBDEV_USBMSC_H */
