diff --git a/ChangeLog b/ChangeLog index a825178b45..fa6fdd153a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7031,3 +7031,17 @@ Kconfig; update naming to include SAM34_ (2014-3-24). * configs/sam4e-ek/include/board.h: Update HSMCI timing to use the CLKODD bit (2014-3-24). + * drivers/include/mtd/Kconfig, sector512.c, and include/nuttx/mtd/mtd.h: + Add a new MTD driver that can be used to contain another driver and + force its apparent sector size to be 512 bytes (2014-3-24). + * arch/arm/src/sam34/sam_lowputc.c sam_serial.c: Fix a mysterious + multithreading bug that can log up the serial port (2014-3-14). + * drivers/usbdev/Kconfig, usbmsc.c, usbmsc.h, and usbmsc_scsi.c: + Redesign threading module used with the USB MSC driver. It was using + pthreads before and these were changed to a kernel thread. The reason + for this has to do with task grouping: A pthread is a memory of the + group of the task that started it. A kernel thread is independent of + the task that started in (other than knowing it as the parent). This + allows me to remove so kludge logic to "deparent" the pthread on + startup (2014-3-25). + diff --git a/drivers/usbdev/Kconfig b/drivers/usbdev/Kconfig index 8da9d9e35c..9fdc6c92ce 100644 --- a/drivers/usbdev/Kconfig +++ b/drivers/usbdev/Kconfig @@ -584,10 +584,25 @@ config USBMSC_VERSIONNO default "0x399" config USBMSC_REMOVABLE - bool "Mass storage remove able" + bool "Mass storage removable" default n ---help--- Select if the media is removable USB Composite Device Configuration +config USBMSC_SCSI_PRIO + int "USBMSC SCSI daemon priority" + default 128 + ---help--- + Priority of the SCSI kernel thread. This must be a relatively high + priority so that the SCSI daemon can be response to USB block driver + accesses. + +config USBMSC_SCSI_STACKSIZE + int "USBMSC SCSI daemon stack size" + default 2048 + ---help--- + Stack size used with the SCSI kernel thread. The default value + is not tuned. + endif diff --git a/drivers/usbdev/usbmsc.c b/drivers/usbdev/usbmsc.c index 1a25293692..2e51ee6d81 100644 --- a/drivers/usbdev/usbmsc.c +++ b/drivers/usbdev/usbmsc.c @@ -66,13 +66,13 @@ #include #include #include -#include #include #include #include #include #include +#include #include #include #include @@ -160,6 +160,10 @@ static struct usbdevclass_driverops_s g_driverops = NULL /* resume */ }; +/* Used to hand-off the state structure when the SCSI worker thread is started */ + +FAR struct usbmsc_dev_s *g_usbmsc_handoff; + /**************************************************************************** * Public Data ****************************************************************************/ @@ -642,7 +646,7 @@ static int usbmsc_setup(FAR struct usbdevclass_driver_s *driver, priv->theventset |= USBMSC_EVENT_CFGCHANGE; priv->thvalue = value; - pthread_cond_signal(&priv->cond); + usbmsc_scsi_signal(priv); /* Return here... the response will be provided later by the * worker thread. @@ -681,7 +685,7 @@ static int usbmsc_setup(FAR struct usbdevclass_driver_s *driver, /* Signal to instantiate the interface change */ priv->theventset |= USBMSC_EVENT_IFCHANGE; - pthread_cond_signal(&priv->cond); + usbmsc_scsi_signal(priv); /* Return here... the response will be provided later by the * worker thread. @@ -748,7 +752,7 @@ static int usbmsc_setup(FAR struct usbdevclass_driver_s *driver, /* Signal to stop the current operation and reinitialize state */ priv->theventset |= USBMSC_EVENT_RESET; - pthread_cond_signal(&priv->cond); + usbmsc_scsi_signal(priv); /* Return here... the response will be provided later by the * worker thread. @@ -870,7 +874,7 @@ static void usbmsc_disconnect(FAR struct usbdevclass_driver_s *driver, /* Signal the worker thread */ priv->theventset |= USBMSC_EVENT_DISCONNECT; - pthread_cond_signal(&priv->cond); + usbmsc_scsi_signal(priv); irqrestore(flags); /* Perform the soft connect function so that we will we can be @@ -1109,7 +1113,7 @@ void usbmsc_wrcomplete(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req) /* Inform the worker thread that a write request has been returned */ priv->theventset |= USBMSC_EVENT_WRCOMPLETE; - pthread_cond_signal(&priv->cond); + usbmsc_scsi_signal(priv); } /**************************************************************************** @@ -1160,7 +1164,7 @@ void usbmsc_rdcomplete(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req) /* Signal the worker thread that there is received data to be processed */ priv->theventset |= USBMSC_EVENT_RDCOMPLETE; - pthread_cond_signal(&priv->cond); + usbmsc_scsi_signal(priv); } break; @@ -1261,6 +1265,26 @@ void usbmsc_deferredresponse(FAR struct usbmsc_dev_s *priv, bool failed) } } +/**************************************************************************** + * Name: usbmsc_sync_wait + * + * Description: + * Wait for the worker thread to obtain the USB MSC state data + * + ****************************************************************************/ + +static inline void usbmsc_sync_wait(FAR struct usbmsc_dev_s *priv) +{ + int ret; + + do + { + ret = sem_wait(&priv->thsynch); + DEBUGASSERT(ret == OK || errno == EINTR); + } + while (ret < 0); +} + /**************************************************************************** * User Interfaces ****************************************************************************/ @@ -1314,8 +1338,9 @@ int usbmsc_configure(unsigned int nluns, void **handle) priv = &alloc->dev; memset(priv, 0, sizeof(struct usbmsc_dev_s)); - pthread_mutex_init(&priv->mutex, NULL); - pthread_cond_init(&priv->cond, NULL); + sem_init(&priv->thsynch, 0, 0); + sem_init(&priv->thlock, 0, 1); + sem_init(&priv->thwaitsem, 0, 0); sq_init(&priv->wrreqlist); priv->nluns = nluns; @@ -1549,7 +1574,7 @@ int usbmsc_unbindlun(FAR void *handle, unsigned int lunno) #endif lun = &priv->luntab[lunno]; - pthread_mutex_lock(&priv->mutex); + usbmsc_scsi_lock(priv); #ifdef CONFIG_DEBUG if (lun->inode == NULL) @@ -1566,7 +1591,7 @@ int usbmsc_unbindlun(FAR void *handle, unsigned int lunno) ret = OK; } - pthread_mutex_unlock(&priv->mutex); + usbmsc_scsi_unlock(priv); return ret; } @@ -1594,10 +1619,7 @@ int usbmsc_exportluns(FAR void *handle) FAR struct usbmsc_dev_s *priv; FAR struct usbmsc_driver_s *drvr; irqstate_t flags; -#ifdef SDCC - pthread_attr_t attr; -#endif - int ret; + int ret = OK; #ifdef CONFIG_DEBUG if (!alloc) @@ -1610,35 +1632,35 @@ int usbmsc_exportluns(FAR void *handle) priv = &alloc->dev; drvr = &alloc->drvr; - /* Start the worker thread */ - - pthread_mutex_lock(&priv->mutex); - priv->thstate = USBMSC_STATE_NOTSTARTED; - priv->theventset = USBMSC_EVENT_NOEVENTS; - -#ifdef SDCC - (void)pthread_attr_init(&attr); - ret = pthread_create(&priv->thread, &attr, usbmsc_workerthread, (pthread_addr_t)priv); -#else - ret = pthread_create(&priv->thread, NULL, usbmsc_workerthread, (pthread_addr_t)priv); -#endif - if (ret != OK) - { - usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_THREADCREATE), (uint16_t)-ret); - goto errout_with_mutex; - } - - /* Detach the pthread so that we do not create a memory leak. + /* Start the worker thread * - * REVISIT: See related comments in usbmsc_uninitialize() + * REVISIT: g_usbmsc_handoff is a global and, hence, really requires + * some protection against re-entrant usage. */ - ret = pthread_detach(priv->thread); - if (ret != OK) + usbmsc_scsi_lock(priv); + + priv->thstate = USBMSC_STATE_NOTSTARTED; + priv->theventset = USBMSC_EVENT_NOEVENTS; + + g_usbmsc_handoff = priv; + + uvdbg("Starting SCSI worker thread\n"); + priv->thpid = KERNEL_THREAD("scsid", CONFIG_USBMSC_SCSI_PRIO, + CONFIG_USBMSC_SCSI_STACKSIZE, + usbmsc_scsi_main, NULL); + if (priv->thpid <= 0) { - usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_DETACH), (uint16_t)-ret); + usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_THREADCREATE), (uint16_t)errno); + goto errout_with_lock; } + /* Wait for the worker thread to run and initialize */ + + uvdbg("Waiting for the SCSI worker thread\n"); + usbmsc_sync_wait(priv); + DEBUGASSERT(g_usbmsc_handoff == NULL); + /* Register the USB storage class driver (unless we are part of a composite device) */ #ifndef CONFIG_USBMSC_COMPOSITE @@ -1646,19 +1668,20 @@ int usbmsc_exportluns(FAR void *handle) if (ret != OK) { usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_DEVREGISTER), (uint16_t)-ret); - goto errout_with_mutex; + goto errout_with_lock; } #endif /* Signal to start the thread */ + uvdbg("Signalling for the SCSI worker thread\n"); flags = irqsave(); priv->theventset |= USBMSC_EVENT_READY; - pthread_cond_signal(&priv->cond); + usbmsc_scsi_signal(priv); irqrestore(flags); -errout_with_mutex: - pthread_mutex_unlock(&priv->mutex); +errout_with_lock: + usbmsc_scsi_unlock(priv); return ret; } @@ -1742,7 +1765,7 @@ void usbmsc_uninitialize(FAR void *handle) * first pass uninitialization. */ - if (priv->thread == 0) + if (priv->thpid == 0) { /* In this second and final pass, all that remains to be done is to * free the memory resources. @@ -1759,54 +1782,28 @@ void usbmsc_uninitialize(FAR void *handle) { /* The thread was started.. Is it still running? */ - pthread_mutex_lock(&priv->mutex); + usbmsc_scsi_lock(priv); if (priv->thstate != USBMSC_STATE_TERMINATED) { /* Yes.. Ask the thread to stop */ flags = irqsave(); priv->theventset |= USBMSC_EVENT_TERMINATEREQUEST; - pthread_cond_signal(&priv->cond); + usbmsc_scsi_signal(priv); irqrestore(flags); } - pthread_mutex_unlock(&priv->mutex); + usbmsc_scsi_unlock(priv); - /* Wait for the thread to exit. This is necessary even if the - * thread has already exitted in order to collect the join - * garbage - */ + /* Wait for the thread to exit */ -#if 0 - /* REVISIT: pthread_join does not work in all contexts. In - * particular, if usbmsc_uninitialize() executes in a different - * task group than the group that includes the SCSI thread, then - * pthread_join will fail. - * - * NOTE: If, for some reason, you wanted to restore this code, - * there is now a matching pthread_detach() elsewhere to prevent - * memory leaks. - */ - - (void)pthread_join(priv->thread, &value); - -#else - /* REVISIT: Calling pthread_mutex_lock and pthread_cond_wait - * from outside of the task group is equally non-standard. - * However, this actually works. - */ - - pthread_mutex_lock(&priv->mutex); while ((priv->theventset & USBMSC_EVENT_TERMINATEREQUEST) != 0) { - pthread_cond_wait(&priv->cond, &priv->mutex); + usbmsc_sync_wait(priv); } - - pthread_mutex_unlock(&priv->mutex); -#endif } - priv->thread = 0; + priv->thpid = 0; /* Unregister the driver (unless we are a part of a composite device) */ @@ -1832,14 +1829,15 @@ void usbmsc_uninitialize(FAR void *handle) /* Uninitialize and release the driver structure */ - pthread_mutex_destroy(&priv->mutex); - pthread_cond_destroy(&priv->cond); + sem_destroy(&priv->thsynch); + sem_destroy(&priv->thlock); + sem_destroy(&priv->thwaitsem); #ifndef CONFIG_USBMSC_COMPOSITE /* For the case of the composite driver, there is a two pass * uninitialization sequence. We cannot yet free the driver structure. * We will do that on the second pass (and we will know that it is the - * second pass because of priv->thread == 0) + * second pass because of priv->thpid == 0) */ kfree(priv); diff --git a/drivers/usbdev/usbmsc.h b/drivers/usbdev/usbmsc.h index 23c6236927..c153f48e25 100644 --- a/drivers/usbdev/usbmsc.h +++ b/drivers/usbdev/usbmsc.h @@ -47,8 +47,8 @@ #include #include #include -#include #include +#include #include #include @@ -194,6 +194,16 @@ #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 + /* Debug -- must be consistent with include/debug.h */ #ifdef CONFIG_CPP_HAVE_VARARGS @@ -443,17 +453,19 @@ struct usbmsc_lun_s size_t nsectors; /* Number of sectors in the partition */ }; -/* Describes the overall state of the driver */ +/* 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) */ - /* Worker thread interface */ + /* SCSI worker kernel 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 */ + 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) */ @@ -542,10 +554,55 @@ EXTERN const char g_compserialstr[]; #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 * @@ -607,7 +664,7 @@ FAR const struct usb_qualdesc_s *usbmsc_getqualdesc(void); #endif /**************************************************************************** - * Name: usbmsc_workerthread + * Name: usbmsc_scsi_main * * Description: * This is the main function of the USB storage worker thread. It loops @@ -615,7 +672,7 @@ FAR const struct usb_qualdesc_s *usbmsc_getqualdesc(void); * ****************************************************************************/ -EXTERN void *usbmsc_workerthread(void *arg); +int usbmsc_scsi_main(int argc, char *argv[]); /**************************************************************************** * Name: usbmsc_setconfig @@ -626,7 +683,7 @@ EXTERN void *usbmsc_workerthread(void *arg); * ****************************************************************************/ -EXTERN int usbmsc_setconfig(FAR struct usbmsc_dev_s *priv, uint8_t config); +int usbmsc_setconfig(FAR struct usbmsc_dev_s *priv, uint8_t config); /**************************************************************************** * Name: usbmsc_resetconfig @@ -636,7 +693,7 @@ EXTERN int usbmsc_setconfig(FAR struct usbmsc_dev_s *priv, uint8_t config); * ****************************************************************************/ -EXTERN void usbmsc_resetconfig(FAR struct usbmsc_dev_s *priv); +void usbmsc_resetconfig(FAR struct usbmsc_dev_s *priv); /**************************************************************************** * Name: usbmsc_wrcomplete @@ -647,8 +704,8 @@ EXTERN void usbmsc_resetconfig(FAR struct usbmsc_dev_s *priv); * ****************************************************************************/ -EXTERN void usbmsc_wrcomplete(FAR struct usbdev_ep_s *ep, - FAR struct usbdev_req_s *req); +void usbmsc_wrcomplete(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req); /**************************************************************************** * Name: usbmsc_rdcomplete @@ -659,8 +716,8 @@ EXTERN void usbmsc_wrcomplete(FAR struct usbdev_ep_s *ep, * ****************************************************************************/ -EXTERN void usbmsc_rdcomplete(FAR struct usbdev_ep_s *ep, - FAR struct usbdev_req_s *req); +void usbmsc_rdcomplete(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req); /**************************************************************************** * Name: usbmsc_deferredresponse @@ -684,7 +741,7 @@ EXTERN void usbmsc_rdcomplete(FAR struct usbdev_ep_s *ep, * ****************************************************************************/ -EXTERN void usbmsc_deferredresponse(FAR struct usbmsc_dev_s *priv, bool failed); +void usbmsc_deferredresponse(FAR struct usbmsc_dev_s *priv, bool failed); #undef EXTERN #if defined(__cplusplus) diff --git a/drivers/usbdev/usbmsc_scsi.c b/drivers/usbdev/usbmsc_scsi.c index 13e9621b89..2da4c5df86 100644 --- a/drivers/usbdev/usbmsc_scsi.c +++ b/drivers/usbdev/usbmsc_scsi.c @@ -61,12 +61,13 @@ #include #include #include -#include +#include #include #include #include #include +#include #include #include #include @@ -353,6 +354,52 @@ static void usbmsc_putle32(uint8_t *buf, uint32_t val) * SCSI Worker Thread ****************************************************************************/ +/**************************************************************************** + * Name: usbmsc_scsi_wait + * + * Description: + * Wait for a SCSI worker thread event. + * + ****************************************************************************/ + +static void usbmsc_scsi_wait(FAR struct usbmsc_dev_s *priv) +{ + irqstate_t flags = irqsave(); + int ret; + + /* We must hold the SCSI lock to call this function */ + + DEBUGASSERT(priv->thlock.semcount < 1); + + /* A flag is used to prevent driving up the semaphore count. This function + * is called (primarily) from the SCSI work thread so we must disable + * interrupts momentarily to assure that test of the flag and the wait fo + * the semaphore count are atomic. Interrupts will, of course, be re- + * enabled while we wait for the event. + */ + + flags = irqsave(); + priv->thwaiting = true; + + /* Relinquish our lock on the SCSI state data */ + + usbmsc_scsi_unlock(priv); + + /* Now wait for a SCSI event to be signalled */ + + do + { + ret = sem_wait(&priv->thwaitsem); + DEBUGASSERT(ret == OK || errno == EINTR); + } + while (priv->thwaiting); + + /* Re-acquire our lock on the SCSI state data */ + + usbmsc_scsi_lock(priv); + irqrestore(flags); +} + /**************************************************************************** * Name: usbmsc_cmdtestunitready * @@ -406,8 +453,8 @@ static inline int usbmsc_cmdrequestsense(FAR struct usbmsc_dev_s *priv, } ret = usbmsc_setupcmd(priv, cdblen, - USBMSC_FLAGS_DIRDEVICE2HOST|USBMSC_FLAGS_LUNNOTNEEDED| - USBMSC_FLAGS_UACOKAY|USBMSC_FLAGS_RETAINSENSEDATA); + USBMSC_FLAGS_DIRDEVICE2HOST | USBMSC_FLAGS_LUNNOTNEEDED | + USBMSC_FLAGS_UACOKAY | USBMSC_FLAGS_RETAINSENSEDATA); if (ret == OK) { lun = priv->lun; @@ -467,12 +514,14 @@ static inline int usbmsc_cmdread6(FAR struct usbmsc_dev_s *priv) priv->u.xfrlen = 256; } - ret = usbmsc_setupcmd(priv, SCSICMD_READ6_SIZEOF, USBMSC_FLAGS_DIRDEVICE2HOST|USBMSC_FLAGS_BLOCKXFR); + ret = usbmsc_setupcmd(priv, SCSICMD_READ6_SIZEOF, + USBMSC_FLAGS_DIRDEVICE2HOST | USBMSC_FLAGS_BLOCKXFR); if (ret == OK) { /* Get the Logical Block Address (LBA) from cdb[] as the starting sector */ - priv->sector = (uint32_t)(read6->mslba & SCSICMD_READ6_MSLBAMASK) << 16 | (uint32_t)usbmsc_getbe16(read6->lslba); + priv->sector = (uint32_t)(read6->mslba & SCSICMD_READ6_MSLBAMASK) << 16 | + (uint32_t)usbmsc_getbe16(read6->lslba); /* Verify that a block driver has been bound to the LUN */ @@ -496,10 +545,12 @@ static inline int usbmsc_cmdread6(FAR struct usbmsc_dev_s *priv) else { - usbtrace(TRACE_CLASSSTATE(USBMSC_CLASSSTATE_CMDPARSECMDREAD6), priv->cdb[0]); + usbtrace(TRACE_CLASSSTATE(USBMSC_CLASSSTATE_CMDPARSECMDREAD6), + priv->cdb[0]); priv->thstate = USBMSC_STATE_CMDREAD; } } + return ret; } @@ -523,7 +574,8 @@ static inline int usbmsc_cmdwrite6(FAR struct usbmsc_dev_s *priv) priv->u.xfrlen = 256; } - ret = usbmsc_setupcmd(priv, SCSICMD_WRITE6_SIZEOF, USBMSC_FLAGS_DIRHOST2DEVICE|USBMSC_FLAGS_BLOCKXFR); + ret = usbmsc_setupcmd(priv, SCSICMD_WRITE6_SIZEOF, + USBMSC_FLAGS_DIRHOST2DEVICE | USBMSC_FLAGS_BLOCKXFR); if (ret == OK) { /* Get the Logical Block Address (LBA) from cdb[] as the starting sector */ @@ -565,6 +617,7 @@ static inline int usbmsc_cmdwrite6(FAR struct usbmsc_dev_s *priv) priv->thstate = USBMSC_STATE_CMDWRITE; } } + return ret; } @@ -586,7 +639,8 @@ static inline int usbmsc_cmdinquiry(FAR struct usbmsc_dev_s *priv, priv->u.alloclen = usbmsc_getbe16(inquiry->alloclen); ret = usbmsc_setupcmd(priv, SCSICMD_INQUIRY_SIZEOF, - USBMSC_FLAGS_DIRDEVICE2HOST|USBMSC_FLAGS_LUNNOTNEEDED|USBMSC_FLAGS_UACOKAY); + USBMSC_FLAGS_DIRDEVICE2HOST | USBMSC_FLAGS_LUNNOTNEEDED | + USBMSC_FLAGS_UACOKAY); if (ret == OK) { if (!priv->lun) @@ -657,7 +711,8 @@ static inline int usbmsc_cmdmodeselect6(FAR struct usbmsc_dev_s *priv) FAR struct scsicmd_modeselect6_s *modeselect = (FAR struct scsicmd_modeselect6_s *)priv->cdb; priv->u.alloclen = modeselect->plen; - (void)usbmsc_setupcmd(priv, SCSICMD_MODESELECT6_SIZEOF, USBMSC_FLAGS_DIRHOST2DEVICE); + (void)usbmsc_setupcmd(priv, SCSICMD_MODESELECT6_SIZEOF, + USBMSC_FLAGS_DIRHOST2DEVICE); /* Not supported */ @@ -739,7 +794,8 @@ static int inline usbmsc_cmdmodesense6(FAR struct usbmsc_dev_s *priv, int ret; priv->u.alloclen = modesense->alloclen; - ret = usbmsc_setupcmd(priv, SCSICMD_MODESENSE6_SIZEOF, USBMSC_FLAGS_DIRDEVICE2HOST); + ret = usbmsc_setupcmd(priv, SCSICMD_MODESENSE6_SIZEOF, + USBMSC_FLAGS_DIRDEVICE2HOST); if (ret == OK) { if ((modesense->flags & ~SCSICMD_MODESENSE6_DBD) != 0 || modesense->subpgcode != 0) @@ -773,6 +829,7 @@ static int inline usbmsc_cmdmodesense6(FAR struct usbmsc_dev_s *priv, } } } + return ret; } @@ -789,7 +846,8 @@ static inline int usbmsc_cmdstartstopunit(FAR struct usbmsc_dev_s *priv) int ret; priv->u.alloclen = 0; - ret = usbmsc_setupcmd(priv, SCSICMD_STARTSTOPUNIT_SIZEOF, USBMSC_FLAGS_DIRNONE); + ret = usbmsc_setupcmd(priv, SCSICMD_STARTSTOPUNIT_SIZEOF, + USBMSC_FLAGS_DIRNONE); if (ret == OK) { #ifndef CONFIG_USBMSC_REMOVABLE @@ -802,6 +860,7 @@ static inline int usbmsc_cmdstartstopunit(FAR struct usbmsc_dev_s *priv) ret = -EINVAL; #endif } + return ret; } @@ -822,7 +881,8 @@ static inline int usbmsc_cmdpreventmediumremoval(FAR struct usbmsc_dev_s *priv) int ret; priv->u.alloclen = 0; - ret = usbmsc_setupcmd(priv, SCSICMD_PREVENTMEDIUMREMOVAL_SIZEOF, USBMSC_FLAGS_DIRNONE); + ret = usbmsc_setupcmd(priv, SCSICMD_PREVENTMEDIUMREMOVAL_SIZEOF, + USBMSC_FLAGS_DIRNONE); if (ret == OK) { #ifndef CONFIG_USBMSC_REMOVABLE @@ -839,6 +899,7 @@ static inline int usbmsc_cmdpreventmediumremoval(FAR struct usbmsc_dev_s *priv) lun->locked = pmr->prevent & SCSICMD_PREVENTMEDIUMREMOVAL_TRANSPORT; #endif } + return ret; } @@ -859,7 +920,8 @@ static inline int usbmsc_cmdreadformatcapacity(FAR struct usbmsc_dev_s *priv, int ret; priv->u.alloclen = usbmsc_getbe16(rfc->alloclen); - ret = usbmsc_setupcmd(priv, SCSICMD_READFORMATCAPACITIES_SIZEOF, USBMSC_FLAGS_DIRDEVICE2HOST); + ret = usbmsc_setupcmd(priv, SCSICMD_READFORMATCAPACITIES_SIZEOF, + USBMSC_FLAGS_DIRDEVICE2HOST); if (ret == OK) { hdr = (FAR struct scsiresp_readformatcapacities_s *)buf; @@ -873,6 +935,7 @@ static inline int usbmsc_cmdreadformatcapacity(FAR struct usbmsc_dev_s *priv, usbmsc_putbe24(hdr->blocklen, lun->sectorsize); priv->nreqbytes = SCSIRESP_READFORMATCAPACITIES_SIZEOF; } + return ret; } @@ -894,7 +957,8 @@ static int inline usbmsc_cmdreadcapacity10(FAR struct usbmsc_dev_s *priv, int ret; priv->u.alloclen = SCSIRESP_READCAPACITY10_SIZEOF; /* Fake the allocation length */ - ret = usbmsc_setupcmd(priv, SCSICMD_READCAPACITY10_SIZEOF, USBMSC_FLAGS_DIRDEVICE2HOST); + ret = usbmsc_setupcmd(priv, SCSICMD_READCAPACITY10_SIZEOF, + USBMSC_FLAGS_DIRDEVICE2HOST); if (ret == OK) { /* Check the PMI and LBA fields */ @@ -914,6 +978,7 @@ static int inline usbmsc_cmdreadcapacity10(FAR struct usbmsc_dev_s *priv, priv->nreqbytes = SCSIRESP_READCAPACITY10_SIZEOF; } } + return ret; } @@ -932,7 +997,8 @@ static inline int usbmsc_cmdread10(FAR struct usbmsc_dev_s *priv) int ret; priv->u.xfrlen = usbmsc_getbe16(read10->xfrlen); - ret = usbmsc_setupcmd(priv, SCSICMD_READ10_SIZEOF, USBMSC_FLAGS_DIRDEVICE2HOST|USBMSC_FLAGS_BLOCKXFR); + ret = usbmsc_setupcmd(priv, SCSICMD_READ10_SIZEOF, + USBMSC_FLAGS_DIRDEVICE2HOST | USBMSC_FLAGS_BLOCKXFR); if (ret == OK) { /* Get the Logical Block Address (LBA) from cdb[] as the starting sector */ @@ -993,7 +1059,8 @@ static inline int usbmsc_cmdwrite10(FAR struct usbmsc_dev_s *priv) int ret; priv->u.xfrlen = usbmsc_getbe16(write10->xfrlen); - ret = usbmsc_setupcmd(priv, SCSICMD_WRITE10_SIZEOF, USBMSC_FLAGS_DIRHOST2DEVICE|USBMSC_FLAGS_BLOCKXFR); + ret = usbmsc_setupcmd(priv, SCSICMD_WRITE10_SIZEOF, + USBMSC_FLAGS_DIRHOST2DEVICE | USBMSC_FLAGS_BLOCKXFR); if (ret == OK) { /* Get the Logical Block Address (LBA) from cdb[] as the starting sector */ @@ -1044,6 +1111,7 @@ static inline int usbmsc_cmdwrite10(FAR struct usbmsc_dev_s *priv) priv->thstate = USBMSC_STATE_CMDWRITE; } } + return ret; } @@ -1122,6 +1190,7 @@ static inline int usbmsc_cmdverify10(FAR struct usbmsc_dev_s *priv) } } } + return ret; } @@ -1149,8 +1218,10 @@ static inline int usbmsc_cmdsynchronizecache10(FAR struct usbmsc_dev_s *priv) } else { - ret = usbmsc_setupcmd(priv, SCSICMD_SYNCHRONIZECACHE10_SIZEOF, USBMSC_FLAGS_DIRNONE); + ret = usbmsc_setupcmd(priv, SCSICMD_SYNCHRONIZECACHE10_SIZEOF, + USBMSC_FLAGS_DIRNONE); } + return ret; } @@ -1167,7 +1238,8 @@ static inline int usbmsc_cmdmodeselect10(FAR struct usbmsc_dev_s *priv) FAR struct scsicmd_modeselect10_s *modeselect = (FAR struct scsicmd_modeselect10_s *)priv->cdb; priv->u.alloclen = usbmsc_getbe16(modeselect->parmlen); - (void)usbmsc_setupcmd(priv, SCSICMD_MODESELECT10_SIZEOF, USBMSC_FLAGS_DIRHOST2DEVICE); + (void)usbmsc_setupcmd(priv, SCSICMD_MODESELECT10_SIZEOF, + USBMSC_FLAGS_DIRHOST2DEVICE); /* Not supported */ @@ -1192,10 +1264,12 @@ static int inline usbmsc_cmdmodesense10(FAR struct usbmsc_dev_s *priv, int ret; priv->u.alloclen = usbmsc_getbe16(modesense->alloclen); - ret = usbmsc_setupcmd(priv, SCSICMD_MODESENSE10_SIZEOF, USBMSC_FLAGS_DIRDEVICE2HOST); + ret = usbmsc_setupcmd(priv, SCSICMD_MODESENSE10_SIZEOF, + USBMSC_FLAGS_DIRDEVICE2HOST); if (ret == OK) { - if ((modesense->flags & ~SCSICMD_MODESENSE10_DBD) != 0 || modesense->subpgcode != 0) + if ((modesense->flags & ~SCSICMD_MODESENSE10_DBD) != 0 || + modesense->subpgcode != 0) { usbtrace(TRACE_CLSERROR(USBMSC_TRACEERR_MODESENSE10FLAGS), 0); priv->lun->sd = SCSI_KCQIR_INVALIDFIELDINCBA; @@ -1225,6 +1299,7 @@ static int inline usbmsc_cmdmodesense10(FAR struct usbmsc_dev_s *priv, } } } + return ret; } @@ -1243,7 +1318,8 @@ static inline int usbmsc_cmdread12(FAR struct usbmsc_dev_s *priv) int ret; priv->u.xfrlen = usbmsc_getbe32(read12->xfrlen); - ret = usbmsc_setupcmd(priv, SCSICMD_READ12_SIZEOF, USBMSC_FLAGS_DIRDEVICE2HOST|USBMSC_FLAGS_BLOCKXFR); + ret = usbmsc_setupcmd(priv, SCSICMD_READ12_SIZEOF, + USBMSC_FLAGS_DIRDEVICE2HOST | USBMSC_FLAGS_BLOCKXFR); if (ret == OK) { /* Get the Logical Block Address (LBA) from cdb[] as the starting sector */ @@ -1285,6 +1361,7 @@ static inline int usbmsc_cmdread12(FAR struct usbmsc_dev_s *priv) priv->thstate = USBMSC_STATE_CMDREAD; } } + return ret; } @@ -1303,7 +1380,8 @@ static inline int usbmsc_cmdwrite12(FAR struct usbmsc_dev_s *priv) int ret; priv->u.xfrlen = usbmsc_getbe32(write12->xfrlen); - ret = usbmsc_setupcmd(priv, SCSICMD_WRITE12_SIZEOF, USBMSC_FLAGS_DIRHOST2DEVICE|USBMSC_FLAGS_BLOCKXFR); + ret = usbmsc_setupcmd(priv, SCSICMD_WRITE12_SIZEOF, + USBMSC_FLAGS_DIRHOST2DEVICE | USBMSC_FLAGS_BLOCKXFR); if (ret == OK) { /* Get the Logical Block Address (LBA) from cdb[] as the starting sector */ @@ -1364,20 +1442,21 @@ static inline int usbmsc_cmdwrite12(FAR struct usbmsc_dev_s *priv) * and verification operations that are common to all SCSI commands. This * function performs the following common setup operations: * - * 1. Determine the direction of the response - * 2. Verify lengths - * 3. Setup and verify the LUN + * 1. Determine the direction of the response + * 2. Verify lengths + * 3. Setup and verify the LUN * * Includes special logic for INQUIRY and REQUESTSENSE commands * ****************************************************************************/ -static int inline usbmsc_setupcmd(FAR struct usbmsc_dev_s *priv, uint8_t cdblen, uint8_t flags) +static int inline usbmsc_setupcmd(FAR struct usbmsc_dev_s *priv, + uint8_t cdblen, uint8_t flags) { FAR struct usbmsc_lun_s *lun = NULL; uint32_t datlen; - uint8_t dir = flags & USBMSC_FLAGS_DIRMASK; - int ret = OK; + uint8_t dir = flags & USBMSC_FLAGS_DIRMASK; + int ret = OK; /* Verify the LUN and set up the current LUN reference in the * device structure @@ -1469,7 +1548,7 @@ static int inline usbmsc_setupcmd(FAR struct usbmsc_dev_s *priv, uint8_t cdblen, } /* Compare the length of data in the cdb[] with the expected length - * of the command. + * of the command. These sizes should match exactly. */ if (cdblen != priv->cdblen) @@ -1479,6 +1558,8 @@ static int inline usbmsc_setupcmd(FAR struct usbmsc_dev_s *priv, uint8_t cdblen, ret = -EINVAL; } + /* Was a valid LUN provided? */ + if (lun) { /* Retain the sense data for the REQUEST SENSE command */ @@ -1517,8 +1598,10 @@ static int inline usbmsc_setupcmd(FAR struct usbmsc_dev_s *priv, uint8_t cdblen, { lun->sd = SCSI_KCQIR_INVALIDFIELDINCBA; } - ret = -EINVAL; + + ret = -EINVAL; } + return ret; } @@ -1724,7 +1807,7 @@ static int usbmsc_cmdparsestate(FAR struct usbmsc_dev_s *priv) /* Get exclusive access to the block driver */ - pthread_mutex_lock(&priv->mutex); + usbmsc_scsi_lock(priv); switch (priv->cdb[0]) { case SCSI_CMD_TESTUNITREADY: /* 0x00 Mandatory */ @@ -1911,7 +1994,8 @@ static int usbmsc_cmdparsestate(FAR struct usbmsc_dev_s *priv) } break; } - pthread_mutex_unlock(&priv->mutex); + + usbmsc_scsi_unlock(priv); /* Is a response required? (Not for read6/10/12 and write6/10/12). */ @@ -2520,7 +2604,7 @@ static int usbmsc_cmdstatusstate(FAR struct usbmsc_dev_s *priv) ****************************************************************************/ /**************************************************************************** - * Name: usbmsc_workerthread + * Name: usbmsc_scsi_main * * Description: * This is the main function of the USB storage worker thread. It loops @@ -2528,31 +2612,44 @@ static int usbmsc_cmdstatusstate(FAR struct usbmsc_dev_s *priv) * ****************************************************************************/ -void *usbmsc_workerthread(void *arg) +int usbmsc_scsi_main(int argc, char *argv[]) { - struct usbmsc_dev_s *priv = (struct usbmsc_dev_s *)arg; + FAR struct usbmsc_dev_s *priv; irqstate_t flags; uint16_t eventset; int ret; + uvdbg("Started\n"); + + /* Get the SCSI state data handed off from the initialization logic */ + + priv = g_usbmsc_handoff; + DEBUGASSERT(priv); + + g_usbmsc_handoff = NULL; + usbmsc_synch_signal(priv); + /* This thread is started before the USB storage class is fully initialized. * wait here until we are told to begin. Start in the NOTINITIALIZED state */ - pthread_mutex_lock(&priv->mutex); + uvdbg("Waiting to be signalled\n"); + usbmsc_scsi_lock(priv); priv->thstate = USBMSC_STATE_STARTED; while ((priv->theventset & USBMSC_EVENT_READY) != 0 && (priv->theventset & USBMSC_EVENT_TERMINATEREQUEST) != 0) { - pthread_cond_wait(&priv->cond, &priv->mutex); + usbmsc_scsi_wait(priv); } + uvdbg("Running\n"); + /* Transition to the INITIALIZED/IDLE state */ priv->thstate = USBMSC_STATE_IDLE; eventset = priv->theventset; priv->theventset = USBMSC_EVENT_NOEVENTS; - pthread_mutex_unlock(&priv->mutex); + usbmsc_scsi_unlock(priv); /* Then loop until we are asked to terminate */ @@ -2563,11 +2660,11 @@ void *usbmsc_workerthread(void *arg) * interrupts (to eliminate race conditions with USB interrupt handling. */ - pthread_mutex_lock(&priv->mutex); + usbmsc_scsi_lock(priv); flags = irqsave(); if (priv->theventset == USBMSC_EVENT_NOEVENTS) { - pthread_cond_wait(&priv->cond, &priv->mutex); + usbmsc_scsi_wait(priv); } /* Sample any events before re-enabling interrupts. Any events that @@ -2577,7 +2674,7 @@ void *usbmsc_workerthread(void *arg) eventset = priv->theventset; priv->theventset = USBMSC_EVENT_NOEVENTS; - pthread_mutex_unlock(&priv->mutex); + usbmsc_scsi_unlock(priv); /* Were we awakened by some event that requires immediate action? * @@ -2614,7 +2711,7 @@ void *usbmsc_workerthread(void *arg) /* These events require that a new configuration be established */ - if ((eventset & (USBMSC_EVENT_CFGCHANGE|USBMSC_EVENT_IFCHANGE)) != 0) + if ((eventset & (USBMSC_EVENT_CFGCHANGE)) != 0) { usbmsc_setconfig(priv, priv->thvalue); } @@ -2689,8 +2786,54 @@ void *usbmsc_workerthread(void *arg) /* Transition to the TERMINATED state and exit */ priv->thstate = USBMSC_STATE_TERMINATED; - pthread_mutex_lock(&priv->mutex); /* REVISIT: See comments in usbmsc_uninitialize() */ - pthread_cond_signal(&priv->cond); - pthread_mutex_unlock(&priv->mutex); - return NULL; + usbmsc_synch_signal(priv); + return EXIT_SUCCESS; +} + +/**************************************************************************** + * 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) +{ + irqstate_t flags = irqsave(); + + /* A flag is used to prevent driving up the semaphore count. This function + * is called (primarily) from interrupt level logic so we must disable + * interrupts momentarily to assure that test of the flag and the increment + * of the semaphore count are atomic. + */ + + flags = irqsave(); + if (priv->thwaiting) + { + priv->thwaiting = false; + sem_post(&priv->thwaitsem); + } + + irqrestore(flags); +} + +/**************************************************************************** + * Name: usbmsc_scsi_lock + * + * Description: + * Get exclusive access to SCSI state data. + * + ****************************************************************************/ + +void usbmsc_scsi_lock(FAR struct usbmsc_dev_s *priv) +{ + int ret; + + do + { + ret = sem_wait(&priv->thlock); + DEBUGASSERT(ret == OK || errno == EINTR); + } + while (ret < 0); }