From 4aeef100f6d78313e1cc58fb6ef6d6b0da5c879d Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 26 Oct 2017 16:53:25 -0700 Subject: [PATCH] atmel-samd: More USB polish * Introduce a python script to generate the USB descriptor instead of a bunch of C macros. In the future, we can use this dynamically in CircuitPython. * Add support for detecting read-only mass storage mounts. Fixes #377 --- extmod/vfs_fat.h | 4 +- extmod/vfs_fat_diskio.c | 4 +- main.c | 8 +- ports/atmel-samd/Makefile | 11 +- ports/atmel-samd/asf4 | 2 +- .../asf4_conf/samd21/hpl_usb_config.h | 16 +- .../asf4_conf/samd51/hpl_usb_config.h | 4 +- .../boards/arduino_zero/mpconfigboard.mk | 2 + .../mpconfigboard.mk | 2 + .../feather_m0_adalogger/mpconfigboard.mk | 2 + .../boards/feather_m0_basic/mpconfigboard.mk | 2 + .../feather_m0_express/mpconfigboard.mk | 2 + .../boards/gemma_m0/mpconfigboard.mk | 2 + .../boards/metro_m0_express/mpconfigboard.mk | 2 + .../boards/metro_m4_express/mpconfigboard.mk | 2 + .../boards/trinket_m0/mpconfigboard.mk | 2 + .../trinket_m0_haxpress/mpconfigboard.mk | 2 + .../atmel-samd/common-hal/storage/__init__.c | 2 +- ports/atmel-samd/flash_api.c | 8 +- ports/atmel-samd/flash_api.h | 2 +- ports/atmel-samd/supervisor/filesystem.c | 4 +- ports/atmel-samd/supervisor/serial.c | 40 ++-- .../atmel-samd/tools/autogen_usb_descriptor.h | 36 ++++ ports/atmel-samd/tools/gen_usb_descriptor.py | 150 ++++++++++++++ ports/atmel-samd/usb.c | 12 +- ports/atmel-samd/usb_mass_storage.c | 33 +++- ports/atmel-samd/usb_mass_storage.h | 1 + supervisor/filesystem.h | 2 +- supervisor/stub/filesystem.c | 2 +- tools/usb_descriptor/cdc.py | 53 +++++ tools/usb_descriptor/core.py | 187 ++++++++++++++++++ 31 files changed, 542 insertions(+), 59 deletions(-) create mode 100644 ports/atmel-samd/tools/autogen_usb_descriptor.h create mode 100644 ports/atmel-samd/tools/gen_usb_descriptor.py create mode 100644 tools/usb_descriptor/cdc.py create mode 100644 tools/usb_descriptor/core.py diff --git a/extmod/vfs_fat.h b/extmod/vfs_fat.h index 5de1a237b4..273ddc84eb 100644 --- a/extmod/vfs_fat.h +++ b/extmod/vfs_fat.h @@ -35,8 +35,8 @@ #define FSUSER_NATIVE (0x0001) // readblocks[2]/writeblocks[2] contain native func #define FSUSER_FREE_OBJ (0x0002) // fs_user_mount_t obj should be freed on umount #define FSUSER_HAVE_IOCTL (0x0004) // new protocol with ioctl -// Device is write-able over USB and read-only to MicroPython. -#define FSUSER_USB_WRITEABLE (0x0008) +// Device is writable over USB and read-only to MicroPython. +#define FSUSER_USB_WRITABLE (0x0008) typedef struct _fs_user_mount_t { mp_obj_base_t base; diff --git a/extmod/vfs_fat_diskio.c b/extmod/vfs_fat_diskio.c index ad769001df..3b9d86037e 100644 --- a/extmod/vfs_fat_diskio.c +++ b/extmod/vfs_fat_diskio.c @@ -98,9 +98,9 @@ DSTATUS disk_status ( } // This is used to determine the writeability of the disk from MicroPython. - // So, if its USB writeable we make it read-only from MicroPython. + // So, if its USB writable we make it read-only from MicroPython. if (vfs->writeblocks[0] == MP_OBJ_NULL || - (vfs->flags & FSUSER_USB_WRITEABLE) != 0) { + (vfs->flags & FSUSER_USB_WRITABLE) != 0) { return STA_PROTECT; } else { return 0; diff --git a/main.c b/main.c index a2e4b9f729..d5680d05de 100644 --- a/main.c +++ b/main.c @@ -234,8 +234,8 @@ int __attribute__((used)) main(void) { autoreload_enable(); // By default our internal flash is readonly to local python code and - // writeable over USB. Set it here so that boot.py can change it. - filesystem_default_writeable(false); + // writable over USB. Set it here so that boot.py can change it. + filesystem_writable_by_python(false); // If not in safe mode, run boot before initing USB and capture output in a // file. @@ -244,12 +244,12 @@ int __attribute__((used)) main(void) { #ifdef CIRCUITPY_BOOT_OUTPUT_FILE // Since USB isn't up yet we can cheat and let ourselves write the boot // output file. - filesystem_default_writeable(true); + filesystem_writable_by_python(true); FIL file_pointer; boot_output_file = &file_pointer; f_open(&((fs_user_mount_t *) MP_STATE_VM(vfs_mount_table)->obj)->fatfs, boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_WRITE | FA_CREATE_ALWAYS); - filesystem_default_writeable(false); + filesystem_writable_by_python(false); #endif // TODO(tannewt): Re-add support for flashing boot error output. diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile index ef168dbc90..ef4012802c 100644 --- a/ports/atmel-samd/Makefile +++ b/ports/atmel-samd/Makefile @@ -73,8 +73,6 @@ BASE_CFLAGS = \ -Wcast-align \ -Wno-error=lto-type-mismatch \ -D__$(CHIP_VARIANT)__ \ - -DCONF_USB_COMPOSITE_IDPRODUCT=$(USB_PID) \ - -DCONF_USB_COMPOSITE_IDVENDER=$(USB_VID) \ -ffunction-sections \ -fdata-sections \ -fshort-enums \ @@ -233,6 +231,7 @@ SRC_C = \ lib/utils/sys_stdio_mphal.c \ lib/libc/string0.c \ lib/mp-readline/readline.c \ + $(BUILD)/autogen_usb_descriptor.c \ # freetouch/adafruit_ptc.c # Choose which flash filesystem impl to use. @@ -359,6 +358,14 @@ $(BUILD)/firmware.uf2: $(BUILD)/firmware.bin $(ECHO) "Create $@" python2 $(TOP)/tools/uf2/utils/uf2conv.py -b $(BOOTLOADER_SIZE) -c -o $@ $^ +$(BUILD)/autogen_usb_descriptor.c: tools/gen_usb_descriptor.py Makefile + python3 tools/gen_usb_descriptor.py \ + --manufacturer $(USB_MANUFACTURER)\ + --product $(USB_PRODUCT)\ + --vid $(USB_VID)\ + --pid $(USB_PID)\ + $@ + deploy: $(BUILD)/firmware.bin $(ECHO) "Writing $< to the board" $(BOSSAC) -u $< diff --git a/ports/atmel-samd/asf4 b/ports/atmel-samd/asf4 index e1d02fde5d..1ca9e1f869 160000 --- a/ports/atmel-samd/asf4 +++ b/ports/atmel-samd/asf4 @@ -1 +1 @@ -Subproject commit e1d02fde5d23a704d33d74e18fcf624eb56132c5 +Subproject commit 1ca9e1f86921d99f0fbc907698f864a60caf675a diff --git a/ports/atmel-samd/asf4_conf/samd21/hpl_usb_config.h b/ports/atmel-samd/asf4_conf/samd21/hpl_usb_config.h index 0a895dbf82..2108077bca 100644 --- a/ports/atmel-samd/asf4_conf/samd21/hpl_usb_config.h +++ b/ports/atmel-samd/asf4_conf/samd21/hpl_usb_config.h @@ -39,7 +39,7 @@ // Max possible (by "Max Endpoint Number" config) // usbd_num_ep_sp #ifndef CONF_USB_D_NUM_EP_SP -#define CONF_USB_D_NUM_EP_SP CONF_USB_D_EP_N_MAX +#define CONF_USB_D_NUM_EP_SP CONF_USB_D_N_EP_MAX #endif // @@ -118,7 +118,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_ep1_I_CACHE #ifndef CONF_USB_EP1_I_CACHE -#define CONF_USB_EP1_I_CACHE 64 +#define CONF_USB_EP1_I_CACHE 0 #endif // @@ -176,7 +176,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_arch_ep3_cache #ifndef CONF_USB_EP3_CACHE -#define CONF_USB_EP3_CACHE 64 +#define CONF_USB_EP3_CACHE 0 #endif // Cache buffer size for EP3 IN @@ -194,7 +194,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_ep3_I_CACHE #ifndef CONF_USB_EP3_I_CACHE -#define CONF_USB_EP3_I_CACHE 0 +#define CONF_USB_EP3_I_CACHE 64 #endif // @@ -252,7 +252,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_arch_ep5_cache #ifndef CONF_USB_EP5_CACHE -#define CONF_USB_EP5_CACHE 64 +#define CONF_USB_EP5_CACHE 0 #endif // Cache buffer size for EP5 IN @@ -290,7 +290,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_arch_ep6_cache #ifndef CONF_USB_EP6_CACHE -#define CONF_USB_EP6_CACHE 64 +#define CONF_USB_EP6_CACHE 0 #endif // Cache buffer size for EP6 IN @@ -308,7 +308,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_ep6_I_CACHE #ifndef CONF_USB_EP6_I_CACHE -#define CONF_USB_EP6_I_CACHE 64 +#define CONF_USB_EP6_I_CACHE 0 #endif // @@ -328,7 +328,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_arch_ep7_cache #ifndef CONF_USB_EP7_CACHE -#define CONF_USB_EP7_CACHE 64 +#define CONF_USB_EP7_CACHE 0 #endif // Cache buffer size for EP7 IN diff --git a/ports/atmel-samd/asf4_conf/samd51/hpl_usb_config.h b/ports/atmel-samd/asf4_conf/samd51/hpl_usb_config.h index 0f8155ecb3..92dfcaa9ba 100644 --- a/ports/atmel-samd/asf4_conf/samd51/hpl_usb_config.h +++ b/ports/atmel-samd/asf4_conf/samd51/hpl_usb_config.h @@ -120,7 +120,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_ep1_I_CACHE #ifndef CONF_USB_EP1_I_CACHE -#define CONF_USB_EP1_I_CACHE 64 +#define CONF_USB_EP1_I_CACHE 0 #endif // @@ -158,7 +158,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_ep2_I_CACHE #ifndef CONF_USB_EP2_I_CACHE -#define CONF_USB_EP2_I_CACHE 0 +#define CONF_USB_EP2_I_CACHE 64 #endif // diff --git a/ports/atmel-samd/boards/arduino_zero/mpconfigboard.mk b/ports/atmel-samd/boards/arduino_zero/mpconfigboard.mk index 345c9a2a50..b24cc9c647 100644 --- a/ports/atmel-samd/boards/arduino_zero/mpconfigboard.mk +++ b/ports/atmel-samd/boards/arduino_zero/mpconfigboard.mk @@ -1,6 +1,8 @@ LD_FILE = boards/samd21x18-bootloader.ld USB_VID = 0x2341 USB_PID = 0x824D +USB_PRODUCT = "Arduino Zero" +USB_MANUFACTURER = "Arduino" INTERNAL_FLASH_FILESYSTEM = 1 diff --git a/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.mk b/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.mk index b2922fb066..1831f9337c 100644 --- a/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.mk @@ -1,6 +1,8 @@ LD_FILE = boards/samd21x18-bootloader-crystalless.ld USB_VID = 0x239A USB_PID = 0x8019 +USB_PRODUCT = "CircuitPlayground Express" +USB_MANUFACTURER = "Adafruit Industries LLC" #SPI_FLASH_FILESYSTEM = 1 INTERNAL_FLASH_FILESYSTEM = 1 diff --git a/ports/atmel-samd/boards/feather_m0_adalogger/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m0_adalogger/mpconfigboard.mk index 7580ed66f3..debd3d775c 100644 --- a/ports/atmel-samd/boards/feather_m0_adalogger/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m0_adalogger/mpconfigboard.mk @@ -1,6 +1,8 @@ LD_FILE = boards/samd21x18-bootloader.ld USB_VID = 0x239A USB_PID = 0x8015 +USB_PRODUCT = "Feather M0 Adalogger" +USB_MANUFACTURER = "Adafruit Industries LLC" INTERNAL_FLASH_FILESYSTEM = 1 diff --git a/ports/atmel-samd/boards/feather_m0_basic/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m0_basic/mpconfigboard.mk index 7580ed66f3..641611176f 100644 --- a/ports/atmel-samd/boards/feather_m0_basic/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m0_basic/mpconfigboard.mk @@ -1,6 +1,8 @@ LD_FILE = boards/samd21x18-bootloader.ld USB_VID = 0x239A USB_PID = 0x8015 +USB_PRODUCT = "Feather M0" +USB_MANUFACTURER = "Adafruit Industries LLC" INTERNAL_FLASH_FILESYSTEM = 1 diff --git a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk index b677f4e515..46c58d04a3 100644 --- a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk @@ -1,6 +1,8 @@ LD_FILE = boards/samd21x18-bootloader.ld USB_VID = 0x239A USB_PID = 0x801b +USB_PRODUCT = "Feather M0 Express" +USB_MANUFACTURER = "Adafruit Industries LLC" #SPI_FLASH_FILESYSTEM = 1 INTERNAL_FLASH_FILESYSTEM = 1 diff --git a/ports/atmel-samd/boards/gemma_m0/mpconfigboard.mk b/ports/atmel-samd/boards/gemma_m0/mpconfigboard.mk index de1c7c19af..a406e8f58f 100644 --- a/ports/atmel-samd/boards/gemma_m0/mpconfigboard.mk +++ b/ports/atmel-samd/boards/gemma_m0/mpconfigboard.mk @@ -1,6 +1,8 @@ LD_FILE = boards/samd21x18-bootloader.ld USB_VID = 0x239A USB_PID = 0x801D +USB_PRODUCT = "Gemma M0" +USB_MANUFACTURER = "Adafruit Industries LLC" INTERNAL_FLASH_FILESYSTEM = 1 diff --git a/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk b/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk index 2845e6bf29..7d63321af9 100644 --- a/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk @@ -1,6 +1,8 @@ LD_FILE = boards/samd21x18-bootloader.ld USB_VID = 0x239A USB_PID = 0x8014 +USB_PRODUCT = "Metro M0 Express" +USB_MANUFACTURER = "Adafruit Industries LLC" #SPI_FLASH_FILESYSTEM = 1 INTERNAL_FLASH_FILESYSTEM = 1 diff --git a/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.mk b/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.mk index d8ec94fdf7..194116545c 100644 --- a/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.mk @@ -1,6 +1,8 @@ LD_FILE = boards/samd51x19-bootloader.ld USB_VID = 0x239A USB_PID = 0x8021 +USB_PRODUCT = "Metro M4 Express" +USB_MANUFACTURER = "Adafruit Industries LLC" #SPI_FLASH_FILESYSTEM = 1 INTERNAL_FLASH_FILESYSTEM = 1 diff --git a/ports/atmel-samd/boards/trinket_m0/mpconfigboard.mk b/ports/atmel-samd/boards/trinket_m0/mpconfigboard.mk index 1e3da53321..43d07eb5ff 100644 --- a/ports/atmel-samd/boards/trinket_m0/mpconfigboard.mk +++ b/ports/atmel-samd/boards/trinket_m0/mpconfigboard.mk @@ -1,6 +1,8 @@ LD_FILE = boards/samd21x18-bootloader.ld USB_VID = 0x239A USB_PID = 0x801F +USB_PRODUCT = "Trinket M0" +USB_MANUFACTURER = "Adafruit Industries LLC" INTERNAL_FLASH_FILESYSTEM = 1 diff --git a/ports/atmel-samd/boards/trinket_m0_haxpress/mpconfigboard.mk b/ports/atmel-samd/boards/trinket_m0_haxpress/mpconfigboard.mk index b893d94e4c..04dc9e4015 100644 --- a/ports/atmel-samd/boards/trinket_m0_haxpress/mpconfigboard.mk +++ b/ports/atmel-samd/boards/trinket_m0_haxpress/mpconfigboard.mk @@ -1,6 +1,8 @@ LD_FILE = boards/samd21x18-bootloader-external-flash-crystalless.ld USB_VID = 0x239A USB_PID = 0x801F +USB_PRODUCT="Trinket M0 Haxpress" +USB_MANUFACTURER="Radomir Dopieralski" #SPI_FLASH_FILESYSTEM = 1 INTERNAL_FLASH_FILESYSTEM = 1 diff --git a/ports/atmel-samd/common-hal/storage/__init__.c b/ports/atmel-samd/common-hal/storage/__init__.c index b7c4fa6dd6..184184b8b3 100644 --- a/ports/atmel-samd/common-hal/storage/__init__.c +++ b/ports/atmel-samd/common-hal/storage/__init__.c @@ -42,5 +42,5 @@ void common_hal_storage_remount(const char* mount_path, bool readonly) { mp_raise_RuntimeError("Cannot remount '/' when USB is active."); } - flash_set_usb_writeable(readonly); + flash_set_usb_writable(readonly); } diff --git a/ports/atmel-samd/flash_api.c b/ports/atmel-samd/flash_api.c index 7c032a2bec..b8a2127188 100644 --- a/ports/atmel-samd/flash_api.c +++ b/ports/atmel-samd/flash_api.c @@ -30,7 +30,7 @@ #define VFS_INDEX 0 -void flash_set_usb_writeable(bool usb_writeable) { +void 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) { @@ -43,9 +43,9 @@ void flash_set_usb_writeable(bool usb_writeable) { } fs_user_mount_t *vfs = (fs_user_mount_t *) current_mount->obj; - if (usb_writeable) { - vfs->flags |= FSUSER_USB_WRITEABLE; + if (usb_writable) { + vfs->flags |= FSUSER_USB_WRITABLE; } else { - vfs->flags &= ~FSUSER_USB_WRITEABLE; + vfs->flags &= ~FSUSER_USB_WRITABLE; } } diff --git a/ports/atmel-samd/flash_api.h b/ports/atmel-samd/flash_api.h index 5017891afc..3e41f59a24 100644 --- a/ports/atmel-samd/flash_api.h +++ b/ports/atmel-samd/flash_api.h @@ -31,6 +31,6 @@ extern void flash_init_vfs(fs_user_mount_t *vfs); extern void flash_flush(void); -void flash_set_usb_writeable(bool usb_writeable); +void flash_set_usb_writable(bool usb_writable); #endif // MICROPY_INCLUDED_ATMEL_SAMD_FLASH_API_H diff --git a/ports/atmel-samd/supervisor/filesystem.c b/ports/atmel-samd/supervisor/filesystem.c index f189ed8b99..45dc5667be 100644 --- a/ports/atmel-samd/supervisor/filesystem.c +++ b/ports/atmel-samd/supervisor/filesystem.c @@ -85,8 +85,8 @@ void filesystem_flush(void) { flash_flush(); } -void filesystem_default_writeable(bool writeable) { - flash_set_usb_writeable(writeable); +void filesystem_writable_by_python(bool writable) { + flash_set_usb_writable(!writable); } bool filesystem_present(void) { diff --git a/ports/atmel-samd/supervisor/serial.c b/ports/atmel-samd/supervisor/serial.c index 67f06baa4f..0410c74176 100644 --- a/ports/atmel-samd/supervisor/serial.c +++ b/ports/atmel-samd/supervisor/serial.c @@ -29,25 +29,33 @@ #include "supervisor/serial.h" #include "usb.h" +#include "tools/autogen_usb_descriptor.h" -// Serial number as hex characters. -// char serial_number[USB_DEVICE_GET_SERIAL_NAME_LENGTH]; -// void load_serial_number(void) { -// char nibble_to_hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', -// 'B', 'C', 'D', 'E', 'F'}; -// uint32_t* addresses[4] = {(uint32_t *) 0x0080A00C, (uint32_t *) 0x0080A040, -// (uint32_t *) 0x0080A044, (uint32_t *) 0x0080A048}; -// for (int i = 0; i < 4; i++) { -// for (int j = 0; j < 8; j++) { -// uint8_t nibble = (*(addresses[i]) >> j * 4) & 0xf; -// serial_number[i * 8 + j] = nibble_to_hex[nibble]; -// } -// } -// } - -// SAMD51 addresses: 0x008061FC, 0x00806010, 0x00806014, 0x00806018 +// Serial number as hex characters. This writes directly to the USB +// descriptor. +void load_serial_number(void) { + char nibble_to_hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F'}; + #ifdef SAMD21 + uint32_t* addresses[4] = {(uint32_t *) 0x0080A00C, (uint32_t *) 0x0080A040, + (uint32_t *) 0x0080A044, (uint32_t *) 0x0080A048}; + #endif + #ifdef SAMD51 + uint32_t* addresses[4] = {(uint32_t *) 0x008061FC, (uint32_t *) 0x00806010, + (uint32_t *) 0x00806014, (uint32_t *) 0x00806018}; + #endif + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 8; j++) { + uint8_t nibble = (*(addresses[i]) >> j * 4) & 0xf; + // Strings are UTF-16-LE encoded. + serial_number[i * 16 + j * 2] = nibble_to_hex[nibble]; + serial_number[i * 16 + j * 2 + 1] = 0; + } + } +} void serial_init(void) { + load_serial_number(); init_usb(); } diff --git a/ports/atmel-samd/tools/autogen_usb_descriptor.h b/ports/atmel-samd/tools/autogen_usb_descriptor.h new file mode 100644 index 0000000000..00937d42d7 --- /dev/null +++ b/ports/atmel-samd/tools/autogen_usb_descriptor.h @@ -0,0 +1,36 @@ +/* + * This file is part of the Micro Python 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. + */ + +#ifndef __MICROPY_INCLUDED_ATMEL_SAMD_TOOLS_AUTOGEN_USB_DESCRIPTOR_H__ +#define __MICROPY_INCLUDED_ATMEL_SAMD_TOOLS_AUTOGEN_USB_DESCRIPTOR_H__ + +#include "usb/device/usbdc.h" + +struct usbd_descriptors descriptor_bounds; +uint8_t* serial_number; +uint8_t serial_number_length; + +#endif // __MICROPY_INCLUDED_ATMEL_SAMD_TOOLS_AUTOGEN_USB_DESCRIPTOR_H__ diff --git a/ports/atmel-samd/tools/gen_usb_descriptor.py b/ports/atmel-samd/tools/gen_usb_descriptor.py new file mode 100644 index 0000000000..2a2b217fd8 --- /dev/null +++ b/ports/atmel-samd/tools/gen_usb_descriptor.py @@ -0,0 +1,150 @@ +import argparse + +import os +import sys + +# path hacking +sys.path.append("../../tools") + +from usb_descriptor import core +from usb_descriptor import cdc + +parser = argparse.ArgumentParser(description='Generate USB descriptors.') +parser.add_argument('--manufacturer', type=str, + help='manufacturer of the device') +parser.add_argument('--product', type=str, + help='product name of the device') +parser.add_argument('--vid', type=lambda x: int(x, 16), + help='vendor id') +parser.add_argument('--pid', type=lambda x: int(x, 16), + help='product id') +parser.add_argument('--serial_number_length', type=int, default=32, + help='length needed for the serial number in digits') +parser.add_argument('output_file', type=argparse.FileType('w')) + +args = parser.parse_args() + +langid = core.StringDescriptor("\u0409") +manufacturer = core.StringDescriptor(args.manufacturer) +product = core.StringDescriptor(args.product) +serial_number = core.StringDescriptor("serial number. you should fill in a unique serial number here."[:args.serial_number_length]) +strings = [langid, manufacturer, product, serial_number] + +# vid = 0x239A +# pid = 0x8021 + +device = core.DeviceDescriptor( + idVendor=args.vid, + idProduct=args.pid, + iManufacturer=strings.index(manufacturer), + iProduct=strings.index(product), + iSerialNumber=strings.index(serial_number)) + +# Interface numbers are interface set local and endpoints are interface local +# until core.join_interfaces renumbers them. +cdc_interfaces = [ + core.InterfaceDescriptor( + bInterfaceClass=0x2, # Communications Device Class + bInterfaceSubClass=0x02, # Abstract control model + bInterfaceProtocol=0x01, # Common AT Commands + subdescriptors=[ + # Working 2.x + # radix: hexadecimal + # 05 24 00 10 01 header + # 05 24 01 03 01 call manage + # 04 24 02 06 acm + # 05 24 06 00 01 union + cdc.Header(bcdCDC=0x0110), + cdc.CallManagement(bmCapabilities=0x03, bDataInterface=0x01), + cdc.AbstractControlManagement(bmCapabilities=0x02), + cdc.Union(bMasterInterface=0x00, + bSlaveInterface=[0x01]), + core.EndpointDescriptor( + bEndpointAddress=0x0 | core.EndpointDescriptor.DIRECTION_IN, + bmAttributes=core.EndpointDescriptor.TYPE_INTERRUPT, + wMaxPacketSize=0x8, + bInterval=10) + ] + ), + core.InterfaceDescriptor( + bInterfaceClass=0x0a, + subdescriptors=[ + core.EndpointDescriptor( + bEndpointAddress=0x0 | core.EndpointDescriptor.DIRECTION_IN, + bmAttributes=core.EndpointDescriptor.TYPE_BULK), + core.EndpointDescriptor( + bEndpointAddress=0x0 | core.EndpointDescriptor.DIRECTION_OUT, + bmAttributes=core.EndpointDescriptor.TYPE_BULK) + ] + ) +] + +msc_interfaces = [ + core.InterfaceDescriptor( + bInterfaceClass=0x08, + bInterfaceSubClass=0x06, + bInterfaceProtocol=0x50, + subdescriptors=[ + core.EndpointDescriptor( + bEndpointAddress=0x0 | core.EndpointDescriptor.DIRECTION_IN, + bmAttributes=core.EndpointDescriptor.TYPE_BULK), + core.EndpointDescriptor( + bEndpointAddress=0x1 | core.EndpointDescriptor.DIRECTION_OUT, + bmAttributes=core.EndpointDescriptor.TYPE_BULK) + ] + ) +] + +interfaces = core.join_interfaces(cdc_interfaces, msc_interfaces) + +cdc_function = core.InterfaceAssociationDescriptor( + bFirstInterface=interfaces.index(cdc_interfaces[0]), + bInterfaceCount=len(cdc_interfaces), + bFunctionClass=0x2, # Communications Device Class + bFunctionSubClass=0x2, # Abstract control model + bFunctionProtocol=0x1) # Common AT Commands + +configuration = core.ConfigurationDescriptor( + wTotalLength=(core.ConfigurationDescriptor.bLength + + cdc_function.bLength + + sum([len(bytes(x)) for x in interfaces])), + bNumInterfaces=len(interfaces)) + +descriptor_list = [device, configuration, cdc_function] +descriptor_list.extend(interfaces) +descriptor_list.extend(strings) + +output_file = args.output_file + +output_file.write("#include \n\n") +output_file.write("#include \"tools/autogen_usb_descriptor.h\"\n\n") +output_file.write("uint8_t usb_descriptors[] = {\n") +descriptor_length = 0 +serial_number_offset = None +for descriptor in descriptor_list: + output_file.write("// " + str(descriptor) + "\n") + b = bytes(descriptor) + i = 0 + if descriptor == serial_number: + # Add two for the length and descriptor type bytes. + serial_number_offset = descriptor_length + 2 + # This prints each subdescriptor on a separate line. + while i < len(b): + length = b[i] + for j in range(length): + output_file.write("0x{:02x}, ".format(b[i + j])) + output_file.write("\n") + i += length + descriptor_length += length + + output_file.write("\n") +output_file.write("};\n\n") +output_file.write("struct usbd_descriptors descriptor_bounds = " + + "{usb_descriptors," + + " usb_descriptors + sizeof(usb_descriptors)};\n") +output_file.write("uint8_t* serial_number = usb_descriptors + " + + str(serial_number_offset) + ";\n") +output_file.write("uint8_t serial_number_length = " + + str(args.serial_number_length) + ";\n") + +output_file.close() diff --git a/ports/atmel-samd/usb.c b/ports/atmel-samd/usb.c index bd712a5d2c..993ac7eaad 100644 --- a/ports/atmel-samd/usb.c +++ b/ports/atmel-samd/usb.c @@ -44,6 +44,7 @@ #include "hpl/gclk/hpl_gclk_base.h" #include "lib/utils/interrupt_char.h" +#include "tools/autogen_usb_descriptor.h" #include "reset.h" #include "usb_mass_storage.h" @@ -67,13 +68,6 @@ volatile uint8_t usb_rx_count = 0; volatile bool mp_cdc_enabled = false; volatile bool usb_transmitting = false; -static uint8_t multi_desc_bytes[] = { - /* Device descriptors and Configuration descriptors list. */ - COMPOSITE_DESCES_LS_FS, -}; - -static struct usbd_descriptors multi_desc = {multi_desc_bytes, multi_desc_bytes + sizeof(multi_desc_bytes)}; - /** Ctrl endpoint buffer */ COMPILER_ALIGNED(4) static uint8_t ctrl_buffer[64]; @@ -221,8 +215,10 @@ void init_usb(void) { mscdf_register_callback(MSCDF_CB_EJECT_DISK, (FUNC_PTR)usb_msc_disk_eject); mscdf_register_callback(MSCDF_CB_TEST_DISK_READY, (FUNC_PTR)usb_msc_disk_is_ready); mscdf_register_callback(MSCDF_CB_XFER_BLOCKS_DONE, (FUNC_PTR)usb_msc_xfer_done); + mscdf_register_callback(MSCDF_CB_IS_WRITABLE, (FUNC_PTR)usb_msc_disk_is_writable); + + usbdc_start(&descriptor_bounds); - usbdc_start(&multi_desc); usbdc_attach(); } diff --git a/ports/atmel-samd/usb_mass_storage.c b/ports/atmel-samd/usb_mass_storage.c index 6b9a22d253..3e96c37877 100644 --- a/ports/atmel-samd/usb_mass_storage.c +++ b/ports/atmel-samd/usb_mass_storage.c @@ -86,6 +86,28 @@ int32_t usb_msc_disk_eject(uint8_t lun) { return ERR_NONE; } +/** + * \brief Inquiry whether Disk is writable. ERR_DENIED if it is not writable. + * ERR_NONE if it is. ERR_NOT_FOUND if its missing. + * \param[in] lun logic unit number + * \return Operation status. + */ +int32_t usb_msc_disk_is_writable(uint8_t lun) { + if (lun > 1) { + return ERR_NOT_FOUND; + } + + fs_user_mount_t* vfs = get_vfs(lun); + if (vfs == NULL) { + return ERR_NOT_FOUND; + } + if (vfs->writeblocks[0] == MP_OBJ_NULL || + (vfs->flags & FSUSER_USB_WRITABLE) == 0) { + return ERR_DENIED; + } + return ERR_NONE; +} + /** * \brief Inquiry whether Disk is ready * \param[in] lun logic unit number @@ -178,7 +200,7 @@ COMPILER_ALIGNED(4) uint8_t sector_buffer[512]; */ int32_t usb_msc_new_read(uint8_t lun, uint32_t addr, uint32_t nblocks) { if (lun > 1) { - return ERR_DENIED; + return ERR_NOT_FOUND; } // Store transfer info so we can service it in the "background". @@ -199,13 +221,16 @@ int32_t usb_msc_new_read(uint8_t lun, uint32_t addr, uint32_t nblocks) { */ int32_t usb_msc_new_write(uint8_t lun, uint32_t addr, uint32_t nblocks) { if (lun > 1) { - return ERR_DENIED; + return ERR_NOT_FOUND; } fs_user_mount_t * vfs = get_vfs(lun); // This is used to determine the writeability of the disk from USB. - if (vfs == NULL || vfs->writeblocks[0] == MP_OBJ_NULL /*|| - (vfs->flags & FSUSER_USB_WRITEABLE) == 0*/) { + if (vfs == NULL) { + return ERR_NOT_FOUND; + } + if (vfs->writeblocks[0] == MP_OBJ_NULL || + (vfs->flags & FSUSER_USB_WRITABLE) == 0) { return ERR_DENIED; } diff --git a/ports/atmel-samd/usb_mass_storage.h b/ports/atmel-samd/usb_mass_storage.h index d8906a16f2..767a6779ca 100644 --- a/ports/atmel-samd/usb_mass_storage.h +++ b/ports/atmel-samd/usb_mass_storage.h @@ -37,6 +37,7 @@ void usb_msc_background(void); // Callbacks that hook into ASF4's USB stack. int32_t usb_msc_disk_eject(uint8_t lun); +int32_t usb_msc_disk_is_writable(uint8_t lun); int32_t usb_msc_disk_is_ready(uint8_t lun); int32_t usb_msc_new_read(uint8_t lun, uint32_t addr, uint32_t nblocks); int32_t usb_msc_new_write(uint8_t lun, uint32_t addr, uint32_t nblocks); diff --git a/supervisor/filesystem.h b/supervisor/filesystem.h index fd2bf0364a..cfd1ed0273 100644 --- a/supervisor/filesystem.h +++ b/supervisor/filesystem.h @@ -31,7 +31,7 @@ void filesystem_init(bool create_allowed); void filesystem_flush(void); -void filesystem_default_writeable(bool writeable); +void filesystem_writable_by_python(bool writable); bool filesystem_present(void); #endif // MICROPY_INCLUDED_SUPERVISOR_FILESYSTEM_H diff --git a/supervisor/stub/filesystem.c b/supervisor/stub/filesystem.c index 462ee62a4e..12fe9fb109 100644 --- a/supervisor/stub/filesystem.c +++ b/supervisor/stub/filesystem.c @@ -32,7 +32,7 @@ void filesystem_init(void) { void filesystem_flush(void) { } -void filesystem_default_writeable(bool writeable) { +void filesystem_writable_by_python(bool writable) { } bool filesystem_present(void) { diff --git a/tools/usb_descriptor/cdc.py b/tools/usb_descriptor/cdc.py new file mode 100644 index 0000000000..4042e76736 --- /dev/null +++ b/tools/usb_descriptor/cdc.py @@ -0,0 +1,53 @@ +from . import core +import struct + +class FunctionalDescriptor(core.Descriptor): + bDescriptorType = 0x24 + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fmt = " 1: + raise TypeError("Only one arg or keyword args expected.") + elif len(kwargs) == 0: + raise TypeError("Only one arg or keyword args expected.") + + self.data = [] + for field, _, default in self.fields: + if field in kwargs: + self.data.append(kwargs[field]) + elif default is not None: + self.data.append(default) + else: + raise ValueError("Missing {} argument.".format(field)) + + def __bytes__(self): + return struct.pack(self.fmt, self.bLength, self.bDescriptorType, *self.data) + + @property + def bDescriptorType(self): + return self._bDescriptorType + +class EndpointDescriptor(Descriptor): + fields = [('bEndpointAddress', "B", None), + ('bmAttributes', "B", None), + ('wMaxPacketSize', "H", 0x40), + ('bInterval', "B", 0)] + + bLength = 0x07 + bDescriptorType = 0x5 + + TYPE_CONTROL = 0b00 + TYPE_ISOCHRONOUS = 0b01 + TYPE_BULK = 0b10 + TYPE_INTERRUPT = 0b11 + + DIRECTION_IN = 0x80 + DIRECTION_OUT = 0x00 + + @property + def bEndpointAddress(self): + return self.data[0] + + @bEndpointAddress.setter + def bEndpointAddress(self, value): + self.data[0] = value + + +class InterfaceDescriptor(Descriptor): + fields = [('bInterfaceNumber', "B", 0), + ('bAlternateSetting', "B", 0), + ('bNumEndpoints', "B", 0), + ('bInterfaceClass', "B", None), + ('bInterfaceSubClass', "B", 0), + ('bInterfaceProtocol', "B", 0), + ('iInterface', "B", 0)] + + bLength = 0x09 + bDescriptorType = 0x4 + + def __init__(self, *args, **kwargs): + self.subdescriptors = [] + if "subdescriptors" in kwargs: + self.subdescriptors = kwargs["subdescriptors"] + super().__init__(*args, **kwargs) + + def __bytes__(self): + endpoint_count = 0 + subdescriptor_bytes = [] + for desc in self.subdescriptors: + subdescriptor_bytes.append(bytes(desc)) + if desc.bDescriptorType == EndpointDescriptor.bDescriptorType: + endpoint_count += 1 + subdescriptor_bytes = b"".join(subdescriptor_bytes) + self.data[2] = endpoint_count + return super().__bytes__() + subdescriptor_bytes + + @property + def bInterfaceNumber(self): + return self.data[0] + + @bInterfaceNumber.setter + def bInterfaceNumber(self, value): + self.data[0] = value + +class InterfaceAssociationDescriptor(Descriptor): + fields = [('bFirstInterface', "B", None), + ('bInterfaceCount', "B", None), + ('bFunctionClass', "B", None), + ('bFunctionSubClass', "B", None), + ('bFunctionProtocol', "B", None), + ('iFunction', "B", 0)] + + bLength = 0x08 + bDescriptorType = 0xB + + +class ConfigurationDescriptor(Descriptor): + fields = [('wTotalLength', "H", None), + ('bNumInterfaces', "B", None), + ('bConfigurationValue', "B", 0x1), + ('iConfiguration', "B", 0), + # bus powered (bit 6), no remote wakeup (bit 5), bit 7 is always 1 and 0-4 are always 0 + ('bmAttributes', "B", 0x80), + # 100 mA by default + ('bMaxPower', "B", 50)] + + bLength = 0x09 + bDescriptorType = 0x2 + +class DeviceDescriptor(Descriptor): + fields = [('bcdUSB', "H", 0x200), + ('bDeviceClass', "B", 0xef), + ('bDeviceSubClass', "B", 0x02), + ('bDeviceProtocol', "B", 0x01), + ('bMaxPacketSize0', "B", 0x40), + ('idVendor', "H", None), + ('idProduct', "H", None), + ('bcdDevice', "H", 0x100), + ('iManufacturer', "B", None), + ('iProduct', "B", None), + ('iSerialNumber', "B", None), + ('bNumConfigurations', "B", 1)] + + bLength = 0x12 + bDescriptorType = 0x1 + +class StringDescriptor: + def __init__(self, value): + if type(value) == str: + self._bString = value.encode("utf-16-le") + self._bLength = len(self._bString) + 2 + elif len(value) > 1: + self._bLength = value[0] + if value[1] != 3: + raise ValueError("Sequence not a StringDescriptor") + self._bString = value[2:2+self.bLength] + + def __bytes__(self): + return struct.pack("BB{}s".format(len(self._bString)), self.bLength, self.bDescriptorType, self._bString) + + @property + def bString(self): + return self._bString.decode("utf-16-le") + + @bString.setter + def bString(self, value): + self._bString = value.encode("utf-16-le") + self._bLength = len(self.encoded) + 2 + + @property + def bDescriptorType(self): + return 3 + + @property + def bLength(self): + return self._bLength