300 lines
8.5 KiB
C
300 lines
8.5 KiB
C
/*
|
|
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
/*-*
|
|
* @file usb_msc.c
|
|
* @brief USB Mass Storage Class code.
|
|
*
|
|
* @addtogroup USB_MSC
|
|
* @{
|
|
*/
|
|
|
|
#include "ch.h"
|
|
#include "hal.h"
|
|
|
|
#include "usb_msc.h"
|
|
|
|
/*===========================================================================*/
|
|
/* Driver exported variables. */
|
|
/*===========================================================================*/
|
|
|
|
/*===========================================================================*/
|
|
/* Driver local variables. */
|
|
/*===========================================================================*/
|
|
|
|
/**
|
|
* @brief Zero-filled constant buffer.
|
|
*/
|
|
static const uint8_t zerobuf[4] = {0, 0, 0, 0};
|
|
|
|
/**
|
|
* @brief Answer to the INQUIRY command.
|
|
*/
|
|
static const uint8_t scsi_inquiry_data[] = {
|
|
0x00, /* Direct Access Device. */
|
|
0x80, /* RMB = 1: Removable Medium. */
|
|
0x02, /* ISO, ECMA, ANSI = 2. */
|
|
0x00, /* UFI response format. */
|
|
|
|
36 - 4, /* Additional Length. */
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
/* Vendor Identification */
|
|
'C', 'h', 'i', 'b', 'i', 'O', 'S', ' ',
|
|
/* Product Identification */
|
|
'S', 'D', ' ', 'F', 'l', 'a', 's', 'h',
|
|
' ', 'D', 'i', 's', 'k', ' ', ' ', ' ',
|
|
/* Product Revision Level */
|
|
'1', '.', '0', ' '
|
|
};
|
|
|
|
/**
|
|
* @brief Generic buffer.
|
|
*/
|
|
uint8_t buf[16];
|
|
|
|
/*===========================================================================*/
|
|
/* MMC interface code. */
|
|
/*===========================================================================*/
|
|
|
|
/*===========================================================================*/
|
|
/* SCSI emulation code. */
|
|
/*===========================================================================*/
|
|
|
|
static uint8_t scsi_read_format_capacities(uint32_t *nblocks,
|
|
uint32_t *secsize) {
|
|
|
|
*nblocks = 1024;
|
|
*secsize = 512;
|
|
return 3; /* No Media.*/
|
|
}
|
|
|
|
/*===========================================================================*/
|
|
/* Mass Storage Class related code. */
|
|
/*===========================================================================*/
|
|
|
|
/**
|
|
* @brief MSC state machine current state.
|
|
*/
|
|
static mscstate_t msc_state;
|
|
|
|
/**
|
|
* @brief Received CBW.
|
|
*/
|
|
static msccbw_t CBW;
|
|
|
|
/**
|
|
* @brief CSW to be transmitted.
|
|
*/
|
|
static msccsw_t CSW;
|
|
|
|
/**
|
|
* @brief MSC state machine initialization.
|
|
*
|
|
* @param[in] usbp pointer to the @p USBDriver object
|
|
*/
|
|
static void msc_reset(USBDriver *usbp) {
|
|
|
|
msc_state = MSC_IDLE;
|
|
chSysLockFromIsr();
|
|
usbStartReceiveI(usbp, MSC_DATA_OUT_EP, (uint8_t *)&CBW, sizeof CBW);
|
|
chSysUnlockFromIsr();
|
|
}
|
|
|
|
static void msc_transmit(USBDriver *usbp, const uint8_t *p, size_t n) {
|
|
|
|
if (n > CBW.dCBWDataTransferLength)
|
|
n = CBW.dCBWDataTransferLength;
|
|
CSW.dCSWDataResidue = CBW.dCBWDataTransferLength - (uint32_t)n;
|
|
chSysLockFromIsr();
|
|
usbStartTransmitI(usbp, MSC_DATA_IN_EP, p, n);
|
|
chSysUnlockFromIsr();
|
|
}
|
|
|
|
static void msc_sendstatus(USBDriver *usbp) {
|
|
|
|
msc_state = MSC_SENDING_CSW;
|
|
chSysLockFromIsr();
|
|
usbStartTransmitI(usbp, MSC_DATA_IN_EP, (uint8_t *)&CSW, sizeof CSW);
|
|
chSysUnlockFromIsr();
|
|
}
|
|
|
|
static bool_t msc_decode(USBDriver *usbp) {
|
|
uint32_t nblocks, secsize;
|
|
|
|
switch (CBW.CBWCB[0]) {
|
|
case SCSI_REQUEST_SENSE:
|
|
break;
|
|
case SCSI_INQUIRY:
|
|
msc_transmit(usbp, (uint8_t *)&scsi_inquiry_data,
|
|
sizeof scsi_inquiry_data);
|
|
CSW.bCSWStatus = MSC_CSW_STATUS_PASSED;
|
|
break;
|
|
case SCSI_READ_FORMAT_CAPACITIES:
|
|
buf[8] = scsi_read_format_capacities(&nblocks, &secsize);
|
|
buf[0] = buf[1] = buf[2] = 0;
|
|
buf[3] = 8;
|
|
buf[4] = (uint8_t)(nblocks >> 24);
|
|
buf[5] = (uint8_t)(nblocks >> 16);
|
|
buf[6] = (uint8_t)(nblocks >> 8);
|
|
buf[7] = (uint8_t)(nblocks >> 0);
|
|
buf[9] = (uint8_t)(secsize >> 16);
|
|
buf[10] = (uint8_t)(secsize >> 8);
|
|
buf[11] = (uint8_t)(secsize >> 0);
|
|
msc_transmit(usbp, buf, 12);
|
|
CSW.bCSWStatus = MSC_CSW_STATUS_PASSED;
|
|
break;
|
|
default:
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*===========================================================================*/
|
|
/* Driver exported functions. */
|
|
/*===========================================================================*/
|
|
|
|
/**
|
|
* @brief Default requests hook.
|
|
* @details The application must use this function as callback for the
|
|
* messages hook.
|
|
* The following requests are emulated:
|
|
* - MSC_GET_MAX_LUN_COMMAND.
|
|
* - MSC_MASS_STORAGE_RESET_COMMAND.
|
|
* .
|
|
*
|
|
* @param[in] usbp pointer to the @p USBDriver object
|
|
* @return The hook status.
|
|
* @retval TRUE Message handled internally.
|
|
* @retval FALSE Message not handled.
|
|
*/
|
|
bool_t mscRequestsHook(USBDriver *usbp) {
|
|
|
|
if ((usbp->setup[0] & (USB_RTYPE_TYPE_MASK | USB_RTYPE_RECIPIENT_MASK)) ==
|
|
(USB_RTYPE_TYPE_CLASS | USB_RTYPE_RECIPIENT_INTERFACE)) {
|
|
switch (usbp->setup[1]) {
|
|
case MSC_GET_MAX_LUN_COMMAND:
|
|
usbSetupTransfer(usbp, (uint8_t *)zerobuf, 1, NULL);
|
|
return TRUE;
|
|
case MSC_MASS_STORAGE_RESET_COMMAND:
|
|
msc_reset(usbp);
|
|
usbSetupTransfer(usbp, NULL, 0, NULL);
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief Default data transmitted callback.
|
|
* @details The application must use this function as callback for the IN
|
|
* data endpoint.
|
|
*
|
|
* @param[in] usbp pointer to the @p USBDriver object
|
|
* @param[in] ep endpoint number
|
|
*/
|
|
void mscDataTransmitted(USBDriver *usbp, usbep_t ep) {
|
|
|
|
switch (msc_state) {
|
|
case MSC_DATA_IN:
|
|
CSW.dCSWSignature = MSC_CSW_SIGNATURE;
|
|
CSW.dCSWTag = CBW.dCBWTag;
|
|
chSysLockFromIsr();
|
|
usbStartTransmitI(usbp, ep, (uint8_t *)&CSW, sizeof CSW);
|
|
chSysUnlockFromIsr();
|
|
msc_state = MSC_SENDING_CSW;
|
|
break;
|
|
case MSC_SENDING_CSW:
|
|
chSysLockFromIsr();
|
|
usbStartReceiveI(usbp, MSC_DATA_OUT_EP, (uint8_t *)&CBW, sizeof CBW);
|
|
chSysUnlockFromIsr();
|
|
msc_state = MSC_IDLE;
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Default data received callback.
|
|
* @details The application must use this function as callback for the OUT
|
|
* data endpoint.
|
|
*
|
|
* @param[in] usbp pointer to the @p USBDriver object
|
|
* @param[in] ep endpoint number
|
|
*/
|
|
void mscDataReceived(USBDriver *usbp, usbep_t ep) {
|
|
size_t n;
|
|
|
|
n = usbGetReceiveTransactionSizeI(usbp, ep);
|
|
switch (msc_state) {
|
|
case MSC_IDLE:
|
|
if ((n != sizeof(msccbw_t)) || (CBW.dCBWSignature != MSC_CBW_SIGNATURE))
|
|
goto stall_out; /* 6.6.1 */
|
|
|
|
/* Decoding SCSI command.*/
|
|
if (msc_decode(usbp)) {
|
|
if (CBW.dCBWDataTransferLength == 0) {
|
|
CSW.bCSWStatus = MSC_CSW_STATUS_FAILED;
|
|
CSW.dCSWDataResidue = 0;
|
|
msc_sendstatus(usbp);
|
|
return;
|
|
}
|
|
goto stall_both;
|
|
}
|
|
|
|
/* Commands with zero transfer length, 5.1.*/
|
|
if (CBW.dCBWDataTransferLength == 0) {
|
|
msc_sendstatus(usbp);
|
|
return;
|
|
}
|
|
|
|
/* Transfer direction.*/
|
|
if (CBW.bmCBWFlags & 0x80) {
|
|
/* IN, Device to Host.*/
|
|
msc_state = MSC_DATA_IN;
|
|
}
|
|
else {
|
|
/* OUT, Host to Device.*/
|
|
msc_state = MSC_DATA_OUT;
|
|
}
|
|
break;
|
|
case MSC_DATA_OUT:
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
return;
|
|
stall_out:
|
|
msc_state = MSC_ERROR;
|
|
chSysLockFromIsr();
|
|
usbStallReceiveI(usbp, ep);
|
|
chSysUnlockFromIsr();
|
|
return;
|
|
stall_both:
|
|
msc_state = MSC_ERROR;
|
|
chSysLockFromIsr();
|
|
usbStallTransmitI(usbp, ep);
|
|
usbStallReceiveI(usbp, ep);
|
|
chSysUnlockFromIsr();
|
|
return;
|
|
}
|
|
|
|
/** @} */
|