Exemple #1
0
/***************************************************************************//**
 * @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;
}