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.
This commit is contained in:
Scott Shawcroft 2016-10-13 14:53:31 -07:00
parent 6fe8c7b32c
commit 306c921ed1
15 changed files with 506 additions and 111 deletions

View File

@ -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 \

View File

@ -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;
}
}

View File

@ -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__

View File

@ -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

View File

@ -1,3 +1,5 @@
LD_FILE = boards/samd21x18-bootloader.ld
USB_VID = 0x2341
USB_PID = 0x824D
FLASH_IMPL = internal_flash.c

View File

@ -1,3 +1,5 @@
LD_FILE = boards/samd21x18-bootloader.ld
USB_VID = 0x239A
USB_PID = 0x8015
FLASH_IMPL = internal_flash.c

View File

@ -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;
}

View File

@ -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__

View File

@ -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() {

View File

@ -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) },
};

View File

@ -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)

299
atmel-samd/spi_flash.c Normal file
View File

@ -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 <stdint.h>
#include <string.h>
#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;
}

View File

@ -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__

View File

@ -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

View File

@ -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) */