diff options
| author | Scott Shawcroft <scott@tannewt.org> | 2018-11-30 14:40:01 -0800 |
|---|---|---|
| committer | Scott Shawcroft <scott@tannewt.org> | 2018-11-30 14:40:01 -0800 |
| commit | 95e03092630bbad9949c5fe32cd51e7c29bc065b (patch) | |
| tree | 484cb7535142eaabe873bc10552bbfa1b48ab43b /supervisor | |
| parent | 754d6afd163ae5f295444166863b5d11afd1b2e4 (diff) | |
| parent | ab94344baee1a5d73d3890de16319cab1e2616de (diff) | |
Merge remote-tracking branch 'adafruit/master' into mini_sam
Diffstat (limited to 'supervisor')
27 files changed, 2627 insertions, 7 deletions
diff --git a/supervisor/flash.h b/supervisor/flash.h new file mode 100644 index 000000000..ae6a44fd0 --- /dev/null +++ b/supervisor/flash.h @@ -0,0 +1,54 @@ +/* + * This file is part of the MicroPython 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_SUPERVISOR_FLASH_H +#define MICROPY_INCLUDED_SUPERVISOR_FLASH_H + +#include <stdint.h> +#include <stdbool.h> + +#include "py/mpconfig.h" + +#ifdef EXTERNAL_FLASH_DEVICE_COUNT +#include "supervisor/shared/external_flash/external_flash.h" +#else +#include "supervisor/internal_flash.h" +#endif + +void supervisor_flash_set_usb_writable(bool usb_writable); +void supervisor_flash_init(void); +uint32_t supervisor_flash_get_block_size(void); +uint32_t supervisor_flash_get_block_count(void); +void supervisor_flash_flush(void); + +// these return 0 on success, non-zero on error +mp_uint_t supervisor_flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks); +mp_uint_t supervisor_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks); + +struct _fs_user_mount_t; +void supervisor_flash_init_vfs(struct _fs_user_mount_t *vfs); +void supervisor_flash_flush(void); + +#endif // MICROPY_INCLUDED_SUPERVISOR_FLASH_H diff --git a/supervisor/flash_root_pointers.h b/supervisor/flash_root_pointers.h new file mode 100644 index 000000000..634ae58d3 --- /dev/null +++ b/supervisor/flash_root_pointers.h @@ -0,0 +1,35 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC + * + * 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_SUPERVISOR_FLASH_ROOT_POINTERS_H +#define MICROPY_INCLUDED_SUPERVISOR_FLASH_ROOT_POINTERS_H + +#ifdef EXTERNAL_FLASH_DEVICE_COUNT +#include "supervisor/shared/external_flash/external_flash_root_pointers.h" +#else +#include "supervisor/internal_flash_root_pointers.h" +#endif + +#endif // MICROPY_INCLUDED_SUPERVISOR_FLASH_ROOT_POINTERS_H diff --git a/supervisor/port.h b/supervisor/port.h index f30d87b47..dd4a82209 100644 --- a/supervisor/port.h +++ b/supervisor/port.h @@ -57,6 +57,9 @@ void reset_port(void); // Reset the rest of the board. void reset_board(void); +// Reset to the bootloader +void reset_to_bootloader(void); + #ifdef NRF52_SERIES void HardFault_Handler(void); #endif diff --git a/supervisor/serial.h b/supervisor/serial.h index 9446ee71c..84b3062a3 100644 --- a/supervisor/serial.h +++ b/supervisor/serial.h @@ -30,8 +30,18 @@ #include <stdbool.h> #include <stdint.h> +#include "py/mpconfig.h" + +#ifdef CIRCUITPY_BOOT_OUTPUT_FILE +#include "lib/oofatfs/ff.h" + +FIL* boot_output_file; +#endif + void serial_init(void); void serial_write(const char* text); +// Only writes up to given length. Does not check for null termination at all. +void serial_write_substring(const char* text, uint32_t length); char serial_read(void); bool serial_bytes_available(void); bool serial_connected(void); diff --git a/supervisor/shared/external_flash/common_commands.h b/supervisor/shared/external_flash/common_commands.h new file mode 100644 index 000000000..cc0da2175 --- /dev/null +++ b/supervisor/shared/external_flash/common_commands.h @@ -0,0 +1,47 @@ +/* + * This file is part of the MicroPython 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_EXTERNAL_FLASH_COMMON_COMMANDS_H +#define MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_COMMON_COMMANDS_H + +#define CMD_READ_JEDEC_ID 0x9f +#define CMD_READ_DATA 0x03 +#define CMD_FAST_READ_DATA 0x0B +#define CMD_SECTOR_ERASE 0x20 +// #define CMD_SECTOR_ERASE CMD_READ_JEDEC_ID +#define CMD_DISABLE_WRITE 0x04 +#define CMD_ENABLE_WRITE 0x06 +#define CMD_PAGE_PROGRAM 0x02 +// #define CMD_PAGE_PROGRAM CMD_READ_JEDEC_ID +#define CMD_READ_STATUS 0x05 +#define CMD_READ_STATUS2 0x35 +#define CMD_WRITE_STATUS_BYTE1 0x01 +#define CMD_WRITE_STATUS_BYTE2 0x31 +#define CMD_DUAL_READ 0x3b +#define CMD_QUAD_READ 0x6b +#define CMD_ENABLE_RESET 0x66 +#define CMD_RESET 0x99 + +#endif // MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_COMMON_COMMANDS_H diff --git a/supervisor/shared/external_flash/devices.h b/supervisor/shared/external_flash/devices.h new file mode 100644 index 000000000..86e0868d0 --- /dev/null +++ b/supervisor/shared/external_flash/devices.h @@ -0,0 +1,375 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC + * + * 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_EXTERNAL_FLASH_DEVICES_H +#define MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_DEVICES_H + +#include <stdbool.h> +#include <stdint.h> + +typedef struct { + uint32_t total_size; + uint16_t start_up_time_us; + + // Three response bytes to 0x9f JEDEC ID command. + uint8_t manufacturer_id; + uint8_t memory_type; + uint8_t capacity; + + // Max clock speed for all operations and the fastest read mode. + uint8_t max_clock_speed_mhz; + + // Bitmask for Quad Enable bit if present. 0x00 otherwise. This is for the highest byte in the + // status register. + uint8_t quad_enable_bit_mask; + + bool has_sector_protection : 1; + + // Supports the 0x0b fast read command with 8 dummy cycles. + bool supports_fast_read : 1; + + // Supports the fast read, quad output command 0x6b with 8 dummy cycles. + bool supports_qspi : 1; + + // Supports the quad input page program command 0x32. This is known as 1-1-4 because it only + // uses all four lines for data. + bool supports_qspi_writes: 1; + + // Requires a separate command 0x31 to write to the second byte of the status register. + // Otherwise two byte are written via 0x01. + bool write_status_register_split: 1; + + // True when the status register is a single byte. This implies the Quad Enable bit is in the + // first byte and the Read Status Register 2 command (0x35) is unsupported. + bool single_status_byte: 1; +} external_flash_device; + +// Settings for the Adesto Tech AT25DF081A 1MiB SPI flash. Its on the SAMD21 +// Xplained board. +// Datasheet: https://www.adestotech.com/wp-content/uploads/doc8715.pdf +#define AT25DF081A {\ + .total_size = (1 << 20), /* 1 MiB */ \ + .start_up_time_us = 10000, \ + .manufacturer_id = 0x1f, \ + .memory_type = 0x45, \ + .capacity = 0x01, \ + .max_clock_speed_mhz = 85, \ + .quad_enable_bit_mask = 0x00, \ + .has_sector_protection = true, \ + .supports_fast_read = true, \ + .supports_qspi = false, \ + .supports_qspi_writes = false, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} + +// Settings for the Gigadevice GD25Q16C 2MiB SPI flash. +// Datasheet: http://www.gigadevice.com/datasheet/gd25q16c/ +#define GD25Q16C {\ + .total_size = (1 << 21), /* 2 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xc8, \ + .memory_type = 0x40, \ + .capacity = 0x15, \ + .max_clock_speed_mhz = 104, /* if we need 120 then we can turn on high performance mode */ \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} + +// Settings for the Gigadevice GD25Q64C 8MiB SPI flash. +// Datasheet: http://www.elm-tech.com/en/products/spi-flash-memory/gd25q64/gd25q64.pdf +#define GD25Q64C {\ + .total_size = (1 << 23), /* 8 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xc8, \ + .memory_type = 0x40, \ + .capacity = 0x17, \ + .max_clock_speed_mhz = 104, /* if we need 120 then we can turn on high performance mode */ \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = true, \ + .single_status_byte = false, \ +} + +// Settings for the Cypress (was Spansion) S25FL064L 8MiB SPI flash. +// Datasheet: http://www.cypress.com/file/316661/download +#define S25FL064L {\ + .total_size = (1 << 23), /* 8 MiB */ \ + .start_up_time_us = 300, \ + .manufacturer_id = 0x01, \ + .memory_type = 0x60, \ + .capacity = 0x17, \ + .max_clock_speed_mhz = 108, \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} + +// Settings for the Cypress (was Spansion) S25FL116K 2MiB SPI flash. +// Datasheet: http://www.cypress.com/file/196886/download +#define S25FL116K {\ + .total_size = (1 << 21), /* 2 MiB */ \ + .start_up_time_us = 10000, \ + .manufacturer_id = 0x01, \ + .memory_type = 0x40, \ + .capacity = 0x15, \ + .max_clock_speed_mhz = 108, \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .supports_qspi_writes = false, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} + +// Settings for the Cypress (was Spansion) S25FL216K 2MiB SPI flash. +// Datasheet: http://www.cypress.com/file/197346/download +#define S25FL216K {\ + .total_size = (1 << 21), /* 2 MiB */ \ + .start_up_time_us = 10000, \ + .manufacturer_id = 0x01, \ + .memory_type = 0x40, \ + .capacity = 0x15, \ + .max_clock_speed_mhz = 65, \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = false, \ + .supports_qspi_writes = false, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} + +// Settings for the Winbond W25Q16FW 2MiB SPI flash. +// Datasheet: https://www.winbond.com/resource-files/w25q16fw%20revj%2005182017%20sfdp.pdf +#define W25Q16FW {\ + .total_size = (1 << 21), /* 2 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xef, \ + .memory_type = 0x60, \ + .capacity = 0x15, \ + .max_clock_speed_mhz = 133, \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} + +// Settings for the Winbond W25Q16JV-IQ 2MiB SPI flash. Note that JV-IM has a different .memory_type (0x70) +// Datasheet: https://www.winbond.com/resource-files/w25q16jv%20spi%20revf%2005092017.pdf +#define W25Q16JV_IQ {\ + .total_size = (1 << 21), /* 2 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xef, \ + .memory_type = 0x40, \ + .capacity = 0x15, \ + .max_clock_speed_mhz = 133, \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} + +// Settings for the Winbond W25Q16JV-IM 2MiB SPI flash. Note that JV-IQ has a different .memory_type (0x40) +// Datasheet: https://www.winbond.com/resource-files/w25q16jv%20spi%20revf%2005092017.pdf +#define W25Q16JV_IM {\ + .total_size = (1 << 21), /* 2 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xef, \ + .memory_type = 0x70, \ + .capacity = 0x15, \ + .max_clock_speed_mhz = 133, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .has_quad_enable = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = false, \ +} + +// Settings for the Winbond W25Q32BV 4MiB SPI flash. +// Datasheet: https://www.winbond.com/resource-files/w25q32bv_revi_100413_wo_automotive.pdf +#define W25Q32BV {\ + .total_size = (1 << 22), /* 4 MiB */ \ + .start_up_time_us = 10000, \ + .manufacturer_id = 0xef, \ + .memory_type = 0x60, \ + .capacity = 0x16, \ + .max_clock_speed_mhz = 104, \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .supports_qspi_writes = false, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} +// Settings for the Winbond W25Q32JV-IM 4MiB SPI flash. +// Datasheet: https://www.winbond.com/resource-files/w25q32jv%20revg%2003272018%20plus.pdf +#define W25Q32JV_IM {\ + .total_size = (1 << 22), /* 4 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xef, \ + .memory_type = 0x70, \ + .capacity = 0x16, \ + .max_clock_speed_mhz = 133, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .has_quad_enable = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = false, \ +} + +// Settings for the Winbond W25Q64JV-IM 8MiB SPI flash. Note that JV-IQ has a different .memory_type (0x40) +// Datasheet: http://www.winbond.com/resource-files/w25q64jv%20revj%2003272018%20plus.pdf +#define W25Q64JV_IM {\ + .total_size = (1 << 23), /* 8 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xef, \ + .memory_type = 0x70, \ + .capacity = 0x17, \ + .max_clock_speed_mhz = 133, \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .has_quad_enable = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} + +// Settings for the Winbond W25Q64JV-IQ 8MiB SPI flash. Note that JV-IM has a different .memory_type (0x70) +// Datasheet: http://www.winbond.com/resource-files/w25q64jv%20revj%2003272018%20plus.pdf +#define W25Q64JV_IQ {\ + .total_size = (1 << 23), /* 8 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xef, \ + .memory_type = 0x40, \ + .capacity = 0x17, \ + .max_clock_speed_mhz = 133, \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} + +// Settings for the Winbond W25Q80DL 1MiB SPI flash. +// Datasheet: https://www.winbond.com/resource-files/w25q80dv%20dl_revh_10022015.pdf +#define W25Q80DL {\ + .total_size = (1 << 20), /* 1 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xef, \ + .memory_type = 0x60, \ + .capacity = 0x14, \ + .max_clock_speed_mhz = 104, \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .supports_qspi_writes = false, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} + + +// Settings for the Winbond W25Q128JV-SQ 16MiB SPI flash. Note that JV-IM has a different .memory_type (0x70) +// Datasheet: https://www.winbond.com/resource-files/w25q128jv%20revf%2003272018%20plus.pdf +#define W25Q128JV_SQ {\ + .total_size = (1 << 24), /* 16 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xef, \ + .memory_type = 0x40, \ + .capacity = 0x18, \ + .max_clock_speed_mhz = 133, \ + .quad_enable_bit_mask = 0x02, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = false, \ + .single_status_byte = false, \ +} + +// Settings for the Macronix MX25R6435F 8MiB SPI flash. +// Datasheet: http://www.macronix.com/Lists/Datasheet/Attachments/7428/MX25R6435F,%20Wide%20Range,%2064Mb,%20v1.4.pdf +// By default its in lower power mode which can only do 8mhz. In high power mode it can do 80mhz. +#define MX25R6435F {\ + .total_size = (1 << 23), /* 8 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xc2, \ + .memory_type = 0x28, \ + .capacity = 0x17, \ + .max_clock_speed_mhz = 8, \ + .quad_enable_bit_mask = 0x40, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = false, \ + .single_status_byte = true, \ +} + +// Settings for the Winbond W25Q128JV-PM 16MiB SPI flash. Note that JV-IM has a different .memory_type (0x70) +// Datasheet: https://www.winbond.com/resource-files/w25q128jv%20revf%2003272018%20plus.pdf +#define W25Q128JV_PM {\ + .total_size = (1 << 24), /* 16 MiB */ \ + .start_up_time_us = 5000, \ + .manufacturer_id = 0xef, \ + .memory_type = 0x70, \ + .capacity = 0x18, \ + .max_clock_speed_mhz = 133, \ + .has_sector_protection = false, \ + .supports_fast_read = true, \ + .supports_qspi = true, \ + .has_quad_enable = true, \ + .supports_qspi_writes = true, \ + .write_status_register_split = false, \ +} + +#endif // MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_DEVICES_H diff --git a/supervisor/shared/external_flash/external_flash.c b/supervisor/shared/external_flash/external_flash.c new file mode 100644 index 000000000..73b7f7f91 --- /dev/null +++ b/supervisor/shared/external_flash/external_flash.c @@ -0,0 +1,546 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016, 2017 Scott Shawcroft for Adafruit Industries + * + * 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 "external_flash.h" + +#include <stdint.h> +#include <string.h> + +#include "supervisor/spi_flash_api.h" +#include "supervisor/shared/external_flash/common_commands.h" +#include "extmod/vfs.h" +#include "extmod/vfs_fat.h" +#include "py/misc.h" +#include "py/obj.h" +#include "py/runtime.h" +#include "lib/oofatfs/ff.h" +#include "shared-bindings/microcontroller/__init__.h" +#include "supervisor/memory.h" +#include "supervisor/shared/rgb_led_status.h" + +#define NO_SECTOR_LOADED 0xFFFFFFFF + +// The currently cached sector in the cache, ram or flash based. +static uint32_t current_sector; + +const external_flash_device possible_devices[EXTERNAL_FLASH_DEVICE_COUNT] = {EXTERNAL_FLASH_DEVICES}; + +static const external_flash_device* flash_device = NULL; + +// Track which blocks (up to 32) in the current sector currently live in the +// cache. +static uint32_t dirty_mask; + +static supervisor_allocation* supervisor_cache = NULL; + +// Wait until both the write enable and write in progress bits have cleared. +static bool wait_for_flash_ready(void) { + uint8_t read_status_response[1] = {0x00}; + bool ok = true; + // Both the write enable and write in progress bits should be low. + do { + ok = spi_flash_read_command(CMD_READ_STATUS, read_status_response, 1); + } while (ok && (read_status_response[0] & 0x3) != 0); + return ok; +} + +// Turn on the write enable bit so we can program and erase the flash. +static bool write_enable(void) { + return spi_flash_command(CMD_ENABLE_WRITE); +} + +// Read data_length's worth of bytes starting at address into data. +static bool read_flash(uint32_t address, uint8_t* data, uint32_t data_length) { + if (flash_device == NULL) { + return false; + } + if (!wait_for_flash_ready()) { + return false; + } + return spi_flash_read_data(address, data, data_length); +} + +// Writes data_length's worth of bytes starting at address from data. Assumes +// that the sector that address resides in has already been erased. So make sure +// to run erase_sector. +static bool write_flash(uint32_t address, const uint8_t* data, uint32_t data_length) { + if (flash_device == NULL) { + return false; + } + // Don't bother writing if the data is all 1s. Thats equivalent to the flash + // state after an erase. + bool all_ones = true; + for (uint16_t i = 0; i < data_length; i++) { + if (data[i] != 0xff) { + all_ones = false; + break; + } + } + if (all_ones) { + return true; + } + + for (uint32_t bytes_written = 0; + bytes_written < data_length; + bytes_written += SPI_FLASH_PAGE_SIZE) { + if (!wait_for_flash_ready() || !write_enable()) { + return false; + } + + if (!spi_flash_write_data(address + bytes_written, (uint8_t*) data + bytes_written, + SPI_FLASH_PAGE_SIZE)) { + return false; + } + } + return true; +} + +static bool page_erased(uint32_t sector_address) { + // Check the first few bytes to catch the common case where there is data + // without using a bunch of memory. + uint8_t short_buffer[4]; + if (read_flash(sector_address, short_buffer, 4)) { + for (uint16_t i = 0; i < 4; i++) { + if (short_buffer[i] != 0xff) { + return false; + } + } + } else { + return false; + } + + // Now check the full length. + uint8_t full_buffer[FILESYSTEM_BLOCK_SIZE]; + if (read_flash(sector_address, full_buffer, FILESYSTEM_BLOCK_SIZE)) { + for (uint16_t i = 0; i < FILESYSTEM_BLOCK_SIZE; i++) { + if (short_buffer[i] != 0xff) { + return false; + } + } + } else { + return false; + } + return true; +} + +// Erases the given sector. Make sure you copied all of the data out of it you +// need! Also note, sector_address is really 24 bits. +static bool erase_sector(uint32_t sector_address) { + // Before we erase the sector we need to wait for any writes to finish and + // and then enable the write again. + if (!wait_for_flash_ready() || !write_enable()) { + return false; + } + + spi_flash_sector_command(CMD_SECTOR_ERASE, sector_address); + return true; +} + +// Sector is really 24 bits. +static bool copy_block(uint32_t src_address, uint32_t dest_address) { + // Copy page by page to minimize RAM buffer. + uint16_t page_size = SPI_FLASH_PAGE_SIZE; + uint8_t buffer[page_size]; + for (uint32_t i = 0; i < FILESYSTEM_BLOCK_SIZE / page_size; i++) { + if (!read_flash(src_address + i * page_size, buffer, page_size)) { + return false; + } + if (!write_flash(dest_address + i * page_size, buffer, page_size)) { + return false; + } + } + return true; +} + +void supervisor_flash_init(void) { + if (flash_device != NULL) { + return; + } + + // Delay to give the SPI Flash time to get going. + // TODO(tannewt): Only do this when we know power was applied vs a reset. + uint16_t max_start_up_delay_us = 0; + for (uint8_t i = 0; i < EXTERNAL_FLASH_DEVICE_COUNT; i++) { + if (possible_devices[i].start_up_time_us > max_start_up_delay_us) { + max_start_up_delay_us = possible_devices[i].start_up_time_us; + } + } + common_hal_mcu_delay_us(max_start_up_delay_us); + + spi_flash_init(); + + // The response will be 0xff if the flash needs more time to start up. + uint8_t jedec_id_response[3] = {0xff, 0xff, 0xff}; + while (jedec_id_response[0] == 0xff) { + spi_flash_read_command(CMD_READ_JEDEC_ID, jedec_id_response, 3); + } + + for (uint8_t i = 0; i < EXTERNAL_FLASH_DEVICE_COUNT; i++) { + const external_flash_device* possible_device = &possible_devices[i]; + if (jedec_id_response[0] == possible_device->manufacturer_id && + jedec_id_response[1] == possible_device->memory_type && + jedec_id_response[2] == possible_device->capacity) { + flash_device = possible_device; + break; + } + } + + if (flash_device == NULL) { + return; + } + + // We don't know what state the flash is in so wait for any remaining writes and then reset. + uint8_t read_status_response[1] = {0x00}; + // The write in progress bit should be low. + do { + spi_flash_read_command(CMD_READ_STATUS, read_status_response, 1); + } while ((read_status_response[0] & 0x1) != 0); + // The suspended write/erase bit should be low. + do { + spi_flash_read_command(CMD_READ_STATUS2, read_status_response, 1); + } while ((read_status_response[0] & 0x80) != 0); + + + spi_flash_command(CMD_ENABLE_RESET); + spi_flash_command(CMD_RESET); + + // Wait 30us for the reset + common_hal_mcu_delay_us(30); + + spi_flash_init_device(flash_device); + + // Activity LED for flash writes. +#ifdef MICROPY_HW_LED_MSC + gpio_set_pin_function(SPI_FLASH_CS_PIN, GPIO_PIN_FUNCTION_OFF); + gpio_set_pin_direction(MICROPY_HW_LED_MSC, GPIO_DIRECTION_OUT); + // There's already a pull-up on the board. + gpio_set_pin_level(MICROPY_HW_LED_MSC, false); +#endif + + if (flash_device->has_sector_protection) { + write_enable(); + + // Turn off sector protection + uint8_t data[1] = {0x00}; + spi_flash_write_command(CMD_WRITE_STATUS_BYTE1, data, 1); + } + + // Turn off writes in case this is a microcontroller only reset. + spi_flash_command(CMD_DISABLE_WRITE); + + wait_for_flash_ready(); + + current_sector = NO_SECTOR_LOADED; + dirty_mask = 0; + MP_STATE_VM(flash_ram_cache) = NULL; +} + +// The size of each individual block. +uint32_t supervisor_flash_get_block_size(void) { + return FILESYSTEM_BLOCK_SIZE; +} + +// The total number of available blocks. +uint32_t supervisor_flash_get_block_count(void) { + // We subtract one erase sector size because we may use it as a staging area + // for writes. + return (flash_device->total_size - SPI_FLASH_ERASE_SIZE) / FILESYSTEM_BLOCK_SIZE; +} + +// Flush the cache that was written to the scratch portion of flash. Only used +// when ram is tight. +static bool flush_scratch_flash(void) { + // First, copy out any blocks that we haven't touched from the sector we've + // cached. + bool copy_to_scratch_ok = true; + uint32_t scratch_sector = flash_device->total_size - SPI_FLASH_ERASE_SIZE; + for (uint8_t i = 0; i < SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE; i++) { + if ((dirty_mask & (1 << i)) == 0) { + copy_to_scratch_ok = copy_to_scratch_ok && + copy_block(current_sector + i * FILESYSTEM_BLOCK_SIZE, + scratch_sector + i * FILESYSTEM_BLOCK_SIZE); + } + } + if (!copy_to_scratch_ok) { + // TODO(tannewt): Do more here. We opted to not erase and copy bad data + // in. We still risk losing the data written to the scratch sector. + return false; + } + // Second, erase the current sector. + erase_sector(current_sector); + // Finally, copy the new version into it. + for (uint8_t i = 0; i < SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE; i++) { + copy_block(scratch_sector + i * FILESYSTEM_BLOCK_SIZE, + current_sector + i * FILESYSTEM_BLOCK_SIZE); + } + return true; +} + +// Attempts to allocate a new set of page buffers for caching a full sector in +// ram. Each page is allocated separately so that the GC doesn't need to provide +// one huge block. We can free it as we write if we want to also. +static bool allocate_ram_cache(void) { + uint8_t blocks_per_sector = SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE; + uint8_t pages_per_block = FILESYSTEM_BLOCK_SIZE / SPI_FLASH_PAGE_SIZE; + + uint32_t table_size = blocks_per_sector * pages_per_block * sizeof(uint32_t); + // Attempt to allocate outside the heap first. + supervisor_cache = allocate_memory(table_size + SPI_FLASH_ERASE_SIZE, false); + if (supervisor_cache != NULL) { + MP_STATE_VM(flash_ram_cache) = (uint8_t **) supervisor_cache->ptr; + uint8_t* page_start = (uint8_t *) supervisor_cache->ptr + table_size; + + for (uint8_t i = 0; i < blocks_per_sector; i++) { + for (uint8_t j = 0; j < pages_per_block; j++) { + uint32_t offset = i * pages_per_block + j; + MP_STATE_VM(flash_ram_cache)[offset] = page_start + offset * SPI_FLASH_PAGE_SIZE; + } + } + return true; + } + + MP_STATE_VM(flash_ram_cache) = m_malloc_maybe(blocks_per_sector * pages_per_block * sizeof(uint32_t), false); + if (MP_STATE_VM(flash_ram_cache) == NULL) { + return false; + } + // Declare i and j outside the loops in case we fail to allocate everything + // we need. In that case we'll give it back. + uint8_t i = 0; + uint8_t j = 0; + bool success = true; + for (i = 0; i < blocks_per_sector; i++) { + for (j = 0; j < pages_per_block; j++) { + uint8_t *page_cache = m_malloc_maybe(SPI_FLASH_PAGE_SIZE, false); + if (page_cache == NULL) { + success = false; + break; + } + MP_STATE_VM(flash_ram_cache)[i * pages_per_block + j] = page_cache; + } + if (!success) { + break; + } + } + // We couldn't allocate enough so give back what we got. + if (!success) { + // We add 1 so that we delete 0 when i is 1. Going to zero (i >= 0) + // would never stop because i is unsigned. + i++; + for (; i > 0; i--) { + for (; j > 0; j--) { + m_free(MP_STATE_VM(flash_ram_cache)[(i - 1) * pages_per_block + (j - 1)]); + } + j = pages_per_block; + } + m_free(MP_STATE_VM(flash_ram_cache)); + MP_STATE_VM(flash_ram_cache) = NULL; + } + return success; +} + +// Flush the cached sector from ram onto the flash. We'll free the cache unless +// keep_cache is true. +static bool flush_ram_cache(bool keep_cache) { + // First, copy out any blocks that we haven't touched from the sector + // we've cached. If we don't do this we'll erase the data during the sector + // erase below. + bool copy_to_ram_ok = true; + uint8_t pages_per_block = FILESYSTEM_BLOCK_SIZE / SPI_FLASH_PAGE_SIZE; + for (uint8_t i = 0; i < SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE; i++) { + if ((dirty_mask & (1 << i)) == 0) { + for (uint8_t j = 0; j < pages_per_block; j++) { + copy_to_ram_ok = read_flash( + current_sector + (i * pages_per_block + j) * SPI_FLASH_PAGE_SIZE, + MP_STATE_VM(flash_ram_cache)[i * pages_per_block + j], + SPI_FLASH_PAGE_SIZE); + if (!copy_to_ram_ok) { + break; + } + } + } + if (!copy_to_ram_ok) { + break; + } + } + + if (!copy_to_ram_ok) { + return false; + } + // Second, erase the current sector. + erase_sector(current_sector); + // Lastly, write all the data in ram that we've cached. + for (uint8_t i = 0; i < SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE; i++) { + for (uint8_t j = 0; j < pages_per_block; j++) { + write_flash(current_sector + (i * pages_per_block + j) * SPI_FLASH_PAGE_SIZE, + MP_STATE_VM(flash_ram_cache)[i * pages_per_block + j], + SPI_FLASH_PAGE_SIZE); + if (!keep_cache && supervisor_cache == NULL) { + m_free(MP_STATE_VM(flash_ram_cache)[i * pages_per_block + j]); + } + } + } + // We're done with the cache for now so give it back. + if (!keep_cache) { + if (supervisor_cache != NULL) { + free_memory(supervisor_cache); + supervisor_cache = NULL; + } else { + m_free(MP_STATE_VM(flash_ram_cache)); + } + MP_STATE_VM(flash_ram_cache) = NULL; + } + return true; +} + +// Delegates to the correct flash flush method depending on the existing cache. +static void spi_flash_flush_keep_cache(bool keep_cache) { + if (current_sector == NO_SECTOR_LOADED) { + return; + } + #ifdef MICROPY_HW_LED_MSC + port_pin_set_output_level(MICROPY_HW_LED_MSC, true); + #endif + temp_status_color(ACTIVE_WRITE); + // If we've cached to the flash itself flush from there. + if (MP_STATE_VM(flash_ram_cache) == NULL) { + flush_scratch_flash(); + } else { + flush_ram_cache(keep_cache); + } + current_sector = NO_SECTOR_LOADED; + clear_temp_status(); + #ifdef MICROPY_HW_LED_MSC + port_pin_set_output_level(MICROPY_HW_LED_MSC, false); + #endif +} + +// External flash function used. If called externally we assume we won't need +// the cache after. +void supervisor_flash_flush(void) { + spi_flash_flush_keep_cache(false); +} + +static int32_t convert_block_to_flash_addr(uint32_t block) { + if (0 <= block && block < supervisor_flash_get_block_count()) { + // a block in partition 1 + return block * FILESYSTEM_BLOCK_SIZE; + } + // bad block + return -1; +} + +bool external_flash_read_block(uint8_t *dest, uint32_t block) { + int32_t address = convert_block_to_flash_addr(block); + if (address == -1) { + // bad block number + return false; + } + + // Mask out the lower bits that designate the address within the sector. + uint32_t this_sector = address & (~(SPI_FLASH_ERASE_SIZE - 1)); + uint8_t block_index = (address / FILESYSTEM_BLOCK_SIZE) % (SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE); + uint8_t mask = 1 << (block_index); + // We're reading from the currently cached sector. + if (current_sector == this_sector && (mask & dirty_mask) > 0) { + if (MP_STATE_VM(flash_ram_cache) != NULL) { + uint8_t pages_per_block = FILESYSTEM_BLOCK_SIZE / SPI_FLASH_PAGE_SIZE; + for (int i = 0; i < pages_per_block; i++) { + memcpy(dest + i * SPI_FLASH_PAGE_SIZE, + MP_STATE_VM(flash_ram_cache)[block_index * pages_per_block + i], + SPI_FLASH_PAGE_SIZE); + } + return true; + } else { + uint32_t scratch_address = flash_device->total_size - SPI_FLASH_ERASE_SIZE + block_index * FILESYSTEM_BLOCK_SIZE; + return read_flash(scratch_address, dest, FILESYSTEM_BLOCK_SIZE); + } + } + return read_flash(address, dest, FILESYSTEM_BLOCK_SIZE); +} + +bool external_flash_write_block(const uint8_t *data, uint32_t block) { + // Non-MBR block, copy to cache + int32_t address = convert_block_to_flash_addr(block); + if (address == -1) { + // bad block number + return false; + } + // Wait for any previous writes to finish. + wait_for_flash_ready(); + // Mask out the lower bits that designate the address within the sector. + uint32_t this_sector = address & (~(SPI_FLASH_ERASE_SIZE - 1)); + uint8_t block_index = (address / FILESYSTEM_BLOCK_SIZE) % (SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE); + uint8_t mask = 1 << (block_index); + // Flush the cache if we're moving onto a sector or we're writing the + // same block again. + if (current_sector != this_sector || (mask & dirty_mask) > 0) { + // Check to see if we'd write to an erased page. In that case we + // can write directly. + if (page_erased(address)) { + return write_flash(address, data, FILESYSTEM_BLOCK_SIZE); + } + if (current_sector != NO_SECTOR_LOADED) { + spi_flash_flush_keep_cache(true); + } + if (MP_STATE_VM(flash_ram_cache) == NULL && !allocate_ram_cache()) { + erase_sector(flash_device->total_size - SPI_FLASH_ERASE_SIZE); + wait_for_flash_ready(); + } + current_sector = this_sector; + dirty_mask = 0; + } + dirty_mask |= mask; + // Copy the block to the appropriate cache. + if (MP_STATE_VM(flash_ram_cache) != NULL) { + uint8_t pages_per_block = FILESYSTEM_BLOCK_SIZE / SPI_FLASH_PAGE_SIZE; + for (int i = 0; i < pages_per_block; i++) { + memcpy(MP_STATE_VM(flash_ram_cache)[block_index * pages_per_block + i], + data + i * SPI_FLASH_PAGE_SIZE, + SPI_FLASH_PAGE_SIZE); + } + return true; + } else { + uint32_t scratch_address = flash_device->total_size - SPI_FLASH_ERASE_SIZE + block_index * FILESYSTEM_BLOCK_SIZE; + return write_flash(scratch_address, data, FILESYSTEM_BLOCK_SIZE); + } +} + +mp_uint_t supervisor_flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) { + for (size_t i = 0; i < num_blocks; i++) { + if (!external_flash_read_block(dest + i * FILESYSTEM_BLOCK_SIZE, block_num + i)) { + return 1; // error + } + } + return 0; // success +} + +mp_uint_t supervisor_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 (!external_flash_write_block(src + i * FILESYSTEM_BLOCK_SIZE, block_num + i)) { + return 1; // error + } + } + return 0; // success +} diff --git a/supervisor/shared/external_flash/external_flash.h b/supervisor/shared/external_flash/external_flash.h new file mode 100644 index 000000000..72b619a2a --- /dev/null +++ b/supervisor/shared/external_flash/external_flash.h @@ -0,0 +1,48 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC + * + * 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_SUPERVISOR_SHARED_EXTERNAL_FLASH_EXTERNAL_FLASH_H +#define MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_EXTERNAL_FLASH_H + +#include <stdbool.h> +#include <stdint.h> + +#include "py/mpconfig.h" + +// Erase sector size. +#define SPI_FLASH_SECTOR_SIZE (0x1000 - 100) + +// These are common across all NOR Flash. +#define SPI_FLASH_ERASE_SIZE (1 << 12) +#define SPI_FLASH_PAGE_SIZE (256) + +#define SPI_FLASH_SYSTICK_MASK (0x1ff) // 512ms +#define SPI_FLASH_IDLE_TICK(tick) (((tick) & SPI_FLASH_SYSTICK_MASK) == 2) + +#ifndef SPI_FLASH_MAX_BAUDRATE +#define SPI_FLASH_MAX_BAUDRATE 8000000 +#endif + +#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_EXTERNAL_FLASH_H diff --git a/supervisor/shared/external_flash/external_flash_root_pointers.h b/supervisor/shared/external_flash/external_flash_root_pointers.h new file mode 100644 index 000000000..cb1b86d19 --- /dev/null +++ b/supervisor/shared/external_flash/external_flash_root_pointers.h @@ -0,0 +1,35 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC + * + * 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_SUPERVISOR_SHARED_EXTERNAL_FLASH_EXTERNAL_FLASH_ROOT_POINTERS_H +#define MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_EXTERNAL_FLASH_ROOT_POINTERS_H + +#include <stdint.h> + +// We use this when we can allocate the whole cache in RAM. +#define FLASH_ROOT_POINTERS \ + uint8_t** flash_ram_cache; \ + +#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_EXTERNAL_FLASH_ROOT_POINTERS_H diff --git a/supervisor/shared/external_flash/qspi_flash.c b/supervisor/shared/external_flash/qspi_flash.c new file mode 100644 index 000000000..48266540c --- /dev/null +++ b/supervisor/shared/external_flash/qspi_flash.c @@ -0,0 +1,56 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016, 2017, 2018 Scott Shawcroft for Adafruit Industries + * + * 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 "supervisor/spi_flash_api.h" + +#include "supervisor/shared/external_flash/common_commands.h" + +void check_quad_enable(const external_flash_device* device) { + if (device->quad_enable_bit_mask == 0x00) { + return; + } + + // Verify that QSPI mode is enabled. + uint8_t status; + if (device->single_status_byte) { + spi_flash_read_command(CMD_READ_STATUS, &status, 1); + } else { + spi_flash_read_command(CMD_READ_STATUS2, &status, 1); + } + + // Check the quad enable bit. + if ((status & device->quad_enable_bit_mask) == 0) { + uint8_t full_status[2] = {0x00, device->quad_enable_bit_mask}; + spi_flash_command(CMD_ENABLE_WRITE); + if (device->write_status_register_split) { + spi_flash_write_command(CMD_WRITE_STATUS_BYTE2, full_status + 1, 1); + } else if (device->single_status_byte) { + spi_flash_write_command(CMD_WRITE_STATUS_BYTE1, full_status + 1, 1); + } else { + spi_flash_write_command(CMD_WRITE_STATUS_BYTE1, full_status, 2); + } + } +} diff --git a/supervisor/shared/external_flash/qspi_flash.h b/supervisor/shared/external_flash/qspi_flash.h new file mode 100644 index 000000000..b72e37b26 --- /dev/null +++ b/supervisor/shared/external_flash/qspi_flash.h @@ -0,0 +1,31 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC + * + * 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_SUPERVISOR_SHARED_EXTERNAL_FLASH_QSPI_FLASH_H +#define MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_QSPI_FLASH_H + +void check_quad_enable(const external_flash_device* device); + +#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_QSPI_FLASH_H diff --git a/supervisor/shared/external_flash/spi_flash.c b/supervisor/shared/external_flash/spi_flash.c new file mode 100644 index 000000000..ec7101bc9 --- /dev/null +++ b/supervisor/shared/external_flash/spi_flash.c @@ -0,0 +1,150 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016, 2017 Scott Shawcroft for Adafruit Industries + * + * 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 "supervisor/spi_flash_api.h" + +#include <stdint.h> +#include <string.h> + +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "supervisor/shared/external_flash/common_commands.h" +#include "supervisor/shared/external_flash/external_flash.h" +#include "py/mpconfig.h" + +digitalio_digitalinout_obj_t cs_pin; +busio_spi_obj_t spi; + +const external_flash_device* flash_device; +uint32_t spi_flash_baudrate; + +// Enable the flash over SPI. +static void flash_enable(void) { + while (!common_hal_busio_spi_try_lock(&spi)) {} + common_hal_digitalio_digitalinout_set_value(&cs_pin, false); +} + +// Disable the flash over SPI. +static void flash_disable(void) { + common_hal_digitalio_digitalinout_set_value(&cs_pin, true); + common_hal_busio_spi_unlock(&spi); +} + +static bool transfer(uint8_t* command, uint32_t command_length, uint8_t* data_in, uint8_t* data_out, uint32_t data_length) { + flash_enable(); + bool status = common_hal_busio_spi_write(&spi, command, command_length); + if (status) { + if (data_in != NULL && data_out != NULL) { + status = common_hal_busio_spi_transfer(&spi, data_out, data_in, data_length); + } else if (data_out != NULL) { + status = common_hal_busio_spi_read(&spi, data_out, data_length, 0xff); + } else if (data_in != NULL) { + status = common_hal_busio_spi_write(&spi, data_in, data_length); + } + } + flash_disable(); + return status; +} + +static bool transfer_command(uint8_t command, uint8_t* data_in, uint8_t* data_out, uint32_t data_length) { + return transfer(&command, 1, data_in, data_out, data_length); +} + +bool spi_flash_command(uint8_t command) { + return transfer_command(command, NULL, NULL, 0); +} + +bool spi_flash_read_command(uint8_t command, uint8_t* data, uint32_t data_length) { + return transfer_command(command, NULL, data, data_length); +} + +bool spi_flash_write_command(uint8_t command, uint8_t* data, uint32_t data_length) { + return transfer_command(command, data, NULL, data_length); +} + +// Pack the low 24 bits of the address into a uint8_t array. +static void address_to_bytes(uint32_t address, uint8_t* bytes) { + bytes[0] = (address >> 16) & 0xff; + bytes[1] = (address >> 8) & 0xff; + bytes[2] = address & 0xff; +} + +bool spi_flash_sector_command(uint8_t command, uint32_t address) { + uint8_t request[4] = {command, 0x00, 0x00, 0x00}; + address_to_bytes(address, request + 1); + return transfer(request, 4, NULL, NULL, 0); +} + +bool spi_flash_write_data(uint32_t address, uint8_t* data, uint32_t data_length) { + uint8_t request[4] = {CMD_PAGE_PROGRAM, 0x00, 0x00, 0x00}; + // Write the SPI flash write address into the bytes following the command byte. + address_to_bytes(address, request + 1); + flash_enable(); + common_hal_busio_spi_configure(&spi, spi_flash_baudrate, 0, 0, 8); + bool status = common_hal_busio_spi_write(&spi, request, 4); + if (status) { + status = common_hal_busio_spi_write(&spi, data, data_length); + } + flash_disable(); + return status; +} + +bool spi_flash_read_data(uint32_t address, uint8_t* data, uint32_t data_length) { + uint8_t request[5] = {CMD_READ_DATA, 0x00, 0x00, 0x00}; + uint8_t command_length = 4; + if (flash_device->supports_fast_read) { + request[0] = CMD_FAST_READ_DATA; + command_length = 5; + } + // Write the SPI flash write address into the bytes following the command byte. + address_to_bytes(address, request + 1); + flash_enable(); + common_hal_busio_spi_configure(&spi, spi_flash_baudrate, 0, 0, 8); + bool status = common_hal_busio_spi_write(&spi, request, command_length); + if (status) { + status = common_hal_busio_spi_read(&spi, data, data_length, 0xff); + } + flash_disable(); + return status; +} + +void spi_flash_init(void) { + common_hal_digitalio_digitalinout_construct(&cs_pin, SPI_FLASH_CS_PIN); + + // Set CS high (disabled). + common_hal_digitalio_digitalinout_switch_to_output(&cs_pin, true, DRIVE_MODE_PUSH_PULL); + + common_hal_busio_spi_construct(&spi, SPI_FLASH_SCK_PIN, SPI_FLASH_MOSI_PIN, SPI_FLASH_MISO_PIN); + common_hal_busio_spi_never_reset(&spi); +} + +void spi_flash_init_device(const external_flash_device* device) { + flash_device = device; + spi_flash_baudrate = device->max_clock_speed_mhz * 1000000; + if (spi_flash_baudrate > SPI_FLASH_MAX_BAUDRATE) { + spi_flash_baudrate = SPI_FLASH_MAX_BAUDRATE; + } +} diff --git a/supervisor/shared/filesystem.c b/supervisor/shared/filesystem.c new file mode 100644 index 000000000..d968f4798 --- /dev/null +++ b/supervisor/shared/filesystem.c @@ -0,0 +1,107 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * + * 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 "extmod/vfs_fat.h" +#include "lib/oofatfs/ff.h" +#include "lib/oofatfs/diskio.h" + +#include "py/mpstate.h" + +#include "supervisor/flash.h" + +static mp_vfs_mount_t _mp_vfs; +static fs_user_mount_t _internal_vfs; + +static void make_empty_file(FATFS *fatfs, const char *path) { + FIL fp; + f_open(fatfs, &fp, path, FA_WRITE | FA_CREATE_ALWAYS); + f_close(&fp); +} + +// 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 filesystem_init(bool create_allowed, bool force_create) { + // init the vfs object + fs_user_mount_t *vfs_fat = &_internal_vfs; + vfs_fat->flags = 0; + supervisor_flash_init_vfs(vfs_fat); + + // try to mount the flash + FRESULT res = f_mount(&vfs_fat->fatfs); + + if ((res == FR_NO_FILESYSTEM && create_allowed) || force_create) { + // No filesystem so create a fresh one, or reformat has been requested. + uint8_t working_buf[_MAX_SS]; + res = f_mkfs(&vfs_fat->fatfs, FM_FAT, 0, working_buf, sizeof(working_buf)); + // Flush the new file system to make sure it's repaired immediately. + supervisor_flash_flush(); + if (res != FR_OK) { + return; + } + + // set label + f_setlabel(&vfs_fat->fatfs, "CIRCUITPY"); + + // inhibit file indexing on MacOS + f_mkdir(&vfs_fat->fatfs, "/.fseventsd"); + make_empty_file(&vfs_fat->fatfs, "/.metadata_never_index"); + make_empty_file(&vfs_fat->fatfs, "/.Trashes"); + make_empty_file(&vfs_fat->fatfs, "/.fseventsd/no_log"); + + // and ensure everything is flushed + supervisor_flash_flush(); + } else if (res != FR_OK) { + return; + } + mp_vfs_mount_t *vfs = &_mp_vfs; + vfs->str = "/"; + vfs->len = 1; + vfs->obj = MP_OBJ_FROM_PTR(vfs_fat); + vfs->next = NULL; + MP_STATE_VM(vfs_mount_table) = vfs; + + // The current directory is used as the boot up directory. + // It is set to the internal flash filesystem by default. + MP_STATE_PORT(vfs_cur) = vfs; +} + +void filesystem_flush(void) { + supervisor_flash_flush(); +} + +void filesystem_writable_by_python(bool writable) { + fs_user_mount_t *vfs = &_internal_vfs; + + if (writable) { + vfs->flags |= FSUSER_USB_WRITABLE; + } else { + vfs->flags &= ~FSUSER_USB_WRITABLE; + } +} + +bool filesystem_present(void) { + return true; +} diff --git a/supervisor/shared/flash.c b/supervisor/shared/flash.c new file mode 100644 index 000000000..c8921aa85 --- /dev/null +++ b/supervisor/shared/flash.c @@ -0,0 +1,202 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft + * + * 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 "supervisor/flash.h" + +#include "extmod/vfs_fat.h" +#include "py/runtime.h" +#include "lib/oofatfs/ff.h" + +#define VFS_INDEX 0 + +#define PART1_START_BLOCK (0x1) + +void supervisor_flash_set_usb_writable(bool usb_writable) { + mp_vfs_mount_t* current_mount = MP_STATE_VM(vfs_mount_table); + for (uint8_t i = 0; current_mount != NULL; i++) { + if (i == VFS_INDEX) { + break; + } + current_mount = current_mount->next; + } + if (current_mount == NULL) { + return; + } + fs_user_mount_t *vfs = (fs_user_mount_t *) current_mount->obj; + + if (usb_writable) { + vfs->flags |= FSUSER_USB_WRITABLE; + } else { + vfs->flags &= ~FSUSER_USB_WRITABLE; + } +} + +// there is a singleton Flash object +const mp_obj_type_t supervisor_flash_type; +STATIC const mp_obj_base_t supervisor_flash_obj = {&supervisor_flash_type}; + +STATIC mp_obj_t supervisor_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)&supervisor_flash_obj; +} + +uint32_t flash_get_block_count(void) { + return PART1_START_BLOCK + supervisor_flash_get_block_count(); +} + +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; +} + +mp_uint_t flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) { + if (block_num == 0) { + if (block_num > 1) { + return 1; // error + } + // 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 */, PART1_START_BLOCK, supervisor_flash_get_block_count()); + 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 0; // ok + + } + return supervisor_flash_read_blocks(dest, block_num - PART1_START_BLOCK, num_blocks); +} + +mp_uint_t flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks) { + if (block_num == 0) { + if (num_blocks > 1) { + return 1; // error + } + // can't write MBR, but pretend we did + return 0; + } else { + return supervisor_flash_write_blocks(src, block_num - PART1_START_BLOCK, num_blocks); + } +} + +STATIC mp_obj_t supervisor_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 = flash_read_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FILESYSTEM_BLOCK_SIZE); + return MP_OBJ_NEW_SMALL_INT(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(supervisor_flash_obj_readblocks_obj, supervisor_flash_obj_readblocks); + +STATIC mp_obj_t supervisor_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 = flash_write_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FILESYSTEM_BLOCK_SIZE); + return MP_OBJ_NEW_SMALL_INT(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(supervisor_flash_obj_writeblocks_obj, supervisor_flash_obj_writeblocks); + +STATIC mp_obj_t supervisor_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: supervisor_flash_init(); return MP_OBJ_NEW_SMALL_INT(0); + case BP_IOCTL_DEINIT: supervisor_flash_flush(); return MP_OBJ_NEW_SMALL_INT(0); // TODO properly + case BP_IOCTL_SYNC: supervisor_flash_flush(); return MP_OBJ_NEW_SMALL_INT(0); + case BP_IOCTL_SEC_COUNT: return MP_OBJ_NEW_SMALL_INT(flash_get_block_count()); + case BP_IOCTL_SEC_SIZE: return MP_OBJ_NEW_SMALL_INT(supervisor_flash_get_block_size()); + default: return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(supervisor_flash_obj_ioctl_obj, supervisor_flash_obj_ioctl); + +STATIC const mp_rom_map_elem_t supervisor_flash_obj_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&supervisor_flash_obj_readblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&supervisor_flash_obj_writeblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&supervisor_flash_obj_ioctl_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(supervisor_flash_obj_locals_dict, supervisor_flash_obj_locals_dict_table); + +const mp_obj_type_t supervisor_flash_type = { + { &mp_type_type }, + .name = MP_QSTR_Flash, + .make_new = supervisor_flash_obj_make_new, + .locals_dict = (mp_obj_t)&supervisor_flash_obj_locals_dict, +}; + +void supervisor_flash_init_vfs(fs_user_mount_t *vfs) { + vfs->base.type = &mp_fat_vfs_type; + vfs->flags |= FSUSER_NATIVE | FSUSER_HAVE_IOCTL; + vfs->fatfs.drv = vfs; + vfs->fatfs.part = 1; // flash filesystem lives on first partition + vfs->readblocks[0] = (mp_obj_t)&supervisor_flash_obj_readblocks_obj; + vfs->readblocks[1] = (mp_obj_t)&supervisor_flash_obj; + vfs->readblocks[2] = (mp_obj_t)flash_read_blocks; // native version + vfs->writeblocks[0] = (mp_obj_t)&supervisor_flash_obj_writeblocks_obj; + vfs->writeblocks[1] = (mp_obj_t)&supervisor_flash_obj; + vfs->writeblocks[2] = (mp_obj_t)flash_write_blocks; // native version + vfs->u.ioctl[0] = (mp_obj_t)&supervisor_flash_obj_ioctl_obj; + vfs->u.ioctl[1] = (mp_obj_t)&supervisor_flash_obj; +} diff --git a/supervisor/shared/micropython.c b/supervisor/shared/micropython.c new file mode 100644 index 000000000..245db11d4 --- /dev/null +++ b/supervisor/shared/micropython.c @@ -0,0 +1,58 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * + * 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 <string.h> + +#include "supervisor/serial.h" +#include "lib/oofatfs/ff.h" +#include "py/mpconfig.h" + +#include "supervisor/shared/status_leds.h" + +int mp_hal_stdin_rx_chr(void) { + for (;;) { + #ifdef MICROPY_VM_HOOK_LOOP + MICROPY_VM_HOOK_LOOP + #endif + if (serial_bytes_available()) { + toggle_rx_led(); + return serial_read(); + } + } +} + +void mp_hal_stdout_tx_strn(const char *str, size_t len) { + toggle_tx_led(); + + #ifdef CIRCUITPY_BOOT_OUTPUT_FILE + if (boot_output_file != NULL) { + UINT bytes_written = 0; + f_write(boot_output_file, str, len, &bytes_written); + } + #endif + + serial_write_substring(str, len); +} diff --git a/supervisor/shared/rgb_led_status.h b/supervisor/shared/rgb_led_status.h index 24e071aac..9542b3197 100644 --- a/supervisor/shared/rgb_led_status.h +++ b/supervisor/shared/rgb_led_status.h @@ -33,7 +33,7 @@ #include "lib/utils/pyexec.h" #include "supervisor/port.h" -#include "mpconfigport.h" +#include "py/mpconfig.h" #include "rgb_led_colors.h" // Overall, the time module must be implemented. diff --git a/supervisor/shared/serial.c b/supervisor/shared/serial.c new file mode 100644 index 000000000..c57688ddc --- /dev/null +++ b/supervisor/shared/serial.c @@ -0,0 +1,63 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * + * 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 <string.h> + +#include "supervisor/serial.h" +#include "supervisor/usb.h" + +#include "tusb.h" + +void serial_init(void) { + usb_init(); +} + +bool serial_connected(void) { + return tud_cdc_connected(); +} + +char serial_read(void) { + return (char) tud_cdc_read_char(); +} + +bool serial_bytes_available(void) { + return tud_cdc_available() > 0; +} + +void serial_write_substring(const char* text, uint32_t length) { + if (!tud_cdc_connected()) { + return; + } + uint32_t count = 0; + while (count < length) { + count += tud_cdc_write(text + count, length - count); + usb_background(); + } +} + +void serial_write(const char* text) { + serial_write_substring(text, strlen(text)); +} diff --git a/supervisor/shared/status_leds.c b/supervisor/shared/status_leds.c new file mode 100644 index 000000000..d99853ceb --- /dev/null +++ b/supervisor/shared/status_leds.c @@ -0,0 +1,62 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * + * 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 "supervisor/shared/status_leds.h" + +#include "common-hal/digitalio/DigitalInOut.h" +#include "shared-bindings/digitalio/DigitalInOut.h" + +#ifdef MICROPY_HW_LED_RX +digitalio_digitalinout_obj_t rx_led; +#endif + +#ifdef MICROPY_HW_LED_TX +digitalio_digitalinout_obj_t tx_led; +#endif + +void init_status_leds(void) { + #ifdef MICROPY_HW_LED_RX + common_hal_digitalio_digitalinout_construct(&rx_led, MICROPY_HW_LED_RX); + common_hal_digitalio_digitalinout_switch_to_output(&rx_led, true, DRIVE_MODE_PUSH_PULL); + #endif + #ifdef MICROPY_HW_LED_TX + common_hal_digitalio_digitalinout_construct(&tx_led, MICROPY_HW_LED_TX); + common_hal_digitalio_digitalinout_switch_to_output(&tx_led, true, DRIVE_MODE_PUSH_PULL); + #endif +} + +void toggle_rx_led(void) { + #ifdef MICROPY_HW_LED_RX + common_hal_digitalio_digitalinout_set_value(&rx_led, !common_hal_digitalio_digitalinout_get_value(&rx_led)); + #endif +} + + +void toggle_tx_led(void) { + #ifdef MICROPY_HW_LED_TX + common_hal_digitalio_digitalinout_set_value(&tx_led, !common_hal_digitalio_digitalinout_get_value(&tx_led)); + #endif +} diff --git a/supervisor/shared/status_leds.h b/supervisor/shared/status_leds.h new file mode 100644 index 000000000..30132753f --- /dev/null +++ b/supervisor/shared/status_leds.h @@ -0,0 +1,36 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * + * 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_SUPERVISOR_STATUS_LEDS_H +#define MICROPY_INCLUDED_SUPERVISOR_STATUS_LEDS_H + +void init_status_leds(void); + +void toggle_rx_led(void); + +void toggle_tx_led(void); + +#endif // MICROPY_INCLUDED_SUPERVISOR_STATUS_LEDS_H diff --git a/supervisor/shared/usb/tusb_config.h b/supervisor/shared/usb/tusb_config.h new file mode 100644 index 000000000..649c390ac --- /dev/null +++ b/supervisor/shared/usb/tusb_config.h @@ -0,0 +1,116 @@ +/**************************************************************************/ +/*! + @file tusb_config.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2013, hathach (tinyusb.org) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ +/**************************************************************************/ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#include "genhdr/autogen_usb_descriptor.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// COMMON CONFIGURATION +//--------------------------------------------------------------------+ +#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE + +#define CFG_TUSB_DEBUG 0 + +/*------------- RTOS -------------*/ +#define CFG_TUSB_OS OPT_OS_NONE +//#define CFG_TUD_TASK_QUEUE_SZ 16 +//#define CFG_TUD_TASK_PRIO 0 +//#define CFG_TUD_TASK_STACK_SZ 150 + +//--------------------------------------------------------------------+ +// DEVICE CONFIGURATION +//--------------------------------------------------------------------+ + +#define CFG_TUD_ENDOINT0_SIZE 64 + +/*------------- Descriptors -------------*/ +/* Enable auto generated descriptor, tinyusb will try its best to create + * descriptor ( device, configuration, hid ) that matches enabled CFG_* in this file + * + * Note: All CFG_TUD_DESC_* are relevant only if CFG_TUD_DESC_AUTO is enabled + */ +#define CFG_TUD_DESC_AUTO 0 + +//------------- CLASS -------------// +#define CFG_TUD_CDC 1 +#define CFG_TUD_MSC 1 +#define CFG_TUD_HID 1 +#define CFG_TUD_CUSTOM_CLASS 0 + +/*------------------------------------------------------------------*/ +/* CLASS DRIVER + *------------------------------------------------------------------*/ + +/* TX is sent automatically on every Start of Frame event ~ 1ms. + * If not enabled, application must call tud_cdc_flush() periodically + * Note: Enabled this could overflow device task, if it does, define + * CFG_TUD_TASK_QUEUE_SZ with large value + */ +#define CFG_TUD_CDC_FLUSH_ON_SOF 0 + + +/*------------- MSC -------------*/ +// Number of supported Logical Unit Number (At least 1) +#define CFG_TUD_MSC_MAXLUN 1 + +// Number of Blocks +#define CFG_TUD_MSC_BLOCK_NUM (256*1024)/512 + + + +// Product revision string included in Inquiry response, max 4 bytes +#define CFG_TUD_MSC_PRODUCT_REV "1.0" + + +//--------------------------------------------------------------------+ +// USB RAM PLACEMENT +//--------------------------------------------------------------------+ +#define CFG_TUSB_ATTR_USBRAM +#define CFG_TUSB_MEM_ALIGN ATTR_ALIGNED(4) + + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/supervisor/shared/usb/usb.c b/supervisor/shared/usb/usb.c new file mode 100644 index 000000000..1aa34e9e6 --- /dev/null +++ b/supervisor/shared/usb/usb.c @@ -0,0 +1,140 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 hathach for Adafruit Industries + * + * 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 "tick.h" +#include "shared-bindings/microcontroller/Processor.h" +#include "supervisor/port.h" +#include "supervisor/usb.h" +#include "lib/utils/interrupt_char.h" +#include "lib/mp-readline/readline.h" + +#include "tusb.h" + +// Serial number as hex characters. This writes directly to the USB +// descriptor. +extern uint16_t usb_serial_number[1 + COMMON_HAL_MCU_PROCESSOR_UID_LENGTH * 2]; + +void load_serial_number(void) { + // create serial number based on device unique id + uint8_t raw_id[COMMON_HAL_MCU_PROCESSOR_UID_LENGTH]; + common_hal_mcu_processor_get_uid(raw_id); + + const char nibble_to_hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F'}; + for (int i = 0; i < COMMON_HAL_MCU_PROCESSOR_UID_LENGTH; i++) { + for (int j = 0; j < 2; j++) { + uint8_t nibble = (raw_id[i] >> (j * 4)) & 0xf; + // Strings are UTF-16-LE encoded. + usb_serial_number[1 + i * 2 + j] = nibble_to_hex[nibble]; + } + } +} + +bool _usb_enabled = false; + +bool usb_enabled(void) { + return _usb_enabled; +} + +void usb_init(void) { + init_usb_hardware(); + load_serial_number(); + + tusb_init(); + _usb_enabled = true; + +#if MICROPY_KBD_EXCEPTION + // Set Ctrl+C as wanted char, tud_cdc_rx_wanted_cb() callback will be invoked when Ctrl+C is received + // This callback always got invoked regardless of mp_interrupt_char value since we only set it once here + tud_cdc_set_wanted_char(CHAR_CTRL_C); +#endif +} + +void usb_background(void) { + if (usb_enabled()) { + tusb_task(); + tud_cdc_write_flush(); + } +} + +//--------------------------------------------------------------------+ +// tinyusb callbacks +//--------------------------------------------------------------------+ + +// Invoked when device is mounted +void tud_mount_cb(void) { +} + +// Invoked when device is unmounted +void tud_umount_cb(void) { +} + +uint32_t tusb_hal_millis(void) { + uint64_t ms; + uint32_t us; + current_tick(&ms, &us); + return (uint32_t) ms; +} + + +// Invoked when cdc when line state changed e.g connected/disconnected +// Use to reset to DFU when disconnect with 1200 bps +void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) { + (void) itf; // interface ID, not used + + // DTR = false is counted as disconnected + if ( !dtr ) + { + cdc_line_coding_t coding; + tud_cdc_get_line_coding(&coding); + + if ( coding.bit_rate == 1200 ) + { + reset_to_bootloader(); + } + } +} + +#if MICROPY_KBD_EXCEPTION + +/** + * Callback invoked when received an "wanted" char. + * @param itf Interface index (for multiple cdc interfaces) + * @param wanted_char The wanted char (set previously) + */ +void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) +{ + (void) itf; // not used + + // Workaround for using lib/utils/interrupt_char.c + // Compare mp_interrupt_char with wanted_char and ignore if not matched + if (mp_interrupt_char == wanted_char) { + tud_cdc_read_flush(); // flush read fifo + mp_keyboard_interrupt(); + } +} + +#endif diff --git a/supervisor/shared/usb/usb_desc.c b/supervisor/shared/usb/usb_desc.c new file mode 100644 index 000000000..5a0de1ffc --- /dev/null +++ b/supervisor/shared/usb/usb_desc.c @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 hathach for Adafruit Industries + * + * 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 "supervisor/shared/usb/usb_desc.h" +#include "shared-module/usb_hid/Device.h" + +#include "genhdr/autogen_usb_descriptor.h" + +// tud_desc_set is required by tinyusb stack +tud_desc_set_t tud_desc_set = +{ + .device = &usb_desc_dev, + .config = &usb_desc_cfg, + .string_arr = (uint8_t const **) string_desc_arr, + .string_count = sizeof(string_desc_arr)/sizeof(string_desc_arr[0]), + + .hid_report = + { + .generic = hid_report_descriptor, + .boot_keyboard = NULL, + .boot_mouse = NULL + } +}; diff --git a/supervisor/shared/usb/usb_desc.h b/supervisor/shared/usb/usb_desc.h new file mode 100644 index 000000000..250147aa6 --- /dev/null +++ b/supervisor/shared/usb/usb_desc.h @@ -0,0 +1,43 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 hathach for Adafruit Industries + * + * 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 USB_DESC_H_ +#define USB_DESC_H_ + +#include "lib/tinyusb/src/tusb.h" + +#ifdef __cplusplus + extern "C" { +#endif + +// Descriptors set used by tinyusb stack +extern tud_desc_set_t tud_desc_set; + +#ifdef __cplusplus + } +#endif + +#endif /* USB_DESC_H_ */ diff --git a/supervisor/shared/usb/usb_msc_flash.c b/supervisor/shared/usb/usb_msc_flash.c new file mode 100644 index 000000000..658aab0d8 --- /dev/null +++ b/supervisor/shared/usb/usb_msc_flash.c @@ -0,0 +1,208 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 hathach for Adafruit Industries + * + * 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 "tusb.h" +// // #include "supervisor/flash.h" + +// For updating fatfs's cache +#include "extmod/vfs.h" +#include "extmod/vfs_fat.h" +#include "lib/oofatfs/diskio.h" +#include "lib/oofatfs/ff.h" +#include "py/mpstate.h" + +#include "supervisor/shared/autoreload.h" + +#define MSC_FLASH_BLOCK_SIZE 512 + +static bool ejected[1]; + +// The root FS is always at the end of the list. +static fs_user_mount_t* get_vfs(int lun) { + // TODO(tannewt): Return the mount which matches the lun where 0 is the end + // and is counted in reverse. + if (lun > 0) { + return NULL; + } + mp_vfs_mount_t* current_mount = MP_STATE_VM(vfs_mount_table); + if (current_mount == NULL) { + return NULL; + } + while (current_mount->next != NULL) { + current_mount = current_mount->next; + } + return current_mount->obj; +} + +// Callback invoked when received an SCSI command not in built-in list below +// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE +// - READ10 and WRITE10 have their own callbacks +int32_t tud_msc_scsi_cb (uint8_t lun, const uint8_t scsi_cmd[16], void* buffer, uint16_t bufsize) { + const void* response = NULL; + int32_t resplen = 0; + + switch ( scsi_cmd[0] ) { + case SCSI_CMD_TEST_UNIT_READY: + // Command that host uses to check our readiness before sending other commands + resplen = 0; + if (lun > 1) { + resplen = -1; + } else { + fs_user_mount_t* current_mount = get_vfs(lun); + if (current_mount == NULL) { + resplen = -1; + } + if (ejected[lun]) { + resplen = -1; + } + } + break; + + case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: + // Host is about to read/write etc ... better not to disconnect disk + resplen = 0; + break; + + case SCSI_CMD_START_STOP_UNIT: + { + // Host try to eject/safe remove/poweroff us. We could safely disconnect with disk storage, or go into lower power + const scsi_start_stop_unit_t* start_stop = (const scsi_start_stop_unit_t*) scsi_cmd; + // Start bit = 0 : low power mode, if load_eject = 1 : unmount disk storage as well + // Start bit = 1 : Ready mode, if load_eject = 1 : mount disk storage + resplen = 0; + if (start_stop->load_eject == 1) { + if (lun > 1) { + resplen = -1; + } else { + fs_user_mount_t* current_mount = get_vfs(lun); + if (current_mount == NULL) { + resplen = -1; + } + if (disk_ioctl(current_mount, CTRL_SYNC, NULL) != RES_OK) { + resplen = -1; + } else { + ejected[lun] = true; + } + } + } + } + break; + + default: + // Set Sense = Invalid Command Operation + tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); + + // negative means error -> tinyusb could stall and/or response with failed status + resplen = -1; + break; + } + + // return len must not larger than bufsize + if ( resplen > bufsize ) { + resplen = bufsize; + } + + // copy response to stack's buffer if any + if ( response && resplen ) { + memcpy(buffer, response, resplen); + } + + return resplen; +} + +void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) { + fs_user_mount_t * vfs = get_vfs(lun); + disk_ioctl(vfs, GET_SECTOR_COUNT, block_count); + disk_ioctl(vfs, GET_SECTOR_SIZE, block_size); +} + +bool tud_msc_is_writable_cb(uint8_t lun) { + if (lun > 1) { + return false; + } + + fs_user_mount_t* vfs = get_vfs(lun); + if (vfs == NULL) { + return false; + } + if (vfs->writeblocks[0] == MP_OBJ_NULL || + (vfs->flags & FSUSER_USB_WRITABLE) == 0) { + return false; + } + return true; +} + +// Callback invoked when received READ10 command. +// Copy disk's data to buffer (up to bufsize) and return number of copied bytes. +int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) { + (void) lun; + (void) offset; + + const uint32_t block_count = bufsize / MSC_FLASH_BLOCK_SIZE; + + fs_user_mount_t * vfs = get_vfs(lun); + disk_read(vfs, buffer, lba, block_count); + + return block_count * MSC_FLASH_BLOCK_SIZE; +} + +// Callback invoked when received WRITE10 command. +// Process data in buffer to disk's storage and return number of written bytes +int32_t tud_msc_write10_cb (uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) { + (void) lun; + (void) offset; + + const uint32_t block_count = bufsize / MSC_FLASH_BLOCK_SIZE; + + fs_user_mount_t * vfs = get_vfs(lun); + disk_write(vfs, buffer, lba, block_count); + // Since by getting here we assume the mount is read-only to + // MicroPython let's update the cached FatFs sector if it's the one + // we just wrote. + #if _MAX_SS != _MIN_SS + if (vfs->ssize == MSC_FLASH_BLOCK_SIZE) { + #else + // The compiler can optimize this away. + if (_MAX_SS == FILESYSTEM_BLOCK_SIZE) { + #endif + if (lba == vfs->fatfs.winsect && lba > 0) { + memcpy(vfs->fatfs.win, + buffer + MSC_FLASH_BLOCK_SIZE * (vfs->fatfs.winsect - lba), + MSC_FLASH_BLOCK_SIZE); + } + } + + return block_count * MSC_FLASH_BLOCK_SIZE; +} + +// Callback invoked when WRITE10 command is completed (status received and accepted by host). +// used to flush any pending cache. +void tud_msc_write10_complete_cb (uint8_t lun) { + (void) lun; + + // This write is complete, start the autoreload clock. + autoreload_start(); +} diff --git a/supervisor/spi_flash_api.h b/supervisor/spi_flash_api.h new file mode 100644 index 000000000..28cccb1b1 --- /dev/null +++ b/supervisor/spi_flash_api.h @@ -0,0 +1,45 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC + * + * 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_SUPERVISOR_SPI_FLASH_H +#define MICROPY_INCLUDED_SUPERVISOR_SPI_FLASH_H + +#include <stdbool.h> +#include <stdint.h> + +#include "supervisor/shared/external_flash/devices.h" + +// This API is implemented for both normal SPI peripherals and QSPI peripherals. + +bool spi_flash_command(uint8_t command); +bool spi_flash_read_command(uint8_t command, uint8_t* response, uint32_t length); +bool spi_flash_write_command(uint8_t command, uint8_t* data, uint32_t length); +bool spi_flash_sector_command(uint8_t command, uint32_t address); +bool spi_flash_write_data(uint32_t address, uint8_t* data, uint32_t data_length); +bool spi_flash_read_data(uint32_t address, uint8_t* data, uint32_t data_length); +void spi_flash_init(void); +void spi_flash_init_device(const external_flash_device* device); + +#endif // MICROPY_INCLUDED_SUPERVISOR_SPI_FLASH_H diff --git a/supervisor/supervisor.mk b/supervisor/supervisor.mk index e516dcb2d..73c76ebb7 100644 --- a/supervisor/supervisor.mk +++ b/supervisor/supervisor.mk @@ -2,22 +2,82 @@ SRC_SUPERVISOR = \ main.c \ supervisor/port.c \ supervisor/shared/autoreload.c \ + supervisor/shared/filesystem.c \ + supervisor/shared/flash.c \ + supervisor/shared/micropython.c \ supervisor/shared/rgb_led_status.c \ supervisor/shared/stack.c \ + supervisor/shared/status_leds.c \ supervisor/shared/translate.c -ifeq ($(wildcard atmel-samd/supervisor/filesystem.c),) - SRC_SUPERVISOR += supervisor/filesystem.c +ifndef $(NO_USB) +NO_USB = $(wildcard supervisor/usb.c) +endif + + +# Choose which flash filesystem impl to use. +# (Right now INTERNAL_FLASH_FILESYSTEM and SPI_FLASH_FILESYSTEM are mutually exclusive. +# But that might not be true in the future.) +ifdef EXTERNAL_FLASH_DEVICES + CFLAGS += -DEXTERNAL_FLASH_DEVICES=$(EXTERNAL_FLASH_DEVICES) \ + -DEXTERNAL_FLASH_DEVICE_COUNT=$(EXTERNAL_FLASH_DEVICE_COUNT) + + SRC_SUPERVISOR += supervisor/shared/external_flash/external_flash.c + ifeq ($(SPI_FLASH_FILESYSTEM),1) + CFLAGS += -DSPI_FLASH_FILESYSTEM + SRC_SUPERVISOR += supervisor/shared/external_flash/spi_flash.c + endif + ifeq ($(QSPI_FLASH_FILESYSTEM),1) + CFLAGS += -DQSPI_FLASH_FILESYSTEM + SRC_SUPERVISOR += supervisor/qspi_flash.c supervisor/shared/external_flash/qspi_flash.c + endif else - SRC_SUPERVISOR += supervisor/stub/filesystem.c + SRC_SUPERVISOR += supervisor/internal_flash.c endif -ifeq ($(wildcard atmel-samd/supervisor/serial.c),) - SRC_SUPERVISOR += supervisor/serial.c +ifeq ($(USB),FALSE) + ifeq ($(wildcard supervisor/serial.c),) + SRC_SUPERVISOR += supervisor/stub/serial.c + else + SRC_SUPERVISOR += supervisor/serial.c + endif else - SRC_SUPERVISOR += supervisor/stub/serial.c + SRC_SUPERVISOR += lib/tinyusb/src/common/tusb_fifo.c \ + lib/tinyusb/src/device/usbd.c \ + lib/tinyusb/src/device/usbd_control.c \ + lib/tinyusb/src/class/msc/msc_device.c \ + lib/tinyusb/src/class/cdc/cdc_device.c \ + lib/tinyusb/src/class/hid/hid_device.c \ + lib/tinyusb/src/tusb.c \ + supervisor/shared/serial.c \ + supervisor/usb.c \ + supervisor/shared/usb/usb_desc.c \ + supervisor/shared/usb/usb.c \ + supervisor/shared/usb/usb_msc_flash.c \ + shared-bindings/usb_hid/__init__.c \ + shared-bindings/usb_hid/Device.c \ + shared-module/usb_hid/__init__.c \ + shared-module/usb_hid/Device.c \ + $(BUILD)/autogen_usb_descriptor.c + CFLAGS += -DUSB_AVAILABLE endif SUPERVISOR_O = $(addprefix $(BUILD)/, $(SRC_SUPERVISOR:.c=.o)) $(BUILD)/supervisor/shared/translate.o: $(HEADER_BUILD)/qstrdefs.generated.h + +$(BUILD)/autogen_usb_descriptor.c $(BUILD)/genhdr/autogen_usb_descriptor.h: autogen_usb_descriptor.intermediate + +.INTERMEDIATE: autogen_usb_descriptor.intermediate + +autogen_usb_descriptor.intermediate: ../../tools/gen_usb_descriptor.py Makefile | $(HEADER_BUILD) + $(STEPECHO) "GEN $@" + $(Q)install -d $(BUILD)/genhdr + $(Q)$(PYTHON3) ../../tools/gen_usb_descriptor.py \ + --manufacturer $(USB_MANUFACTURER)\ + --product $(USB_PRODUCT)\ + --vid $(USB_VID)\ + --pid $(USB_PID)\ + --serial_number_length $(USB_SERIAL_NUMBER_LENGTH)\ + --output_c_file $(BUILD)/autogen_usb_descriptor.c\ + --output_h_file $(BUILD)/genhdr/autogen_usb_descriptor.h diff --git a/supervisor/usb.h b/supervisor/usb.h new file mode 100644 index 000000000..c87540d40 --- /dev/null +++ b/supervisor/usb.h @@ -0,0 +1,44 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 hathach for Adafruit Industries + * + * 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_SUPERVISOR_USB_H +#define MICROPY_INCLUDED_SUPERVISOR_USB_H + +#include <stdbool.h> + +// Ports must call this as frequently as they can in order to keep the USB connection +// alive and responsive. +void usb_background(void); + +// Only inits the USB peripheral clocks and pins. The peripheral will be initialized by +// TinyUSB. +void init_usb_hardware(void); + +// Shared implementation. +bool usb_enabled(void); +void usb_init(void); + +#endif // MICROPY_INCLUDED_SUPERVISOR_USB_H |
