diff options
Diffstat (limited to 'os/hal')
-rw-r--r-- | os/hal/hal.mk | 3 | ||||
-rw-r--r-- | os/hal/include/hal_community.h | 5 | ||||
-rw-r--r-- | os/hal/include/hal_onewire.h | 1 | ||||
-rw-r--r-- | os/hal/include/hal_usb_msd.h | 192 | ||||
-rw-r--r-- | os/hal/src/hal_usb_msd.c | 386 |
5 files changed, 585 insertions, 2 deletions
diff --git a/os/hal/hal.mk b/os/hal/hal.mk index ce74620..f05ddbc 100644 --- a/os/hal/hal.mk +++ b/os/hal/hal.mk @@ -18,6 +18,7 @@ HALSRC += ${CHIBIOS_CONTRIB}/os/hal/src/hal_community.c \ ${CHIBIOS_CONTRIB}/os/hal/src/hal_eeprom.c \
${CHIBIOS_CONTRIB}/os/hal/src/hal_timcap.c \
${CHIBIOS_CONTRIB}/os/hal/src/hal_qei.c \
- ${CHIBIOS_CONTRIB}/os/hal/src/hal_usb_hid.c
+ ${CHIBIOS_CONTRIB}/os/hal/src/hal_usb_hid.c \
+ ${CHIBIOS_CONTRIB}/os/hal/src/hal_usb_msd.c
HALINC += ${CHIBIOS_CONTRIB}/os/hal/include
diff --git a/os/hal/include/hal_community.h b/os/hal/include/hal_community.h index 1518c7e..430df7c 100644 --- a/os/hal/include/hal_community.h +++ b/os/hal/include/hal_community.h @@ -67,6 +67,10 @@ #define HAL_USE_USB_HID FALSE
#endif
+#if !defined(HAL_USE_USB_MSD)
+#define HAL_USE_USB_MSD FALSE
+#endif
+
/* Abstract interfaces.*/
/* Shared headers.*/
@@ -84,6 +88,7 @@ #include "hal_crc.h"
#include "hal_eeprom.h"
#include "hal_usb_hid.h"
+#include "hal_usb_msd.h"
/*===========================================================================*/
/* Driver constants. */
diff --git a/os/hal/include/hal_onewire.h b/os/hal/include/hal_onewire.h index 9fb5be2..0454212 100644 --- a/os/hal/include/hal_onewire.h +++ b/os/hal/include/hal_onewire.h @@ -328,7 +328,6 @@ extern onewireDriver OWD1; #ifdef __cplusplus extern "C" { #endif - void onewireInit(void); void onewireObjectInit(onewireDriver *owp); void onewireStart(onewireDriver *owp, const onewireConfig *config); void onewireStop(onewireDriver *owp); diff --git a/os/hal/include/hal_usb_msd.h b/os/hal/include/hal_usb_msd.h new file mode 100644 index 0000000..08241df --- /dev/null +++ b/os/hal/include/hal_usb_msd.h @@ -0,0 +1,192 @@ +/* + ChibiOS/HAL - Copyright (C) 2016 Uladzimir Pylinsky aka barthess + + 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 hal_usb_msd.h + * @brief USM mass storage device driver macros and structures. + * + * @addtogroup usb_msd + * @{ + */ + +#ifndef HAL_USB_MSD_H +#define HAL_USB_MSD_H + +#if (HAL_USE_USB_MSD == TRUE) || defined(__DOXYGEN__) + +#include "lib_scsi.h" + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +#define USB_MSD_DATA_EP 0x01 +#define USB_MSD_EP_SIZE 0x40 + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if !HAL_USE_USB +#error "Mass storage Driver requires HAL_USE_USB" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of a structure representing an USB mass storage driver. + */ +typedef struct USBMassStorageDriver USBMassStorageDriver; + +/** + * @brief Type of a driver state machine possible states. + */ +typedef enum { + USB_MSD_UNINIT = 0, + USB_MSD_STOP, + USB_MSD_READY, +} usbmsdstate_t; + +/** + * @brief Represents command block wrapper structure. + * @details See USB Mass Storage Class Specification. + */ +typedef struct PACKED_VAR { + uint32_t signature; + uint32_t tag; + uint32_t data_len; + uint8_t flags; + uint8_t lun; + uint8_t cmd_len; + uint8_t cmd_data[16]; +} msd_cbw_t; + +/** + * @brief Represents command status wrapper structure. + * @details See USB Mass Storage Class Specification. + */ +typedef struct PACKED_VAR { + uint32_t signature; + uint32_t tag; + uint32_t data_residue; + uint8_t status; +} msd_csw_t; + +/** + * @brief Transport handler passed to SCSI layer. + */ +typedef struct { + /** + * @brief Pointer to the @p USBDriver object. + */ + USBDriver *usbp; + /** + * @brief USB endpoint number. + */ + usbep_t ep; +} usb_scsi_transport_handler_t; + + +/** + * @brief Structure representing an USB mass storage driver. + */ +struct USBMassStorageDriver { + /** + * @brief Pointer to the @p USBDriver object. + */ + USBDriver *usbp; + /** + * @brief Driver state. + */ + usbmsdstate_t state; + /** + * @brief CBW structure. + */ + msd_cbw_t cbw; + /** + * @brief CSW structure. + */ + msd_csw_t csw; + /** + * @brief Thread working area. + */ + THD_WORKING_AREA( waMSDWorker, 512); + /** + * @brief Worker thread handler. + */ + thread_reference_t worker; + /** + * @brief SCSI target driver structure. + */ + SCSITarget scsi_target; + /** + * @brief SCSI target configuration structure. + */ + SCSITargetConfig scsi_config; + /** + * @brief SCSI transport structure. + */ + SCSITransport scsi_transport; + /** + * @brief SCSI over USB transport handler structure. + */ + usb_scsi_transport_handler_t usb_scsi_transport_handler; +}; + + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +extern USBMassStorageDriver USBMSD1; + +#ifdef __cplusplus +extern "C" { +#endif + void msdObjectInit(USBMassStorageDriver *msdp); + void msdStart(USBMassStorageDriver *msdp, USBDriver *usbp, + BaseBlockDevice *blkdev, uint8_t *blkbuf, + const scsi_inquiry_response_t *scsi_inquiry_response); + void msdStop(USBMassStorageDriver *msdp); + bool msd_request_hook(USBDriver *usbp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_USB_MSD */ + +#endif /* HAL_USB_MSD_H */ + +/** @} */ + + + + + + + + + diff --git a/os/hal/src/hal_usb_msd.c b/os/hal/src/hal_usb_msd.c new file mode 100644 index 0000000..14f1165 --- /dev/null +++ b/os/hal/src/hal_usb_msd.c @@ -0,0 +1,386 @@ +/* + ChibiOS/HAL - Copyright (C) 2016 Uladzimir Pylinsky aka barthess + + 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 hal_usb_msd.c + * @brief USM mass storage device code. + * + * @addtogroup usb_msd + * @{ + */ + +#include "hal.h" + +#if (HAL_USE_USB_MSD == TRUE) || defined(__DOXYGEN__) + +#include <string.h> + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define MSD_REQ_RESET 0xFF +#define MSD_GET_MAX_LUN 0xFE + +#define MSD_CBW_SIGNATURE 0x43425355 +#define MSD_CSW_SIGNATURE 0x53425355 + +#define MSD_THD_PRIO NORMALPRIO + +#define CBW_FLAGS_RESERVED_MASK 0b01111111 +#define CBW_LUN_RESERVED_MASK 0b11110000 +#define CBW_CMD_LEN_RESERVED_MASK 0b11000000 + +#define CSW_STATUS_PASSED 0x00 +#define CSW_STATUS_FAILED 0x01 +#define CSW_STATUS_PHASE_ERROR 0x02 + +#define MSD_SETUP_WORD(setup, index) (uint16_t)(((uint16_t)setup[index+1] << 8)\ + | (setup[index] & 0x00FF)) + +#define MSD_SETUP_VALUE(setup) MSD_SETUP_WORD(setup, 2) +#define MSD_SETUP_INDEX(setup) MSD_SETUP_WORD(setup, 4) +#define MSD_SETUP_LENGTH(setup) MSD_SETUP_WORD(setup, 6) + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ +/** + * @brief USB mass storage driver identifier. + */ +USBMassStorageDriver USBMSD1; + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/** + * @brief Hardcoded default SCSI inquiry response structure. + */ +static const scsi_inquiry_response_t default_scsi_inquiry_response = { + 0x00, /* direct access block device */ + 0x80, /* removable */ + 0x04, /* SPC-2 */ + 0x02, /* response data format */ + 0x20, /* response has 0x20 + 4 bytes */ + 0x00, + 0x00, + 0x00, + "Chibios", + "Mass Storage", + {'v',CH_KERNEL_MAJOR+'0','.',CH_KERNEL_MINOR+'0'} +}; + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + + */ +/** + * @brief Checks validity of CBW content. + * @details The device shall consider the CBW valid when: + * • The CBW was received after the device had sent a CSW or after a reset, + * • the CBW is 31 (1Fh) bytes in length, + * • and the dCBWSignature is equal to 43425355h. + * + * @param[in] cbw pointer to the @p msd_cbw_t object + * @param[in] recvd number of received bytes + * + * @return Operation status. + * @retval true CBW is meaningful. + * @retval false CBW is bad. + * + * @notapi + */ +static bool cbw_valid(const msd_cbw_t *cbw, msg_t recvd) { + if ((sizeof(msd_cbw_t) != recvd) || (cbw->signature != MSD_CBW_SIGNATURE)) { + return false; + } + else { + return true; + } +} + +/** + * @brief Checks meaningfulness of CBW content. + * @details The device shall consider the contents of a valid CBW meaningful when: + * • no reserved bits are set, + * • the bCBWLUN contains a valid LUN supported by the device, + * • and both bCBWCBLength and the content of the CBWCB are in + * accordance with bInterfaceSubClass. + * + * @param[in] cbw pointer to the @p msd_cbw_t object + * + * @return Operation status. + * @retval true CBW is meaningful. + * @retval false CBW is bad. + * + * @notapi + */ +static bool cbw_meaningful(const msd_cbw_t *cbw) { + if (((cbw->cmd_len & CBW_CMD_LEN_RESERVED_MASK) != 0) + || ((cbw->flags & CBW_FLAGS_RESERVED_MASK) != 0) + || (cbw->lun != 0)) { + return false; + } + else { + return true; + } +} + +/** + * @brief SCSI transport transmit function. + * + * @param[in] transport pointer to the @p SCSITransport object + * @param[in] data payload + * @param[in] len number of bytes to be transmitted + * + * @return Number of successfully transmitted bytes. + + * @notapi + */ +static uint32_t scsi_transport_transmit(const SCSITransport *transport, + const uint8_t *data, size_t len) { + + usb_scsi_transport_handler_t *trp = transport->handler; + msg_t status = usbTransmit(trp->usbp, trp->ep, data, len); + if (MSG_OK == status) + return len; + else + return 0; +} + +/** + * @brief SCSI transport receive function. + * + * @param[in] transport pointer to the @p SCSITransport object + * @param[in] data payload + * @param[in] len number bytes to be received + * + * @return Number of successfully received bytes. + + * @notapi + */ +static uint32_t scsi_transport_receive(const SCSITransport *transport, + uint8_t *data, size_t len) { + + usb_scsi_transport_handler_t *trp = transport->handler; + msg_t status = usbReceive(trp->usbp, trp->ep, data, len); + if (MSG_RESET != status) + return status; + else + return 0; +} + +/** + * @brief Fills and sends CSW message. + * + * @param[in] msdp pointer to the @p USBMassStorageDriver object + * @param[in] status status returned by SCSI layer + * @param[in] residue number of residue bytes in case of failed transaction + * + * @notapi + */ +static void send_csw(USBMassStorageDriver *msdp, uint8_t status, + uint32_t residue) { + + msdp->csw.signature = MSD_CSW_SIGNATURE; + msdp->csw.data_residue = residue; + msdp->csw.tag = msdp->cbw.tag; + msdp->csw.status = status; + + usbTransmit(msdp->usbp, USB_MSD_DATA_EP, (uint8_t *)&msdp->csw, + sizeof(msd_csw_t)); +} + +/** + * @brief Mass storage worker thread. + * + * @param[in] arg pointer to the @p USBMassStorageDriver object + * + * @notapi + */ +static THD_FUNCTION(usb_msd_worker, arg) { + USBMassStorageDriver *msdp = arg; + + while(! chThdShouldTerminateX()) { + const msg_t status = usbReceive(msdp->usbp, USB_MSD_DATA_EP, + (uint8_t *)&msdp->cbw, sizeof(msd_cbw_t)); + if (MSG_RESET == status) { + osalThreadSleepMilliseconds(50); + } + else if (cbw_valid(&msdp->cbw, status) && cbw_meaningful(&msdp->cbw)) { + if (SCSI_SUCCESS == scsiExecCmd(&msdp->scsi_target, msdp->cbw.cmd_data)) { + send_csw(msdp, CSW_STATUS_PASSED, 0); + } + else { + send_csw(msdp, CSW_STATUS_FAILED, scsiResidue(&msdp->scsi_target)); + } + } + else { + ; /* do NOT send CSW here. Incorrect CBW must be silently ignored */ + } + } + + chThdExit(MSG_OK); +} + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Mass storage specific request hook for USB. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +bool msd_request_hook(USBDriver *usbp) { + + if (((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) && + ((usbp->setup[0] & USB_RTYPE_RECIPIENT_MASK) == USB_RTYPE_RECIPIENT_INTERFACE)) { + /* check that the request is for interface 0.*/ + if (MSD_SETUP_INDEX(usbp->setup) != 0) + return false; + + /* act depending on bRequest = setup[1] */ + switch(usbp->setup[1]) { + case MSD_REQ_RESET: + /* check that it is a HOST2DEV request */ + if (((usbp->setup[0] & USB_RTYPE_DIR_MASK) != USB_RTYPE_DIR_HOST2DEV) || + (MSD_SETUP_LENGTH(usbp->setup) != 0) || + (MSD_SETUP_VALUE(usbp->setup) != 0)) + return false; + + /* reset all endpoints */ + /* TODO!*/ + /* The device shall NAK the status stage of the device request until + * the Bulk-Only Mass Storage Reset is complete. + */ + return true; + + case MSD_GET_MAX_LUN: + /* check that it is a DEV2HOST request */ + if (((usbp->setup[0] & USB_RTYPE_DIR_MASK) != USB_RTYPE_DIR_DEV2HOST) || + (MSD_SETUP_LENGTH(usbp->setup) != 1) || + (MSD_SETUP_VALUE(usbp->setup) != 0)) + return false; + + /* stall to indicate that we don't support LUN */ + osalSysLockFromISR(); + usbStallTransmitI(usbp, 0); + osalSysUnlockFromISR(); + return true; + + default: + return false; + break; + } + } + return false; +} + +/** + * @brief Initializes the standard part of a @p USBMassStorageDriver structure. + * + * @param[out] msdp pointer to the @p USBMassStorageDriver object + * + * @init + */ +void msdObjectInit(USBMassStorageDriver *msdp) { + + memset(msdp, 0x55, sizeof(USBMassStorageDriver)); + msdp->state = USB_MSD_STOP; + msdp->usbp = NULL; + msdp->worker = NULL; + + scsiObjectInit(&msdp->scsi_target); +} + +/** + * @brief Stops the USB mass storage driver. + * + * @param[in] msdp pointer to the @p USBMassStorageDriver object + * + * @api + */ +void msdStop(USBMassStorageDriver *msdp) { + + osalDbgCheck(msdp != NULL); + osalDbgAssert((msdp->state == USB_MSD_READY), "invalid state"); + + chThdTerminate(msdp->worker); + chThdWait(msdp->worker); + + scsiStop(&msdp->scsi_target); + + msdp->worker = NULL; + msdp->state = USB_MSD_STOP; + msdp->usbp = NULL; +} + +/** + * @brief Configures and activates the USB mass storage driver. + * + * @param[in] msdp pointer to the @p USBMassStorageDriver object + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] blkdev pointer to the @p BaseBlockDevice object + * @param[in] blkbuf pointer to the working area buffer, must be allocated + * by user, must be big enough to store 1 data block + * @param[in] inquiry pointer to the SCSI inquiry response structure, + * set it to @p NULL to use default hardcoded value. + * + * @api + */ +void msdStart(USBMassStorageDriver *msdp, USBDriver *usbp, + BaseBlockDevice *blkdev, uint8_t *blkbuf, + const scsi_inquiry_response_t *inquiry) { + + osalDbgCheck((msdp != NULL) && (usbp != NULL) + && (blkdev != NULL) && (blkbuf != NULL)); + osalDbgAssert((msdp->state == USB_MSD_STOP), "invalid state"); + + msdp->usbp = usbp; + + msdp->usb_scsi_transport_handler.usbp = msdp->usbp; + msdp->usb_scsi_transport_handler.ep = USB_MSD_DATA_EP; + msdp->scsi_transport.handler = &msdp->usb_scsi_transport_handler; + msdp->scsi_transport.transmit = scsi_transport_transmit; + msdp->scsi_transport.receive = scsi_transport_receive; + + if (NULL == inquiry) { + msdp->scsi_config.inquiry_response = &default_scsi_inquiry_response; + } + else { + msdp->scsi_config.inquiry_response = inquiry; + } + msdp->scsi_config.blkbuf = blkbuf; + msdp->scsi_config.blkdev = blkdev; + msdp->scsi_config.transport = &msdp->scsi_transport; + + scsiStart(&msdp->scsi_target, &msdp->scsi_config); + + msdp->state = USB_MSD_READY; + msdp->worker = chThdCreateStatic(msdp->waMSDWorker, sizeof(msdp->waMSDWorker), + MSD_THD_PRIO, usb_msd_worker, msdp); +} + +#endif /* HAL_USE_USB_MSD */ + +/** @} */ |