diff --git a/lib/tinyusb b/lib/tinyusb index 421ae8fc82b88..6d96b12e27ae6 160000 --- a/lib/tinyusb +++ b/lib/tinyusb @@ -1 +1 @@ -Subproject commit 421ae8fc82b884e087e99c8ab3fa25883a3dd623 +Subproject commit 6d96b12e27ae60ca500365ee2137105145ada9dd diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 0057bdc24986f..367ef6e5f0cfc 100755 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -102,7 +102,6 @@ SRC_C += \ tick.c \ background.c \ internal_flash.c \ - interrupt_char.c \ drivers/bluetooth/ble_drv.c \ drivers/bluetooth/ble_uart.c \ boards/$(BOARD)/board.c \ @@ -115,26 +114,11 @@ SRC_C += \ lib/utils/buffer_helper.c \ lib/utils/context_manager_helpers.c \ lib/utils/pyexec.c \ + lib/utils/interrupt_char.c \ lib/utils/stdout_helpers.c \ lib/libc/string0.c \ lib/mp-readline/readline.c \ -ifeq ($(MCU_SUB_VARIANT),nrf52840) - -SRC_C += \ - usb/usb.c \ - usb/usb_msc_flash.c \ - lib/tinyusb/src/portable/nordic/nrf5x/dcd_nrf5x.c \ - lib/tinyusb/src/portable/nordic/nrf5x/hal_nrf5x.c \ - lib/tinyusb/src/common/tusb_fifo.c \ - lib/tinyusb/src/device/usbd.c \ - lib/tinyusb/src/device/usbd_desc.c \ - lib/tinyusb/src/class/msc/msc_device.c \ - lib/tinyusb/src/class/cdc/cdc_device.c \ - lib/tinyusb/src/tusb.c \ - -endif - DRIVERS_SRC_C += $(addprefix modules/,\ ubluepy/modubluepy.c \ ubluepy/ubluepy_peripheral.c \ @@ -218,7 +202,29 @@ SRC_SHARED_BINDINGS = \ bitbangio/I2C.c \ bitbangio/SPI.c \ bitbangio/OneWire.c \ - random/__init__.c + random/__init__.c \ + +# USB source files for nrf52840 +ifeq ($(MCU_SUB_VARIANT),nrf52840) + +SRC_C += \ + usb/usb.c \ + usb/usb_msc_flash.c \ + usb/usb_desc.c \ + lib/tinyusb/src/portable/nordic/nrf5x/dcd_nrf5x.c \ + lib/tinyusb/src/portable/nordic/nrf5x/hal_nrf5x.c \ + lib/tinyusb/src/common/tusb_fifo.c \ + lib/tinyusb/src/device/usbd.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 \ + +SRC_COMMON_HAL += \ + usb_hid/__init__.c \ + usb_hid/Device.c \ + +endif SRC_SHARED_MODULE_EXPANDED = $(addprefix shared-bindings/, $(SRC_SHARED_BINDINGS)) \ diff --git a/ports/nrf/common-hal/usb_hid/Device.c b/ports/nrf/common-hal/usb_hid/Device.c new file mode 100644 index 0000000000000..c2fff7001648f --- /dev/null +++ b/ports/nrf/common-hal/usb_hid/Device.c @@ -0,0 +1,88 @@ +/* + * 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 +#include "tick.h" +#include "common-hal/usb_hid/Device.h" +#include "py/runtime.h" +#include "shared-bindings/usb_hid/Device.h" +#include "tusb.h" + +uint8_t common_hal_usb_hid_device_get_usage_page(usb_hid_device_obj_t *self) { + return self->usage_page; +} + +uint8_t common_hal_usb_hid_device_get_usage(usb_hid_device_obj_t *self) { + return self->usage; +} + +void common_hal_usb_hid_device_send_report(usb_hid_device_obj_t *self, uint8_t* report, uint8_t len) { + if (len != self->report_length) { + mp_raise_ValueError_varg("Buffer incorrect size. Should be %d bytes.", self->report_length); + } + + // Wait until interface is ready, timeout = 2 seconds + uint64_t end_ticks = ticks_ms + 2000; + while ( (ticks_ms < end_ticks) && !tud_hid_generic_ready() ) { } + + if ( !tud_hid_generic_ready() ) { + mp_raise_msg(&mp_type_OSError, "USB Busy"); + } + + memcpy(self->report_buffer, report, len); + + if ( !tud_hid_generic_report(self->report_id, self->report_buffer, len) ) { + mp_raise_msg(&mp_type_OSError, "USB Error"); + } +} + +// Callbacks invoked when receive Get_Report request through control endpoint +uint16_t tud_hid_generic_get_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) { + // only support Input Report + if ( report_type != HID_REPORT_TYPE_INPUT ) return 0; + + // index is ID-1 + uint8_t idx = ( report_id ? (report_id-1) : 0 ); + + // fill buffer with current report + memcpy(buffer, usb_hid_devices[idx].report_buffer, reqlen); + return reqlen; +} + +// Callbacks invoked when receive Set_Report request through control endpoint +void tud_hid_generic_set_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) { + // index is ID-1 + uint8_t idx = ( report_id ? (report_id-1) : 0 ); + + if ( report_type == HID_REPORT_TYPE_OUTPUT ) { + // Check if it is Keyboard device + if ( (usb_hid_devices[idx].usage_page == HID_USAGE_PAGE_DESKTOP) && (usb_hid_devices[idx].usage == HID_USAGE_DESKTOP_KEYBOARD) ) { + // This is LED indicator (CapsLock, NumLock) + // TODO Light up some LED here + } + } +} + diff --git a/ports/nrf/common-hal/usb_hid/Device.h b/ports/nrf/common-hal/usb_hid/Device.h new file mode 100644 index 0000000000000..612423715cfa0 --- /dev/null +++ b/ports/nrf/common-hal/usb_hid/Device.h @@ -0,0 +1,94 @@ +/* + * 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 COMMON_HAL_USB_HID_DEVICE_H +#define COMMON_HAL_USB_HID_DEVICE_H + +#include +#include + +#include "py/obj.h" + +#ifdef __cplusplus + extern "C" { +#endif + +// 1 to enable device, 0 to disable +#define USB_HID_DEVICE_KEYBOARD 1 +#define USB_HID_DEVICE_MOUSE 1 +#define USB_HID_DEVICE_CONSUMER 1 +#define USB_HID_DEVICE_SYS_CONTROL 1 +#define USB_HID_DEVICE_GAMEPAD 1 +#define USB_HID_DEVICE_DIGITIZER 0 // not supported yet + +enum { + USB_HID_REPORT_ID_UNUSED = 0, + +#if USB_HID_DEVICE_KEYBOARD + USB_HID_REPORT_ID_KEYBOARD, +#endif + +#if USB_HID_DEVICE_MOUSE + USB_HID_REPORT_ID_MOUSE, +#endif + +#if USB_HID_DEVICE_CONSUMER + USB_HID_REPORT_ID_CONSUMER, +#endif + +#if USB_HID_DEVICE_SYS_CONTROL + USB_HID_REPORT_ID_SYS_CONTROL, +#endif + +#if USB_HID_DEVICE_GAMEPAD + USB_HID_REPORT_ID_GAMEPAD, +#endif + +#if USB_HID_DEVICE_DIGITIZER + USB_HID_REPORT_ID_DIGITIZER, +#endif +}; + +#define USB_HID_NUM_DEVICES (USB_HID_DEVICE_KEYBOARD + USB_HID_DEVICE_MOUSE + USB_HID_DEVICE_CONSUMER + \ + USB_HID_DEVICE_SYS_CONTROL + USB_HID_DEVICE_GAMEPAD + USB_HID_DEVICE_DIGITIZER ) + +typedef struct { + mp_obj_base_t base; + uint8_t* report_buffer; + uint8_t report_id; + uint8_t report_length; + uint8_t usage_page; + uint8_t usage; +} usb_hid_device_obj_t; + + +extern usb_hid_device_obj_t usb_hid_devices[]; + +#ifdef __cplusplus + } +#endif + +#endif /* COMMON_HAL_USB_HID_DEVICE_H */ diff --git a/ports/nrf/common-hal/usb_hid/__init__.c b/ports/nrf/common-hal/usb_hid/__init__.c new file mode 100644 index 0000000000000..00fc8bd0023ce --- /dev/null +++ b/ports/nrf/common-hal/usb_hid/__init__.c @@ -0,0 +1,160 @@ +/* + * 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 "py/obj.h" +#include "py/mphal.h" +#include "py/runtime.h" + +#include "common-hal/usb_hid/Device.h" +#include "shared-bindings/usb_hid/Device.h" +#include "tusb.h" + +#define USB_HID_REPORT_LENGTH_KEYBOARD 8 +#define USB_HID_REPORT_LENGTH_MOUSE 4 +#define USB_HID_REPORT_LENGTH_CONSUMER 2 +#define USB_HID_REPORT_LENGTH_SYS_CONTROL 1 +#define USB_HID_REPORT_LENGTH_GAMEPAD 6 +#define USB_HID_REPORT_LENGTH_DIGITIZER 5 + +#if USB_HID_DEVICE_KEYBOARD +static uint8_t keyboard_report_buffer[USB_HID_REPORT_LENGTH_KEYBOARD]; +#endif + +#if USB_HID_DEVICE_MOUSE +static uint8_t mouse_report_buffer[USB_HID_REPORT_LENGTH_MOUSE]; +#endif + +#if USB_HID_DEVICE_CONSUMER +static uint8_t consumer_report_buffer[USB_HID_REPORT_LENGTH_CONSUMER]; +#endif + +#if USB_HID_DEVICE_SYS_CONTROL +static uint8_t sys_control_report_buffer[USB_HID_REPORT_LENGTH_SYS_CONTROL]; +#endif + +#if USB_HID_DEVICE_GAMEPAD +static uint8_t gamepad_report_buffer[USB_HID_REPORT_LENGTH_GAMEPAD]; +#endif + +#if USB_HID_DEVICE_DIGITIZER +static uint8_t digitizer_report_buffer[USB_HID_REPORT_LENGTH_DIGITIZER]; +#endif + +usb_hid_device_obj_t usb_hid_devices[] = { +#if USB_HID_DEVICE_KEYBOARD + { + .base = { .type = &usb_hid_device_type } , + .report_buffer = keyboard_report_buffer , + .report_id = USB_HID_REPORT_ID_KEYBOARD , + .report_length = USB_HID_REPORT_LENGTH_KEYBOARD , + .usage_page = HID_USAGE_PAGE_DESKTOP , + .usage = HID_USAGE_DESKTOP_KEYBOARD , + }, +#endif + +#if USB_HID_DEVICE_MOUSE + { + .base = { .type = &usb_hid_device_type } , + .report_buffer = mouse_report_buffer , + .report_id = USB_HID_REPORT_ID_MOUSE , + .report_length = USB_HID_REPORT_LENGTH_MOUSE , + .usage_page = HID_USAGE_PAGE_DESKTOP , + .usage = HID_USAGE_DESKTOP_MOUSE , + }, +#endif + +#if USB_HID_DEVICE_CONSUMER + { + .base = { .type = &usb_hid_device_type } , + .report_buffer = consumer_report_buffer , + .report_id = USB_HID_REPORT_ID_CONSUMER , + .report_length = USB_HID_REPORT_LENGTH_CONSUMER , + .usage_page = HID_USAGE_PAGE_CONSUMER , + .usage = HID_USAGE_CONSUMER_CONTROL , + }, +#endif + +#if USB_HID_DEVICE_SYS_CONTROL + { + .base = { .type = &usb_hid_device_type } , + .report_buffer = sys_control_report_buffer , + .report_id = USB_HID_REPORT_ID_SYS_CONTROL , + .report_length = USB_HID_REPORT_LENGTH_SYS_CONTROL , + .usage_page = HID_USAGE_PAGE_DESKTOP , + .usage = HID_USAGE_DESKTOP_SYSTEM_CONTROL , + }, +#endif + +#if USB_HID_DEVICE_GAMEPAD + { + .base = { .type = &usb_hid_device_type } , + .report_buffer = gamepad_report_buffer , + .report_id = USB_HID_REPORT_ID_GAMEPAD , + .report_length = USB_HID_REPORT_LENGTH_GAMEPAD , + .usage_page = HID_USAGE_PAGE_DESKTOP , + .usage = HID_USAGE_DESKTOP_GAMEPAD , + }, +#endif + +#if USB_HID_DEVICE_DIGITIZER + { + .base = { .type = &usb_hid_device_type } , + .report_buffer = digitizer_report_buffer , + .report_id = USB_HID_REPORT_ID_DIGITIZER , + .report_length = USB_HID_REPORT_LENGTH_DIGITIZER , + .usage_page = 0x0D , + .usage = 0x02 , + }, +#endif +}; + + +mp_obj_tuple_t common_hal_usb_hid_devices = { + .base = { + .type = &mp_type_tuple, + }, + .len = USB_HID_NUM_DEVICES, + .items = { +#if USB_HID_NUM_DEVICES >= 1 + (mp_obj_t) &usb_hid_devices[0], +#endif +#if USB_HID_NUM_DEVICES >= 2 + (mp_obj_t) &usb_hid_devices[1], +#endif +#if USB_HID_NUM_DEVICES >= 3 + (mp_obj_t) &usb_hid_devices[2], +#endif +#if USB_HID_NUM_DEVICES >= 4 + (mp_obj_t) &usb_hid_devices[3], +#endif +#if USB_HID_NUM_DEVICES >= 5 + (mp_obj_t) &usb_hid_devices[4], +#endif +#if USB_HID_NUM_DEVICES >= 6 + (mp_obj_t) &usb_hid_devices[5], +#endif + } +}; diff --git a/ports/nrf/interrupt_char.c b/ports/nrf/interrupt_char.c deleted file mode 100644 index 8793b0d889f02..0000000000000 --- a/ports/nrf/interrupt_char.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2013-2016 Damien P. George - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "py/obj.h" -#include "py/mpstate.h" - -#ifdef NRF52840_XXAA -#include "usb.h" -#endif - -#if MICROPY_KBD_EXCEPTION - -int mp_interrupt_char; - -void mp_hal_set_interrupt_char(int c) { - if (c != -1) { - mp_obj_exception_clear_traceback(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))); - } - mp_interrupt_char = c; - -#ifdef NRF52840_XXAA - tud_cdc_set_wanted_char(c); -#endif -} - -void mp_keyboard_interrupt(void) { - MP_STATE_VM(mp_pending_exception) = MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception)); - #if MICROPY_ENABLE_SCHEDULER - if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) { - MP_STATE_VM(sched_state) = MP_SCHED_PENDING; - } - #endif -} - -#endif diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 7d7d8796962a7..c11d2d81edf03 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -95,9 +95,9 @@ #define MICROPY_MODULE_BUILTIN_INIT (1) #define MICROPY_PY_ALL_SPECIAL_METHODS (0) #define MICROPY_PY_MICROPYTHON_MEM_INFO (1) -#define MICROPY_PY_ARRAY_SLICE_ASSIGN (0) +#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) #define MICROPY_NONSTANDARD_TYPECODES (0) -#define MICROPY_PY_BUILTINS_SLICE_ATTRS (0) +#define MICROPY_PY_BUILTINS_SLICE_ATTRS (1) #define MICROPY_PY_SYS_EXIT (1) #define MICROPY_PY_SYS_MAXSIZE (1) #define MICROPY_PY_SYS_STDFILES (0) @@ -177,6 +177,7 @@ extern const struct _mp_obj_module_t struct_module; extern const struct _mp_obj_module_t time_module; extern const struct _mp_obj_module_t supervisor_module; extern const struct _mp_obj_module_t gamepad_module; +extern const struct _mp_obj_module_t usb_hid_module; extern const struct _mp_obj_module_t bleio_module; extern const struct _mp_obj_module_t mp_module_ubluepy; @@ -193,6 +194,12 @@ extern const struct _mp_obj_module_t mp_module_ubluepy; #define BLEIO_MODULE #endif +#ifdef NRF52840_XXAA +#define USBHID_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_usb_hid), (mp_obj_t)&usb_hid_module }, +#else +#define USBHID_MODULE +#endif + #define MICROPY_PORT_BUILTIN_MODULES \ { MP_OBJ_NEW_QSTR (MP_QSTR_board ), (mp_obj_t)&board_module }, \ { MP_OBJ_NEW_QSTR (MP_QSTR_busio ), (mp_obj_t)&busio_module }, \ @@ -203,19 +210,20 @@ extern const struct _mp_obj_module_t mp_module_ubluepy; { MP_OBJ_NEW_QSTR (MP_QSTR_bitbangio ), (mp_obj_t)&bitbangio_module }, \ { MP_OBJ_NEW_QSTR (MP_QSTR_os ), (mp_obj_t)&os_module }, \ { MP_OBJ_NEW_QSTR (MP_QSTR_random ), (mp_obj_t)&random_module }, \ - { MP_OBJ_NEW_QSTR (MP_QSTR_storage ), (mp_obj_t)&storage_module },\ + { MP_OBJ_NEW_QSTR (MP_QSTR_storage ), (mp_obj_t)&storage_module }, \ { MP_OBJ_NEW_QSTR (MP_QSTR_struct ), (mp_obj_t)&struct_module }, \ { MP_OBJ_NEW_QSTR (MP_QSTR_supervisor ), (mp_obj_t)&supervisor_module }, \ { MP_OBJ_NEW_QSTR (MP_QSTR_gamepad ), (mp_obj_t)&gamepad_module }, \ { MP_OBJ_NEW_QSTR (MP_QSTR_time ), (mp_obj_t)&time_module }, \ - BLEIO_MODULE \ + USBHID_MODULE \ + BLEIO_MODULE \ UBLUEPY_MODULE \ // extra built in names to add to the global namespace #define MICROPY_PORT_BUILTINS \ - { MP_ROM_QSTR (MP_QSTR_help), MP_ROM_PTR(&mp_builtin_help_obj) }, \ - { MP_OBJ_NEW_QSTR (MP_QSTR_input), (mp_obj_t)&mp_builtin_input_obj }, \ - { MP_ROM_QSTR (MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, \ + { MP_ROM_QSTR (MP_QSTR_help ), MP_ROM_PTR(&mp_builtin_help_obj) }, \ + { MP_OBJ_NEW_QSTR (MP_QSTR_input ), (mp_obj_t)&mp_builtin_input_obj }, \ + { MP_ROM_QSTR (MP_QSTR_open ), MP_ROM_PTR(&mp_builtin_open_obj) }, \ #define MP_STATE_PORT MP_STATE_VM diff --git a/ports/nrf/usb/tusb_config.h b/ports/nrf/usb/tusb_config.h index 755e4cb6a67af..626b99eeb290f 100644 --- a/ports/nrf/usb/tusb_config.h +++ b/ports/nrf/usb/tusb_config.h @@ -45,13 +45,13 @@ //--------------------------------------------------------------------+ // COMMON CONFIGURATION //--------------------------------------------------------------------+ -#define CFG_TUSB_MCU OPT_MCU_NRF5X -#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE +#define CFG_TUSB_MCU OPT_MCU_NRF5X +#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE -#define CFG_TUSB_DEBUG 0 +#define CFG_TUSB_DEBUG 0 /*------------- RTOS -------------*/ -#define CFG_TUSB_OS OPT_OS_NONE +#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 @@ -60,24 +60,26 @@ // DEVICE CONFIGURATION //--------------------------------------------------------------------+ -/*------------- Core -------------*/ -#define CFG_TUD_DESC_AUTO 1 -#define CFG_TUD_DESC_VID 0x239A -#define CFG_TUD_DESC_PID 0x802A +#define CFG_TUD_ENDOINT0_SIZE 64 -#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_KEYBOARD 0 -#define CFG_TUD_HID_MOUSE 0 -#define CFG_TUD_HID_GENERIC 0 +#define CFG_TUD_CDC 1 +#define CFG_TUD_MSC 1 +#define CFG_TUD_HID 1 /*------------------------------------------------------------------*/ /* CLASS DRIVER *------------------------------------------------------------------*/ +/*------------- CDC -------------*/ // FIFO size of CDC TX and RX #define CFG_TUD_CDC_RX_BUFSIZE 1024 #define CFG_TUD_CDC_TX_BUFSIZE 1024 @@ -89,6 +91,8 @@ */ #define CFG_TUD_CDC_FLUSH_ON_SOF 0 + +/*------------- MSC -------------*/ // Number of supported Logical Unit Number (At least 1) #define CFG_TUD_MSC_MAXLUN 1 diff --git a/ports/nrf/usb/usb.c b/ports/nrf/usb/usb.c index 648ee708dd4b8..953b61e396918 100644 --- a/ports/nrf/usb/usb.c +++ b/ports/nrf/usb/usb.c @@ -29,52 +29,13 @@ #include "tick.h" #include "usb.h" #include "lib/utils/interrupt_char.h" +#include "lib/mp-readline/readline.h" #ifdef SOFTDEVICE_PRESENT #include "nrf_sdm.h" #include "nrf_soc.h" #endif -//--------------------------------------------------------------------+ -// STRING DESCRIPTORS -//--------------------------------------------------------------------+ - -// array of pointer to string descriptors -uint16_t const * const string_desc_arr [] = -{ - // 0 index is supported language = English - TUD_DESC_STRCONV(0x0409), - - // Manufacturer - TUD_DESC_STRCONV('A','d','a','f','r','u','i','t',' ','I','n','d','u','s','t','r','i','e','s'), - - // Product - TUD_DESC_STRCONV('C','i','r','c','u','i','t','P','Y',' ','n','R','F','5','2'), - - // Serials TODO use chip ID - TUD_DESC_STRCONV('1', '2', '3', '4', '5'), - - // CDC Interface - TUD_DESC_STRCONV('B','l','u','e','f','r','u','i','t',' ','S','e','r','i','a','l'), - - // MSC Interface - TUD_DESC_STRCONV('B','l','u','e','f','r','u','i','t',' ','S','t','o','r','a','g','e'), - - // Custom Interface - TUD_DESC_STRCONV('B','l','u','e','f','r','u','i','t',' ','C','u','s','t','o','m') -}; - -// tud_desc_set is required by tinyusb stack -// since CFG_TUD_DESC_AUTO is enabled, we only need to set string_arr -tud_desc_set_t tud_desc_set = -{ - .device = NULL, - .config = NULL, - .string_arr = (uint8_t const **) string_desc_arr, - .hid_report = NULL -}; - - /* tinyusb function that handles power event (detected, ready, removed) * We must call it within SD's SOC event handler, or set it as power event handler if SD is not enabled. */ @@ -122,6 +83,12 @@ void usb_init(void) { } tusb_init(); + +#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 } //--------------------------------------------------------------------+ @@ -151,6 +118,8 @@ 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(); diff --git a/ports/nrf/usb/usb_desc.c b/ports/nrf/usb/usb_desc.c new file mode 100644 index 0000000000000..31f80b80d225d --- /dev/null +++ b/ports/nrf/usb/usb_desc.c @@ -0,0 +1,391 @@ +/* + * 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 "usb_desc.h" +#include "common-hal/usb_hid/Device.h" + +//--------------------------------------------------------------------+ +// MACRO TYPEDEF CONSTANT ENUM DECLARATION +//--------------------------------------------------------------------+ +#define USB_VID 0x239A + +/* Note: different class combination e.g CDC and (CDC + MSC) should have different + * PID since Host OS will "remembered" device driver after the first plug */ +#define USB_PID 0x802A + +/*------------- Interface Numbering -------------*/ +enum { + ITF_NUM_CDC = 0 , + ITF_NUM_CDC_DATA , + ITF_NUM_MSC , + ITF_NUM_HID_GEN , + ITF_NUM_TOTAL +}; + +enum { + ITF_STR_LANGUAGE = 0 , + ITF_STR_MANUFACTURER , + ITF_STR_PRODUCT , + ITF_STR_SERIAL , + ITF_STR_CDC , + ITF_STR_MSC , + ITF_STR_HID +}; + +/*------------- Endpoint Numbering & Size -------------*/ +#define _EP_IN(x) (0x80 | (x)) +#define _EP_OUT(x) (x) + +// CDC +#define EP_CDC_NOTIF _EP_IN ( ITF_NUM_CDC+1 ) +#define EP_CDC_NOTIF_SIZE 8 + +#define EP_CDC_OUT _EP_OUT( ITF_NUM_CDC+2 ) +#define EP_CDC_IN _EP_IN ( ITF_NUM_CDC+2 ) + +// Mass Storage +#define EP_MSC_OUT _EP_OUT( ITF_NUM_MSC+1 ) +#define EP_MSC_IN _EP_IN ( ITF_NUM_MSC+1 ) + +// HID composite = keyboard + mouse + gamepad + etc ... +#define EP_HID_GEN _EP_IN ( ITF_NUM_HID_GEN+1 ) +#define EP_HID_GEN_SIZE 16 + +//--------------------------------------------------------------------+ +// STRING DESCRIPTORS +//--------------------------------------------------------------------+ + +// array of pointer to string descriptors +uint16_t const * const string_desc_arr [] = +{ + // 0 index is supported language = English + TUD_DESC_STRCONV(0x0409), + + // 1 Manufacturer + TUD_DESC_STRCONV('A','d','a','f','r','u','i','t',' ','I','n','d','u','s','t','r','i','e','s'), + + // 2 Product + TUD_DESC_STRCONV('C','i','r','c','u','i','t','P','y',' ','n','R','F','5','2'), + + // 3 Serials TODO use chip ID + TUD_DESC_STRCONV('1', '2', '3', '4', '5'), + + // 4 CDC Interface + TUD_DESC_STRCONV('C','i','r','c','u','i','t','P','y',' ','S','e','r','i','a','l'), + + // 5 MSC Interface + TUD_DESC_STRCONV('C','i','r','c','u','i','t','P','y',' ','S','t','o','r','a','g','e'), + + // 6 HID Interface + TUD_DESC_STRCONV('C','i','r','c','u','i','t','P','y',' ','H','I','D'), + + // Custom Interface +// TUD_DESC_STRCONV('C','i','r','c','u','i','t','P','y',' ','C','u','s','t','o','m') +}; + + +//--------------------------------------------------------------------+ +// Device Descriptor +//--------------------------------------------------------------------+ +tusb_desc_device_t const usb_desc_dev = +{ + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + + #if CFG_TUD_CDC + // Use Interface Association Descriptor (IAD) for CDC + // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + #else + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + #endif + + .bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE, + + .idVendor = USB_VID, + .idProduct = USB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01 // TODO multiple configurations +}; + +//--------------------------------------------------------------------+ +// HID Report Descriptor +//--------------------------------------------------------------------+ +uint8_t const usb_desc_hid_generic_report[] = +{ +#if USB_HID_DEVICE_KEYBOARD + HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(USB_HID_REPORT_ID_KEYBOARD), ), +#endif + +#if USB_HID_DEVICE_MOUSE + HID_REPORT_DESC_MOUSE( HID_REPORT_ID(USB_HID_REPORT_ID_MOUSE), ), +#endif + +#if USB_HID_DEVICE_CONSUMER + HID_REPORT_DESC_CONSUMER( HID_REPORT_ID(USB_HID_REPORT_ID_CONSUMER), ), +#endif + +#if USB_HID_DEVICE_SYS_CONTROL + HID_REPORT_DESC_SYSTEM_CONTROL( HID_REPORT_ID(USB_HID_REPORT_ID_SYS_CONTROL ), ), +#endif + +#if USB_HID_DEVICE_GAMEPAD + HID_REPORT_DESC_GAMEPAD( HID_REPORT_ID(USB_HID_REPORT_ID_GAMEPAD ), ) +#endif + +}; + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ +usb_desc_cfg_t const usb_desc_cfg = +{ + .config = + { + .bLength = sizeof(tusb_desc_configuration_t), + .bDescriptorType = TUSB_DESC_CONFIGURATION, + .wTotalLength = sizeof(usb_desc_cfg_t), + .bNumInterfaces = ITF_NUM_TOTAL, + .bConfigurationValue = 1, + .iConfiguration = 0x00, + .bmAttributes = TUSB_DESC_CONFIG_ATT_BUS_POWER, + .bMaxPower = TUSB_DESC_CONFIG_POWER_MA(100) + }, + + // IAD points to CDC Interfaces + .cdc = + { + .iad = + { + .bLength = sizeof(tusb_desc_interface_assoc_t), + .bDescriptorType = TUSB_DESC_INTERFACE_ASSOCIATION, + + .bFirstInterface = ITF_NUM_CDC, + .bInterfaceCount = 2, + + .bFunctionClass = TUSB_CLASS_CDC, + .bFunctionSubClass = CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL, + .bFunctionProtocol = CDC_COMM_PROTOCOL_ATCOMMAND, + .iFunction = 0 + }, + + //------------- CDC Communication Interface -------------// + .comm_itf = + { + .bLength = sizeof(tusb_desc_interface_t), + .bDescriptorType = TUSB_DESC_INTERFACE, + .bInterfaceNumber = ITF_NUM_CDC, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = TUSB_CLASS_CDC, + .bInterfaceSubClass = CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL, + .bInterfaceProtocol = CDC_COMM_PROTOCOL_ATCOMMAND, + .iInterface = ITF_STR_CDC + }, + + .header = + { + .bLength = sizeof(cdc_desc_func_header_t), + .bDescriptorType = TUSB_DESC_CLASS_SPECIFIC, + .bDescriptorSubType = CDC_FUNC_DESC_HEADER, + .bcdCDC = 0x0120 + }, + + .call = + { + .bLength = sizeof(cdc_desc_func_call_management_t), + .bDescriptorType = TUSB_DESC_CLASS_SPECIFIC, + .bDescriptorSubType = CDC_FUNC_DESC_CALL_MANAGEMENT, + .bmCapabilities = { 0 }, + .bDataInterface = ITF_NUM_CDC+1, + }, + + .acm = + { + .bLength = sizeof(cdc_desc_func_acm_t), + .bDescriptorType = TUSB_DESC_CLASS_SPECIFIC, + .bDescriptorSubType = CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, + .bmCapabilities = { // 0x02 + .support_line_request = 1, + } + }, + + .union_func = + { + .bLength = sizeof(cdc_desc_func_union_t), // plus number of + .bDescriptorType = TUSB_DESC_CLASS_SPECIFIC, + .bDescriptorSubType = CDC_FUNC_DESC_UNION, + .bControlInterface = ITF_NUM_CDC, + .bSubordinateInterface = ITF_NUM_CDC+1, + }, + + .ep_notif = + { + .bLength = sizeof(tusb_desc_endpoint_t), + .bDescriptorType = TUSB_DESC_ENDPOINT, + .bEndpointAddress = EP_CDC_NOTIF, + .bmAttributes = { .xfer = TUSB_XFER_INTERRUPT }, + .wMaxPacketSize = { .size = EP_CDC_NOTIF_SIZE }, + .bInterval = 0x10 + }, + + //------------- CDC Data Interface -------------// + .data_itf = + { + .bLength = sizeof(tusb_desc_interface_t), + .bDescriptorType = TUSB_DESC_INTERFACE, + .bInterfaceNumber = ITF_NUM_CDC+1, + .bAlternateSetting = 0x00, + .bNumEndpoints = 2, + .bInterfaceClass = TUSB_CLASS_CDC_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 0x00 + }, + + .ep_out = + { + .bLength = sizeof(tusb_desc_endpoint_t), + .bDescriptorType = TUSB_DESC_ENDPOINT, + .bEndpointAddress = EP_CDC_OUT, + .bmAttributes = { .xfer = TUSB_XFER_BULK }, + .wMaxPacketSize = { .size = CFG_TUD_CDC_EPSIZE }, + .bInterval = 0 + }, + + .ep_in = + { + .bLength = sizeof(tusb_desc_endpoint_t), + .bDescriptorType = TUSB_DESC_ENDPOINT, + .bEndpointAddress = EP_CDC_IN, + .bmAttributes = { .xfer = TUSB_XFER_BULK }, + .wMaxPacketSize = { .size = CFG_TUD_CDC_EPSIZE }, + .bInterval = 0 + }, + }, + + //------------- Mass Storage-------------// + .msc = + { + .itf = + { + .bLength = sizeof(tusb_desc_interface_t), + .bDescriptorType = TUSB_DESC_INTERFACE, + .bInterfaceNumber = ITF_NUM_MSC, + .bAlternateSetting = 0x00, + .bNumEndpoints = 2, + .bInterfaceClass = TUSB_CLASS_MSC, + .bInterfaceSubClass = MSC_SUBCLASS_SCSI, + .bInterfaceProtocol = MSC_PROTOCOL_BOT, + .iInterface = ITF_STR_MSC + }, + + .ep_out = + { + .bLength = sizeof(tusb_desc_endpoint_t), + .bDescriptorType = TUSB_DESC_ENDPOINT, + .bEndpointAddress = EP_MSC_OUT, + .bmAttributes = { .xfer = TUSB_XFER_BULK }, + .wMaxPacketSize = { .size = CFG_TUD_MSC_EPSIZE}, + .bInterval = 1 + }, + + .ep_in = + { + .bLength = sizeof(tusb_desc_endpoint_t), + .bDescriptorType = TUSB_DESC_ENDPOINT, + .bEndpointAddress = EP_MSC_IN, + .bmAttributes = { .xfer = TUSB_XFER_BULK }, + .wMaxPacketSize = { .size = CFG_TUD_MSC_EPSIZE}, + .bInterval = 1 + } + }, + + //------------- HID Generic Multiple report -------------// + .hid_generic = + { + .itf = + { + .bLength = sizeof(tusb_desc_interface_t), + .bDescriptorType = TUSB_DESC_INTERFACE, + .bInterfaceNumber = ITF_NUM_HID_GEN, + .bAlternateSetting = 0x00, + .bNumEndpoints = 1, + .bInterfaceClass = TUSB_CLASS_HID, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = ITF_STR_HID + }, + + .hid_desc = + { + .bLength = sizeof(tusb_hid_descriptor_hid_t), + .bDescriptorType = HID_DESC_TYPE_HID, + .bcdHID = 0x0111, + .bCountryCode = HID_Local_NotSupported, + .bNumDescriptors = 1, + .bReportType = HID_DESC_TYPE_REPORT, + .wReportLength = sizeof(usb_desc_hid_generic_report) + }, + + .ep_in = + { + .bLength = sizeof(tusb_desc_endpoint_t), + .bDescriptorType = TUSB_DESC_ENDPOINT, + .bEndpointAddress = EP_HID_GEN, + .bmAttributes = { .xfer = TUSB_XFER_INTERRUPT }, + .wMaxPacketSize = { .size = EP_HID_GEN_SIZE }, + .bInterval = 0x0A + } + } +}; + + +// 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 = usb_desc_hid_generic_report, + .boot_keyboard = NULL, + .boot_mouse = NULL + } +}; diff --git a/ports/nrf/usb/usb_desc.h b/ports/nrf/usb/usb_desc.h new file mode 100644 index 0000000000000..19f3f6d5bfbae --- /dev/null +++ b/ports/nrf/usb/usb_desc.h @@ -0,0 +1,87 @@ +/* + * 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 "tusb.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/*------------- Configuration Descriptor -------------*/ +typedef struct ATTR_PACKED +{ + tusb_desc_configuration_t config; + + //------------- CDC -------------// + struct ATTR_PACKED + { + tusb_desc_interface_assoc_t iad; + + //CDC Control Interface + tusb_desc_interface_t comm_itf; + cdc_desc_func_header_t header; + cdc_desc_func_call_management_t call; + cdc_desc_func_acm_t acm; + cdc_desc_func_union_t union_func; + tusb_desc_endpoint_t ep_notif; + + //CDC Data Interface + tusb_desc_interface_t data_itf; + tusb_desc_endpoint_t ep_out; + tusb_desc_endpoint_t ep_in; + }cdc; + + //------------- Mass Storage -------------// + struct ATTR_PACKED + { + tusb_desc_interface_t itf; + tusb_desc_endpoint_t ep_out; + tusb_desc_endpoint_t ep_in; + } msc; + + //------------- HID -------------// + struct ATTR_PACKED + { + tusb_desc_interface_t itf; + tusb_hid_descriptor_hid_t hid_desc; + tusb_desc_endpoint_t ep_in; + } hid_generic; + +} usb_desc_cfg_t; + + +// Descriptors set used by tinyusb stack +extern tud_desc_set_t tud_desc_set; + + +#ifdef __cplusplus + } +#endif + +#endif /* USB_DESC_H_ */ diff --git a/ports/nrf/usb/usb_msc_flash.c b/ports/nrf/usb/usb_msc_flash.c index f77a9e5c5cabd..2d0e9bec7f8eb 100644 --- a/ports/nrf/usb/usb_msc_flash.c +++ b/ports/nrf/usb/usb_msc_flash.c @@ -53,90 +53,60 @@ #define FL_PAGE_SZ 4096 -static scsi_sense_fixed_data_t mscd_sense_data = -{ - .response_code = 0x70, - .sense_key = 0, // no errors - .additional_sense_len = sizeof(scsi_sense_fixed_data_t) - 8 -}; - -static scsi_mode_parameters_t const msc_dev_mode_para = -{ - .mode_data_length = 3, - .medium_type = 0, - .device_specific_para = 0, - .block_descriptor_length = 0 -}; - -//--------------------------------------------------------------------+ -// tinyusb callbacks -//--------------------------------------------------------------------+ -int32_t tud_msc_scsi_cb (uint8_t rhport, uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) { - // read10 & write10 has their own callback and MUST not be handled here - - void const* ptr = NULL; - uint16_t len = 0; - - // most scsi handled is input - bool in_xfer = true; - - switch ( scsi_cmd[0] ) - { - case SCSI_CMD_REQUEST_SENSE: - ptr = &mscd_sense_data; - len = sizeof(scsi_sense_fixed_data_t); - break; - - case SCSI_CMD_MODE_SENSE_6: - ptr = &msc_dev_mode_para; - len = sizeof(msc_dev_mode_para); - break; - +// 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 has their own callbacks +int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) { + void const* response = NULL; + uint16_t resplen = 0; + + switch ( scsi_cmd[0] ) { case SCSI_CMD_TEST_UNIT_READY: - ptr = NULL; - len = 0; + // Command that host uses to check our readiness before sending other commands + resplen = 0; break; case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: - ptr = NULL; - len = 0; + // 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 + /* scsi_start_stop_unit_t const * start_stop = (scsi_start_stop_unit_t const *) 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 + start_stop->start; + start_stop->load_eject; + */ + resplen = 0; break; default: - // negative is error -> Data stage is STALL, status = failed - return -1; + // 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 - TU_ASSERT(bufsize >= len); - - if ( ptr && len ) - { - if ( in_xfer ) - { - memcpy(buffer, ptr, len); - } else - { - // SCSI output - } + if ( resplen > bufsize ) { + resplen = bufsize; } - //------------- clear sense data if it is not request sense command -------------// - if ( SCSI_CMD_REQUEST_SENSE != scsi_cmd[0] ) - { - mscd_sense_data.sense_key = SCSI_SENSEKEY_NONE; - mscd_sense_data.additional_sense_code = 0; - mscd_sense_data.additional_sense_qualifier = 0; + // copy response to stack's buffer if any + if ( response && resplen ) { + memcpy(buffer, response, resplen); } - return len; + return resplen; } -/*------------------------------------------------------------------*/ -/* Tinyusb Flash READ10 & WRITE10 - *------------------------------------------------------------------*/ -int32_t tud_msc_read10_cb (uint8_t rhport, uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) { - (void) rhport; +// 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; @@ -147,8 +117,9 @@ int32_t tud_msc_read10_cb (uint8_t rhport, uint8_t lun, uint32_t lba, uint32_t o return block_count * MSC_FLASH_BLOCK_SIZE; } -int32_t tud_msc_write10_cb (uint8_t rhport, uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) { - (void) rhport; +// 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, void* buffer, uint32_t bufsize) { (void) lun; (void) offset; @@ -167,8 +138,9 @@ int32_t tud_msc_write10_cb (uint8_t rhport, uint8_t lun, uint32_t lba, uint32_t return block_count * MSC_FLASH_BLOCK_SIZE; } -void tud_msc_write10_complete_cb (uint8_t rhport, uint8_t lun) { - (void) rhport; +// 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; // flush pending cache when write10 is complete