msdd.c

Go to the documentation of this file.
00001 /**************************************************************************/
00017 #include "em_usb.h"
00018 #include "em_cmu.h"
00019 #include "em_gpio.h"
00020 #include "msdbot.h"
00021 #include "msdscsi.h"
00022 #include "msdd.h"
00023 #include "msddmedia.h"
00024 
00027 /*** Typedef's and defines. ***/
00028 
00029 #define BULK_OUT        0x01
00030 #define BULK_IN         0x81
00031 #define DIR_DATA_OUT    0
00032 #define DIR_DATA_IN     1
00033 #define MAX_BURST       32768U          /* 32 * 1024 */
00034 
00035 /**************************************************************************/
00038 typedef enum
00039 {
00040   MSDD_IDLE                 = 0,
00041   MSDD_WAITFOR_CBW          = 1,
00042   MSDD_WAITFOR_RECOVERY     = 2,
00043   MSDD_SEND_CSW             = 3,
00044   MSDD_WAIT_FOR_INUNSTALLED = 4,
00045   MSDD_STALL_IN             = 5,
00046   MSDD_ACCESS_INDIRECT      = 6,
00047   MSDD_WRITE_INDIRECT       = 7,
00048   MSDD_DO_CMD_TASK          = 8,
00049 } msdState_TypeDef;
00050 
00051 /*** Function prototypes. ***/
00052 
00053 static int            CbwCallback(USB_Status_TypeDef status, uint32_t xferred, uint32_t remaining);
00054 __STATIC_INLINE bool  CswMeaningful(void);
00055 __STATIC_INLINE bool  CswValid(void);
00056 __STATIC_INLINE void  EnableNextCbw(void);
00057 static void           ProcessScsiCdb(void);
00058 __STATIC_INLINE void  SendCsw(void);
00059 static int            UsbSetupCmd(const USB_Setup_TypeDef *setup);
00060 static void           UsbStateChangeEvent(USBD_State_TypeDef oldState, USBD_State_TypeDef newState);
00061 static void           UsbXferBotData(uint8_t *data, uint32_t len, USB_XferCompleteCb_TypeDef cb);
00062 static void           XferBotData(uint32_t length);
00063 static int            XferBotDataCallback(USB_Status_TypeDef status, uint32_t xferred, uint32_t remaining);
00064 static int            XferBotDataIndirectCallback(USB_Status_TypeDef status, uint32_t xferred, uint32_t remaining);
00065 
00066 /*** Include device descriptor definitions. ***/
00067 
00068 #include "descriptors.h"
00069 
00070 /*** Variables ***/
00071 
00072 /* Storage for one CBW */
00073 STATIC_UBUF(cbw, USB_MAX_EP_SIZE);
00074 static MSDBOT_CBW_TypeDef *pCbw = (MSDBOT_CBW_TypeDef*) &cbw;
00075 
00076 EFM32_ALIGN(4)
00077 /* Storage for one CSW */
00078 static MSDBOT_CSW_TypeDef csw __attribute__ ((aligned(4)));
00079 static MSDBOT_CSW_TypeDef *pCsw = &csw;
00080 
00081 STATIC_UBUF(mediaBuffer, MEDIA_BUFSIZ);  /* Intermediate media storage buffer */
00082 
00083 static MSDD_CmdStatus_TypeDef CmdStatus;
00084 static MSDD_CmdStatus_TypeDef *pCmdStatus = &CmdStatus;
00085 static msdState_TypeDef       savedState; /* MSD state machine state. */
00086 static int ledPort;
00087 static unsigned int ledPin;
00088 
00089 /**************************************************************************/
00092 EFM32_ALIGN(4)
00093 static const MSDSCSI_InquiryData_TypeDef InquiryData __attribute__ ((aligned(4))) =
00094 {
00095   { .PeripheralDeviceType = 0, .PeripheralQualifier = 0 }, /* Block device  */
00096   { .Reserved1            = 0, .Removable           = 1 },
00097 
00098   .Version = 5,                                       /* T10 SPC-3 compliant */
00099 
00100   { .ResponseDataFormat = 2,                          /* T10 SPC-3 compliant reponse data */
00101     .HiSup = 0, .NormACA = 0, .Obsolete1 = 0 },
00102 
00103   .AdditionalLength = 31,
00104 
00105   { .Protect = 0, .Reserved2 = 0, .ThirdPartyCode = 0,
00106     .Tpgs    = 0, .Acc       = 0, .Sccs           = 0 },
00107 
00108   { .Addr16 = 0, .Obsolete2 = 0, .MChngr = 0, .MultiP = 0,
00109     .Vs1    = 0, .EncServ   = 0, .BQue   = 0 },
00110 
00111   { .Vs2  = 0, .CmdQue = 0, .Obsolete3 = 0, .Linked = 0,
00112     .Sync = 0, .Wbus16 = 0, .Obsolete4 = 0 },
00113 
00114   .T10VendorId          = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' },
00115   .ProductId            = { 'E', 'F', 'M', '3', '2', ' ', 'M', 'S', 'D', ' ', 'D', 'e', 'v', 'i', 'c', 'e' },
00116   .ProductRevisionLevel ={ '1', '.', '0', '0' }
00117 };
00118 
00119 /**************************************************************************/
00124 EFM32_ALIGN(4)
00125 static const MSDSCSI_RequestSenseData_TypeDef NoSenseData __attribute__ ((aligned(4))) =
00126 {
00127   { .ResponseCode = 0x70, .Valid = 0 },
00128   .Obsolete = 0,
00129   { .SenseKey = 0, .Reserved =0, .Ili = 0, .Eom = 0, .FileMark = 0 },
00130   .Information      = 0,
00131   .AdditionalLength = 10,
00132   .CmdSpecificInfo  = 0,
00133   .Asc              = 0,
00134   .Ascq             = 0,
00135   .Fruc             = 0,
00136   { .SenseKeySpecific1 = 0, .Sksv = 0 },
00137   .SenseKeySpecific2 = 0,
00138   .SenseKeySpecific3 = 0
00139 };
00140 
00141 /**************************************************************************/
00147 EFM32_ALIGN(4)
00148 static const MSDSCSI_RequestSenseData_TypeDef IllegalSenseData __attribute__ ((aligned(4))) =
00149 {
00150   { .ResponseCode = 0x70, .Valid = 0 },
00151   .Obsolete = 0,
00152   { .SenseKey = 5,                          /* SensKey = 5 => ILLEGAL REQUEST */
00153     .Reserved = 0, .Ili = 0, .Eom = 0, .FileMark = 0 },
00154   .Information      = 0,
00155   .AdditionalLength = 10,
00156   .CmdSpecificInfo  = 0,
00157   .Asc              = 0x24,    /* Asc/Ascq = 0x24/0x00 => INVALID FIELD IN CDB*/
00158   .Ascq             = 0,
00159   .Fruc             = 0,
00160   { .SenseKeySpecific1 = 0, .Sksv  = 0 },
00161   .SenseKeySpecific2 = 0,
00162   .SenseKeySpecific3 = 0
00163 };
00164 
00165 static volatile msdState_TypeDef        msdState;     
00166 static MSDSCSI_RequestSenseData_TypeDef *pSenseData;  
00170 /**************************************************************************/
00180 void MSDD_Init(int activityLedPort, uint32_t activityLedPin)
00181 {
00182   if ( ( sizeof(MSDSCSI_Read10_TypeDef)           != SCSI_READ10_LEN           ) ||
00183        ( sizeof(MSDSCSI_Write10_TypeDef)          != SCSI_WRITE10_LEN          ) ||
00184        ( sizeof(MSDSCSI_RequestSense_TypeDef)     != SCSI_REQUESTSENSE_LEN     ) ||
00185        ( sizeof(InquiryData)                      != SCSI_INQUIRYDATA_LEN      ) ||
00186        ( sizeof(NoSenseData)                      != SCSI_REQUESTSENSEDATA_LEN ) ||
00187        ( sizeof(IllegalSenseData)                 != SCSI_REQUESTSENSEDATA_LEN ) ||
00188        ( sizeof(MSDSCSI_ReadCapacity_TypeDef)     != SCSI_READCAPACITY_LEN     ) ||
00189        ( sizeof(MSDSCSI_ReadCapacityData_TypeDef) != SCSI_READCAPACITYDATA_LEN )    )
00190   {
00191     DEBUG_USB_API_PUTS("\nMSDD_Init(), typedef size error");
00192     EFM_ASSERT(false);
00193     return;
00194   }
00195 
00196   if ( ( activityLedPort >= gpioPortA ) && ( activityLedPort <= gpioPortF ) )
00197     ledPort = activityLedPort;
00198   else
00199     ledPort = -1;
00200 
00201   ledPin     = activityLedPin;
00202   msdState   = MSDD_IDLE;
00203   pSenseData = (MSDSCSI_RequestSenseData_TypeDef*) &NoSenseData;
00204   USBD_Init(&initstruct);     /* Start USB. */
00205 
00206   if ( ledPort != -1 )
00207   {
00208     CMU_ClockEnable(cmuClock_GPIO, true);
00209     GPIO_PinModeSet((GPIO_Port_TypeDef)ledPort, ledPin, gpioModePushPull, 0);
00210   }
00211 
00212   /*
00213    * When using a debugger it is practical to uncomment the following three
00214    * lines to force host to re-enumerate the device.
00215    */
00216   /* USBD_Disconnect(); */
00217   /* USBTIMER_DelayMs( 1000 ); */
00218   /* USBD_Connect(); */
00219 }
00220 
00221 /**************************************************************************/
00230 bool MSDD_Handler(void)
00231 {
00232   static uint32_t len;        /* Note: len is static ! */
00233 
00234   switch (msdState)
00235   {
00236   case MSDD_ACCESS_INDIRECT:
00237     if (pCmdStatus->xferLen)
00238     {
00239       len = EFM32_MIN(pCmdStatus->xferLen, pCmdStatus->maxBurst);
00240 
00241       msdState = MSDD_IDLE;
00242       if (pCmdStatus->direction)
00243       {
00244         MSDDMEDIA_Read(pCmdStatus, mediaBuffer, len / 512);
00245       }
00246       UsbXferBotData(mediaBuffer, len, XferBotDataIndirectCallback);
00247     }
00248     else
00249     {
00250       /* We are done ! */
00251       msdState = savedState;
00252 
00253       if (msdState == MSDD_SEND_CSW)
00254       {
00255         SendCsw();
00256         EnableNextCbw();
00257         msdState = MSDD_WAITFOR_CBW;
00258       }
00259 
00260       else if (msdState == MSDD_STALL_IN)
00261       {
00262         USBD_StallEp(BULK_IN);
00263         msdState = MSDD_WAIT_FOR_INUNSTALLED;
00264       }
00265     }
00266     break;
00267 
00268   case MSDD_WRITE_INDIRECT:
00269     MSDDMEDIA_Write(pCmdStatus, mediaBuffer, len / 512);
00270     pCmdStatus->lba += len / 512;
00271     msdState         = MSDD_ACCESS_INDIRECT;
00272     break;
00273 
00274   case MSDD_DO_CMD_TASK:
00275     if (pCbw->CBWCB[ 0 ] == SCSI_STARTSTOP_UNIT)
00276     {
00277       MSDDMEDIA_Flush();
00278     }
00279     /* else if ( .... )  Add more when needed. */
00280     SendCsw();
00281     EnableNextCbw();
00282     msdState = MSDD_WAITFOR_CBW;
00283     break;
00284 
00285   default:
00286     break;
00287   }
00288   return (msdState == MSDD_WAITFOR_CBW) || (msdState == MSDD_IDLE);
00289 }
00290 
00293 /**************************************************************************/
00310 static int CbwCallback(USB_Status_TypeDef status,
00311                        uint32_t xferred, uint32_t remaining)
00312 {
00313   (void) remaining;
00314 
00315   if ((msdState == MSDD_WAITFOR_CBW) &&
00316       (status == USB_STATUS_OK) &&
00317       (xferred == CBW_LEN) &&
00318       (CswValid()) &&
00319       (CswMeaningful()))
00320   {
00321     if ( ledPort != -1 )
00322       GPIO_PinOutToggle((GPIO_Port_TypeDef)ledPort, ledPin);
00323 
00324     /* Check the SCSI command descriptor block (CDB) */
00325     ProcessScsiCdb();
00326 
00327     if (pCmdStatus->valid)
00328       pCsw->bCSWStatus = USB_CLASS_MSD_CSW_CMDPASSED;
00329     else
00330       pCsw->bCSWStatus = USB_CLASS_MSD_CSW_CMDFAILED;
00331 
00332     pCsw->dCSWSignature   = CSW_SIGNATURE;
00333     pCsw->dCSWTag         = pCbw->dCBWTag;
00334     pCsw->dCSWDataResidue = pCbw->dCBWDataTransferLength;
00335 
00336     /* Check the "thirteen cases" */
00337 
00338     if ((pCbw->dCBWDataTransferLength != 0) &&
00339         (pCbw->Direction != pCmdStatus->direction))
00340     {
00341       /* Handle cases 8 and 10 */
00342       pCsw->bCSWStatus = USB_CLASS_MSD_CSW_PHASEERROR;
00343 
00344       if (pCbw->Direction)
00345       {
00346         /* Host expects to receive data, case 8 */
00347         USBD_StallEp(BULK_IN);
00348         msdState = MSDD_WAIT_FOR_INUNSTALLED;
00349       }
00350       else
00351       {
00352         /* Host expects to send data, case 10 */
00353         USBD_StallEp(BULK_OUT);
00354         SendCsw();
00355         msdState = MSDD_IDLE;
00356       }
00357     }
00358 
00359     else if (pCbw->Direction || (pCbw->dCBWDataTransferLength == 0))
00360     {
00361       /* SCSI IN commands or commands without data phase */
00362       /* Handle cases 1-7 */
00363 
00364       if (pCbw->dCBWDataTransferLength == 0)
00365       {
00366         /* Host expects no data, case 1, 2 or 3 */
00367         if (pCmdStatus->xferLen)
00368         {
00369           /* Device has data to transmit, case 2 & 3 */
00370           pCsw->bCSWStatus = USB_CLASS_MSD_CSW_PHASEERROR;
00371         }
00372 
00373         if ((pCmdStatus->xferLen == 0) &&
00374             (pCmdStatus->xferType == XFER_INDIRECT))
00375         {
00376           /* Commands with no data phase which require timeconsuming  */
00377           /* processing are executed in MSDD_Handler()                */
00378           msdState = MSDD_DO_CMD_TASK;
00379         }
00380         else
00381         {
00382           SendCsw();
00383           EnableNextCbw();
00384           msdState = MSDD_WAITFOR_CBW;
00385         }
00386       }
00387       else if (pCbw->dCBWDataTransferLength == pCmdStatus->xferLen)
00388       {
00389         /* Host and device agree on transferlength, case 6 */
00390         /* Send data to host */
00391         msdState = MSDD_SEND_CSW;
00392         XferBotData(pCmdStatus->xferLen);
00393       }
00394       else if (pCbw->dCBWDataTransferLength > pCmdStatus->xferLen)
00395       {
00396         /* Host expects more data than device can provide, case 4 and 5 */
00397 
00398         if (pCmdStatus->xferLen > 0)
00399         {
00400           /* Device has data, case 5 */
00401           /* Send data to host */
00402           msdState = MSDD_STALL_IN;
00403           XferBotData(pCmdStatus->xferLen);
00404         }
00405         else
00406         {
00407           /* Device has no data, case 4 */
00408           USBD_StallEp(BULK_IN);
00409           msdState = MSDD_WAIT_FOR_INUNSTALLED;
00410         }
00411       }
00412       else
00413       {
00414         /* Host expects less data than device will provide, case 7 */
00415         pCsw->bCSWStatus = USB_CLASS_MSD_CSW_PHASEERROR;
00416         /* Send data to host */
00417         msdState = MSDD_SEND_CSW;
00418         XferBotData(pCbw->dCBWDataTransferLength);
00419       }
00420     }
00421 
00422     else /* Host Direction is OUT and Host transferlength > 0 */
00423     {
00424       /* SCSI OUT commands */
00425       /* Handle cases 9, 11, 12 and 13 */
00426 
00427       if (pCbw->dCBWDataTransferLength == pCmdStatus->xferLen)
00428       {
00429         /* Host and device agree on transferlength, case 12 */
00430 
00431         /* Read data from host */
00432         msdState = MSDD_SEND_CSW;
00433         XferBotData(pCmdStatus->xferLen);
00434       }
00435       else if (pCbw->dCBWDataTransferLength > pCmdStatus->xferLen)
00436       {
00437         /* Host intend to send more data than device expects, case 9 & 11 */
00438         pCsw->bCSWStatus = USB_CLASS_MSD_CSW_CMDFAILED;
00439         USBD_StallEp(BULK_OUT);
00440         SendCsw();
00441         msdState = MSDD_IDLE;
00442       }
00443       else
00444       {
00445         /* Host has less data than device expects to receive, case 13 */
00446         pCsw->bCSWStatus = USB_CLASS_MSD_CSW_PHASEERROR;
00447         USBD_StallEp(BULK_OUT);
00448         SendCsw();
00449         msdState = MSDD_IDLE;
00450       }
00451     }
00452     return USB_STATUS_OK;
00453   }
00454 
00455   if ((status == USB_STATUS_OK) &&
00456       (USBD_GetUsbState() == USBD_STATE_CONFIGURED))
00457   {
00458     /* Stall both Ep's and wait for reset recovery */
00459     USBD_StallEp(BULK_OUT);
00460     USBD_StallEp(BULK_IN);
00461     msdState = MSDD_WAITFOR_RECOVERY;
00462   }
00463 
00464   return USB_STATUS_OK;
00465 }
00466 
00467 /**************************************************************************/
00472 __STATIC_INLINE bool CswMeaningful(void)
00473 {
00474   if ( ( pCbw->Reserved1 == 0 ) &&
00475        ( pCbw->Obsolete  == 0 ) &&
00476        ( pCbw->Reserved2 == 0 ) &&
00477        ( pCbw->Lun       == 0 ) &&
00478        ( pCbw->Reserved3 == 0 )    )
00479   {
00480     return true;
00481   }
00482 
00483   return false;
00484 }
00485 
00486 /**************************************************************************/
00491 __STATIC_INLINE bool CswValid(void)
00492 {
00493   return pCbw->dCBWSignature == CBW_SIGNATURE ? true : false; /* Ascii USBC */
00494 }
00495 
00496 /**************************************************************************/
00500 __STATIC_INLINE void EnableNextCbw(void)
00501 {
00502   USBD_Read(BULK_OUT, (void*) &cbw, USB_MAX_EP_SIZE, CbwCallback);
00503 }
00504 
00505 /**************************************************************************/
00510 static void ProcessScsiCdb(void)
00511 {
00512   MSDSCSI_Inquiry_TypeDef      *cbI;
00513   MSDSCSI_RequestSense_TypeDef *cbRS;
00514   MSDSCSI_ReadCapacity_TypeDef *cbRC;
00515   MSDSCSI_Read10_TypeDef       *cbR10;
00516   MSDSCSI_Write10_TypeDef      *cbW10;
00517 
00518   EFM32_ALIGN(4)
00519   static MSDSCSI_ReadCapacityData_TypeDef ReadCapData __attribute__ ((aligned(4)));
00520 
00521   pCmdStatus->valid    = false;
00522   pCmdStatus->xferType = XFER_MEMORYMAPPED;
00523   pCmdStatus->maxBurst = MAX_BURST;
00524 
00525   switch (pCbw->CBWCB[ 0 ])
00526   {
00527   case SCSI_INQUIRY:
00528     cbI = (MSDSCSI_Inquiry_TypeDef*) &pCbw->CBWCB;
00529 
00530     if ((cbI->Evpd == 0) && (cbI->PageCode == 0))
00531     {
00532       /* Standard Inquiry data request */
00533       pCmdStatus->valid     = true;
00534       pCmdStatus->direction = DIR_DATA_IN;
00535       pCmdStatus->pData     = (uint8_t*) &InquiryData;
00536       pCmdStatus->xferLen   = EFM32_MIN(SCSI_INQUIRYDATA_LEN,
00537                                         __REV16(cbI->AllocationLength));
00538     }
00539     break;
00540 
00541   case SCSI_REQUESTSENSE:
00542     cbRS = (MSDSCSI_RequestSense_TypeDef*) &pCbw->CBWCB;
00543 
00544     if ((cbRS->Desc == 0) && (cbRS->Reserved1 == 0) &&
00545         (cbRS->Reserved2 == 0) && (cbRS->Reserved3 == 0))
00546     {
00547       pCmdStatus->valid     = true;
00548       pCmdStatus->direction = DIR_DATA_IN;
00549       pCmdStatus->pData     = (uint8_t*) pSenseData;
00550       pCmdStatus->xferLen   = EFM32_MIN(SCSI_REQUESTSENSEDATA_LEN,
00551                                         cbRS->AllocationLength);
00552       pSenseData = (MSDSCSI_RequestSenseData_TypeDef*) &NoSenseData;
00553     }
00554     break;
00555 
00556   case SCSI_READCAPACITY:
00557     cbRC = (MSDSCSI_ReadCapacity_TypeDef*) &pCbw->CBWCB;
00558 
00559     if ((cbRC->Pmi == 0) && (cbRC->Lba == 0))
00560     {
00561       ReadCapData.LogicalBlockAddress = __REV(MSDDMEDIA_GetSectorCount() - 1);
00562       ReadCapData.LogicalBlockLength  = __REV(512);
00563 
00564       pCmdStatus->valid     = true;
00565       pCmdStatus->direction = DIR_DATA_IN;
00566       pCmdStatus->pData     = (uint8_t*) &ReadCapData;
00567       pCmdStatus->xferLen   = SCSI_READCAPACITYDATA_LEN;
00568     }
00569     break;
00570 
00571   case SCSI_READ10:
00572     cbR10 = (MSDSCSI_Read10_TypeDef*) &pCbw->CBWCB;
00573 
00574     pCmdStatus->direction = DIR_DATA_IN;
00575     pCmdStatus->valid     = MSDDMEDIA_CheckAccess(pCmdStatus,
00576                                                   __REV(cbR10->Lba),
00577                                                   __REV16(cbR10->TransferLength));
00578     break;
00579 
00580   case SCSI_WRITE10:
00581     cbW10 = (MSDSCSI_Write10_TypeDef*) &pCbw->CBWCB;
00582 
00583     pCmdStatus->direction = DIR_DATA_OUT;
00584     pCmdStatus->valid     = MSDDMEDIA_CheckAccess(pCmdStatus,
00585                                                   __REV(cbW10->Lba),
00586                                                   __REV16(cbW10->TransferLength));
00587     break;
00588 
00589   case SCSI_TESTUNIT_READY:
00590     pCmdStatus->valid     = true;
00591     pCmdStatus->direction = pCbw->Direction;
00592     pCmdStatus->xferLen   = 0;
00593     break;
00594 
00595   case SCSI_STARTSTOP_UNIT:
00596     pCmdStatus->valid     = true;
00597     pCmdStatus->direction = pCbw->Direction;
00598     pCmdStatus->xferLen   = 0;
00599     pCmdStatus->xferType  = XFER_INDIRECT;
00600     break;
00601   }
00602 
00603   if (!pCmdStatus->valid)
00604   {
00605     pCmdStatus->xferLen   = 0;
00606     pCmdStatus->direction = pCbw->Direction;
00607     pSenseData            = (MSDSCSI_RequestSenseData_TypeDef*) &IllegalSenseData;
00608   }
00609 }
00610 
00611 /**************************************************************************/
00615 __STATIC_INLINE void SendCsw(void)
00616 {
00617   if ( ledPort != -1 )
00618     GPIO_PinOutToggle((GPIO_Port_TypeDef)ledPort, ledPin);
00619 
00620   USBD_Write(BULK_IN, (void*) &csw, CSW_LEN, NULL);
00621 }
00622 
00623 /**************************************************************************/
00635 static int UsbSetupCmd(const USB_Setup_TypeDef *setup)
00636 {
00637   int             retVal;
00638   static uint32_t tmp;
00639 
00640   retVal = USB_STATUS_REQ_UNHANDLED;
00641 
00642   /* Check if it is MSD class command: "Bulk-Only Mass Storage Reset" */
00643 
00644   if ( ( setup->Type      == USB_SETUP_TYPE_CLASS          ) &&
00645        ( setup->Direction == USB_SETUP_DIR_OUT             ) &&
00646        ( setup->Recipient == USB_SETUP_RECIPIENT_INTERFACE ) &&
00647        ( setup->bRequest  == USB_MSD_BOTRESET              ) &&
00648        ( setup->wValue    == 0                             ) &&
00649        ( setup->wIndex    == 0                             ) &&
00650        ( setup->wLength   == 0                             )    )
00651   {
00652     if (msdState == MSDD_WAITFOR_RECOVERY)
00653     {
00654       msdState = MSDD_IDLE;
00655     }
00656     retVal = USB_STATUS_OK;
00657   }
00658 
00659 
00660   /* Check if it is MSD class command: "Get Max LUN" */
00661 
00662   else if ( ( setup->Type      == USB_SETUP_TYPE_CLASS          ) &&
00663             ( setup->Direction == USB_SETUP_DIR_IN              ) &&
00664             ( setup->Recipient == USB_SETUP_RECIPIENT_INTERFACE ) &&
00665             ( setup->bRequest  == USB_MSD_GETMAXLUN             ) &&
00666             ( setup->wValue    == 0                             ) &&
00667             ( setup->wIndex    == 0                             ) &&
00668             ( setup->wLength   == 1                             )    )
00669   {
00670     /* Only one LUN (i.e. no support for multiple LUN's). Reply "0". */
00671     tmp    = 0;
00672     retVal = USBD_Write(0, (void*) &tmp, 1, NULL);
00673   }
00674 
00675 
00676   /* Check if it is a standard CLEAR_FEATURE endpoint command */
00677 
00678   else if ( ( setup->Type      == USB_SETUP_TYPE_STANDARD      ) &&
00679             ( setup->Direction == USB_SETUP_DIR_OUT            ) &&
00680             ( setup->Recipient == USB_SETUP_RECIPIENT_ENDPOINT ) &&
00681             ( setup->bRequest  == CLEAR_FEATURE                ) &&
00682             ( setup->wValue    == USB_FEATURE_ENDPOINT_HALT    ) &&
00683             ( setup->wLength   == 0                            )    )
00684   {
00685     if ( ( ( setup->wIndex & 0xFF) == BULK_OUT ) ||
00686          ( ( setup->wIndex & 0xFF) == BULK_IN  )    )
00687     {
00688       retVal = USB_STATUS_OK;
00689 
00690       /* Dont unstall ep's when waiting for reset recovery */
00691       if (msdState != MSDD_WAITFOR_RECOVERY)
00692       {
00693         retVal = USBD_UnStallEp(setup->wIndex & 0xFF);
00694 
00695         if ((setup->wIndex & 0xFF) == BULK_IN)
00696         {
00697           if (msdState == MSDD_WAIT_FOR_INUNSTALLED)
00698           {
00699             SendCsw();
00700             EnableNextCbw();
00701             msdState = MSDD_WAITFOR_CBW;
00702           }
00703         }
00704         else
00705         {
00706           EnableNextCbw();
00707           msdState = MSDD_WAITFOR_CBW;
00708         }
00709       }
00710     }
00711   }
00712 
00713   return retVal;
00714 }
00715 
00716 /**************************************************************************/
00726 static void UsbStateChangeEvent(USBD_State_TypeDef oldState,
00727                                 USBD_State_TypeDef newState)
00728 {
00729   if (newState == USBD_STATE_CONFIGURED)
00730   {
00731     /* We have been configured, start MSD functionality ! */
00732     EnableNextCbw();
00733     msdState = MSDD_WAITFOR_CBW;
00734   }
00735 
00736   else if ((oldState == USBD_STATE_CONFIGURED) &&
00737            (newState != USBD_STATE_SUSPENDED))
00738   {
00739     /* We have been de-configured */
00740     msdState = MSDD_IDLE;
00741   }
00742 
00743   else if (newState == USBD_STATE_SUSPENDED)
00744   {
00745     /* We have been suspended.                     */
00746     msdState = MSDD_IDLE;
00747 
00748     /* Reduce current consumption to below 2.5 mA. */
00749   }
00750 }
00751 
00752 /**************************************************************************/
00766 static void UsbXferBotData(uint8_t *data, uint32_t len,
00767                            USB_XferCompleteCb_TypeDef cb)
00768 {
00769   if (pCmdStatus->direction)
00770   {
00771     USBD_Write(BULK_IN, data, len, cb);
00772   }
00773   else
00774   {
00775     USBD_Read(BULK_OUT, data, len, cb);
00776   }
00777 }
00778 
00779 /**************************************************************************/
00787 static void XferBotData(uint32_t length)
00788 {
00789   pCmdStatus->xferLen   = length;
00790   pCsw->dCSWDataResidue = pCbw->dCBWDataTransferLength;
00791 
00792   if (pCmdStatus->xferType == XFER_INDIRECT)
00793   {
00794     /* Access media in "background" polling loop, i.e. in MSDD_Handler() */
00795     savedState = msdState;
00796     msdState   = MSDD_ACCESS_INDIRECT;
00797   }
00798   else
00799   {
00800     UsbXferBotData(pCmdStatus->pData,
00801                    EFM32_MIN(length, pCmdStatus->maxBurst),
00802                    XferBotDataCallback);
00803   }
00804 }
00805 
00806 /**************************************************************************/
00825 static int XferBotDataCallback(USB_Status_TypeDef status,
00826                                uint32_t xferred, uint32_t remaining)
00827 {
00828   (void) status;
00829   (void) remaining;
00830 
00831   pCmdStatus->xferLen   -= xferred;
00832   pCsw->dCSWDataResidue -= xferred;
00833 
00834   if (pCmdStatus->xferLen)
00835   {
00836     pCmdStatus->pData += xferred;
00837     UsbXferBotData(pCmdStatus->pData,
00838                    EFM32_MIN(pCmdStatus->xferLen, pCmdStatus->maxBurst),
00839                    XferBotDataCallback);
00840   }
00841   else
00842   {
00843     if (msdState == MSDD_SEND_CSW)
00844     {
00845       SendCsw();
00846       EnableNextCbw();
00847       msdState = MSDD_WAITFOR_CBW;
00848     }
00849 
00850     else if (msdState == MSDD_STALL_IN)
00851     {
00852       USBD_StallEp(BULK_IN);
00853       msdState = MSDD_WAIT_FOR_INUNSTALLED;
00854     }
00855   }
00856 
00857   return USB_STATUS_OK;
00858 }
00859 
00860 /**************************************************************************/
00877 static int XferBotDataIndirectCallback(USB_Status_TypeDef status,
00878                                        uint32_t xferred, uint32_t remaining)
00879 {
00880   (void) status;
00881   (void) remaining;
00882 
00883   pCmdStatus->xferLen   -= xferred;
00884   pCsw->dCSWDataResidue -= xferred;
00885 
00886   if (pCmdStatus->direction)
00887   {
00888     pCmdStatus->lba += xferred / 512;
00889     msdState         = MSDD_ACCESS_INDIRECT;
00890   }
00891   else
00892   {
00893     msdState = MSDD_WRITE_INDIRECT;
00894   }
00895 
00896   return USB_STATUS_OK;
00897 }
00898