Merge pull request #385 from tannewt/usb_write_protection
Usb write protection + fixes
This commit is contained in:
commit
123c2262e9
@ -35,8 +35,8 @@
|
|||||||
#define FSUSER_NATIVE (0x0001) // readblocks[2]/writeblocks[2] contain native func
|
#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_FREE_OBJ (0x0002) // fs_user_mount_t obj should be freed on umount
|
||||||
#define FSUSER_HAVE_IOCTL (0x0004) // new protocol with ioctl
|
#define FSUSER_HAVE_IOCTL (0x0004) // new protocol with ioctl
|
||||||
// Device is write-able over USB and read-only to MicroPython.
|
// Device is writable over USB and read-only to MicroPython.
|
||||||
#define FSUSER_USB_WRITEABLE (0x0008)
|
#define FSUSER_USB_WRITABLE (0x0008)
|
||||||
|
|
||||||
typedef struct _fs_user_mount_t {
|
typedef struct _fs_user_mount_t {
|
||||||
mp_obj_base_t base;
|
mp_obj_base_t base;
|
||||||
|
@ -98,9 +98,9 @@ DSTATUS disk_status (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This is used to determine the writeability of the disk from MicroPython.
|
// 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 ||
|
if (vfs->writeblocks[0] == MP_OBJ_NULL ||
|
||||||
(vfs->flags & FSUSER_USB_WRITEABLE) != 0) {
|
(vfs->flags & FSUSER_USB_WRITABLE) != 0) {
|
||||||
return STA_PROTECT;
|
return STA_PROTECT;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
|
8
main.c
8
main.c
@ -234,8 +234,8 @@ int __attribute__((used)) main(void) {
|
|||||||
autoreload_enable();
|
autoreload_enable();
|
||||||
|
|
||||||
// By default our internal flash is readonly to local python code and
|
// 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.
|
// writable over USB. Set it here so that boot.py can change it.
|
||||||
filesystem_default_writeable(false);
|
filesystem_writable_by_python(false);
|
||||||
|
|
||||||
// If not in safe mode, run boot before initing USB and capture output in a
|
// If not in safe mode, run boot before initing USB and capture output in a
|
||||||
// file.
|
// file.
|
||||||
@ -244,12 +244,12 @@ int __attribute__((used)) main(void) {
|
|||||||
#ifdef CIRCUITPY_BOOT_OUTPUT_FILE
|
#ifdef CIRCUITPY_BOOT_OUTPUT_FILE
|
||||||
// Since USB isn't up yet we can cheat and let ourselves write the boot
|
// Since USB isn't up yet we can cheat and let ourselves write the boot
|
||||||
// output file.
|
// output file.
|
||||||
filesystem_default_writeable(true);
|
filesystem_writable_by_python(true);
|
||||||
FIL file_pointer;
|
FIL file_pointer;
|
||||||
boot_output_file = &file_pointer;
|
boot_output_file = &file_pointer;
|
||||||
f_open(&((fs_user_mount_t *) MP_STATE_VM(vfs_mount_table)->obj)->fatfs,
|
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);
|
boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_WRITE | FA_CREATE_ALWAYS);
|
||||||
filesystem_default_writeable(false);
|
filesystem_writable_by_python(false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// TODO(tannewt): Re-add support for flashing boot error output.
|
// TODO(tannewt): Re-add support for flashing boot error output.
|
||||||
|
@ -73,8 +73,6 @@ BASE_CFLAGS = \
|
|||||||
-Wcast-align \
|
-Wcast-align \
|
||||||
-Wno-error=lto-type-mismatch \
|
-Wno-error=lto-type-mismatch \
|
||||||
-D__$(CHIP_VARIANT)__ \
|
-D__$(CHIP_VARIANT)__ \
|
||||||
-DCONF_USB_COMPOSITE_IDPRODUCT=$(USB_PID) \
|
|
||||||
-DCONF_USB_COMPOSITE_IDVENDER=$(USB_VID) \
|
|
||||||
-ffunction-sections \
|
-ffunction-sections \
|
||||||
-fdata-sections \
|
-fdata-sections \
|
||||||
-fshort-enums \
|
-fshort-enums \
|
||||||
@ -233,6 +231,7 @@ SRC_C = \
|
|||||||
lib/utils/sys_stdio_mphal.c \
|
lib/utils/sys_stdio_mphal.c \
|
||||||
lib/libc/string0.c \
|
lib/libc/string0.c \
|
||||||
lib/mp-readline/readline.c \
|
lib/mp-readline/readline.c \
|
||||||
|
$(BUILD)/autogen_usb_descriptor.c \
|
||||||
# freetouch/adafruit_ptc.c
|
# freetouch/adafruit_ptc.c
|
||||||
|
|
||||||
# Choose which flash filesystem impl to use.
|
# Choose which flash filesystem impl to use.
|
||||||
@ -359,6 +358,14 @@ $(BUILD)/firmware.uf2: $(BUILD)/firmware.bin
|
|||||||
$(ECHO) "Create $@"
|
$(ECHO) "Create $@"
|
||||||
python2 $(TOP)/tools/uf2/utils/uf2conv.py -b $(BOOTLOADER_SIZE) -c -o $@ $^
|
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
|
deploy: $(BUILD)/firmware.bin
|
||||||
$(ECHO) "Writing $< to the board"
|
$(ECHO) "Writing $< to the board"
|
||||||
$(BOSSAC) -u $<
|
$(BOSSAC) -u $<
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit e1d02fde5d23a704d33d74e18fcf624eb56132c5
|
Subproject commit 1ca9e1f86921d99f0fbc907698f864a60caf675a
|
@ -39,7 +39,7 @@
|
|||||||
// <CONF_USB_D_N_EP_MAX"> Max possible (by "Max Endpoint Number" config)
|
// <CONF_USB_D_N_EP_MAX"> Max possible (by "Max Endpoint Number" config)
|
||||||
// <id> usbd_num_ep_sp
|
// <id> usbd_num_ep_sp
|
||||||
#ifndef CONF_USB_D_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
|
#endif
|
||||||
|
|
||||||
// </h>
|
// </h>
|
||||||
@ -118,7 +118,7 @@
|
|||||||
// <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP)
|
// <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP)
|
||||||
// <id> usb_ep1_I_CACHE
|
// <id> usb_ep1_I_CACHE
|
||||||
#ifndef CONF_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
|
#endif
|
||||||
// </h>
|
// </h>
|
||||||
|
|
||||||
@ -176,7 +176,7 @@
|
|||||||
// <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP)
|
// <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP)
|
||||||
// <id> usb_arch_ep3_cache
|
// <id> usb_arch_ep3_cache
|
||||||
#ifndef CONF_USB_EP3_CACHE
|
#ifndef CONF_USB_EP3_CACHE
|
||||||
#define CONF_USB_EP3_CACHE 64
|
#define CONF_USB_EP3_CACHE 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// <o> Cache buffer size for EP3 IN
|
// <o> Cache buffer size for EP3 IN
|
||||||
@ -194,7 +194,7 @@
|
|||||||
// <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP)
|
// <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP)
|
||||||
// <id> usb_ep3_I_CACHE
|
// <id> usb_ep3_I_CACHE
|
||||||
#ifndef CONF_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
|
#endif
|
||||||
// </h>
|
// </h>
|
||||||
|
|
||||||
@ -252,7 +252,7 @@
|
|||||||
// <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP)
|
// <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP)
|
||||||
// <id> usb_arch_ep5_cache
|
// <id> usb_arch_ep5_cache
|
||||||
#ifndef CONF_USB_EP5_CACHE
|
#ifndef CONF_USB_EP5_CACHE
|
||||||
#define CONF_USB_EP5_CACHE 64
|
#define CONF_USB_EP5_CACHE 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// <o> Cache buffer size for EP5 IN
|
// <o> Cache buffer size for EP5 IN
|
||||||
@ -290,7 +290,7 @@
|
|||||||
// <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP)
|
// <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP)
|
||||||
// <id> usb_arch_ep6_cache
|
// <id> usb_arch_ep6_cache
|
||||||
#ifndef CONF_USB_EP6_CACHE
|
#ifndef CONF_USB_EP6_CACHE
|
||||||
#define CONF_USB_EP6_CACHE 64
|
#define CONF_USB_EP6_CACHE 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// <o> Cache buffer size for EP6 IN
|
// <o> Cache buffer size for EP6 IN
|
||||||
@ -308,7 +308,7 @@
|
|||||||
// <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP)
|
// <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP)
|
||||||
// <id> usb_ep6_I_CACHE
|
// <id> usb_ep6_I_CACHE
|
||||||
#ifndef CONF_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
|
#endif
|
||||||
// </h>
|
// </h>
|
||||||
|
|
||||||
@ -328,7 +328,7 @@
|
|||||||
// <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP)
|
// <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP)
|
||||||
// <id> usb_arch_ep7_cache
|
// <id> usb_arch_ep7_cache
|
||||||
#ifndef CONF_USB_EP7_CACHE
|
#ifndef CONF_USB_EP7_CACHE
|
||||||
#define CONF_USB_EP7_CACHE 64
|
#define CONF_USB_EP7_CACHE 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// <o> Cache buffer size for EP7 IN
|
// <o> Cache buffer size for EP7 IN
|
||||||
|
@ -120,7 +120,7 @@
|
|||||||
// <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP)
|
// <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP)
|
||||||
// <id> usb_ep1_I_CACHE
|
// <id> usb_ep1_I_CACHE
|
||||||
#ifndef CONF_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
|
#endif
|
||||||
// </h>
|
// </h>
|
||||||
|
|
||||||
@ -158,7 +158,7 @@
|
|||||||
// <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP)
|
// <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP)
|
||||||
// <id> usb_ep2_I_CACHE
|
// <id> usb_ep2_I_CACHE
|
||||||
#ifndef CONF_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
|
#endif
|
||||||
// </h>
|
// </h>
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
LD_FILE = boards/samd21x18-bootloader.ld
|
LD_FILE = boards/samd21x18-bootloader.ld
|
||||||
USB_VID = 0x2341
|
USB_VID = 0x2341
|
||||||
USB_PID = 0x824D
|
USB_PID = 0x824D
|
||||||
|
USB_PRODUCT = "Arduino Zero"
|
||||||
|
USB_MANUFACTURER = "Arduino"
|
||||||
|
|
||||||
INTERNAL_FLASH_FILESYSTEM = 1
|
INTERNAL_FLASH_FILESYSTEM = 1
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
LD_FILE = boards/samd21x18-bootloader-crystalless.ld
|
LD_FILE = boards/samd21x18-bootloader-crystalless.ld
|
||||||
USB_VID = 0x239A
|
USB_VID = 0x239A
|
||||||
USB_PID = 0x8019
|
USB_PID = 0x8019
|
||||||
|
USB_PRODUCT = "CircuitPlayground Express"
|
||||||
|
USB_MANUFACTURER = "Adafruit Industries LLC"
|
||||||
|
|
||||||
#SPI_FLASH_FILESYSTEM = 1
|
#SPI_FLASH_FILESYSTEM = 1
|
||||||
INTERNAL_FLASH_FILESYSTEM = 1
|
INTERNAL_FLASH_FILESYSTEM = 1
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
LD_FILE = boards/samd21x18-bootloader.ld
|
LD_FILE = boards/samd21x18-bootloader.ld
|
||||||
USB_VID = 0x239A
|
USB_VID = 0x239A
|
||||||
USB_PID = 0x8015
|
USB_PID = 0x8015
|
||||||
|
USB_PRODUCT = "Feather M0 Adalogger"
|
||||||
|
USB_MANUFACTURER = "Adafruit Industries LLC"
|
||||||
|
|
||||||
INTERNAL_FLASH_FILESYSTEM = 1
|
INTERNAL_FLASH_FILESYSTEM = 1
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
LD_FILE = boards/samd21x18-bootloader.ld
|
LD_FILE = boards/samd21x18-bootloader.ld
|
||||||
USB_VID = 0x239A
|
USB_VID = 0x239A
|
||||||
USB_PID = 0x8015
|
USB_PID = 0x8015
|
||||||
|
USB_PRODUCT = "Feather M0"
|
||||||
|
USB_MANUFACTURER = "Adafruit Industries LLC"
|
||||||
|
|
||||||
INTERNAL_FLASH_FILESYSTEM = 1
|
INTERNAL_FLASH_FILESYSTEM = 1
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
LD_FILE = boards/samd21x18-bootloader.ld
|
LD_FILE = boards/samd21x18-bootloader.ld
|
||||||
USB_VID = 0x239A
|
USB_VID = 0x239A
|
||||||
USB_PID = 0x801b
|
USB_PID = 0x801b
|
||||||
|
USB_PRODUCT = "Feather M0 Express"
|
||||||
|
USB_MANUFACTURER = "Adafruit Industries LLC"
|
||||||
|
|
||||||
#SPI_FLASH_FILESYSTEM = 1
|
#SPI_FLASH_FILESYSTEM = 1
|
||||||
INTERNAL_FLASH_FILESYSTEM = 1
|
INTERNAL_FLASH_FILESYSTEM = 1
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
LD_FILE = boards/samd21x18-bootloader.ld
|
LD_FILE = boards/samd21x18-bootloader.ld
|
||||||
USB_VID = 0x239A
|
USB_VID = 0x239A
|
||||||
USB_PID = 0x801D
|
USB_PID = 0x801D
|
||||||
|
USB_PRODUCT = "Gemma M0"
|
||||||
|
USB_MANUFACTURER = "Adafruit Industries LLC"
|
||||||
|
|
||||||
INTERNAL_FLASH_FILESYSTEM = 1
|
INTERNAL_FLASH_FILESYSTEM = 1
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
LD_FILE = boards/samd21x18-bootloader.ld
|
LD_FILE = boards/samd21x18-bootloader.ld
|
||||||
USB_VID = 0x239A
|
USB_VID = 0x239A
|
||||||
USB_PID = 0x8014
|
USB_PID = 0x8014
|
||||||
|
USB_PRODUCT = "Metro M0 Express"
|
||||||
|
USB_MANUFACTURER = "Adafruit Industries LLC"
|
||||||
|
|
||||||
#SPI_FLASH_FILESYSTEM = 1
|
#SPI_FLASH_FILESYSTEM = 1
|
||||||
INTERNAL_FLASH_FILESYSTEM = 1
|
INTERNAL_FLASH_FILESYSTEM = 1
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
LD_FILE = boards/samd51x19-bootloader.ld
|
LD_FILE = boards/samd51x19-bootloader.ld
|
||||||
USB_VID = 0x239A
|
USB_VID = 0x239A
|
||||||
USB_PID = 0x8021
|
USB_PID = 0x8021
|
||||||
|
USB_PRODUCT = "Metro M4 Express"
|
||||||
|
USB_MANUFACTURER = "Adafruit Industries LLC"
|
||||||
|
|
||||||
#SPI_FLASH_FILESYSTEM = 1
|
#SPI_FLASH_FILESYSTEM = 1
|
||||||
INTERNAL_FLASH_FILESYSTEM = 1
|
INTERNAL_FLASH_FILESYSTEM = 1
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
LD_FILE = boards/samd21x18-bootloader.ld
|
LD_FILE = boards/samd21x18-bootloader.ld
|
||||||
USB_VID = 0x239A
|
USB_VID = 0x239A
|
||||||
USB_PID = 0x801F
|
USB_PID = 0x801F
|
||||||
|
USB_PRODUCT = "Trinket M0"
|
||||||
|
USB_MANUFACTURER = "Adafruit Industries LLC"
|
||||||
|
|
||||||
INTERNAL_FLASH_FILESYSTEM = 1
|
INTERNAL_FLASH_FILESYSTEM = 1
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
LD_FILE = boards/samd21x18-bootloader-external-flash-crystalless.ld
|
LD_FILE = boards/samd21x18-bootloader-external-flash-crystalless.ld
|
||||||
USB_VID = 0x239A
|
USB_VID = 0x239A
|
||||||
USB_PID = 0x801F
|
USB_PID = 0x801F
|
||||||
|
USB_PRODUCT="Trinket M0 Haxpress"
|
||||||
|
USB_MANUFACTURER="Radomir Dopieralski"
|
||||||
|
|
||||||
#SPI_FLASH_FILESYSTEM = 1
|
#SPI_FLASH_FILESYSTEM = 1
|
||||||
INTERNAL_FLASH_FILESYSTEM = 1
|
INTERNAL_FLASH_FILESYSTEM = 1
|
||||||
|
@ -42,5 +42,5 @@ void common_hal_storage_remount(const char* mount_path, bool readonly) {
|
|||||||
mp_raise_RuntimeError("Cannot remount '/' when USB is active.");
|
mp_raise_RuntimeError("Cannot remount '/' when USB is active.");
|
||||||
}
|
}
|
||||||
|
|
||||||
flash_set_usb_writeable(readonly);
|
flash_set_usb_writable(readonly);
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
#define VFS_INDEX 0
|
#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);
|
mp_vfs_mount_t* current_mount = MP_STATE_VM(vfs_mount_table);
|
||||||
for (uint8_t i = 0; current_mount != NULL; i++) {
|
for (uint8_t i = 0; current_mount != NULL; i++) {
|
||||||
if (i == VFS_INDEX) {
|
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;
|
fs_user_mount_t *vfs = (fs_user_mount_t *) current_mount->obj;
|
||||||
|
|
||||||
if (usb_writeable) {
|
if (usb_writable) {
|
||||||
vfs->flags |= FSUSER_USB_WRITEABLE;
|
vfs->flags |= FSUSER_USB_WRITABLE;
|
||||||
} else {
|
} else {
|
||||||
vfs->flags &= ~FSUSER_USB_WRITEABLE;
|
vfs->flags &= ~FSUSER_USB_WRITABLE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,6 @@
|
|||||||
extern void flash_init_vfs(fs_user_mount_t *vfs);
|
extern void flash_init_vfs(fs_user_mount_t *vfs);
|
||||||
extern void flash_flush(void);
|
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
|
#endif // MICROPY_INCLUDED_ATMEL_SAMD_FLASH_API_H
|
||||||
|
@ -85,8 +85,8 @@ void filesystem_flush(void) {
|
|||||||
flash_flush();
|
flash_flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void filesystem_default_writeable(bool writeable) {
|
void filesystem_writable_by_python(bool writable) {
|
||||||
flash_set_usb_writeable(writeable);
|
flash_set_usb_writable(!writable);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool filesystem_present(void) {
|
bool filesystem_present(void) {
|
||||||
|
@ -29,25 +29,33 @@
|
|||||||
#include "supervisor/serial.h"
|
#include "supervisor/serial.h"
|
||||||
|
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
|
#include "tools/autogen_usb_descriptor.h"
|
||||||
|
|
||||||
// Serial number as hex characters.
|
// Serial number as hex characters. This writes directly to the USB
|
||||||
// char serial_number[USB_DEVICE_GET_SERIAL_NAME_LENGTH];
|
// descriptor.
|
||||||
// void load_serial_number(void) {
|
void load_serial_number(void) {
|
||||||
// char nibble_to_hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A',
|
char nibble_to_hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||||
// 'B', 'C', 'D', 'E', 'F'};
|
'A', 'B', 'C', 'D', 'E', 'F'};
|
||||||
// uint32_t* addresses[4] = {(uint32_t *) 0x0080A00C, (uint32_t *) 0x0080A040,
|
#ifdef SAMD21
|
||||||
// (uint32_t *) 0x0080A044, (uint32_t *) 0x0080A048};
|
uint32_t* addresses[4] = {(uint32_t *) 0x0080A00C, (uint32_t *) 0x0080A040,
|
||||||
// for (int i = 0; i < 4; i++) {
|
(uint32_t *) 0x0080A044, (uint32_t *) 0x0080A048};
|
||||||
// for (int j = 0; j < 8; j++) {
|
#endif
|
||||||
// uint8_t nibble = (*(addresses[i]) >> j * 4) & 0xf;
|
#ifdef SAMD51
|
||||||
// serial_number[i * 8 + j] = nibble_to_hex[nibble];
|
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++) {
|
||||||
// SAMD51 addresses: 0x008061FC, 0x00806010, 0x00806014, 0x00806018
|
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) {
|
void serial_init(void) {
|
||||||
|
load_serial_number();
|
||||||
init_usb();
|
init_usb();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
36
ports/atmel-samd/tools/autogen_usb_descriptor.h
Normal file
36
ports/atmel-samd/tools/autogen_usb_descriptor.h
Normal file
@ -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__
|
150
ports/atmel-samd/tools/gen_usb_descriptor.py
Normal file
150
ports/atmel-samd/tools/gen_usb_descriptor.py
Normal file
@ -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 <stdint.h>\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()
|
@ -44,6 +44,7 @@
|
|||||||
#include "hpl/gclk/hpl_gclk_base.h"
|
#include "hpl/gclk/hpl_gclk_base.h"
|
||||||
|
|
||||||
#include "lib/utils/interrupt_char.h"
|
#include "lib/utils/interrupt_char.h"
|
||||||
|
#include "tools/autogen_usb_descriptor.h"
|
||||||
#include "reset.h"
|
#include "reset.h"
|
||||||
#include "usb_mass_storage.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 mp_cdc_enabled = false;
|
||||||
volatile bool usb_transmitting = 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 */
|
/** Ctrl endpoint buffer */
|
||||||
COMPILER_ALIGNED(4) static uint8_t ctrl_buffer[64];
|
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_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_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_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();
|
usbdc_attach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,6 +86,28 @@ int32_t usb_msc_disk_eject(uint8_t lun) {
|
|||||||
return ERR_NONE;
|
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
|
* \brief Inquiry whether Disk is ready
|
||||||
* \param[in] lun logic unit number
|
* \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) {
|
int32_t usb_msc_new_read(uint8_t lun, uint32_t addr, uint32_t nblocks) {
|
||||||
if (lun > 1) {
|
if (lun > 1) {
|
||||||
return ERR_DENIED;
|
return ERR_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store transfer info so we can service it in the "background".
|
// 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) {
|
int32_t usb_msc_new_write(uint8_t lun, uint32_t addr, uint32_t nblocks) {
|
||||||
if (lun > 1) {
|
if (lun > 1) {
|
||||||
return ERR_DENIED;
|
return ERR_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
fs_user_mount_t * vfs = get_vfs(lun);
|
fs_user_mount_t * vfs = get_vfs(lun);
|
||||||
// This is used to determine the writeability of the disk from USB.
|
// This is used to determine the writeability of the disk from USB.
|
||||||
if (vfs == NULL || vfs->writeblocks[0] == MP_OBJ_NULL /*||
|
if (vfs == NULL) {
|
||||||
(vfs->flags & FSUSER_USB_WRITEABLE) == 0*/) {
|
return ERR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
if (vfs->writeblocks[0] == MP_OBJ_NULL ||
|
||||||
|
(vfs->flags & FSUSER_USB_WRITABLE) == 0) {
|
||||||
return ERR_DENIED;
|
return ERR_DENIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ void usb_msc_background(void);
|
|||||||
|
|
||||||
// Callbacks that hook into ASF4's USB stack.
|
// Callbacks that hook into ASF4's USB stack.
|
||||||
int32_t usb_msc_disk_eject(uint8_t lun);
|
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_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_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);
|
int32_t usb_msc_new_write(uint8_t lun, uint32_t addr, uint32_t nblocks);
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
void filesystem_init(bool create_allowed);
|
void filesystem_init(bool create_allowed);
|
||||||
void filesystem_flush(void);
|
void filesystem_flush(void);
|
||||||
void filesystem_default_writeable(bool writeable);
|
void filesystem_writable_by_python(bool writable);
|
||||||
bool filesystem_present(void);
|
bool filesystem_present(void);
|
||||||
|
|
||||||
#endif // MICROPY_INCLUDED_SUPERVISOR_FILESYSTEM_H
|
#endif // MICROPY_INCLUDED_SUPERVISOR_FILESYSTEM_H
|
||||||
|
@ -32,7 +32,7 @@ void filesystem_init(void) {
|
|||||||
void filesystem_flush(void) {
|
void filesystem_flush(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void filesystem_default_writeable(bool writeable) {
|
void filesystem_writable_by_python(bool writable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool filesystem_present(void) {
|
bool filesystem_present(void) {
|
||||||
|
53
tools/usb_descriptor/cdc.py
Normal file
53
tools/usb_descriptor/cdc.py
Normal file
@ -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 = "<BBB" + self.fmt[3:]
|
||||||
|
|
||||||
|
def __bytes__(self):
|
||||||
|
return struct.pack(self.fmt, self.bLength, self.bDescriptorType, self.bDescriptorSubtype, *self.data)
|
||||||
|
|
||||||
|
|
||||||
|
class Header(FunctionalDescriptor):
|
||||||
|
fields = [('bcdCDC', "H", None)]
|
||||||
|
bLength = 0x05
|
||||||
|
bDescriptorSubtype = 0x0
|
||||||
|
|
||||||
|
|
||||||
|
class CallManagement(FunctionalDescriptor):
|
||||||
|
fields = [('bmCapabilities', "b", None),
|
||||||
|
('bDataInterface', "b", None)]
|
||||||
|
bLength = 0x05
|
||||||
|
bDescriptorSubtype = 0x01
|
||||||
|
|
||||||
|
|
||||||
|
class AbstractControlManagement(FunctionalDescriptor):
|
||||||
|
fields = [('bmCapabilities', "b", None)]
|
||||||
|
bLength = 0x04
|
||||||
|
bDescriptorSubtype = 0x02
|
||||||
|
|
||||||
|
|
||||||
|
class DirectLineManagement(FunctionalDescriptor):
|
||||||
|
fields = [('bmCapabilities', "b", None)]
|
||||||
|
bLength = 0x04
|
||||||
|
bDescriptorSubtype = 0x03
|
||||||
|
|
||||||
|
|
||||||
|
class Union(FunctionalDescriptor):
|
||||||
|
fields = [('bMasterInterface', "b", None)]
|
||||||
|
bDescriptorSubtype = 0x06
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.bSlaveInterface = kwargs["bSlaveInterface"]
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def __bytes__(self):
|
||||||
|
return super().__bytes__() + bytes(self.bSlaveInterface)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def bLength(self):
|
||||||
|
return 0x4 + len(self.bSlaveInterface)
|
187
tools/usb_descriptor/core.py
Normal file
187
tools/usb_descriptor/core.py
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
import struct
|
||||||
|
|
||||||
|
|
||||||
|
def join_interfaces(*args):
|
||||||
|
interfaces = []
|
||||||
|
base_endpoint_number = 1
|
||||||
|
for interface_set in args:
|
||||||
|
base_interface_number = len(interfaces)
|
||||||
|
for i, interface in enumerate(interface_set):
|
||||||
|
interfaces.append(interface)
|
||||||
|
interface.bInterfaceNumber = interfaces.index(interface)
|
||||||
|
max_endpoint_address = base_endpoint_number
|
||||||
|
for subdescriptor in interface.subdescriptors:
|
||||||
|
if subdescriptor.bDescriptorType == EndpointDescriptor.bDescriptorType:
|
||||||
|
subdescriptor.bEndpointAddress += base_endpoint_number
|
||||||
|
max_endpoint_address = max(max_endpoint_address, subdescriptor.bEndpointAddress & 0xf)
|
||||||
|
base_endpoint_number = max_endpoint_address + 1
|
||||||
|
return interfaces
|
||||||
|
|
||||||
|
|
||||||
|
class Descriptor:
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.fmt = ["<B", "B"]
|
||||||
|
for field in self.fields:
|
||||||
|
self.fmt.append(field[1])
|
||||||
|
self.fmt = "".join(self.fmt)
|
||||||
|
if len(args) == 1:
|
||||||
|
self.data = struct.unpack(self.fmt, args[0])
|
||||||
|
if self.data[1] != self.bDescriptorType:
|
||||||
|
raise RuntimeError("Descriptor type doesn't match.")
|
||||||
|
return
|
||||||
|
elif len(args) > 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
|
Loading…
Reference in New Issue
Block a user