From 306c921ed1437fc79423958a5e4bd8b0e96bc863 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 13 Oct 2016 14:53:31 -0700 Subject: [PATCH] atmel-samd: Rework mass storage interaction with underlying block storage to use micropython's VFS interface. This makes mass storage work with any VFS implementation rather than a single one. --- atmel-samd/Makefile | 4 +- atmel-samd/{rom_fs.c => access_vfs.c} | 58 +++- atmel-samd/{rom_fs.h => access_vfs.h} | 17 +- atmel-samd/boards/arduino_zero/conf_access.h | 18 +- .../boards/arduino_zero/mpconfigboard.mk | 2 + .../boards/feather_m0_basic/mpconfigboard.mk | 2 + atmel-samd/{storage.c => internal_flash.c} | 118 +++---- atmel-samd/internal_flash.h | 53 ++++ atmel-samd/main.c | 3 +- atmel-samd/modmachine.c | 2 - atmel-samd/mpconfigport.h | 2 + atmel-samd/spi_flash.c | 299 ++++++++++++++++++ atmel-samd/{storage.h => spi_flash.h} | 32 +- extmod/fsusermount.h | 3 + extmod/vfs_fat_diskio.c | 4 +- 15 files changed, 506 insertions(+), 111 deletions(-) rename atmel-samd/{rom_fs.c => access_vfs.c} (73%) rename atmel-samd/{rom_fs.h => access_vfs.h} (76%) rename atmel-samd/{storage.c => internal_flash.c} (57%) create mode 100644 atmel-samd/internal_flash.h create mode 100644 atmel-samd/spi_flash.c rename atmel-samd/{storage.h => spi_flash.h} (62%) diff --git a/atmel-samd/Makefile b/atmel-samd/Makefile index 5d3af6e39a..fbdfa3f93b 100644 --- a/atmel-samd/Makefile +++ b/atmel-samd/Makefile @@ -143,6 +143,7 @@ SRC_ASF = $(addprefix asf/sam0/,\ ) SRC_C = \ + access_vfs.c \ builtin_open.c \ fatfs_port.c \ main.c \ @@ -156,9 +157,8 @@ SRC_C = \ modutime.c \ mphalport.c \ pin_named_pins.c \ - rom_fs.c \ samdneopixel.c \ - storage.c \ + $(FLASH_IMPL) \ asf/common/services/sleepmgr/samd/sleepmgr.c \ asf/common/services/storage/ctrl_access/ctrl_access.c \ asf/common/services/usb/class/cdc/device/udi_cdc.c \ diff --git a/atmel-samd/rom_fs.c b/atmel-samd/access_vfs.c similarity index 73% rename from atmel-samd/rom_fs.c rename to atmel-samd/access_vfs.c index f0b01fa6b7..a3e8aaebcd 100644 --- a/atmel-samd/rom_fs.c +++ b/atmel-samd/access_vfs.c @@ -24,10 +24,16 @@ * THE SOFTWARE. */ -#include "rom_fs.h" +#include "access_vfs.h" #include "asf/common/services/usb/class/msc/device/udi_msc.h" -#include "storage.h" +#include "extmod/fsusermount.h" +#include "lib/fatfs/diskio.h" +#include "py/mpconfig.h" +#include "py/mpstate.h" +#include "py/misc.h" + +#define VFS_INDEX 0 //! This function tests memory state, and starts memory initialization //! @return Ctrl_status @@ -35,9 +41,17 @@ //! Memory unplug -> CTRL_NO_PRESENT //! Not initialized or changed -> CTRL_BUSY //! An error occurred -> CTRL_FAIL -Ctrl_status rom_fs_test_unit_ready(void) +Ctrl_status vfs_test_unit_ready(void) { - return CTRL_GOOD; + if (VFS_INDEX >= MP_ARRAY_SIZE(MP_STATE_PORT(fs_user_mount))) { + return CTRL_FAIL; + } + DSTATUS status = disk_status(VFS_INDEX); + if (status == STA_NOINIT) { + return CTRL_NO_PRESENT; + } + + return CTRL_GOOD; } //! This function returns the address of the last valid sector @@ -47,9 +61,11 @@ Ctrl_status rom_fs_test_unit_ready(void) //! Memory unplug -> CTRL_NO_PRESENT //! Not initialized or changed -> CTRL_BUSY //! An error occurred -> CTRL_FAIL -Ctrl_status rom_fs_read_capacity(uint32_t *uint32_t_nb_sector) +Ctrl_status vfs_read_capacity(uint32_t *uint32_t_nb_sector) { - *uint32_t_nb_sector = storage_get_block_count(); + if (disk_ioctl(VFS_INDEX, GET_SECTOR_COUNT, uint32_t_nb_sector) != RES_OK) { + return CTRL_FAIL; + } return CTRL_GOOD; } @@ -57,20 +73,26 @@ Ctrl_status rom_fs_read_capacity(uint32_t *uint32_t_nb_sector) //! //! @return true if the memory is protected //! -bool rom_fs_wr_protect(void) +bool vfs_wr_protect(void) { - return false; + DSTATUS status = disk_status(VFS_INDEX); + return status == STA_NOINIT || status == STA_PROTECT; } //! This function informs about the memory type //! //! @return true if the memory is removable //! -bool rom_fs_removal(void) +bool vfs_removal(void) { return true; } +bool vfs_unload(bool unload) +{ + return unload; +} + // TODO(tannewt): Transfer more than a single sector at a time if we need more // speed. //! This function transfers the memory data to the USB MSC interface @@ -84,11 +106,17 @@ bool rom_fs_removal(void) //! Not initialized or changed -> CTRL_BUSY //! An error occurred -> CTRL_FAIL //! -Ctrl_status rom_fs_usb_read_10(uint32_t addr, volatile uint16_t nb_sector) +Ctrl_status vfs_usb_read_10(uint32_t addr, volatile uint16_t nb_sector) { uint8_t sector_buffer[FLASH_BLOCK_SIZE]; for (uint16_t sector = 0; sector < nb_sector; sector++) { - storage_read_block(sector_buffer, addr + sector); + DRESULT result = disk_read(VFS_INDEX, sector_buffer, addr + sector, 1); + if (result == RES_PARERR) { + return CTRL_NO_PRESENT; + } + if (result == RES_ERROR) { + return CTRL_FAIL; + } if (!udi_msc_trans_block(true, sector_buffer, FLASH_BLOCK_SIZE, NULL)) { return CTRL_FAIL; // transfer aborted } @@ -108,14 +136,18 @@ Ctrl_status rom_fs_usb_read_10(uint32_t addr, volatile uint16_t nb_sector) //! Not initialized or changed -> CTRL_BUSY //! An error occurred -> CTRL_FAIL //! -Ctrl_status rom_fs_usb_write_10(uint32_t addr, uint16_t nb_sector) +Ctrl_status vfs_usb_write_10(uint32_t addr, uint16_t nb_sector) { uint8_t sector_buffer[FLASH_BLOCK_SIZE]; for (uint16_t sector = 0; sector < nb_sector; sector++) { if (!udi_msc_trans_block(false, sector_buffer, FLASH_BLOCK_SIZE, NULL)) { return CTRL_FAIL; // transfer aborted } - if (!storage_write_block(sector_buffer, addr + sector)) { + DRESULT result = disk_write(VFS_INDEX, sector_buffer, addr + sector, 1); + if (result == RES_PARERR) { + return CTRL_NO_PRESENT; + } + if (result == RES_ERROR) { return CTRL_FAIL; } } diff --git a/atmel-samd/rom_fs.h b/atmel-samd/access_vfs.h similarity index 76% rename from atmel-samd/rom_fs.h rename to atmel-samd/access_vfs.h index 8b1fe2de9e..07af89e229 100644 --- a/atmel-samd/rom_fs.h +++ b/atmel-samd/access_vfs.h @@ -24,17 +24,20 @@ * THE SOFTWARE. */ +// This adapts the ASF access API to MicroPython's VFS API so we can expose all +// VFS block devices as Lun's over USB mass storage control. + #ifndef __MICROPY_INCLUDED_ATMEL_SAMD_ROM_FS_H__ #define __MICROPY_INCLUDED_ATMEL_SAMD_ROM_FS_H__ #include "asf/common/services/storage/ctrl_access/ctrl_access.h" - -Ctrl_status rom_fs_test_unit_ready(void); -Ctrl_status rom_fs_read_capacity(uint32_t *u32_nb_sector); -bool rom_fs_wr_protect(void); -bool rom_fs_removal(void); -Ctrl_status rom_fs_usb_read_10(uint32_t addr, uint16_t nb_sector); -Ctrl_status rom_fs_usb_write_10(uint32_t addr, uint16_t nb_sector); +Ctrl_status vfs_test_unit_ready(void); +Ctrl_status vfs_read_capacity(uint32_t *u32_nb_sector); +bool vfs_wr_protect(void); +bool vfs_removal(void); +bool vfs_unload(bool); +Ctrl_status vfs_usb_read_10(uint32_t addr, uint16_t nb_sector); +Ctrl_status vfs_usb_write_10(uint32_t addr, uint16_t nb_sector); #endif // __MICROPY_INCLUDED_ATMEL_SAMD_ROM_FS_H__ diff --git a/atmel-samd/boards/arduino_zero/conf_access.h b/atmel-samd/boards/arduino_zero/conf_access.h index 8cf104e69c..25109b75ca 100644 --- a/atmel-samd/boards/arduino_zero/conf_access.h +++ b/atmel-samd/boards/arduino_zero/conf_access.h @@ -68,15 +68,15 @@ /*! \name LUN 0 Definitions */ //! @{ -#define LUN_0_INCLUDE "rom_fs.h" -#define Lun_0_test_unit_ready rom_fs_test_unit_ready -#define Lun_0_read_capacity rom_fs_read_capacity -#define Lun_0_unload NULL /* Can not be unloaded */ -#define Lun_0_wr_protect rom_fs_wr_protect -#define Lun_0_removal rom_fs_removal -#define Lun_0_usb_read_10 rom_fs_usb_read_10 -#define Lun_0_usb_write_10 rom_fs_usb_write_10 -#define LUN_0_NAME "\"On-Chip ROM\"" +#define LUN_0_INCLUDE "access_vfs.h" +#define Lun_0_test_unit_ready vfs_test_unit_ready +#define Lun_0_read_capacity vfs_read_capacity +#define Lun_0_unload NULL +#define Lun_0_wr_protect vfs_wr_protect +#define Lun_0_removal vfs_removal +#define Lun_0_usb_read_10 vfs_usb_read_10 +#define Lun_0_usb_write_10 vfs_usb_write_10 +#define LUN_0_NAME "\"MicroPython VFS[0]\"" //! @} #define MEM_USB LUN_USB diff --git a/atmel-samd/boards/arduino_zero/mpconfigboard.mk b/atmel-samd/boards/arduino_zero/mpconfigboard.mk index 1b99569329..848b9fca3a 100644 --- a/atmel-samd/boards/arduino_zero/mpconfigboard.mk +++ b/atmel-samd/boards/arduino_zero/mpconfigboard.mk @@ -1,3 +1,5 @@ LD_FILE = boards/samd21x18-bootloader.ld USB_VID = 0x2341 USB_PID = 0x824D + +FLASH_IMPL = internal_flash.c diff --git a/atmel-samd/boards/feather_m0_basic/mpconfigboard.mk b/atmel-samd/boards/feather_m0_basic/mpconfigboard.mk index d7f5c739bb..2b5c263ace 100644 --- a/atmel-samd/boards/feather_m0_basic/mpconfigboard.mk +++ b/atmel-samd/boards/feather_m0_basic/mpconfigboard.mk @@ -1,3 +1,5 @@ LD_FILE = boards/samd21x18-bootloader.ld USB_VID = 0x239A USB_PID = 0x8015 + +FLASH_IMPL = internal_flash.c diff --git a/atmel-samd/storage.c b/atmel-samd/internal_flash.c similarity index 57% rename from atmel-samd/storage.c rename to atmel-samd/internal_flash.c index a4037da4f6..f7d0aea6c6 100644 --- a/atmel-samd/storage.c +++ b/atmel-samd/internal_flash.c @@ -34,35 +34,35 @@ #include "asf/sam0/drivers/nvm/nvm.h" -#include "storage.h" +#include "internal_flash.h" -#define TOTAL_FLASH_SIZE 0x010000 +#define TOTAL_INTERNAL_FLASH_SIZE 0x010000 -#define FLASH_MEM_SEG1_START_ADDR (0x00040000 - TOTAL_FLASH_SIZE) -#define FLASH_PART1_START_BLOCK (0x100) -#define FLASH_PART1_NUM_BLOCKS (TOTAL_FLASH_SIZE / FLASH_BLOCK_SIZE) +#define INTERNAL_FLASH_MEM_SEG1_START_ADDR (0x00040000 - TOTAL_INTERNAL_FLASH_SIZE) +#define INTERNAL_FLASH_PART1_START_BLOCK (0x100) +#define INTERNAL_FLASH_PART1_NUM_BLOCKS (TOTAL_INTERNAL_FLASH_SIZE / INTERNAL_FLASH_BLOCK_SIZE) -static bool flash_is_initialised = false; +static bool internal_flash_is_initialised = false; -void storage_init(void) { - if (!flash_is_initialised) { +void internal_flash_init(void) { + if (!internal_flash_is_initialised) { struct nvm_config config_nvm; nvm_get_config_defaults(&config_nvm); config_nvm.manual_page_write = false; nvm_set_config(&config_nvm); - flash_is_initialised = true; + internal_flash_is_initialised = true; } } -uint32_t storage_get_block_size(void) { - return FLASH_BLOCK_SIZE; +uint32_t internal_flash_get_block_size(void) { + return INTERNAL_FLASH_BLOCK_SIZE; } -uint32_t storage_get_block_count(void) { - return FLASH_PART1_START_BLOCK + FLASH_PART1_NUM_BLOCKS; +uint32_t internal_flash_get_block_count(void) { + return INTERNAL_FLASH_PART1_START_BLOCK + INTERNAL_FLASH_PART1_NUM_BLOCKS; } -void storage_flush(void) { +void internal_flash_flush(void) { } static void build_partition(uint8_t *buf, int boot, int type, uint32_t start_block, uint32_t num_blocks) { @@ -102,16 +102,16 @@ static void build_partition(uint8_t *buf, int boot, int type, uint32_t start_blo } static uint32_t convert_block_to_flash_addr(uint32_t block) { - if (FLASH_PART1_START_BLOCK <= block && block < FLASH_PART1_START_BLOCK + FLASH_PART1_NUM_BLOCKS) { + if (INTERNAL_FLASH_PART1_START_BLOCK <= block && block < INTERNAL_FLASH_PART1_START_BLOCK + INTERNAL_FLASH_PART1_NUM_BLOCKS) { // a block in partition 1 - block -= FLASH_PART1_START_BLOCK; - return FLASH_MEM_SEG1_START_ADDR + block * FLASH_BLOCK_SIZE; + block -= INTERNAL_FLASH_PART1_START_BLOCK; + return INTERNAL_FLASH_MEM_SEG1_START_ADDR + block * INTERNAL_FLASH_BLOCK_SIZE; } // bad block return -1; } -bool storage_read_block(uint8_t *dest, uint32_t block) { +bool internal_flash_read_block(uint8_t *dest, uint32_t block) { //printf("RD %u\n", block); if (block == 0) { // fake the MBR so we can decide on our own partition table @@ -120,7 +120,7 @@ bool storage_read_block(uint8_t *dest, uint32_t block) { dest[i] = 0; } - build_partition(dest + 446, 0, 0x01 /* FAT12 */, FLASH_PART1_START_BLOCK, FLASH_PART1_NUM_BLOCKS); + build_partition(dest + 446, 0, 0x01 /* FAT12 */, INTERNAL_FLASH_PART1_START_BLOCK, INTERNAL_FLASH_PART1_NUM_BLOCKS); build_partition(dest + 462, 0, 0, 0, 0); build_partition(dest + 478, 0, 0, 0, 0); build_partition(dest + 494, 0, 0, 0, 0); @@ -140,7 +140,7 @@ bool storage_read_block(uint8_t *dest, uint32_t block) { enum status_code error_code; // A block is made up of multiple pages. Read each page // sequentially. - for (int i = 0; i < FLASH_BLOCK_SIZE / NVMCTRL_PAGE_SIZE; i++) { + for (int i = 0; i < INTERNAL_FLASH_BLOCK_SIZE / NVMCTRL_PAGE_SIZE; i++) { do { error_code = nvm_read_buffer(src + i * NVMCTRL_PAGE_SIZE, @@ -152,7 +152,7 @@ bool storage_read_block(uint8_t *dest, uint32_t block) { } } -bool storage_write_block(const uint8_t *src, uint32_t block) { +bool internal_flash_write_block(const uint8_t *src, uint32_t block) { if (block == 0) { // can't write MBR, but pretend we did return true; @@ -184,7 +184,7 @@ bool storage_write_block(const uint8_t *src, uint32_t block) { // A block is made up of multiple pages. Write each page // sequentially. - for (int i = 0; i < FLASH_BLOCK_SIZE / NVMCTRL_PAGE_SIZE; i++) { + for (int i = 0; i < INTERNAL_FLASH_BLOCK_SIZE / NVMCTRL_PAGE_SIZE; i++) { do { error_code = nvm_write_buffer(dest + i * NVMCTRL_PAGE_SIZE, @@ -199,18 +199,18 @@ bool storage_write_block(const uint8_t *src, uint32_t block) { } } -mp_uint_t storage_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) { +mp_uint_t internal_flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) { for (size_t i = 0; i < num_blocks; i++) { - if (!storage_read_block(dest + i * FLASH_BLOCK_SIZE, block_num + i)) { + if (!internal_flash_read_block(dest + i * INTERNAL_FLASH_BLOCK_SIZE, block_num + i)) { return 1; // error } } return 0; // success } -mp_uint_t storage_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks) { +mp_uint_t internal_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks) { for (size_t i = 0; i < num_blocks; i++) { - if (!storage_write_block(src + i * FLASH_BLOCK_SIZE, block_num + i)) { + if (!internal_flash_write_block(src + i * INTERNAL_FLASH_BLOCK_SIZE, block_num + i)) { return 1; // error } } @@ -223,68 +223,68 @@ mp_uint_t storage_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t // Expose the flash as an object with the block protocol. // there is a singleton Flash object -STATIC const mp_obj_base_t flash_obj = {&flash_type}; +STATIC const mp_obj_base_t internal_flash_obj = {&internal_flash_type}; -STATIC mp_obj_t flash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { +STATIC mp_obj_t internal_flash_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { // check arguments mp_arg_check_num(n_args, n_kw, 0, 0, false); // return singleton object - return (mp_obj_t)&flash_obj; + return (mp_obj_t)&internal_flash_obj; } -STATIC mp_obj_t flash_readblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) { +STATIC mp_obj_t internal_flash_obj_readblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_WRITE); - mp_uint_t ret = storage_read_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FLASH_BLOCK_SIZE); + mp_uint_t ret = internal_flash_read_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FLASH_BLOCK_SIZE); return MP_OBJ_NEW_SMALL_INT(ret); } -STATIC MP_DEFINE_CONST_FUN_OBJ_3(flash_readblocks_obj, flash_readblocks); +STATIC MP_DEFINE_CONST_FUN_OBJ_3(internal_flash_obj_readblocks_obj, internal_flash_obj_readblocks); -STATIC mp_obj_t flash_writeblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) { +STATIC mp_obj_t internal_flash_obj_writeblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ); - mp_uint_t ret = storage_write_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FLASH_BLOCK_SIZE); + mp_uint_t ret = internal_flash_write_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FLASH_BLOCK_SIZE); return MP_OBJ_NEW_SMALL_INT(ret); } -STATIC MP_DEFINE_CONST_FUN_OBJ_3(flash_writeblocks_obj, flash_writeblocks); +STATIC MP_DEFINE_CONST_FUN_OBJ_3(internal_flash_obj_writeblocks_obj, internal_flash_obj_writeblocks); -STATIC mp_obj_t flash_ioctl(mp_obj_t self, mp_obj_t cmd_in, mp_obj_t arg_in) { +STATIC mp_obj_t internal_flash_obj_ioctl(mp_obj_t self, mp_obj_t cmd_in, mp_obj_t arg_in) { mp_int_t cmd = mp_obj_get_int(cmd_in); switch (cmd) { - case BP_IOCTL_INIT: storage_init(); return MP_OBJ_NEW_SMALL_INT(0); - case BP_IOCTL_DEINIT: storage_flush(); return MP_OBJ_NEW_SMALL_INT(0); // TODO properly - case BP_IOCTL_SYNC: storage_flush(); return MP_OBJ_NEW_SMALL_INT(0); - case BP_IOCTL_SEC_COUNT: return MP_OBJ_NEW_SMALL_INT(storage_get_block_count()); - case BP_IOCTL_SEC_SIZE: return MP_OBJ_NEW_SMALL_INT(storage_get_block_size()); + case BP_IOCTL_INIT: internal_flash_init(); return MP_OBJ_NEW_SMALL_INT(0); + case BP_IOCTL_DEINIT: internal_flash_flush(); return MP_OBJ_NEW_SMALL_INT(0); // TODO properly + case BP_IOCTL_SYNC: internal_flash_flush(); return MP_OBJ_NEW_SMALL_INT(0); + case BP_IOCTL_SEC_COUNT: return MP_OBJ_NEW_SMALL_INT(internal_flash_get_block_count()); + case BP_IOCTL_SEC_SIZE: return MP_OBJ_NEW_SMALL_INT(internal_flash_get_block_size()); default: return mp_const_none; } } -STATIC MP_DEFINE_CONST_FUN_OBJ_3(flash_ioctl_obj, flash_ioctl); +STATIC MP_DEFINE_CONST_FUN_OBJ_3(internal_flash_obj_ioctl_obj, internal_flash_obj_ioctl); -STATIC const mp_map_elem_t flash_locals_dict_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR_readblocks), (mp_obj_t)&flash_readblocks_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_writeblocks), (mp_obj_t)&flash_writeblocks_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_ioctl), (mp_obj_t)&flash_ioctl_obj }, +STATIC const mp_map_elem_t internal_flash_obj_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR_readblocks), (mp_obj_t)&internal_flash_obj_readblocks_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_writeblocks), (mp_obj_t)&internal_flash_obj_writeblocks_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ioctl), (mp_obj_t)&internal_flash_obj_ioctl_obj }, }; -STATIC MP_DEFINE_CONST_DICT(flash_locals_dict, flash_locals_dict_table); +STATIC MP_DEFINE_CONST_DICT(internal_flash_obj_locals_dict, internal_flash_obj_locals_dict_table); -const mp_obj_type_t flash_type = { +const mp_obj_type_t internal_flash_type = { { &mp_type_type }, - .name = MP_QSTR_Flash, - .make_new = flash_make_new, - .locals_dict = (mp_obj_t)&flash_locals_dict, + .name = MP_QSTR_InternalFlash, + .make_new = internal_flash_obj_make_new, + .locals_dict = (mp_obj_t)&internal_flash_obj_locals_dict, }; void flash_init_vfs(fs_user_mount_t *vfs) { vfs->flags |= FSUSER_NATIVE | FSUSER_HAVE_IOCTL; - vfs->readblocks[0] = (mp_obj_t)&flash_readblocks_obj; - vfs->readblocks[1] = (mp_obj_t)&flash_obj; - vfs->readblocks[2] = (mp_obj_t)storage_read_blocks; // native version - vfs->writeblocks[0] = (mp_obj_t)&flash_writeblocks_obj; - vfs->writeblocks[1] = (mp_obj_t)&flash_obj; - vfs->writeblocks[2] = (mp_obj_t)storage_write_blocks; // native version - vfs->u.ioctl[0] = (mp_obj_t)&flash_ioctl_obj; - vfs->u.ioctl[1] = (mp_obj_t)&flash_obj; + vfs->readblocks[0] = (mp_obj_t)&internal_flash_obj_readblocks_obj; + vfs->readblocks[1] = (mp_obj_t)&internal_flash_obj; + vfs->readblocks[2] = (mp_obj_t)internal_flash_read_blocks; // native version + vfs->writeblocks[0] = (mp_obj_t)&internal_flash_obj_writeblocks_obj; + vfs->writeblocks[1] = (mp_obj_t)&internal_flash_obj; + vfs->writeblocks[2] = (mp_obj_t)internal_flash_write_blocks; // native version + vfs->u.ioctl[0] = (mp_obj_t)&internal_flash_obj_ioctl_obj; + vfs->u.ioctl[1] = (mp_obj_t)&internal_flash_obj; } diff --git a/atmel-samd/internal_flash.h b/atmel-samd/internal_flash.h new file mode 100644 index 0000000000..38886c9304 --- /dev/null +++ b/atmel-samd/internal_flash.h @@ -0,0 +1,53 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 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. + */ +#ifndef __MICROPY_INCLUDED_ATMEL_SAMD_INTERNAL_FLASH_H__ +#define __MICROPY_INCLUDED_ATMEL_SAMD_INTERNAL_FLASH_H__ + +#include "mpconfigport.h" + +#define INTERNAL_FLASH_BLOCK_SIZE (512) + +#define INTERNAL_FLASH_SYSTICK_MASK (0x1ff) // 512ms +#define INTERNAL_FLASH_IDLE_TICK(tick) (((tick) & INTERNAL_FLASH_SYSTICK_MASK) == 2) + +void internal_flash_init(void); +uint32_t internal_flash_get_block_size(void); +uint32_t internal_flash_get_block_count(void); +void internal_flash_irq_handler(void); +void internal_flash_flush(void); +bool internal_flash_read_block(uint8_t *dest, uint32_t block); +bool internal_flash_write_block(const uint8_t *src, uint32_t block); + +// these return 0 on success, non-zero on error +mp_uint_t internal_flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks); +mp_uint_t internal_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks); + +extern const struct _mp_obj_type_t internal_flash_type; + +struct _fs_user_mount_t; +void flash_init_vfs(struct _fs_user_mount_t *vfs); + +#endif // __MICROPY_INCLUDED_ATMEL_SAMD_INTERNAL_FLASH_H__ diff --git a/atmel-samd/main.c b/atmel-samd/main.c index 77a0f53fd8..6d781e7964 100644 --- a/atmel-samd/main.c +++ b/atmel-samd/main.c @@ -23,7 +23,6 @@ #include "mpconfigboard.h" #include "modmachine_pin.h" -#include "storage.h" fs_user_mount_t fs_user_mount_flash; @@ -72,6 +71,8 @@ static const char fresh_readme_txt[] = "Please visit http://micropython.org/help/ for further help.\r\n" ; +extern void flash_init_vfs(fs_user_mount_t *vfs); + // we don't make this function static because it needs a lot of stack and we // want it to be executed without using stack within main() function void init_flash_fs() { diff --git a/atmel-samd/modmachine.c b/atmel-samd/modmachine.c index 67bccc5fdc..791e09ada6 100644 --- a/atmel-samd/modmachine.c +++ b/atmel-samd/modmachine.c @@ -37,7 +37,6 @@ #include "modmachine_dac.h" #include "modmachine_pin.h" #include "modmachine_pwm.h" -#include "storage.h" #if MICROPY_PY_MACHINE // TODO(tannewt): Add the machine_ prefix to all types so that we don't risk @@ -49,7 +48,6 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_i2c_type) }, { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&pin_type) }, { MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&pwm_type) }, - { MP_ROM_QSTR(MP_QSTR_Flash), MP_ROM_PTR(&flash_type) }, { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_spi_type) }, }; diff --git a/atmel-samd/mpconfigport.h b/atmel-samd/mpconfigport.h index 0ebce19583..e38f3fb572 100644 --- a/atmel-samd/mpconfigport.h +++ b/atmel-samd/mpconfigport.h @@ -72,6 +72,8 @@ #define MICROPY_FSUSERMOUNT (1) #define MICROPY_FATFS_MAX_SS (4096) +#define FLASH_BLOCK_SIZE (512) + #define MICROPY_VFS_FAT (1) #define MICROPY_PY_MACHINE (1) #define MICROPY_MODULE_WEAK_LINKS (1) diff --git a/atmel-samd/spi_flash.c b/atmel-samd/spi_flash.c new file mode 100644 index 0000000000..73717ffca5 --- /dev/null +++ b/atmel-samd/spi_flash.c @@ -0,0 +1,299 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 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 +#include + +#include "asf/sam0/drivers/sercom/spi/spi.h" + +#include "py/obj.h" +#include "py/runtime.h" +#include "lib/fatfs/ff.h" +#include "extmod/fsusermount.h" + +#include "asf/sam0/drivers/nvm/nvm.h" + +#include "spi_flash.h" + +#define TOTAL_SPI_FLASH_SIZE 0x010000 + +#define SPI_FLASH_MEM_SEG1_START_ADDR (0x00040000 - TOTAL_SPI_FLASH_SIZE) +#define SPI_FLASH_PART1_START_BLOCK (0x100) +#define SPI_FLASH_PART1_NUM_BLOCKS (TOTAL_SPI_FLASH_SIZE / SPI_FLASH_BLOCK_SIZE) + +static bool spi_flash_is_initialised = false; + +struct spi_module spi_flash_instance; + +void spi_flash_init(void) { + if (!spi_flash_is_initialised) { + struct spi_config config_spi_master; + spi_get_config_defaults(&config_spi_master); + config_spi_master.mux_setting = SPI_FLASH_MUX_SETTING; + config_spi_master.pinmux_pad0 = SPI_FLASH_PAD0_PINMUX; + config_spi_master.pinmux_pad1 = SPI_FLASH_PAD1_PINMUX; + config_spi_master.pinmux_pad2 = SPI_FLASH_PAD2_PINMUX; + config_spi_master.pinmux_pad3 = SPI_FLASH_PAD3_PINMUX; + config_spi_master.mode_specific.master.baudrate = SPI_FLASH_BAUDRATE; + spi_init(&spi_flash_instance, SPI_FLASH_SERCOM, &config_spi_master); + spi_enable(&spi_flash_instance); + } +} + +uint32_t spi_flash_get_block_size(void) { + return SPI_FLASH_BLOCK_SIZE; +} + +uint32_t spi_flash_get_block_count(void) { + return SPI_FLASH_PART1_START_BLOCK + SPI_FLASH_PART1_NUM_BLOCKS; +} + +void spi_flash_flush(void) { +} + +static void build_partition(uint8_t *buf, int boot, int type, uint32_t start_block, uint32_t num_blocks) { + buf[0] = boot; + + if (num_blocks == 0) { + buf[1] = 0; + buf[2] = 0; + buf[3] = 0; + } else { + buf[1] = 0xff; + buf[2] = 0xff; + buf[3] = 0xff; + } + + buf[4] = type; + + if (num_blocks == 0) { + buf[5] = 0; + buf[6] = 0; + buf[7] = 0; + } else { + buf[5] = 0xff; + buf[6] = 0xff; + buf[7] = 0xff; + } + + buf[8] = start_block; + buf[9] = start_block >> 8; + buf[10] = start_block >> 16; + buf[11] = start_block >> 24; + + buf[12] = num_blocks; + buf[13] = num_blocks >> 8; + buf[14] = num_blocks >> 16; + buf[15] = num_blocks >> 24; +} + +static uint32_t convert_block_to_flash_addr(uint32_t block) { + if (SPI_FLASH_PART1_START_BLOCK <= block && block < SPI_FLASH_PART1_START_BLOCK + SPI_FLASH_PART1_NUM_BLOCKS) { + // a block in partition 1 + block -= SPI_FLASH_PART1_START_BLOCK; + return SPI_FLASH_MEM_SEG1_START_ADDR + block * SPI_FLASH_BLOCK_SIZE; + } + // bad block + return -1; +} + +bool spi_flash_read_block(uint8_t *dest, uint32_t block) { + //printf("RD %u\n", block); + if (block == 0) { + // fake the MBR so we can decide on our own partition table + + for (int i = 0; i < 446; i++) { + dest[i] = 0; + } + + build_partition(dest + 446, 0, 0x01 /* FAT12 */, SPI_FLASH_PART1_START_BLOCK, SPI_FLASH_PART1_NUM_BLOCKS); + build_partition(dest + 462, 0, 0, 0, 0); + build_partition(dest + 478, 0, 0, 0, 0); + build_partition(dest + 494, 0, 0, 0, 0); + + dest[510] = 0x55; + dest[511] = 0xaa; + + return true; + + } else { + // non-MBR block, get data from flash memory + uint32_t src = convert_block_to_flash_addr(block); + if (src == -1) { + // bad block number + return false; + } + enum status_code error_code; + // A block is made up of multiple pages. Read each page + // sequentially. + for (int i = 0; i < SPI_FLASH_BLOCK_SIZE / NVMCTRL_PAGE_SIZE; i++) { + do + { + error_code = nvm_read_buffer(src + i * NVMCTRL_PAGE_SIZE, + dest + i * NVMCTRL_PAGE_SIZE, + NVMCTRL_PAGE_SIZE); + } while (error_code == STATUS_BUSY); + } + return true; + } +} + +bool spi_flash_write_block(const uint8_t *src, uint32_t block) { + if (block == 0) { + // can't write MBR, but pretend we did + return true; + + } else { + // non-MBR block, copy to cache + volatile uint32_t dest = convert_block_to_flash_addr(block); + if (dest == -1) { + // bad block number + return false; + } + enum status_code error_code; + // A block is formed by two rows of flash. We must erase each row + // before we write back to it. + do + { + error_code = nvm_erase_row(dest); + } while (error_code == STATUS_BUSY); + if (error_code != STATUS_OK) { + return false; + } + do + { + error_code = nvm_erase_row(dest + NVMCTRL_ROW_SIZE); + } while (error_code == STATUS_BUSY); + if (error_code != STATUS_OK) { + return false; + } + + // A block is made up of multiple pages. Write each page + // sequentially. + for (int i = 0; i < SPI_FLASH_BLOCK_SIZE / NVMCTRL_PAGE_SIZE; i++) { + do + { + error_code = nvm_write_buffer(dest + i * NVMCTRL_PAGE_SIZE, + src + i * NVMCTRL_PAGE_SIZE, + NVMCTRL_PAGE_SIZE); + } while (error_code == STATUS_BUSY); + if (error_code != STATUS_OK) { + return false; + } + } + return true; + } +} + +mp_uint_t spi_flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) { + for (size_t i = 0; i < num_blocks; i++) { + if (!spi_flash_read_block(dest + i * SPI_FLASH_BLOCK_SIZE, block_num + i)) { + return 1; // error + } + } + return 0; // success +} + +mp_uint_t spi_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks) { + for (size_t i = 0; i < num_blocks; i++) { + if (!spi_flash_write_block(src + i * SPI_FLASH_BLOCK_SIZE, block_num + i)) { + return 1; // error + } + } + return 0; // success +} + +/******************************************************************************/ +// MicroPython bindings +// +// Expose the flash as an object with the block protocol. + +// there is a singleton Flash object +STATIC const mp_obj_base_t spi_flash_obj = {&spi_flash_type}; + +STATIC mp_obj_t spi_flash_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + // check arguments + mp_arg_check_num(n_args, n_kw, 0, 0, false); + + // return singleton object + return (mp_obj_t)&spi_flash_obj; +} + +STATIC mp_obj_t spi_flash_obj_readblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_WRITE); + mp_uint_t ret = spi_flash_read_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FLASH_BLOCK_SIZE); + return MP_OBJ_NEW_SMALL_INT(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(spi_flash_obj_readblocks_obj, spi_flash_obj_readblocks); + +STATIC mp_obj_t spi_flash_obj_writeblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ); + mp_uint_t ret = spi_flash_write_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FLASH_BLOCK_SIZE); + return MP_OBJ_NEW_SMALL_INT(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(spi_flash_obj_writeblocks_obj, spi_flash_obj_writeblocks); + +STATIC mp_obj_t spi_flash_obj_ioctl(mp_obj_t self, mp_obj_t cmd_in, mp_obj_t arg_in) { + mp_int_t cmd = mp_obj_get_int(cmd_in); + switch (cmd) { + case BP_IOCTL_INIT: spi_flash_init(); return MP_OBJ_NEW_SMALL_INT(0); + case BP_IOCTL_DEINIT: spi_flash_flush(); return MP_OBJ_NEW_SMALL_INT(0); // TODO properly + case BP_IOCTL_SYNC: spi_flash_flush(); return MP_OBJ_NEW_SMALL_INT(0); + case BP_IOCTL_SEC_COUNT: return MP_OBJ_NEW_SMALL_INT(spi_flash_get_block_count()); + case BP_IOCTL_SEC_SIZE: return MP_OBJ_NEW_SMALL_INT(spi_flash_get_block_size()); + default: return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(spi_flash_obj_ioctl_obj, spi_flash_obj_ioctl); + +STATIC const mp_map_elem_t spi_flash_obj_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR_readblocks), (mp_obj_t)&spi_flash_obj_readblocks_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_writeblocks), (mp_obj_t)&spi_flash_obj_writeblocks_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ioctl), (mp_obj_t)&spi_flash_obj_ioctl_obj }, +}; + +STATIC MP_DEFINE_CONST_DICT(spi_flash_obj_locals_dict, spi_flash_obj_locals_dict_table); + +const mp_obj_type_t spi_flash_type = { + { &mp_type_type }, + .name = MP_QSTR_SPIFlash, + .make_new = spi_flash_obj_make_new, + .locals_dict = (mp_obj_t)&spi_flash_obj_locals_dict, +}; + +void flash_init_vfs(fs_user_mount_t *vfs) { + vfs->flags |= FSUSER_NATIVE | FSUSER_HAVE_IOCTL; + vfs->readblocks[0] = (mp_obj_t)&spi_flash_obj_readblocks_obj; + vfs->readblocks[1] = (mp_obj_t)&spi_flash_obj; + vfs->readblocks[2] = (mp_obj_t)spi_flash_read_blocks; // native version + vfs->writeblocks[0] = (mp_obj_t)&spi_flash_obj_writeblocks_obj; + vfs->writeblocks[1] = (mp_obj_t)&spi_flash_obj; + vfs->writeblocks[2] = (mp_obj_t)spi_flash_write_blocks; // native version + vfs->u.ioctl[0] = (mp_obj_t)&spi_flash_obj_ioctl_obj; + vfs->u.ioctl[1] = (mp_obj_t)&spi_flash_obj; +} diff --git a/atmel-samd/storage.h b/atmel-samd/spi_flash.h similarity index 62% rename from atmel-samd/storage.h rename to atmel-samd/spi_flash.h index 8b814c34e1..1a8ac0c8e7 100644 --- a/atmel-samd/storage.h +++ b/atmel-samd/spi_flash.h @@ -23,31 +23,31 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#ifndef __MICROPY_INCLUDED_ATMEL_SAMD_STORAGE_H__ -#define __MICROPY_INCLUDED_ATMEL_SAMD_STORAGE_H__ +#ifndef __MICROPY_INCLUDED_ATMEL_SAMD_SPI_FLASH_H__ +#define __MICROPY_INCLUDED_ATMEL_SAMD_SPI_FLASH_H__ #include "mpconfigport.h" -#define FLASH_BLOCK_SIZE (512) +#define SPI_FLASH_BLOCK_SIZE (512) -#define STORAGE_SYSTICK_MASK (0x1ff) // 512ms -#define STORAGE_IDLE_TICK(tick) (((tick) & STORAGE_SYSTICK_MASK) == 2) +#define SPI_FLASH_SYSTICK_MASK (0x1ff) // 512ms +#define SPI_FLASH_IDLE_TICK(tick) (((tick) & SPI_FLASH_SYSTICK_MASK) == 2) -void storage_init(void); -uint32_t storage_get_block_size(void); -uint32_t storage_get_block_count(void); -void storage_irq_handler(void); -void storage_flush(void); -bool storage_read_block(uint8_t *dest, uint32_t block); -bool storage_write_block(const uint8_t *src, uint32_t block); +void spi_flash_init(void); +uint32_t spi_flash_get_block_size(void); +uint32_t spi_flash_get_block_count(void); +void spi_flash_irq_handler(void); +void spi_flash_flush(void); +bool spi_flash_read_block(uint8_t *dest, uint32_t block); +bool spi_flash_write_block(const uint8_t *src, uint32_t block); // these return 0 on success, non-zero on error -mp_uint_t storage_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks); -mp_uint_t storage_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks); +mp_uint_t spi_flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks); +mp_uint_t spi_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks); -extern const struct _mp_obj_type_t flash_type; +extern const struct _mp_obj_type_t spi_flash_type; struct _fs_user_mount_t; void flash_init_vfs(struct _fs_user_mount_t *vfs); -#endif // __MICROPY_INCLUDED_ATMEL_SAMD_STORAGE_H__ +#endif // __MICROPY_INCLUDED_ATMEL_SAMD_SPI_FLASH_H__ diff --git a/extmod/fsusermount.h b/extmod/fsusermount.h index e1f26f2ce8..3eb54f8bd4 100644 --- a/extmod/fsusermount.h +++ b/extmod/fsusermount.h @@ -24,6 +24,9 @@ * THE SOFTWARE. */ +#include "lib/fatfs/ff.h" +#include "py/obj.h" + // these are the values for fs_user_mount_t.flags #define FSUSER_NATIVE (0x0001) // readblocks[2]/writeblocks[2] contain native func #define FSUSER_FREE_OBJ (0x0002) // fs_user_mount_t obj should be freed on umount diff --git a/extmod/vfs_fat_diskio.c b/extmod/vfs_fat_diskio.c index 3f1902b2e0..5608e06453 100644 --- a/extmod/vfs_fat_diskio.c +++ b/extmod/vfs_fat_diskio.c @@ -90,7 +90,7 @@ DSTATUS disk_initialize ( /*-----------------------------------------------------------------------*/ DSTATUS disk_status ( - BYTE pdrv /* Physical drive nmuber (0..) */ + BYTE pdrv /* Physical drive number (0..) */ ) { fs_user_mount_t *vfs = disk_get_device(pdrv); @@ -110,7 +110,7 @@ DSTATUS disk_status ( /*-----------------------------------------------------------------------*/ DRESULT disk_read ( - BYTE pdrv, /* Physical drive nmuber (0..) */ + BYTE pdrv, /* Physical drive number (0..) */ BYTE *buff, /* Data buffer to store read data */ DWORD sector, /* Sector address (LBA) */ UINT count /* Number of sectors to read (1..128) */