Important fixed to USB storage and SCSI state machine from David Hewson
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@2571 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
parent
5a39e4e761
commit
7b7d120995
@ -1,7 +1,7 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* drivers/usbdev/usbdev_storage.c
|
* drivers/usbdev/usbdev_storage.c
|
||||||
*
|
*
|
||||||
* Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
|
* Copyright (C) 2008-2010 Gregory Nutt. All rights reserved.
|
||||||
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
|
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
|
||||||
*
|
*
|
||||||
* Mass storage class device. Bulk-only with SCSI subclass.
|
* Mass storage class device. Bulk-only with SCSI subclass.
|
||||||
@ -78,6 +78,21 @@
|
|||||||
* Pre-processor Definitions
|
* Pre-processor Definitions
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
|
/* Configuration ************************************************************/
|
||||||
|
|
||||||
|
/* Race condition workaround found by David Hewson. This race condition
|
||||||
|
* "seems to relate to stalling the endpoint when a short response is
|
||||||
|
* generated which causes a residue to exist when the CSW would be returned.
|
||||||
|
* I think there's two issues here. The first being if the transfer which
|
||||||
|
* had just been queued before the stall had not completed then it wouldn’t
|
||||||
|
* then complete once the endpoint was stalled? The second is that the
|
||||||
|
* subsequent transfer for the CSW would be dropped on the floor (by the
|
||||||
|
* epsubmit() function) if the end point was still stalled as the control
|
||||||
|
* transfer to resume it hadn't occurred."
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define CONFIG_USBSTRG_RACEWAR 1
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Private Types
|
* Private Types
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
@ -1702,13 +1717,13 @@ static int usbstrg_cmdparsestate(FAR struct usbstrg_dev_s *priv)
|
|||||||
* case SCSI_CMD_REASSIGNBLOCKS: * 0x07 Optional */
|
* case SCSI_CMD_REASSIGNBLOCKS: * 0x07 Optional */
|
||||||
|
|
||||||
case SCSI_CMD_READ6: /* 0x08 Mandatory */
|
case SCSI_CMD_READ6: /* 0x08 Mandatory */
|
||||||
return usbstrg_cmdread6(priv);
|
ret = usbstrg_cmdread6(priv);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* * 0x09 Vendor specific */
|
/* * 0x09 Vendor specific */
|
||||||
|
|
||||||
case SCSI_CMD_WRITE6: /* 0x0a Optional */
|
case SCSI_CMD_WRITE6: /* 0x0a Optional */
|
||||||
return usbstrg_cmdwrite6(priv);
|
ret = usbstrg_cmdwrite6(priv);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* case SCSI_CMD_SEEK6: * 0x0b Obsolete
|
/* case SCSI_CMD_SEEK6: * 0x0b Obsolete
|
||||||
@ -1757,13 +1772,13 @@ static int usbstrg_cmdparsestate(FAR struct usbstrg_dev_s *priv)
|
|||||||
|
|
||||||
/* * 0x26-27 Vendor specific */
|
/* * 0x26-27 Vendor specific */
|
||||||
case SCSI_CMD_READ10: /* 0x28 Mandatory */
|
case SCSI_CMD_READ10: /* 0x28 Mandatory */
|
||||||
return usbstrg_cmdread10(priv);
|
ret = usbstrg_cmdread10(priv);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* * 0x29 Vendor specific */
|
/* * 0x29 Vendor specific */
|
||||||
|
|
||||||
case SCSI_CMD_WRITE10: /* 0x2a Optional */
|
case SCSI_CMD_WRITE10: /* 0x2a Optional */
|
||||||
return usbstrg_cmdwrite10(priv);
|
ret = usbstrg_cmdwrite10(priv);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* case SCSI_CMD_SEEK10: * 0x2b Obsolete
|
/* case SCSI_CMD_SEEK10: * 0x2b Obsolete
|
||||||
@ -1839,11 +1854,11 @@ static int usbstrg_cmdparsestate(FAR struct usbstrg_dev_s *priv)
|
|||||||
* case SCSI_CMD_MOVEMEDIUMATTACHED: * 0xa7 Optional (MCHNGR==0) */
|
* case SCSI_CMD_MOVEMEDIUMATTACHED: * 0xa7 Optional (MCHNGR==0) */
|
||||||
|
|
||||||
case SCSI_CMD_READ12: /* 0xa8 Optional */
|
case SCSI_CMD_READ12: /* 0xa8 Optional */
|
||||||
return usbstrg_cmdread12(priv);
|
ret = usbstrg_cmdread12(priv);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SCSI_CMD_WRITE12: /* 0xaa Optional */
|
case SCSI_CMD_WRITE12: /* 0xaa Optional */
|
||||||
return usbstrg_cmdwrite12(priv);
|
ret = usbstrg_cmdwrite12(priv);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* case SCSI_CMD_READMEDIASERIALNUMBER: * 0xab Optional
|
/* case SCSI_CMD_READMEDIASERIALNUMBER: * 0xab Optional
|
||||||
@ -1871,63 +1886,69 @@ static int usbstrg_cmdparsestate(FAR struct usbstrg_dev_s *priv)
|
|||||||
}
|
}
|
||||||
pthread_mutex_unlock(&priv->mutex);
|
pthread_mutex_unlock(&priv->mutex);
|
||||||
|
|
||||||
/* All commands come through this path (EXCEPT read6/10/12 and write6/10/12).
|
/* Is a response required? (Not for read6/10/12 and write6/10/12). */
|
||||||
* For all other commands, the following setup is expected for the response
|
|
||||||
* based on data direction:
|
|
||||||
*
|
|
||||||
* For direction NONE:
|
|
||||||
* 1. priv->u.alloclen == 0
|
|
||||||
* 2. priv->nreqbytes == 0
|
|
||||||
*
|
|
||||||
* For direction = device-to-host:
|
|
||||||
* 1. priv->u.alloclen == allocation length; space set aside by the
|
|
||||||
* host to receive the device data. The size of the response
|
|
||||||
* cannot exceed this value.
|
|
||||||
* 2. response data is in the request currently at the head of
|
|
||||||
* the priv->wrreqlist queue. priv->nreqbytes is set to the length
|
|
||||||
* of data in the response.
|
|
||||||
*
|
|
||||||
* For direction host-to-device
|
|
||||||
* At present, there are no supported commands that should have host-to-device
|
|
||||||
* transfers (except write6/10/12 and that command logic does not take this
|
|
||||||
* path. The 'residue' is left at the full host-to-device data size.
|
|
||||||
*
|
|
||||||
* For all:
|
|
||||||
* ret set to <0 if an error occurred in parsing the commands.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* For from device-to-hose operations, the residue is the expected size
|
if (priv->thstate == USBSTRG_STATE_CMDPARSE)
|
||||||
* (the initial value of 'residue') minus the amount actually returned
|
|
||||||
* in the response:
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (priv->cbwdir == USBSTRG_FLAGS_DIRDEVICE2HOST)
|
|
||||||
{
|
{
|
||||||
/* The number of bytes in the response cannot exceed the host
|
/* All commands come through this path (EXCEPT read6/10/12 and write6/10/12).
|
||||||
* 'allocation length' in the command.
|
* For all other commands, the following setup is expected for the response
|
||||||
|
* based on data direction:
|
||||||
|
*
|
||||||
|
* For direction NONE:
|
||||||
|
* 1. priv->u.alloclen == 0
|
||||||
|
* 2. priv->nreqbytes == 0
|
||||||
|
*
|
||||||
|
* For direction = device-to-host:
|
||||||
|
* 1. priv->u.alloclen == allocation length; space set aside by the
|
||||||
|
* host to receive the device data. The size of the response
|
||||||
|
* cannot exceed this value.
|
||||||
|
* 2. response data is in the request currently at the head of
|
||||||
|
* the priv->wrreqlist queue. priv->nreqbytes is set to the length
|
||||||
|
* of data in the response.
|
||||||
|
*
|
||||||
|
* For direction host-to-device
|
||||||
|
* At present, there are no supported commands that should have host-to-device
|
||||||
|
* transfers (except write6/10/12 and that command logic does not take this
|
||||||
|
* path. The 'residue' is left at the full host-to-device data size.
|
||||||
|
*
|
||||||
|
* For all:
|
||||||
|
* ret set to <0 if an error occurred in parsing the commands.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (priv->nreqbytes > priv->u.alloclen)
|
/* For from device-to-hose operations, the residue is the expected size
|
||||||
|
* (the initial value of 'residue') minus the amount actually returned
|
||||||
|
* in the response:
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (priv->cbwdir == USBSTRG_FLAGS_DIRDEVICE2HOST)
|
||||||
{
|
{
|
||||||
priv->nreqbytes = priv->u.alloclen;
|
/* The number of bytes in the response cannot exceed the host
|
||||||
|
* 'allocation length' in the command.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (priv->nreqbytes > priv->u.alloclen)
|
||||||
|
{
|
||||||
|
priv->nreqbytes = priv->u.alloclen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The residue is then the number of bytes that were not sent */
|
||||||
|
|
||||||
|
priv->residue -= priv->nreqbytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The residue is then the number of bytes that were not sent */
|
/* On return, we need the following:
|
||||||
|
*
|
||||||
|
* 1. Setup for CMDFINISH state if appropriate
|
||||||
|
* 2. priv->thstate set to either CMDPARSE if no buffer was available or
|
||||||
|
* CMDFINISH to send the response
|
||||||
|
* 3. Return OK to continue; <0 to wait for the next event
|
||||||
|
*/
|
||||||
|
|
||||||
priv->residue -= priv->nreqbytes;
|
usbtrace(TRACE_CLASSSTATE(USBSTRG_CLASSSTATE_CMDPARSECMDFINISH), priv->cdb[0]);
|
||||||
|
priv->thstate = USBSTRG_STATE_CMDFINISH;
|
||||||
|
ret = OK;
|
||||||
}
|
}
|
||||||
|
return ret;
|
||||||
/* On return, we need the following:
|
|
||||||
*
|
|
||||||
* 1. Setup for CMDFINISH state if appropriate
|
|
||||||
* 2. priv->thstate set to either CMDPARSE if no buffer was available or
|
|
||||||
* CMDFINISH to send the response
|
|
||||||
* 3. Return OK to continue; <0 to wait for the next event
|
|
||||||
*/
|
|
||||||
|
|
||||||
usbtrace(TRACE_CLASSSTATE(USBSTRG_CLASSSTATE_CMDPARSECMDFINISH), priv->cdb[0]);
|
|
||||||
priv->thstate = USBSTRG_STATE_CMDFINISH;
|
|
||||||
return OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
@ -2301,7 +2322,21 @@ static int usbstrg_cmdfinishstate(FAR struct usbstrg_dev_s *priv)
|
|||||||
if (priv->residue > 0)
|
if (priv->residue > 0)
|
||||||
{
|
{
|
||||||
usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_CMDFINISHRESIDUE), (uint16_t)priv->residue);
|
usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_CMDFINISHRESIDUE), (uint16_t)priv->residue);
|
||||||
|
|
||||||
|
#ifdef CONFIG_USBSTRG_RACEWAR
|
||||||
|
/* (See description of the workaround at the top of the file).
|
||||||
|
* First, wait for the transfer to complete, then stall the endpoint
|
||||||
|
*/
|
||||||
|
|
||||||
|
usleep (100000);
|
||||||
(void)EP_STALL(priv->epbulkin);
|
(void)EP_STALL(priv->epbulkin);
|
||||||
|
|
||||||
|
/* now wait for stall to go away .... */
|
||||||
|
|
||||||
|
usleep (100000);
|
||||||
|
#else
|
||||||
|
(void)EP_STALL(priv->epbulkin);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -2513,6 +2548,7 @@ void *usbstrg_workerthread(void *arg)
|
|||||||
|
|
||||||
eventset = priv->theventset;
|
eventset = priv->theventset;
|
||||||
priv->theventset = USBSTRG_EVENT_NOEVENTS;
|
priv->theventset = USBSTRG_EVENT_NOEVENTS;
|
||||||
|
pthread_mutex_unlock(&priv->mutex);
|
||||||
|
|
||||||
/* Were we awakened by some event that requires immediate action?
|
/* Were we awakened by some event that requires immediate action?
|
||||||
*
|
*
|
||||||
|
@ -1717,7 +1717,6 @@ int usbstrg_unbindlun(FAR void *handle, unsigned int lunno)
|
|||||||
if (lun->inode == NULL)
|
if (lun->inode == NULL)
|
||||||
{
|
{
|
||||||
usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_LUNNOTBOUND), 0);
|
usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_LUNNOTBOUND), 0);
|
||||||
pthread_mutex_lock(&priv->mutex);
|
|
||||||
ret = -EBUSY;
|
ret = -EBUSY;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
Loading…
x
Reference in New Issue
Block a user