msdd.c

Go to the documentation of this file.
00001 /**************************************************************************/
00035 #include "em_usb.h"
00036 #include "em_cmu.h"
00037 #include "em_gpio.h"
00038 #include "msdbot.h"
00039 #include "msdscsi.h"
00040 #include "msdd.h"
00041 #include "msddmedia.h"
00042 
00045 /*** Typedef's and defines. ***/
00046 
00047 #define BULK_OUT        0x01
00048 #define BULK_IN         0x81
00049 #define DIR_DATA_OUT    0
00050 #define DIR_DATA_IN     1
00051 #define MAX_BURST       32768U          /* 32 * 1024 */
00052 
00053 /**************************************************************************/
00056 typedef enum
00057 {
00058   MSDD_IDLE                 = 0,
00059   MSDD_WAITFOR_CBW          = 1,
00060   MSDD_WAITFOR_RECOVERY     = 2,
00061   MSDD_SEND_CSW             = 3,
00062   MSDD_WAIT_FOR_INUNSTALLED = 4,
00063   MSDD_STALL_IN             = 5,
00064   MSDD_ACCESS_INDIRECT      = 6,
00065   MSDD_WRITE_INDIRECT       = 7,
00066   MSDD_DO_CMD_TASK          = 8,
00067 } msdState_TypeDef;
00068 
00069 /*** Function prototypes. ***/
00070 
00071 static int            CbwCallback(USB_Status_TypeDef status, uint32_t xferred, uint32_t remaining);
00072 __STATIC_INLINE bool  CswMeaningful(void);
00073 __STATIC_INLINE bool  CswValid(void);
00074 __STATIC_INLINE void  EnableNextCbw(void);
00075 static void           ProcessScsiCdb(void);
00076 __STATIC_INLINE void  SendCsw(void);
00077 static int            UsbSetupCmd(const USB_Setup_TypeDef *setup);
00078 static void           UsbStateChangeEvent(USBD_State_TypeDef oldState, USBD_State_TypeDef newState);
00079 static void           UsbXferBotData(uint8_t *data, uint32_t len, USB_XferCompleteCb_TypeDef cb);
00080 static void           XferBotData(uint32_t length);
00081 static int            XferBotDataCallback(USB_Status_TypeDef status, uint32_t xferred, uint32_t remaining);
00082 static int            XferBotDataIndirectCallback(USB_Status_TypeDef status, uint32_t xferred, uint32_t remaining);
00083 
00084 /*** Include device descriptor definitions. ***/
00085 
00086 #include "descriptors.h"
00087 
00088 /*** Variables ***/
00089 
00090 /* Storage for one CBW */
00091 STATIC_UBUF(cbw, USB_MAX_EP_SIZE);
00092 static MSDBOT_CBW_TypeDef *pCbw = (MSDBOT_CBW_TypeDef*) &cbw;
00093 
00094 EFM32_ALIGN(4)
00095 /* Storage for one CSW */
00096 static MSDBOT_CSW_TypeDef csw __attribute__ ((aligned(4)));
00097 static MSDBOT_CSW_TypeDef *pCsw = &csw;
00098 
00099 STATIC_UBUF(mediaBuffer, MEDIA_BUFSIZ);  /* Intermediate media storage buffer */
00100 
00101 static MSDD_CmdStatus_TypeDef CmdStatus;
00102 static MSDD_CmdStatus_TypeDef *pCmdStatus = &CmdStatus;
00103 static msdState_TypeDef       savedState; /* MSD state machine state. */
00104 static int ledPort;
00105 static unsigned int ledPin;
00106 
00107 /**************************************************************************/
00110 EFM32_ALIGN(4)
00111 static const MSDSCSI_InquiryData_TypeDef InquiryData __attribute__ ((aligned(4))) =
00112 {
00113   { .PeripheralDeviceType = 0, .PeripheralQualifier = 0 }, /* Block device  */
00114   { .Reserved1            = 0, .Removable           = 1 },
00115 
00116   .Version = 5,                                       /* T10 SPC-3 compliant */
00117 
00118   { .ResponseDataFormat = 2,                          /* T10 SPC-3 compliant reponse data */
00119     .HiSup              = 0, .NormACA = 0, .Obsolete1 = 0 },
00120 
00121   .AdditionalLength = 31,
00122 
00123   { .Protect = 0, .Reserved2           = 0, .ThirdPartyCode = 0,
00124     .Tpgs    = 0, .Acc = 0, .Sccs = 0 },
00125 
00126   { .Addr16 = 0, .Obsolete2           = 0, .MChngr = 0, .MultiP = 0,
00127     .Vs1    = 0, .EncServ = 0, .BQue = 0 },
00128 
00129   { .Vs2  = 0, .CmdQue              =0, .Obsolete3 = 0, .Linked = 0,
00130     .Sync = 0, .Wbus16 = 0, .Obsolete4 = 0 },
00131 
00132   .T10VendorId          = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' },
00133   .ProductId            = { 'E', 'F', 'M', '3', '2', ' ', 'M', 'S', 'D', ' ', 'D', 'e', 'v', 'i', 'c', 'e' },
00134   .ProductRevisionLevel ={ '1', '.', '0', '0' }
00135 };
00136 
00137 /**************************************************************************/
00142 EFM32_ALIGN(4)
00143 static const MSDSCSI_RequestSenseData_TypeDef NoSenseData __attribute__ ((aligned(4))) =
00144 {
00145   { .ResponseCode      = 0x70, .Valid    = 0 },
00146   .Obsolete = 0,
00147   { .SenseKey          =    0, .Reserved =0, .Ili = 0, .Eom = 0, .FileMark = 0 },
00148   .Information      = 0,
00149   .AdditionalLength = 10,
00150   .CmdSpecificInfo  = 0,
00151   .Asc              = 0,
00152   .Ascq             = 0,
00153   .Fruc             = 0,
00154   { .SenseKeySpecific1 =    0, .Sksv     = 0 },
00155   .SenseKeySpecific2 = 0,
00156   .SenseKeySpecific3 = 0
00157 };
00158 
00159 /**************************************************************************/
00165 EFM32_ALIGN(4)
00166 static const MSDSCSI_RequestSenseData_TypeDef IllegalSenseData __attribute__ ((aligned(4))) =
00167 {
00168   { .ResponseCode      = 0x70, .Valid = 0 },
00169   .Obsolete = 0,
00170   { .SenseKey =    5,          /* SensKey = 5 => ILLEGAL REQUEST */
00171     .Reserved = 0, .Ili = 0, .Eom = 0, .FileMark = 0 },
00172   .Information      = 0,
00173   .AdditionalLength = 10,
00174   .CmdSpecificInfo  = 0,
00175   .Asc              = 0x24,    /* Asc/Ascq = 0x24/0x00 => INVALID FIELD IN CDB*/
00176   .Ascq             = 0,
00177   .Fruc             = 0,
00178   { .SenseKeySpecific1 =    0, .Sksv  = 0 },
00179   .SenseKeySpecific2 = 0,
00180   .SenseKeySpecific3 = 0
00181 };
00182 
00183 static volatile msdState_TypeDef        msdState;     
00184 static MSDSCSI_RequestSenseData_TypeDef *pSenseData;  
00188 /**************************************************************************/
00198 void MSDD_Init(int activityLedPort, uint32_t activityLedPin)
00199 {
00200   if ((sizeof(MSDSCSI_Read10_TypeDef) != SCSI_READ10_LEN) ||
00201       (sizeof(MSDSCSI_Write10_TypeDef) != SCSI_WRITE10_LEN) ||
00202       (sizeof(MSDSCSI_RequestSense_TypeDef) != SCSI_REQUESTSENSE_LEN) ||
00203       (sizeof(InquiryData) != SCSI_INQUIRYDATA_LEN) ||
00204       (sizeof(NoSenseData) != SCSI_REQUESTSENSEDATA_LEN) ||
00205       (sizeof(IllegalSenseData) != SCSI_REQUESTSENSEDATA_LEN) ||
00206       (sizeof(MSDSCSI_ReadCapacity_TypeDef) != SCSI_READCAPACITY_LEN) ||
00207       (sizeof(MSDSCSI_ReadCapacityData_TypeDef) != SCSI_READCAPACITYDATA_LEN))
00208   {
00209     DEBUG_USB_API_PUTS("\nMSDD_Init(), typedef size error");
00210     EFM_ASSERT(false);
00211     return;
00212   }
00213 
00214   if ( ( activityLedPort >= gpioPortA ) && ( activityLedPort <= gpioPortF ) )
00215     ledPort = activityLedPort;
00216   else
00217     ledPort = -1;
00218 
00219   ledPin     = activityLedPin;
00220   msdState   = MSDD_IDLE;
00221   pSenseData = (MSDSCSI_RequestSenseData_TypeDef*) &NoSenseData;
00222   USBD_Init(&initstruct);     /* Start USB. */
00223 
00224   if ( ledPort != -1 )
00225   {
00226     CMU_ClockEnable(cmuClock_GPIO, true);
00227     GPIO_PinModeSet((GPIO_Port_TypeDef)ledPort, ledPin, gpioModePushPull, 0);
00228   }
00229 
00230   /*
00231    * When using a debugger it is practical to uncomment the following three
00232    * lines to force host to re-enumerate the device.
00233    */
00234   /* USBD_Disconnect(); */
00235   /* USBTIMER_DelayMs( 1000 ); */
00236   /* USBD_Connect(); */
00237 }
00238 
00239 /**************************************************************************/
00248 bool MSDD_Handler(void)
00249 {
00250   static uint32_t len;        /* Note: len is static ! */
00251 
00252   switch (msdState)
00253   {
00254   case MSDD_ACCESS_INDIRECT:
00255     if (pCmdStatus->xferLen)
00256     {
00257       len = EFM32_MIN(pCmdStatus->xferLen, pCmdStatus->maxBurst);
00258 
00259       msdState = MSDD_IDLE;
00260       if (pCmdStatus->direction)
00261       {
00262         MSDDMEDIA_Read(pCmdStatus, mediaBuffer, len / 512);
00263       }
00264       UsbXferBotData(mediaBuffer, len, XferBotDataIndirectCallback);
00265     }
00266     else
00267     {
00268       /* We are done ! */
00269       msdState = savedState;
00270 
00271       if (msdState == MSDD_SEND_CSW)
00272       {
00273         SendCsw();
00274         EnableNextCbw();
00275         msdState = MSDD_WAITFOR_CBW;
00276       }
00277 
00278       else if (msdState == MSDD_STALL_IN)
00279       {
00280         USBD_StallEp(BULK_IN);
00281         msdState = MSDD_WAIT_FOR_INUNSTALLED;
00282       }
00283     }
00284     break;
00285 
00286   case MSDD_WRITE_INDIRECT:
00287     MSDDMEDIA_Write(pCmdStatus, mediaBuffer, len / 512);
00288     pCmdStatus->lba += len / 512;
00289     msdState         = MSDD_ACCESS_INDIRECT;
00290     break;
00291 
00292   case MSDD_DO_CMD_TASK:
00293     if (pCbw->CBWCB[ 0 ] == SCSI_STARTSTOP_UNIT)
00294     {
00295       MSDDMEDIA_Flush();
00296     }
00297     /* else if ( .... )  Add more when needed. */
00298     SendCsw();
00299     EnableNextCbw();
00300     msdState = MSDD_WAITFOR_CBW;
00301     break;
00302 
00303   default:
00304     break;
00305   }
00306   return (msdState == MSDD_WAITFOR_CBW) || (msdState == MSDD_IDLE);
00307 }
00308 
00311 /**************************************************************************/
00328 static int CbwCallback(USB_Status_TypeDef status,
00329                        uint32_t xferred, uint32_t remaining)
00330 {
00331   (void) remaining;
00332 
00333   if ((msdState == MSDD_WAITFOR_CBW) &&
00334       (status == USB_STATUS_OK) &&
00335       (xferred == CBW_LEN) &&
00336       (CswValid()) &&
00337       (CswMeaningful()))
00338   {
00339     if ( ledPort != -1 )
00340       GPIO_PinOutToggle((GPIO_Port_TypeDef)ledPort, ledPin);
00341 
00342     /* Check the SCSI command descriptor block (CDB) */
00343     ProcessScsiCdb();
00344 
00345     if (pCmdStatus->valid)
00346       pCsw->bCSWStatus = USB_CLASS_MSD_CSW_CMDPASSED;
00347     else
00348       pCsw->bCSWStatus = USB_CLASS_MSD_CSW_CMDFAILED;
00349 
00350     pCsw->dCSWSignature   = CSW_SIGNATURE;
00351     pCsw->dCSWTag         = pCbw->dCBWTag;
00352     pCsw->dCSWDataResidue = pCbw->dCBWDataTransferLength;
00353 
00354     /* Check the "thirteen cases" */
00355 
00356     if ((pCbw->dCBWDataTransferLength != 0) &&
00357         (pCbw->Direction != pCmdStatus->direction))
00358     {
00359       /* Handle cases 8 and 10 */
00360       pCsw->bCSWStatus = USB_CLASS_MSD_CSW_PHASEERROR;
00361 
00362       if (pCbw->Direction)
00363       {
00364         /* Host expects to receive data, case 8 */
00365         USBD_StallEp(BULK_IN);
00366         msdState = MSDD_WAIT_FOR_INUNSTALLED;
00367       }
00368       else
00369       {
00370         /* Host expects to send data, case 10 */
00371         USBD_StallEp(BULK_OUT);
00372         SendCsw();
00373         msdState = MSDD_IDLE;
00374       }
00375     }
00376 
00377     else if (pCbw->Direction || (pCbw->dCBWDataTransferLength == 0))
00378     {
00379       /* SCSI IN commands or commands without data phase */
00380       /* Handle cases 1-7 */
00381 
00382       if (pCbw->dCBWDataTransferLength == 0)
00383       {
00384         /* Host expects no data, case 1, 2 or 3 */
00385         if (pCmdStatus->xferLen)
00386         {
00387           /* Device has data to transmit, case 2 & 3 */
00388           pCsw->bCSWStatus = USB_CLASS_MSD_CSW_PHASEERROR;
00389         }
00390 
00391         if ((pCmdStatus->xferLen == 0) &&
00392             (pCmdStatus->xferType == XFER_INDIRECT))
00393         {
00394           /* Commands with no data phase which require timeconsuming  */
00395           /* processing are executed in MSDD_Handler()                */
00396           msdState = MSDD_DO_CMD_TASK;
00397         }
00398         else
00399         {
00400           SendCsw();
00401           EnableNextCbw();
00402           msdState = MSDD_WAITFOR_CBW;
00403         }
00404       }
00405       else if (pCbw->dCBWDataTransferLength == pCmdStatus->xferLen)
00406       {
00407         /* Host and device agree on transferlength, case 6 */
00408         /* Send data to host */
00409         msdState = MSDD_SEND_CSW;
00410         XferBotData(pCmdStatus->xferLen);
00411       }
00412       else if (pCbw->dCBWDataTransferLength > pCmdStatus->xferLen)
00413       {
00414         /* Host expects more data than device can provide, case 4 and 5 */
00415 
00416         if (pCmdStatus->xferLen > 0)
00417         {
00418           /* Device has data, case 5 */
00419           /* Send data to host */
00420           msdState = MSDD_STALL_IN;
00421           XferBotData(pCmdStatus->xferLen);
00422         }
00423         else
00424         {
00425           /* Device has no data, case 4 */
00426           USBD_StallEp(BULK_IN);
00427           msdState = MSDD_WAIT_FOR_INUNSTALLED;
00428         }
00429       }
00430       else
00431       {
00432         /* Host expects less data than device will provide, case 7 */
00433         pCsw->bCSWStatus = USB_CLASS_MSD_CSW_PHASEERROR;
00434         /* Send data to host */
00435         msdState = MSDD_SEND_CSW;
00436         XferBotData(pCbw->dCBWDataTransferLength);
00437       }
00438     }
00439 
00440     else /* Host Direction is OUT and Host transferlength > 0 */
00441     {
00442       /* SCSI OUT commands */
00443       /* Handle cases 9, 11, 12 and 13 */
00444 
00445       if (pCbw->dCBWDataTransferLength == pCmdStatus->xferLen)
00446       {
00447         /* Host and device agree on transferlength, case 12 */
00448 
00449         /* Read data from host */
00450         msdState = MSDD_SEND_CSW;
00451         XferBotData(pCmdStatus->xferLen);
00452       }
00453       else if (pCbw->dCBWDataTransferLength > pCmdStatus->xferLen)
00454       {
00455         /* Host intend to send more data than device expects, case 9 & 11 */
00456         pCsw->bCSWStatus = USB_CLASS_MSD_CSW_CMDFAILED;
00457         USBD_StallEp(BULK_OUT);
00458         SendCsw();
00459         msdState = MSDD_IDLE;
00460       }
00461       else
00462       {
00463         /* Host has less data than device expects to receive, case 13 */
00464         pCsw->bCSWStatus = USB_CLASS_MSD_CSW_PHASEERROR;
00465         USBD_StallEp(BULK_OUT);
00466         SendCsw();
00467         msdState = MSDD_IDLE;
00468       }
00469     }
00470     return USB_STATUS_OK;
00471   }
00472 
00473   if ((status == USB_STATUS_OK) &&
00474       (USBD_GetUsbState() == USBD_STATE_CONFIGURED))
00475   {
00476     /* Stall both Ep's and wait for reset recovery */
00477     USBD_StallEp(BULK_OUT);
00478     USBD_StallEp(BULK_IN);
00479     msdState = MSDD_WAITFOR_RECOVERY;
00480   }
00481 
00482   return USB_STATUS_OK;
00483 }
00484 
00485 /**************************************************************************/
00490 __STATIC_INLINE bool CswMeaningful(void)
00491 {
00492   if ((pCbw->Reserved1 == 0) &&
00493       (pCbw->Obsolete == 0) &&
00494       (pCbw->Reserved2 == 0) &&
00495       (pCbw->Lun == 0) &&
00496       (pCbw->Reserved3 == 0))
00497   {
00498     return true;
00499   }
00500 
00501   return false;
00502 }
00503 
00504 /**************************************************************************/
00509 __STATIC_INLINE bool CswValid(void)
00510 {
00511   return pCbw->dCBWSignature == CBW_SIGNATURE ? true : false; /* Ascii USBC */
00512 }
00513 
00514 /**************************************************************************/
00518 __STATIC_INLINE void EnableNextCbw(void)
00519 {
00520   USBD_Read(BULK_OUT, (void*) &cbw, USB_MAX_EP_SIZE, CbwCallback);
00521 }
00522 
00523 /**************************************************************************/
00528 static void ProcessScsiCdb(void)
00529 {
00530   MSDSCSI_Inquiry_TypeDef      *cbI;
00531   MSDSCSI_RequestSense_TypeDef *cbRS;
00532   MSDSCSI_ReadCapacity_TypeDef *cbRC;
00533   MSDSCSI_Read10_TypeDef       *cbR10;
00534   MSDSCSI_Write10_TypeDef      *cbW10;
00535 
00536   EFM32_ALIGN(4)
00537   static MSDSCSI_ReadCapacityData_TypeDef ReadCapData __attribute__ ((aligned(4)));
00538 
00539   pCmdStatus->valid    = false;
00540   pCmdStatus->xferType = XFER_MEMORYMAPPED;
00541   pCmdStatus->maxBurst = MAX_BURST;
00542 
00543   switch (pCbw->CBWCB[ 0 ])
00544   {
00545   case SCSI_INQUIRY:
00546     cbI = (MSDSCSI_Inquiry_TypeDef*) &pCbw->CBWCB;
00547 
00548     if ((cbI->Evpd == 0) && (cbI->PageCode == 0))
00549     {
00550       /* Standard Inquiry data request */
00551       pCmdStatus->valid     = true;
00552       pCmdStatus->direction = DIR_DATA_IN;
00553       pCmdStatus->pData     = (uint8_t*) &InquiryData;
00554       pCmdStatus->xferLen   = EFM32_MIN(SCSI_INQUIRYDATA_LEN,
00555                                         __REV16(cbI->AllocationLength));
00556     }
00557     break;
00558 
00559   case SCSI_REQUESTSENSE:
00560     cbRS = (MSDSCSI_RequestSense_TypeDef*) &pCbw->CBWCB;
00561 
00562     if ((cbRS->Desc == 0) && (cbRS->Reserved1 == 0) &&
00563         (cbRS->Reserved2 == 0) && (cbRS->Reserved3 == 0))
00564     {
00565       pCmdStatus->valid     = true;
00566       pCmdStatus->direction = DIR_DATA_IN;
00567       pCmdStatus->pData     = (uint8_t*) pSenseData;
00568       pCmdStatus->xferLen   = EFM32_MIN(SCSI_REQUESTSENSEDATA_LEN,
00569                                         cbRS->AllocationLength);
00570       pSenseData = (MSDSCSI_RequestSenseData_TypeDef*) &NoSenseData;
00571     }
00572     break;
00573 
00574   case SCSI_READCAPACITY:
00575     cbRC = (MSDSCSI_ReadCapacity_TypeDef*) &pCbw->CBWCB;
00576 
00577     if ((cbRC->Pmi == 0) && (cbRC->Lba == 0))
00578     {
00579       ReadCapData.LogicalBlockAddress = __REV(MSDDMEDIA_GetSectorCount() - 1);
00580       ReadCapData.LogicalBlockLength  = __REV(512);
00581 
00582       pCmdStatus->valid     = true;
00583       pCmdStatus->direction = DIR_DATA_IN;
00584       pCmdStatus->pData     = (uint8_t*) &ReadCapData;
00585       pCmdStatus->xferLen   = SCSI_READCAPACITYDATA_LEN;
00586     }
00587     break;
00588 
00589   case SCSI_READ10:
00590     cbR10 = (MSDSCSI_Read10_TypeDef*) &pCbw->CBWCB;
00591 
00592     pCmdStatus->direction = DIR_DATA_IN;
00593     pCmdStatus->valid     = MSDDMEDIA_CheckAccess(pCmdStatus,
00594                                                   __REV(cbR10->Lba),
00595                                                   __REV16(cbR10->TransferLength));
00596     break;
00597 
00598   case SCSI_WRITE10:
00599     cbW10 = (MSDSCSI_Write10_TypeDef*) &pCbw->CBWCB;
00600 
00601     pCmdStatus->direction = DIR_DATA_OUT;
00602     pCmdStatus->valid     = MSDDMEDIA_CheckAccess(pCmdStatus,
00603                                                   __REV(cbW10->Lba),
00604                                                   __REV16(cbW10->TransferLength));
00605     break;
00606 
00607   case SCSI_TESTUNIT_READY:
00608     pCmdStatus->valid     = true;
00609     pCmdStatus->direction = pCbw->Direction;
00610     pCmdStatus->xferLen   = 0;
00611     break;
00612 
00613   case SCSI_STARTSTOP_UNIT:
00614     pCmdStatus->valid     = true;
00615     pCmdStatus->direction = pCbw->Direction;
00616     pCmdStatus->xferLen   = 0;
00617     pCmdStatus->xferType  = XFER_INDIRECT;
00618     break;
00619   }
00620 
00621   if (!pCmdStatus->valid)
00622   {
00623     pCmdStatus->xferLen   = 0;
00624     pCmdStatus->direction = pCbw->Direction;
00625     pSenseData            = (MSDSCSI_RequestSenseData_TypeDef*) &IllegalSenseData;
00626   }
00627 }
00628 
00629 /**************************************************************************/
00633 __STATIC_INLINE void SendCsw(void)
00634 {
00635   if ( ledPort != -1 )
00636     GPIO_PinOutToggle((GPIO_Port_TypeDef)ledPort, ledPin);
00637 
00638   USBD_Write(BULK_IN, (void*) &csw, CSW_LEN, NULL);
00639 }
00640 
00641 /**************************************************************************/
00653 static int UsbSetupCmd(const USB_Setup_TypeDef *setup)
00654 {
00655   int             retVal;
00656   static uint32_t tmp;
00657 
00658   retVal = USB_STATUS_REQ_UNHANDLED;
00659 
00660   /* Check if it is MSD class command: "Bulk-Only Mass Storage Reset" */
00661 
00662   if ((setup->Type == USB_SETUP_TYPE_CLASS) &&
00663       (setup->Direction == USB_SETUP_DIR_OUT) &&
00664       (setup->Recipient == USB_SETUP_RECIPIENT_INTERFACE) &&
00665       (setup->bRequest == USB_MSD_BOTRESET) &&
00666       (setup->wValue == 0) &&
00667       (setup->wIndex == 0) &&
00668       (setup->wLength == 0))
00669   {
00670     if (msdState == MSDD_WAITFOR_RECOVERY)
00671     {
00672       msdState = MSDD_IDLE;
00673     }
00674     retVal = USB_STATUS_OK;
00675   }
00676 
00677 
00678   /* Check if it is MSD class command: "Get Max LUN" */
00679 
00680   else if ((setup->Type == USB_SETUP_TYPE_CLASS) &&
00681            (setup->Direction == USB_SETUP_DIR_IN) &&
00682            (setup->Recipient == USB_SETUP_RECIPIENT_INTERFACE) &&
00683            (setup->bRequest == USB_MSD_GETMAXLUN) &&
00684            (setup->wValue == 0) &&
00685            (setup->wIndex == 0) &&
00686            (setup->wLength == 1))
00687   {
00688     /* Only one LUN (i.e. no support for multiple LUN's). Reply "0". */
00689     tmp    = 0;
00690     retVal = USBD_Write(0, (void*) &tmp, 1, NULL);
00691   }
00692 
00693 
00694   /* Check if it is a standard CLEAR_FEATURE endpoint command */
00695 
00696   else if ((setup->Type == USB_SETUP_TYPE_STANDARD) &&
00697            (setup->Direction == USB_SETUP_DIR_OUT) &&
00698            (setup->Recipient == USB_SETUP_RECIPIENT_ENDPOINT) &&
00699            (setup->bRequest == CLEAR_FEATURE) &&
00700            (setup->wValue == USB_FEATURE_ENDPOINT_HALT) &&
00701            (setup->wLength == 0))
00702   {
00703     if (((setup->wIndex & 0xFF) == BULK_OUT) ||
00704         ((setup->wIndex & 0xFF) == BULK_IN))
00705     {
00706       retVal = USB_STATUS_OK;
00707 
00708       /* Dont unstall ep's when waiting for reset recovery */
00709       if (msdState != MSDD_WAITFOR_RECOVERY)
00710       {
00711         retVal = USBD_UnStallEp(setup->wIndex & 0xFF);
00712 
00713         if ((setup->wIndex & 0xFF) == BULK_IN)
00714         {
00715           if (msdState == MSDD_WAIT_FOR_INUNSTALLED)
00716           {
00717             SendCsw();
00718             EnableNextCbw();
00719             msdState = MSDD_WAITFOR_CBW;
00720           }
00721         }
00722         else
00723         {
00724           EnableNextCbw();
00725           msdState = MSDD_WAITFOR_CBW;
00726         }
00727       }
00728     }
00729   }
00730 
00731   return retVal;
00732 }
00733 
00734 /**************************************************************************/
00744 static void UsbStateChangeEvent(USBD_State_TypeDef oldState,
00745                                 USBD_State_TypeDef newState)
00746 {
00747   if (newState == USBD_STATE_CONFIGURED)
00748   {
00749     /* We have been configured, start MSD functionality ! */
00750     EnableNextCbw();
00751     msdState = MSDD_WAITFOR_CBW;
00752   }
00753 
00754   else if ((oldState == USBD_STATE_CONFIGURED) &&
00755            (newState != USBD_STATE_SUSPENDED))
00756   {
00757     /* We have been de-configured */
00758     msdState = MSDD_IDLE;
00759   }
00760 
00761   else if (newState == USBD_STATE_SUSPENDED)
00762   {
00763     /* We have been suspended.                     */
00764     msdState = MSDD_IDLE;
00765 
00766     /* Reduce current consumption to below 2.5 mA. */
00767   }
00768 }
00769 
00770 /**************************************************************************/
00784 static void UsbXferBotData(uint8_t *data, uint32_t len,
00785                            USB_XferCompleteCb_TypeDef cb)
00786 {
00787   if (pCmdStatus->direction)
00788   {
00789     USBD_Write(BULK_IN, data, len, cb);
00790   }
00791   else
00792   {
00793     USBD_Read(BULK_OUT, data, len, cb);
00794   }
00795 }
00796 
00797 /**************************************************************************/
00805 static void XferBotData(uint32_t length)
00806 {
00807   pCmdStatus->xferLen   = length;
00808   pCsw->dCSWDataResidue = pCbw->dCBWDataTransferLength;
00809 
00810   if (pCmdStatus->xferType == XFER_INDIRECT)
00811   {
00812     /* Access media in "background" polling loop, i.e. in MSDD_Handler() */
00813     savedState = msdState;
00814     msdState   = MSDD_ACCESS_INDIRECT;
00815   }
00816   else
00817   {
00818     UsbXferBotData(pCmdStatus->pData,
00819                    EFM32_MIN(length, pCmdStatus->maxBurst),
00820                    XferBotDataCallback);
00821   }
00822 }
00823 
00824 /**************************************************************************/
00843 static int XferBotDataCallback(USB_Status_TypeDef status,
00844                                uint32_t xferred, uint32_t remaining)
00845 {
00846   (void) status;
00847   (void) remaining;
00848 
00849   pCmdStatus->xferLen   -= xferred;
00850   pCsw->dCSWDataResidue -= xferred;
00851 
00852   if (pCmdStatus->xferLen)
00853   {
00854     pCmdStatus->pData += xferred;
00855     UsbXferBotData(pCmdStatus->pData,
00856                    EFM32_MIN(pCmdStatus->xferLen, pCmdStatus->maxBurst),
00857                    XferBotDataCallback);
00858   }
00859   else
00860   {
00861     if (msdState == MSDD_SEND_CSW)
00862     {
00863       SendCsw();
00864       EnableNextCbw();
00865       msdState = MSDD_WAITFOR_CBW;
00866     }
00867 
00868     else if (msdState == MSDD_STALL_IN)
00869     {
00870       USBD_StallEp(BULK_IN);
00871       msdState = MSDD_WAIT_FOR_INUNSTALLED;
00872     }
00873   }
00874 
00875   return USB_STATUS_OK;
00876 }
00877 
00878 /**************************************************************************/
00895 static int XferBotDataIndirectCallback(USB_Status_TypeDef status,
00896                                        uint32_t xferred, uint32_t remaining)
00897 {
00898   (void) status;
00899   (void) remaining;
00900 
00901   pCmdStatus->xferLen   -= xferred;
00902   pCsw->dCSWDataResidue -= xferred;
00903 
00904   if (pCmdStatus->direction)
00905   {
00906     pCmdStatus->lba += xferred / 512;
00907     msdState         = MSDD_ACCESS_INDIRECT;
00908   }
00909   else
00910   {
00911     msdState = MSDD_WRITE_INDIRECT;
00912   }
00913 
00914   return USB_STATUS_OK;
00915 }
00916