Reimagine the USB MSC worker thread as a kernel thread (instead of a pthread)
This commit is contained in:
parent
93380d8156
commit
b951732a2b
14
ChangeLog
14
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).
|
||||
|
||||
|
@ -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
|
||||
|
@ -66,13 +66,13 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <queue.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <nuttx/kmalloc.h>
|
||||
#include <nuttx/kthread.h>
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/fs/fs.h>
|
||||
#include <nuttx/usb/usb.h>
|
||||
@ -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);
|
||||
|
@ -47,8 +47,8 @@
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
#include <queue.h>
|
||||
#include <semaphore.h>
|
||||
|
||||
#include <nuttx/fs/fs.h>
|
||||
#include <nuttx/usb/storage.h>
|
||||
@ -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)
|
||||
|
@ -61,12 +61,13 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <queue.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <nuttx/kthread.h>
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/scsi.h>
|
||||
#include <nuttx/usb/storage.h>
|
||||
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user