/***************************************************************************//** * @brief * Perform an MSD Bulk Only Transfer (BOT). * * @param[in] cbw * Pointer to a Command Block Wrapper (CBW) data structure. * * @param[in] data * Data buffer for data to be transferred. * * @return * A positive (or zero) value indicating the number of bytes transferred. * @n A negative value indicates a transfer error code enumerated in * @ref MSDBOT_Status_TypeDef. ******************************************************************************/ int MSDBOT_Xfer(void* cbw, void* data) { uint32_t len; int timeout, result, direction, retVal; MSDBOT_CBW_TypeDef *pCbw = (MSDBOT_CBW_TypeDef*) cbw; /* Send CBW. */ result = USBH_WriteB(epOut, cbw, CBW_LEN, DEFAULT_TIMEOUT); if (result != CBW_LEN) { ResetRecovery(); return MSDBOT_XFER_ERROR; } retVal = 0; direction = pCbw->Direction; len = pCbw->dCBWDataTransferLength; /* Send/receive data (optional phase). */ if (len) { timeout = DEFAULT_TIMEOUT + (len / timeoutFactor); if (direction) result = USBH_ReadB(epIn, data, len, timeout); else result = USBH_WriteB(epOut, data, len, timeout); retVal = result; if (result == USB_STATUS_EP_STALLED) { if (direction) USBH_UnStallEpB(epIn); else USBH_UnStallEpB(epOut); } else if (result <= 0) { ResetRecovery(); return MSDBOT_XFER_ERROR; } } /* Retrieve CSW. */ result = USBH_ReadB(epIn, csw, CSW_LEN, DEFAULT_TIMEOUT); if (result != CSW_LEN) { if (result == USB_STATUS_EP_STALLED) { if (direction) USBH_UnStallEpB(epIn); else USBH_UnStallEpB(epOut); result = USBH_ReadB(epIn, csw, CSW_LEN, DEFAULT_TIMEOUT); if (result != CSW_LEN) { ResetRecovery(); return MSDBOT_XFER_ERROR; } } else { ResetRecovery(); return MSDBOT_XFER_ERROR; } } if (CswValid(pCbw) && CswMeaningful(pCbw)) { if (pCsw->bCSWStatus == USB_CLASS_MSD_CSW_CMDPASSED) { return retVal; } return MSDBOT_CMD_FAILED; } ResetRecovery(); return MSDBOT_XFER_ERROR; }
/**************************************************************************//** * @brief * Called on USB transfer completion callback. Will qualify and parse a * Command Block Wrapper (CBW). * * @param[in] status * The transfer status. * * @param[in] xferred * Number of bytes actually transferred. * * @param[in] remaining * Number of bytes not transferred. * * @return * USB_STATUS_OK. *****************************************************************************/ static int CbwCallback(USB_Status_TypeDef status, uint32_t xferred, uint32_t remaining) { (void) remaining; if ((msdState == MSDD_WAITFOR_CBW) && (status == USB_STATUS_OK) && (xferred == CBW_LEN) && (CswValid()) && (CswMeaningful())) { if ( ledPort != -1 ) GPIO_PinOutToggle((GPIO_Port_TypeDef)ledPort, ledPin); /* Check the SCSI command descriptor block (CDB) */ ProcessScsiCdb(); if (pCmdStatus->valid) pCsw->bCSWStatus = USB_CLASS_MSD_CSW_CMDPASSED; else pCsw->bCSWStatus = USB_CLASS_MSD_CSW_CMDFAILED; pCsw->dCSWSignature = CSW_SIGNATURE; pCsw->dCSWTag = pCbw->dCBWTag; pCsw->dCSWDataResidue = pCbw->dCBWDataTransferLength; /* Check the "thirteen cases" */ if ((pCbw->dCBWDataTransferLength != 0) && (pCbw->Direction != pCmdStatus->direction)) { /* Handle cases 8 and 10 */ pCsw->bCSWStatus = USB_CLASS_MSD_CSW_PHASEERROR; if (pCbw->Direction) { /* Host expects to receive data, case 8 */ USBD_StallEp(BULK_IN); msdState = MSDD_WAIT_FOR_INUNSTALLED; } else { /* Host expects to send data, case 10 */ USBD_StallEp(BULK_OUT); SendCsw(); msdState = MSDD_IDLE; } } else if (pCbw->Direction || (pCbw->dCBWDataTransferLength == 0)) { /* SCSI IN commands or commands without data phase */ /* Handle cases 1-7 */ if (pCbw->dCBWDataTransferLength == 0) { /* Host expects no data, case 1, 2 or 3 */ if (pCmdStatus->xferLen) { /* Device has data to transmit, case 2 & 3 */ pCsw->bCSWStatus = USB_CLASS_MSD_CSW_PHASEERROR; } if ((pCmdStatus->xferLen == 0) && (pCmdStatus->xferType == XFER_INDIRECT)) { /* Commands with no data phase which require timeconsuming */ /* processing are executed in MSDD_Handler() */ msdState = MSDD_DO_CMD_TASK; } else { SendCsw(); EnableNextCbw(); msdState = MSDD_WAITFOR_CBW; } } else if (pCbw->dCBWDataTransferLength == pCmdStatus->xferLen) { /* Host and device agree on transferlength, case 6 */ /* Send data to host */ msdState = MSDD_SEND_CSW; XferBotData(pCmdStatus->xferLen); } else if (pCbw->dCBWDataTransferLength > pCmdStatus->xferLen) { /* Host expects more data than device can provide, case 4 and 5 */ if (pCmdStatus->xferLen > 0) { /* Device has data, case 5 */ /* Send data to host */ msdState = MSDD_STALL_IN; XferBotData(pCmdStatus->xferLen); } else { /* Device has no data, case 4 */ USBD_StallEp(BULK_IN); msdState = MSDD_WAIT_FOR_INUNSTALLED; } } else { /* Host expects less data than device will provide, case 7 */ pCsw->bCSWStatus = USB_CLASS_MSD_CSW_PHASEERROR; /* Send data to host */ msdState = MSDD_SEND_CSW; XferBotData(pCbw->dCBWDataTransferLength); } } else /* Host Direction is OUT and Host transferlength > 0 */ { /* SCSI OUT commands */ /* Handle cases 9, 11, 12 and 13 */ if (pCbw->dCBWDataTransferLength == pCmdStatus->xferLen) { /* Host and device agree on transferlength, case 12 */ /* Read data from host */ msdState = MSDD_SEND_CSW; XferBotData(pCmdStatus->xferLen); } else if (pCbw->dCBWDataTransferLength > pCmdStatus->xferLen) { /* Host intend to send more data than device expects, case 9 & 11 */ pCsw->bCSWStatus = USB_CLASS_MSD_CSW_CMDFAILED; USBD_StallEp(BULK_OUT); SendCsw(); msdState = MSDD_IDLE; } else { /* Host has less data than device expects to receive, case 13 */ pCsw->bCSWStatus = USB_CLASS_MSD_CSW_PHASEERROR; USBD_StallEp(BULK_OUT); SendCsw(); msdState = MSDD_IDLE; } } return USB_STATUS_OK; } if ((status == USB_STATUS_OK) && (USBD_GetUsbState() == USBD_STATE_CONFIGURED)) { /* Stall both Ep's and wait for reset recovery */ USBD_StallEp(BULK_OUT); USBD_StallEp(BULK_IN); msdState = MSDD_WAITFOR_RECOVERY; } return USB_STATUS_OK; }