diff options
Diffstat (limited to 'ports/stm32/usbd_msc_interface.c')
| -rw-r--r-- | ports/stm32/usbd_msc_interface.c | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/ports/stm32/usbd_msc_interface.c b/ports/stm32/usbd_msc_interface.c new file mode 100644 index 000000000..493dece1f --- /dev/null +++ b/ports/stm32/usbd_msc_interface.c @@ -0,0 +1,263 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <stdint.h> + +#include "usbd_cdc_msc_hid.h" +#include "usbd_msc_interface.h" + +#include "extmod/vfs.h" +#include "storage.h" +#include "sdcard.h" + +#define USBD_MSC_MAX_LUN (2) + +// This flag is needed to support removal of the medium, so that the USB drive +// can be unmounted and won't be remounted automatically. +#define FLAGS_STARTED (0x01) + +#define FLAGS_READONLY (0x02) + +STATIC const void *usbd_msc_lu_data[USBD_MSC_MAX_LUN]; +STATIC uint8_t usbd_msc_lu_num; +STATIC uint16_t usbd_msc_lu_flags; + +static inline void lu_flag_set(uint8_t lun, uint8_t flag) { + usbd_msc_lu_flags |= flag << (lun * 2); +} + +static inline void lu_flag_clr(uint8_t lun, uint8_t flag) { + usbd_msc_lu_flags &= ~(flag << (lun * 2)); +} + +static inline bool lu_flag_is_set(uint8_t lun, uint8_t flag) { + return usbd_msc_lu_flags & (flag << (lun * 2)); +} + +STATIC const int8_t usbd_msc_inquiry_data[36] = { + 0x00, // peripheral qualifier; peripheral device type + 0x80, // 0x00 for a fixed drive, 0x80 for a removable drive + 0x02, // version + 0x02, // response data format + (STANDARD_INQUIRY_DATA_LEN - 5), // additional length + 0x00, // various flags + 0x00, // various flags + 0x00, // various flags + 'M', 'i', 'c', 'r', 'o', 'P', 'y', ' ', // Manufacturer : 8 bytes + 'p', 'y', 'b', 'o', 'a', 'r', 'd', ' ', // Product : 16 Bytes + 'F', 'l', 'a', 's', 'h', ' ', ' ', ' ', + '1', '.', '0' ,'0', // Version : 4 Bytes +}; + +// Set the logical units that will be exposed over MSC +void usbd_msc_init_lu(size_t lu_n, const void *lu_data) { + usbd_msc_lu_num = MIN(lu_n, USBD_MSC_MAX_LUN); + memcpy(usbd_msc_lu_data, lu_data, sizeof(void*) * usbd_msc_lu_num); + usbd_msc_lu_flags = 0; +} + +// Helper function to perform an ioctl on a logical unit +STATIC int lu_ioctl(uint8_t lun, int op, uint32_t *data) { + if (lun >= usbd_msc_lu_num) { + return -1; + } + const void *lu = usbd_msc_lu_data[lun]; + + if (lu == &pyb_flash_type) { + switch (op) { + case BP_IOCTL_INIT: + storage_init(); + *data = 0; + return 0; + case BP_IOCTL_SYNC: + storage_flush(); + return 0; + case BP_IOCTL_SEC_SIZE: + *data = storage_get_block_size(); + return 0; + case BP_IOCTL_SEC_COUNT: + *data = storage_get_block_count(); + return 0; + default: + return -1; + } + } else if (lu == &pyb_sdcard_type + #if MICROPY_HW_ENABLE_MMCARD + || lu == &pyb_mmcard_type + #endif + ) { + switch (op) { + case BP_IOCTL_INIT: + if (!sdcard_power_on()) { + return -1; + } + *data = 0; + return 0; + case BP_IOCTL_SYNC: + return 0; + case BP_IOCTL_SEC_SIZE: + *data = SDCARD_BLOCK_SIZE; + return 0; + case BP_IOCTL_SEC_COUNT: + *data = sdcard_get_capacity_in_bytes() / (uint64_t)SDCARD_BLOCK_SIZE; + return 0; + default: + return -1; + } + } else { + return -1; + } +} + +// Initialise all logical units (it's only ever called once, with lun_in=0) +STATIC int8_t usbd_msc_Init(uint8_t lun_in) { + if (lun_in != 0) { + return 0; + } + for (int lun = 0; lun < usbd_msc_lu_num; ++lun) { + uint32_t data = 0; + int res = lu_ioctl(lun, BP_IOCTL_INIT, &data); + if (res != 0) { + lu_flag_clr(lun, FLAGS_STARTED); + } else { + lu_flag_set(lun, FLAGS_STARTED); + if (data) { + lu_flag_set(lun, FLAGS_READONLY); + } + } + } + return 0; +} + +// Get storage capacity of a logical unit +STATIC int8_t usbd_msc_GetCapacity(uint8_t lun, uint32_t *block_num, uint16_t *block_size) { + uint32_t block_size_u32 = 0; + int res = lu_ioctl(lun, BP_IOCTL_SEC_SIZE, &block_size_u32); + if (res != 0) { + return -1; + } + *block_size = block_size_u32; + return lu_ioctl(lun, BP_IOCTL_SEC_COUNT, block_num); +} + +// Check if a logical unit is ready +STATIC int8_t usbd_msc_IsReady(uint8_t lun) { + if (lun >= usbd_msc_lu_num) { + return -1; + } + return lu_flag_is_set(lun, FLAGS_STARTED) ? 0 : -1; +} + +// Check if a logical unit is write protected +STATIC int8_t usbd_msc_IsWriteProtected(uint8_t lun) { + if (lun >= usbd_msc_lu_num) { + return -1; + } + return lu_flag_is_set(lun, FLAGS_READONLY) ? 1 : 0; +} + +// Start or stop a logical unit +STATIC int8_t usbd_msc_StartStopUnit(uint8_t lun, uint8_t started) { + if (lun >= usbd_msc_lu_num) { + return -1; + } + if (started) { + lu_flag_set(lun, FLAGS_STARTED); + } else { + lu_flag_clr(lun, FLAGS_STARTED); + } + return 0; +} + +// Prepare a logical unit for possible removal +STATIC int8_t usbd_msc_PreventAllowMediumRemoval(uint8_t lun, uint8_t param) { + uint32_t dummy; + // Sync the logical unit so the device can be unplugged/turned off + return lu_ioctl(lun, BP_IOCTL_SYNC, &dummy); +} + +// Read data from a logical unit +STATIC int8_t usbd_msc_Read(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) { + if (lun >= usbd_msc_lu_num) { + return -1; + } + const void *lu = usbd_msc_lu_data[lun]; + + if (lu == &pyb_flash_type) { + storage_read_blocks(buf, blk_addr, blk_len); + return 0; + } else if (lu == &pyb_sdcard_type + #if MICROPY_HW_ENABLE_MMCARD + || lu == &pyb_mmcard_type + #endif + ) { + if (sdcard_read_blocks(buf, blk_addr, blk_len) == 0) { + return 0; + } + } + return -1; +} + +// Write data to a logical unit +STATIC int8_t usbd_msc_Write(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) { + if (lun >= usbd_msc_lu_num) { + return -1; + } + const void *lu = usbd_msc_lu_data[lun]; + + if (lu == &pyb_flash_type) { + storage_write_blocks(buf, blk_addr, blk_len); + return 0; + } else if (lu == &pyb_sdcard_type + #if MICROPY_HW_ENABLE_MMCARD + || lu == &pyb_mmcard_type + #endif + ) { + if (sdcard_write_blocks(buf, blk_addr, blk_len) == 0) { + return 0; + } + } + return -1; +} + +// Get the number of attached logical units +STATIC int8_t usbd_msc_GetMaxLun(void) { + return usbd_msc_lu_num - 1; +} + +// Table of operations for the SCSI layer to call +const USBD_StorageTypeDef usbd_msc_fops = { + usbd_msc_Init, + usbd_msc_GetCapacity, + usbd_msc_IsReady, + usbd_msc_IsWriteProtected, + usbd_msc_StartStopUnit, + usbd_msc_PreventAllowMediumRemoval, + usbd_msc_Read, + usbd_msc_Write, + usbd_msc_GetMaxLun, + (int8_t *)usbd_msc_inquiry_data, +}; |
