aboutsummaryrefslogtreecommitdiffstats
path: root/os/various/usb_msc.c
diff options
context:
space:
mode:
authorgdisirio <gdisirio@35acf78f-673a-0410-8e92-d51de3d6d3f4>2013-06-15 15:58:20 +0000
committergdisirio <gdisirio@35acf78f-673a-0410-8e92-d51de3d6d3f4>2013-06-15 15:58:20 +0000
commit7c68ef157d009f9932ac47ba21ba5d74e321623f (patch)
tree6c2442ca44cd090656ec3059ffb959f3d747e18d /os/various/usb_msc.c
parent076746af63d317f8e96766b9137a65679f60463f (diff)
parente0d850113610f3efa0c0ac4946901f683e5e7332 (diff)
downloadChibiOS-7c68ef157d009f9932ac47ba21ba5d74e321623f.tar.gz
ChibiOS-7c68ef157d009f9932ac47ba21ba5d74e321623f.tar.bz2
ChibiOS-7c68ef157d009f9932ac47ba21ba5d74e321623f.zip
git-svn-id: svn://svn.code.sf.net/p/chibios/svn/branches/kernel_3_dev@5854 35acf78f-673a-0410-8e92-d51de3d6d3f4
Diffstat (limited to 'os/various/usb_msc.c')
-rw-r--r--os/various/usb_msc.c299
1 files changed, 299 insertions, 0 deletions
diff --git a/os/various/usb_msc.c b/os/various/usb_msc.c
new file mode 100644
index 000000000..61f4202d3
--- /dev/null
+++ b/os/various/usb_msc.c
@@ -0,0 +1,299 @@
+/*
+ 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;
+}
+
+/** @} */