From 0a2f7d9abbd176e77c58c316a8e7bb59f531188e Mon Sep 17 00:00:00 2001 From: patacongo Date: Wed, 15 Dec 2010 18:39:19 +0000 Subject: Add disconnected method git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@3182 42af7a65-404d-4744-a932-0658087f49c3 --- nuttx/drivers/usbhost/usbhost_storage.c | 285 ++++++++++++++++++++++++++++---- nuttx/include/nuttx/usb/usbhost.h | 41 ++++- 2 files changed, 283 insertions(+), 43 deletions(-) diff --git a/nuttx/drivers/usbhost/usbhost_storage.c b/nuttx/drivers/usbhost/usbhost_storage.c index b7f14ea1a..dd3d44a9c 100644 --- a/nuttx/drivers/usbhost/usbhost_storage.c +++ b/nuttx/drivers/usbhost/usbhost_storage.c @@ -44,6 +44,7 @@ #include #include +#include #include #include @@ -51,6 +52,18 @@ * Pre-processor Definitions ****************************************************************************/ +/* Configuration ************************************************************/ + +/* 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 + * interrupt level, then class instances must be pre-allocated. + */ + +#ifndef CONFIG_USBHOST_NPREALLOC +# define CONFIG_USBHOST_NPREALLOC 0 +#endif + /**************************************************************************** * Private Types ****************************************************************************/ @@ -82,23 +95,26 @@ struct usbhost_state_s /* struct usbhost_registry_s methods */ -static struct usbhost_class_s *usbhost_create(struct usbhost_driver_s *drvr); +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); /* struct usbhost_class_s methods */ -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); +static int usbhost_disconnected(FAR struct usbhost_class_s *class); /* struct block_operations methods */ -static int usbhost_open(FAR struct inode *inode); -static int usbhost_close(FAR struct inode *inode); +static int usbhost_open(FAR struct inode *inode); +static int usbhost_close(FAR struct inode *inode); static ssize_t usbhost_read(FAR struct inode *inode, FAR unsigned char *buffer, - size_t startsector, unsigned int nsectors); + size_t startsector, unsigned int nsectors); #ifdef CONFIG_FS_WRITABLE static ssize_t usbhost_write(FAR struct inode *inode, - FAR const unsigned char *buffer, size_t startsector, - unsigned int nsectors); + FAR const unsigned char *buffer, size_t startsector, + unsigned int nsectors); #endif static int usbhost_geometry(FAR struct inode *inode, FAR struct geometry *geometry); @@ -109,6 +125,11 @@ static int usbhost_ioctl(FAR struct inode *inode, int cmd, * Private Data ****************************************************************************/ +/* This structure provides the registry entry ID informatino that will be + * used to associate the USB host mass storage class to a connected USB + * device. + */ + static const const struct usbhost_id_s g_id = { USB_CLASS_MASS_STORAGE, /* base */ @@ -118,6 +139,8 @@ static const const struct usbhost_id_s g_id = 0 /* pid */ }; +/* This is the USB host storage class's registry entry */ + static struct usbhost_registry_s g_storage = { NULL, /* flink */ @@ -126,6 +149,10 @@ static struct usbhost_registry_s g_storage = &g_id /* id[] */ }; +/* Block driver operations. This is the interface exposed to NuttX by the + * class that permits it to behave like a block driver. + */ + static const struct block_operations g_bops = { usbhost_open, /* open */ @@ -140,6 +167,18 @@ static const struct block_operations g_bops = usbhost_ioctl /* ioctl */ }; +/* This is an array of pre-allocated USB host storage class instances */ + +#if CONFIG_USBHOST_NPREALLOC > 0 +static struct usbhost_state_s g_prealloc[CONFIG_USBHOST_NPREALLOC]; +#endif + +/* This is a list of free, pre-allocated USB host storage class instances */ + +#if CONFIG_USBHOST_NPREALLOC > 0 +static struct usbhost_state_s *g_freelist; +#endif + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -148,6 +187,52 @@ static const struct block_operations g_bops = * struct usbhost_registry_s methods ****************************************************************************/ +/**************************************************************************** + * Name: usbhost_allocclass + * + * Description: + * This is really part of the logic that implementes the create() method + * of struct usbhost_registry_s. This function allocates memory for one + * new class instance. + * + * Input Parameters: + * None + * + * Returned Values: + * On success, this function will return a non-NULL instance of struct + * usbhost_class_s. NULL is returned on failure; this function will + * will fail only if there are insufficient resources to create another + * USB host class instance. + * + ****************************************************************************/ + +static inline struct usbhost_state_s *usbhost_allocclass(void) +{ + struct usbhost_state_s *priv; + +#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. + */ + + priv = g_freelist; + if (priv) + { + g_freelist = priv->class.flink; + priv->class.flink = NULL; + } +#else + /* 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; +} + /**************************************************************************** * Name: usbhost_create * @@ -175,25 +260,26 @@ static const struct block_operations g_bops = * ****************************************************************************/ -static struct usbhost_class_s *usbhost_create(struct usbhost_driver_s *drvr, - const struct usbhost_id_s *id) +static FAR struct usbhost_class_s *usbhost_create(FAR struct usbhost_driver_s *drvr, + FAR const struct usbhost_id_s *id) { - struct usbhost_state_s *priv; + FAR struct usbhost_state_s *priv; /* Allocate a USB host mass storage class instance */ - priv = (struct usbhost_state_s *)malloc(sizeof(struct usbhost_state_s)); + priv = usbhost_allocclass(void); if (priv) { /* Initialize the allocated storage class instance */ memset(priv, 0, sizeof(struct usbhost_state_s); - priv->class.configdesc = usbhost_configdesc; - priv->crefs = 1; + priv->class.configdesc = usbhost_configdesc; + priv->class.disconnected = usbhost_disconnected; + priv->crefs = 1; /* Bind the driver to the storage class instance */ - priv->drvr = drvr; + priv->drvr = drvr; /* NOTE: We do not yet know the geometry of the USB mass storage device */ @@ -237,6 +323,50 @@ static int usbhost_configdesc(struct usbhost_class_s *class, return -ENOSYS; } +/**************************************************************************** + * Name: usbhost_disconnected + * + * Description: + * This function implements the disconnected() method of struct + * usbhost_class_s. This method is a callback into the class + * implementation. It is used to inform the class that the USB device has + * been disconnected. + * + * Input Parameters: + * class - The USB host class entry previously obtained from a call to + * create(). + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + ****************************************************************************/ + +static int usbhost_disconnected(struct usbhost_class_s *class) +{ + FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)class; + 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. + */ + + priv->drvr = NULL; + + /* Now check the number of references on the class instance. If it is one, + * then we can free the class instance now. Otherwise, we will have to + * wait until the holders of the references free them by closing the + * block driver. + */ + +#warning "Missing Implementation" + + /* Unregister the block device */ + +#warning "Missing Implementation" + return -ENOSYS; +} + /**************************************************************************** * struct block_operations methods ****************************************************************************/ @@ -250,18 +380,34 @@ static int usbhost_configdesc(struct usbhost_class_s *class, static int usbhost_open(FAR struct inode *inode) { FAR struct usbhost_state_s *priv; + int ret; uvdbg("Entry\n"); DEBUGASSERT(inode && inode->i_private); priv = (FAR struct usbhost_state_s *)inode->i_private; - /* Just increment the reference count on the driver */ + /* Check if the mass storage device is still connected */ - DEBUGASSERT(priv->crefs < MAX_CREFS); - usbhost_takesem(priv); - priv->crefs++; - usbhost_givesem(priv); - return OK; + 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. + */ + + ret = -ENODEV; + } + else + { + /* Otherwise, just increment the reference count on the driver */ + + DEBUGASSERT(priv->crefs < MAX_CREFS); + usbhost_takesem(priv); + priv->crefs++; + usbhost_givesem(priv); + ret = OK; + } + + return ret; } /**************************************************************************** @@ -284,6 +430,14 @@ static int usbhost_close(FAR struct inode *inode) DEBUGASSERT(priv->crefs > 0); usbhost_takesem(priv); priv->crefs--; + + /* 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); return OK; } @@ -308,7 +462,17 @@ static ssize_t usbhost_read(FAR struct inode *inode, unsigned char *buffer, uvdbg("startsector: %d nsectors: %d sectorsize: %d\n", startsector, nsectors, priv->blocksize); - if (nsectors > 0) + /* Check if the mass storage device is still connected */ + + 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. + */ + + ret = -ENODEV; + } + else if (nsectors > 0) { usbhost_takesem(priv); #warning "Missing logic" @@ -340,9 +504,22 @@ static ssize_t usbhost_write(FAR struct inode *inode, const unsigned char *buffe DEBUGASSERT(inode && inode->i_private); priv = (FAR struct usbhost_state_s *)inode->i_private; - usbhost_takesem(priv); + /* Check if the mass storage device is still connected */ + + 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. + */ + + ret = -ENODEV; + } + else + { + usbhost_takesem(priv); #warning "Missing logic" - usbhost_givesem(priv); + usbhost_givesem(priv); + } /* On success, return the number of blocks written */ @@ -365,7 +542,17 @@ static int usbhost_geometry(FAR struct inode *inode, struct geometry *geometry) uvdbg("Entry\n"); DEBUGASSERT(inode && inode->i_private); - if (geometry) + /* Check if the mass storage device is still connected */ + + 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. + */ + + ret = -ENODEV; + } + else if (geometry) { /* Return the geometry of the USB mass storage device */ @@ -408,19 +595,32 @@ static int usbhost_ioctl(FAR struct inode *inode, int cmd, unsigned long arg) DEBUGASSERT(inode && inode->i_private); priv = (FAR struct usbhost_state_s *)inode->i_private; - /* Process the IOCTL by command */ + /* Check if the mass storage device is still connected */ - usbhost_takesem(priv); - switch (cmd) + if (!priv->drvr) { - /* Add support for ioctl commands here */ + /* No... the block driver is no longer bound to the class. That means that + * the USB storage device is no longer connected. + */ - default: - ret = -ENOTTY; - break; + ret = -ENODEV; } + else + { + /* Process the IOCTL by command */ - usbhost_givesem(priv); + usbhost_takesem(priv); + switch (cmd) + { + /* Add support for ioctl commands here */ + + default: + ret = -ENOTTY; + break; + } + + usbhost_givesem(priv); + } return ret; } @@ -447,7 +647,24 @@ static int usbhost_ioctl(FAR struct inode *inode, int cmd, unsigned long arg) int usbhost_storageinit(void) { - /* Advertise our availability to support mass storage devices */ + /* If we have been configured to use pre-allocated storage class instances, + * then place all of the pre-allocated USB host storage class instances + * into a free list. + */ + +#if CONFIG_USBHOST_NPREALLOC > 0 + int i; + + g_freelist = NULL; + for (i = 0; i < CONFIG_USBHOST_NPREALLOC; i++) + { + struct usbhost_state_s *class = &g_prealloc[i]; + class->class.flink = g_freelist; + g_freelist = class; + } +#endif + + /* Advertise our availability to support (certain) mass storage devices */ return usbhost_registerclass(&g_storage); } diff --git a/nuttx/include/nuttx/usb/usbhost.h b/nuttx/include/nuttx/usb/usbhost.h index aedd57f26..b4d51e258 100644 --- a/nuttx/include/nuttx/usb/usbhost.h +++ b/nuttx/include/nuttx/usb/usbhost.h @@ -83,7 +83,7 @@ * ************************************************************************************/ -#define CLASS_CREATE(reg, drvr, id) (reg->create(drvr)) +#define CLASS_CREATE(reg, drvr, id) ((reg)->create(drvr)) /************************************************************************************ * Name: CLASS_CONFIGDESC @@ -105,7 +105,26 @@ * ************************************************************************************/ -#definei CLASS_CONFIGDESC(class, configdesc, desclen) (class->create(class, configdesc, desclen)) +#definei CLASS_CONFIGDESC(class, configdesc, desclen) ((class)->configdesc(class, configdesc, desclen)) + +/************************************************************************************ + * Name: CLASS_DISCONNECTED + * + * Description: + * This macro will call the disconnected() method of struct usbhost_class_s. This + * method is a callback into the class implementation. It is used to inform the + * class that the USB device has been disconnected. + * + * Input Parameters: + * class - The USB host class entry previously obtained from a call to create(). + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + ************************************************************************************/ + +#definei CLASS_DISCONNECTED(class) ((class)->disconnected(class)) /************************************************************************************ * Public Types @@ -139,7 +158,7 @@ struct usbhost_registry_s * provide those instances in write-able memory (RAM). */ - struct usbhost_registry_s flink; + struct usbhost_registry_s flink; /* This is a callback into the class implementation. It is used to (1) create * a new instance of the USB host class state and to (2) bind a USB host driver @@ -148,15 +167,15 @@ struct usbhost_registry_s * simultaneously connected (see the CLASS_CREATE() macro above). */ - struct usbhost_class_s *(*create)(struct usbhost_driver_s *drvr, - const struct usbhost_id_s *id) + FAR struct usbhost_class_s *(*create)(FAR struct usbhost_driver_s *drvr, + FAR const struct usbhost_id_s *id) /* This information uniquely identifies the USB host class implementation that * goes with a specific USB device. */ - uint8_t nids; /* Number of IDs in the id[] array */ - const struct usbhost_id_s *id; /* An array of ID info. Actual dimension is nids */ + uint8_t nids; /* Number of IDs in the id[] array */ + FAR const struct usbhost_id_s *id; /* An array of ID info. Actual dimension is nids */ }; /* struct usbhost_class_s provides access from the USB host driver to the USB host @@ -165,12 +184,16 @@ struct usbhost_registry_s struct usbhost_class_s { - /* Provides the configuration descripor to the class. The configuration + /* Provides the configuration descriptor to the class. The configuration * descriptor contains critical information needed by the class in order to * initialize properly (such as endpoint selections). */ - int (*configdesc)(struct usbhost_class_s *class, const uint8_t *confidesc, int desclen); + int (*configdesc)(FAR struct usbhost_class_s *class, FAR const uint8_t *confidesc, int desclen); + + /* This method informs the class that the USB device has been disconnected. */ + + int (*disconnected)(FAR struct usbhost_class_s *class); }; /* struct usbhost_driver_s provides access to the USB host driver from the USB host -- cgit v1.2.3