summaryrefslogblamecommitdiff
path: root/nuttx/drivers/usbdev/usbdev_storage.h
blob: 9d874c16605fb5f4ffd0abdf5efdd507ef830811 (plain) (tree)


















































                                                                              
                              









































































































































































































































































































































                                                                                                        






                                                                                     
                               

















































































































































                                                                                                  
/****************************************************************************
 * drivers/usbdev/usbdev_storage.h
 *
 *   Copyright (C) 2008 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <spudmonkey@racsa.co.cr>
 *
 * 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_USBDEV_STORAGE_H
#define __DRIVERS_USBDEV_USBDEV_STORAGE_H

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

#include <nuttx/config.h>
#include <sys/types.h>

#include <pthread.h>
#include <queue.h>

#include <nuttx/fs.h>
#include <nuttx/usb_storage.h>
#include <nuttx/usbdev.h>

/****************************************************************************
 * Definitions
 ****************************************************************************/

/* Configuration ************************************************************/

/* Number of requests in the write queue */

#ifndef CONFIG_USBSTRG_NWRREQS
#  define CONFIG_USBSTRG_NWRREQS 4
#endif

/* Number of requests in the read queue */

#ifndef CONFIG_USBSTRG_NRDREQS
#  define CONFIG_USBSTRG_NRDREQS 4
#endif

/* Logical endpoint numbers / max packet sizes */

#ifndef CONFIG_USBSTRG_EPBULKOUT
#  warning "EPBULKOUT not defined in the configuration"
#  define CONFIG_USBSTRG_EPBULKOUT 2
#endif

#ifndef CONFIG_USBSTRG_EPBULKIN
#  warning "EPBULKIN not defined in the configuration"
#  define CONFIG_USBSTRG_EPBULKIN 3
#endif

/* Packet and request buffer sizes */

#ifndef CONFIG_USBSTRG_EP0MAXPACKET
#  define CONFIG_USBSTRG_EP0MAXPACKET 64
#endif

#ifndef CONFIG_USBSTRG_BULKINREQLEN
#  ifdef CONFIG_USBDEV_DUALSPEED
#    define CONFIG_USBSTRG_BULKINREQLEN 512
#  else
#    define CONFIG_USBSTRG_BULKINREQLEN 64
#  endif
#else
#  ifdef CONFIG_USBDEV_DUALSPEED
#    if CONFIG_USBSTRG_BULKINREQLEN < 512
#      warning "Bulk in buffer size smaller than max packet"
#      undef CONFIG_USBSTRG_BULKINREQLEN
#      define CONFIG_USBSTRG_BULKINREQLEN 512
#    endif
#  else
#    if CONFIG_USBSTRG_BULKINREQLEN < 64
#      warning "Bulk in buffer size smaller than max packet"
#      undef CONFIG_USBSTRG_BULKINREQLEN
#      define CONFIG_USBSTRG_BULKINREQLEN 64
#    endif
#  endif
#endif

#ifndef CONFIG_USBSTRG_BULKOUTREQLEN
#  ifdef CONFIG_USBDEV_DUALSPEED
#    define CONFIG_USBSTRG_BULKOUTREQLEN 512
#  else
#    define CONFIG_USBSTRG_BULKOUTREQLEN 64
#  endif
#else
#  ifdef CONFIG_USBDEV_DUALSPEED
#    if CONFIG_USBSTRG_BULKOUTREQLEN < 512
#      warning "Bulk in buffer size smaller than max packet"
#      undef CONFIG_USBSTRG_BULKOUTREQLEN
#      define CONFIG_USBSTRG_BULKOUTREQLEN 512
#    endif
#  else
#    if CONFIG_USBSTRG_BULKOUTREQLEN < 64
#      warning "Bulk in buffer size smaller than max packet"
#      undef CONFIG_USBSTRG_BULKOUTREQLEN
#      define CONFIG_USBSTRG_BULKOUTREQLEN 64
#    endif
#  endif
#endif

/* Vendor and product IDs and strings */

#ifndef CONFIG_USBSTRG_VENDORID
#  warning "CONFIG_USBSTRG_VENDORID not defined"
#  define CONFIG_USBSTRG_VENDORID 0x584e
#endif

#ifndef CONFIG_USBSTRG_PRODUCTID
#  warning "CONFIG_USBSTRG_PRODUCTID not defined"
#  define CONFIG_USBSTRG_PRODUCTID 0x5342
#endif

#ifndef CONFIG_USBSTRG_VERSIONNO
#  define CONFIG_USBSTRG_VERSIONNO (0x0399)
#endif

#ifndef CONFIG_USBSTRG_VENDORSTR
#  warning "No Vendor string specified"
#  define CONFIG_USBSTRG_VENDORSTR  "NuttX"
#endif

#ifndef CONFIG_USBSTRG_PRODUCTSTR
#  warning "No Product string specified"
#  define CONFIG_USBSTRG_PRODUCTSTR "USBdev Storage"
#endif

#undef CONFIG_USBSTRG_SERIALSTR
#define CONFIG_USBSTRG_SERIALSTR "0101"

#undef CONFIG_USBSTRG_CONFIGSTR
#define CONFIG_USBSTRG_CONFIGSTR "Bulk"

/* Debug -- must be consistent with include/debug.h */

#ifdef CONFIG_CPP_HAVE_VARARGS
#  ifdef CONFIG_DEBUG
#    ifdef CONFIG_ARCH_LOWPUTC
#      define dbgprintf(format, arg...) lib_lowprintf(format, ##arg)
#    else
#      define dbgprintf(format, arg...) lib_rawprintf(format, ##arg)
#     endif
#  else
#      define dbgprintf(x...)
#  endif
#else
#  ifdef CONFIG_DEBUG
#    ifdef CONFIG_ARCH_LOWPUTC
#      define dbgprintf lib_lowprintf
#    else
#      define dbgprintf lib_rawprintf
#     endif
#  else
#      define dbgprintf (void)
#  endif
#endif

/* Packet and request buffer sizes */

#ifndef CONFIG_USBSTRG_EP0MAXPACKET
#  define CONFIG_USBSTRG_EP0MAXPACKET 64
#endif

/* USB Controller */

#ifndef CONFIG_USBDEV_SELFPOWERED
#  define SELFPOWERED USB_CONFIG_ATT_SELFPOWER
#else
#  define SELFPOWERED (0)
#endif

#ifndef  CONFIG_USBDEV_REMOTEWAKEUP
#  define REMOTEWAKEUP USB_CONFIG_ATTR_WAKEUP
#else
#  define REMOTEWAKEUP (0)
#endif

#ifndef CONFIG_USBDEV_MAXPOWER
#  define CONFIG_USBDEV_MAXPOWER 100
#endif

/* Current state of the worker thread */

#define USBSTRG_STATE_NOTSTARTED       (0)  /* Thread has not yet been started */
#define USBSTRG_STATE_STARTED          (1)  /* Started, but is not yet initialized */
#define USBSTRG_STATE_IDLE             (2)  /* Started and waiting for commands */
#define USBSTRG_STATE_CMDPARSE         (3)  /* Processing a received command */
#define USBSTRG_STATE_CMDREAD          (4)  /* Processing a SCSI read command */
#define USBSTRG_STATE_CMDWRITE         (5)  /* Processing a SCSI write command */
#define USBSTRG_STATE_CMDFINISH        (6)  /* Finish command processing */
#define USBSTRG_STATE_CMDSTATUS        (7)  /* Processing the final status of the command */
#define USBSTRG_STATE_TERMINATED       (8)  /* Thread has exitted */

/* Event communicated to worker thread */

#define USBSTRG_EVENT_NOEVENTS         (0)      /* There are no outstanding events */
#define USBSTRG_EVENT_READY            (1 << 0) /* Initialization is complete */
#define USBSTRG_EVENT_RDCOMPLETE       (1 << 1) /* A read has completed there is data to be processed */
#define USBSTRG_EVENT_WRCOMPLETE       (1 << 2) /* A write has completed and a request is available */
#define USBSTRG_EVENT_TERMINATEREQUEST (1 << 3) /* Shutdown requested */
#define USBSTRG_EVENT_DISCONNECT       (1 << 4) /* USB disconnect received */
#define USBSTRG_EVENT_RESET            (1 << 5) /* USB storage setup reset received */
#define USBSTRG_EVENT_CFGCHANGE        (1 << 6) /* USB setup configuration change received */
#define USBSTRG_EVENT_IFCHANGE         (1 << 7) /* USB setup interface change received */
#define USBSTRG_EVENT_ABORTBULKOUT     (1 << 8) /* SCSI receive failure */

/* SCSI command flags (passed to usbstrg_setupcmd()) */

#define USBSTRG_FLAGS_DIRMASK          (0x03) /* Bits 0-1: Data direction */
#define USBSTRG_FLAGS_DIRNONE          (0x00) /*   No data to send */
#define USBSTRG_FLAGS_DIRHOST2DEVICE   (0x01) /*   Host-to-device */
#define USBSTRG_FLAGS_DIRDEVICE2HOST   (0x02) /*   Device-to-host */
#define USBSTRG_FLAGS_BLOCKXFR         (0x04) /* Bit 2: Command is a block transfer request */
#define USBSTRG_FLAGS_LUNNOTNEEDED     (0x08) /* Bit 3: Command does not require a valid LUN */
#define USBSTRG_FLAGS_UACOKAY          (0x10) /* Bit 4: Command OK if unit attention condition */
#define USBSTRG_FLAGS_RETAINSENSEDATA  (0x20) /* Bit 5: Do not clear sense data */

/* Descriptors **************************************************************/

/* Big enough to hold our biggest descriptor */

#define USBSTRG_MXDESCLEN              (64)

/* String language */

#define USBSTRG_STR_LANGUAGE           (0x0409) /* en-us */

/* Descriptor strings */

#define USBSTRG_MANUFACTURERSTRID      (1)
#define USBSTRG_PRODUCTSTRID           (2)
#define USBSTRG_SERIALSTRID            (3)
#define USBSTRG_CONFIGSTRID            (4)
#define USBSTRG_NCONFIGS               (1) /* Number of configurations supported */

/* Configuration Descriptor */

#define USBSTRG_NINTERFACES            (1) /* Number of interfaces in the configuration */
#define USBSTRG_INTERFACEID            (0)
#define USBSTRG_ALTINTERFACEID         (0)
#define USBSTRG_CONFIGIDNONE           (0) /* Config ID means to return to address mode */
#define USBSTRG_CONFIGID               (1) /* The only supported configuration ID */

#define STRING_MANUFACTURER            (1)
#define STRING_PRODUCT                 (2)
#define STRING_SERIAL                  (3)

#define USBSTRG_CONFIGID               (1) /* There is only one configuration. */

/* Interface description */

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

/* Endpoint configuration */

#define USBSTRG_EPOUTBULK_ADDR         (CONFIG_USBSTRG_EPBULKOUT)
#define USBSTRG_EPOUTBULK_ATTR         (USB_EP_ATTR_XFER_BULK)

#define USBSTRG_EPINBULK_ADDR          (USB_DIR_IN|CONFIG_USBSTRG_EPBULKIN)
#define USBSTRG_EPINBULK_ATTR          (USB_EP_ATTR_XFER_BULK)

#define USBSTRG_HSBULKMAXPACKET        (512)
#define USBSTRG_HSBULKMXPKTSHIFT       (9)
#define USBSTRG_HSBULKMXPKTMASK        (0x000001ff)
#define USBSTRG_FSBULKMAXPACKET        (64)
#define USBSTRG_FSBULKMXPKTSHIFT       (6)
#define USBSTRG_FSBULKMXPKTMASK        (0x0000003f)

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

#ifdef  CONFIG_USBDEV_DUALSPEED
#  define USBSTRG_EPBULKINDESC(hs)  ((hs) ? (g_hsepbulkindesc) : (g_fsepbulkindesc))
#  define USBSTRG_EPBULKOUTDESC(hs) ((hs) ? (g_hsepbulkoutdesc) : (g_fsepbulkoutdesc))
#  define USBSTRG_BULKMAXPACKET(hs) \
   ((hs) ? USBSTRG_HSBULKMAXPACKET : USBSTRG_FSBULKMAXPACKET)
#  define USBSTRG_BULKMXPKTSHIFT(d) \
   (((d)->speed==USB_SPEED_HIGH) ? USBSTRG_HSBULKMXPKTSHIFT : USBSTRG_FSBULKMXPKTSHIFT)
#  define USBSTRG_BULKMXPKTMASK(d) \
   (((d)->speed==USB_SPEED_HIGH) ? USBSTRG_HSBULKMXPKTMASK : USBSTRG_FSBULKMXPKTMASK)
#else
#  define USBSTRG_EPBULKINDESC(d)   (g_fsepbulkindesc))
#  define USBSTRG_EPBULKOUTDESC(d)  (g_fsepbulkoutdesc))
#  define USBSTRG_BULKMAXPACKET(hs)  USBSTRG_FSBULKMAXPACKET
#  define USBSTRG_BULKMXPKTSHIFT(d)  USBSTRG_FSBULKMXPKTSHIFT
#  define USBSTRG_BULKMXPKTMASK(d)   USBSTRG_FSBULKMXPKTMASK
#endif

/* Block driver helpers *****************************************************/

#define USBSTRG_DRVR_READ(l,b,s,n) ((l)->inode->u.i_bops->read((l)->inode,b,s,n))
#define USBSTRG_DRVR_WRITE(l,b,s,n) ((l)->inode->u.i_bops->write((l)->inode,b,s,n))
#define USBSTRG_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
 ****************************************************************************/

/* Container to support a list of requests */

struct usbstrg_req_s
{
  FAR struct usbstrg_req_s *flink;    /* Implements a singly linked list */
  FAR struct usbdev_req_s *req;       /* The contained request */
};

/* This structure describes one LUN: */

struct usbstrg_lun_s
{
  struct inode    *inode;             /* Inode structure of open'ed block driver */
  ubyte            readonly:1;        /* Media is read-only */
  ubyte            locked:1;          /* Media removal is prevented */
  uint16           sectorsize;        /* The size of one sector */
  uint32           sd;                /* Sense data */
  uint32           sdinfo;            /* Sense data information */
  uint32           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 the driver */

struct usbstrg_dev_s
{
  FAR struct usbdev_s     *usbdev;    /* usbdev driver pointer (Non-null if registered) */

  /* Worker thread interface */

  pthread_t        thread;            /* The worker thread */
  pthread_mutex_t  mutex;             /* Mutually exclusive access to resources*/
  pthread_cond_t   cond;              /* Used to signal worker thread */
  volatile ubyte   thstate;           /* State of the worker thread */
  volatile uint16  theventset;        /* Set of pending events signaled to worker thread */
  volatile ubyte   thvalue;           /* Value passed with the event (must persist) */

  /* Storage class configuration and state */

  ubyte            nluns:4;           /* Number of LUNs */
  ubyte            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 usbstrg_lun_s    *lun;       /* Currently selected LUN */
  struct usbstrg_lun_s    *luntab;    /* Allocated table of all LUNs */
  ubyte cdb[USBSTRG_MAXCDBLEN];       /* Command data (cdb[]) from CBW */
  ubyte            phaseerror:1;      /* Need to send phase sensing status */
  ubyte            shortpacket:1;     /* Host transmission stopped unexpectedly */
  ubyte            cbwdir:2;          /* Direction from CBW. See USBSTRG_FLAGS_DIR* definitions */
  ubyte            cdblen;            /* Length of cdb[] from CBW */
  ubyte            cbwlun;            /* LUN from the CBW */
  uint16           nsectbytes;        /* Bytes buffered in iobuffer[] */
  uint16           nreqbytes;         /* Bytes buffered in head write requests */
  uint16           iosize;            /* Size of iobuffer[] */
  uint32           cbwlen;            /* Length of data from CBW */
  uint32           cbwtag;            /* Tag from the CBW */
  union
    {
      uint32       xfrlen;            /* Read/Write: Sectors remaining to be transferred */
      uint32       alloclen;          /* Other device-to-host: Host allocation length */
    } u;
  uint32           sector;            /* Current sector (relative to lun->startsector) */
  uint32           residue;           /* Untransferred amount reported in the CSW */
  ubyte           *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 usbstrg_req_s    wrreqs[CONFIG_USBSTRG_NWRREQS];
  struct usbstrg_req_s    rdreqs[CONFIG_USBSTRG_NRDREQS];
};

/****************************************************************************
 * Public Data
 ****************************************************************************/

#undef EXTERN
#if defined(__cplusplus)
#  define EXTERN extern "C"
extern "C"
{
#else
#  define EXTERN extern
#endif

/* String *******************************************************************/

EXTERN const char g_vendorstr[];
EXTERN const char g_productstr[];
EXTERN const char g_serialstr[];

/************************************************************************************
 * Public Function Prototypes
 ************************************************************************************/

/****************************************************************************
 * Name: usbstrg_workerthread
 *
 * Description:
 *   This is the main function of the USB storage worker thread.  It loops
 *   until USB-related events occur, then processes those events accordingly
 *
 ****************************************************************************/

EXTERN void *usbstrg_workerthread(void *arg);

/****************************************************************************
 * Name: usbstrg_setconfig
 *
 * Description:
 *   Set the device configuration by allocating and configuring endpoints and
 *   by allocating and queue read and write requests.
 *
 ****************************************************************************/

EXTERN int usbstrg_setconfig(FAR struct usbstrg_dev_s *priv, ubyte config);

/****************************************************************************
 * Name: usbstrg_resetconfig
 *
 * Description:
 *   Mark the device as not configured and disable all endpoints.
 *
 ****************************************************************************/

EXTERN void usbstrg_resetconfig(FAR struct usbstrg_dev_s *priv);

/****************************************************************************
 * Name: usbstrg_wrcomplete
 *
 * Description:
 *   Handle completion of write request.  This function probably executes
 *   in the context of an interrupt handler.
 *
 ****************************************************************************/

EXTERN void usbstrg_wrcomplete(FAR struct usbdev_ep_s *ep,
                               FAR struct usbdev_req_s *req);

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

EXTERN void usbstrg_rdcomplete(FAR struct usbdev_ep_s *ep,
                               FAR struct usbdev_req_s *req);

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

EXTERN void usbstrg_deferredresponse(FAR struct usbstrg_dev_s *priv, boolean failed);

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

#endif /* #define __DRIVERS_USBDEV_USBDEV_STORAGE_H */