diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index 37ac78b7339f4..021b830e0f899 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -125,10 +125,13 @@ set(MICROPY_SOURCE_PORT pendsv.c rp2_flash.c rp2_pio.c - tusb_port.c uart.c + usbd.c msc_disk.c mbedtls/mbedtls_port.c + ${MICROPY_DIR}/shared/tinyusb/usbd_descriptor.c + ${MICROPY_DIR}/shared/tinyusb/usbd_hid.c + ${MICROPY_DIR}/shared/tinyusb/usbd.c ) set(MICROPY_SOURCE_QSTR @@ -136,6 +139,9 @@ set(MICROPY_SOURCE_QSTR ${MICROPY_DIR}/shared/readline/readline.c ${MICROPY_DIR}/shared/runtime/mpirq.c ${MICROPY_DIR}/shared/runtime/sys_stdio_mphal.c + ${MICROPY_DIR}/shared/tinyusb/usbd.c + ${MICROPY_DIR}/shared/tinyusb/usbd_descriptor.c + ${MICROPY_DIR}/shared/tinyusb/usbd_hid.c ${PROJECT_SOURCE_DIR}/machine_adc.c ${PROJECT_SOURCE_DIR}/machine_i2c.c ${PROJECT_SOURCE_DIR}/machine_i2s.c @@ -233,6 +239,11 @@ if(MICROPY_BLUETOOTH_NIMBLE) list(APPEND MICROPY_INC_CORE ${NIMBLE_INCLUDE}) endif() +# tinyusb helper +target_include_directories(${MICROPY_TARGET} PRIVATE + ${MICROPY_DIR}/shared/tinyusb/ +) + if (MICROPY_PY_NETWORK_CYW43) string(CONCAT GIT_SUBMODULES "${GIT_SUBMODULES} " lib/cyw43-driver) if((NOT (${ECHO_SUBMODULES})) AND NOT EXISTS ${MICROPY_DIR}/lib/cyw43-driver/src/cyw43.h) diff --git a/ports/rp2/main.c b/ports/rp2/main.c index 1062236a3916b..572e9e42c18f2 100644 --- a/ports/rp2/main.c +++ b/ports/rp2/main.c @@ -43,6 +43,7 @@ #include "modrp2.h" #include "mpbthciport.h" #include "genhdr/mpversion.h" +#include "usbd.h" #include "pico/stdlib.h" #include "pico/binary_info.h" @@ -87,6 +88,7 @@ int main(int argc, char **argv) { #if MICROPY_HW_ENABLE_USBDEV bi_decl(bi_program_feature("USB REPL")) tusb_init(); + usbd_reset_all(); // run now just in case usb initialization occurs early #endif #if MICROPY_PY_THREAD @@ -159,6 +161,9 @@ int main(int argc, char **argv) { machine_pin_init(); rp2_pio_init(); machine_i2s_init0(); + #if MICROPY_HW_ENABLE_USBDEV + usbd_reset_all(); + #endif #if MICROPY_PY_BLUETOOTH mp_bluetooth_hci_init(); diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index a3725789f0848..f0c3f7e1e950e 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -109,6 +109,7 @@ #define MICROPY_VFS_FAT (1) #define MICROPY_SSL_MBEDTLS (1) #define MICROPY_PY_LWIP_SOCK_RAW (MICROPY_PY_LWIP) +#define MICROPY_HW_USB_HID (1) // fatfs configuration #define MICROPY_FATFS_ENABLE_LFN (1) @@ -198,6 +199,13 @@ extern const struct _mod_network_nic_type_t mod_network_nic_type_wiznet5k; // Miscellaneous settings +#ifndef MICROPY_HW_USB_VID +#define MICROPY_HW_USB_VID (0x2E8A) // Raspberry Pi +#endif +#ifndef MICROPY_HW_USB_PID +#define MICROPY_HW_USB_PID (0x0005) // RP2 MicroPython +#endif + // Entering a critical section. extern uint32_t mp_thread_begin_atomic_section(void); extern void mp_thread_end_atomic_section(uint32_t); diff --git a/ports/rp2/tusb_port.c b/ports/rp2/tusb_port.c deleted file mode 100644 index 4aac08791e94e..0000000000000 --- a/ports/rp2/tusb_port.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 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 "tusb.h" -#include "pico/unique_id.h" - -#ifndef MICROPY_HW_USB_VID -#define MICROPY_HW_USB_VID (0x2E8A) // Raspberry Pi -#endif -#ifndef MICROPY_HW_USB_PID -#define MICROPY_HW_USB_PID (0x0005) // RP2 MicroPython -#endif - -#if CFG_TUD_MSC -#define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN) -#else -#define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) -#endif -#define USBD_MAX_POWER_MA (250) - -#define USBD_ITF_CDC (0) // needs 2 interfaces -#define USBD_ITF_MSC (2) -#if CFG_TUD_MSC -#define USBD_ITF_MAX (3) -#else -#define USBD_ITF_MAX (2) -#endif - -#define USBD_CDC_EP_CMD (0x81) -#define USBD_CDC_EP_OUT (0x02) -#define USBD_CDC_EP_IN (0x82) -#define USBD_CDC_CMD_MAX_SIZE (8) -#define USBD_CDC_IN_OUT_MAX_SIZE (64) - -#define EPNUM_MSC_OUT (0x03) -#define EPNUM_MSC_IN (0x83) - -#define USBD_STR_0 (0x00) -#define USBD_STR_MANUF (0x01) -#define USBD_STR_PRODUCT (0x02) -#define USBD_STR_SERIAL (0x03) -#define USBD_STR_CDC (0x04) -#define USBD_STR_MSC (0x05) - -// Note: descriptors returned from callbacks must exist long enough for transfer to complete - -static const tusb_desc_device_t usbd_desc_device = { - .bLength = sizeof(tusb_desc_device_t), - .bDescriptorType = TUSB_DESC_DEVICE, - .bcdUSB = 0x0200, - .bDeviceClass = TUSB_CLASS_MISC, - .bDeviceSubClass = MISC_SUBCLASS_COMMON, - .bDeviceProtocol = MISC_PROTOCOL_IAD, - .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, - .idVendor = MICROPY_HW_USB_VID, - .idProduct = MICROPY_HW_USB_PID, - .bcdDevice = 0x0100, - .iManufacturer = USBD_STR_MANUF, - .iProduct = USBD_STR_PRODUCT, - .iSerialNumber = USBD_STR_SERIAL, - .bNumConfigurations = 1, -}; - -static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = { - TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_0, USBD_DESC_LEN, - 0, USBD_MAX_POWER_MA), - - TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD, - USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, USBD_CDC_IN_OUT_MAX_SIZE), - #if CFG_TUD_MSC - TUD_MSC_DESCRIPTOR(USBD_ITF_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64), - #endif -}; - -static const char *const usbd_desc_str[] = { - [USBD_STR_MANUF] = "MicroPython", - [USBD_STR_PRODUCT] = "Board in FS mode", - [USBD_STR_SERIAL] = NULL, // generated dynamically - [USBD_STR_CDC] = "Board CDC", - #if CFG_TUD_MSC - [USBD_STR_MSC] = "Board MSC", - #endif -}; - -const uint8_t *tud_descriptor_device_cb(void) { - return (const uint8_t *)&usbd_desc_device; -} - -const uint8_t *tud_descriptor_configuration_cb(uint8_t index) { - (void)index; - return usbd_desc_cfg; -} - -const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { - #define DESC_STR_MAX (20) - static uint16_t desc_str[DESC_STR_MAX]; - - uint8_t len; - if (index == 0) { - desc_str[1] = 0x0409; // supported language is English - len = 1; - } else { - if (index >= sizeof(usbd_desc_str) / sizeof(usbd_desc_str[0])) { - return NULL; - } - // check, if serial is requested - if (index == USBD_STR_SERIAL) { - pico_unique_board_id_t id; - pico_get_unique_board_id(&id); - // byte by byte conversion - for (len = 0; len < 16; len += 2) { - const char *hexdig = "0123456789abcdef"; - desc_str[1 + len] = hexdig[id.id[len >> 1] >> 4]; - desc_str[1 + len + 1] = hexdig[id.id[len >> 1] & 0x0f]; - } - } else { - const char *str = usbd_desc_str[index]; - for (len = 0; len < DESC_STR_MAX - 1 && str[len]; ++len) { - desc_str[1 + len] = str[len]; - } - } - } - - // first byte is length (including header), second byte is string type - desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * len + 2); - - return desc_str; -} diff --git a/ports/rp2/usbd.c b/ports/rp2/usbd.c new file mode 100644 index 0000000000000..a36006b87b118 --- /dev/null +++ b/ports/rp2/usbd.c @@ -0,0 +1,14 @@ + +#include "usbd.h" +#include "string.h" +#include "pico/unique_id.h" + +int usbd_serialnumber(uint8_t *buf) { + pico_unique_board_id_t id; + const int len = 8; + + pico_get_unique_board_id(&id); + memcpy(buf, id.id, len); + + return len; +} diff --git a/ports/rp2/tusb_config.h b/shared/tinyusb/tusb_config.h similarity index 79% rename from ports/rp2/tusb_config.h rename to shared/tinyusb/tusb_config.h index ce321616a6bc3..115637d86f0e6 100644 --- a/ports/rp2/tusb_config.h +++ b/shared/tinyusb/tusb_config.h @@ -22,9 +22,11 @@ * THE SOFTWARE. * */ -#ifndef MICROPY_INCLUDED_RP2_TUSB_CONFIG_H -#define MICROPY_INCLUDED_RP2_TUSB_CONFIG_H +#ifndef MICROPY_INCLUDED_EXTMOD_TUSB_CONFIG_H +#define MICROPY_INCLUDED_EXTMOD_TUSB_CONFIG_H + +#include #include "mpconfigport.h" #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE) @@ -41,4 +43,14 @@ #define CFG_TUD_MSC_BUFSIZE (MICROPY_FATFS_MAX_SS) #endif -#endif // MICROPY_INCLUDED_RP2_TUSB_CONFIG_H +#if MICROPY_HW_USB_HID +#define CFG_TUD_HID (1) +#endif + +// HID buffer size Should be sufficient to hold ID (if any) + Data +#define CFG_TUD_HID_EP_BUFSIZE (64) + +#define MICROPY_HW_USB_MAX_DESCRIPTORS (10) +#define MICROPY_HW_USB_MAX_HID (3) + +#endif // MICROPY_INCLUDED_EXTMOD_TUSB_CONFIG_H diff --git a/shared/tinyusb/usbd.c b/shared/tinyusb/usbd.c new file mode 100644 index 0000000000000..cd2324b32bb41 --- /dev/null +++ b/shared/tinyusb/usbd.c @@ -0,0 +1,50 @@ + +#include "py/runtime.h" +#include "usbd.h" +#include "tusb_config.h" + +// tusb.h is not available when running the string preprocessor +#ifndef NO_QSTR +#include "tusb.h" +#endif + +void usbd_reset_all(void) { + usbd_reset_descriptor(); + usbd_reset_hid(); +} + +STATIC mp_obj_t usbd_ready(void) { + return mp_obj_new_bool(tud_ready()); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(usbd_ready_obj, usbd_ready); + +STATIC mp_obj_t usbd_connected(void) { + return mp_obj_new_bool(tud_connected()); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(usbd_connected_obj, usbd_connected); + +STATIC mp_obj_t usbd_remote_wakeup(void) { + return mp_obj_new_bool(tud_remote_wakeup()); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(usbd_remote_wakeup_obj, usbd_remote_wakeup); + +STATIC const mp_rom_map_elem_t usb_device_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_usb_device) }, + + { MP_ROM_QSTR(MP_QSTR_ready), (mp_obj_t)&usbd_ready_obj}, + { MP_ROM_QSTR(MP_QSTR_connected), (mp_obj_t)&usbd_connected_obj}, + { MP_ROM_QSTR(MP_QSTR_remote_wakeup), (mp_obj_t)&usbd_remote_wakeup_obj}, + + { MP_ROM_QSTR(MP_QSTR_Descriptor), MP_ROM_PTR(&usbd_descriptor_type) }, + #if MICROPY_HW_USB_HID + { MP_ROM_QSTR(MP_QSTR_HID), MP_ROM_PTR(&usbd_hid_type) }, + #endif +}; +STATIC MP_DEFINE_CONST_DICT(usb_device_module_globals, usb_device_module_globals_table); + +const mp_obj_module_t mp_module_usb_device = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&usb_device_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_usb_device, mp_module_usb_device); diff --git a/shared/tinyusb/usbd.h b/shared/tinyusb/usbd.h new file mode 100644 index 0000000000000..23bd65012338a --- /dev/null +++ b/shared/tinyusb/usbd.h @@ -0,0 +1,24 @@ + +#ifndef MICROPY_INCLUDED_USBD_SHARED_H_ +#define MICROPY_INCLUDED_USBD_SHARED_H_ + +#include "py/obj.h" + +// defined externally (needed per port) + +int usbd_serialnumber(uint8_t *buf); + +// external use + +void usbd_reset_all(void); +void usbd_reset_descriptor(void); +void usbd_reset_hid(void); + +// internal + +extern const mp_obj_type_t usbd_descriptor_type; +extern const mp_obj_type_t usbd_hid_type; + +int usbd_desc_add_descriptor(uint8_t *desc, int len, char *name); + +#endif diff --git a/shared/tinyusb/usbd_descriptor.c b/shared/tinyusb/usbd_descriptor.c new file mode 100644 index 0000000000000..b16ee4d16c565 --- /dev/null +++ b/shared/tinyusb/usbd_descriptor.c @@ -0,0 +1,424 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 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/runtime.h" +#include "py/obj.h" +#include "py/runtime.h" +#include "py/mphal.h" +#include "usbd.h" +#include "mpconfigport.h" + +#define DESC_STR_MAX (31) + +// tusb.h is not available when running the string preprocessor +#ifndef NO_QSTR +#include "tusb.h" +#endif + +#if CFG_TUD_MSC +#define USBD_DESC_DEFAULT_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN) +#else +#define USBD_DESC_DEFAULT_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) +#endif +#define USBD_MAX_POWER_MA (250) + +#define USBD_ITF_CDC (0) // needs 2 interfaces +#define USBD_ITF_MSC (2) +#if CFG_TUD_MSC +#define USBD_ITF_MAX (3) +#else +#define USBD_ITF_MAX (2) +#endif + +#define USBD_CDC_EP_CMD (0x81) +#define USBD_CDC_EP_OUT (0x02) +#define USBD_CDC_EP_IN (0x82) +#define USBD_CDC_CMD_MAX_SIZE (8) +#define USBD_CDC_IN_OUT_MAX_SIZE (64) + +#define EPNUM_MSC_OUT (0x03) +#define EPNUM_MSC_IN (0x83) + +#define USBD_STR_0 (0x00) +#define USBD_STR_MANUF (0x01) +#define USBD_STR_PRODUCT (0x02) +#define USBD_STR_SERIAL (0x03) +#define USBD_STR_CDC (0x04) +#define USBD_STR_MSC (0x05) + +// Note: descriptors returned from callbacks must exist long enough for transfer to complete + +typedef struct _usb_device_descriptor_obj_t { + mp_obj_base_t base; +} usb_device_descriptor_obj_t; + +// singleton +STATIC const usb_device_descriptor_obj_t usb_device_descriptor_obj = {{&usbd_descriptor_type}}; + +STATIC mp_obj_t usb_device_descriptor_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 0, false); + + return (mp_obj_t)&usb_device_descriptor_obj; +} + +const tusb_desc_device_t usbd_desc_device_default = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .idVendor = MICROPY_HW_USB_VID, + .idProduct = MICROPY_HW_USB_PID, + .bcdDevice = 0x0100, + .iManufacturer = USBD_STR_MANUF, + .iProduct = USBD_STR_PRODUCT, + .iSerialNumber = USBD_STR_SERIAL, + .bNumConfigurations = 1, +}; +static tusb_desc_device_t usbd_desc_device; + +typedef struct { + uint8_t cfg[512]; + int len; + int itf_count; + int in_count; + int out_count; + char names[MICROPY_HW_USB_MAX_DESCRIPTORS][DESC_STR_MAX]; + int names_len; +} desc_cfg_t; +static desc_cfg_t usbd_desc_cfg; + +// device(bDeviceClass=None, bDeviceSubclass=None, bDeviceProtocol=None, +// idVendor=None, idProduct=None, bcdDevice=None, bmAttributes=None, bMaxPower=None) +STATIC mp_obj_t usb_device_descriptor_device(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { + ARG_bDeviceClass, ARG_bDeviceSubClass, ARG_bDeviceProtocol, + ARG_idVendor, ARG_idProduct, ARG_bcdDevice, + ARG_bmAttributes, ARG_bMaxPower + }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_bDeviceClass, MP_ARG_OBJ, {.u_obj = MP_ROM_NONE} }, + { MP_QSTR_bDeviceSubclass, MP_ARG_OBJ, {.u_obj = MP_ROM_NONE} }, + { MP_QSTR_bDeviceProtocol, MP_ARG_OBJ, {.u_obj = MP_ROM_NONE} }, + { MP_QSTR_idVendor, MP_ARG_OBJ, {.u_obj = MP_ROM_NONE} }, + { MP_QSTR_idProduct, MP_ARG_OBJ, {.u_obj = MP_ROM_NONE} }, + { MP_QSTR_bcdDevice, MP_ARG_OBJ, {.u_obj = MP_ROM_NONE} }, + { MP_QSTR_bmAttributes, MP_ARG_OBJ, {.u_obj = MP_ROM_NONE} }, + { MP_QSTR_bMaxPower, MP_ARG_OBJ, {.u_obj = MP_ROM_NONE} } + }; + tusb_desc_configuration_t *desc_config; + + desc_config = (tusb_desc_configuration_t*) usbd_desc_cfg.cfg; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if (args[ARG_bDeviceClass].u_obj != mp_const_none) { + usbd_desc_device.bDeviceClass = mp_obj_get_int(args[ARG_bDeviceClass].u_obj); + } + if (args[ARG_bDeviceSubClass].u_obj != mp_const_none) { + usbd_desc_device.bDeviceSubClass = mp_obj_get_int(args[ARG_bDeviceSubClass].u_obj); + } + if (args[ARG_bDeviceProtocol].u_obj != mp_const_none) { + usbd_desc_device.bDeviceProtocol = mp_obj_get_int(args[ARG_bDeviceProtocol].u_obj); + } + if (args[ARG_idVendor].u_obj != mp_const_none) { + usbd_desc_device.idVendor = mp_obj_get_int(args[ARG_idVendor].u_obj); + } + if (args[ARG_idProduct].u_obj != mp_const_none) { + usbd_desc_device.idProduct = mp_obj_get_int(args[ARG_idProduct].u_obj); + } + if (args[ARG_bDeviceClass].u_obj != mp_const_none) { + usbd_desc_device.bDeviceClass = mp_obj_get_int(args[ARG_bDeviceClass].u_obj); + } + if (args[ARG_bmAttributes].u_obj != mp_const_none) { + desc_config->bmAttributes = mp_obj_get_int(args[ARG_bmAttributes].u_obj); + } + if (args[ARG_bMaxPower].u_obj != mp_const_none) { + desc_config->bMaxPower = mp_obj_get_int(args[ARG_bMaxPower].u_obj); + } + + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(usb_device_descriptor_device_obj, 1, usb_device_descriptor_device); + +STATIC mp_obj_t usb_device_descriptor_strings(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { + ARG_Manufacturer, ARG_Product, ARG_CDC, ARG_MSC + }; + char *name; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_Manufacturer, MP_ARG_OBJ, {.u_obj = MP_ROM_NONE}}, + { MP_QSTR_Product, MP_ARG_OBJ, {.u_obj = MP_ROM_NONE}}, + { MP_QSTR_CDC, MP_ARG_OBJ, {.u_obj = MP_ROM_NONE}}, + { MP_QSTR_MSC, MP_ARG_OBJ, {.u_obj = MP_ROM_NONE}} + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if (args[ARG_Manufacturer].u_obj != mp_const_none) { + name = (char*) mp_obj_str_get_str(args[ARG_Manufacturer].u_obj); + strcpy(usbd_desc_cfg.names[1], name); + } + if (args[ARG_Product].u_obj != mp_const_none) { + name = (char*) mp_obj_str_get_str(args[ARG_Product].u_obj); + strcpy(usbd_desc_cfg.names[2], name); + } + if (args[ARG_CDC].u_obj != mp_const_none) { + name = (char*) mp_obj_str_get_str(args[ARG_CDC].u_obj); + strcpy(usbd_desc_cfg.names[4], name); + } + #if CFG_TUD_MSC + if (args[ARG_MSC].u_obj != mp_const_none) { + name = (char*) mp_obj_str_get_str(args[ARG_MSC].u_obj); + strcpy(usbd_desc_cfg.names[5], name); + } + #endif + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(usb_device_descriptor_strings_obj, 1, usb_device_descriptor_strings); + +uint8_t usbd_desc_cfg_cdc[] = {TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD, USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, USBD_CDC_IN_OUT_MAX_SIZE)}; + +#if CFG_TUD_MSC +uint8_t usbd_desc_cfg_msc[] = {TUD_MSC_DESCRIPTOR(USBD_ITF_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64)}; +#endif + +static int desc_cfg_namesfull(desc_cfg_t *cfg) { + if (cfg->names_len >= MICROPY_HW_USB_MAX_DESCRIPTORS) { + return -1; + } + return 0; +} + +static int desc_cfg_addname(desc_cfg_t *cfg, char *name) { + int len; + char *spot; + + if (desc_cfg_namesfull(cfg)) { + return -1; + } + len = strlen(name); + if (len > DESC_STR_MAX-1) { + len = DESC_STR_MAX-1; + } + spot = cfg->names[cfg->names_len]; + memcpy(spot, name, len); + spot[len] = 0; + cfg->names_len++; + return cfg->names_len - 1; +} + +static int desc_cfg_adddesc(desc_cfg_t *cfg, uint8_t *desc, int len, char *name) { + uint8_t *new_desc; + uint8_t *line; + tusb_desc_interface_t *desc_itf; + tusb_desc_endpoint_t *desc_endpoint; + int i; + + if (desc_cfg_namesfull(cfg)) { + return -1; + } + + // get the new spot + new_desc = &(cfg->cfg[cfg->len]); + // and copy the descriptor over + memcpy(new_desc, desc, len); + + for (i=0; ibAlternateSetting == 0) { + desc_itf->bInterfaceNumber = cfg->itf_count; + cfg->itf_count++; + if (name) { + desc_itf->iInterface = desc_cfg_addname(cfg, name); + name = NULL; // only save name once + } + } + } else if (line[1] == TUSB_DESC_ENDPOINT) { + desc_endpoint = (tusb_desc_endpoint_t*) line; + if (desc_endpoint->bEndpointAddress & 0x80) { // if it's in + desc_endpoint->bEndpointAddress = 0x80 | cfg->in_count; + cfg->in_count++; + } else { // if it's out + desc_endpoint->bEndpointAddress = cfg->out_count; + cfg->out_count++; + } + } + i += line[0]; // jump to the next "line" + if (line[0] == 0) { // no infinite loops for me! + return 0; // also, this means we didn't save it at all (desc_len not updated) + } + } + cfg->len += len; + + return 0; +} + + +int usbd_desc_add_descriptor(uint8_t *desc, int len, char *name) { + return desc_cfg_adddesc(&usbd_desc_cfg, desc, len, name); +} + +static void desc_cfg_init(desc_cfg_t *cfg) { + tusb_desc_configuration_t const initial_cfg = + { + .bLength = sizeof(tusb_desc_configuration_t), + .bDescriptorType = TUSB_DESC_CONFIGURATION, + + // Total Length & Interface Number will be updated later + .wTotalLength = 0, + .bNumInterfaces = 0, + .bConfigurationValue = 1, + .iConfiguration = 0x00, + .bmAttributes = TU_BIT(7) | TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, + .bMaxPower = TUSB_DESC_CONFIG_POWER_MA(USBD_MAX_POWER_MA) + }; + + // clear the config + memset(cfg->cfg, 0, sizeof(cfg->cfg)); + // copy the initial contents + memcpy(cfg->cfg, &initial_cfg, sizeof(tusb_desc_configuration_t)); + cfg->len = sizeof(tusb_desc_configuration_t); + cfg->itf_count = 0; + cfg->in_count = 1; + cfg->out_count = 1; + cfg->names_len = 0; + desc_cfg_addname(cfg, ""); // language code + desc_cfg_addname(cfg, "MicroPython"); + desc_cfg_addname(cfg, "Board in FS mode"); + desc_cfg_addname(cfg, ""); // this is the serial number, will be dynamic + + desc_cfg_adddesc(cfg, usbd_desc_cfg_cdc, sizeof(usbd_desc_cfg_cdc), "Board CDC"); + #if CFG_TUD_MSC + desc_cfg_adddesc(cfg, usbd_desc_cfg_msc, sizeof(usbd_desc_cfg_msc), "Board MSC"); + #endif +} + +static void desc_cfg_finalize(desc_cfg_t *cfg) { + tusb_desc_configuration_t *conf; + + conf = (tusb_desc_configuration_t*) cfg->cfg; + conf->wTotalLength = cfg->len; // update total length + conf->bNumInterfaces = cfg->itf_count; // update interface count +} + + +const uint8_t *tud_descriptor_device_cb(void) { + return (const uint8_t *)&usbd_desc_device; +} + +const uint8_t *tud_descriptor_configuration_cb(uint8_t index) { + (void)index; + + desc_cfg_finalize(&usbd_desc_cfg); + return usbd_desc_cfg.cfg; +} + +const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { + static uint16_t desc_str[DESC_STR_MAX]; + + uint8_t len; + if (index == 0) { + desc_str[1] = 0x0409; // supported language is English + len = 1; + } else { + if (index > usbd_desc_cfg.names_len) { + return NULL; + } + // check, if serial is requested + if (index == USBD_STR_SERIAL) { + uint8_t buffer[32]; + int buflen; + const char *hexdig = "0123456789abcdef"; + + buflen = usbd_serialnumber(buffer); + // byte by byte conversion + len = 0; + for (int i=0; i> 4]; + desc_str[2 + len] = hexdig[val & 0x0F]; + len += 2; + } + } else { + const char *str = usbd_desc_cfg.names[index]; + for (len = 0; len < DESC_STR_MAX - 1 && str[len]; ++len) { + desc_str[1 + len] = str[len]; + } + } + } + + // first byte is length (including header), second byte is string type + desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * len + 2); + + return desc_str; +} + +void usbd_reset_descriptor(void) { + usbd_desc_device = usbd_desc_device_default; + desc_cfg_init(&usbd_desc_cfg); +} + +STATIC const mp_rom_map_elem_t usb_device_descriptors_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_device), (mp_obj_t)&usb_device_descriptor_device_obj}, + { MP_ROM_QSTR(MP_QSTR_strings), (mp_obj_t)&usb_device_descriptor_strings_obj}, + + { MP_ROM_QSTR(MP_QSTR_CLASS_UNSPECIFIED), MP_ROM_INT(TUSB_CLASS_UNSPECIFIED)}, + { MP_ROM_QSTR(MP_QSTR_CLASS_AUDIO), MP_ROM_INT(TUSB_CLASS_AUDIO)}, + { MP_ROM_QSTR(MP_QSTR_CLASS_CDC), MP_ROM_INT(TUSB_CLASS_CDC)}, + { MP_ROM_QSTR(MP_QSTR_CLASS_HID), MP_ROM_INT(TUSB_CLASS_HID)}, + { MP_ROM_QSTR(MP_QSTR_CLASS_PHYSICAL), MP_ROM_INT(TUSB_CLASS_PHYSICAL)}, + { MP_ROM_QSTR(MP_QSTR_CLASS_IMAGE), MP_ROM_INT(TUSB_CLASS_IMAGE)}, + { MP_ROM_QSTR(MP_QSTR_CLASS_PRINTER), MP_ROM_INT(TUSB_CLASS_PRINTER)}, + { MP_ROM_QSTR(MP_QSTR_CLASS_MSC), MP_ROM_INT(TUSB_CLASS_MSC)}, + { MP_ROM_QSTR(MP_QSTR_CLASS_HUB), MP_ROM_INT(TUSB_CLASS_HUB)}, + { MP_ROM_QSTR(MP_QSTR_CLASS_CDC_DATA), MP_ROM_INT(TUSB_CLASS_CDC_DATA)}, + { MP_ROM_QSTR(MP_QSTR_CLASS_SMART_CARD), MP_ROM_INT(TUSB_CLASS_SMART_CARD)}, + { MP_ROM_QSTR(MP_QSTR_CLASS_CONTENT_SECURITY), MP_ROM_INT(TUSB_CLASS_CONTENT_SECURITY)}, + { MP_ROM_QSTR(MP_QSTR_CLASS_VIDEO), MP_ROM_INT(TUSB_CLASS_VIDEO)}, + { MP_ROM_QSTR(MP_QSTR_CLASS_PERSONAL_HEALTHCARE), MP_ROM_INT(TUSB_CLASS_PERSONAL_HEALTHCARE)}, + { MP_ROM_QSTR(MP_QSTR_CLASS_AUDIO_VIDEO), MP_ROM_INT(TUSB_CLASS_AUDIO_VIDEO)}, + { MP_ROM_QSTR(MP_QSTR_CLASS_DIAGNOSTIC), MP_ROM_INT(TUSB_CLASS_DIAGNOSTIC)}, + { MP_ROM_QSTR(MP_QSTR_CLASS_WIRELESS_CONTROLLER), MP_ROM_INT(TUSB_CLASS_WIRELESS_CONTROLLER)}, + { MP_ROM_QSTR(MP_QSTR_CLASS_MISC), MP_ROM_INT(TUSB_CLASS_MISC)}, + { MP_ROM_QSTR(MP_QSTR_CLASS_APPLICATION_SPECIFIC), MP_ROM_INT(TUSB_CLASS_APPLICATION_SPECIFIC)}, + { MP_ROM_QSTR(MP_QSTR_CLASS_VENDOR_SPECIFIC), MP_ROM_INT(TUSB_CLASS_VENDOR_SPECIFIC)}, +}; +STATIC MP_DEFINE_CONST_DICT(usb_device_descriptors_locals_dict, usb_device_descriptors_locals_dict_table); + +const mp_obj_type_t usbd_descriptor_type = { + { &mp_type_type }, + .name = MP_QSTR_Descriptors, + .make_new = usb_device_descriptor_make_new, + .locals_dict = (mp_obj_t)&usb_device_descriptors_locals_dict, +}; \ No newline at end of file diff --git a/shared/tinyusb/usbd_hid.c b/shared/tinyusb/usbd_hid.c new file mode 100644 index 0000000000000..747fff4a8a679 --- /dev/null +++ b/shared/tinyusb/usbd_hid.c @@ -0,0 +1,184 @@ + +#include "py/runtime.h" +#include "usbd.h" + +// tusb.h is not available when running the string preprocessor +#ifndef NO_QSTR +#include "tusb.h" +#endif + +#if MICROPY_HW_USB_HID + +typedef struct { + uint8_t descriptors[MICROPY_HW_USB_MAX_HID][255]; + int len; +} usbd_desc_hid_t; +usbd_desc_hid_t usbd_desc_hid; + +void usbd_reset_hid(void) { + usbd_desc_hid.len = 0; +} + +static int usbd_desc_hid_add(usbd_desc_hid_t *hid, char *name, uint8_t protocol, uint8_t ep_bufsize, uint8_t interval, uint8_t *desc, uint8_t desc_len) { + uint8_t *spot; + + // trim the endpoint buffer size if too big + if (ep_bufsize > CFG_TUD_HID_EP_BUFSIZE) { + ep_bufsize = CFG_TUD_HID_EP_BUFSIZE; + } + + uint8_t descriptor[] = { + TUD_HID_DESCRIPTOR(0, 0, protocol, desc_len, 0x80, ep_bufsize, interval), + }; + // check for hid slots + if (hid->len > MICROPY_HW_USB_MAX_HID) { + return -1; + } + // check for description too big + if (desc_len > 255) { + return -1; + } + + // try to save the usb descriptor + if (usbd_desc_add_descriptor(descriptor, sizeof(descriptor), name)) { + return -1; + } + + // save the hid descriptor + spot = hid->descriptors[hid->len]; + memcpy(spot, desc, desc_len); + hid->len++; + return 0; +} + +uint8_t const *tud_hid_descriptor_report_cb(uint8_t instance) { + if (instance >= usbd_desc_hid.len) { + return NULL; + } + return usbd_desc_hid.descriptors[instance]; +} + +void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol) { + (void) instance; + (void) protocol; +} + +void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len) { + (void) instance; + (void) report; + (void) len; +} + +uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) { + (void) instance; + (void) report_id; + (void) report_type; + (void) buffer; + (void) reqlen; + + return 0; +} + +void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) { + (void) instance; + (void) report_id; + (void) report_type; + (void) buffer; + (void) bufsize; +} + +typedef struct _usb_device_hid_obj_t { + mp_obj_base_t base; +} usb_device_hid_obj_t; + +// singleton +STATIC const usb_device_hid_obj_t usb_device_hid_obj = {{&usbd_hid_type}}; + +STATIC mp_obj_t usb_device_hid_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 0, false); + + return (mp_obj_t)&usb_device_hid_obj; +} + +// HID.add_descriptor(name, protocol, ep_bufsize, interval, descriptor) +STATIC mp_obj_t usbd_hid_add_descriptor(size_t n_args, const mp_obj_t *args) { + (void) n_args; + mp_buffer_info_t bufinfo = {0}; + char *name; + uint8_t protocol; + uint8_t ep_bufsize; + uint8_t interval; + int err; + + mp_check_self(mp_obj_is_str_or_bytes(args[1])); + name = (char*) mp_obj_str_get_str(args[1]); + protocol = mp_obj_get_int(args[2]); + ep_bufsize = mp_obj_get_int(args[3]); + interval = mp_obj_get_int(args[4]); + mp_get_buffer_raise(args[5], &bufinfo, MP_BUFFER_READ); + err = usbd_desc_hid_add(&usbd_desc_hid, name, protocol, ep_bufsize, interval, bufinfo.buf, bufinfo.len); + if (err) { + mp_raise_ValueError(MP_ERROR_TEXT("could not add descriptor")); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(usbd_hid_add_descriptor_obj, 6, 6, usbd_hid_add_descriptor); + +// HID.report(instance, report_id, buffer) +STATIC mp_obj_t usbd_hid_report(size_t n_args, const mp_obj_t *args) { + (void) n_args; + mp_buffer_info_t bufinfo = {0}; + uint8_t report_id; + uint8_t instance; + + instance = mp_obj_get_int(args[1]); + report_id = mp_obj_get_int(args[2]); + mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); + + // run the task once if not ready, then just exit + if (!tud_hid_n_ready(instance)) { // if the hid is not ready + if (tud_task_event_ready()) { // if there is an event ready, process it then try again + tud_task(); + if (!tud_hid_n_ready(instance)) { // processed but still not ready, return false + return mp_obj_new_bool(false); + } + } else { // no event and not ready, return false + return mp_obj_new_bool(false); + } + } + + return mp_obj_new_bool(tud_hid_n_report(instance, report_id, bufinfo.buf, bufinfo.len)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(usbd_hid_report_obj, 4, 4, usbd_hid_report); + +// HID.ready(instance) +STATIC mp_obj_t usbd_hid_ready(mp_obj_t obj0, mp_obj_t obj1) { + (void) obj0; + int instance; + + instance = mp_obj_get_int(obj1); + return mp_obj_new_bool(tud_hid_n_ready(instance)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(usbd_hid_ready_obj, usbd_hid_ready); + +STATIC const mp_rom_map_elem_t usb_device_hid_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_add_descriptor), (mp_obj_t)&usbd_hid_add_descriptor_obj}, + { MP_ROM_QSTR(MP_QSTR_report), (mp_obj_t)&usbd_hid_report_obj}, + { MP_ROM_QSTR(MP_QSTR_ready), (mp_obj_t)&usbd_hid_ready_obj}, +}; +STATIC MP_DEFINE_CONST_DICT(usb_device_hid_locals_dict, usb_device_hid_locals_dict_table); + +const mp_obj_type_t usbd_hid_type = { + { &mp_type_type }, + .name = MP_QSTR_HID, + .make_new = usb_device_hid_make_new, + .locals_dict = (mp_obj_t)&usb_device_hid_locals_dict, +}; + +#else // #if MICROPY_HW_USB_HID + +void usbd_reset_hid(void) { + +} + +#endif \ No newline at end of file pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy