msdd.c

Go to the documentation of this file.
00001 /**************************************************************************/
00015 #include "em_usb.h"
00016 #include "em_cmu.h"
00017 #include "em_gpio.h"
00018 #include "msdbot.h"
00019 #include "msdscsi.h"
00020 #include "msdd.h"
00021 #include "msddmedia.h"
00022 
00023 /**************************************************************************/
00058 /*** Typedef's and defines. ***/
00059 #define MSD_DIR_DATA_OUT    0
00060 #define MSD_DIR_DATA_IN     1
00061 #define MSD_MAX_BURST       32768U          /* 32 * 1024 */
00062 
00063 /**************************************************************************/
00066 typedef enum
00067 {
00068   MSDD_IDLE                 = 0,
00069   MSDD_WAITFOR_CBW          = 1,
00070   MSDD_WAITFOR_RECOVERY     = 2,
00071   MSDD_SEND_CSW             = 3,
00072   MSDD_WAIT_FOR_INUNSTALLED = 4,
00073   MSDD_STALL_IN             = 5,
00074   MSDD_ACCESS_INDIRECT      = 6,
00075   MSDD_WRITE_INDIRECT       = 7,
00076   MSDD_DO_CMD_TASK          = 8,
00077 } msdState_TypeDef;
00078 
00079 /*** Function prototypes. ***/
00080 
00081 static int            CbwCallback(USB_Status_TypeDef status, uint32_t xferred, uint32_t remaining);
00082 __STATIC_INLINE bool  CswMeaningful(void);
00083 __STATIC_INLINE bool  CswValid(void);
00084 __STATIC_INLINE void  EnableNextCbw(void);
00085 static void           ProcessScsiCdb(void);
00086 __STATIC_INLINE void  SendCsw(void);
00087 static void           UsbXferBotData(uint8_t *data, uint32_t len, USB_XferCompleteCb_TypeDef cb);
00088 static void           XferBotData(uint32_t length);
00089 static int            XferBotDataCallback(USB_Status_TypeDef status, uint32_t xferred, uint32_t remaining);
00090 static int            XferBotDataIndirectCallback(USB_Status_TypeDef status, uint32_t xferred, uint32_t remaining);
00091 
00092 /*** Variables ***/
00093 
00094 /* Storage for one CBW */
00095 STATIC_UBUF(cbw, USB_FS_BULK_EP_MAXSIZE);
00096 static MSDBOT_CBW_TypeDef *pCbw = (MSDBOT_CBW_TypeDef*) &cbw;
00097 
00098 EFM32_ALIGN(4)
00099 /* Storage for one CSW */
00100 static MSDBOT_CSW_TypeDef csw __attribute__ ((aligned(4)));
00101 static MSDBOT_CSW_TypeDef *pCsw = &csw;
00102 
00103 STATIC_UBUF(mediaBuffer, MEDIA_BUFSIZ);  /* Intermediate media storage buffer */
00104 
00105 static MSDD_CmdStatus_TypeDef CmdStatus;
00106 static MSDD_CmdStatus_TypeDef *pCmdStatus = &CmdStatus;
00107 static msdState_TypeDef       savedState; /* MSD state machine state. */
00108 static int ledPort;
00109 static unsigned int ledPin;
00110 
00111 /**************************************************************************/
00114 EFM32_ALIGN(4)
00115 static const MSDSCSI_InquiryData_TypeDef InquiryData __attribute__ ((aligned(4))) =
00116 {
00117   { .PeripheralDeviceType = 0, .PeripheralQualifier = 0 }, /* Block device  */
00118   { .Reserved1            = 0, .Removable           = 1 },
00119 
00120   .Version = 5,                                       /* T10 SPC-3 compliant */
00121 
00122   { .ResponseDataFormat = 2,                          /* T10 SPC-3 compliant reponse data */
00123     .HiSup = 0, .NormACA = 0, .Obsolete1 = 0 },
00124 
00125   .AdditionalLength = 31,
00126 
00127   { .Protect = 0, .Reserved2 = 0, .ThirdPartyCode = 0,
00128     .Tpgs    = 0, .Acc       = 0, .Sccs           = 0 },
00129 
00130   { .Addr16 = 0, .Obsolete2 = 0, .MChngr = 0, .MultiP = 0,
00131     .Vs1    = 0, .EncServ   = 0, .BQue   = 0 },
00132 
00133   { .Vs2  = 0, .CmdQue = 0, .Obsolete3 = 0, .Linked = 0,
00134     .Sync = 0, .Wbus16 = 0, .Obsolete4 = 0 },
00135 
00136   .T10VendorId          = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' },
00137   .ProductId            = { 'E', 'F', 'M', '3', '2', ' ', 'M', 'S', 'D', ' ', 'D', 'e', 'v', 'i', 'c', 'e' },
00138   .ProductRevisionLevel ={ '1', '.', '0', '0' }
00139 };
00140 
00141 /**************************************************************************/
00146 EFM32_ALIGN(4)
00147 static const MSDSCSI_RequestSenseData_TypeDef NoSenseData __attribute__ ((aligned(4))) =
00148 {
00149   { .ResponseCode = 0x70, .Valid = 0 },
00150   .Obsolete = 0,
00151   { .SenseKey = 0, .Reserved =0, .Ili = 0, .Eom = 0, .FileMark = 0 },
00152   .Information      = 0,
00153   .AdditionalLength = 10,
00154   .CmdSpecificInfo  = 0,
00155   .Asc              = 0,
00156   .Ascq             = 0,
00157   .Fruc             = 0,
00158   { .SenseKeySpecific1 = 0, .Sksv = 0 },
00159   .SenseKeySpecific2 = 0,
00160   .SenseKeySpecific3 = 0
00161 };
00162 
00163 /**************************************************************************/
00169 EFM32_ALIGN(4)
00170 static const MSDSCSI_RequestSenseData_TypeDef IllegalSenseData __attribute__ ((aligned(4))) =
00171 {
00172   { .ResponseCode = 0x70, .Valid = 0 },
00173   .Obsolete = 0,
00174   { .SenseKey = 5,                          /* SensKey = 5 => ILLEGAL REQUEST */
00175     .Reserved = 0, .Ili = 0, .Eom = 0, .FileMark = 0 },
00176   .Information      = 0,
00177   .AdditionalLength = 10,
00178   .CmdSpecificInfo  = 0,
00179   .Asc              = 0x24,    /* Asc/Ascq = 0x24/0x00 => INVALID FIELD IN CDB*/
00180   .Ascq             = 0,
00181   .Fruc             = 0,
00182   { .SenseKeySpecific1 = 0, .Sksv  = 0 },
00183   .SenseKeySpecific2 = 0,
00184   .SenseKeySpecific3 = 0
00185 };
00186 
00187 static volatile msdState_TypeDef        msdState;     
00188 static MSDSCSI_RequestSenseData_TypeDef *pSenseData;  
00192 /**************************************************************************/
00202 void MSDD_Init(int activityLedPort, uint32_t activityLedPin)
00203 {
00204   if ( ( sizeof(MSDSCSI_Read10_TypeDef)           != SCSI_READ10_LEN           ) ||
00205        ( sizeof(MSDSCSI_Write10_TypeDef)          != SCSI_WRITE10_LEN          ) ||
00206        ( sizeof(MSDSCSI_Verify10_TypeDef)         != SCSI_VERIFY10_LEN         ) ||
00207        ( sizeof(MSDSCSI_RequestSense_TypeDef)     != SCSI_REQUESTSENSE_LEN     ) ||
00208        ( sizeof(InquiryData)                      != SCSI_INQUIRYDATA_LEN      ) ||
00209        ( sizeof(NoSenseData)                      != SCSI_REQUESTSENSEDATA_LEN ) ||
00210        ( sizeof(IllegalSenseData)                 != SCSI_REQUESTSENSEDATA_LEN ) ||
00211        ( sizeof(MSDSCSI_ReadCapacity_TypeDef)     != SCSI_READCAPACITY_LEN     ) ||
00212        ( sizeof(MSDSCSI_ReadCapacityData_TypeDef) != SCSI_READCAPACITYDATA_LEN )    )
00213   {
00214     DEBUG_USB_API_PUTS("\nMSDD_Init(), typedef size error");
00215     EFM_ASSERT(false);
00216     return;
00217   }
00218 
00219   if ( ( activityLedPort >= gpioPortA ) && ( activityLedPort <= gpioPortF ) )
00220     ledPort = activityLedPort;
00221   else
00222     ledPort = -1;
00223 
00224   ledPin     = activityLedPin;
00225   msdState   = MSDD_IDLE;
00226   pSenseData = (MSDSCSI_RequestSenseData_TypeDef*) &NoSenseData;
00227 
00228   if ( ledPort != -1 )
00229   {
00230     CMU_ClockEnable(cmuClock_GPIO, true);
00231     GPIO_PinModeSet((GPIO_Port_TypeDef)ledPort, ledPin, gpioModePushPull, 0);
00232   }
00233 }
00234 
00235 /**************************************************************************/
00244 bool MSDD_Handler(void)
00245 {
00246   static uint32_t len;        /* Note: len is static ! */
00247 
00248   switch (msdState)
00249   {
00250   case MSDD_ACCESS_INDIRECT:
00251     if (pCmdStatus->xferLen)
00252     {
00253       len = EFM32_MIN(pCmdStatus->xferLen, pCmdStatus->maxBurst);
00254 
00255       msdState = MSDD_IDLE;
00256       if (pCmdStatus->direction)
00257       {
00258         MSDDMEDIA_Read(pCmdStatus, mediaBuffer, len / 512);
00259       }
00260       UsbXferBotData(mediaBuffer, len, XferBotDataIndirectCallback);
00261     }
00262     else
00263     {
00264       /* We are done ! */
00265       msdState = savedState;
00266 
00267       if (msdState == MSDD_SEND_CSW)
00268       {
00269         SendCsw();
00270         EnableNextCbw();
00271         msdState = MSDD_WAITFOR_CBW;
00272       }
00273 
00274       else if (msdState == MSDD_STALL_IN)
00275       {
00276         USBD_StallEp(MSD_BULK_IN);
00277         msdState = MSDD_WAIT_FOR_INUNSTALLED;
00278       }
00279     }
00280     break;
00281 
00282   case MSDD_WRITE_INDIRECT:
00283     MSDDMEDIA_Write(pCmdStatus, mediaBuffer, len / 512);
00284     pCmdStatus->lba += len / 512;
00285     msdState         = MSDD_ACCESS_INDIRECT;
00286     break;
00287 
00288   case MSDD_DO_CMD_TASK:
00289     if (pCbw->CBWCB[ 0 ] == SCSI_STARTSTOP_UNIT)
00290     {
00291       MSDDMEDIA_Flush();
00292     }
00293     /* else if ( .... )  Add more when needed. */
00294     SendCsw();
00295     EnableNextCbw();
00296     msdState = MSDD_WAITFOR_CBW;
00297     break;
00298 
00299   default:
00300     break;
00301   }
00302   return (msdState == MSDD_WAITFOR_CBW) || (msdState == MSDD_IDLE);
00303 }
00304 
00305 /**************************************************************************/
00317 int MSDD_SetupCmd(const USB_Setup_TypeDef *setup)
00318 {
00319   int retVal;
00320   static uint32_t tmp;
00321 
00322   retVal = USB_STATUS_REQ_UNHANDLED;
00323 
00324   /* Check if it is MSD class command: "Bulk-Only Mass Storage Reset" */
00325 
00326   if ( ( setup->Type      == USB_SETUP_TYPE_CLASS          ) &&
00327        ( setup->Direction == USB_SETUP_DIR_OUT             ) &&
00328        ( setup->Recipient == USB_SETUP_RECIPIENT_INTERFACE ) &&
00329        ( setup->bRequest  == USB_MSD_BOTRESET              ) &&
00330        ( setup->wValue    == 0                             ) &&
00331        ( setup->wIndex    == MSD_INTERFACE_NO              ) &&
00332        ( setup->wLength   == 0                             )    )
00333   {
00334     if (msdState == MSDD_WAITFOR_RECOVERY)
00335     {
00336       msdState = MSDD_IDLE;
00337     }
00338     retVal = USB_STATUS_OK;
00339   }
00340 
00341 
00342   /* Check if it is MSD class command: "Get Max LUN" */
00343 
00344   else if ( ( setup->Type      == USB_SETUP_TYPE_CLASS          ) &&
00345             ( setup->Direction == USB_SETUP_DIR_IN              ) &&
00346             ( setup->Recipient == USB_SETUP_RECIPIENT_INTERFACE ) &&
00347             ( setup->bRequest  == USB_MSD_GETMAXLUN             ) &&
00348             ( setup->wValue    == 0                             ) &&
00349             ( setup->wIndex    == MSD_INTERFACE_NO              ) &&
00350             ( setup->wLength   == 1                             )    )
00351   {
00352     /* Only one LUN (i.e. no support for multiple LUN's). Reply "0". */
00353     tmp    = 0;
00354     retVal = USBD_Write(0, (void*) &tmp, 1, NULL);
00355   }
00356 
00357 
00358   /* Check if it is a standard CLEAR_FEATURE endpoint command */
00359 
00360   else if ( ( setup->Type      == USB_SETUP_TYPE_STANDARD      ) &&
00361             ( setup->Direction == USB_SETUP_DIR_OUT            ) &&
00362             ( setup->Recipient == USB_SETUP_RECIPIENT_ENDPOINT ) &&
00363             ( setup->bRequest  == CLEAR_FEATURE                ) &&
00364             ( setup->wValue    == USB_FEATURE_ENDPOINT_HALT    ) &&
00365             ( setup->wLength   == 0                            )    )
00366   {
00367     if ( ( ( setup->wIndex & 0xFF) == MSD_BULK_OUT ) ||
00368          ( ( setup->wIndex & 0xFF) == MSD_BULK_IN  )    )
00369     {
00370       retVal = USB_STATUS_OK;
00371 
00372       /* Dont unstall ep's when waiting for reset recovery */
00373       if (msdState != MSDD_WAITFOR_RECOVERY)
00374       {
00375         retVal = USBD_UnStallEp(setup->wIndex & 0xFF);
00376 
00377         if ((setup->wIndex & 0xFF) == MSD_BULK_IN)
00378         {
00379           if (msdState == MSDD_WAIT_FOR_INUNSTALLED)
00380           {
00381             SendCsw();
00382             EnableNextCbw();
00383             msdState = MSDD_WAITFOR_CBW;
00384           }
00385         }
00386         else
00387         {
00388           EnableNextCbw();
00389           msdState = MSDD_WAITFOR_CBW;
00390         }
00391       }
00392     }
00393   }
00394 
00395   return retVal;
00396 }
00397 
00398 /**************************************************************************/
00408 void MSDD_StateChangeEvent( USBD_State_TypeDef oldState,
00409                             USBD_State_TypeDef newState )
00410 {
00411   if (newState == USBD_STATE_CONFIGURED)
00412   {
00413     /* We have been configured, start MSD functionality ! */
00414     EnableNextCbw();
00415     msdState = MSDD_WAITFOR_CBW;
00416   }
00417 
00418   else if ((oldState == USBD_STATE_CONFIGURED) &&
00419            (newState != USBD_STATE_SUSPENDED))
00420   {
00421     /* We have been de-configured */
00422     msdState = MSDD_IDLE;
00423   }
00424 
00425   else if (newState == USBD_STATE_SUSPENDED)
00426   {
00427     /* We have been suspended.                     */
00428     msdState = MSDD_IDLE;
00429 
00430     /* Reduce current consumption to below 2.5 mA. */
00431   }
00432 }
00433 
00436 /**************************************************************************/
00453 static int CbwCallback(USB_Status_TypeDef status,
00454                        uint32_t xferred, uint32_t remaining)
00455 {
00456   (void) remaining;
00457 
00458   if ( ( msdState == MSDD_WAITFOR_CBW ) &&
00459        ( status   == USB_STATUS_OK    ) &&
00460        ( xferred  == CBW_LEN          ) &&
00461        ( CswValid()                   ) &&
00462        ( CswMeaningful()              )    )
00463   {
00464     if ( ledPort != -1 )
00465       GPIO_PinOutToggle((GPIO_Port_TypeDef)ledPort, ledPin);
00466 
00467     /* Check the SCSI command descriptor block (CDB) */
00468     ProcessScsiCdb();
00469 
00470     if (pCmdStatus->valid)
00471       pCsw->bCSWStatus = USB_CLASS_MSD_CSW_CMDPASSED;
00472     else
00473       pCsw->bCSWStatus = USB_CLASS_MSD_CSW_CMDFAILED;
00474 
00475     pCsw->dCSWSignature   = CSW_SIGNATURE;
00476     pCsw->dCSWTag         = pCbw->dCBWTag;
00477     pCsw->dCSWDataResidue = pCbw->dCBWDataTransferLength;
00478 
00479     /* Check the "thirteen cases" */
00480 
00481     if ((pCbw->dCBWDataTransferLength != 0) &&
00482         (pCbw->Direction != pCmdStatus->direction))
00483     {
00484       /* Handle cases 8 and 10 */
00485       pCsw->bCSWStatus = USB_CLASS_MSD_CSW_PHASEERROR;
00486 
00487       if (pCbw->Direction)
00488       {
00489         /* Host expects to receive data, case 8 */
00490         USBD_StallEp(MSD_BULK_IN);
00491         msdState = MSDD_WAIT_FOR_INUNSTALLED;
00492       }
00493       else
00494       {
00495         /* Host expects to send data, case 10 */
00496         USBD_StallEp(MSD_BULK_OUT);
00497         SendCsw();
00498         msdState = MSDD_IDLE;
00499       }
00500     }
00501 
00502     else if (pCbw->Direction || (pCbw->dCBWDataTransferLength == 0))
00503     {
00504       /* SCSI IN commands or commands without data phase */
00505       /* Handle cases 1-7 */
00506 
00507       if (pCbw->dCBWDataTransferLength == 0)
00508       {
00509         /* Host expects no data, case 1, 2 or 3 */
00510         if (pCmdStatus->xferLen)
00511         {
00512           /* Device has data to transmit, case 2 & 3 */
00513           pCsw->bCSWStatus = USB_CLASS_MSD_CSW_PHASEERROR;
00514         }
00515 
00516         if ((pCmdStatus->xferLen == 0) &&
00517             (pCmdStatus->xferType == XFER_INDIRECT))
00518         {
00519           /* Commands with no data phase which require timeconsuming  */
00520           /* processing are executed in MSDD_Handler()                */
00521           msdState = MSDD_DO_CMD_TASK;
00522         }
00523         else
00524         {
00525           SendCsw();
00526           EnableNextCbw();
00527           msdState = MSDD_WAITFOR_CBW;
00528         }
00529       }
00530       else if (pCbw->dCBWDataTransferLength == pCmdStatus->xferLen)
00531       {
00532         /* Host and device agree on transferlength, case 6 */
00533         /* Send data to host */
00534         msdState = MSDD_SEND_CSW;
00535         XferBotData(pCmdStatus->xferLen);
00536       }
00537       else if (pCbw->dCBWDataTransferLength > pCmdStatus->xferLen)
00538       {
00539         /* Host expects more data than device can provide, case 4 and 5 */
00540 
00541         if (pCmdStatus->xferLen > 0)
00542         {
00543           /* Device has data, case 5 */
00544           /* Send data to host */
00545           msdState = MSDD_STALL_IN;
00546           XferBotData(pCmdStatus->xferLen);
00547         }
00548         else
00549         {
00550           /* Device has no data, case 4 */
00551           USBD_StallEp(MSD_BULK_IN);
00552           msdState = MSDD_WAIT_FOR_INUNSTALLED;
00553         }
00554       }
00555       else
00556       {
00557         /* Host expects less data than device will provide, case 7 */
00558         pCsw->bCSWStatus = USB_CLASS_MSD_CSW_PHASEERROR;
00559         /* Send data to host */
00560         msdState = MSDD_SEND_CSW;
00561         XferBotData(pCbw->dCBWDataTransferLength);
00562       }
00563     }
00564 
00565     else /* Host Direction is OUT and Host transferlength > 0 */
00566     {
00567       /* SCSI OUT commands */
00568       /* Handle cases 9, 11, 12 and 13 */
00569 
00570       if (pCbw->dCBWDataTransferLength == pCmdStatus->xferLen)
00571       {
00572         /* Host and device agree on transferlength, case 12 */
00573 
00574         /* Read data from host */
00575         msdState = MSDD_SEND_CSW;
00576         XferBotData(pCmdStatus->xferLen);
00577       }
00578       else if (pCbw->dCBWDataTransferLength > pCmdStatus->xferLen)
00579       {
00580         /* Host intend to send more data than device expects, case 9 & 11 */
00581         pCsw->bCSWStatus = USB_CLASS_MSD_CSW_CMDFAILED;
00582         USBD_StallEp(MSD_BULK_OUT);
00583         SendCsw();
00584         msdState = MSDD_IDLE;
00585       }
00586       else
00587       {
00588         /* Host has less data than device expects to receive, case 13 */
00589         pCsw->bCSWStatus = USB_CLASS_MSD_CSW_PHASEERROR;
00590         USBD_StallEp(MSD_BULK_OUT);
00591         SendCsw();
00592         msdState = MSDD_IDLE;
00593       }
00594     }
00595     return USB_STATUS_OK;
00596   }
00597 
00598   if ((status == USB_STATUS_OK) &&
00599       (USBD_GetUsbState() == USBD_STATE_CONFIGURED))
00600   {
00601     /* Stall both Ep's and wait for reset recovery */
00602     USBD_StallEp(MSD_BULK_OUT);
00603     USBD_StallEp(MSD_BULK_IN);
00604     msdState = MSDD_WAITFOR_RECOVERY;
00605   }
00606 
00607   return USB_STATUS_OK;
00608 }
00609 
00610 /**************************************************************************/
00615 __STATIC_INLINE bool CswMeaningful(void)
00616 {
00617   if ( ( pCbw->Reserved1 == 0 ) &&
00618        ( pCbw->Obsolete  == 0 ) &&
00619        ( pCbw->Reserved2 == 0 ) &&
00620        ( pCbw->Lun       == 0 ) &&
00621        ( pCbw->Reserved3 == 0 )    )
00622   {
00623     return true;
00624   }
00625 
00626   return false;
00627 }
00628 
00629 /**************************************************************************/
00634 __STATIC_INLINE bool CswValid(void)
00635 {
00636   return pCbw->dCBWSignature == CBW_SIGNATURE ? true : false; /* Ascii USBC */
00637 }
00638 
00639 /**************************************************************************/
00643 __STATIC_INLINE void EnableNextCbw(void)
00644 {
00645   USBD_Read(MSD_BULK_OUT, (void*) &cbw, USB_FS_BULK_EP_MAXSIZE, CbwCallback);
00646 }
00647 
00648 /**************************************************************************/
00653 static void ProcessScsiCdb(void)
00654 {
00655   MSDSCSI_Inquiry_TypeDef      *cbI;
00656   MSDSCSI_RequestSense_TypeDef *cbRS;
00657   MSDSCSI_ReadCapacity_TypeDef *cbRC;
00658   MSDSCSI_Read10_TypeDef       *cbR10;
00659   MSDSCSI_Write10_TypeDef      *cbW10;
00660   MSDSCSI_Verify10_TypeDef     *cbV10;
00661 
00662   EFM32_ALIGN(4)
00663   static MSDSCSI_ReadCapacityData_TypeDef ReadCapData __attribute__ ((aligned(4)));
00664 
00665   pCmdStatus->valid    = false;
00666   pCmdStatus->xferType = XFER_MEMORYMAPPED;
00667   pCmdStatus->maxBurst = MSD_MAX_BURST;
00668 
00669   switch (pCbw->CBWCB[ 0 ])
00670   {
00671   case SCSI_INQUIRY:
00672     cbI = (MSDSCSI_Inquiry_TypeDef*) &pCbw->CBWCB;
00673 
00674     if ((cbI->Evpd == 0) && (cbI->PageCode == 0))
00675     {
00676       /* Standard Inquiry data request */
00677       pCmdStatus->valid     = true;
00678       pCmdStatus->direction = MSD_DIR_DATA_IN;
00679       pCmdStatus->pData     = (uint8_t*) &InquiryData;
00680       pCmdStatus->xferLen   = EFM32_MIN(SCSI_INQUIRYDATA_LEN,
00681                                         __REV16(cbI->AllocationLength));
00682     }
00683     break;
00684 
00685   case SCSI_REQUESTSENSE:
00686     cbRS = (MSDSCSI_RequestSense_TypeDef*) &pCbw->CBWCB;
00687 
00688     if ((cbRS->Desc == 0) && (cbRS->Reserved1 == 0) &&
00689         (cbRS->Reserved2 == 0) && (cbRS->Reserved3 == 0))
00690     {
00691       pCmdStatus->valid     = true;
00692       pCmdStatus->direction = MSD_DIR_DATA_IN;
00693       pCmdStatus->pData     = (uint8_t*) pSenseData;
00694       pCmdStatus->xferLen   = EFM32_MIN(SCSI_REQUESTSENSEDATA_LEN,
00695                                         cbRS->AllocationLength);
00696       pSenseData = (MSDSCSI_RequestSenseData_TypeDef*) &NoSenseData;
00697     }
00698     break;
00699 
00700   case SCSI_READCAPACITY:
00701     cbRC = (MSDSCSI_ReadCapacity_TypeDef*) &pCbw->CBWCB;
00702 
00703     if ((cbRC->Pmi == 0) && (cbRC->Lba == 0))
00704     {
00705       ReadCapData.LogicalBlockAddress = __REV(MSDDMEDIA_GetSectorCount() - 1);
00706       ReadCapData.LogicalBlockLength  = __REV(512);
00707 
00708       pCmdStatus->valid     = true;
00709       pCmdStatus->direction = MSD_DIR_DATA_IN;
00710       pCmdStatus->pData     = (uint8_t*) &ReadCapData;
00711       pCmdStatus->xferLen   = SCSI_READCAPACITYDATA_LEN;
00712     }
00713     break;
00714 
00715   case SCSI_READ10:
00716     cbR10 = (MSDSCSI_Read10_TypeDef*) &pCbw->CBWCB;
00717 
00718     pCmdStatus->direction = MSD_DIR_DATA_IN;
00719     pCmdStatus->valid     = MSDDMEDIA_CheckAccess(pCmdStatus,
00720                                                   __REV(cbR10->Lba),
00721                                                   __REV16(cbR10->TransferLength));
00722     break;
00723 
00724   case SCSI_WRITE10:
00725     cbW10 = (MSDSCSI_Write10_TypeDef*) &pCbw->CBWCB;
00726 
00727     pCmdStatus->direction = MSD_DIR_DATA_OUT;
00728     pCmdStatus->valid     = MSDDMEDIA_CheckAccess(pCmdStatus,
00729                                                   __REV(cbW10->Lba),
00730                                                   __REV16(cbW10->TransferLength));
00731     break;
00732 
00733   case SCSI_VERIFY10:
00734     cbV10 = (MSDSCSI_Verify10_TypeDef*) &pCbw->CBWCB;
00735 
00736     if ((cbV10->BytChk      == 0) && (cbV10->Reserved1 == 0) &&
00737         (cbV10->Dpo         == 0) && (cbV10->VrProtect == 0) &&
00738         (cbV10->GroupNumber == 0) && (cbV10->Reserved2 == 0) &&
00739         (cbV10->Restricted  == 0))
00740     {
00741       pCmdStatus->valid     = true;
00742       pCmdStatus->direction = pCbw->Direction;
00743       pCmdStatus->xferLen   = 0;
00744     }
00745     break;
00746 
00747   case SCSI_TESTUNIT_READY:
00748     pCmdStatus->valid     = true;
00749     pCmdStatus->direction = pCbw->Direction;
00750     pCmdStatus->xferLen   = 0;
00751     break;
00752 
00753   case SCSI_STARTSTOP_UNIT:
00754     pCmdStatus->valid     = true;
00755     pCmdStatus->direction = pCbw->Direction;
00756     pCmdStatus->xferLen   = 0;
00757     pCmdStatus->xferType  = XFER_INDIRECT;
00758     break;
00759   }
00760 
00761   if (!pCmdStatus->valid)
00762   {
00763     pCmdStatus->xferLen   = 0;
00764     pCmdStatus->direction = pCbw->Direction;
00765     pSenseData            = (MSDSCSI_RequestSenseData_TypeDef*) &IllegalSenseData;
00766   }
00767 }
00768 
00769 /**************************************************************************/
00773 __STATIC_INLINE void SendCsw(void)
00774 {
00775   if ( ledPort != -1 )
00776     GPIO_PinOutToggle((GPIO_Port_TypeDef)ledPort, ledPin);
00777 
00778   USBD_Write(MSD_BULK_IN, (void*) &csw, CSW_LEN, NULL);
00779 }
00780 
00781 /**************************************************************************/
00795 static void UsbXferBotData(uint8_t *data, uint32_t len,
00796                            USB_XferCompleteCb_TypeDef cb)
00797 {
00798   if (pCmdStatus->direction)
00799   {
00800     USBD_Write(MSD_BULK_IN, data, len, cb);
00801   }
00802   else
00803   {
00804     USBD_Read(MSD_BULK_OUT, data, len, cb);
00805   }
00806 }
00807 
00808 /**************************************************************************/
00816 static void XferBotData(uint32_t length)
00817 {
00818   pCmdStatus->xferLen   = length;
00819   pCsw->dCSWDataResidue = pCbw->dCBWDataTransferLength;
00820 
00821   if (pCmdStatus->xferType == XFER_INDIRECT)
00822   {
00823     /* Access media in "background" polling loop, i.e. in MSDD_Handler() */
00824     savedState = msdState;
00825     msdState   = MSDD_ACCESS_INDIRECT;
00826   }
00827   else
00828   {
00829     UsbXferBotData(pCmdStatus->pData,
00830                    EFM32_MIN(length, pCmdStatus->maxBurst),
00831                    XferBotDataCallback);
00832   }
00833 }
00834 
00835 /**************************************************************************/
00854 static int XferBotDataCallback(USB_Status_TypeDef status,
00855                                uint32_t xferred, uint32_t remaining)
00856 {
00857   (void) status;
00858   (void) remaining;
00859 
00860   pCmdStatus->xferLen   -= xferred;
00861   pCsw->dCSWDataResidue -= xferred;
00862 
00863   if (pCmdStatus->xferLen)
00864   {
00865     pCmdStatus->pData += xferred;
00866     UsbXferBotData(pCmdStatus->pData,
00867                    EFM32_MIN(pCmdStatus->xferLen, pCmdStatus->maxBurst),
00868                    XferBotDataCallback);
00869   }
00870   else
00871   {
00872     if (msdState == MSDD_SEND_CSW)
00873     {
00874       SendCsw();
00875       EnableNextCbw();
00876       msdState = MSDD_WAITFOR_CBW;
00877     }
00878 
00879     else if (msdState == MSDD_STALL_IN)
00880     {
00881       USBD_StallEp(MSD_BULK_IN);
00882       msdState = MSDD_WAIT_FOR_INUNSTALLED;
00883     }
00884   }
00885 
00886   return USB_STATUS_OK;
00887 }
00888 
00889 /**************************************************************************/
00906 static int XferBotDataIndirectCallback(USB_Status_TypeDef status,
00907                                        uint32_t xferred, uint32_t remaining)
00908 {
00909   (void) status;
00910   (void) remaining;
00911 
00912   pCmdStatus->xferLen   -= xferred;
00913   pCsw->dCSWDataResidue -= xferred;
00914 
00915   if (pCmdStatus->direction)
00916   {
00917     pCmdStatus->lba += xferred / 512;
00918     msdState         = MSDD_ACCESS_INDIRECT;
00919   }
00920   else
00921   {
00922     msdState = MSDD_WRITE_INDIRECT;
00923   }
00924 
00925   return USB_STATUS_OK;
00926 }
00927