Extend USB host mass storage class

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@3183 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2010-12-15 22:28:35 +00:00
parent 134c2e4fe2
commit bfb879efc0
2 changed files with 423 additions and 48 deletions

View File

@ -45,6 +45,7 @@
#include <nuttx/fs.h>
#include <nuttx/arch.h>
#include <nuttx/wqueue.h>
#include <nuttx/usb/usb.h>
#include <nuttx/usb/usbhost.h>
@ -54,6 +55,10 @@
/* Configuration ************************************************************/
#ifndef CONFIG_SCHED_WORKQUEUE
# error "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)"
#endif
/* If the create() method is called by the USB host device driver from an
* interrupt handler, then it will be unable to call malloc() in order to
* allocate a new class instance. If the create() method is called from the
@ -64,11 +69,22 @@
# define CONFIG_USBHOST_NPREALLOC 0
#endif
#if CONFIG_USBHOST_NPREALLOC > 26
# error "Currently limited to 26 devices /dev/sda-z"
#endif
/* This format is used to construct the /dev/sd[n] device driver path. It
* defined here so that it will be used consistently in all places.
*/
#define DEV_FORMAT "/dev/sd%c"
#define DEV_NAMELEN 10
/****************************************************************************
* Private Types
****************************************************************************/
/* This structur contains the internal, private state of the USB host mass
/* This structure contains the internal, private state of the USB host mass
* storage class.
*/
@ -84,18 +100,40 @@ struct usbhost_state_s
/* The remainder of the fields are provide o the mass storage class */
int crefs; /* Reference count on the driver instance */
int16_t crefs; /* Reference count on the driver instance */
char sdchar; /* Character identifying the /dev/sd[n] device */
uint16_t blocksize; /* Block size of USB mass storage device */
uint32_t nblocks; /* Number of blocks on the USB mass storage device */
struct work_s work; /* For interacting with the worker thread */
struct usbhost_epdesc_s bulkin; /* Bulk IN endpoint */
struct usbhost_epdesc_s bulkout; /* Bulk OUT endpoint */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Memory allocation services */
static inline struct usbhost_state_s *usbhost_allocclass(void);
static inline usbhost_freeclass(struct usbhost_state_s *class);
/* Device name management */
static int usbhost_allocdevno(FAR struct usbhost_state_s *priv);
static void usbhost_freedevno(FAR struct usbhost_state_s *priv);
static inline void usbhost_mkdevname(FAR struct usbhost_state_s *priv, char *devname);
/* Worker thread actions */
static void usbhost_destroy(FAR void *arg);
/* Data helpers */
static inline uint16_t usbhost_getint16(const uint8_t *val);
/* struct usbhost_registry_s methods */
static inline struct usbhost_state_s *usbhost_allocclass(void);
static struct usbhost_class_s *usbhost_create(FAR struct usbhost_driver_s *drvr,
FAR const struct usbhost_id_s *id);
@ -116,10 +154,10 @@ static ssize_t usbhost_write(FAR struct inode *inode,
FAR const unsigned char *buffer, size_t startsector,
unsigned int nsectors);
#endif
static int usbhost_geometry(FAR struct inode *inode,
FAR struct geometry *geometry);
static int usbhost_ioctl(FAR struct inode *inode, int cmd,
unsigned long arg);
static int usbhost_geometry(FAR struct inode *inode,
FAR struct geometry *geometry);
static int usbhost_ioctl(FAR struct inode *inode, int cmd,
unsigned long arg);
/****************************************************************************
* Private Data
@ -179,19 +217,19 @@ static struct usbhost_state_s g_prealloc[CONFIG_USBHOST_NPREALLOC];
static struct usbhost_state_s *g_freelist;
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/* This is a bitmap that is used to allocate device names /dev/sda-z. */
static uint32_t g_devinuse;
/****************************************************************************
* struct usbhost_registry_s methods
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: usbhost_allocclass
*
* Description:
* This is really part of the logic that implementes the create() method
* This is really part of the logic that implements the create() method
* of struct usbhost_registry_s. This function allocates memory for one
* new class instance.
*
@ -206,32 +244,186 @@ static struct usbhost_state_s *g_freelist;
*
****************************************************************************/
#if CONFIG_USBHOST_NPREALLOC > 0
static inline struct usbhost_state_s *usbhost_allocclass(void)
{
struct usbhost_state_s *priv;
irqstate_t flags;
#if CONFIG_USBHOST_NPREALLOC > 0
/* We are executing from an interrupt handler so we need to take one of our
* pre-allocated class instances from the free list. No special protection
* is needed if we are in an interrupt handler.
/* We may be executing from an interrupt handler so we need to take one of
* our pre-allocated class instances from the free list.
*/
flags = irqsave();
priv = g_freelist;
if (priv)
{
g_freelist = priv->class.flink;
priv->class.flink = NULL;
}
irqrestore(flags);
return priv;
}
#else
static inline struct usbhost_state_s *usbhost_allocclass(void)
{
/* We are not executing from an interrupt handler so we can just call
* malloc() to get memory for the class instance.
*/
DEBUGASSERT(!up_interrupt_context());
priv = (struct usbhost_state_s *)malloc(sizeof(struct usbhost_state_s));
#endif
return priv;
return (struct usbhost_state_s *)malloc(sizeof(struct usbhost_state_s));
}
#endif
/****************************************************************************
* Name: usbhost_freeclass
*
* Description:
* Free a class instance previously allocated by usbhost_allocclass().
*
* Input Parameters:
* class - A reference to the class instance to be freed.
*
* Returned Values:
* None
*
****************************************************************************/
#if CONFIG_USBHOST_NPREALLOC > 0
static inline usbhost_freeclass(struct usbhost_state_s *class)
{
irqstate_t flags;
DEBUGASSERT(class != NULL);
/* Just put the pre-allocated class structure back on the freelist */
flags = irqsave();
class->class.flink = g_freelist;
g_freelist = class;
irqrestore(flags);
}
#else
static inline usbhost_freeclass(struct usbhost_state_s *class)
{
DEBUGASSERT(class != NULL);
/* Free the class instance (calling sched_free() in case we are executing
* from an interrupt handler.
*/
free(class);
}
#endif
/****************************************************************************
* Name: Device name management
*
* Description:
* Some tiny functions to coordinate management of mass storage device names.
*
****************************************************************************/
static int usbhost_allocdevno(FAR struct usbhost_state_s *priv)
{
irqstate_t flags;
int devno;
flags = irqsave();
for (devno = 0; devno < 26; devno++)
{
uint32_t bitno = 1 << devno;
if ((g_devinuse & bitno) == 0)
{
g_devinuse |= bitno;
priv->sdchar = 'a' + devno;
irqrestore(flags);
return OK;
}
}
irqrestore(flags);
return -EMFILE;
}
static void usbhost_freedevno(FAR struct usbhost_state_s *priv)
{
int devno = 'a' - priv->sdchar;
if (devno >= 0 && devno < 26)
{
irqstate_t flags = irqsave();
g_devinuse &= ~(1 << devno);
irqrestore(flags);
}
}
static inline void usbhost_mkdevname(FAR struct usbhost_state_s *priv, char *devname);
{
(void)snprintf(devname, DEV_NAMELEN, DEV_FORMAT, priv->sdchar);
}
/****************************************************************************
* Name: usbhost_destroy
*
* Description:
* The USB mass storage device has been disconnected and the refernce count
* on the USB host class instance has gone to 1.. Time to destroy the USB
* host class instance.
*
* Input Parameters:
* arg - A reference to the class instance to be freed.
*
* Returned Values:
* None
*
****************************************************************************/
static void usbhost_destroy(FAR void *arg)
{
FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)arg;
char devname[DEV_NAMELEN];
DEBUGASSERT(priv != NULL);
/* Unregister the block driver */
usbhost_mkdevname(priv, devname);
(void)unregister_blockdriver(devname);
/* Release the device name used by this connection */
usbhost_freedevno(priv);
/* And free the class instance. Hmmm.. this may execute on the worker
* thread and the work structure is part of what is getting freed.
*/
usbhost_freeclass(priv);
}
/****************************************************************************
* Name: usbhost_getint16
*
* Description:
* Get a (possibly unaligned) 16-bit little endian value.
*
* Input Parameters:
* val - A pointer to the first byte of the little endian value.
*
* Returned Values:
* A uin16_t representing the whole 16-bit integer value
*
****************************************************************************/
static inline uint16_t usbhost_getint16(const uint8_t *val)
{
return (uint16_t)val[1] << 8 + (uint16_t)val[0];
}
/****************************************************************************
* struct usbhost_registry_s methods
****************************************************************************/
/****************************************************************************
* Name: usbhost_create
@ -273,23 +465,33 @@ static FAR struct usbhost_class_s *usbhost_create(FAR struct usbhost_driver_s *d
/* Initialize the allocated storage class instance */
memset(priv, 0, sizeof(struct usbhost_state_s);
priv->class.configdesc = usbhost_configdesc;
priv->class.disconnected = usbhost_disconnected;
priv->crefs = 1;
/* Bind the driver to the storage class instance */
/* Assign a device number to this class instance */
priv->drvr = drvr;
if (usbhost_allocdevno(priv) == OK)
{
priv->class.configdesc = usbhost_configdesc;
priv->class.disconnected = usbhost_disconnected;
priv->crefs = 1;
/* NOTE: We do not yet know the geometry of the USB mass storage device */
/* Return the instance of the USB mass storage class */
/* Bind the driver to the storage class instance */
priv->drvr = drvr;
/* NOTE: We do not yet know the geometry of the USB mass storage device */
return &priv->class;
/* Return the instance of the USB mass storage class */
return &priv->class;
}
}
/* Return NULL on all failures */
/* An error occurred. Free the allocation and return NULL on all failures */
if (priv)
{
usbhost_free(priv);
}
return NULL;
}
@ -316,11 +518,142 @@ static FAR struct usbhost_class_s *usbhost_create(FAR struct usbhost_driver_s *d
*
****************************************************************************/
static int usbhost_configdesc(struct usbhost_class_s *class,
const uint8_t *configdesc, int desclen)
static int usbhost_configdesc(FAR struct usbhost_class_s *class,
FAR const uint8_t *configdesc, int desclen)
{
#warning "Missing Implementation"
return -ENOSYS;
FAR struct usb_cfgdesc_s *cfgdesc;
FAR struct usb_desc_s *desc;
int remaining;
bool iffound = false;
bool boutfound = false;
bool binfound = false;
/* Verify that we were passed a configuration descriptor */
cfgdesc = (FAR struct usb_cfgdesc_s *)configdesc;
if (desc->type != USB_DESC_TYPE_CONFIG)
{
return -EINVAL;
}
/* Get the total length of the configuration descriptor (little endian).
* It might be a good check to get the number of interfaces here too.
*/
remaining = (int)usbhost_getint16(cfgdesc->totallen);
/* Skip to the next entry descriptor */
configdesc += desc->len;
remaining -= desc->len;
/* Loop where there are more dscriptors to examine */
while (remaining >= sizeof(struct usb_desc_s))
{
/* What is the next descriptor? */
desc = (FAR struct usb_desc_s *)configdesc;
switch (desc->type)
{
/* Interface descriptor. We really should get the number of endpoints
* from this descriptor too.
*/
case USB_DESC_TYPE_INTERFACE:
{
DEBUGASSERT(remaining >= sizeof(struct usb_ifdesc_s));
if (iffound)
{
/* Oops.. more than one interface. We don't know what to do with this. */
return -ENOSYS;
}
iffound = true;
}
break;
/* Endpoint descriptor. We expect two bulk endpoints, an IN and an OUT */
case USB_DESC_TYPE_ENDPOINT:
{
FAR struct usb_epdesc_s *epdesc = (FAR struct usb_epdesc_s *)configdesc;
DEBUGASSERT(remaining >= sizeof(struct usb_epdesc_s));
/* Check for a bulk endpoint. We only support the bulk-only
* protocol so I suppose anything else should really be an error.
*/
if ((epdesc->attr & USB_EP_ATTR_XFERTYPE_MASK) == USB_EP_ATTR_XFER_BULK)
{
/* Yes.. it is a bulk endpoint. IN or OUT? */
if (USB_ISEPOUT(epdesc->addr))
{
/* It is an OUT bulk endpoint. There should be only one bulk OUT endpoint. */
if (boutfound)
{
/* Oops.. more than one interface. We don't know what to do with this. */
return -EINVAL;
}
boutfound = true;
/* Save the bulk OUT endpoint information */
priv->bulkout.addr = epdesc->addr & USB_EP_ADDR_NUMBER_MASK;
priv->bulkout.in = false;
priv->bulkout.mxpacketsize = usbhost_getint16(pdesc->mxpacketsize);
}
else
{
/* It is an IN bulk endpoint. There should be only one bulk IN endpoint. */
if (binfound)
{
/* Oops.. more than one interface. We don't know what to do with this. */
return -EINVAL;
}
binfound = true;
/* Save the bulk IN endpoint information */
priv->bulkin.addr = epdesc->addr & USB_EP_ADDR_NUMBER_MASK;
priv->bulkin.in = true;
priv->bulkin.mxpacketsize = usbhost_getint16(pdesc->mxpacketsize);
}
}
}
break;
/* Other descriptors are just ignored for now */
default:
break;
}
/* Increment the address of the next descriptor */
configdesc += desc->len;
remaining -= desc->len;
}
/* Sanity checking... did we find all of things that we need? Hmmm.. I wonder..
* can we work read-only or write-only if only one bulk endpoint found?
*/
if (!iffound || !binfound || !boutfound)
{
ulldbg("ERROR: Found IF:%s BIN:%s BOUT:%s\n",
iffound ? "YES" : "NO",
binfound ? "YES" : "NO",
outfound ? "YES" : "NO");
return -EINVAL;
}
ullvdbg("Mass Storage device connected\n");
return OK;
}
/****************************************************************************
@ -345,12 +678,15 @@ static int usbhost_configdesc(struct usbhost_class_s *class,
static int usbhost_disconnected(struct usbhost_class_s *class)
{
FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)class;
irqstate_t flags;
DEBUGASSERT(priv != NULL);
/* Nullify the driver instance. This will be our indication to any users
* of the mass storage device that the device is no longer available.
*/
flags = irqsave();
priv->drvr = NULL;
/* Now check the number of references on the class instance. If it is one,
@ -359,12 +695,25 @@ static int usbhost_disconnected(struct usbhost_class_s *class)
* block driver.
*/
#warning "Missing Implementation"
if (priv->crefs == 1)
{
/* Destroy the class instance */
/* Are we in an interrupt handler? */
/* Unregister the block device */
if (up_interrupt_context())
{
/* Yes.. do the destruction on the worker thread */
#warning "Missing Implementation"
return -ENOSYS;
(void)work_queue(&priv->work, usbhost_destroy, priv, 0);
}
else
{
/* No.. destroy the class now */
usbhost_destroy(priv);
}
irqrestore(flags);
return OK;
}
/****************************************************************************
@ -391,7 +740,8 @@ static int usbhost_open(FAR struct inode *inode)
if (!priv->drvr)
{
/* No... the block driver is no longer bound to the class. That means that
* the USB storage device is no longer connected.
* the USB storage device is no longer connected. Refuse any further
* attempts to open the driver.
*/
ret = -ENODEV;
@ -431,14 +781,25 @@ static int usbhost_close(FAR struct inode *inode)
usbhost_takesem(priv);
priv->crefs--;
/* Release the semaphore. The following operations when crefs == 1 are
* safe because we know that there is no outstanding open references to
* the block driver.
*/
usbhost_givesem(priv);
/* Check if the USB mass storage device is still connected. If the
* storage device is not connected and the reference count just
* decremented to one, then unregister the block driver and free
* the class instance.
*/
#warning "Missing Implementation"
usbhost_givesem(priv);
if (priv->crefs <= 1 && priv->drvr == NULL)
{
/* Destroy the class instance */
usbhost_destroy(priv);
}
return OK;
}
@ -467,7 +828,8 @@ static ssize_t usbhost_read(FAR struct inode *inode, unsigned char *buffer,
if (!priv->drvr)
{
/* No... the block driver is no longer bound to the class. That means that
* the USB storage device is no longer connected.
* the USB storage device is no longer connected. Refuse any attempt to read
* from the device.
*/
ret = -ENODEV;
@ -509,7 +871,8 @@ static ssize_t usbhost_write(FAR struct inode *inode, const unsigned char *buffe
if (!priv->drvr)
{
/* No... the block driver is no longer bound to the class. That means that
* the USB storage device is no longer connected.
* the USB storage device is no longer connected. Refuse any attempt to
* write to the device.
*/
ret = -ENODEV;
@ -547,7 +910,8 @@ static int usbhost_geometry(FAR struct inode *inode, struct geometry *geometry)
if (!priv->drvr)
{
/* No... the block driver is no longer bound to the class. That means that
* the USB storage device is no longer connected.
* the USB storage device is no longer connected. Refuse to return any
* geometry info.
*/
ret = -ENODEV;
@ -600,7 +964,8 @@ static int usbhost_ioctl(FAR struct inode *inode, int cmd, unsigned long arg)
if (!priv->drvr)
{
/* No... the block driver is no longer bound to the class. That means that
* the USB storage device is no longer connected.
* the USB storage device is no longer connected. Refuse to process any
* ioctl commands.
*/
ret = -ENODEV;

View File

@ -200,19 +200,29 @@ struct usbhost_class_s
* class implementation.
*/
struct usbhost_epdesc_s;
struct usbhost_driver_s
{
/* Receive a process a transfer descriptor */
int (*transfer)();
int (*transfer)(FAR struct usbhost_epdesc_s *ed);
/* Enumerate the connected device */
int (*enumerate)();
int (*enumerate)(FAR struct usbhost_epdesc_s *ed);
/* Receive control information */
int (*rcvctrl)();
int (*rcvctrl)(FAR struct usbhost_epdesc_s *ed);
};
/* This structure describes one endpoint */
struct usbhost_epdesc_s
{
uint8_t addr; /* Endpoint address */
bool in; /* Direction: TRUE = IN */
uint16_t mxpacketsize; /* Max packetsize */
};
/************************************************************************************