From e7694fc77f1f30ee693d3697460639a6a492a2f6 Mon Sep 17 00:00:00 2001 From: Drashna Jael're Date: Wed, 20 Sep 2023 19:20:38 -0700 Subject: [PATCH] [Keyboard] !!! USB HOST !!! Quantizer Mini (20999) --- .../sekigon/keyboard_quantizer/mini/c1.h | 8 + .../sekigon/keyboard_quantizer/mini/c1_main.c | 48 + .../sekigon/keyboard_quantizer/mini/c1_usbh.c | 24 + .../sekigon/keyboard_quantizer/mini/config.h | 20 + .../sekigon/keyboard_quantizer/mini/info.json | 259 +++ .../mini/keymaps/default/keymap.json | 201 ++ .../mini/lib/Pico-PIO-USB/LICENSE | 21 + .../mini/lib/Pico-PIO-USB/src/pio_usb.c | 466 +++++ .../mini/lib/Pico-PIO-USB/src/pio_usb.h | 32 + .../Pico-PIO-USB/src/pio_usb_configuration.h | 54 + .../lib/Pico-PIO-USB/src/pio_usb_device.c | 498 +++++ .../mini/lib/Pico-PIO-USB/src/pio_usb_host.c | 1334 ++++++++++++++ .../mini/lib/Pico-PIO-USB/src/pio_usb_ll.h | 196 ++ .../mini/lib/Pico-PIO-USB/src/usb_crc.c | 66 + .../mini/lib/Pico-PIO-USB/src/usb_crc.h | 18 + .../lib/Pico-PIO-USB/src/usb_definitions.h | 342 ++++ .../mini/lib/Pico-PIO-USB/src/usb_rx.pio | 238 +++ .../mini/lib/Pico-PIO-USB/src/usb_rx.pio.h | 246 +++ .../mini/lib/Pico-PIO-USB/src/usb_tx.pio | 217 +++ .../mini/lib/Pico-PIO-USB/src/usb_tx.pio.h | 218 +++ .../mini/lib/tinyusb/LICENSE | 21 + .../mini/lib/tinyusb/src/class/hid/hid.h | 1131 ++++++++++++ .../mini/lib/tinyusb/src/class/hid/hid_host.c | 677 +++++++ .../mini/lib/tinyusb/src/class/hid/hid_host.h | 152 ++ .../mini/lib/tinyusb/src/common/tusb_common.h | 281 +++ .../lib/tinyusb/src/common/tusb_compiler.h | 281 +++ .../mini/lib/tinyusb/src/common/tusb_debug.h | 178 ++ .../mini/lib/tinyusb/src/common/tusb_fifo.c | 1065 +++++++++++ .../mini/lib/tinyusb/src/common/tusb_fifo.h | 206 +++ .../mini/lib/tinyusb/src/common/tusb_mcu.h | 308 ++++ .../lib/tinyusb/src/common/tusb_private.h | 173 ++ .../lib/tinyusb/src/common/tusb_timeout.h | 80 + .../mini/lib/tinyusb/src/common/tusb_types.h | 581 ++++++ .../mini/lib/tinyusb/src/common/tusb_verify.h | 156 ++ .../mini/lib/tinyusb/src/host/hcd.h | 223 +++ .../mini/lib/tinyusb/src/host/hub.c | 490 +++++ .../mini/lib/tinyusb/src/host/hub.h | 219 +++ .../mini/lib/tinyusb/src/host/usbh.c | 1633 +++++++++++++++++ .../mini/lib/tinyusb/src/host/usbh.h | 259 +++ .../lib/tinyusb/src/host/usbh_classdriver.h | 96 + .../mini/lib/tinyusb/src/osal/osal.h | 96 + .../raspberrypi/pio_usb/hcd_pio_usb.c | 225 +++ .../mini/lib/tinyusb/src/tusb.c | 523 ++++++ .../mini/lib/tinyusb/src/tusb.h | 138 ++ .../mini/lib/tinyusb/src/tusb_option.h | 440 +++++ .../sekigon/keyboard_quantizer/mini/matrix.c | 163 ++ .../sekigon/keyboard_quantizer/mini/mcuconf.h | 9 + .../sekigon/keyboard_quantizer/mini/mini.c | 20 + .../sekigon/keyboard_quantizer/mini/readme.md | 26 + .../sekigon/keyboard_quantizer/mini/rules.mk | 27 + .../keyboard_quantizer/mini/tusb_config.h | 85 + .../keyboard_quantizer/mini/tusb_os_custom.c | 24 + .../keyboard_quantizer/mini/tusb_os_custom.h | 128 ++ 53 files changed, 14620 insertions(+) create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/c1.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/c1_main.c create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/c1_usbh.c create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/config.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/info.json create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/keymaps/default/keymap.json create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/LICENSE create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb.c create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb_configuration.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb_device.c create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb_host.c create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb_ll.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_crc.c create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_crc.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_definitions.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_rx.pio create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_rx.pio.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_tx.pio create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_tx.pio.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/LICENSE create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/class/hid/hid.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/class/hid/hid_host.c create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/class/hid/hid_host.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_common.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_compiler.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_debug.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_fifo.c create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_fifo.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_mcu.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_private.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_timeout.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_types.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_verify.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/hcd.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/hub.c create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/hub.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/usbh.c create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/usbh.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/usbh_classdriver.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/osal/osal.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/tusb.c create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/tusb.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/tusb_option.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/matrix.c create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/mcuconf.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/mini.c create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/readme.md create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/rules.mk create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/tusb_config.h create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/tusb_os_custom.c create mode 100644 keyboards/sekigon/keyboard_quantizer/mini/tusb_os_custom.h diff --git a/keyboards/sekigon/keyboard_quantizer/mini/c1.h b/keyboards/sekigon/keyboard_quantizer/mini/c1.h new file mode 100644 index 0000000000..0135bd27e3 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/c1.h @@ -0,0 +1,8 @@ +// Copyright 2023 sekigon-gonnoc +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +void c1_main_task(void); +void c1_usbh(void); +void c1_start_timer(void); \ No newline at end of file diff --git a/keyboards/sekigon/keyboard_quantizer/mini/c1_main.c b/keyboards/sekigon/keyboard_quantizer/mini/c1_main.c new file mode 100644 index 0000000000..6a45571fb8 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/c1_main.c @@ -0,0 +1,48 @@ +// Copyright 2023 sekigon-gonnoc +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "c1.h" +#include "ch.h" +#include "hal.h" + +static virtual_timer_t vt; + +// Main process for core1 +static THD_WORKING_AREA(wa_c1_main_task_wrapper, 2048); +static THD_FUNCTION(c1_main_task_wrapper, arg) { + while (1) { + c1_main_task(); + chThdSleepMicroseconds(125); + } +} + +// Entry point of core1 +void c1_main(void) { + chSysWaitSystemState(ch_sys_running); + chInstanceObjectInit(&ch1, &ch_core1_cfg); + chSysUnlock(); + + // USB host stack uses PIO and DMA + hal_lld_peripheral_unreset(RESETS_ALLREG_PIO0); + hal_lld_peripheral_unreset(RESETS_ALLREG_PIO1); + hal_lld_peripheral_unreset(RESETS_ALLREG_DMA); + + // Initialize USB host stack + c1_usbh(); + + // Start main task + chThdCreateStatic(wa_c1_main_task_wrapper, sizeof(wa_c1_main_task_wrapper), NORMALPRIO + 1, c1_main_task_wrapper, NULL); +} + +// 1ms repeat timer for USB frame +void sof_timer(void *); +static void timer_cb(virtual_timer_t *_vt, void *_) { + // Start USB frame + sof_timer(NULL); +} + +// Start 1ms timer +void c1_start_timer(void) { + chVTObjectInit(&vt); + chVTSetContinuous(&vt, TIME_MS2I(1), timer_cb, NULL); +} \ No newline at end of file diff --git a/keyboards/sekigon/keyboard_quantizer/mini/c1_usbh.c b/keyboards/sekigon/keyboard_quantizer/mini/c1_usbh.c new file mode 100644 index 0000000000..3c6bf1d7d1 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/c1_usbh.c @@ -0,0 +1,24 @@ +// Copyright 2023 sekigon-gonnoc +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "c1.h" +#include "tusb.h" +#include "pio_usb.h" +#include "pio_usb_ll.h" +#include "hardware/sync.h" + +// Initialize USB host stack on core1 +void c1_usbh(void) { + pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG; + pio_cfg.pin_dp = 4; + pio_cfg.extra_error_retry_count = 10; + tuh_configure(1, TUH_CFGID_RPI_PIO_USB_CONFIGURATION, &pio_cfg); + + tuh_init(1); + c1_start_timer(); +} + +// USB host stack main task +void c1_main_task(void) { + tuh_task(); +} \ No newline at end of file diff --git a/keyboards/sekigon/keyboard_quantizer/mini/config.h b/keyboards/sekigon/keyboard_quantizer/mini/config.h new file mode 100644 index 0000000000..1492def5aa --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/config.h @@ -0,0 +1,20 @@ +// Copyright 2023 sekigon-gonnoc (@sekigon-gonnoc) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +/* + * Feature disable options + * These options are also useful to firmware size reduction. + */ + +/* disable debug print */ +//#define NO_DEBUG + +/* disable print */ +//#define NO_PRINT + +/* disable action features */ +//#define NO_ACTION_LAYER +//#define NO_ACTION_TAPPING +//#define NO_ACTION_ONESHOT diff --git a/keyboards/sekigon/keyboard_quantizer/mini/info.json b/keyboards/sekigon/keyboard_quantizer/mini/info.json new file mode 100644 index 0000000000..491c2e52d5 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/info.json @@ -0,0 +1,259 @@ +{ + "manufacturer": "sekigon-gonnoc", + "keyboard_name": "sekigon/keyboard_quantizer/mini", + "maintainer": "sekigon-gonnoc", + "bootloader": "rp2040", + "diode_direction": "COL2ROW", + "features": { + "bootmagic": true, + "command": true, + "console": true, + "extrakey": true, + "mousekey": true, + "nkro": true, + "virtser": true + }, + "matrix_pins": { + "cols": [ + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN" + ], + "rows": [ + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN", + "NO_PIN" + ] + }, + "processor": "RP2040", + "url": "", + "usb": { + "device_version": "1.0.0", + "pid": "0x999B", + "vid": "0xFEED" + }, + "layouts": { + "LAYOUT": { + "layout": [ + { "matrix": [0, 0], "x": 0, "y": 0 }, + { "matrix": [0, 1], "x": 1, "y": 0 }, + { "matrix": [0, 2], "x": 2, "y": 0 }, + { "matrix": [0, 3], "x": 3, "y": 0 }, + { "matrix": [0, 4], "x": 4, "y": 0 }, + { "matrix": [0, 5], "x": 5, "y": 0 }, + { "matrix": [0, 6], "x": 6, "y": 0 }, + { "matrix": [0, 7], "x": 7, "y": 0 }, + { "matrix": [1, 0], "x": 0, "y": 1 }, + { "matrix": [1, 1], "x": 1, "y": 1 }, + { "matrix": [1, 2], "x": 2, "y": 1 }, + { "matrix": [1, 3], "x": 3, "y": 1 }, + { "matrix": [1, 4], "x": 4, "y": 1 }, + { "matrix": [1, 5], "x": 5, "y": 1 }, + { "matrix": [1, 6], "x": 6, "y": 1 }, + { "matrix": [1, 7], "x": 7, "y": 1 }, + { "matrix": [2, 0], "x": 0, "y": 2 }, + { "matrix": [2, 1], "x": 1, "y": 2 }, + { "matrix": [2, 2], "x": 2, "y": 2 }, + { "matrix": [2, 3], "x": 3, "y": 2 }, + { "matrix": [2, 4], "x": 4, "y": 2 }, + { "matrix": [2, 5], "x": 5, "y": 2 }, + { "matrix": [2, 6], "x": 6, "y": 2 }, + { "matrix": [2, 7], "x": 7, "y": 2 }, + { "matrix": [3, 0], "x": 0, "y": 3 }, + { "matrix": [3, 1], "x": 1, "y": 3 }, + { "matrix": [3, 2], "x": 2, "y": 3 }, + { "matrix": [3, 3], "x": 3, "y": 3 }, + { "matrix": [3, 4], "x": 4, "y": 3 }, + { "matrix": [3, 5], "x": 5, "y": 3 }, + { "matrix": [3, 6], "x": 6, "y": 3 }, + { "matrix": [3, 7], "x": 7, "y": 3 }, + { "matrix": [4, 0], "x": 0, "y": 4 }, + { "matrix": [4, 1], "x": 1, "y": 4 }, + { "matrix": [4, 2], "x": 2, "y": 4 }, + { "matrix": [4, 3], "x": 3, "y": 4 }, + { "matrix": [4, 4], "x": 4, "y": 4 }, + { "matrix": [4, 5], "x": 5, "y": 4 }, + { "matrix": [4, 6], "x": 6, "y": 4 }, + { "matrix": [4, 7], "x": 7, "y": 4 }, + { "matrix": [5, 0], "x": 0, "y": 5 }, + { "matrix": [5, 1], "x": 1, "y": 5 }, + { "matrix": [5, 2], "x": 2, "y": 5 }, + { "matrix": [5, 3], "x": 3, "y": 5 }, + { "matrix": [5, 4], "x": 4, "y": 5 }, + { "matrix": [5, 5], "x": 5, "y": 5 }, + { "matrix": [5, 6], "x": 6, "y": 5 }, + { "matrix": [5, 7], "x": 7, "y": 5 }, + { "matrix": [6, 0], "x": 0, "y": 6 }, + { "matrix": [6, 1], "x": 1, "y": 6 }, + { "matrix": [6, 2], "x": 2, "y": 6 }, + { "matrix": [6, 3], "x": 3, "y": 6 }, + { "matrix": [6, 4], "x": 4, "y": 6 }, + { "matrix": [6, 5], "x": 5, "y": 6 }, + { "matrix": [6, 6], "x": 6, "y": 6 }, + { "matrix": [6, 7], "x": 7, "y": 6 }, + { "matrix": [7, 0], "x": 0, "y": 7 }, + { "matrix": [7, 1], "x": 1, "y": 7 }, + { "matrix": [7, 2], "x": 2, "y": 7 }, + { "matrix": [7, 3], "x": 3, "y": 7 }, + { "matrix": [7, 4], "x": 4, "y": 7 }, + { "matrix": [7, 5], "x": 5, "y": 7 }, + { "matrix": [7, 6], "x": 6, "y": 7 }, + { "matrix": [7, 7], "x": 7, "y": 7 }, + { "matrix": [8, 0], "x": 0, "y": 8 }, + { "matrix": [8, 1], "x": 1, "y": 8 }, + { "matrix": [8, 2], "x": 2, "y": 8 }, + { "matrix": [8, 3], "x": 3, "y": 8 }, + { "matrix": [8, 4], "x": 4, "y": 8 }, + { "matrix": [8, 5], "x": 5, "y": 8 }, + { "matrix": [8, 6], "x": 6, "y": 8 }, + { "matrix": [8, 7], "x": 7, "y": 8 }, + { "matrix": [9, 0], "x": 0, "y": 9 }, + { "matrix": [9, 1], "x": 1, "y": 9 }, + { "matrix": [9, 2], "x": 2, "y": 9 }, + { "matrix": [9, 3], "x": 3, "y": 9 }, + { "matrix": [9, 4], "x": 4, "y": 9 }, + { "matrix": [9, 5], "x": 5, "y": 9 }, + { "matrix": [9, 6], "x": 6, "y": 9 }, + { "matrix": [9, 7], "x": 7, "y": 9 }, + { "matrix": [10, 0], "x": 0, "y": 10 }, + { "matrix": [10, 1], "x": 1, "y": 10 }, + { "matrix": [10, 2], "x": 2, "y": 10 }, + { "matrix": [10, 3], "x": 3, "y": 10 }, + { "matrix": [10, 4], "x": 4, "y": 10 }, + { "matrix": [10, 5], "x": 5, "y": 10 }, + { "matrix": [10, 6], "x": 6, "y": 10 }, + { "matrix": [10, 7], "x": 7, "y": 10 }, + { "matrix": [11, 0], "x": 0, "y": 11 }, + { "matrix": [11, 1], "x": 1, "y": 11 }, + { "matrix": [11, 2], "x": 2, "y": 11 }, + { "matrix": [11, 3], "x": 3, "y": 11 }, + { "matrix": [11, 4], "x": 4, "y": 11 }, + { "matrix": [11, 5], "x": 5, "y": 11 }, + { "matrix": [11, 6], "x": 6, "y": 11 }, + { "matrix": [11, 7], "x": 7, "y": 11 }, + { "matrix": [12, 0], "x": 0, "y": 12 }, + { "matrix": [12, 1], "x": 1, "y": 12 }, + { "matrix": [12, 2], "x": 2, "y": 12 }, + { "matrix": [12, 3], "x": 3, "y": 12 }, + { "matrix": [12, 4], "x": 4, "y": 12 }, + { "matrix": [12, 5], "x": 5, "y": 12 }, + { "matrix": [12, 6], "x": 6, "y": 12 }, + { "matrix": [12, 7], "x": 7, "y": 12 }, + { "matrix": [13, 0], "x": 0, "y": 13 }, + { "matrix": [13, 1], "x": 1, "y": 13 }, + { "matrix": [13, 2], "x": 2, "y": 13 }, + { "matrix": [13, 3], "x": 3, "y": 13 }, + { "matrix": [13, 4], "x": 4, "y": 13 }, + { "matrix": [13, 5], "x": 5, "y": 13 }, + { "matrix": [13, 6], "x": 6, "y": 13 }, + { "matrix": [13, 7], "x": 7, "y": 13 }, + { "matrix": [14, 0], "x": 0, "y": 14 }, + { "matrix": [14, 1], "x": 1, "y": 14 }, + { "matrix": [14, 2], "x": 2, "y": 14 }, + { "matrix": [14, 3], "x": 3, "y": 14 }, + { "matrix": [14, 4], "x": 4, "y": 14 }, + { "matrix": [14, 5], "x": 5, "y": 14 }, + { "matrix": [14, 6], "x": 6, "y": 14 }, + { "matrix": [14, 7], "x": 7, "y": 14 }, + { "matrix": [15, 0], "x": 0, "y": 15 }, + { "matrix": [15, 1], "x": 1, "y": 15 }, + { "matrix": [15, 2], "x": 2, "y": 15 }, + { "matrix": [15, 3], "x": 3, "y": 15 }, + { "matrix": [15, 4], "x": 4, "y": 15 }, + { "matrix": [15, 5], "x": 5, "y": 15 }, + { "matrix": [15, 6], "x": 6, "y": 15 }, + { "matrix": [15, 7], "x": 7, "y": 15 }, + { "matrix": [16, 0], "x": 0, "y": 16 }, + { "matrix": [16, 1], "x": 1, "y": 16 }, + { "matrix": [16, 2], "x": 2, "y": 16 }, + { "matrix": [16, 3], "x": 3, "y": 16 }, + { "matrix": [16, 4], "x": 4, "y": 16 }, + { "matrix": [16, 5], "x": 5, "y": 16 }, + { "matrix": [16, 6], "x": 6, "y": 16 }, + { "matrix": [16, 7], "x": 7, "y": 16 }, + { "matrix": [17, 0], "x": 0, "y": 17 }, + { "matrix": [17, 1], "x": 1, "y": 17 }, + { "matrix": [17, 2], "x": 2, "y": 17 }, + { "matrix": [17, 3], "x": 3, "y": 17 }, + { "matrix": [17, 4], "x": 4, "y": 17 }, + { "matrix": [17, 5], "x": 5, "y": 17 }, + { "matrix": [17, 6], "x": 6, "y": 17 }, + { "matrix": [17, 7], "x": 7, "y": 17 }, + { "matrix": [18, 0], "x": 0, "y": 18 }, + { "matrix": [18, 1], "x": 1, "y": 18 }, + { "matrix": [18, 2], "x": 2, "y": 18 }, + { "matrix": [18, 3], "x": 3, "y": 18 }, + { "matrix": [18, 4], "x": 4, "y": 18 }, + { "matrix": [18, 5], "x": 5, "y": 18 }, + { "matrix": [18, 6], "x": 6, "y": 18 }, + { "matrix": [18, 7], "x": 7, "y": 18 }, + { "matrix": [19, 0], "x": 0, "y": 19 }, + { "matrix": [19, 1], "x": 1, "y": 19 }, + { "matrix": [19, 2], "x": 2, "y": 19 }, + { "matrix": [19, 3], "x": 3, "y": 19 }, + { "matrix": [19, 4], "x": 4, "y": 19 }, + { "matrix": [19, 5], "x": 5, "y": 19 }, + { "matrix": [19, 6], "x": 6, "y": 19 }, + { "matrix": [19, 7], "x": 7, "y": 19 }, + { "matrix": [20, 0], "x": 0, "y": 20 }, + { "matrix": [20, 1], "x": 1, "y": 20 }, + { "matrix": [20, 2], "x": 2, "y": 20 }, + { "matrix": [20, 3], "x": 3, "y": 20 }, + { "matrix": [20, 4], "x": 4, "y": 20 }, + { "matrix": [20, 5], "x": 5, "y": 20 }, + { "matrix": [20, 6], "x": 6, "y": 20 }, + { "matrix": [20, 7], "x": 7, "y": 20 }, + { "matrix": [21, 0], "x": 0, "y": 21 }, + { "matrix": [21, 1], "x": 1, "y": 21 }, + { "matrix": [21, 2], "x": 2, "y": 21 }, + { "matrix": [21, 3], "x": 3, "y": 21 }, + { "matrix": [21, 4], "x": 4, "y": 21 }, + { "matrix": [21, 5], "x": 5, "y": 21 }, + { "matrix": [21, 6], "x": 6, "y": 21 }, + { "matrix": [21, 7], "x": 7, "y": 21 }, + { "matrix": [22, 0], "x": 0, "y": 22 }, + { "matrix": [22, 1], "x": 1, "y": 22 }, + { "matrix": [22, 2], "x": 2, "y": 22 }, + { "matrix": [22, 3], "x": 3, "y": 22 }, + { "matrix": [22, 4], "x": 4, "y": 22 }, + { "matrix": [22, 5], "x": 5, "y": 22 }, + { "matrix": [22, 6], "x": 6, "y": 22 }, + { "matrix": [22, 7], "x": 7, "y": 22 }, + { "matrix": [23, 0], "x": 0, "y": 23 }, + { "matrix": [23, 1], "x": 1, "y": 23 }, + { "matrix": [23, 2], "x": 2, "y": 23 }, + { "matrix": [23, 3], "x": 3, "y": 23 }, + { "matrix": [23, 4], "x": 4, "y": 23 }, + { "matrix": [23, 5], "x": 5, "y": 23 }, + { "matrix": [23, 6], "x": 6, "y": 23 }, + { "matrix": [23, 7], "x": 7, "y": 23 } + ] + } + } +} \ No newline at end of file diff --git a/keyboards/sekigon/keyboard_quantizer/mini/keymaps/default/keymap.json b/keyboards/sekigon/keyboard_quantizer/mini/keymaps/default/keymap.json new file mode 100644 index 0000000000..a601c4b00c --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/keymaps/default/keymap.json @@ -0,0 +1,201 @@ +{ + "keyboard": "sekigon/keyboard_quantizer/mini", + "keymap": "default", + "layout": "LAYOUT", + "layers": [ + [ + "KC_NO", + "KC_TRANSPARENT", + "KC_NO", + "KC_NO", + "KC_A", + "KC_B", + "KC_C", + "KC_D", + "KC_E", + "KC_F", + "KC_G", + "KC_H", + "KC_I", + "KC_J", + "KC_K", + "KC_L", + "KC_M", + "KC_N", + "KC_O", + "KC_P", + "KC_Q", + "KC_R", + "KC_S", + "KC_T", + "KC_U", + "KC_V", + "KC_W", + "KC_X", + "KC_Y", + "KC_Z", + "KC_1", + "KC_2", + "KC_3", + "KC_4", + "KC_5", + "KC_6", + "KC_7", + "KC_8", + "KC_9", + "KC_0", + "KC_ENTER", + "KC_ESCAPE", + "KC_BACKSPACE", + "KC_TAB", + "KC_SPACE", + "KC_MINUS", + "KC_EQUAL", + "KC_LEFT_BRACKET", + "KC_RIGHT_BRACKET", + "KC_BACKSLASH", + "KC_NONUS_HASH", + "KC_SEMICOLON", + "KC_QUOTE", + "KC_GRAVE", + "KC_COMMA", + "KC_DOT", + "KC_SLASH", + "KC_CAPS_LOCK", + "KC_F1", + "KC_F2", + "KC_F3", + "KC_F4", + "KC_F5", + "KC_F6", + "KC_F7", + "KC_F8", + "KC_F9", + "KC_F10", + "KC_F11", + "KC_F12", + "KC_PRINT_SCREEN", + "KC_SCROLL_LOCK", + "KC_PAUSE", + "KC_INSERT", + "KC_HOME", + "KC_PAGE_UP", + "KC_DELETE", + "KC_END", + "KC_PAGE_DOWN", + "KC_RIGHT", + "KC_LEFT", + "KC_DOWN", + "KC_UP", + "KC_NUM_LOCK", + "KC_KP_SLASH", + "KC_KP_ASTERISK", + "KC_KP_MINUS", + "KC_KP_PLUS", + "KC_KP_ENTER", + "KC_KP_1", + "KC_KP_2", + "KC_KP_3", + "KC_KP_4", + "KC_KP_5", + "KC_KP_6", + "KC_KP_7", + "KC_KP_8", + "KC_KP_9", + "KC_KP_0", + "KC_KP_DOT", + "KC_NONUS_BACKSLASH", + "KC_APPLICATION", + "KC_KB_POWER", + "KC_KP_EQUAL", + "KC_F13", + "KC_F14", + "KC_F15", + "KC_F16", + "KC_F17", + "KC_F18", + "KC_F19", + "KC_F20", + "KC_F21", + "KC_F22", + "KC_F23", + "KC_F24", + "KC_EXECUTE", + "KC_HELP", + "KC_MENU", + "KC_SELECT", + "KC_STOP", + "KC_AGAIN", + "KC_UNDO", + "KC_CUT", + "KC_COPY", + "KC_PASTE", + "KC_FIND", + "KC_KB_MUTE", + "KC_KB_VOLUME_UP", + "KC_KB_VOLUME_DOWN", + "KC_LOCKING_CAPS_LOCK", + "KC_LOCKING_NUM_LOCK", + "KC_LOCKING_SCROLL_LOCK", + "KC_KP_COMMA", + "KC_KP_EQUAL_AS400", + "KC_INTERNATIONAL_1", + "KC_INTERNATIONAL_2", + "KC_INTERNATIONAL_3", + "KC_INTERNATIONAL_4", + "KC_INTERNATIONAL_5", + "KC_INTERNATIONAL_6", + "KC_INTERNATIONAL_7", + "KC_INTERNATIONAL_8", + "KC_INTERNATIONAL_9", + "KC_LANGUAGE_1", + "KC_LANGUAGE_2", + "KC_LANGUAGE_3", + "KC_LANGUAGE_4", + "KC_LANGUAGE_5", + "KC_LANGUAGE_6", + "KC_LANGUAGE_7", + "KC_LANGUAGE_8", + "KC_LANGUAGE_9", + "KC_ALTERNATE_ERASE", + "KC_SYSTEM_REQUEST", + "KC_CANCEL", + "KC_CLEAR", + "KC_PRIOR", + "KC_RETURN", + "KC_SEPARATOR", + "KC_OUT", + "KC_OPER", + "KC_CLEAR_AGAIN", + "KC_CRSEL", + "KC_EXSEL", + "KC_SYSTEM_POWER", + "KC_SYSTEM_SLEEP", + "KC_SYSTEM_WAKE", + "KC_LEFT_CTRL", + "KC_LEFT_SHIFT", + "KC_LEFT_ALT", + "KC_LEFT_GUI", + "KC_RIGHT_CTRL", + "KC_RIGHT_SHIFT", + "KC_RIGHT_ALT", + "KC_RIGHT_GUI", + "KC_MS_BTN1", + "KC_MS_BTN2", + "KC_MS_BTN3", + "KC_MS_BTN4", + "KC_MS_BTN5", + "KC_MS_BTN6", + "KC_MS_BTN7", + "KC_MS_BTN8", + "KC_NO", + "KC_NO", + "KC_NO", + "KC_NO", + "KC_MS_WH_UP", + "KC_MS_WH_DOWN", + "KC_MS_WH_LEFT", + "KC_MS_WH_RIGHT" + ] + ] +} \ No newline at end of file diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/LICENSE b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/LICENSE new file mode 100644 index 0000000000..780f9c4f61 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 sekigon-gonnoc + +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. diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb.c b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb.c new file mode 100644 index 0000000000..3f92eb8a97 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb.c @@ -0,0 +1,466 @@ +/** + * Copyright (c) 2021 sekigon-gonnoc + */ + +#pragma GCC push_options +#pragma GCC optimize("-O3") + +#include +#include +#include // memcpy + +#include "hardware/clocks.h" +#include "hardware/dma.h" +#include "hardware/pio.h" +#include "hardware/sync.h" +#include "pico/bootrom.h" +#include "pico/stdlib.h" + +#include "pio_usb.h" +#include "usb_definitions.h" +#include "pio_usb_ll.h" +#include "usb_crc.h" +#include "usb_tx.pio.h" +#include "usb_rx.pio.h" + +#define UNUSED_PARAMETER(x) (void)x + +usb_device_t pio_usb_device[PIO_USB_DEVICE_CNT]; +pio_port_t pio_port[1]; +root_port_t pio_usb_root_port[PIO_USB_ROOT_PORT_CNT]; +endpoint_t pio_usb_ep_pool[PIO_USB_EP_POOL_CNT]; + +//--------------------------------------------------------------------+ +// Bus functions +//--------------------------------------------------------------------+ + +static void __no_inline_not_in_flash_func(send_pre)(const pio_port_t *pp) { + uint8_t data[] = {USB_SYNC, USB_PID_PRE}; + + // send PRE token in full-speed + pio_sm_set_enabled(pp->pio_usb_tx, pp->sm_tx, false); + for (uint i = 0; i < USB_TX_EOP_DISABLER_LEN; ++i) { + uint16_t instr = usb_tx_fs_pre_program.instructions[i + USB_TX_EOP_OFFSET]; + pp->pio_usb_tx->instr_mem[pp->offset_tx + i + USB_TX_EOP_OFFSET] = instr; + } + + SM_SET_CLKDIV(pp->pio_usb_tx, pp->sm_tx, pp->clk_div_fs_tx); + + dma_channel_transfer_from_buffer_now(pp->tx_ch, data, 2); + + pio_sm_set_enabled(pp->pio_usb_tx, pp->sm_tx, true); + pp->pio_usb_tx->irq |= IRQ_TX_ALL_MASK; // clear complete flag + pp->pio_usb_tx->irq_force |= IRQ_TX_EOP_MASK; // disable eop + + while ((pp->pio_usb_tx->irq & IRQ_TX_COMP_MASK) == 0) { + continue; + } + + // change bus speed to low-speed + pio_sm_set_enabled(pp->pio_usb_tx, pp->sm_tx, false); + for (uint i = 0; i < USB_TX_EOP_DISABLER_LEN; ++i) { + uint16_t instr = usb_tx_fs_program.instructions[i + USB_TX_EOP_OFFSET]; + pp->pio_usb_tx->instr_mem[pp->offset_tx + i + USB_TX_EOP_OFFSET] = instr; + } + SM_SET_CLKDIV(pp->pio_usb_tx, pp->sm_tx, pp->clk_div_ls_tx); + + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_rx, false); + SM_SET_CLKDIV_MAXSPEED(pp->pio_usb_rx, pp->sm_rx); + + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_eop, false); + SM_SET_CLKDIV(pp->pio_usb_rx, pp->sm_eop, pp->clk_div_ls_rx); + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_eop, true); +} + +void __not_in_flash_func(pio_usb_bus_usb_transfer)(const pio_port_t *pp, + uint8_t *data, uint16_t len) { + if (pp->need_pre) { + send_pre(pp); + } + + dma_channel_transfer_from_buffer_now(pp->tx_ch, data, len); + + pio_sm_set_enabled(pp->pio_usb_tx, pp->sm_tx, true); + pp->pio_usb_tx->irq |= IRQ_TX_ALL_MASK; // clear complete flag + + while ((pp->pio_usb_tx->irq & IRQ_TX_ALL_MASK) == 0) { + continue; + } +} + +void __no_inline_not_in_flash_func(pio_usb_bus_send_handshake)( + const pio_port_t *pp, uint8_t pid) { + uint8_t data[] = {USB_SYNC, pid}; + pio_usb_bus_usb_transfer(pp, data, sizeof(data)); +} + +void __no_inline_not_in_flash_func(pio_usb_bus_send_token)(const pio_port_t *pp, + uint8_t token, + uint8_t addr, + uint8_t ep_num) { + + uint8_t packet[4] = {USB_SYNC, token, 0, 0}; + uint16_t dat = ((uint16_t)(ep_num & 0xf) << 7) | (addr & 0x7f); + uint8_t crc = calc_usb_crc5(dat); + packet[2] = dat & 0xff; + packet[3] = (crc << 3) | ((dat >> 8) & 0x1f); + + pio_usb_bus_usb_transfer(pp, packet, sizeof(packet)); +} + +void __no_inline_not_in_flash_func(pio_usb_bus_prepare_receive)(const pio_port_t *pp) { + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_rx, false); + pio_sm_clear_fifos(pp->pio_usb_rx, pp->sm_rx); + pio_sm_restart(pp->pio_usb_rx, pp->sm_rx); + pio_sm_exec(pp->pio_usb_rx, pp->sm_rx, pp->rx_reset_instr); + pio_sm_exec(pp->pio_usb_rx, pp->sm_rx, pp->rx_reset_instr2); +} + +void __no_inline_not_in_flash_func(pio_usb_bus_start_receive)(const pio_port_t *pp) { + pp->pio_usb_rx->ctrl |= (1 << pp->sm_rx); + pp->pio_usb_rx->irq = IRQ_RX_ALL_MASK; +} + +uint8_t __no_inline_not_in_flash_func(pio_usb_bus_wait_handshake)(pio_port_t* pp) { + int16_t t = 240; + int16_t idx = 0; + + while (t--) { + if (pio_sm_get_rx_fifo_level(pp->pio_usb_rx, pp->sm_rx)) { + uint8_t data = pio_sm_get(pp->pio_usb_rx, pp->sm_rx) >> 24; + pp->usb_rx_buffer[idx++] = data; + if (idx == 2) { + break; + } + } + } + + if (t > 0) { + while ((pp->pio_usb_rx->irq & IRQ_RX_COMP_MASK) == 0) { + continue; + } + } + + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_rx, false); + + return pp->usb_rx_buffer[1]; +} + +int __no_inline_not_in_flash_func(pio_usb_bus_receive_packet_and_handshake)( + pio_port_t *pp, uint8_t handshake) { + uint16_t crc = 0xffff; + uint16_t crc_prev = 0xffff; + uint16_t crc_prev2 = 0xffff; + uint16_t crc_receive = 0xffff; + uint16_t crc_receive_inverse; + bool crc_match = false; + int16_t t = 240; + uint16_t idx = 0; + + while (t--) { + if (pio_sm_get_rx_fifo_level(pp->pio_usb_rx, pp->sm_rx)) { + uint8_t data = pio_sm_get(pp->pio_usb_rx, pp->sm_rx) >> 24; + pp->usb_rx_buffer[idx++] = data; + if (idx == 2) { + break; + } + } + } + + // timing critical start + if (t > 0) { + if (handshake == USB_PID_ACK) { + while ((pp->pio_usb_rx->irq & IRQ_RX_COMP_MASK) == 0) { + if (pio_sm_get_rx_fifo_level(pp->pio_usb_rx, pp->sm_rx)) { + uint8_t data = pio_sm_get(pp->pio_usb_rx, pp->sm_rx) >> 24; + crc_prev2 = crc_prev; + crc_prev = crc; + crc = update_usb_crc16(crc, data); + pp->usb_rx_buffer[idx++] = data; + crc_receive = (crc_receive >> 8) | (data << 8); + crc_receive_inverse = crc_receive ^ 0xffff; + crc_match = (crc_receive_inverse == crc_prev2); + } + } + + if (idx >= 4 && crc_match) { + pio_usb_bus_send_handshake(pp, USB_PID_ACK); + // timing critical end + return idx - 4; + } + + if (pp->usb_rx_buffer[1] == USB_PID_NAK || + pp->usb_rx_buffer[1] == USB_PID_STALL) { + return 0; + } + } else { + // just discard received data since we NAK/STALL anyway + while ((pp->pio_usb_rx->irq & IRQ_RX_COMP_MASK) == 0) { + continue; + } + pio_sm_clear_fifos(pp->pio_usb_rx, pp->sm_rx); + + pio_usb_bus_send_handshake(pp, handshake); + } + } + + return -1; +} + +static __always_inline void add_pio_host_rx_program(PIO pio, + const pio_program_t *program, + const pio_program_t *debug_program, + uint *offset, int debug_pin) { + if (debug_pin < 0) { + *offset = pio_add_program(pio, program); + } else { + *offset = pio_add_program(pio, debug_program); + } +} + +static void __no_inline_not_in_flash_func(initialize_host_programs)( + pio_port_t *pp, const pio_usb_configuration_t *c, root_port_t *port) { + pp->offset_tx = pio_add_program(pp->pio_usb_tx, &usb_tx_fs_program); + usb_tx_fs_program_init(pp->pio_usb_tx, pp->sm_tx, pp->offset_tx, + port->pin_dp); + + add_pio_host_rx_program(pp->pio_usb_rx, &usb_nrzi_decoder_program, + &usb_nrzi_decoder_debug_program, &pp->offset_rx, + c->debug_pin_rx); + usb_rx_fs_program_init(pp->pio_usb_rx, pp->sm_rx, pp->offset_rx, port->pin_dp, + c->debug_pin_rx); + pp->rx_reset_instr = pio_encode_jmp(pp->offset_rx); + pp->rx_reset_instr2 = pio_encode_set(pio_x, 0); + + add_pio_host_rx_program(pp->pio_usb_rx, &usb_edge_detector_program, + &usb_edge_detector_debug_program, &pp->offset_eop, + c->debug_pin_eop); + eop_detect_fs_program_init(pp->pio_usb_rx, c->sm_eop, pp->offset_eop, + port->pin_dp, true, c->debug_pin_eop); + + usb_tx_configure_pins(pp->pio_usb_tx, pp->sm_tx, port->pin_dp); + + pio_sm_set_jmp_pin(pp->pio_usb_rx, pp->sm_rx, port->pin_dp); + pio_sm_set_jmp_pin(pp->pio_usb_rx, pp->sm_eop, port->pin_dm); + pio_sm_set_in_pins(pp->pio_usb_rx, pp->sm_eop, port->pin_dp); +} + +static void configure_tx_channel(uint8_t ch, PIO pio, uint sm) { + dma_channel_config conf = dma_channel_get_default_config(ch); + + channel_config_set_read_increment(&conf, true); + channel_config_set_write_increment(&conf, false); + channel_config_set_transfer_data_size(&conf, DMA_SIZE_8); + channel_config_set_dreq(&conf, pio_get_dreq(pio, sm, true)); + + dma_channel_set_config(ch, &conf, false); + dma_channel_set_write_addr(ch, &pio->txf[sm], false); +} + +static void apply_config(pio_port_t *pp, const pio_usb_configuration_t *c, + root_port_t *port) { + pp->pio_usb_tx = c->pio_tx_num == 0 ? pio0 : pio1; + pp->sm_tx = c->sm_tx; + pp->tx_ch = c->tx_ch; + pp->pio_usb_rx = c->pio_rx_num == 0 ? pio0 : pio1; + pp->sm_rx = c->sm_rx; + pp->sm_eop = c->sm_eop; + port->pin_dp = c->pin_dp; + port->pin_dm = c->pin_dp + 1; + + pp->debug_pin_rx = c->debug_pin_rx; + pp->debug_pin_eop = c->debug_pin_eop; + + pp->extra_error_retry_count = c->extra_error_retry_count; + + pio_sm_claim(pp->pio_usb_tx, pp->sm_tx); + pio_sm_claim(pp->pio_usb_rx, pp->sm_rx); + pio_sm_claim(pp->pio_usb_rx, pp->sm_eop); +} + +static void port_pin_drive_setting(const root_port_t *port) { + gpio_set_slew_rate(port->pin_dp, GPIO_SLEW_RATE_FAST); + gpio_set_slew_rate(port->pin_dm, GPIO_SLEW_RATE_FAST); + gpio_set_drive_strength(port->pin_dp, GPIO_DRIVE_STRENGTH_12MA); + gpio_set_drive_strength(port->pin_dm, GPIO_DRIVE_STRENGTH_12MA); +} + +void pio_usb_bus_init(pio_port_t *pp, const pio_usb_configuration_t *c, + root_port_t *root) { + memset(root, 0, sizeof(root_port_t)); + + pp->pio_usb_tx = c->pio_tx_num == 0 ? pio0 : pio1; + dma_claim_mask(1<tx_ch); + configure_tx_channel(c->tx_ch, pp->pio_usb_tx, c->sm_tx); + + apply_config(pp, c, root); + initialize_host_programs(pp, c, root); + port_pin_drive_setting(root); + root->initialized = true; + root->dev_addr = 0; +} + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + +endpoint_t *pio_usb_get_endpoint(usb_device_t *device, uint8_t idx) { + uint8_t ep_id = device->endpoint_id[idx]; + if (ep_id == 0) { + return NULL; + } else if (ep_id >= 1) { + return &pio_usb_ep_pool[ep_id - 1]; + } + return NULL; +} + +int __no_inline_not_in_flash_func(pio_usb_get_in_data)(endpoint_t *ep, + uint8_t *buffer, + uint8_t len) { + if (ep->has_transfer || ep->is_tx) { + return -1; + } + + if (ep->new_data_flag) { + len = len < ep->actual_len ? len : ep->actual_len; + memcpy(buffer, (void *)ep->buffer, len); + + ep->new_data_flag = false; + + return pio_usb_ll_transfer_start(ep, ep->buffer, ep->size) ? len : -1; + } + + return -1; +} + +int __no_inline_not_in_flash_func(pio_usb_set_out_data)(endpoint_t *ep, + const uint8_t *buffer, + uint8_t len) { + if (ep->has_transfer || !ep->is_tx) { + return -1; + } + + return pio_usb_ll_transfer_start(ep, (uint8_t *)buffer, len) ? 0 : -1; +} + +//--------------------------------------------------------------------+ +// Low Level Function +//--------------------------------------------------------------------+ + +void __no_inline_not_in_flash_func(pio_usb_ll_configure_endpoint)( + endpoint_t *ep, uint8_t const *desc_endpoint) { + const endpoint_descriptor_t *d = (const endpoint_descriptor_t *)desc_endpoint; + ep->size = d->max_size[0] | (d->max_size[1] << 8); + ep->ep_num = d->epaddr; + ep->attr = d->attr; + ep->interval = d->interval; + ep->interval_counter = 0; + ep->data_id = 0; +} + +static inline __force_inline void prepare_tx_data(endpoint_t *ep) { + uint16_t const xact_len = pio_usb_ll_get_transaction_len(ep); + ep->buffer[0] = USB_SYNC; + ep->buffer[1] = (ep->data_id == 1) + ? USB_PID_DATA1 + : USB_PID_DATA0; // USB_PID_SETUP also DATA0 + memcpy(ep->buffer + 2, ep->app_buf, xact_len); + + uint16_t const crc16 = calc_usb_crc16(ep->app_buf, xact_len); + ep->buffer[2 + xact_len] = crc16 & 0xff; + ep->buffer[2 + xact_len + 1] = crc16 >> 8; +} + +bool __no_inline_not_in_flash_func(pio_usb_ll_transfer_start)(endpoint_t *ep, + uint8_t *buffer, + uint16_t buflen) { + if (ep->has_transfer) { + return false; + } + + ep->app_buf = buffer; + ep->total_len = buflen; + ep->actual_len = 0; + + if (ep->is_tx) { + prepare_tx_data(ep); + } else { + ep->new_data_flag = false; + } + + ep->has_transfer = true; + + return true; +} + +bool __no_inline_not_in_flash_func(pio_usb_ll_transfer_continue)( + endpoint_t *ep, uint16_t xferred_bytes) { + ep->app_buf += xferred_bytes; + ep->actual_len += xferred_bytes; + ep->data_id ^= 1; + + if ((xferred_bytes < ep->size) || (ep->actual_len >= ep->total_len)) { + // complete if all bytes transferred or short packet + pio_usb_ll_transfer_complete(ep, PIO_USB_INTS_ENDPOINT_COMPLETE_BITS); + return false; + } else { + if (ep->is_tx) { + prepare_tx_data(ep); + } + + return true; + } +} + +void __no_inline_not_in_flash_func(pio_usb_ll_transfer_complete)( + endpoint_t *ep, uint32_t flag) { + root_port_t *rport = PIO_USB_ROOT_PORT(ep->root_idx); + uint32_t const ep_mask = (1u << (ep - pio_usb_ep_pool)); + + rport->ints |= flag; + + if (flag == PIO_USB_INTS_ENDPOINT_COMPLETE_BITS) { + rport->ep_complete |= ep_mask; + if (!ep->is_tx) { + ep->new_data_flag = true; + } + } else if (flag == PIO_USB_INTS_ENDPOINT_ERROR_BITS) { + rport->ep_error |= ep_mask; + } else if (flag == PIO_USB_INTS_ENDPOINT_STALLED_BITS) { + rport->ep_stalled |= ep_mask; + } else { + // something wrong + } + + ep->has_transfer = false; +} + +int pio_usb_host_add_port(uint8_t pin_dp) { + for (int idx = 0; idx < PIO_USB_ROOT_PORT_CNT; idx++) { + root_port_t *root = PIO_USB_ROOT_PORT(idx); + if (!root->initialized) { + root->pin_dp = pin_dp; + root->pin_dm = pin_dp + 1; + + PIO_USB_ROOT_PORT(idx)->pin_dp = pin_dp; + PIO_USB_ROOT_PORT(idx)->pin_dm = pin_dp + 1; + + gpio_pull_down(pin_dp); + gpio_pull_down(pin_dp + 1); + pio_gpio_init(pio_port[0].pio_usb_tx, pin_dp); + pio_gpio_init(pio_port[0].pio_usb_tx, pin_dp + 1); + gpio_set_inover(pin_dp, GPIO_OVERRIDE_INVERT); + gpio_set_inover(pin_dp + 1, GPIO_OVERRIDE_INVERT); + pio_sm_set_pindirs_with_mask(pio_port[0].pio_usb_tx, pio_port[0].sm_tx, 0, + (0b11 << pin_dp)); + port_pin_drive_setting(root); + root->initialized = true; + + return 0; + } + } + + return -1; +} + +#pragma GCC pop_options diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb.h new file mode 100644 index 0000000000..510754b6e3 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb.h @@ -0,0 +1,32 @@ +// Copyright 2021 sekigon-gonnoc +// SPDX-License-Identifier: MIT + +#pragma once + +#include "pio_usb_configuration.h" +#include "usb_definitions.h" + +#ifdef __cplusplus + extern "C" { +#endif + +// Host functions +usb_device_t *pio_usb_host_init(const pio_usb_configuration_t *c); +int pio_usb_host_add_port(uint8_t pin_dp); +void pio_usb_host_task(void); +void pio_usb_host_stop(void); +void pio_usb_host_restart(void); + +// Device functions +usb_device_t *pio_usb_device_init(const pio_usb_configuration_t *c, + const usb_descriptor_buffers_t *buffers); +void pio_usb_device_task(void); + +// Common functions +endpoint_t *pio_usb_get_endpoint(usb_device_t *device, uint8_t idx); +int pio_usb_get_in_data(endpoint_t *ep, uint8_t *buffer, uint8_t len); +int pio_usb_set_out_data(endpoint_t *ep, const uint8_t *buffer, uint8_t len); + +#ifdef __cplusplus + } +#endif diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb_configuration.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb_configuration.h new file mode 100644 index 0000000000..c352557db2 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb_configuration.h @@ -0,0 +1,54 @@ +// Copyright 2021 sekigon-gonnoc +// SPDX-License-Identifier: MIT + +#pragma once + +typedef struct { + uint8_t pin_dp; + uint8_t pio_tx_num; + uint8_t sm_tx; + uint8_t tx_ch; + uint8_t pio_rx_num; + uint8_t sm_rx; + uint8_t sm_eop; + void* alarm_pool; + int8_t debug_pin_rx; + int8_t debug_pin_eop; + uint8_t extra_error_retry_count; +} pio_usb_configuration_t; + +#ifndef PIO_USB_DP_PIN_DEFAULT +#define PIO_USB_DP_PIN_DEFAULT 0 +#endif + +#define PIO_USB_DM_PIN_DEFAULT (PIO_USB_DP_PIN_DEFAULT + 1) + +#define PIO_USB_TX_DEFAULT 0 +#define PIO_SM_USB_TX_DEFAULT 0 +#define PIO_USB_DMA_TX_DEFAULT 0 + +#define PIO_USB_RX_DEFAULT 1 +#define PIO_SM_USB_RX_DEFAULT 0 +#define PIO_SM_USB_EOP_DEFAULT 1 + +#define PIO_USB_DEBUG_PIN_NONE (-1) + +// Newly added retry count, in addition to the retry defined by the +// specification. +#define PIO_USB_EXTRA_ERROR_RETRY_COUNT_DEFAULT 0 + +#define PIO_USB_DEFAULT_CONFIG \ + { \ + PIO_USB_DP_PIN_DEFAULT, PIO_USB_TX_DEFAULT, PIO_SM_USB_TX_DEFAULT, \ + PIO_USB_DMA_TX_DEFAULT, PIO_USB_RX_DEFAULT, PIO_SM_USB_RX_DEFAULT, \ + PIO_SM_USB_EOP_DEFAULT, NULL, PIO_USB_DEBUG_PIN_NONE, \ + PIO_USB_DEBUG_PIN_NONE, PIO_USB_EXTRA_ERROR_RETRY_COUNT_DEFAULT, \ + } + +#define PIO_USB_EP_POOL_CNT 32 +#define PIO_USB_DEV_EP_CNT 16 +#define PIO_USB_DEVICE_CNT 4 +#define PIO_USB_HUB_PORT_CNT 8 +#define PIO_USB_ROOT_PORT_CNT 2 + +#define PIO_USB_EP_SIZE 64 diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb_device.c b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb_device.c new file mode 100644 index 0000000000..119f473a27 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb_device.c @@ -0,0 +1,498 @@ +/** + * Copyright (c) 2021 sekigon-gonnoc + * Ha Thach (thach@tinyusb.org) + */ + +#pragma GCC push_options +#pragma GCC optimize("-O3") + +#include +#include +#include +#include + +#include "pio_usb.h" +#include "pio_usb_ll.h" +#include "usb_crc.h" + +#include "usb_rx.pio.h" +#include "usb_tx.pio.h" + +#include "hardware/dma.h" +#include "hardware/irq.h" + +static uint8_t new_devaddr = 0; +static uint8_t ep0_crc5_lut[16]; +static __unused usb_descriptor_buffers_t descriptor_buffers; + +static void __no_inline_not_in_flash_func(update_ep0_crc5_lut)(uint8_t addr) { + uint16_t dat; + uint8_t crc; + + for (int epnum = 0; epnum < 16; epnum++) { + dat = (addr) | (epnum << 7); + crc = calc_usb_crc5(dat); + ep0_crc5_lut[epnum] = (crc << 3) | ((epnum >> 1) & 0x07); + } +} + +static __always_inline void restart_usb_reveiver(pio_port_t *pp) { + pio_sm_exec(pp->pio_usb_rx, pp->sm_rx, pp->rx_reset_instr); + pio_sm_exec(pp->pio_usb_rx, pp->sm_rx, pp->rx_reset_instr2); + pio_sm_restart(pp->pio_usb_rx, pp->sm_rx); + pp->pio_usb_rx->irq = IRQ_RX_ALL_MASK; +} + +static __always_inline int8_t device_receive_token(uint8_t *buffer, + uint8_t dev_addr) { + pio_port_t *pp = PIO_USB_PIO_PORT(0); + uint8_t idx = 0; + uint8_t addr; + uint8_t ep; + bool match = false; + + static uint8_t eplut[2][8] = {{0, 2, 4, 6, 8, 10, 12, 14}, + {1, 3, 5, 7, 9, 11, 13, 15}}; + uint8_t *current_lut; + + if ((pp->pio_usb_rx->irq & IRQ_RX_COMP_MASK) == 0) { + while ((pp->pio_usb_rx->irq & IRQ_RX_COMP_MASK) == 0) { + if (pio_sm_get_rx_fifo_level(pp->pio_usb_rx, pp->sm_rx)) { + buffer[idx++] = pio_sm_get(pp->pio_usb_rx, pp->sm_rx) >> 24; + if ((idx == 3) && (buffer[1] != USB_PID_SOF)) { + addr = buffer[2] & 0x7f; + current_lut = &eplut[buffer[2] >> 7][0]; + match = dev_addr == addr ? true : false; + } + } + } + } else { + // host is probably timeout. Ignore this packets. + pio_sm_clear_fifos(pp->pio_usb_rx, pp->sm_rx); + } + + restart_usb_reveiver(pp); + + if (match) { + ep = current_lut[buffer[3] & 0x07]; + if (ep0_crc5_lut[ep] == buffer[3]) { + return ep; + } else { + return -1; + } + } + + return -1; +} + +static void __no_inline_not_in_flash_func(usb_device_packet_handler)(void) { + static uint8_t token_buf[64]; + pio_port_t *pp = PIO_USB_PIO_PORT(0); + root_port_t *rport = PIO_USB_ROOT_PORT(0); + + // + // time critical start + // + int8_t ep_num = device_receive_token(token_buf, rport->dev_addr); + + if (token_buf[1] == USB_PID_IN) { + if (ep_num < 0) { + return; + } + static uint8_t hand_shake_token[2] = {USB_SYNC, USB_PID_STALL}; + + endpoint_t *ep = PIO_USB_ENDPOINT((ep_num << 1) | 0x01); + uint16_t const xact_len = pio_usb_ll_get_transaction_len(ep); + + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_rx, false); + volatile bool has_transfer = ep->has_transfer; + + if (has_transfer) { + dma_channel_transfer_from_buffer_now(pp->tx_ch, ep->buffer, xact_len + 4); + } else if (ep->stalled) { + hand_shake_token[1] = USB_PID_STALL; + dma_channel_transfer_from_buffer_now(pp->tx_ch, hand_shake_token, 2); + } else { + hand_shake_token[1] = USB_PID_NAK; + dma_channel_transfer_from_buffer_now(pp->tx_ch, hand_shake_token, 2); + } + + pp->pio_usb_tx->irq = IRQ_TX_ALL_MASK; // clear complete flag + while ((pp->pio_usb_tx->irq & IRQ_TX_ALL_MASK) == 0) { + continue; + } + + if (has_transfer) { + pp->pio_usb_rx->irq = IRQ_RX_ALL_MASK; + irq_clear(pp->device_rx_irq_num); + pio_usb_bus_start_receive(pp); + + // wait for ack + pio_usb_bus_wait_handshake(pp); + + pio_usb_bus_start_receive(pp); + irq_clear(pp->device_rx_irq_num); + + // + // time critical end + // + + if (ep->ep_num == 0x80 && new_devaddr > 0) { + rport->dev_addr = new_devaddr; + new_devaddr = 0; + update_ep0_crc5_lut(rport->dev_addr); + } + + pio_usb_ll_transfer_continue(ep, xact_len); + } else { + pp->pio_usb_rx->irq = IRQ_RX_ALL_MASK; + irq_clear(pp->device_rx_irq_num); + pio_usb_bus_start_receive(pp); + + // + // time critical end + // + } + } else if (token_buf[1] == USB_PID_OUT) { + if (ep_num < 0) { + return; + } + endpoint_t *ep = PIO_USB_ENDPOINT(ep_num << 1); + + uint8_t hanshake = ep->stalled + ? USB_PID_STALL + : (ep->has_transfer ? USB_PID_ACK : USB_PID_NAK); + int res = pio_usb_bus_receive_packet_and_handshake(pp, hanshake); + pio_sm_clear_fifos(pp->pio_usb_rx, pp->sm_rx); + restart_usb_reveiver(pp); + irq_clear(pp->device_rx_irq_num); + + if (ep->has_transfer) { + if (res >= 0) { + memcpy(ep->app_buf, pp->usb_rx_buffer + 2, res); + pio_usb_ll_transfer_continue(ep, res); + } + } + } else if (token_buf[1] == USB_PID_SETUP) { + if (ep_num < 0) { + return; + } + int res = pio_usb_bus_receive_packet_and_handshake(pp, USB_PID_ACK); + pio_sm_clear_fifos(pp->pio_usb_rx, pp->sm_rx); + restart_usb_reveiver(pp); + irq_clear(pp->device_rx_irq_num); + + if (res >= 0) { + rport->setup_packet = pp->usb_rx_buffer + 2; + rport->ints |= PIO_USB_INTS_SETUP_REQ_BITS; + + // DATA1 for both data and status stage + PIO_USB_ENDPOINT(0)->has_transfer = PIO_USB_ENDPOINT(1)->has_transfer = false; + PIO_USB_ENDPOINT(0)->data_id = PIO_USB_ENDPOINT(1)->data_id = 1; + PIO_USB_ENDPOINT(0)->stalled = PIO_USB_ENDPOINT(1)->stalled = false; + } + } else if (token_buf[1] == USB_PID_SOF) { + // SOF interrupt + } + + + token_buf[0] = 0; // clear received token + token_buf[1] = 0; + + if (rport->ints) { + pio_usb_device_irq_handler(0); + } +} + +usb_device_t *pio_usb_device_init(const pio_usb_configuration_t *c, + const usb_descriptor_buffers_t *buffers) { + pio_port_t *pp = PIO_USB_PIO_PORT(0); + root_port_t *rport = PIO_USB_ROOT_PORT(0); + usb_device_t *dev = &pio_usb_device[0]; + + pio_usb_bus_init(pp, c, rport); + rport->mode = PIO_USB_MODE_DEVICE; + + memset(dev, 0, sizeof(*dev)); + for (int i = 0; i < PIO_USB_DEV_EP_CNT; i++) { + dev->endpoint_id[i] = 2 * (i + 1); // only index IN endpoint + } + + update_ep0_crc5_lut(rport->dev_addr); + + float const cpu_freq = (float)clock_get_hz(clk_sys); + + pio_calculate_clkdiv_from_float(cpu_freq / 48000000, + &pp->clk_div_fs_tx.div_int, + &pp->clk_div_fs_tx.div_frac); + pio_calculate_clkdiv_from_float(cpu_freq / 96000000, + &pp->clk_div_fs_rx.div_int, + &pp->clk_div_fs_rx.div_frac); + + pio_sm_set_jmp_pin(pp->pio_usb_rx, pp->sm_rx, rport->pin_dp); + SM_SET_CLKDIV_MAXSPEED(pp->pio_usb_rx, pp->sm_rx); + + pio_sm_set_jmp_pin(pp->pio_usb_rx, pp->sm_eop, rport->pin_dm); + pio_sm_set_in_pins(pp->pio_usb_rx, pp->sm_eop, rport->pin_dp); + SM_SET_CLKDIV(pp->pio_usb_rx, pp->sm_eop, pp->clk_div_fs_rx); + + descriptor_buffers = *buffers; + + pio_sm_set_enabled(pp->pio_usb_tx, pp->sm_tx, true); + pio_usb_bus_prepare_receive(pp); + pp->pio_usb_rx->ctrl |= (1 << pp->sm_rx); + pp->pio_usb_rx->irq |= IRQ_RX_ALL_MASK; + + // configure PIOx_IRQ_0 to detect packet receive start + pio_set_irqn_source_enabled(pp->pio_usb_rx, 0, pis_interrupt0 + IRQ_RX_START, + true); + pp->device_rx_irq_num = (pp->pio_usb_rx == pio0) ? PIO0_IRQ_0 : PIO1_IRQ_0; + irq_set_exclusive_handler(pp->device_rx_irq_num, usb_device_packet_handler); + irq_set_enabled(pp->device_rx_irq_num, true); + + return dev; +} + +//--------------------------------------------------------------------+ +// Device Controller functions +//--------------------------------------------------------------------+ + +void pio_usb_device_set_address(uint8_t dev_addr) { + new_devaddr = dev_addr; +} + +bool __no_inline_not_in_flash_func(pio_usb_device_endpoint_open)( + uint8_t const *desc_endpoint) { + const endpoint_descriptor_t *d = (const endpoint_descriptor_t *)desc_endpoint; + endpoint_t *ep = pio_usb_device_get_endpoint_by_address(d->epaddr); + + pio_usb_ll_configure_endpoint(ep, desc_endpoint); + ep->root_idx = 0; + ep->dev_addr = 0; // not used + ep->need_pre = 0; + ep->is_tx = (d->epaddr & 0x80) ? true : false; // device: endpoint in is tx + + return true; +} + +bool pio_usb_device_transfer(uint8_t ep_address, uint8_t *buffer, + uint16_t buflen) { + endpoint_t *ep = pio_usb_device_get_endpoint_by_address(ep_address); + return pio_usb_ll_transfer_start(ep, buffer, buflen); +} + +//--------------------------------------------------------------------+ +// USB Device Stack +//--------------------------------------------------------------------+ +static int8_t ep0_desc_request_type = -1; +static uint16_t ep0_desc_request_len; +static uint8_t ep0_desc_request_idx; + +static void __no_inline_not_in_flash_func(prepare_ep0_data)(uint8_t *data, + uint8_t len) { + // 0: control out (rx), 1 : control in (tx) + endpoint_t *ep = &pio_usb_ep_pool[1]; + + pio_usb_ll_transfer_start(ep, data, len); + + if (len) { + // there is data, prepare for status as well + pio_usb_ll_transfer_start(&pio_usb_ep_pool[0], NULL, 0); + } +} + +static void __no_inline_not_in_flash_func(prepare_ep0_rx)(uint8_t *data, + uint8_t len) { + // 0: control out (rx), 1 : control in (tx) + endpoint_t *ep = &pio_usb_ep_pool[0]; + + pio_usb_ll_transfer_start(ep, data, len); + + if (len) { + // there is data, prepare for status as well + pio_usb_ll_transfer_start(&pio_usb_ep_pool[1], NULL, 0); + } +} + +void pio_usb_device_task(void) { + switch (ep0_desc_request_type) { + case DESC_TYPE_CONFIG: { + uint16_t req_len = ep0_desc_request_len; + uint16_t desc_len = + descriptor_buffers.config[2] | (descriptor_buffers.config[3] << 8); + req_len = req_len > desc_len ? desc_len : req_len; + prepare_ep0_data((uint8_t *)descriptor_buffers.config, req_len); + ep0_desc_request_type = -1; + } break; + case DESC_TYPE_STRING: { + const uint16_t *str = + (uint16_t *)&descriptor_buffers.string[ep0_desc_request_idx]; + prepare_ep0_data((uint8_t *)str, str[0] & 0xff); + ep0_desc_request_type = -1; + } break; + case DESC_TYPE_HID_REPORT:{ + prepare_ep0_data( + (uint8_t *)descriptor_buffers.hid_report[ep0_desc_request_idx], + ep0_desc_request_len); + ep0_desc_request_type = -1; + } + default: + break; + } + + root_port_t *rport = PIO_USB_ROOT_PORT(0); + uint32_t se0_time_us =0; + while (pio_usb_bus_get_line_state(rport) == PORT_PIN_SE0) { + busy_wait_us_32(1); + se0_time_us++; + + if (se0_time_us == 1000) { + memset(pio_usb_ep_pool, 0, sizeof(pio_usb_ep_pool)); + rport->dev_addr = 0; + update_ep0_crc5_lut(rport->dev_addr); + + // init endpoint control in/out + PIO_USB_ENDPOINT(0)->size = 64; + PIO_USB_ENDPOINT(0)->ep_num = 0; + PIO_USB_ENDPOINT(0)->is_tx = false; + + PIO_USB_ENDPOINT(1)->size = 64; + PIO_USB_ENDPOINT(1)->ep_num = 0x80; + PIO_USB_ENDPOINT(1)->is_tx = true; + + // TODO should be reset end, this is reset start only + rport->ep_complete = rport->ep_stalled = rport->ep_error = 0; + rport->ints |= PIO_USB_INTS_RESET_END_BITS; + + pio_port_t *pp = PIO_USB_PIO_PORT(0); + restart_usb_reveiver(pp); + } + } +} + +static void __no_inline_not_in_flash_func(configure_all_endpoints)(uint8_t const *desc) { + uint8_t const *desc_end = desc + (descriptor_buffers.config[2] | + (descriptor_buffers.config[3] << 8)); + while (desc < desc_end) { + if (desc[1] == DESC_TYPE_ENDPOINT) { + pio_usb_device_endpoint_open(desc); + } + desc += desc[0]; + } +} + +static int __no_inline_not_in_flash_func(process_device_setup_stage)(uint8_t *buffer) { + int res = -1; + const usb_setup_packet_t *packet = (usb_setup_packet_t *)buffer; + + if (packet->request_type == USB_REQ_DIR_IN) { + if (packet->request == 0x06) { + if (packet->value_msb == DESC_TYPE_DEVICE) { + prepare_ep0_data((uint8_t *)descriptor_buffers.device, 18); + res = 0; + } else if (packet->value_msb == DESC_TYPE_CONFIG) { + ep0_desc_request_len = (packet->length_lsb | (packet->length_msb << 8)); + ep0_desc_request_type = DESC_TYPE_CONFIG; + res = 0; + } else if (packet->value_msb == DESC_TYPE_STRING) { + if (descriptor_buffers.string != NULL) { + ep0_desc_request_idx = packet->value_lsb; + ep0_desc_request_type = DESC_TYPE_STRING; + res = 0; + } + } + } + } else if (packet->request_type == USB_REQ_DIR_OUT) { + if (packet->request == 0x05) { + // set address + new_devaddr = packet->value_lsb; + prepare_ep0_data(NULL, 0); + res = 0; + } else if (packet->request == 0x09) { + // set configuration + configure_all_endpoints(descriptor_buffers.config); + prepare_ep0_data(NULL, 0); + res = 0; + } + } else if (packet->request_type == (USB_REQ_DIR_IN | USB_REQ_REC_IFACE)) { + if (packet->request == 0x06 && packet->value_msb == DESC_TYPE_HID_REPORT) { + // get hid report desc + ep0_desc_request_len = (packet->length_lsb | (packet->length_msb << 8)); + ep0_desc_request_idx = packet->index_lsb; + ep0_desc_request_type = DESC_TYPE_HID_REPORT; + res = 0; + } + } else if (packet->request_type == (USB_REQ_TYP_CLASS | USB_REQ_REC_IFACE)) { + if (packet->request == 0x09) { + // set hid report + static __unused uint8_t received_hid_report[8]; // not used + prepare_ep0_rx(received_hid_report, 8); + res = 0; + } else if (packet->request == 0x0A) { + // set hid idle request + prepare_ep0_data(NULL, 0); + res = 0; + } else if (packet->request == 0x0B) { + // set hid protocol request + prepare_ep0_data(NULL, 0); + res = 0; + } + } else if (packet->request_type == (USB_REQ_REC_EP)) { + prepare_ep0_data(NULL, 0); + res = 0; + } + + return res; +} + +// IRQ Handler +static void __no_inline_not_in_flash_func(__pio_usb_device_irq_handler)(uint8_t root_idx) { + root_port_t *root = PIO_USB_ROOT_PORT(root_idx); + usb_device_t *dev = &pio_usb_device[0]; + + uint32_t const ints = root->ints; + + if (ints & PIO_USB_INTS_RESET_END_BITS) { + memset(dev, 0, sizeof(*dev)); + for (int i = 0; i < PIO_USB_DEV_EP_CNT; i++) { + dev->endpoint_id[i] = 2 * (i + 1); // only index IN endpoint + } + } + + if (ints & PIO_USB_INTS_SETUP_REQ_BITS) { + process_device_setup_stage(root->setup_packet); + dev->control_pipe.stage = STAGE_DATA; + } + + if (ints & PIO_USB_INTS_ENDPOINT_COMPLETE_BITS) { + const uint32_t ep_all = root->ep_complete; + + // control out + if (ep_all & 0x01) { + if (dev->control_pipe.stage == STAGE_STATUS) { + dev->control_pipe.stage = STAGE_COMPLETE; + } else if (dev->control_pipe.stage == STAGE_DATA) { + dev->control_pipe.stage = STAGE_STATUS; + prepare_ep0_data(NULL, 0); + } + } + + // control in + if (ep_all & 0x02) { + if (dev->control_pipe.stage == STAGE_STATUS) { + dev->control_pipe.stage = STAGE_COMPLETE; + } + } + + // clear all + root->ep_complete &= ~ep_all; + } + + // clear all + root->ints &= ~ints; +} + +// weak alias to __pio_usb_device_irq_handler +void pio_usb_device_irq_handler(uint8_t root_id) __attribute__ ((weak, alias("__pio_usb_device_irq_handler"))); + +#pragma GCC pop_options diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb_host.c b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb_host.c new file mode 100644 index 0000000000..2014bf5df6 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb_host.c @@ -0,0 +1,1334 @@ +/** + * Copyright (c) 2021 sekigon-gonnoc + * Ha Thach (thach@tinyusb.org) + */ + +#pragma GCC push_options +#pragma GCC optimize("-O3") + +#include +#include +#include +#include + +#include "hardware/sync.h" + +#include "pio_usb.h" +#include "pio_usb_ll.h" +#include "usb_crc.h" +#include "usb_rx.pio.h" +#include "usb_tx.pio.h" + +static alarm_pool_t *_alarm_pool = NULL; +static repeating_timer_t sof_rt; +static bool timer_active; + +static volatile bool cancel_timer_flag; +static volatile bool start_timer_flag; +static __unused uint32_t int_stat; + +bool sof_timer(repeating_timer_t *_rt); + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + +static void start_timer(alarm_pool_t *alarm_pool) { + if (timer_active) { + return; + } + + // if (alarm_pool != NULL) { + // alarm_pool_add_repeating_timer_us(alarm_pool, -1000, sof_timer, NULL, + // &sof_rt); + // } else { + // add_repeating_timer_us(-1000, sof_timer, NULL, &sof_rt); + // } + + timer_active = true; +} + +static __unused void stop_timer(void) { + cancel_repeating_timer(&sof_rt); + timer_active = false; +} + +usb_device_t *pio_usb_host_init(const pio_usb_configuration_t *c) { + pio_port_t *pp = PIO_USB_PIO_PORT(0); + root_port_t *root = PIO_USB_ROOT_PORT(0); + + pio_usb_bus_init(pp, c, root); + root->mode = PIO_USB_MODE_HOST; + + float const cpu_freq = (float)clock_get_hz(clk_sys); + pio_calculate_clkdiv_from_float(cpu_freq / 48000000, + &pp->clk_div_fs_tx.div_int, + &pp->clk_div_fs_tx.div_frac); + pio_calculate_clkdiv_from_float(cpu_freq / 6000000, + &pp->clk_div_ls_tx.div_int, + &pp->clk_div_ls_tx.div_frac); + + pio_calculate_clkdiv_from_float(cpu_freq / 96000000, + &pp->clk_div_fs_rx.div_int, + &pp->clk_div_fs_rx.div_frac); + pio_calculate_clkdiv_from_float(cpu_freq / 12000000, + &pp->clk_div_ls_rx.div_int, + &pp->clk_div_ls_rx.div_frac); + + // _alarm_pool = c->alarm_pool; + // if (!_alarm_pool) { + // _alarm_pool = alarm_pool_create(2, 1); + // } + + start_timer(_alarm_pool); + + return &pio_usb_device[0]; +} + +void pio_usb_host_stop(void) { + cancel_timer_flag = true; + while (cancel_timer_flag) { + continue; + } +} + +void pio_usb_host_restart(void) { + start_timer_flag = true; + while (start_timer_flag) { + continue; + } +} + +//--------------------------------------------------------------------+ +// Bus functions +//--------------------------------------------------------------------+ + +static void __no_inline_not_in_flash_func(override_pio_program)(PIO pio, const pio_program_t* program, uint offset) { + for (uint i = 0; i < program->length; ++i) { + uint16_t instr = program->instructions[i]; + pio->instr_mem[offset + i] = + pio_instr_bits_jmp != _pio_major_instr_bits(instr) ? instr + : instr + offset; + } +} + +static __always_inline void override_pio_rx_program(PIO pio, + const pio_program_t *program, + const pio_program_t *debug_program, + uint offset, int debug_pin) { + if (debug_pin < 0) { + override_pio_program(pio, program, offset); + } else { + override_pio_program(pio, debug_program, offset); + } +} + +static void __no_inline_not_in_flash_func(configure_fullspeed_host)( + pio_port_t const *pp, root_port_t *port) { + override_pio_program(pp->pio_usb_tx, &usb_tx_fs_program, pp->offset_tx); + SM_SET_CLKDIV(pp->pio_usb_tx, pp->sm_tx, pp->clk_div_fs_tx); + + pio_sm_set_jmp_pin(pp->pio_usb_rx, pp->sm_rx, port->pin_dp); + SM_SET_CLKDIV_MAXSPEED(pp->pio_usb_rx, pp->sm_rx); + + pio_sm_set_jmp_pin(pp->pio_usb_rx, pp->sm_eop, port->pin_dm); + pio_sm_set_in_pins(pp->pio_usb_rx, pp->sm_eop, port->pin_dp); + SM_SET_CLKDIV(pp->pio_usb_rx, pp->sm_eop, pp->clk_div_fs_rx); + + usb_tx_configure_pins(pp->pio_usb_tx, pp->sm_tx, port->pin_dp); +} + +static void __no_inline_not_in_flash_func(configure_lowspeed_host)( + pio_port_t const *pp, root_port_t *port) { + override_pio_program(pp->pio_usb_tx, &usb_tx_ls_program, pp->offset_tx); + SM_SET_CLKDIV(pp->pio_usb_tx, pp->sm_tx, pp->clk_div_ls_tx); + + pio_sm_set_jmp_pin(pp->pio_usb_rx, pp->sm_rx, port->pin_dm); + SM_SET_CLKDIV_MAXSPEED(pp->pio_usb_rx, pp->sm_rx); + + pio_sm_set_jmp_pin(pp->pio_usb_rx, pp->sm_eop, port->pin_dp); + pio_sm_set_in_pins(pp->pio_usb_rx, pp->sm_eop, port->pin_dm); + SM_SET_CLKDIV(pp->pio_usb_rx, pp->sm_eop, pp->clk_div_ls_rx); + + usb_tx_configure_pins(pp->pio_usb_tx, pp->sm_tx, port->pin_dp); +} + +static void __no_inline_not_in_flash_func(configure_root_port)( + pio_port_t *pp, root_port_t *root) { + if (root->is_fullspeed) { + configure_fullspeed_host(pp, root); + } else { + configure_lowspeed_host(pp, root); + } +} + +static void __no_inline_not_in_flash_func(restore_fs_bus)(const pio_port_t *pp) { + // change bus speed to full-speed + pio_sm_set_enabled(pp->pio_usb_tx, pp->sm_tx, false); + SM_SET_CLKDIV(pp->pio_usb_tx, pp->sm_tx, pp->clk_div_fs_tx); + + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_rx, false); + SM_SET_CLKDIV_MAXSPEED(pp->pio_usb_rx, pp->sm_rx); + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_rx, true); + + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_eop, false); + SM_SET_CLKDIV(pp->pio_usb_rx, pp->sm_eop, pp->clk_div_fs_rx); + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_eop, true); +} + +static bool __no_inline_not_in_flash_func(connection_check)(root_port_t *port) { + if (pio_usb_bus_get_line_state(port) == PORT_PIN_SE0) { + busy_wait_us_32(1); + + if (pio_usb_bus_get_line_state(port) == PORT_PIN_SE0) { + busy_wait_us_32(1); + // device disconnect + port->connected = false; + port->suspended = true; + port->ints |= PIO_USB_INTS_DISCONNECT_BITS; + return false; + } + } + + return true; +} + +//--------------------------------------------------------------------+ +// SOF +//--------------------------------------------------------------------+ +static int usb_setup_transaction(pio_port_t *pp, endpoint_t *ep); +static int usb_in_transaction(pio_port_t *pp, endpoint_t *ep); +static int usb_out_transaction(pio_port_t *pp, endpoint_t *ep); + +bool __no_inline_not_in_flash_func(sof_timer)(repeating_timer_t *_rt) { + static uint8_t sof_packet[4] = {USB_SYNC, USB_PID_SOF, 0x00, 0x10}; + static uint8_t sof_count = 0; + (void)_rt; + + pio_port_t *pp = PIO_USB_PIO_PORT(0); + + // Send SOF + for (int root_idx = 0; root_idx < PIO_USB_ROOT_PORT_CNT; root_idx++) { + root_port_t *root = PIO_USB_ROOT_PORT(root_idx); + if (!(root->initialized && root->connected && !root->suspended && + connection_check(root))) { + continue; + } + configure_root_port(pp, root); + pio_usb_bus_usb_transfer(pp, sof_packet, 4); + } + + // Carry out all queued endpoint transaction + for (int root_idx = 0; root_idx < PIO_USB_ROOT_PORT_CNT; root_idx++) { + root_port_t *root = PIO_USB_ROOT_PORT(root_idx); + if (!(root->initialized && root->connected && !root->suspended)) { + continue; + } + + configure_root_port(pp, root); + + for (int ep_pool_idx = 0; ep_pool_idx < PIO_USB_EP_POOL_CNT; + ep_pool_idx++) { + endpoint_t *ep = PIO_USB_ENDPOINT(ep_pool_idx); + if ((ep->root_idx == root_idx) && ep->size) { + bool const is_periodic = ((ep->attr & 0x03) == EP_ATTR_INTERRUPT); + + if (is_periodic && (ep->interval_counter > 0)) { + ep->interval_counter--; + continue; + } + + if (ep->has_transfer) { + if (ep->need_pre) { + pp->need_pre = true; + } + + if (ep->ep_num == 0 && ep->data_id == USB_PID_SETUP) { + usb_setup_transaction(pp, ep); + } else { + int transaction_result = 0; + if (ep->ep_num & EP_IN) { + transaction_result = usb_in_transaction(pp, ep); + } else { + transaction_result = usb_out_transaction(pp, ep); + } + + if (is_periodic && transaction_result == 0) { + ep->interval_counter = ep->interval - 1; + } + } + + if (ep->need_pre) { + pp->need_pre = false; + restore_fs_bus(pp); + } + } + } + } + } + + // check for new connection to root hub + for (int root_idx = 0; root_idx < PIO_USB_ROOT_PORT_CNT; root_idx++) { + root_port_t *root = PIO_USB_ROOT_PORT(root_idx); + if (root->initialized && !root->connected) { + port_pin_status_t const line_state = pio_usb_bus_get_line_state(root); + if (line_state == PORT_PIN_FS_IDLE || line_state == PORT_PIN_LS_IDLE) { + root->is_fullspeed = (line_state == PORT_PIN_FS_IDLE); + root->connected = true; + root->suspended = true; // need a bus reset before operating + root->ints |= PIO_USB_INTS_CONNECT_BITS; + pp->total_transaction_count++; + } + } + } + + // Invoke IRQHandler if interrupt status is set + for (uint8_t root_idx = 0; root_idx < PIO_USB_ROOT_PORT_CNT; root_idx++) { + if (PIO_USB_ROOT_PORT(root_idx)->ints) { + pp->total_transaction_count++; + pio_usb_host_irq_handler(root_idx); + pp->total_transaction_count++; + } + } + + sof_count = (sof_count + 1) & 0x1f; + sof_packet[2] = sof_count & 0xff; + sof_packet[3] = (calc_usb_crc5(sof_count) << 3) | (sof_count >> 8); + + return true; +} + +//--------------------------------------------------------------------+ +// Host Controller functions +//--------------------------------------------------------------------+ + +void pio_usb_host_port_reset_start(uint8_t root_idx) { + root_port_t *root = PIO_USB_ROOT_PORT(root_idx); + pio_port_t *pp = PIO_USB_PIO_PORT(0); + + // bus is not operating while in reset + root->suspended = true; + + // Force line state to SE0 + pio_sm_set_pins_with_mask(pp->pio_usb_tx, pp->sm_tx, (0b00 << root->pin_dp), + (0b11u << root->pin_dp)); + pio_sm_set_pindirs_with_mask(pp->pio_usb_tx, pp->sm_tx, + (0b11u << root->pin_dp), + (0b11u << root->pin_dp)); +} + +void pio_usb_host_port_reset_end(uint8_t root_idx) { + root_port_t *root = PIO_USB_ROOT_PORT(root_idx); + pio_port_t *pp = PIO_USB_PIO_PORT(0); + + // line state to input + pio_sm_set_pindirs_with_mask(pp->pio_usb_tx, pp->sm_tx, + (0b00u << root->pin_dp), + (0b11u << root->pin_dp)); + + busy_wait_us(100); // TODO check if this is neccessary + + // bus back to operating + root->suspended = false; +} + +void pio_usb_host_close_device(uint8_t root_idx, uint8_t device_address) { + for (int ep_pool_idx = 0; ep_pool_idx < PIO_USB_EP_POOL_CNT; ep_pool_idx++) { + endpoint_t *ep = PIO_USB_ENDPOINT(ep_pool_idx); + if ((ep->root_idx == root_idx) && (ep->dev_addr == device_address) && + ep->size) { + ep->size = 0; + ep->has_transfer = false; + } + } +} + +static inline __force_inline endpoint_t * _find_ep(uint8_t root_idx, + uint8_t device_address, uint8_t ep_address) { + for (int ep_pool_idx = 0; ep_pool_idx < PIO_USB_EP_POOL_CNT; ep_pool_idx++) { + endpoint_t *ep = PIO_USB_ENDPOINT(ep_pool_idx); + // note 0x00 and 0x80 are matched as control endpoint of opposite direction + if ((ep->root_idx == root_idx) && (ep->dev_addr == device_address) && + ep->size && + ((ep->ep_num == ep_address) || + (((ep_address & 0x7f) == 0) && ((ep->ep_num & 0x7f) == 0)))) { + return ep; + } + } + + return NULL; +} + +bool pio_usb_host_endpoint_open(uint8_t root_idx, uint8_t device_address, + uint8_t const *desc_endpoint, bool need_pre) { + const endpoint_descriptor_t *d = (const endpoint_descriptor_t *)desc_endpoint; + for (int ep_pool_idx = 0; ep_pool_idx < PIO_USB_EP_POOL_CNT; ep_pool_idx++) { + endpoint_t *ep = PIO_USB_ENDPOINT(ep_pool_idx); + // ep size is used as valid indicator + if (ep->size == 0) { + pio_usb_ll_configure_endpoint(ep, desc_endpoint); + ep->root_idx = root_idx; + ep->dev_addr = device_address; + ep->need_pre = need_pre; + ep->is_tx = (d->epaddr & 0x80) ? false : true; // host endpoint out is tx + return true; + } + } + + return false; +} + +bool pio_usb_host_send_setup(uint8_t root_idx, uint8_t device_address, + uint8_t const setup_packet[8]) { + endpoint_t *ep = _find_ep(root_idx, device_address, 0); + if (!ep) { + printf("cannot find ep 0x00\r\n"); + return false; + } + + ep->ep_num = 0; // setup is is OUT + ep->data_id = USB_PID_SETUP; + ep->is_tx = true; + + return pio_usb_ll_transfer_start(ep, (uint8_t *)setup_packet, 8); +} + +bool pio_usb_host_endpoint_transfer(uint8_t root_idx, uint8_t device_address, + uint8_t ep_address, uint8_t *buffer, + uint16_t buflen) { + endpoint_t *ep = _find_ep(root_idx, device_address, ep_address); + if (!ep) { + printf("no endpoint 0x%02X\r\n", ep_address); + return false; + } + + // Control endpoint, address may switch between 0x00 <-> 0x80 + // therefore we need to update ep_num and is_tx + if ((ep_address & 0x7f) == 0) { + ep->ep_num = ep_address; + ep->is_tx = (ep_address == 0) ? true : false; + ep->data_id = 1; // data and status always start with DATA1 + } + + return pio_usb_ll_transfer_start(ep, buffer, buflen); +} + +//--------------------------------------------------------------------+ +// Transaction helper +//--------------------------------------------------------------------+ + +static int __no_inline_not_in_flash_func(usb_in_transaction)(pio_port_t *pp, + endpoint_t *ep) { + int res = 0; + uint8_t expect_pid = (ep->data_id == 1) ? USB_PID_DATA1 : USB_PID_DATA0; + int retry = ep->attr == EP_ATTR_ISOCHRONOUS + ? 0 + : 3; // Host can retry transaction three times except for + // isochronous EP(USB1.1 spec. 10.2.6) + int receive_len = 0; + uint8_t receive_pid = 0; + do { + pp->usb_rx_buffer[1] = 0; + pio_usb_bus_prepare_receive(pp); + pio_usb_bus_send_token(pp, USB_PID_IN, ep->dev_addr, ep->ep_num); + pio_usb_bus_start_receive(pp); + + receive_len = pio_usb_bus_receive_packet_and_handshake(pp, USB_PID_ACK); + receive_pid = pp->usb_rx_buffer[1]; + pp->total_transaction_count++; + } while ((receive_len < 0 || + (receive_pid != expect_pid && receive_pid != USB_PID_NAK && + receive_pid != USB_PID_STALL)) && + --retry > 0); + + if (receive_len >= 0) { + if (receive_pid == expect_pid) { + memcpy(ep->app_buf, &pp->usb_rx_buffer[2], receive_len); + pio_usb_ll_transfer_continue(ep, receive_len); + ep->error_count = 0; + } else if (receive_pid == USB_PID_NAK) { + // NAK try again next frame + res = -4; // nak received + ep->error_count = 0; + } else if (receive_pid == USB_PID_STALL) { + ep->error_count = 0; + pio_usb_ll_transfer_complete(ep, PIO_USB_INTS_ENDPOINT_STALLED_BITS); + } else { + // DATA0/1 mismatched, 0 for re-try next frame + res = -3; // invalid pid + } + } else { + res = -1; // no packet received + if ((pp->pio_usb_rx->irq & IRQ_RX_COMP_MASK) == 0) { + res = -2; // invalid CRC + } + pp->total_error_count++; + if (++ep->error_count > pp->extra_error_retry_count) { + pp->total_fatal_error_count++; + ep->error_count = 0; + pio_usb_ll_transfer_complete(ep, PIO_USB_INTS_ENDPOINT_ERROR_BITS); + } + } + + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_rx, false); + pp->usb_rx_buffer[0] = 0; + pp->usb_rx_buffer[1] = 0; + + return res; +} + +static int __no_inline_not_in_flash_func(usb_out_transaction)(pio_port_t *pp, + endpoint_t *ep) { + int res = 0; + uint8_t receive_token; + int retry = ep->attr == EP_ATTR_ISOCHRONOUS + ? 0 + : 3; // Host can retry transaction three times except for + // isochronous EP(USB1.1 spec. 10.2.6) + uint16_t const xact_len = pio_usb_ll_get_transaction_len(ep); + + do { + pio_usb_bus_prepare_receive(pp); + pio_usb_bus_send_token(pp, USB_PID_OUT, ep->dev_addr, ep->ep_num); + // ensure previous tx complete + while ((pp->pio_usb_tx->irq & IRQ_TX_COMP_MASK) == 0) { + continue; + } + + pio_usb_bus_usb_transfer(pp, ep->buffer, xact_len + 4); + pio_usb_bus_start_receive(pp); + + pio_usb_bus_wait_handshake(pp); + receive_token = pp->usb_rx_buffer[1]; + pp->total_transaction_count++; + } while ((receive_token != USB_PID_ACK && receive_token != USB_PID_NAK && + receive_token != USB_PID_STALL) && + --retry > 0); + + if (receive_token == USB_PID_ACK) { + pio_usb_ll_transfer_continue(ep, xact_len); + } else if (receive_token == USB_PID_NAK) { + // NAK try again next frame + res = -1; + } else if (receive_token == USB_PID_STALL) { + pio_usb_ll_transfer_complete(ep, PIO_USB_INTS_ENDPOINT_STALLED_BITS); + } else { + pp->total_error_count++; + pio_usb_ll_transfer_complete(ep, PIO_USB_INTS_ENDPOINT_ERROR_BITS); + } + + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_rx, false); + pp->usb_rx_buffer[0] = 0; + pp->usb_rx_buffer[1] = 0; + + return res; +} + +static int __no_inline_not_in_flash_func(usb_setup_transaction)( + pio_port_t *pp, endpoint_t *ep) { + + int res = 0; + + pp->total_transaction_count++; + // Setup token + pio_usb_bus_prepare_receive(pp); + + pp->total_transaction_count++; + // Setup token + pio_usb_bus_send_token(pp, USB_PID_SETUP, ep->dev_addr, 0); + pp->total_transaction_count++; + // Setup token + // ensure previous tx complete + while ((pp->pio_usb_tx->irq & IRQ_TX_COMP_MASK) == 0) { + continue; + } + + pp->total_transaction_count++; + // Setup token + // Data + ep->data_id = 0; // set to DATA0 + pio_usb_bus_usb_transfer(pp, ep->buffer, 12); + + pp->total_transaction_count++; + // Setup token + // Handshake + pio_usb_bus_start_receive(pp); + pp->total_transaction_count++; + // Setup token + pio_usb_bus_wait_handshake(pp); + pp->total_transaction_count++; + // Setup token + + ep->actual_len = 8; + + pp->total_transaction_count++; + // Setup token + if (pp->usb_rx_buffer[0] == USB_SYNC && pp->usb_rx_buffer[1] == USB_PID_ACK) { + pio_usb_ll_transfer_complete(ep, PIO_USB_INTS_ENDPOINT_COMPLETE_BITS); + } else { + res = -1; + pio_usb_ll_transfer_complete(ep, PIO_USB_INTS_ENDPOINT_ERROR_BITS); + } + + pp->total_transaction_count++; + // Setup token + pp->usb_rx_buffer[1] = 0; // reset buffer + + return res; +} + +//--------------------------------------------------------------------+ +// USB Host Stack +//--------------------------------------------------------------------+ +static void on_device_connect(pio_port_t *pp, root_port_t *root, + int device_idx) { + bool fullspeed_flag = false; + + if (pio_usb_bus_get_line_state(root) == PORT_PIN_FS_IDLE) { + fullspeed_flag = true; + } else if (pio_usb_bus_get_line_state(root) == PORT_PIN_LS_IDLE) { + fullspeed_flag = false; + } + + pio_sm_set_pins_with_mask(pp->pio_usb_tx, pp->sm_tx, (0b00 << root->pin_dp), + (0b11u << root->pin_dp)); + pio_sm_set_pindirs_with_mask(pp->pio_usb_tx, pp->sm_tx, (0b11u << root->pin_dp), + (0b11u << root->pin_dp)); + + busy_wait_ms(100); + + pio_sm_set_pindirs_with_mask(pp->pio_usb_tx, pp->sm_tx, (0b00u << root->pin_dp), + (0b11u << root->pin_dp)); + + busy_wait_us(100); + + root->suspended = false; + + if (fullspeed_flag && pio_usb_bus_get_line_state(root) == PORT_PIN_FS_IDLE) { + root->root_device = &pio_usb_device[device_idx]; + if (!root->root_device->connected) { + configure_fullspeed_host(pp, root); + root->root_device->is_fullspeed = true; + root->root_device->is_root = true; + root->root_device->connected = true; + root->root_device->root = root; + root->root_device->event = EVENT_CONNECT; + } + } else if (!fullspeed_flag && pio_usb_bus_get_line_state(root) == PORT_PIN_LS_IDLE) { + root->root_device = &pio_usb_device[device_idx]; + if (!root->root_device->connected) { + configure_lowspeed_host(pp, root); + root->root_device->is_fullspeed = false; + root->root_device->is_root = true; + root->root_device->connected = true; + root->root_device->root = root; + root->root_device->event = EVENT_CONNECT; + } + } + + endpoint_descriptor_t ep0_desc = { + sizeof(endpoint_descriptor_t), DESC_TYPE_ENDPOINT, 0x00, 0x00, { 0x08, 0x00 }, 0x00 + }; + pio_usb_host_endpoint_open(root - pio_usb_root_port, 0x00, + (uint8_t const *)&ep0_desc, false); +} + +static int __no_inline_not_in_flash_func(control_out_protocol)( + usb_device_t *device, uint8_t *setup_data, uint16_t setup_length, + uint8_t *out_data, uint16_t out_length) { + int res = 0; + + control_pipe_t *pipe = &device->control_pipe; + + if (pipe->operation == CONTROL_NONE) { + pipe->setup_packet.tx_address = setup_data; + pipe->setup_packet.tx_length = setup_length; + pipe->out_data_packet.tx_address = out_data; + pipe->out_data_packet.tx_length = out_length; + pipe->operation = CONTROL_OUT; + + pio_usb_host_send_setup(device->root - pio_usb_root_port, device->address, setup_data); + } else { + return -1; + } + + const uint64_t timeout = 5000 * 1000; // 5s + uint64_t start_time = time_us_64(); + while (pipe->operation == CONTROL_OUT && + time_us_64() - start_time < timeout) { + if (!device->connected) { + pipe->operation = CONTROL_NONE; + return -1; + } + } + + if (time_us_64() - start_time >= timeout) { + printf("control out[timeout]\n"); + res = -2; + } else if (pipe->operation == CONTROL_ERROR) { + printf("control out[error]\n"); + res = -1; + } else if (pipe->operation == CONTROL_COMPLETE) { + printf("control out[complete]\n"); + res = 0; + } + pipe->operation = CONTROL_NONE; + + return res; +} + +static int __no_inline_not_in_flash_func(control_in_protocol)( + usb_device_t *device, uint8_t *tx_data, uint16_t tx_length, + uint8_t *rx_buffer, uint16_t request_length) { + int res = 0; + + control_pipe_t *pipe = &device->control_pipe; + + if (pipe->operation == CONTROL_NONE) { + pipe->setup_packet.tx_address = tx_data; + pipe->setup_packet.tx_length = tx_length; + pipe->rx_buffer = rx_buffer; + pipe->request_length = request_length; + pipe->operation = CONTROL_IN; + + pio_usb_host_send_setup(device->root - pio_usb_root_port, device->address, tx_data); + } else { + return -1; + } + + const uint64_t timeout = 5000 * 1000; // 5s + uint64_t start_time = time_us_64(); + while (pipe->operation == CONTROL_IN && + time_us_64() - start_time < timeout) { + if (!device->connected) { + pipe->operation = CONTROL_NONE; + return -1; + } + } + + if (time_us_64() - start_time >= timeout) { + printf("control in[timeout]\n"); + res = -2; + } else if (pipe->operation == CONTROL_ERROR) { + printf("control in[error]\n"); + res = -1; + } else if (pipe->operation == CONTROL_COMPLETE) { + printf("control in[complete]\n"); + res = 0; + } + pipe->operation = CONTROL_NONE; + + return res; +} + +static int set_hub_feature(usb_device_t *device, uint8_t port, uint8_t value) { + usb_setup_packet_t req = SET_HUB_FEATURE_REQUEST; + req.index_lsb = port + 1; + req.value_lsb = value; + return control_out_protocol(device, (uint8_t *)&req, sizeof(req), NULL, 0); +} + +static int clear_hub_feature(usb_device_t *device, uint8_t port, + uint8_t value) { + usb_setup_packet_t req = CLEAR_HUB_FEATURE_REQUEST; + req.index_lsb = port + 1; + req.value_lsb = value; + return control_out_protocol(device, (uint8_t *)&req, sizeof(req), NULL, 0); +} + +static int get_hub_port_status(usb_device_t *device, uint8_t port, + hub_port_status_t *status) { + usb_setup_packet_t req = GET_HUB_PORT_STATUS_REQUEST; + req.index_lsb = port + 1; + return control_in_protocol(device, (uint8_t *)&req, sizeof(req), (uint8_t*)status, + sizeof(*status)); +} + +static int initialize_hub(usb_device_t *device) { + uint8_t rx_buffer[16]; + int res = 0; + printf("USB Hub detected\n"); + usb_setup_packet_t get_hub_desc_request = GET_HUB_DESCRPTOR_REQUEST; + control_in_protocol(device, (uint8_t *)&get_hub_desc_request, + sizeof(get_hub_desc_request), rx_buffer, 8); + const hub_descriptor_t *desc = (hub_descriptor_t *)rx_buffer; + uint8_t port_num = desc->port_num; + + printf("\tTurn on port powers\n"); + for (int idx = 0; idx < port_num; idx++) { + res = set_hub_feature(device, idx, HUB_SET_PORT_POWER); + if (res != 0) { + printf("\tFailed to turn on ports\n"); + break; + } + } + + busy_wait_ms(500); + + return res; +} + +static int get_string_descriptor(usb_device_t *device, uint8_t idx, + uint8_t *rx_buffer, uint8_t *str_buffer) { + int res = -1; + usb_setup_packet_t req = GET_DEVICE_DESCRIPTOR_REQ_DEFAULT; + req.value_msb = DESC_TYPE_STRING; + req.value_lsb = idx; + req.length_lsb = 1; + req.length_msb = 0; + res = control_in_protocol(device, (uint8_t *)&req, sizeof(req), rx_buffer, 1); + if (res != 0) { + return res; + } + + uint8_t len = rx_buffer[0]; + req.length_lsb = len; + req.length_msb = 0; + res = + control_in_protocol(device, (uint8_t *)&req, sizeof(req), rx_buffer, len); + if (res != 0) { + return res; + } + + uint16_t *wchar_buffer = (uint16_t *)(uintptr_t) rx_buffer; + for (int i = 0; i < (len - 2) / 2; i++) { + str_buffer[i] = wchar_buffer[i + 1]; + } + str_buffer[(len - 2) / 2] = '\0'; + + return res; +} + +static int enumerate_device(usb_device_t *device, uint8_t address) { + int res = 0; + uint8_t rx_buffer[512]; + + usb_setup_packet_t get_device_descriptor_request = + GET_DEVICE_DESCRIPTOR_REQ_DEFAULT; + res = control_in_protocol(device, (uint8_t *)&get_device_descriptor_request, + sizeof(get_device_descriptor_request), rx_buffer, 18); + if (res != 0) { + pio_usb_host_close_device(device->root - pio_usb_root_port, 0); + return res; + } + + const device_descriptor_t *desc = + (device_descriptor_t *)device->control_pipe.rx_buffer; + device->vid = desc->vid[0] | (desc->vid[1] << 8); + device->pid = desc->pid[0] | (desc->pid[1] << 8); + device->device_class = desc->device_class; + uint8_t idx_manufacture = desc->manufacture; + uint8_t idx_product = desc->product; + uint8_t idx_serial = desc->serial; + + printf("Enumerating %04x:%04x, class:%d, address:%d\n", device->vid, + device->pid, device->device_class, address); + + usb_setup_packet_t set_address_request = SET_ADDRESS_REQ_DEFAULT; + set_address_request.value_lsb = address; + set_address_request.value_msb = 0; + res = control_out_protocol(device, (uint8_t *)&set_address_request, + sizeof(set_address_request), NULL, 0); + pio_usb_host_close_device(device->root - pio_usb_root_port, 0); + if (res != 0) { + return res; + } + device->address = address; + + endpoint_descriptor_t ep0_desc = { + sizeof(endpoint_descriptor_t), DESC_TYPE_ENDPOINT, 0x00, 0x00, { desc->max_packet_size, 0x00 }, 0x00 + }; + pio_usb_host_endpoint_open(device->root - pio_usb_root_port, address, + (uint8_t const *)&ep0_desc, + !device->is_root && !device->is_fullspeed); + + uint8_t str[64]; + if (idx_manufacture != 0) { + res = get_string_descriptor(device, idx_manufacture, rx_buffer, str); + if (res == 0) { + printf("Manufacture:%s\n", str); + } else { + printf("Failed to get string descriptor (Manufacture)\n"); + } + stdio_flush(); + } + + if (idx_product != 0) { + res = get_string_descriptor(device, idx_product, rx_buffer, str); + if (res == 0) { + printf("Product:%s\n", str); + } else { + printf("Failed to get string descriptor (Product)\n"); + } + stdio_flush(); + } + + if (idx_serial != 0) { + res = get_string_descriptor(device, idx_serial, rx_buffer, str); + if (res == 0) { + printf("Serial:%s\n", str); + } else { + printf("Failed to get string descriptor (Serial)\n"); + } + stdio_flush(); + } + + usb_setup_packet_t get_configuration_descriptor_request = + GET_CONFIGURATION_DESCRIPTOR_REQ_DEFAULT; + get_configuration_descriptor_request.length_lsb = 9; + get_configuration_descriptor_request.length_msb = 0; + res = control_in_protocol( + device, (uint8_t *)&get_configuration_descriptor_request, + sizeof(get_configuration_descriptor_request), rx_buffer, 9); + if (res != 0) { + return res; + } + + get_configuration_descriptor_request.length_lsb = + ((configuration_descriptor_t *)(device->control_pipe.rx_buffer)) + ->total_length_lsb; + get_configuration_descriptor_request.length_msb = + ((configuration_descriptor_t *)(device->control_pipe.rx_buffer)) + ->total_length_msb; + uint16_t request_length = + get_configuration_descriptor_request.length_lsb | + (get_configuration_descriptor_request.length_msb << 8); + res = control_in_protocol( + device, (uint8_t *)&get_configuration_descriptor_request, + sizeof(get_configuration_descriptor_request), rx_buffer, request_length); + + if (res != 0) { + return res; + } + uint8_t configuration_descrptor_data[512]; + int16_t configuration_descrptor_length = + request_length > 512 ? 512 : request_length; + memcpy(configuration_descrptor_data, + (const void *)device->control_pipe.rx_buffer, + configuration_descrptor_length); + + usb_setup_packet_t set_usb_configuration_request = + SET_CONFIGURATION_REQ_DEFAULT; + set_usb_configuration_request.value_lsb = + ((configuration_descriptor_t *)(device->control_pipe.rx_buffer)) + ->configuration_value; + res = control_out_protocol(device, (uint8_t *)&set_usb_configuration_request, + sizeof(set_usb_configuration_request), NULL, 0); + + if (res != 0) { + return res; + } + volatile uint8_t ep_id_idx = 0; + volatile uint8_t interface = 0; + volatile uint8_t class = 0; + uint8_t *descriptor = configuration_descrptor_data; + while (configuration_descrptor_length > 0) { + switch (descriptor[1]) { + case DESC_TYPE_INTERFACE: { + const interface_descriptor_t *d = + (const interface_descriptor_t *)descriptor; + printf( + "inum:%d, altsetting:%d, numep:%d, iclass:%d, isubclass:%d, " + "iprotcol:%d, iface:%d\n", + d->inum, d->altsetting, d->numep, d->iclass, d->isubclass, + d->iprotocol, d->iface); + interface = d->inum; + class = d->iclass; + } break; + case DESC_TYPE_ENDPOINT: { + const endpoint_descriptor_t *d = + (const endpoint_descriptor_t *)descriptor; + printf("\t\t\tepaddr:0x%02x, attr:%d, size:%d, interval:%d\n", + d->epaddr, d->attr, d->max_size[0] | (d->max_size[1] << 8), + d->interval); + + if ((class == CLASS_HID || class == CLASS_HUB) && + d->attr == EP_ATTR_INTERRUPT) { + volatile endpoint_t *ep = NULL; + for (int ep_pool_idx = 0; ep_pool_idx < PIO_USB_EP_POOL_CNT; + ep_pool_idx++) { + if (pio_usb_ep_pool[ep_pool_idx].size == 0) { + ep = &pio_usb_ep_pool[ep_pool_idx]; + device->endpoint_id[ep_id_idx] = ep_pool_idx + 1; + ep_id_idx++; + break; + } + } + + if (ep != NULL) { + ep->interval = d->interval; + ep->interval_counter = 0; + ep->size = d->max_size[0] | (d->max_size[1] << 8); + ep->attr = d->attr | EP_ATTR_ENUMERATING; + ep->ep_num = d->epaddr; + + ep->root_idx = device->root - pio_usb_root_port; + ep->dev_addr = device->address; + ep->need_pre = !device->is_root && !device->is_fullspeed; + ep->is_tx = (d->epaddr & 0x80) ? false : true; + } else { + printf("No empty EP\n"); + } + } + } break; + case DESC_TYPE_HID: { + const hid_descriptor_t *d = (const hid_descriptor_t *)descriptor; + printf( + "\tbcdHID:%x.%x, country:%d, desc num:%d, desc_type:%d, " + "desc_size:%d\n", + d->bcd_hid[1], d->bcd_hid[0], d->contry_code, d->num_desc, + d->desc_type, d->desc_len[0] | (d->desc_len[1] << 8)); + + usb_setup_packet_t set_hid_idle_request = SET_HID_IDLE_REQ_DEFAULT; + set_hid_idle_request.value_lsb = interface; + set_hid_idle_request.value_msb = 0; + control_out_protocol(device, (uint8_t *)&set_hid_idle_request, + sizeof(set_hid_idle_request), NULL, 0); + + usb_setup_packet_t get_hid_report_descrpitor_request = + GET_HID_REPORT_DESCRIPTOR_DEFAULT; + get_hid_report_descrpitor_request.index_lsb = interface; + get_hid_report_descrpitor_request.index_msb = 0; + get_hid_report_descrpitor_request.length_lsb = d->desc_len[0]; + get_hid_report_descrpitor_request.length_msb = d->desc_len[1]; + uint16_t desc_len = d->desc_len[0] | (d->desc_len[1] << 8); + control_in_protocol( + device, (uint8_t *)&get_hid_report_descrpitor_request, + sizeof(get_hid_report_descrpitor_request), rx_buffer, desc_len); + printf("\t\tReport descriptor:"); + for (int i = 0; i < desc_len; i++) { + printf("%02x ", device->control_pipe.rx_buffer[i]); + } + printf("\n"); + stdio_flush(); + + } break; + default: + break; + } + + configuration_descrptor_length -= descriptor[0]; + descriptor += descriptor[0]; + } + + for (int epidx = 0; epidx < PIO_USB_DEV_EP_CNT; epidx++) { + endpoint_t *ep = pio_usb_get_endpoint(device, epidx); + if (ep == NULL) { + break; + } + ep->attr &= ~EP_ATTR_ENUMERATING; + + // prepare transfer + if (ep->ep_num & EP_IN) { + pio_usb_ll_transfer_start(ep, ep->buffer, ep->size); + } + } + + return res; +} + +static void device_disconnect(usb_device_t *device) { + printf("Disconnect device %d\n", device->address); + for (int port = 0; port < PIO_USB_HUB_PORT_CNT; port++) { + if (device->child_devices[port] != 0) { + device_disconnect(&pio_usb_device[device->child_devices[port]]); + } + } + + for (int ep_idx = 0; ep_idx < PIO_USB_DEV_EP_CNT; ep_idx++) { + endpoint_t *ep = pio_usb_get_endpoint(device, ep_idx); + if (ep == NULL) { + break; + } + memset(ep, 0, sizeof(*ep)); + } + + if (device->address == 0 && device->root != NULL) { + device->root->addr0_exists = false; + } + + memset(device, 0, sizeof(*device)); +} + +static int device_pool_vacant(void) { + for (int idx = 0; idx < PIO_USB_DEVICE_CNT; idx++) { + if (!pio_usb_device[idx].connected) { + return idx; + } + } + + return -1; +} + +static int assign_new_device_to_port(usb_device_t *hub_device, uint8_t port, bool is_ls) { + int idx = device_pool_vacant(); + if (idx >= 0) { + hub_device->child_devices[port] = idx; + pio_usb_device[idx].parent_device = hub_device; + pio_usb_device[idx].parent_port = port; + pio_usb_device[idx].root = hub_device->root; + pio_usb_device[idx].connected = true; + pio_usb_device[idx].is_fullspeed = !is_ls; + pio_usb_device[idx].event = EVENT_CONNECT; + printf("Assign device %d to %d-%d\n", idx, hub_device->address, port); + + endpoint_descriptor_t ep0_desc = { + sizeof(endpoint_descriptor_t), DESC_TYPE_ENDPOINT, 0x00, 0x00, { 0x08, 0x00 }, 0x00 + }; + pio_usb_host_endpoint_open(hub_device->root - pio_usb_root_port, 0x00, + (uint8_t const *)&ep0_desc, is_ls); + + return 0; + } + + printf("Failed to assign device\n"); + + return -1; +} + +static void __no_inline_not_in_flash_func(process_hub_event)( + usb_device_t *device) { + volatile endpoint_t *ep = pio_usb_get_endpoint(device, 0); + uint8_t bitmap = ep->buffer[0]; + for (int bit = 1; bit < 8; bit++) { + if (!(bitmap & (1 << bit))) { + continue; + } + uint8_t port = bit - 1; + hub_port_status_t status; + int res = get_hub_port_status(device, port, &status); + if (res != 0) { + printf("Failed to get port%d-%d status\n", device->address, port); + continue; + } + printf("port%d-%d status:%d %d\n", device->address, port, + status.port_change, status.port_status); + + if (status.port_change & HUB_CHANGE_PORT_CONNECTION) { + if (status.port_status & HUB_STAT_PORT_CONNECTION) { + printf("new device on port %d, reset port\n", port); + if (device->child_devices[port] != 0) { + printf("device is already assigned. disconnect previous\n"); + device_disconnect(&pio_usb_device[device->child_devices[port]]); + } + + if (device->root->addr0_exists) { + printf("Address 0 already exists\n"); + continue; + } + + if (device_pool_vacant() >= 0) { + set_hub_feature(device, port, HUB_SET_PORT_RESET); + device->root->addr0_exists = true; + } else { + printf("No vacant in device pool\n"); + } + } else { + printf("device removed from port %d\n", port); + if (device->child_devices[port] != 0) { + device_disconnect(&pio_usb_device[device->child_devices[port]]); + } + } + clear_hub_feature(device, port, HUB_CLR_PORT_CONNECTION); + } else if (status.port_change & HUB_CHANGE_PORT_RESET) { + printf("reset port %d complete\n", port); + res = clear_hub_feature(device, port, HUB_CLR_PORT_RESET); + if (res == 0) { + assign_new_device_to_port(device, port, + status.port_status & HUB_STAT_PORT_LOWSPEED); + } + } else if (status.port_change & HUB_CHANGE_PORT_ENABLE) { + clear_hub_feature(device, port, HUB_CLR_PORT_ENABLE); + } + } + + device->event = EVENT_NONE; +} + +void __no_inline_not_in_flash_func(pio_usb_host_task)(void) { + for (int root_idx = 0; root_idx < PIO_USB_ROOT_PORT_CNT; root_idx++) { + if (pio_usb_root_port[root_idx].event == EVENT_CONNECT) { + printf("Root %d connected\n", root_idx); + int dev_idx = device_pool_vacant(); + if (dev_idx >= 0) { + on_device_connect(&pio_port[0], &pio_usb_root_port[root_idx], dev_idx); + pio_usb_root_port[root_idx].addr0_exists = true; + } + pio_usb_root_port[root_idx].event = EVENT_NONE; + } else if (pio_usb_root_port[root_idx].event == EVENT_DISCONNECT) { + printf("Root %d disconnected\n", root_idx); + pio_usb_host_close_device( + root_idx, pio_usb_root_port[root_idx].root_device->address); + pio_usb_root_port[root_idx].root_device->connected = false; + pio_usb_root_port[root_idx].root_device->event = EVENT_DISCONNECT; + pio_usb_root_port[root_idx].root_device = NULL; + pio_usb_root_port[root_idx].event = EVENT_NONE; + } + } + + for (int idx = 0; idx < PIO_USB_DEVICE_CNT; idx++) { + usb_device_t *device = &pio_usb_device[idx]; + + if (device->event == EVENT_CONNECT) { + device->event = EVENT_NONE; + printf("Device %d Connected\n", idx); + int res = enumerate_device(device, idx + 1); + if (res == 0) { + device->enumerated = true; + device->root->addr0_exists = false; + if (device->device_class == CLASS_HUB) { + res = initialize_hub(device); + } + } + + if (res != 0) { + printf("Enumeration failed(%d)\n", res); + // retry + if (device->is_root) { + device->root->event = EVENT_DISCONNECT; + } else { + set_hub_feature(device->parent_device, device->parent_port, + HUB_SET_PORT_RESET); + device_disconnect(device); + } + } + } else if (device->event == EVENT_DISCONNECT) { + device->event = EVENT_NONE; + printf("Disconnect\n"); + device_disconnect(device); + } else if (device->event == EVENT_HUB_PORT_CHANGE) { + process_hub_event(device); + } + } + + if (cancel_timer_flag) { + int_stat = save_and_disable_interrupts(); + stop_timer(); + if (pio_usb_root_port->root_device != NULL) { + device_disconnect(pio_usb_root_port->root_device); + } + cancel_timer_flag = false; + } + + if (start_timer_flag) { + start_timer(_alarm_pool); + restore_interrupts(int_stat); + start_timer_flag = false; + } +} + +static void __no_inline_not_in_flash_func(handle_endpoint_irq)( + root_port_t *root, uint32_t flag, volatile uint32_t *ep_reg) { + (void)root; + const uint32_t ep_all = *ep_reg; + + for (uint8_t ep_idx = 0; ep_idx < PIO_USB_EP_POOL_CNT; ep_idx++) { + if (ep_all & (1u << ep_idx)) { + endpoint_t *ep = PIO_USB_ENDPOINT(ep_idx); + usb_device_t *device = NULL; + + // find device this endpoint belongs to + for (int idx = 0; idx < PIO_USB_DEVICE_CNT; idx++) { + usb_device_t *dev = &pio_usb_device[idx]; + if (dev->connected && (ep->dev_addr == dev->address)) { + device = dev; + break; + } + } + + if (device) { + // control endpoint is either 0x00 or 0x80 + if ((ep->ep_num & 0x7f) == 0) { + control_pipe_t *pipe = &device->control_pipe; + + if (flag != PIO_USB_INTS_ENDPOINT_COMPLETE_BITS) { + pipe->stage = STAGE_SETUP; + pipe->operation = CONTROL_ERROR; + } else { + ep->data_id = 1; // both data and status have DATA1 + if (pipe->stage == STAGE_SETUP) { + if (pipe->operation == CONTROL_IN) { + pipe->stage = STAGE_IN; + ep->ep_num = 0x80; + ep->is_tx = false; + pio_usb_ll_transfer_start(ep, + (uint8_t *)(uintptr_t)pipe->rx_buffer, + pipe->request_length); + } else if (pipe->operation == CONTROL_OUT) { + if (pipe->out_data_packet.tx_address != NULL) { + pipe->stage = STAGE_OUT; + ep->ep_num = 0x00; + ep->is_tx = true; + pio_usb_ll_transfer_start(ep, + pipe->out_data_packet.tx_address, + pipe->out_data_packet.tx_length); + } else { + pipe->stage = STAGE_STATUS; + ep->ep_num = 0x80; + ep->is_tx = false; + pio_usb_ll_transfer_start(ep, NULL, 0); + } + } + } else if (pipe->stage == STAGE_IN) { + pipe->stage = STAGE_STATUS; + ep->ep_num = 0x00; + ep->is_tx = true; + pio_usb_ll_transfer_start(ep, NULL, 0); + } else if (pipe->stage == STAGE_OUT) { + pipe->stage = STAGE_STATUS; + ep->ep_num = 0x80; + ep->is_tx = false; + pio_usb_ll_transfer_start(ep, NULL, 0); + } else if (pipe->stage == STAGE_STATUS) { + pipe->stage = STAGE_SETUP; + pipe->operation = CONTROL_COMPLETE; + } + } + } else if (device->device_class == CLASS_HUB && (ep->ep_num & EP_IN)) { + // hub interrupt endpoint + device->event = EVENT_HUB_PORT_CHANGE; + } + } + } + } + + // clear all + (*ep_reg) &= ~ep_all; +} + +// IRQ Handler +static void __no_inline_not_in_flash_func(__pio_usb_host_irq_handler)(uint8_t root_id) { + root_port_t *root = PIO_USB_ROOT_PORT(root_id); + uint32_t const ints = root->ints; + + if (ints & PIO_USB_INTS_CONNECT_BITS) { + root->event = EVENT_CONNECT; + } + + if (ints & PIO_USB_INTS_DISCONNECT_BITS) { + root->event = EVENT_DISCONNECT; + } + + if (ints & PIO_USB_INTS_ENDPOINT_COMPLETE_BITS) { + handle_endpoint_irq(root, PIO_USB_INTS_ENDPOINT_COMPLETE_BITS, + &root->ep_complete); + } + + if (ints & PIO_USB_INTS_ENDPOINT_STALLED_BITS) { + handle_endpoint_irq(root, PIO_USB_INTS_ENDPOINT_STALLED_BITS, + &root->ep_stalled); + } + + if (ints & PIO_USB_INTS_ENDPOINT_ERROR_BITS) { + handle_endpoint_irq(root, PIO_USB_INTS_ENDPOINT_ERROR_BITS, + &root->ep_error); + } + + // clear all + root->ints &= ~ints; +} + +// weak alias to __pio_usb_host_irq_handler +void pio_usb_host_irq_handler(uint8_t root_id) __attribute__ ((weak, alias("__pio_usb_host_irq_handler"))); + +#pragma GCC pop_options diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb_ll.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb_ll.h new file mode 100644 index 0000000000..3329ebc04b --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/pio_usb_ll.h @@ -0,0 +1,196 @@ +/** + * Copyright (c) 2021 sekigon-gonnoc + * Ha Thach (thach@tinyusb.org) + */ + +#pragma once + +#include "hardware/pio.h" +#include "pio_usb_configuration.h" +#include "usb_definitions.h" + +enum { + PIO_USB_INTS_CONNECT_POS = 0, + PIO_USB_INTS_DISCONNECT_POS, + PIO_USB_INTS_RESET_END_POS, + PIO_USB_INTS_SETUP_REQ_POS, + PIO_USB_INTS_SOF_POS, + + PIO_USB_INTS_ENDPOINT_COMPLETE_POS, + PIO_USB_INTS_ENDPOINT_ERROR_POS, + PIO_USB_INTS_ENDPOINT_STALLED_POS, +}; + +#define PIO_USB_INTS_CONNECT_BITS (1u << PIO_USB_INTS_CONNECT_POS) +#define PIO_USB_INTS_DISCONNECT_BITS (1u << PIO_USB_INTS_DISCONNECT_POS) +#define PIO_USB_INTS_RESET_END_BITS (1u << PIO_USB_INTS_RESET_END_POS) +#define PIO_USB_INTS_SETUP_REQ_BITS (1u << PIO_USB_INTS_SETUP_REQ_POS) + +#define PIO_USB_INTS_SOF_BITS (1u << PIO_USB_INTS_SOF_POS) + +#define PIO_USB_INTS_ENDPOINT_COMPLETE_BITS \ + (1u << PIO_USB_INTS_ENDPOINT_COMPLETE_POS) +#define PIO_USB_INTS_ENDPOINT_ERROR_BITS (1u << PIO_USB_INTS_ENDPOINT_ERROR_POS) +#define PIO_USB_INTS_ENDPOINT_STALLED_BITS \ + (1u << PIO_USB_INTS_ENDPOINT_STALLED_POS) + +typedef enum { + PORT_PIN_SE0 = 0b00, + PORT_PIN_FS_IDLE = 0b01, + PORT_PIN_LS_IDLE = 0b10, + PORT_PIN_SE1 = 0b11, +} port_pin_status_t; + +typedef struct { + uint16_t div_int; + uint8_t div_frac; +} pio_clk_div_t; + +typedef struct { + PIO pio_usb_tx; // could not set to volatile + uint sm_tx; + uint offset_tx; + uint tx_ch; + + PIO pio_usb_rx; // could not set to volatile + uint sm_rx; + uint offset_rx; + uint sm_eop; + uint offset_eop; + uint rx_reset_instr; + uint rx_reset_instr2; + uint device_rx_irq_num; + + int8_t debug_pin_rx; + int8_t debug_pin_eop; + uint8_t error_count; + uint8_t extra_error_retry_count; + uint32_t total_error_count; + uint32_t total_fatal_error_count; + uint32_t total_transaction_count; + + pio_clk_div_t clk_div_fs_tx; + pio_clk_div_t clk_div_fs_rx; + pio_clk_div_t clk_div_ls_tx; + pio_clk_div_t clk_div_ls_rx; + + bool need_pre; + + uint8_t usb_rx_buffer[128]; +} pio_port_t; + +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ + +enum { + PIO_USB_MODE_INVALID = 0, + PIO_USB_MODE_DEVICE, + PIO_USB_MODE_HOST, +}; + +extern usb_device_t pio_usb_device[PIO_USB_DEVICE_CNT]; + +extern root_port_t pio_usb_root_port[PIO_USB_ROOT_PORT_CNT]; +#define PIO_USB_ROOT_PORT(_idx) (pio_usb_root_port + (_idx)) + +extern endpoint_t pio_usb_ep_pool[PIO_USB_EP_POOL_CNT]; +#define PIO_USB_ENDPOINT(_idx) (pio_usb_ep_pool + (_idx)) + +extern pio_port_t pio_port[1]; +#define PIO_USB_PIO_PORT(_idx) (pio_port + (_idx)) + +//--------------------------------------------------------------------+ +// Bus functions +//--------------------------------------------------------------------+ + +#define IRQ_TX_EOP_MASK (1 << usb_tx_fs_IRQ_EOP) +#define IRQ_TX_COMP_MASK (1 << usb_tx_fs_IRQ_COMP) +#define IRQ_TX_ALL_MASK (IRQ_TX_EOP_MASK | IRQ_TX_COMP_MASK) +#define IRQ_RX_COMP_MASK (1 << IRQ_RX_EOP) +#define IRQ_RX_ALL_MASK \ + ((1 << IRQ_RX_EOP) | (1 << IRQ_RX_BS_ERR) | (1 << IRQ_RX_START) | \ + (1 << DECODER_TRIGGER)) + +#define SM_SET_CLKDIV(pio, sm, div) \ + pio_sm_set_clkdiv_int_frac(pio, sm, div.div_int, div.div_frac) +#define SM_SET_CLKDIV_MAXSPEED(pio, sm) \ + pio_sm_set_clkdiv_int_frac(pio, sm, 1, 0) + +void pio_usb_bus_init(pio_port_t *pp, const pio_usb_configuration_t *c, + root_port_t *root); + +void pio_usb_bus_start_receive(const pio_port_t *pp); +void pio_usb_bus_prepare_receive(const pio_port_t *pp); +int pio_usb_bus_receive_packet_and_handshake(pio_port_t *pp, uint8_t handshake); +void pio_usb_bus_usb_transfer(const pio_port_t *pp, uint8_t *data, + uint16_t len); + +uint8_t pio_usb_bus_wait_handshake(pio_port_t *pp); +void pio_usb_bus_send_handshake(const pio_port_t *pp, uint8_t pid); +void pio_usb_bus_send_token(const pio_port_t *pp, uint8_t token, uint8_t addr, + uint8_t ep_num); + +static __always_inline port_pin_status_t +pio_usb_bus_get_line_state(root_port_t *root) { + uint8_t dp = gpio_get(root->pin_dp) ? 0 : 1; + uint8_t dm = gpio_get(root->pin_dm) ? 0 : 1; + + return (dm << 1) | dp; +} + +//--------------------------------------------------------------------+ +// Low Level functions +//--------------------------------------------------------------------+ + +void pio_usb_ll_configure_endpoint(endpoint_t *ep, + uint8_t const *desc_endpoint); +bool pio_usb_ll_transfer_start(endpoint_t *ep, uint8_t *buffer, + uint16_t buflen); +bool pio_usb_ll_transfer_continue(endpoint_t *ep, uint16_t xferred_bytes); +void pio_usb_ll_transfer_complete(endpoint_t *ep, uint32_t flag); + +static inline __force_inline uint16_t +pio_usb_ll_get_transaction_len(endpoint_t *ep) { + uint16_t remaining = ep->total_len - ep->actual_len; + return (remaining < ep->size) ? remaining : ep->size; +} + +//-------------------------------------------------------------------- +// Host Controller functions +//-------------------------------------------------------------------- + +// Host IRQ Handler +void pio_usb_host_irq_handler(uint8_t root_idx); + +void pio_usb_host_port_reset_start(uint8_t root_idx); +void pio_usb_host_port_reset_end(uint8_t root_idx); + +void pio_usb_host_close_device(uint8_t root_idx, uint8_t device_address); + +bool pio_usb_host_endpoint_open(uint8_t root_idx, uint8_t device_address, + uint8_t const *desc_endpoint, bool need_pre); +bool pio_usb_host_send_setup(uint8_t root_idx, uint8_t device_address, + uint8_t const setup_packet[8]); +bool pio_usb_host_endpoint_transfer(uint8_t root_idx, uint8_t device_address, + uint8_t ep_address, uint8_t *buffer, + uint16_t buflen); + +//-------------------------------------------------------------------- +// Device Controller functions +//-------------------------------------------------------------------- + +// Device IRQ Handler +void pio_usb_device_irq_handler(uint8_t root_idx); + +void pio_usb_device_set_address(uint8_t dev_addr); +bool pio_usb_device_endpoint_open(uint8_t const *desc_endpoint); +bool pio_usb_device_transfer(uint8_t ep_address, uint8_t *buffer, + uint16_t buflen); + +static inline __force_inline endpoint_t * +pio_usb_device_get_endpoint_by_address(uint8_t ep_address) { + // index = 2*num + dir e.g out1, in1, out2, in2 + uint8_t const ep_idx = ((ep_address & 0x7f) << 1) | (ep_address >> 7); + return PIO_USB_ENDPOINT(ep_idx); +} diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_crc.c b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_crc.c new file mode 100644 index 0000000000..c957f62d0a --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_crc.c @@ -0,0 +1,66 @@ +// Copyright 2021 sekigon-gonnoc +// SPDX-License-Identifier: MIT + +#include "usb_crc.h" + +uint8_t __not_in_flash_func(calc_usb_crc5)(uint16_t data) { + const uint8_t crc5_tbl[32] = { + 0x00, 0x0b, 0x16, 0x1d, 0x05, 0x0e, 0x13, 0x18, 0x0a, 0x01, + 0x1c, 0x17, 0x0f, 0x04, 0x19, 0x12, 0x14, 0x1f, 0x02, 0x09, + 0x11, 0x1a, 0x07, 0x0c, 0x1e, 0x15, 0x08, 0x03, 0x1b, 0x10, + 0x0d, 0x06, + }; + + data = data ^ 0x1f; + + const uint8_t lsb = (data >> 1) & 0x1f; + const uint8_t msb = (data >> 6) & 0x1f; + + uint8_t crc = (data & 1) ? 0x14 : 0x00; + crc = crc5_tbl[(lsb ^ crc)]; + crc = crc5_tbl[(msb ^ crc)]; + + return crc ^ 0x1f; +} + +// Place to RAM +const uint16_t __not_in_flash("crc_tbl") crc16_tbl[256] = { + 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, 0xc601, + 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, 0xcc01, 0x0cc0, + 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, 0x0a00, 0xcac1, 0xcb81, + 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, 0xd801, 0x18c0, 0x1980, 0xd941, + 0x1b00, 0xdbc1, 0xda81, 0x1a40, 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, + 0x1dc0, 0x1c80, 0xdc41, 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, + 0x1680, 0xd641, 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, + 0x1040, 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, + 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, 0x3c00, + 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, 0xfa01, 0x3ac0, + 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, 0x2800, 0xe8c1, 0xe981, + 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, 0xee01, 0x2ec0, 0x2f80, 0xef41, + 0x2d00, 0xedc1, 0xec81, 0x2c40, 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, + 0xe7c1, 0xe681, 0x2640, 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, + 0x2080, 0xe041, 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, + 0x6240, 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, + 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, 0xaa01, + 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, 0x7800, 0xb8c1, + 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, 0xbe01, 0x7ec0, 0x7f80, + 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, 0xb401, 0x74c0, 0x7580, 0xb541, + 0x7700, 0xb7c1, 0xb681, 0x7640, 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, + 0x71c0, 0x7080, 0xb041, 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, + 0x5280, 0x9241, 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, + 0x5440, 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, + 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, 0x8801, + 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, 0x4e00, 0x8ec1, + 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, 0x4400, 0x84c1, 0x8581, + 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, 0x8201, 0x42c0, 0x4380, 0x8341, + 0x4100, 0x81c1, 0x8081, 0x4040}; + +uint16_t calc_usb_crc16(const uint8_t *data, uint16_t len) { + uint16_t crc = 0xffff; + + for (int idx = 0; idx < len; idx++) { + crc = (crc >> 8) ^ crc16_tbl[(crc ^ data[idx]) & 0xff]; + } + + return crc ^ 0xffff; +} \ No newline at end of file diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_crc.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_crc.h new file mode 100644 index 0000000000..4c87101dfe --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_crc.h @@ -0,0 +1,18 @@ +// Copyright 2021 sekigon-gonnoc +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include "pico/stdlib.h" + +// Calc CRC5-USB of 11bit data +uint8_t calc_usb_crc5(uint16_t data); +// Calc CRC16-USB of array +uint16_t calc_usb_crc16(const uint8_t *data, uint16_t len); + +extern const uint16_t crc16_tbl[256]; +static inline uint16_t __time_critical_func(update_usb_crc16)(uint16_t crc, uint8_t data) { + crc = (crc >> 8) ^ crc16_tbl[(crc ^ data) & 0xff]; + return crc; +} \ No newline at end of file diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_definitions.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_definitions.h new file mode 100644 index 0000000000..938f3785fd --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_definitions.h @@ -0,0 +1,342 @@ +// Copyright 2021 sekigon-gonnoc +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +#include "pio_usb_configuration.h" + +typedef enum { + CONTROL_NONE, + CONTROL_IN, + CONTROL_OUT, + CONTROL_COMPLETE, + CONTROL_ERROR, +} control_transfer_operation_t; + +typedef enum { + EP_IN = 0x80, + EP_OUT = 0x00, +} ep_type_t; + +typedef enum { + STAGE_SETUP, + STAGE_DATA, + STAGE_IN, + STAGE_OUT, + STAGE_STATUS, + STAGE_COMPLETE, + STAGE_ERROR +} setup_transfer_stage_t; + +typedef enum { + STAGE_IDLE, + STAGE_DEVICE, + STAGE_CONFIG, + STAGE_CONFIG2, + STAGE_INTERFACE, + STAGE_ENDPOINT +} usb_enumeration_stage_t; + +typedef struct { + uint8_t *tx_address; + uint8_t tx_length; +} packet_info_t; + +typedef struct { + volatile uint8_t data_in_num; + volatile uint16_t buffer_idx; + volatile uint8_t *rx_buffer; + volatile packet_info_t setup_packet; + volatile packet_info_t out_data_packet; + volatile int16_t request_length; + volatile control_transfer_operation_t operation; + volatile setup_transfer_stage_t stage; +} control_pipe_t; + +typedef struct { + volatile uint8_t root_idx; + volatile uint8_t dev_addr; + bool need_pre; + bool is_tx; // Host out or Device in + + volatile uint8_t ep_num; + volatile uint16_t size; + uint8_t buffer[64 + 4]; + volatile bool new_data_flag; + volatile uint8_t attr; + volatile uint8_t interval; + volatile uint8_t interval_counter; + volatile uint8_t data_id; // data0 or data1 + + volatile bool stalled; + volatile bool has_transfer; + uint8_t *app_buf; + uint16_t total_len; + uint16_t actual_len; + uint8_t error_count; +} endpoint_t; + +typedef enum { + EVENT_NONE, + EVENT_CONNECT, + EVENT_DISCONNECT, + EVENT_HUB_PORT_CHANGE, +} usb_device_event_t; + +typedef struct struct_usb_device_t usb_device_t; +typedef struct struct_root_port_t { + volatile bool initialized; + volatile bool addr0_exists; + volatile uint8_t pin_dp; + volatile uint8_t pin_dm; + volatile usb_device_event_t event; + usb_device_t *root_device; + + volatile bool is_fullspeed; + volatile bool connected; + volatile bool suspended; + uint8_t mode; + + // register interface + volatile uint32_t ints; // interrupt status + volatile uint32_t ep_complete; + volatile uint32_t ep_error; + volatile uint32_t ep_stalled; + + // device only + uint8_t dev_addr; + uint8_t *setup_packet; +} root_port_t; + +struct struct_usb_device_t { + volatile bool connected; + volatile bool enumerated; + volatile usb_device_event_t event; + volatile uint8_t address; + volatile uint16_t vid; + volatile uint16_t pid; + volatile uint8_t device_class; + volatile bool is_fullspeed; + volatile bool is_root; + control_pipe_t control_pipe; + uint8_t endpoint_id[PIO_USB_DEV_EP_CNT]; + uint8_t child_devices[PIO_USB_HUB_PORT_CNT]; + struct struct_usb_device_t *parent_device; + uint8_t parent_port; + root_port_t *root; +}; + +enum { + USB_SYNC = 0x80, + USB_PID_OUT = 0xe1, + USB_PID_IN = 0x69, + USB_PID_SOF = 0xa5, + USB_PID_SETUP = 0x2d, + USB_PID_DATA0 = 0xc3, + USB_PID_DATA1 = 0x4b, + USB_PID_ACK = 0xd2, + USB_PID_NAK = 0x5a, + USB_PID_STALL = 0x1e, + USB_PID_PRE = 0x3c, + USB_CRC16_PLACE = 0, +}; + +enum { + DESC_TYPE_DEVICE = 0x01, + DESC_TYPE_CONFIG = 0x02, + DESC_TYPE_STRING = 0x03, + DESC_TYPE_INTERFACE = 0x04, + DESC_TYPE_ENDPOINT = 0x05, + DESC_TYPE_HID = 0x21, + DESC_TYPE_HID_REPORT = 0x22, +}; + +enum { + CLASS_HID = 0x03, + CLASS_HUB = 0x09, +}; + +enum { + EP_ATTR_CONTROL = 0x00, + EP_ATTR_ISOCHRONOUS = 0x01, + EP_ATTR_BULK = 0x02, + EP_ATTR_INTERRUPT = 0x03, + EP_ATTR_ENUMERATING = 0x80 +}; + +typedef struct { + // uint8_t sync; + // uint8_t pid; + uint8_t request_type; + uint8_t request; + uint8_t value_lsb; + uint8_t value_msb; + uint8_t index_lsb; + uint8_t index_msb; + uint8_t length_lsb; + uint8_t length_msb; + // uint8_t crc16[2]; +} usb_setup_packet_t; + +typedef struct { + uint8_t length; + uint8_t type; + uint8_t bcd_usb[2]; + uint8_t device_class; + uint8_t subclass; + uint8_t protocol; + uint8_t max_packet_size; + uint8_t vid[2]; + uint8_t pid[2]; + uint8_t bcd_device[2]; + uint8_t manufacture; + uint8_t product; + uint8_t serial; + uint8_t num_configu; +} device_descriptor_t; + +typedef struct { + uint8_t length; + uint8_t type; + uint8_t string[62]; +} __attribute__((aligned(2))) string_descriptor_t; + +typedef struct { + uint8_t length; + uint8_t type; + uint8_t inum; + uint8_t altsetting; + uint8_t numep; + uint8_t iclass; + uint8_t isubclass; + uint8_t iprotocol; + uint8_t iface; +} interface_descriptor_t; + +typedef struct { + uint8_t length; + uint8_t type; + uint8_t epaddr; + uint8_t attr; + uint8_t max_size[2]; + uint8_t interval; +} endpoint_descriptor_t; + +typedef struct { + uint8_t length; + uint8_t type; + uint8_t bcd_hid[2]; + uint8_t contry_code; + uint8_t num_desc; + uint8_t desc_type; + uint8_t desc_len[2]; +} hid_descriptor_t; + +typedef struct configuration_descriptor_tag { + uint8_t length; + uint8_t type; + uint8_t total_length_lsb; + uint8_t total_length_msb; + uint8_t num_interfaces; + uint8_t configuration_value; + uint8_t configuration; + uint8_t attributes; + uint8_t max_power; +} configuration_descriptor_t; + +typedef struct { + uint8_t lenght; + uint8_t type; + uint8_t port_num; + uint8_t chara_lsb; + uint8_t chara_msb; + uint8_t pow_on_time; + uint8_t current; + uint8_t removable; +} hub_descriptor_t; + +typedef struct { + uint16_t port_status; + uint16_t port_change; +} hub_port_status_t; + +enum { + HUB_SET_PORT_RESET = 4, + HUB_SET_PORT_POWER = 8, + HUB_CLR_PORT_CONNECTION = 16, + HUB_CLR_PORT_ENABLE = 17, + HUB_CLR_PORT_SUSPEND = 18, + HUB_CLR_PORT_RESET = 20, +}; + +enum { + HUB_STAT_PORT_CONNECTION = (1 << 0), + HUB_STAT_PORT_ENABLE = (1 << 1), + HUB_STAT_PORT_SUSPEND = (1 << 2), + HUB_STAT_PORT_OC = (1 << 3), + HUB_STAT_PORT_RESET = (1 << 4), + HUB_STAT_PORT_POWER = (1 << 8), + HUB_STAT_PORT_LOWSPEED = (1 << 9), +}; + +enum { + HUB_CHANGE_PORT_CONNECTION = (1 << 0), + HUB_CHANGE_PORT_ENABLE = (1 << 1), + HUB_CHANGE_PORT_SUSPEND = (1 << 2), + HUB_CHANGE_PORT_OC = (1 << 3), + HUB_CHANGE_PORT_RESET = (1 << 4), +}; + +enum { + USB_REQ_DIR_IN = 0x80, + USB_REQ_DIR_OUT = 0x00, + USB_REQ_TYP_STANDARD = 0x00, + USB_REQ_TYP_CLASS = 0x20, + USB_REQ_TYP_VENDOR = 0x40, + USB_REQ_REC_DEVICE = 0x00, + USB_REQ_REC_IFACE = 0x01, + USB_REQ_REC_EP = 0x02, + USB_REQ_REC_OTHER = 0x03, +}; + +#define GET_DEVICE_DESCRIPTOR_REQ_DEFAULT \ + { USB_REQ_DIR_IN, 0x06, 0, 0x01, 0, 0, 0x12, 0 } +#define GET_CONFIGURATION_DESCRIPTOR_REQ_DEFAULT \ + { USB_REQ_DIR_IN, 0x06, 0, 0x02, 0, 0, 0x09, 0 } +#define SET_CONFIGURATION_REQ_DEFAULT \ + { USB_REQ_DIR_OUT, 0x09, 0, 0, 0, 0, 0, 0 } +#define SET_ADDRESS_REQ_DEFAULT \ + { USB_REQ_DIR_OUT, 0x5, 0x02, 0, 0, 0, 0, 0 } +#define SET_HID_IDLE_REQ_DEFAULT \ + { USB_REQ_TYP_CLASS | USB_REQ_REC_IFACE, 0x0A, 0, 0, 0, 0, 0, 0 } +#define GET_HID_REPORT_DESCRIPTOR_DEFAULT \ + { USB_REQ_DIR_IN | USB_REQ_REC_IFACE, 0x06, 0, 0x22, 0, 0, 0xff, 0 } +#define GET_HUB_DESCRPTOR_REQUEST \ + { \ + USB_REQ_DIR_IN | USB_REQ_TYP_CLASS | USB_REQ_REC_DEVICE, 0x06, 0, 0x29, 0, \ + 0, 8, 0 \ + } +#define GET_HUB_PORT_STATUS_REQUEST \ + { \ + USB_REQ_DIR_IN | USB_REQ_TYP_CLASS | USB_REQ_REC_OTHER, 0, 0, 0, 0, 0, 4, \ + 0 \ + } +#define SET_HUB_FEATURE_REQUEST \ + { \ + USB_REQ_DIR_OUT | USB_REQ_TYP_CLASS | USB_REQ_REC_OTHER, 0x03, 0, 0, 0, 0, \ + 0, 0 \ + } +#define CLEAR_HUB_FEATURE_REQUEST \ + { \ + USB_REQ_DIR_OUT | USB_REQ_TYP_CLASS | USB_REQ_REC_OTHER, 0x01, 0, 0, 0, 0, \ + 0, 0 \ + } + +typedef struct { + const uint8_t *device; + const uint8_t *config; + const uint8_t **hid_report; + const string_descriptor_t *string; +} usb_descriptor_buffers_t; diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_rx.pio b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_rx.pio new file mode 100644 index 0000000000..bcd9d5e6c9 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_rx.pio @@ -0,0 +1,238 @@ + +; Copyright (c) 2021-2022 sekigon-gonnoc + +.define public IRQ_RX_BS_ERR 1 ; bit stuffinc error +.define public IRQ_RX_EOP 2 ; eop detect flag +.define public IRQ_RX_START 3 ; packet start flag +.define public DECODER_TRIGGER 4 + +.define BIT_REPEAT_COUNT 6 ; bit repeat counter + +.define db0 0 +.define db1 1 + +; USB signal edge and eop detector +; 17 instruction +; FS(12M) LS(1.5M) +; Run at 96MHz 12MHz +; jmp_pin d- d+ +; in_pin d+ d- +; both d+/d- pin should be input invert overrideed +.program usb_edge_detector +eop: + irq wait IRQ_RX_EOP +start: + jmp pin start ; Wait fall edge + irq IRQ_RX_START [1] + +.wrap_target +pin_still_low: + irq DECODER_TRIGGER [1] ; Trigger NRZI decoder + +; Resync on rising edge +pin_low: + jmp pin pin_went_high +pin_went_low: + jmp pin pin_went_high + jmp pin pin_went_high + jmp pin pin_went_high + jmp pin pin_went_high + jmp pin pin_went_high +.wrap + +pin_still_high: + mov x, isr [2] + jmp x-- eop ; Jump to eop if jmp_pin and in_pin are high because both inputs are inverted +; Jump to here on rising edge +pin_went_high: + mov isr, null + in pins, 1 ; Capture the pin to check eop. + irq DECODER_TRIGGER ; Trigger NRZI decoder + jmp pin pin_still_high + jmp pin_went_low ; To adjust interval of decoder trigger, jump to pin_went_low (not pin_low) + +.program usb_edge_detector_debug +.side_set 1 +eop: + irq wait IRQ_RX_EOP side db0 +start: + jmp pin start side db1 ; Wait fall edge + irq IRQ_RX_START [1] side db0 + +.wrap_target +pin_still_low: + irq DECODER_TRIGGER [1] side db0 ; Trigger NRZI decoder + +; Resync on rising edge +pin_low: + jmp pin pin_went_high side db1 +pin_went_low: + jmp pin pin_went_high side db1 + jmp pin pin_went_high side db1 + jmp pin pin_went_high side db1 + jmp pin pin_went_high side db1 + jmp pin pin_went_high side db1 +.wrap + +pin_still_high: + mov x, isr [2] side db1 + jmp x-- eop side db1 ; Jump to eop if jmp_pin and in_pin are high because both inputs are inverted +; Jump to here on rising edge +pin_went_high: + mov isr, null side db1 + in pins, 1 side db0 ; Capture the pin to check eop. + irq DECODER_TRIGGER side db0 ; Trigger NRZI decoder + jmp pin pin_still_high side db0 + jmp pin_went_low side db1 ; To adjust interval of decoder trigger, jump to pin_went_low (not pin_low) + +; USB NRZI data decoder +; 15 instruction +; FS(12M) LS(1.5M) +; Run at as fast as possible +; jmp_pin d+ d- +; both d+/d- pin should be input invert overrideed +; Fill OSR by 1 and set 0 to x before runnning this program +.program usb_nrzi_decoder +start: + ; set x, 0 +.wrap_target +set_y: + set y, BIT_REPEAT_COUNT +irq_wait: + wait 1 irq DECODER_TRIGGER ; wait signal from edge detector + jmp PIN pin_high +pin_low: + jmp !y flip ; ignore stuff bit, without error check + jmp !x K1 +K2: + ; x==1 + in null, 1 + jmp flip +K1: + ; x==0 + in osr, 1 + jmp y-- irq_wait + +pin_high: + jmp !y flip ; ignore stuff bit, without error check + jmp !x J1 +J2: + ; x==1 + in x, 1 + jmp y-- irq_wait +J1: + ; x==0 + in null, 1 +flip: + mov x, ~x +.wrap +.program usb_nrzi_decoder_debug +.side_set 1 opt +start: + ; set x, 0 side db0 +.wrap_target +set_y: + set y, BIT_REPEAT_COUNT +irq_wait: + wait 1 irq DECODER_TRIGGER ; wait signal from edge detector + jmp PIN pin_high +pin_low: + jmp !y flip side db0 ; ignore stuff bit, without error check + jmp !x K1 side db0 +K2: + ; x==1 + in null, 1 + jmp flip +K1: + ; x==0 + in osr, 1 + jmp y-- irq_wait + +pin_high: + jmp !y flip side db1 ; ignore stuff bit, without error check + jmp !x J1 side db1 +J2: + ; x==1 + in x, 1 + jmp y-- irq_wait +J1: + ; x==0 + in null, 1 +flip: + mov x, ~x +.wrap + +% c-sdk { +#include "hardware/clocks.h" + +static __always_inline void pio_sm_set_jmp_pin(PIO pio, uint sm, uint jmp_pin) { + pio->sm[sm].execctrl = + (pio->sm[sm].execctrl & ~PIO_SM0_EXECCTRL_JMP_PIN_BITS) | + (jmp_pin << PIO_SM0_EXECCTRL_JMP_PIN_LSB); +} + +static inline void usb_rx_fs_program_init(PIO pio, uint sm, uint offset, uint pin_dp, int pin_debug) { + pio_sm_set_consecutive_pindirs(pio, sm, pin_dp, 2, false); + gpio_pull_down(pin_dp); + gpio_pull_down(pin_dp + 1); // dm + gpio_set_inover(pin_dp, GPIO_OVERRIDE_INVERT); + gpio_set_inover(pin_dp + 1, GPIO_OVERRIDE_INVERT); + + pio_sm_config c; + + if (pin_debug < 0) { + c = usb_nrzi_decoder_program_get_default_config(offset); + } else { + c = usb_nrzi_decoder_debug_program_get_default_config(offset); + + pio_sm_set_pins_with_mask(pio, sm, 0, 1 << pin_debug); + pio_sm_set_pindirs_with_mask(pio, sm, 1 << pin_debug, 1 << pin_debug); + pio_gpio_init(pio, pin_debug); + sm_config_set_sideset_pins(&c, pin_debug); + } + + sm_config_set_in_pins(&c, pin_dp); // for WAIT, IN + sm_config_set_jmp_pin(&c, pin_dp); // for JMP + + // Shift to right, autopull enabled, 8bit + sm_config_set_in_shift(&c, true, true, 8); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); + + pio_sm_init(pio, sm, offset, &c); + pio_sm_exec(pio, sm, pio_encode_mov_not(pio_osr, pio_null)); + pio_sm_set_enabled(pio, sm, false); +} + +static inline void eop_detect_fs_program_init(PIO pio, uint sm, uint offset, + uint pin_dp, bool is_fs, int pin_debug) { + pio_sm_config c; + + if (pin_debug < 0) { + c = usb_edge_detector_program_get_default_config(offset); + } else { + c = usb_edge_detector_debug_program_get_default_config(offset); + pio_sm_set_pins_with_mask(pio, sm, 0, 1 << pin_debug); + pio_sm_set_pindirs_with_mask(pio, sm, 1 << pin_debug, 1 << pin_debug); + pio_gpio_init(pio, pin_debug); + sm_config_set_sideset_pins(&c, pin_debug); + } + + sm_config_set_in_pins(&c, pin_dp); // for WAIT, IN + sm_config_set_jmp_pin(&c, pin_dp + 1); // for JMP + + sm_config_set_in_shift(&c, false, false, 8); + + float div; + if (is_fs) { + div = (float)clock_get_hz(clk_sys) / (96000000); + } else { + div = (float)clock_get_hz(clk_sys) / (12000000); + } + + sm_config_set_clkdiv(&c, div); + + pio_sm_init(pio, sm, offset + 1, &c); + pio_sm_set_enabled(pio, sm, true); +} + +%} \ No newline at end of file diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_rx.pio.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_rx.pio.h new file mode 100644 index 0000000000..bdd4c122d2 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_rx.pio.h @@ -0,0 +1,246 @@ +// Copyright 2021 sekigon-gonnoc +// SPDX-License-Identifier: MIT + +// -------------------------------------------------- // +// This file is autogenerated by pioasm; do not edit! // +// -------------------------------------------------- // + +#pragma once + +#if !PICO_NO_HARDWARE +#include "hardware/pio.h" +#endif + +#define IRQ_RX_BS_ERR 1 +#define IRQ_RX_EOP 2 +#define IRQ_RX_START 3 +#define DECODER_TRIGGER 4 + +// ----------------- // +// usb_edge_detector // +// ----------------- // + +#define usb_edge_detector_wrap_target 3 +#define usb_edge_detector_wrap 9 + +static const uint16_t usb_edge_detector_program_instructions[] = { + 0xc022, // 0: irq wait 2 + 0x00c1, // 1: jmp pin, 1 + 0xc103, // 2: irq nowait 3 [1] + // .wrap_target + 0xc104, // 3: irq nowait 4 [1] + 0x00cc, // 4: jmp pin, 12 + 0x00cc, // 5: jmp pin, 12 + 0x00cc, // 6: jmp pin, 12 + 0x00cc, // 7: jmp pin, 12 + 0x00cc, // 8: jmp pin, 12 + 0x00cc, // 9: jmp pin, 12 + // .wrap + 0xa226, // 10: mov x, isr [2] + 0x0040, // 11: jmp x--, 0 + 0xa0c3, // 12: mov isr, null + 0x4001, // 13: in pins, 1 + 0xc004, // 14: irq nowait 4 + 0x00ca, // 15: jmp pin, 10 + 0x0005, // 16: jmp 5 +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program usb_edge_detector_program = { + .instructions = usb_edge_detector_program_instructions, + .length = 17, + .origin = -1, +}; + +static inline pio_sm_config usb_edge_detector_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + usb_edge_detector_wrap_target, offset + usb_edge_detector_wrap); + return c; +} +#endif + +// ----------------------- // +// usb_edge_detector_debug // +// ----------------------- // + +#define usb_edge_detector_debug_wrap_target 3 +#define usb_edge_detector_debug_wrap 9 + +static const uint16_t usb_edge_detector_debug_program_instructions[] = { + 0xc022, // 0: irq wait 2 side 0 + 0x10c1, // 1: jmp pin, 1 side 1 + 0xc103, // 2: irq nowait 3 side 0 [1] + // .wrap_target + 0xc104, // 3: irq nowait 4 side 0 [1] + 0x10cc, // 4: jmp pin, 12 side 1 + 0x10cc, // 5: jmp pin, 12 side 1 + 0x10cc, // 6: jmp pin, 12 side 1 + 0x10cc, // 7: jmp pin, 12 side 1 + 0x10cc, // 8: jmp pin, 12 side 1 + 0x10cc, // 9: jmp pin, 12 side 1 + // .wrap + 0xb226, // 10: mov x, isr side 1 [2] + 0x1040, // 11: jmp x--, 0 side 1 + 0xb0c3, // 12: mov isr, null side 1 + 0x4001, // 13: in pins, 1 side 0 + 0xc004, // 14: irq nowait 4 side 0 + 0x00ca, // 15: jmp pin, 10 side 0 + 0x1005, // 16: jmp 5 side 1 +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program usb_edge_detector_debug_program = { + .instructions = usb_edge_detector_debug_program_instructions, + .length = 17, + .origin = -1, +}; + +static inline pio_sm_config usb_edge_detector_debug_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + usb_edge_detector_debug_wrap_target, offset + usb_edge_detector_debug_wrap); + sm_config_set_sideset(&c, 1, false, false); + return c; +} +#endif + +// ---------------- // +// usb_nrzi_decoder // +// ---------------- // + +#define usb_nrzi_decoder_wrap_target 0 +#define usb_nrzi_decoder_wrap 14 + +static const uint16_t usb_nrzi_decoder_program_instructions[] = { + // .wrap_target + 0xe046, // 0: set y, 6 + 0x20c4, // 1: wait 1 irq, 4 + 0x00c9, // 2: jmp pin, 9 + 0x006e, // 3: jmp !y, 14 + 0x0027, // 4: jmp !x, 7 + 0x4061, // 5: in null, 1 + 0x000e, // 6: jmp 14 + 0x40e1, // 7: in osr, 1 + 0x0081, // 8: jmp y--, 1 + 0x006e, // 9: jmp !y, 14 + 0x002d, // 10: jmp !x, 13 + 0x4021, // 11: in x, 1 + 0x0081, // 12: jmp y--, 1 + 0x4061, // 13: in null, 1 + 0xa029, // 14: mov x, !x + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program usb_nrzi_decoder_program = { + .instructions = usb_nrzi_decoder_program_instructions, + .length = 15, + .origin = -1, +}; + +static inline pio_sm_config usb_nrzi_decoder_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + usb_nrzi_decoder_wrap_target, offset + usb_nrzi_decoder_wrap); + return c; +} +#endif + +// ---------------------- // +// usb_nrzi_decoder_debug // +// ---------------------- // + +#define usb_nrzi_decoder_debug_wrap_target 0 +#define usb_nrzi_decoder_debug_wrap 14 + +static const uint16_t usb_nrzi_decoder_debug_program_instructions[] = { + // .wrap_target + 0xe046, // 0: set y, 6 + 0x20c4, // 1: wait 1 irq, 4 + 0x00c9, // 2: jmp pin, 9 + 0x106e, // 3: jmp !y, 14 side 0 + 0x1027, // 4: jmp !x, 7 side 0 + 0x4061, // 5: in null, 1 + 0x000e, // 6: jmp 14 + 0x40e1, // 7: in osr, 1 + 0x0081, // 8: jmp y--, 1 + 0x186e, // 9: jmp !y, 14 side 1 + 0x182d, // 10: jmp !x, 13 side 1 + 0x4021, // 11: in x, 1 + 0x0081, // 12: jmp y--, 1 + 0x4061, // 13: in null, 1 + 0xa029, // 14: mov x, !x + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program usb_nrzi_decoder_debug_program = { + .instructions = usb_nrzi_decoder_debug_program_instructions, + .length = 15, + .origin = -1, +}; + +static inline pio_sm_config usb_nrzi_decoder_debug_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + usb_nrzi_decoder_debug_wrap_target, offset + usb_nrzi_decoder_debug_wrap); + sm_config_set_sideset(&c, 2, true, false); + return c; +} + +#include "hardware/clocks.h" +static __always_inline void pio_sm_set_jmp_pin(PIO pio, uint sm, uint jmp_pin) { + pio->sm[sm].execctrl = + (pio->sm[sm].execctrl & ~PIO_SM0_EXECCTRL_JMP_PIN_BITS) | + (jmp_pin << PIO_SM0_EXECCTRL_JMP_PIN_LSB); +} +static inline void usb_rx_fs_program_init(PIO pio, uint sm, uint offset, uint pin_dp, int pin_debug) { + pio_sm_set_consecutive_pindirs(pio, sm, pin_dp, 2, false); + gpio_pull_down(pin_dp); + gpio_pull_down(pin_dp + 1); // dm + gpio_set_inover(pin_dp, GPIO_OVERRIDE_INVERT); + gpio_set_inover(pin_dp + 1, GPIO_OVERRIDE_INVERT); + pio_sm_config c; + if (pin_debug < 0) { + c = usb_nrzi_decoder_program_get_default_config(offset); + } else { + c = usb_nrzi_decoder_debug_program_get_default_config(offset); + pio_sm_set_pins_with_mask(pio, sm, 0, 1 << pin_debug); + pio_sm_set_pindirs_with_mask(pio, sm, 1 << pin_debug, 1 << pin_debug); + pio_gpio_init(pio, pin_debug); + sm_config_set_sideset_pins(&c, pin_debug); + } + sm_config_set_in_pins(&c, pin_dp); // for WAIT, IN + sm_config_set_jmp_pin(&c, pin_dp); // for JMP + // Shift to right, autopull enabled, 8bit + sm_config_set_in_shift(&c, true, true, 8); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); + pio_sm_init(pio, sm, offset, &c); + pio_sm_exec(pio, sm, pio_encode_mov_not(pio_osr, pio_null)); + pio_sm_set_enabled(pio, sm, false); +} +static inline void eop_detect_fs_program_init(PIO pio, uint sm, uint offset, + uint pin_dp, bool is_fs, int pin_debug) { + pio_sm_config c; + if (pin_debug < 0) { + c = usb_edge_detector_program_get_default_config(offset); + } else { + c = usb_edge_detector_debug_program_get_default_config(offset); + pio_sm_set_pins_with_mask(pio, sm, 0, 1 << pin_debug); + pio_sm_set_pindirs_with_mask(pio, sm, 1 << pin_debug, 1 << pin_debug); + pio_gpio_init(pio, pin_debug); + sm_config_set_sideset_pins(&c, pin_debug); + } + sm_config_set_in_pins(&c, pin_dp); // for WAIT, IN + sm_config_set_jmp_pin(&c, pin_dp + 1); // for JMP + sm_config_set_in_shift(&c, false, false, 8); + float div; + if (is_fs) { + div = (float)clock_get_hz(clk_sys) / (96000000); + } else { + div = (float)clock_get_hz(clk_sys) / (12000000); + } + sm_config_set_clkdiv(&c, div); + pio_sm_init(pio, sm, offset + 1, &c); + pio_sm_set_enabled(pio, sm, true); +} + +#endif + diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_tx.pio b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_tx.pio new file mode 100644 index 0000000000..90672c3dfb --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_tx.pio @@ -0,0 +1,217 @@ +; Copyright (c) 2021 sekigon-gonnoc + +.define public USB_TX_EOP_OFFSET 4 +.define public USB_TX_EOP_DISABLER_LEN 4 + +; USB FS NRZI transmitter +; Run 48 MHz, autopull +.program usb_tx_fs +.side_set 2 opt + +.define J 0b01 +.define K 0b10 +.define SE0 0b00 +.define BR 5 ; bit repeat limit +.define public IRQ_COMP 0 ; complete flag bit +.define public IRQ_EOP 1 ; EOP start flag bit + +start: + set y, BR side J + set pindirs, 0b11 + +.wrap_target +check_eop1: + jmp !osre load_bit1 + nop [1] +send_eop: + irq IRQ_EOP side SE0 [3] ; To catch quik ACK, mark as complete here + nop [3] + nop side J + set pindirs, 0b00 [3] + irq wait IRQ_COMP + jmp start +load_bit1: + out x, 1 + jmp !x low1 +high1: + jmp y-- check_eop1 side J + nop [2] ; bit stuffing +low1: + set y BR side K + +check_eop2: + jmp !osre load_bit2 + jmp send_eop [1] +load_bit2: + out x, 1 + jmp !x low2 +high2: + jmp y-- check_eop2 side K + nop [2] ; bit stuffing +low2: + set y BR side J +.wrap + +; USB FS transmitter for PRE packet (No EOP) +; Run 48 MHz, autopull +.program usb_tx_fs_pre +.side_set 2 opt + +.define J 0b01 +.define K 0b10 +.define SE0 0b00 +.define BR 5 ; bit repeat limit +.define public IRQ_COMP 0 ; complete flag bit +.define public IRQ_EOP 1 ; EOP start flag bit + +start: + set y, BR side J + set pindirs, 0b11 + +.wrap_target +check_eop1: + jmp !osre load_bit1 + nop [1] +send_eop: + irq IRQ_EOP side J [3] + set pindirs, 0b00 + nop ; to align program size + nop ; to align program size + irq wait IRQ_COMP + jmp start +load_bit1: + out x, 1 + jmp !x low1 +high1: + jmp y-- check_eop1 side J + nop [2] ; bit stuffing +low1: + set y BR side K + +check_eop2: + jmp !osre load_bit2 + jmp send_eop [1] +load_bit2: + out x, 1 + jmp !x low2 +high2: + jmp y-- check_eop2 side K + nop [2] ; bit stuffing +low2: + set y BR side J +.wrap + + +; USB LS Transmitter +; Run 6MHz, autopull +.program usb_tx_ls +.side_set 2 opt + +.define J 0b10 +.define K 0b01 +.define SE0 0b00 +.define BR 5 ; bit repeat limit +.define public IRQ_COMP 0 ; complete flag bit +.define public IRQ_EOP 1 ; EOP start flag bit + +start: + set y, BR side J + set pindirs, 0b11 + +.wrap_target +check_eop1: + jmp !osre load_bit1 + nop [1] +send_eop: + irq IRQ_EOP side SE0 [3] + nop [3] + nop side J + set pindirs, 0b00 [3] + irq wait IRQ_COMP + jmp start +load_bit1: + out x, 1 + jmp !x low1 +high1: + jmp y-- check_eop1 side J + nop [2] ; bit stuffing +low1: + set y BR side K + +check_eop2: + jmp !osre load_bit2 + jmp send_eop [1] +load_bit2: + out x, 1 + jmp !x low2 +high2: + jmp y-- check_eop2 side K + nop [2] ; bit stuffing +low2: + set y BR side J +.wrap + + + +% c-sdk { +#include "hardware/clocks.h" + + static void __no_inline_not_in_flash_func(usb_tx_configure_pins)(PIO pio, uint sm, uint pin_dp) { + pio_sm_set_out_pins(pio, sm, pin_dp, 2); + pio_sm_set_set_pins(pio, sm, pin_dp, 2); + pio_sm_set_sideset_pins(pio, sm, pin_dp); + } + + static inline void usb_tx_fs_program_init(PIO pio, uint sm, uint offset, + uint pin_dp) { + pio_sm_set_pins_with_mask(pio, sm, (0b01 << pin_dp), (0b11 << pin_dp)); + + gpio_pull_down(pin_dp); + gpio_pull_down(pin_dp + 1); // dm + pio_gpio_init(pio, pin_dp); + pio_gpio_init(pio, pin_dp + 1); // dm + + pio_sm_config c = usb_tx_fs_program_get_default_config(offset); + + // shifts to left, autopull, 8bit + sm_config_set_out_shift(&c, true, true, 8); + + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + + // run at 48MHz + // clk_sys should be multiply of 12MHz + float div = (float)clock_get_hz(clk_sys) / (48000000UL); + sm_config_set_clkdiv(&c, div); + + pio_sm_init(pio, sm, offset, &c); + usb_tx_configure_pins(pio, sm, pin_dp); + pio_sm_set_enabled(pio, sm, true); + } + + static inline void usb_tx_ls_program_init(PIO pio, uint sm, uint offset, + uint pin_dp) { + pio_sm_set_pins_with_mask(pio, sm, (0b10 << pin_dp), (0b11 << pin_dp)); + + gpio_pull_down(pin_dp); + gpio_pull_down(pin_dp + 1); // dm + pio_gpio_init(pio, pin_dp); + pio_gpio_init(pio, pin_dp + 1); // dm + + pio_sm_config c = usb_tx_ls_program_get_default_config(offset); + + // shifts to left, autopull, 8bit + sm_config_set_out_shift(&c, true, true, 8); + + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + + // run at 6MHz + // clk_sys should be multiply of 12MHz + float div = (float)clock_get_hz(clk_sys) / (6000000UL); + sm_config_set_clkdiv(&c, div); + + pio_sm_init(pio, sm, offset, &c); + usb_tx_configure_pins(pio, sm, pin_dp); + pio_sm_set_enabled(pio, sm, true); + } + +%} diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_tx.pio.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_tx.pio.h new file mode 100644 index 0000000000..8d7140635b --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src/usb_tx.pio.h @@ -0,0 +1,218 @@ +// Copyright 2021 sekigon-gonnoc +// SPDX-License-Identifier: MIT + +// -------------------------------------------------- // +// This file is autogenerated by pioasm; do not edit! // +// -------------------------------------------------- // + +#pragma once + +#if !PICO_NO_HARDWARE +#include "hardware/pio.h" +#endif + +#define USB_TX_EOP_OFFSET 4 +#define USB_TX_EOP_DISABLER_LEN 4 + +// --------- // +// usb_tx_fs // +// --------- // + +#define usb_tx_fs_wrap_target 2 +#define usb_tx_fs_wrap 21 + +#define usb_tx_fs_IRQ_COMP 0 +#define usb_tx_fs_IRQ_EOP 1 + +static const uint16_t usb_tx_fs_program_instructions[] = { + 0xf445, // 0: set y, 5 side 1 + 0xe083, // 1: set pindirs, 3 + // .wrap_target + 0x00ea, // 2: jmp !osre, 10 + 0xa142, // 3: nop [1] + 0xd301, // 4: irq nowait 1 side 0 [3] + 0xa342, // 5: nop [3] + 0xb442, // 6: nop side 1 + 0xe380, // 7: set pindirs, 0 [3] + 0xc020, // 8: irq wait 0 + 0x0000, // 9: jmp 0 + 0x6021, // 10: out x, 1 + 0x002e, // 11: jmp !x, 14 + 0x1482, // 12: jmp y--, 2 side 1 + 0xa242, // 13: nop [2] + 0xf845, // 14: set y, 5 side 2 + 0x00f1, // 15: jmp !osre, 17 + 0x0104, // 16: jmp 4 [1] + 0x6021, // 17: out x, 1 + 0x0035, // 18: jmp !x, 21 + 0x188f, // 19: jmp y--, 15 side 2 + 0xa242, // 20: nop [2] + 0xf445, // 21: set y, 5 side 1 + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program usb_tx_fs_program = { + .instructions = usb_tx_fs_program_instructions, + .length = 22, + .origin = -1, +}; + +static inline pio_sm_config usb_tx_fs_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + usb_tx_fs_wrap_target, offset + usb_tx_fs_wrap); + sm_config_set_sideset(&c, 3, true, false); + return c; +} +#endif + +// ------------- // +// usb_tx_fs_pre // +// ------------- // + +#define usb_tx_fs_pre_wrap_target 2 +#define usb_tx_fs_pre_wrap 21 + +#define usb_tx_fs_pre_IRQ_COMP 0 +#define usb_tx_fs_pre_IRQ_EOP 1 + +static const uint16_t usb_tx_fs_pre_program_instructions[] = { + 0xf445, // 0: set y, 5 side 1 + 0xe083, // 1: set pindirs, 3 + // .wrap_target + 0x00ea, // 2: jmp !osre, 10 + 0xa142, // 3: nop [1] + 0xd701, // 4: irq nowait 1 side 1 [3] + 0xe080, // 5: set pindirs, 0 + 0xa042, // 6: nop + 0xa042, // 7: nop + 0xc020, // 8: irq wait 0 + 0x0000, // 9: jmp 0 + 0x6021, // 10: out x, 1 + 0x002e, // 11: jmp !x, 14 + 0x1482, // 12: jmp y--, 2 side 1 + 0xa242, // 13: nop [2] + 0xf845, // 14: set y, 5 side 2 + 0x00f1, // 15: jmp !osre, 17 + 0x0104, // 16: jmp 4 [1] + 0x6021, // 17: out x, 1 + 0x0035, // 18: jmp !x, 21 + 0x188f, // 19: jmp y--, 15 side 2 + 0xa242, // 20: nop [2] + 0xf445, // 21: set y, 5 side 1 + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program usb_tx_fs_pre_program = { + .instructions = usb_tx_fs_pre_program_instructions, + .length = 22, + .origin = -1, +}; + +static inline pio_sm_config usb_tx_fs_pre_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + usb_tx_fs_pre_wrap_target, offset + usb_tx_fs_pre_wrap); + sm_config_set_sideset(&c, 3, true, false); + return c; +} +#endif + +// --------- // +// usb_tx_ls // +// --------- // + +#define usb_tx_ls_wrap_target 2 +#define usb_tx_ls_wrap 21 + +#define usb_tx_ls_IRQ_COMP 0 +#define usb_tx_ls_IRQ_EOP 1 + +static const uint16_t usb_tx_ls_program_instructions[] = { + 0xf845, // 0: set y, 5 side 2 + 0xe083, // 1: set pindirs, 3 + // .wrap_target + 0x00ea, // 2: jmp !osre, 10 + 0xa142, // 3: nop [1] + 0xd301, // 4: irq nowait 1 side 0 [3] + 0xa342, // 5: nop [3] + 0xb842, // 6: nop side 2 + 0xe380, // 7: set pindirs, 0 [3] + 0xc020, // 8: irq wait 0 + 0x0000, // 9: jmp 0 + 0x6021, // 10: out x, 1 + 0x002e, // 11: jmp !x, 14 + 0x1882, // 12: jmp y--, 2 side 2 + 0xa242, // 13: nop [2] + 0xf445, // 14: set y, 5 side 1 + 0x00f1, // 15: jmp !osre, 17 + 0x0104, // 16: jmp 4 [1] + 0x6021, // 17: out x, 1 + 0x0035, // 18: jmp !x, 21 + 0x148f, // 19: jmp y--, 15 side 1 + 0xa242, // 20: nop [2] + 0xf845, // 21: set y, 5 side 2 + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program usb_tx_ls_program = { + .instructions = usb_tx_ls_program_instructions, + .length = 22, + .origin = -1, +}; + +static inline pio_sm_config usb_tx_ls_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + usb_tx_ls_wrap_target, offset + usb_tx_ls_wrap); + sm_config_set_sideset(&c, 3, true, false); + return c; +} + +#include "hardware/clocks.h" + static void __no_inline_not_in_flash_func(usb_tx_configure_pins)(PIO pio, uint sm, uint pin_dp) { + pio_sm_set_out_pins(pio, sm, pin_dp, 2); + pio_sm_set_set_pins(pio, sm, pin_dp, 2); + pio_sm_set_sideset_pins(pio, sm, pin_dp); + } + static inline void usb_tx_fs_program_init(PIO pio, uint sm, uint offset, + uint pin_dp) { + pio_sm_set_pins_with_mask(pio, sm, (0b01 << pin_dp), (0b11 << pin_dp)); + gpio_pull_down(pin_dp); + gpio_pull_down(pin_dp + 1); // dm + pio_gpio_init(pio, pin_dp); + pio_gpio_init(pio, pin_dp + 1); // dm + pio_sm_config c = usb_tx_fs_program_get_default_config(offset); + // shifts to left, autopull, 8bit + sm_config_set_out_shift(&c, true, true, 8); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + // run at 48MHz + // clk_sys should be multiply of 12MHz + float div = (float)clock_get_hz(clk_sys) / (48000000UL); + sm_config_set_clkdiv(&c, div); + pio_sm_init(pio, sm, offset, &c); + usb_tx_configure_pins(pio, sm, pin_dp); + pio_sm_set_enabled(pio, sm, true); + } + static inline void usb_tx_ls_program_init(PIO pio, uint sm, uint offset, + uint pin_dp) { + pio_sm_set_pins_with_mask(pio, sm, (0b10 << pin_dp), (0b11 << pin_dp)); + gpio_pull_down(pin_dp); + gpio_pull_down(pin_dp + 1); // dm + pio_gpio_init(pio, pin_dp); + pio_gpio_init(pio, pin_dp + 1); // dm + pio_sm_config c = usb_tx_ls_program_get_default_config(offset); + // shifts to left, autopull, 8bit + sm_config_set_out_shift(&c, true, true, 8); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + // run at 6MHz + // clk_sys should be multiply of 12MHz + float div = (float)clock_get_hz(clk_sys) / (6000000UL); + sm_config_set_clkdiv(&c, div); + pio_sm_init(pio, sm, offset, &c); + usb_tx_configure_pins(pio, sm, pin_dp); + pio_sm_set_enabled(pio, sm, true); + } + +#endif + diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/LICENSE b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/LICENSE new file mode 100644 index 0000000000..ddd4ab410c --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018, hathach (tinyusb.org) + +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. diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/class/hid/hid.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/class/hid/hid.h new file mode 100644 index 0000000000..d9b0ead105 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/class/hid/hid.h @@ -0,0 +1,1131 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +/** \ingroup group_class + * \defgroup ClassDriver_HID Human Interface Device (HID) + * @{ */ + +#ifndef _TUSB_HID_H_ +#define _TUSB_HID_H_ + +#include "common/tusb_common.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Common Definitions +//--------------------------------------------------------------------+ +/** \defgroup ClassDriver_HID_Common Common Definitions + * @{ */ + +/// USB HID Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength; /**< Numeric expression that is the total size of the HID descriptor */ + uint8_t bDescriptorType; /**< Constant name specifying type of HID descriptor. */ + + uint16_t bcdHID; /**< Numeric expression identifying the HID Class Specification release */ + uint8_t bCountryCode; /**< Numeric expression identifying country code of the localized hardware. */ + uint8_t bNumDescriptors; /**< Numeric expression specifying the number of class descriptors */ + + uint8_t bReportType; /**< Type of HID class report. */ + uint16_t wReportLength; /**< the total size of the Report descriptor. */ +} tusb_hid_descriptor_hid_t; + +/// HID Subclass +typedef enum +{ + HID_SUBCLASS_NONE = 0, ///< No Subclass + HID_SUBCLASS_BOOT = 1 ///< Boot Interface Subclass +}hid_subclass_enum_t; + +/// HID Interface Protocol +typedef enum +{ + HID_ITF_PROTOCOL_NONE = 0, ///< None + HID_ITF_PROTOCOL_KEYBOARD = 1, ///< Keyboard + HID_ITF_PROTOCOL_MOUSE = 2 ///< Mouse +}hid_interface_protocol_enum_t; + +/// HID Descriptor Type +typedef enum +{ + HID_DESC_TYPE_HID = 0x21, ///< HID Descriptor + HID_DESC_TYPE_REPORT = 0x22, ///< Report Descriptor + HID_DESC_TYPE_PHYSICAL = 0x23 ///< Physical Descriptor +}hid_descriptor_enum_t; + +/// HID Request Report Type +typedef enum +{ + HID_REPORT_TYPE_INVALID = 0, + HID_REPORT_TYPE_INPUT, ///< Input + HID_REPORT_TYPE_OUTPUT, ///< Output + HID_REPORT_TYPE_FEATURE ///< Feature +}hid_report_type_t; + +/// HID Class Specific Control Request +typedef enum +{ + HID_REQ_CONTROL_GET_REPORT = 0x01, ///< Get Report + HID_REQ_CONTROL_GET_IDLE = 0x02, ///< Get Idle + HID_REQ_CONTROL_GET_PROTOCOL = 0x03, ///< Get Protocol + HID_REQ_CONTROL_SET_REPORT = 0x09, ///< Set Report + HID_REQ_CONTROL_SET_IDLE = 0x0a, ///< Set Idle + HID_REQ_CONTROL_SET_PROTOCOL = 0x0b ///< Set Protocol +}hid_request_enum_t; + +/// HID Local Code +typedef enum +{ + HID_LOCAL_NotSupported = 0 , ///< NotSupported + HID_LOCAL_Arabic , ///< Arabic + HID_LOCAL_Belgian , ///< Belgian + HID_LOCAL_Canadian_Bilingual , ///< Canadian_Bilingual + HID_LOCAL_Canadian_French , ///< Canadian_French + HID_LOCAL_Czech_Republic , ///< Czech_Republic + HID_LOCAL_Danish , ///< Danish + HID_LOCAL_Finnish , ///< Finnish + HID_LOCAL_French , ///< French + HID_LOCAL_German , ///< German + HID_LOCAL_Greek , ///< Greek + HID_LOCAL_Hebrew , ///< Hebrew + HID_LOCAL_Hungary , ///< Hungary + HID_LOCAL_International , ///< International + HID_LOCAL_Italian , ///< Italian + HID_LOCAL_Japan_Katakana , ///< Japan_Katakana + HID_LOCAL_Korean , ///< Korean + HID_LOCAL_Latin_American , ///< Latin_American + HID_LOCAL_Netherlands_Dutch , ///< Netherlands/Dutch + HID_LOCAL_Norwegian , ///< Norwegian + HID_LOCAL_Persian_Farsi , ///< Persian (Farsi) + HID_LOCAL_Poland , ///< Poland + HID_LOCAL_Portuguese , ///< Portuguese + HID_LOCAL_Russia , ///< Russia + HID_LOCAL_Slovakia , ///< Slovakia + HID_LOCAL_Spanish , ///< Spanish + HID_LOCAL_Swedish , ///< Swedish + HID_LOCAL_Swiss_French , ///< Swiss/French + HID_LOCAL_Swiss_German , ///< Swiss/German + HID_LOCAL_Switzerland , ///< Switzerland + HID_LOCAL_Taiwan , ///< Taiwan + HID_LOCAL_Turkish_Q , ///< Turkish-Q + HID_LOCAL_UK , ///< UK + HID_LOCAL_US , ///< US + HID_LOCAL_Yugoslavia , ///< Yugoslavia + HID_LOCAL_Turkish_F ///< Turkish-F +} hid_local_enum_t; + +// HID protocol value used by GetProtocol / SetProtocol +typedef enum +{ + HID_PROTOCOL_BOOT = 0, + HID_PROTOCOL_REPORT = 1 +} hid_protocol_mode_enum_t; + +/** @} */ + +//--------------------------------------------------------------------+ +// GAMEPAD +//--------------------------------------------------------------------+ +/** \addtogroup ClassDriver_HID_Gamepad Gamepad + * @{ */ + +/* From https://www.kernel.org/doc/html/latest/input/gamepad.html + ____________________________ __ + / [__ZL__] [__ZR__] \ | + / [__ TL __] [__ TR __] \ | Front Triggers + __/________________________________\__ __| + / _ \ | + / /\ __ (N) \ | + / || __ |MO| __ _ _ \ | Main Pad + | <===DP===> |SE| |ST| (W) -|- (E) | | + \ || ___ ___ _ / | + /\ \/ / \ / \ (S) /\ __| + / \________ | LS | ____ | RS | ________/ \ | +| / \ \___/ / \ \___/ / \ | | Control Sticks +| / \_____/ \_____/ \ | __| +| / \ | + \_____/ \_____/ + + |________|______| |______|___________| + D-Pad Left Right Action Pad + Stick Stick + + |_____________| + Menu Pad + + Most gamepads have the following features: + - Action-Pad 4 buttons in diamonds-shape (on the right side) NORTH, SOUTH, WEST and EAST. + - D-Pad (Direction-pad) 4 buttons (on the left side) that point up, down, left and right. + - Menu-Pad Different constellations, but most-times 2 buttons: SELECT - START. + - Analog-Sticks provide freely moveable sticks to control directions, Analog-sticks may also + provide a digital button if you press them. + - Triggers are located on the upper-side of the pad in vertical direction. The upper buttons + are normally named Left- and Right-Triggers, the lower buttons Z-Left and Z-Right. + - Rumble Many devices provide force-feedback features. But are mostly just simple rumble motors. + */ + +/// HID Gamepad Protocol Report. +typedef struct TU_ATTR_PACKED +{ + int8_t x; ///< Delta x movement of left analog-stick + int8_t y; ///< Delta y movement of left analog-stick + int8_t z; ///< Delta z movement of right analog-joystick + int8_t rz; ///< Delta Rz movement of right analog-joystick + int8_t rx; ///< Delta Rx movement of analog left trigger + int8_t ry; ///< Delta Ry movement of analog right trigger + uint8_t hat; ///< Buttons mask for currently pressed buttons in the DPad/hat + uint32_t buttons; ///< Buttons mask for currently pressed buttons +}hid_gamepad_report_t; + +/// Standard Gamepad Buttons Bitmap +typedef enum +{ + GAMEPAD_BUTTON_0 = TU_BIT(0), + GAMEPAD_BUTTON_1 = TU_BIT(1), + GAMEPAD_BUTTON_2 = TU_BIT(2), + GAMEPAD_BUTTON_3 = TU_BIT(3), + GAMEPAD_BUTTON_4 = TU_BIT(4), + GAMEPAD_BUTTON_5 = TU_BIT(5), + GAMEPAD_BUTTON_6 = TU_BIT(6), + GAMEPAD_BUTTON_7 = TU_BIT(7), + GAMEPAD_BUTTON_8 = TU_BIT(8), + GAMEPAD_BUTTON_9 = TU_BIT(9), + GAMEPAD_BUTTON_10 = TU_BIT(10), + GAMEPAD_BUTTON_11 = TU_BIT(11), + GAMEPAD_BUTTON_12 = TU_BIT(12), + GAMEPAD_BUTTON_13 = TU_BIT(13), + GAMEPAD_BUTTON_14 = TU_BIT(14), + GAMEPAD_BUTTON_15 = TU_BIT(15), + GAMEPAD_BUTTON_16 = TU_BIT(16), + GAMEPAD_BUTTON_17 = TU_BIT(17), + GAMEPAD_BUTTON_18 = TU_BIT(18), + GAMEPAD_BUTTON_19 = TU_BIT(19), + GAMEPAD_BUTTON_20 = TU_BIT(20), + GAMEPAD_BUTTON_21 = TU_BIT(21), + GAMEPAD_BUTTON_22 = TU_BIT(22), + GAMEPAD_BUTTON_23 = TU_BIT(23), + GAMEPAD_BUTTON_24 = TU_BIT(24), + GAMEPAD_BUTTON_25 = TU_BIT(25), + GAMEPAD_BUTTON_26 = TU_BIT(26), + GAMEPAD_BUTTON_27 = TU_BIT(27), + GAMEPAD_BUTTON_28 = TU_BIT(28), + GAMEPAD_BUTTON_29 = TU_BIT(29), + GAMEPAD_BUTTON_30 = TU_BIT(30), + GAMEPAD_BUTTON_31 = TU_BIT(31), +}hid_gamepad_button_bm_t; + +/// Standard Gamepad Buttons Naming from Linux input event codes +/// https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h +#define GAMEPAD_BUTTON_A GAMEPAD_BUTTON_0 +#define GAMEPAD_BUTTON_SOUTH GAMEPAD_BUTTON_0 + +#define GAMEPAD_BUTTON_B GAMEPAD_BUTTON_1 +#define GAMEPAD_BUTTON_EAST GAMEPAD_BUTTON_1 + +#define GAMEPAD_BUTTON_C GAMEPAD_BUTTON_2 + +#define GAMEPAD_BUTTON_X GAMEPAD_BUTTON_3 +#define GAMEPAD_BUTTON_NORTH GAMEPAD_BUTTON_3 + +#define GAMEPAD_BUTTON_Y GAMEPAD_BUTTON_4 +#define GAMEPAD_BUTTON_WEST GAMEPAD_BUTTON_4 + +#define GAMEPAD_BUTTON_Z GAMEPAD_BUTTON_5 +#define GAMEPAD_BUTTON_TL GAMEPAD_BUTTON_6 +#define GAMEPAD_BUTTON_TR GAMEPAD_BUTTON_7 +#define GAMEPAD_BUTTON_TL2 GAMEPAD_BUTTON_8 +#define GAMEPAD_BUTTON_TR2 GAMEPAD_BUTTON_9 +#define GAMEPAD_BUTTON_SELECT GAMEPAD_BUTTON_10 +#define GAMEPAD_BUTTON_START GAMEPAD_BUTTON_11 +#define GAMEPAD_BUTTON_MODE GAMEPAD_BUTTON_12 +#define GAMEPAD_BUTTON_THUMBL GAMEPAD_BUTTON_13 +#define GAMEPAD_BUTTON_THUMBR GAMEPAD_BUTTON_14 + +/// Standard Gamepad HAT/DPAD Buttons (from Linux input event codes) +typedef enum +{ + GAMEPAD_HAT_CENTERED = 0, ///< DPAD_CENTERED + GAMEPAD_HAT_UP = 1, ///< DPAD_UP + GAMEPAD_HAT_UP_RIGHT = 2, ///< DPAD_UP_RIGHT + GAMEPAD_HAT_RIGHT = 3, ///< DPAD_RIGHT + GAMEPAD_HAT_DOWN_RIGHT = 4, ///< DPAD_DOWN_RIGHT + GAMEPAD_HAT_DOWN = 5, ///< DPAD_DOWN + GAMEPAD_HAT_DOWN_LEFT = 6, ///< DPAD_DOWN_LEFT + GAMEPAD_HAT_LEFT = 7, ///< DPAD_LEFT + GAMEPAD_HAT_UP_LEFT = 8, ///< DPAD_UP_LEFT +}hid_gamepad_hat_t; + +/// @} + +//--------------------------------------------------------------------+ +// MOUSE +//--------------------------------------------------------------------+ +/** \addtogroup ClassDriver_HID_Mouse Mouse + * @{ */ + +/// Standard HID Boot Protocol Mouse Report. +typedef struct TU_ATTR_PACKED +{ + uint8_t buttons; /**< buttons mask for currently pressed buttons in the mouse. */ + int8_t x; /**< Current delta x movement of the mouse. */ + int8_t y; /**< Current delta y movement on the mouse. */ + int8_t wheel; /**< Current delta wheel movement on the mouse. */ + int8_t pan; // using AC Pan +} hid_mouse_report_t; + +/// Standard Mouse Buttons Bitmap +typedef enum +{ + MOUSE_BUTTON_LEFT = TU_BIT(0), ///< Left button + MOUSE_BUTTON_RIGHT = TU_BIT(1), ///< Right button + MOUSE_BUTTON_MIDDLE = TU_BIT(2), ///< Middle button + MOUSE_BUTTON_BACKWARD = TU_BIT(3), ///< Backward button, + MOUSE_BUTTON_FORWARD = TU_BIT(4), ///< Forward button, +}hid_mouse_button_bm_t; + +/// @} + +//--------------------------------------------------------------------+ +// Keyboard +//--------------------------------------------------------------------+ +/** \addtogroup ClassDriver_HID_Keyboard Keyboard + * @{ */ + +/// Standard HID Boot Protocol Keyboard Report. +typedef struct TU_ATTR_PACKED +{ + uint8_t modifier; /**< Keyboard modifier (KEYBOARD_MODIFIER_* masks). */ + uint8_t reserved; /**< Reserved for OEM use, always set to 0. */ + uint8_t keycode[6]; /**< Key codes of the currently pressed keys. */ +} hid_keyboard_report_t; + +/// Keyboard modifier codes bitmap +typedef enum +{ + KEYBOARD_MODIFIER_LEFTCTRL = TU_BIT(0), ///< Left Control + KEYBOARD_MODIFIER_LEFTSHIFT = TU_BIT(1), ///< Left Shift + KEYBOARD_MODIFIER_LEFTALT = TU_BIT(2), ///< Left Alt + KEYBOARD_MODIFIER_LEFTGUI = TU_BIT(3), ///< Left Window + KEYBOARD_MODIFIER_RIGHTCTRL = TU_BIT(4), ///< Right Control + KEYBOARD_MODIFIER_RIGHTSHIFT = TU_BIT(5), ///< Right Shift + KEYBOARD_MODIFIER_RIGHTALT = TU_BIT(6), ///< Right Alt + KEYBOARD_MODIFIER_RIGHTGUI = TU_BIT(7) ///< Right Window +}hid_keyboard_modifier_bm_t; + +typedef enum +{ + KEYBOARD_LED_NUMLOCK = TU_BIT(0), ///< Num Lock LED + KEYBOARD_LED_CAPSLOCK = TU_BIT(1), ///< Caps Lock LED + KEYBOARD_LED_SCROLLLOCK = TU_BIT(2), ///< Scroll Lock LED + KEYBOARD_LED_COMPOSE = TU_BIT(3), ///< Composition Mode + KEYBOARD_LED_KANA = TU_BIT(4) ///< Kana mode +}hid_keyboard_led_bm_t; + +/// @} + +//--------------------------------------------------------------------+ +// HID KEYCODE +//--------------------------------------------------------------------+ +#define HID_KEY_NONE 0x00 +#define HID_KEY_A 0x04 +#define HID_KEY_B 0x05 +#define HID_KEY_C 0x06 +#define HID_KEY_D 0x07 +#define HID_KEY_E 0x08 +#define HID_KEY_F 0x09 +#define HID_KEY_G 0x0A +#define HID_KEY_H 0x0B +#define HID_KEY_I 0x0C +#define HID_KEY_J 0x0D +#define HID_KEY_K 0x0E +#define HID_KEY_L 0x0F +#define HID_KEY_M 0x10 +#define HID_KEY_N 0x11 +#define HID_KEY_O 0x12 +#define HID_KEY_P 0x13 +#define HID_KEY_Q 0x14 +#define HID_KEY_R 0x15 +#define HID_KEY_S 0x16 +#define HID_KEY_T 0x17 +#define HID_KEY_U 0x18 +#define HID_KEY_V 0x19 +#define HID_KEY_W 0x1A +#define HID_KEY_X 0x1B +#define HID_KEY_Y 0x1C +#define HID_KEY_Z 0x1D +#define HID_KEY_1 0x1E +#define HID_KEY_2 0x1F +#define HID_KEY_3 0x20 +#define HID_KEY_4 0x21 +#define HID_KEY_5 0x22 +#define HID_KEY_6 0x23 +#define HID_KEY_7 0x24 +#define HID_KEY_8 0x25 +#define HID_KEY_9 0x26 +#define HID_KEY_0 0x27 +#define HID_KEY_ENTER 0x28 +#define HID_KEY_ESCAPE 0x29 +#define HID_KEY_BACKSPACE 0x2A +#define HID_KEY_TAB 0x2B +#define HID_KEY_SPACE 0x2C +#define HID_KEY_MINUS 0x2D +#define HID_KEY_EQUAL 0x2E +#define HID_KEY_BRACKET_LEFT 0x2F +#define HID_KEY_BRACKET_RIGHT 0x30 +#define HID_KEY_BACKSLASH 0x31 +#define HID_KEY_EUROPE_1 0x32 +#define HID_KEY_SEMICOLON 0x33 +#define HID_KEY_APOSTROPHE 0x34 +#define HID_KEY_GRAVE 0x35 +#define HID_KEY_COMMA 0x36 +#define HID_KEY_PERIOD 0x37 +#define HID_KEY_SLASH 0x38 +#define HID_KEY_CAPS_LOCK 0x39 +#define HID_KEY_F1 0x3A +#define HID_KEY_F2 0x3B +#define HID_KEY_F3 0x3C +#define HID_KEY_F4 0x3D +#define HID_KEY_F5 0x3E +#define HID_KEY_F6 0x3F +#define HID_KEY_F7 0x40 +#define HID_KEY_F8 0x41 +#define HID_KEY_F9 0x42 +#define HID_KEY_F10 0x43 +#define HID_KEY_F11 0x44 +#define HID_KEY_F12 0x45 +#define HID_KEY_PRINT_SCREEN 0x46 +#define HID_KEY_SCROLL_LOCK 0x47 +#define HID_KEY_PAUSE 0x48 +#define HID_KEY_INSERT 0x49 +#define HID_KEY_HOME 0x4A +#define HID_KEY_PAGE_UP 0x4B +#define HID_KEY_DELETE 0x4C +#define HID_KEY_END 0x4D +#define HID_KEY_PAGE_DOWN 0x4E +#define HID_KEY_ARROW_RIGHT 0x4F +#define HID_KEY_ARROW_LEFT 0x50 +#define HID_KEY_ARROW_DOWN 0x51 +#define HID_KEY_ARROW_UP 0x52 +#define HID_KEY_NUM_LOCK 0x53 +#define HID_KEY_KEYPAD_DIVIDE 0x54 +#define HID_KEY_KEYPAD_MULTIPLY 0x55 +#define HID_KEY_KEYPAD_SUBTRACT 0x56 +#define HID_KEY_KEYPAD_ADD 0x57 +#define HID_KEY_KEYPAD_ENTER 0x58 +#define HID_KEY_KEYPAD_1 0x59 +#define HID_KEY_KEYPAD_2 0x5A +#define HID_KEY_KEYPAD_3 0x5B +#define HID_KEY_KEYPAD_4 0x5C +#define HID_KEY_KEYPAD_5 0x5D +#define HID_KEY_KEYPAD_6 0x5E +#define HID_KEY_KEYPAD_7 0x5F +#define HID_KEY_KEYPAD_8 0x60 +#define HID_KEY_KEYPAD_9 0x61 +#define HID_KEY_KEYPAD_0 0x62 +#define HID_KEY_KEYPAD_DECIMAL 0x63 +#define HID_KEY_EUROPE_2 0x64 +#define HID_KEY_APPLICATION 0x65 +#define HID_KEY_POWER 0x66 +#define HID_KEY_KEYPAD_EQUAL 0x67 +#define HID_KEY_F13 0x68 +#define HID_KEY_F14 0x69 +#define HID_KEY_F15 0x6A +#define HID_KEY_F16 0x6B +#define HID_KEY_F17 0x6C +#define HID_KEY_F18 0x6D +#define HID_KEY_F19 0x6E +#define HID_KEY_F20 0x6F +#define HID_KEY_F21 0x70 +#define HID_KEY_F22 0x71 +#define HID_KEY_F23 0x72 +#define HID_KEY_F24 0x73 +#define HID_KEY_EXECUTE 0x74 +#define HID_KEY_HELP 0x75 +#define HID_KEY_MENU 0x76 +#define HID_KEY_SELECT 0x77 +#define HID_KEY_STOP 0x78 +#define HID_KEY_AGAIN 0x79 +#define HID_KEY_UNDO 0x7A +#define HID_KEY_CUT 0x7B +#define HID_KEY_COPY 0x7C +#define HID_KEY_PASTE 0x7D +#define HID_KEY_FIND 0x7E +#define HID_KEY_MUTE 0x7F +#define HID_KEY_VOLUME_UP 0x80 +#define HID_KEY_VOLUME_DOWN 0x81 +#define HID_KEY_LOCKING_CAPS_LOCK 0x82 +#define HID_KEY_LOCKING_NUM_LOCK 0x83 +#define HID_KEY_LOCKING_SCROLL_LOCK 0x84 +#define HID_KEY_KEYPAD_COMMA 0x85 +#define HID_KEY_KEYPAD_EQUAL_SIGN 0x86 +#define HID_KEY_KANJI1 0x87 +#define HID_KEY_KANJI2 0x88 +#define HID_KEY_KANJI3 0x89 +#define HID_KEY_KANJI4 0x8A +#define HID_KEY_KANJI5 0x8B +#define HID_KEY_KANJI6 0x8C +#define HID_KEY_KANJI7 0x8D +#define HID_KEY_KANJI8 0x8E +#define HID_KEY_KANJI9 0x8F +#define HID_KEY_LANG1 0x90 +#define HID_KEY_LANG2 0x91 +#define HID_KEY_LANG3 0x92 +#define HID_KEY_LANG4 0x93 +#define HID_KEY_LANG5 0x94 +#define HID_KEY_LANG6 0x95 +#define HID_KEY_LANG7 0x96 +#define HID_KEY_LANG8 0x97 +#define HID_KEY_LANG9 0x98 +#define HID_KEY_ALTERNATE_ERASE 0x99 +#define HID_KEY_SYSREQ_ATTENTION 0x9A +#define HID_KEY_CANCEL 0x9B +#define HID_KEY_CLEAR 0x9C +#define HID_KEY_PRIOR 0x9D +#define HID_KEY_RETURN 0x9E +#define HID_KEY_SEPARATOR 0x9F +#define HID_KEY_OUT 0xA0 +#define HID_KEY_OPER 0xA1 +#define HID_KEY_CLEAR_AGAIN 0xA2 +#define HID_KEY_CRSEL_PROPS 0xA3 +#define HID_KEY_EXSEL 0xA4 +// RESERVED 0xA5-DF +#define HID_KEY_CONTROL_LEFT 0xE0 +#define HID_KEY_SHIFT_LEFT 0xE1 +#define HID_KEY_ALT_LEFT 0xE2 +#define HID_KEY_GUI_LEFT 0xE3 +#define HID_KEY_CONTROL_RIGHT 0xE4 +#define HID_KEY_SHIFT_RIGHT 0xE5 +#define HID_KEY_ALT_RIGHT 0xE6 +#define HID_KEY_GUI_RIGHT 0xE7 + + +//--------------------------------------------------------------------+ +// REPORT DESCRIPTOR +//--------------------------------------------------------------------+ + +//------------- ITEM & TAG -------------// +#define HID_REPORT_DATA_0(data) +#define HID_REPORT_DATA_1(data) , data +#define HID_REPORT_DATA_2(data) , U16_TO_U8S_LE(data) +#define HID_REPORT_DATA_3(data) , U32_TO_U8S_LE(data) + +#define HID_REPORT_ITEM(data, tag, type, size) \ + (((tag) << 4) | ((type) << 2) | (size)) HID_REPORT_DATA_##size(data) + +// Report Item Types +enum { + RI_TYPE_MAIN = 0, + RI_TYPE_GLOBAL = 1, + RI_TYPE_LOCAL = 2 +}; + +//------------- Main Items - HID 1.11 section 6.2.2.4 -------------// + +// Report Item Main group +enum { + RI_MAIN_INPUT = 8, + RI_MAIN_OUTPUT = 9, + RI_MAIN_COLLECTION = 10, + RI_MAIN_FEATURE = 11, + RI_MAIN_COLLECTION_END = 12 +}; + +#define HID_INPUT(x) HID_REPORT_ITEM(x, RI_MAIN_INPUT , RI_TYPE_MAIN, 1) +#define HID_OUTPUT(x) HID_REPORT_ITEM(x, RI_MAIN_OUTPUT , RI_TYPE_MAIN, 1) +#define HID_COLLECTION(x) HID_REPORT_ITEM(x, RI_MAIN_COLLECTION , RI_TYPE_MAIN, 1) +#define HID_FEATURE(x) HID_REPORT_ITEM(x, RI_MAIN_FEATURE , RI_TYPE_MAIN, 1) +#define HID_COLLECTION_END HID_REPORT_ITEM(x, RI_MAIN_COLLECTION_END, RI_TYPE_MAIN, 0) + +//------------- Input, Output, Feature - HID 1.11 section 6.2.2.5 -------------// +#define HID_DATA (0<<0) +#define HID_CONSTANT (1<<0) + +#define HID_ARRAY (0<<1) +#define HID_VARIABLE (1<<1) + +#define HID_ABSOLUTE (0<<2) +#define HID_RELATIVE (1<<2) + +#define HID_WRAP_NO (0<<3) +#define HID_WRAP (1<<3) + +#define HID_LINEAR (0<<4) +#define HID_NONLINEAR (1<<4) + +#define HID_PREFERRED_STATE (0<<5) +#define HID_PREFERRED_NO (1<<5) + +#define HID_NO_NULL_POSITION (0<<6) +#define HID_NULL_STATE (1<<6) + +#define HID_NON_VOLATILE (0<<7) +#define HID_VOLATILE (1<<7) + +#define HID_BITFIELD (0<<8) +#define HID_BUFFERED_BYTES (1<<8) + +//------------- Collection Item - HID 1.11 section 6.2.2.6 -------------// +enum { + HID_COLLECTION_PHYSICAL = 0, + HID_COLLECTION_APPLICATION, + HID_COLLECTION_LOGICAL, + HID_COLLECTION_REPORT, + HID_COLLECTION_NAMED_ARRAY, + HID_COLLECTION_USAGE_SWITCH, + HID_COLLECTION_USAGE_MODIFIER +}; + +//------------- Global Items - HID 1.11 section 6.2.2.7 -------------// + +// Report Item Global group +enum { + RI_GLOBAL_USAGE_PAGE = 0, + RI_GLOBAL_LOGICAL_MIN = 1, + RI_GLOBAL_LOGICAL_MAX = 2, + RI_GLOBAL_PHYSICAL_MIN = 3, + RI_GLOBAL_PHYSICAL_MAX = 4, + RI_GLOBAL_UNIT_EXPONENT = 5, + RI_GLOBAL_UNIT = 6, + RI_GLOBAL_REPORT_SIZE = 7, + RI_GLOBAL_REPORT_ID = 8, + RI_GLOBAL_REPORT_COUNT = 9, + RI_GLOBAL_PUSH = 10, + RI_GLOBAL_POP = 11 +}; + +#define HID_USAGE_PAGE(x) HID_REPORT_ITEM(x, RI_GLOBAL_USAGE_PAGE, RI_TYPE_GLOBAL, 1) +#define HID_USAGE_PAGE_N(x, n) HID_REPORT_ITEM(x, RI_GLOBAL_USAGE_PAGE, RI_TYPE_GLOBAL, n) + +#define HID_LOGICAL_MIN(x) HID_REPORT_ITEM(x, RI_GLOBAL_LOGICAL_MIN, RI_TYPE_GLOBAL, 1) +#define HID_LOGICAL_MIN_N(x, n) HID_REPORT_ITEM(x, RI_GLOBAL_LOGICAL_MIN, RI_TYPE_GLOBAL, n) + +#define HID_LOGICAL_MAX(x) HID_REPORT_ITEM(x, RI_GLOBAL_LOGICAL_MAX, RI_TYPE_GLOBAL, 1) +#define HID_LOGICAL_MAX_N(x, n) HID_REPORT_ITEM(x, RI_GLOBAL_LOGICAL_MAX, RI_TYPE_GLOBAL, n) + +#define HID_PHYSICAL_MIN(x) HID_REPORT_ITEM(x, RI_GLOBAL_PHYSICAL_MIN, RI_TYPE_GLOBAL, 1) +#define HID_PHYSICAL_MIN_N(x, n) HID_REPORT_ITEM(x, RI_GLOBAL_PHYSICAL_MIN, RI_TYPE_GLOBAL, n) + +#define HID_PHYSICAL_MAX(x) HID_REPORT_ITEM(x, RI_GLOBAL_PHYSICAL_MAX, RI_TYPE_GLOBAL, 1) +#define HID_PHYSICAL_MAX_N(x, n) HID_REPORT_ITEM(x, RI_GLOBAL_PHYSICAL_MAX, RI_TYPE_GLOBAL, n) + +#define HID_UNIT_EXPONENT(x) HID_REPORT_ITEM(x, RI_GLOBAL_UNIT_EXPONENT, RI_TYPE_GLOBAL, 1) +#define HID_UNIT_EXPONENT_N(x, n) HID_REPORT_ITEM(x, RI_GLOBAL_UNIT_EXPONENT, RI_TYPE_GLOBAL, n) + +#define HID_UNIT(x) HID_REPORT_ITEM(x, RI_GLOBAL_UNIT, RI_TYPE_GLOBAL, 1) +#define HID_UNIT_N(x, n) HID_REPORT_ITEM(x, RI_GLOBAL_UNIT, RI_TYPE_GLOBAL, n) + +#define HID_REPORT_SIZE(x) HID_REPORT_ITEM(x, RI_GLOBAL_REPORT_SIZE, RI_TYPE_GLOBAL, 1) +#define HID_REPORT_SIZE_N(x, n) HID_REPORT_ITEM(x, RI_GLOBAL_REPORT_SIZE, RI_TYPE_GLOBAL, n) + +#define HID_REPORT_ID(x) HID_REPORT_ITEM(x, RI_GLOBAL_REPORT_ID, RI_TYPE_GLOBAL, 1), +#define HID_REPORT_ID_N(x, n) HID_REPORT_ITEM(x, RI_GLOBAL_REPORT_ID, RI_TYPE_GLOBAL, n), + +#define HID_REPORT_COUNT(x) HID_REPORT_ITEM(x, RI_GLOBAL_REPORT_COUNT, RI_TYPE_GLOBAL, 1) +#define HID_REPORT_COUNT_N(x, n) HID_REPORT_ITEM(x, RI_GLOBAL_REPORT_COUNT, RI_TYPE_GLOBAL, n) + +#define HID_PUSH HID_REPORT_ITEM(x, RI_GLOBAL_PUSH, RI_TYPE_GLOBAL, 0) +#define HID_POP HID_REPORT_ITEM(x, RI_GLOBAL_POP, RI_TYPE_GLOBAL, 0) + +//------------- LOCAL ITEMS 6.2.2.8 -------------// + +enum { + RI_LOCAL_USAGE = 0, + RI_LOCAL_USAGE_MIN = 1, + RI_LOCAL_USAGE_MAX = 2, + RI_LOCAL_DESIGNATOR_INDEX = 3, + RI_LOCAL_DESIGNATOR_MIN = 4, + RI_LOCAL_DESIGNATOR_MAX = 5, + // 6 is reserved + RI_LOCAL_STRING_INDEX = 7, + RI_LOCAL_STRING_MIN = 8, + RI_LOCAL_STRING_MAX = 9, + RI_LOCAL_DELIMITER = 10, +}; + +#define HID_USAGE(x) HID_REPORT_ITEM(x, RI_LOCAL_USAGE, RI_TYPE_LOCAL, 1) +#define HID_USAGE_N(x, n) HID_REPORT_ITEM(x, RI_LOCAL_USAGE, RI_TYPE_LOCAL, n) + +#define HID_USAGE_MIN(x) HID_REPORT_ITEM(x, RI_LOCAL_USAGE_MIN, RI_TYPE_LOCAL, 1) +#define HID_USAGE_MIN_N(x, n) HID_REPORT_ITEM(x, RI_LOCAL_USAGE_MIN, RI_TYPE_LOCAL, n) + +#define HID_USAGE_MAX(x) HID_REPORT_ITEM(x, RI_LOCAL_USAGE_MAX, RI_TYPE_LOCAL, 1) +#define HID_USAGE_MAX_N(x, n) HID_REPORT_ITEM(x, RI_LOCAL_USAGE_MAX, RI_TYPE_LOCAL, n) + +//--------------------------------------------------------------------+ +// Usage Table +//--------------------------------------------------------------------+ + +/// HID Usage Table - Table 1: Usage Page Summary +enum { + HID_USAGE_PAGE_DESKTOP = 0x01, + HID_USAGE_PAGE_SIMULATE = 0x02, + HID_USAGE_PAGE_VIRTUAL_REALITY = 0x03, + HID_USAGE_PAGE_SPORT = 0x04, + HID_USAGE_PAGE_GAME = 0x05, + HID_USAGE_PAGE_GENERIC_DEVICE = 0x06, + HID_USAGE_PAGE_KEYBOARD = 0x07, + HID_USAGE_PAGE_LED = 0x08, + HID_USAGE_PAGE_BUTTON = 0x09, + HID_USAGE_PAGE_ORDINAL = 0x0a, + HID_USAGE_PAGE_TELEPHONY = 0x0b, + HID_USAGE_PAGE_CONSUMER = 0x0c, + HID_USAGE_PAGE_DIGITIZER = 0x0d, + HID_USAGE_PAGE_PID = 0x0f, + HID_USAGE_PAGE_UNICODE = 0x10, + HID_USAGE_PAGE_ALPHA_DISPLAY = 0x14, + HID_USAGE_PAGE_MEDICAL = 0x40, + HID_USAGE_PAGE_MONITOR = 0x80, //0x80 - 0x83 + HID_USAGE_PAGE_POWER = 0x84, // 0x084 - 0x87 + HID_USAGE_PAGE_BARCODE_SCANNER = 0x8c, + HID_USAGE_PAGE_SCALE = 0x8d, + HID_USAGE_PAGE_MSR = 0x8e, + HID_USAGE_PAGE_CAMERA = 0x90, + HID_USAGE_PAGE_ARCADE = 0x91, + HID_USAGE_PAGE_FIDO = 0xF1D0, // FIDO alliance HID usage page + HID_USAGE_PAGE_VENDOR = 0xFF00 // 0xFF00 - 0xFFFF +}; + +/// HID Usage Table - Table 6: Generic Desktop Page +enum { + HID_USAGE_DESKTOP_POINTER = 0x01, + HID_USAGE_DESKTOP_MOUSE = 0x02, + HID_USAGE_DESKTOP_JOYSTICK = 0x04, + HID_USAGE_DESKTOP_GAMEPAD = 0x05, + HID_USAGE_DESKTOP_KEYBOARD = 0x06, + HID_USAGE_DESKTOP_KEYPAD = 0x07, + HID_USAGE_DESKTOP_MULTI_AXIS_CONTROLLER = 0x08, + HID_USAGE_DESKTOP_TABLET_PC_SYSTEM = 0x09, + HID_USAGE_DESKTOP_X = 0x30, + HID_USAGE_DESKTOP_Y = 0x31, + HID_USAGE_DESKTOP_Z = 0x32, + HID_USAGE_DESKTOP_RX = 0x33, + HID_USAGE_DESKTOP_RY = 0x34, + HID_USAGE_DESKTOP_RZ = 0x35, + HID_USAGE_DESKTOP_SLIDER = 0x36, + HID_USAGE_DESKTOP_DIAL = 0x37, + HID_USAGE_DESKTOP_WHEEL = 0x38, + HID_USAGE_DESKTOP_HAT_SWITCH = 0x39, + HID_USAGE_DESKTOP_COUNTED_BUFFER = 0x3a, + HID_USAGE_DESKTOP_BYTE_COUNT = 0x3b, + HID_USAGE_DESKTOP_MOTION_WAKEUP = 0x3c, + HID_USAGE_DESKTOP_START = 0x3d, + HID_USAGE_DESKTOP_SELECT = 0x3e, + HID_USAGE_DESKTOP_VX = 0x40, + HID_USAGE_DESKTOP_VY = 0x41, + HID_USAGE_DESKTOP_VZ = 0x42, + HID_USAGE_DESKTOP_VBRX = 0x43, + HID_USAGE_DESKTOP_VBRY = 0x44, + HID_USAGE_DESKTOP_VBRZ = 0x45, + HID_USAGE_DESKTOP_VNO = 0x46, + HID_USAGE_DESKTOP_FEATURE_NOTIFICATION = 0x47, + HID_USAGE_DESKTOP_RESOLUTION_MULTIPLIER = 0x48, + HID_USAGE_DESKTOP_SYSTEM_CONTROL = 0x80, + HID_USAGE_DESKTOP_SYSTEM_POWER_DOWN = 0x81, + HID_USAGE_DESKTOP_SYSTEM_SLEEP = 0x82, + HID_USAGE_DESKTOP_SYSTEM_WAKE_UP = 0x83, + HID_USAGE_DESKTOP_SYSTEM_CONTEXT_MENU = 0x84, + HID_USAGE_DESKTOP_SYSTEM_MAIN_MENU = 0x85, + HID_USAGE_DESKTOP_SYSTEM_APP_MENU = 0x86, + HID_USAGE_DESKTOP_SYSTEM_MENU_HELP = 0x87, + HID_USAGE_DESKTOP_SYSTEM_MENU_EXIT = 0x88, + HID_USAGE_DESKTOP_SYSTEM_MENU_SELECT = 0x89, + HID_USAGE_DESKTOP_SYSTEM_MENU_RIGHT = 0x8A, + HID_USAGE_DESKTOP_SYSTEM_MENU_LEFT = 0x8B, + HID_USAGE_DESKTOP_SYSTEM_MENU_UP = 0x8C, + HID_USAGE_DESKTOP_SYSTEM_MENU_DOWN = 0x8D, + HID_USAGE_DESKTOP_SYSTEM_COLD_RESTART = 0x8E, + HID_USAGE_DESKTOP_SYSTEM_WARM_RESTART = 0x8F, + HID_USAGE_DESKTOP_DPAD_UP = 0x90, + HID_USAGE_DESKTOP_DPAD_DOWN = 0x91, + HID_USAGE_DESKTOP_DPAD_RIGHT = 0x92, + HID_USAGE_DESKTOP_DPAD_LEFT = 0x93, + HID_USAGE_DESKTOP_SYSTEM_DOCK = 0xA0, + HID_USAGE_DESKTOP_SYSTEM_UNDOCK = 0xA1, + HID_USAGE_DESKTOP_SYSTEM_SETUP = 0xA2, + HID_USAGE_DESKTOP_SYSTEM_BREAK = 0xA3, + HID_USAGE_DESKTOP_SYSTEM_DEBUGGER_BREAK = 0xA4, + HID_USAGE_DESKTOP_APPLICATION_BREAK = 0xA5, + HID_USAGE_DESKTOP_APPLICATION_DEBUGGER_BREAK = 0xA6, + HID_USAGE_DESKTOP_SYSTEM_SPEAKER_MUTE = 0xA7, + HID_USAGE_DESKTOP_SYSTEM_HIBERNATE = 0xA8, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_INVERT = 0xB0, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_INTERNAL = 0xB1, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_EXTERNAL = 0xB2, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_BOTH = 0xB3, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_DUAL = 0xB4, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_TOGGLE_INT_EXT = 0xB5, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_SWAP_PRIMARY_SECONDARY = 0xB6, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_LCD_AUTOSCALE = 0xB7 +}; + + +/// HID Usage Table: Consumer Page (0x0C) +/// Only contains controls that supported by Windows (whole list is too long) +enum +{ + // Generic Control + HID_USAGE_CONSUMER_CONTROL = 0x0001, + + // Power Control + HID_USAGE_CONSUMER_POWER = 0x0030, + HID_USAGE_CONSUMER_RESET = 0x0031, + HID_USAGE_CONSUMER_SLEEP = 0x0032, + + // Screen Brightness + HID_USAGE_CONSUMER_BRIGHTNESS_INCREMENT = 0x006F, + HID_USAGE_CONSUMER_BRIGHTNESS_DECREMENT = 0x0070, + + // These HID usages operate only on mobile systems (battery powered) and + // require Windows 8 (build 8302 or greater). + HID_USAGE_CONSUMER_WIRELESS_RADIO_CONTROLS = 0x000C, + HID_USAGE_CONSUMER_WIRELESS_RADIO_BUTTONS = 0x00C6, + HID_USAGE_CONSUMER_WIRELESS_RADIO_LED = 0x00C7, + HID_USAGE_CONSUMER_WIRELESS_RADIO_SLIDER_SWITCH = 0x00C8, + + // Media Control + HID_USAGE_CONSUMER_PLAY_PAUSE = 0x00CD, + HID_USAGE_CONSUMER_SCAN_NEXT = 0x00B5, + HID_USAGE_CONSUMER_SCAN_PREVIOUS = 0x00B6, + HID_USAGE_CONSUMER_STOP = 0x00B7, + HID_USAGE_CONSUMER_VOLUME = 0x00E0, + HID_USAGE_CONSUMER_MUTE = 0x00E2, + HID_USAGE_CONSUMER_BASS = 0x00E3, + HID_USAGE_CONSUMER_TREBLE = 0x00E4, + HID_USAGE_CONSUMER_BASS_BOOST = 0x00E5, + HID_USAGE_CONSUMER_VOLUME_INCREMENT = 0x00E9, + HID_USAGE_CONSUMER_VOLUME_DECREMENT = 0x00EA, + HID_USAGE_CONSUMER_BASS_INCREMENT = 0x0152, + HID_USAGE_CONSUMER_BASS_DECREMENT = 0x0153, + HID_USAGE_CONSUMER_TREBLE_INCREMENT = 0x0154, + HID_USAGE_CONSUMER_TREBLE_DECREMENT = 0x0155, + + // Application Launcher + HID_USAGE_CONSUMER_AL_CONSUMER_CONTROL_CONFIGURATION = 0x0183, + HID_USAGE_CONSUMER_AL_EMAIL_READER = 0x018A, + HID_USAGE_CONSUMER_AL_CALCULATOR = 0x0192, + HID_USAGE_CONSUMER_AL_LOCAL_BROWSER = 0x0194, + + // Browser/Explorer Specific + HID_USAGE_CONSUMER_AC_SEARCH = 0x0221, + HID_USAGE_CONSUMER_AC_HOME = 0x0223, + HID_USAGE_CONSUMER_AC_BACK = 0x0224, + HID_USAGE_CONSUMER_AC_FORWARD = 0x0225, + HID_USAGE_CONSUMER_AC_STOP = 0x0226, + HID_USAGE_CONSUMER_AC_REFRESH = 0x0227, + HID_USAGE_CONSUMER_AC_BOOKMARKS = 0x022A, + + // Mouse Horizontal scroll + HID_USAGE_CONSUMER_AC_PAN = 0x0238, +}; + +/// HID Usage Table: FIDO Alliance Page (0xF1D0) +enum +{ + HID_USAGE_FIDO_U2FHID = 0x01, // U2FHID usage for top-level collection + HID_USAGE_FIDO_DATA_IN = 0x20, // Raw IN data report + HID_USAGE_FIDO_DATA_OUT = 0x21 // Raw OUT data report +}; + +/*-------------------------------------------------------------------- + * ASCII to KEYCODE Conversion + * Expand to array of [128][2] (shift, keycode) + * + * Usage: example to convert input chr into keyboard report (modifier + keycode) + * + * uint8_t const conv_table[128][2] = { HID_ASCII_TO_KEYCODE }; + * + * uint8_t keycode[6] = { 0 }; + * uint8_t modifier = 0; + * + * if ( conv_table[chr][0] ) modifier = KEYBOARD_MODIFIER_LEFTSHIFT; + * keycode[0] = conv_table[chr][1]; + * tud_hid_keyboard_report(report_id, modifier, keycode); + * + *--------------------------------------------------------------------*/ +#define HID_ASCII_TO_KEYCODE \ + {0, 0 }, /* 0x00 Null */ \ + {0, 0 }, /* 0x01 */ \ + {0, 0 }, /* 0x02 */ \ + {0, 0 }, /* 0x03 */ \ + {0, 0 }, /* 0x04 */ \ + {0, 0 }, /* 0x05 */ \ + {0, 0 }, /* 0x06 */ \ + {0, 0 }, /* 0x07 */ \ + {0, HID_KEY_BACKSPACE }, /* 0x08 Backspace */ \ + {0, HID_KEY_TAB }, /* 0x09 Tab */ \ + {0, HID_KEY_ENTER }, /* 0x0A Line Feed */ \ + {0, 0 }, /* 0x0B */ \ + {0, 0 }, /* 0x0C */ \ + {0, HID_KEY_ENTER }, /* 0x0D CR */ \ + {0, 0 }, /* 0x0E */ \ + {0, 0 }, /* 0x0F */ \ + {0, 0 }, /* 0x10 */ \ + {0, 0 }, /* 0x11 */ \ + {0, 0 }, /* 0x12 */ \ + {0, 0 }, /* 0x13 */ \ + {0, 0 }, /* 0x14 */ \ + {0, 0 }, /* 0x15 */ \ + {0, 0 }, /* 0x16 */ \ + {0, 0 }, /* 0x17 */ \ + {0, 0 }, /* 0x18 */ \ + {0, 0 }, /* 0x19 */ \ + {0, 0 }, /* 0x1A */ \ + {0, HID_KEY_ESCAPE }, /* 0x1B Escape */ \ + {0, 0 }, /* 0x1C */ \ + {0, 0 }, /* 0x1D */ \ + {0, 0 }, /* 0x1E */ \ + {0, 0 }, /* 0x1F */ \ + \ + {0, HID_KEY_SPACE }, /* 0x20 */ \ + {1, HID_KEY_1 }, /* 0x21 ! */ \ + {1, HID_KEY_APOSTROPHE }, /* 0x22 " */ \ + {1, HID_KEY_3 }, /* 0x23 # */ \ + {1, HID_KEY_4 }, /* 0x24 $ */ \ + {1, HID_KEY_5 }, /* 0x25 % */ \ + {1, HID_KEY_7 }, /* 0x26 & */ \ + {0, HID_KEY_APOSTROPHE }, /* 0x27 ' */ \ + {1, HID_KEY_9 }, /* 0x28 ( */ \ + {1, HID_KEY_0 }, /* 0x29 ) */ \ + {1, HID_KEY_8 }, /* 0x2A * */ \ + {1, HID_KEY_EQUAL }, /* 0x2B + */ \ + {0, HID_KEY_COMMA }, /* 0x2C , */ \ + {0, HID_KEY_MINUS }, /* 0x2D - */ \ + {0, HID_KEY_PERIOD }, /* 0x2E . */ \ + {0, HID_KEY_SLASH }, /* 0x2F / */ \ + {0, HID_KEY_0 }, /* 0x30 0 */ \ + {0, HID_KEY_1 }, /* 0x31 1 */ \ + {0, HID_KEY_2 }, /* 0x32 2 */ \ + {0, HID_KEY_3 }, /* 0x33 3 */ \ + {0, HID_KEY_4 }, /* 0x34 4 */ \ + {0, HID_KEY_5 }, /* 0x35 5 */ \ + {0, HID_KEY_6 }, /* 0x36 6 */ \ + {0, HID_KEY_7 }, /* 0x37 7 */ \ + {0, HID_KEY_8 }, /* 0x38 8 */ \ + {0, HID_KEY_9 }, /* 0x39 9 */ \ + {1, HID_KEY_SEMICOLON }, /* 0x3A : */ \ + {0, HID_KEY_SEMICOLON }, /* 0x3B ; */ \ + {1, HID_KEY_COMMA }, /* 0x3C < */ \ + {0, HID_KEY_EQUAL }, /* 0x3D = */ \ + {1, HID_KEY_PERIOD }, /* 0x3E > */ \ + {1, HID_KEY_SLASH }, /* 0x3F ? */ \ + \ + {1, HID_KEY_2 }, /* 0x40 @ */ \ + {1, HID_KEY_A }, /* 0x41 A */ \ + {1, HID_KEY_B }, /* 0x42 B */ \ + {1, HID_KEY_C }, /* 0x43 C */ \ + {1, HID_KEY_D }, /* 0x44 D */ \ + {1, HID_KEY_E }, /* 0x45 E */ \ + {1, HID_KEY_F }, /* 0x46 F */ \ + {1, HID_KEY_G }, /* 0x47 G */ \ + {1, HID_KEY_H }, /* 0x48 H */ \ + {1, HID_KEY_I }, /* 0x49 I */ \ + {1, HID_KEY_J }, /* 0x4A J */ \ + {1, HID_KEY_K }, /* 0x4B K */ \ + {1, HID_KEY_L }, /* 0x4C L */ \ + {1, HID_KEY_M }, /* 0x4D M */ \ + {1, HID_KEY_N }, /* 0x4E N */ \ + {1, HID_KEY_O }, /* 0x4F O */ \ + {1, HID_KEY_P }, /* 0x50 P */ \ + {1, HID_KEY_Q }, /* 0x51 Q */ \ + {1, HID_KEY_R }, /* 0x52 R */ \ + {1, HID_KEY_S }, /* 0x53 S */ \ + {1, HID_KEY_T }, /* 0x55 T */ \ + {1, HID_KEY_U }, /* 0x55 U */ \ + {1, HID_KEY_V }, /* 0x56 V */ \ + {1, HID_KEY_W }, /* 0x57 W */ \ + {1, HID_KEY_X }, /* 0x58 X */ \ + {1, HID_KEY_Y }, /* 0x59 Y */ \ + {1, HID_KEY_Z }, /* 0x5A Z */ \ + {0, HID_KEY_BRACKET_LEFT }, /* 0x5B [ */ \ + {0, HID_KEY_BACKSLASH }, /* 0x5C '\' */ \ + {0, HID_KEY_BRACKET_RIGHT }, /* 0x5D ] */ \ + {1, HID_KEY_6 }, /* 0x5E ^ */ \ + {1, HID_KEY_MINUS }, /* 0x5F _ */ \ + \ + {0, HID_KEY_GRAVE }, /* 0x60 ` */ \ + {0, HID_KEY_A }, /* 0x61 a */ \ + {0, HID_KEY_B }, /* 0x62 b */ \ + {0, HID_KEY_C }, /* 0x63 c */ \ + {0, HID_KEY_D }, /* 0x66 d */ \ + {0, HID_KEY_E }, /* 0x65 e */ \ + {0, HID_KEY_F }, /* 0x66 f */ \ + {0, HID_KEY_G }, /* 0x67 g */ \ + {0, HID_KEY_H }, /* 0x68 h */ \ + {0, HID_KEY_I }, /* 0x69 i */ \ + {0, HID_KEY_J }, /* 0x6A j */ \ + {0, HID_KEY_K }, /* 0x6B k */ \ + {0, HID_KEY_L }, /* 0x6C l */ \ + {0, HID_KEY_M }, /* 0x6D m */ \ + {0, HID_KEY_N }, /* 0x6E n */ \ + {0, HID_KEY_O }, /* 0x6F o */ \ + {0, HID_KEY_P }, /* 0x70 p */ \ + {0, HID_KEY_Q }, /* 0x71 q */ \ + {0, HID_KEY_R }, /* 0x72 r */ \ + {0, HID_KEY_S }, /* 0x73 s */ \ + {0, HID_KEY_T }, /* 0x75 t */ \ + {0, HID_KEY_U }, /* 0x75 u */ \ + {0, HID_KEY_V }, /* 0x76 v */ \ + {0, HID_KEY_W }, /* 0x77 w */ \ + {0, HID_KEY_X }, /* 0x78 x */ \ + {0, HID_KEY_Y }, /* 0x79 y */ \ + {0, HID_KEY_Z }, /* 0x7A z */ \ + {1, HID_KEY_BRACKET_LEFT }, /* 0x7B { */ \ + {1, HID_KEY_BACKSLASH }, /* 0x7C | */ \ + {1, HID_KEY_BRACKET_RIGHT }, /* 0x7D } */ \ + {1, HID_KEY_GRAVE }, /* 0x7E ~ */ \ + {0, HID_KEY_DELETE } /* 0x7F Delete */ \ + +/*-------------------------------------------------------------------- + * KEYCODE to Ascii Conversion + * Expand to array of [128][2] (ascii without shift, ascii with shift) + * + * Usage: example to convert ascii from keycode (key) and shift modifier (shift). + * Here we assume key < 128 ( printable ) + * + * uint8_t const conv_table[128][2] = { HID_KEYCODE_TO_ASCII }; + * char ch = shift ? conv_table[chr][1] : conv_table[chr][0]; + * + *--------------------------------------------------------------------*/ +#define HID_KEYCODE_TO_ASCII \ + {0 , 0 }, /* 0x00 */ \ + {0 , 0 }, /* 0x01 */ \ + {0 , 0 }, /* 0x02 */ \ + {0 , 0 }, /* 0x03 */ \ + {'a' , 'A' }, /* 0x04 */ \ + {'b' , 'B' }, /* 0x05 */ \ + {'c' , 'C' }, /* 0x06 */ \ + {'d' , 'D' }, /* 0x07 */ \ + {'e' , 'E' }, /* 0x08 */ \ + {'f' , 'F' }, /* 0x09 */ \ + {'g' , 'G' }, /* 0x0a */ \ + {'h' , 'H' }, /* 0x0b */ \ + {'i' , 'I' }, /* 0x0c */ \ + {'j' , 'J' }, /* 0x0d */ \ + {'k' , 'K' }, /* 0x0e */ \ + {'l' , 'L' }, /* 0x0f */ \ + {'m' , 'M' }, /* 0x10 */ \ + {'n' , 'N' }, /* 0x11 */ \ + {'o' , 'O' }, /* 0x12 */ \ + {'p' , 'P' }, /* 0x13 */ \ + {'q' , 'Q' }, /* 0x14 */ \ + {'r' , 'R' }, /* 0x15 */ \ + {'s' , 'S' }, /* 0x16 */ \ + {'t' , 'T' }, /* 0x17 */ \ + {'u' , 'U' }, /* 0x18 */ \ + {'v' , 'V' }, /* 0x19 */ \ + {'w' , 'W' }, /* 0x1a */ \ + {'x' , 'X' }, /* 0x1b */ \ + {'y' , 'Y' }, /* 0x1c */ \ + {'z' , 'Z' }, /* 0x1d */ \ + {'1' , '!' }, /* 0x1e */ \ + {'2' , '@' }, /* 0x1f */ \ + {'3' , '#' }, /* 0x20 */ \ + {'4' , '$' }, /* 0x21 */ \ + {'5' , '%' }, /* 0x22 */ \ + {'6' , '^' }, /* 0x23 */ \ + {'7' , '&' }, /* 0x24 */ \ + {'8' , '*' }, /* 0x25 */ \ + {'9' , '(' }, /* 0x26 */ \ + {'0' , ')' }, /* 0x27 */ \ + {'\r' , '\r' }, /* 0x28 */ \ + {'\x1b', '\x1b' }, /* 0x29 */ \ + {'\b' , '\b' }, /* 0x2a */ \ + {'\t' , '\t' }, /* 0x2b */ \ + {' ' , ' ' }, /* 0x2c */ \ + {'-' , '_' }, /* 0x2d */ \ + {'=' , '+' }, /* 0x2e */ \ + {'[' , '{' }, /* 0x2f */ \ + {']' , '}' }, /* 0x30 */ \ + {'\\' , '|' }, /* 0x31 */ \ + {'#' , '~' }, /* 0x32 */ \ + {';' , ':' }, /* 0x33 */ \ + {'\'' , '\"' }, /* 0x34 */ \ + {'`' , '~' }, /* 0x35 */ \ + {',' , '<' }, /* 0x36 */ \ + {'.' , '>' }, /* 0x37 */ \ + {'/' , '?' }, /* 0x38 */ \ + \ + {0 , 0 }, /* 0x39 */ \ + {0 , 0 }, /* 0x3a */ \ + {0 , 0 }, /* 0x3b */ \ + {0 , 0 }, /* 0x3c */ \ + {0 , 0 }, /* 0x3d */ \ + {0 , 0 }, /* 0x3e */ \ + {0 , 0 }, /* 0x3f */ \ + {0 , 0 }, /* 0x40 */ \ + {0 , 0 }, /* 0x41 */ \ + {0 , 0 }, /* 0x42 */ \ + {0 , 0 }, /* 0x43 */ \ + {0 , 0 }, /* 0x44 */ \ + {0 , 0 }, /* 0x45 */ \ + {0 , 0 }, /* 0x46 */ \ + {0 , 0 }, /* 0x47 */ \ + {0 , 0 }, /* 0x48 */ \ + {0 , 0 }, /* 0x49 */ \ + {0 , 0 }, /* 0x4a */ \ + {0 , 0 }, /* 0x4b */ \ + {0 , 0 }, /* 0x4c */ \ + {0 , 0 }, /* 0x4d */ \ + {0 , 0 }, /* 0x4e */ \ + {0 , 0 }, /* 0x4f */ \ + {0 , 0 }, /* 0x50 */ \ + {0 , 0 }, /* 0x51 */ \ + {0 , 0 }, /* 0x52 */ \ + {0 , 0 }, /* 0x53 */ \ + \ + {'/' , '/' }, /* 0x54 */ \ + {'*' , '*' }, /* 0x55 */ \ + {'-' , '-' }, /* 0x56 */ \ + {'+' , '+' }, /* 0x57 */ \ + {'\r' , '\r' }, /* 0x58 */ \ + {'1' , 0 }, /* 0x59 */ \ + {'2' , 0 }, /* 0x5a */ \ + {'3' , 0 }, /* 0x5b */ \ + {'4' , 0 }, /* 0x5c */ \ + {'5' , '5' }, /* 0x5d */ \ + {'6' , 0 }, /* 0x5e */ \ + {'7' , 0 }, /* 0x5f */ \ + {'8' , 0 }, /* 0x60 */ \ + {'9' , 0 }, /* 0x61 */ \ + {'0' , 0 }, /* 0x62 */ \ + {'.' , 0 }, /* 0x63 */ \ + {0 , 0 }, /* 0x64 */ \ + {0 , 0 }, /* 0x65 */ \ + {0 , 0 }, /* 0x66 */ \ + {'=' , '=' }, /* 0x67 */ \ + + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_HID_H__ */ + +/// @} diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/class/hid/hid_host.c b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/class/hid/hid_host.c new file mode 100644 index 0000000000..42b5e2f4eb --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/class/hid/hid_host.c @@ -0,0 +1,677 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if (CFG_TUH_ENABLED && CFG_TUH_HID) + +#include "host/usbh.h" +#include "host/usbh_classdriver.h" + +#include "hid_host.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +typedef struct +{ + uint8_t itf_num; + uint8_t ep_in; + uint8_t ep_out; + + uint8_t itf_protocol; // None, Keyboard, Mouse + uint8_t protocol_mode; // Boot (0) or Report protocol (1) + + uint8_t report_desc_type; + uint16_t report_desc_len; + + uint16_t epin_size; + uint16_t epout_size; + + uint8_t epin_buf[CFG_TUH_HID_EPIN_BUFSIZE]; + uint8_t epout_buf[CFG_TUH_HID_EPOUT_BUFSIZE]; +} hidh_interface_t; + +typedef struct +{ + uint8_t inst_count; + hidh_interface_t instances[CFG_TUH_HID]; +} hidh_device_t; + +CFG_TUSB_MEM_SECTION +static hidh_device_t _hidh_dev[CFG_TUH_DEVICE_MAX]; + +//------------- Internal prototypes -------------// + +// Get HID device & interface +TU_ATTR_ALWAYS_INLINE static inline hidh_device_t* get_dev(uint8_t dev_addr); +TU_ATTR_ALWAYS_INLINE static inline hidh_interface_t* get_instance(uint8_t dev_addr, uint8_t instance); +static uint8_t get_instance_id_by_itfnum(uint8_t dev_addr, uint8_t itf); +static uint8_t get_instance_id_by_epaddr(uint8_t dev_addr, uint8_t ep_addr); + +//--------------------------------------------------------------------+ +// Interface API +//--------------------------------------------------------------------+ + +uint8_t tuh_hid_instance_count(uint8_t dev_addr) +{ + return get_dev(dev_addr)->inst_count; +} + +bool tuh_hid_mounted(uint8_t dev_addr, uint8_t instance) +{ + hidh_interface_t* hid_itf = get_instance(dev_addr, instance); + return (hid_itf->ep_in != 0) || (hid_itf->ep_out != 0); +} + +uint8_t tuh_hid_interface_protocol(uint8_t dev_addr, uint8_t instance) +{ + hidh_interface_t* hid_itf = get_instance(dev_addr, instance); + return hid_itf->itf_protocol; +} + +//--------------------------------------------------------------------+ +// Control Endpoint API +//--------------------------------------------------------------------+ + +uint8_t tuh_hid_get_protocol(uint8_t dev_addr, uint8_t instance) +{ + hidh_interface_t* hid_itf = get_instance(dev_addr, instance); + return hid_itf->protocol_mode; +} + +static void set_protocol_complete(tuh_xfer_t* xfer) +{ + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t const daddr = xfer->daddr; + uint8_t const instance = get_instance_id_by_itfnum(daddr, itf_num); + hidh_interface_t* hid_itf = get_instance(daddr, instance); + + if (XFER_RESULT_SUCCESS == xfer->result) + { + hid_itf->protocol_mode = (uint8_t) tu_le16toh(xfer->setup->wValue); + } + + if (tuh_hid_set_protocol_complete_cb) + { + tuh_hid_set_protocol_complete_cb(daddr, instance, hid_itf->protocol_mode); + } +} + + +static bool _hidh_set_protocol(uint8_t dev_addr, uint8_t itf_num, uint8_t protocol, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + TU_LOG2("HID Set Protocol = %d\r\n", protocol); + + tusb_control_request_t const request = + { + .bmRequestType_bit = + { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_OUT + }, + .bRequest = HID_REQ_CONTROL_SET_PROTOCOL, + .wValue = protocol, + .wIndex = itf_num, + .wLength = 0 + }; + + tuh_xfer_t xfer = + { + .daddr = dev_addr, + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = complete_cb, + .user_data = user_data + }; + + TU_ASSERT( tuh_control_xfer(&xfer) ); + return true; +} + +bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t instance, uint8_t protocol) +{ + hidh_interface_t* hid_itf = get_instance(dev_addr, instance); + TU_VERIFY(hid_itf->itf_protocol != HID_ITF_PROTOCOL_NONE); + + return _hidh_set_protocol(dev_addr, hid_itf->itf_num, protocol, set_protocol_complete, 0); +} + +static void set_report_complete(tuh_xfer_t* xfer) +{ + TU_LOG2("HID Set Report complete\r\n"); + + if (tuh_hid_set_report_complete_cb) + { + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t const instance = get_instance_id_by_itfnum(xfer->daddr, itf_num); + + uint8_t const report_type = tu_u16_high(xfer->setup->wValue); + uint8_t const report_id = tu_u16_low(xfer->setup->wValue); + + tuh_hid_set_report_complete_cb(xfer->daddr, instance, report_id, report_type, + (xfer->result == XFER_RESULT_SUCCESS) ? xfer->setup->wLength : 0); + } +} + +bool tuh_hid_set_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, void* report, uint16_t len) +{ + hidh_interface_t* hid_itf = get_instance(dev_addr, instance); + TU_LOG2("HID Set Report: id = %u, type = %u, len = %u\r\n", report_id, report_type, len); + + tusb_control_request_t const request = + { + .bmRequestType_bit = + { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_OUT + }, + .bRequest = HID_REQ_CONTROL_SET_REPORT, + .wValue = tu_u16(report_type, report_id), + .wIndex = hid_itf->itf_num, + .wLength = len + }; + + tuh_xfer_t xfer = + { + .daddr = dev_addr, + .ep_addr = 0, + .setup = &request, + .buffer = report, + .complete_cb = set_report_complete, + .user_data = 0 + }; + + TU_ASSERT( tuh_control_xfer(&xfer) ); + return true; +} + +static bool _hidh_set_idle(uint8_t dev_addr, uint8_t itf_num, uint16_t idle_rate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + // SET IDLE request, device can stall if not support this request + TU_LOG2("HID Set Idle \r\n"); + tusb_control_request_t const request = + { + .bmRequestType_bit = + { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_OUT + }, + .bRequest = HID_REQ_CONTROL_SET_IDLE, + .wValue = idle_rate, + .wIndex = itf_num, + .wLength = 0 + }; + + tuh_xfer_t xfer = + { + .daddr = dev_addr, + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = complete_cb, + .user_data = user_data + }; + + TU_ASSERT( tuh_control_xfer(&xfer) ); + + return true; +} + +//--------------------------------------------------------------------+ +// Interrupt Endpoint API +//--------------------------------------------------------------------+ + +bool tuh_hid_receive_report(uint8_t dev_addr, uint8_t instance) +{ + hidh_interface_t* hid_itf = get_instance(dev_addr, instance); + + // claim endpoint + TU_VERIFY( usbh_edpt_claim(dev_addr, hid_itf->ep_in) ); + + if ( !usbh_edpt_xfer(dev_addr, hid_itf->ep_in, hid_itf->epin_buf, hid_itf->epin_size) ) + { + usbh_edpt_release(dev_addr, hid_itf->ep_in); + return false; + } + + return true; +} + +//bool tuh_n_hid_n_ready(uint8_t dev_addr, uint8_t instance) +//{ +// TU_VERIFY(tuh_n_hid_n_mounted(dev_addr, instance)); +// +// hidh_interface_t* hid_itf = get_instance(dev_addr, instance); +// return !usbh_edpt_busy(dev_addr, hid_itf->ep_in); +//} + +//void tuh_hid_send_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t const* report, uint16_t len); + +//--------------------------------------------------------------------+ +// USBH API +//--------------------------------------------------------------------+ +void hidh_init(void) +{ + tu_memclr(_hidh_dev, sizeof(_hidh_dev)); +} + +bool hidh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) +{ + (void) result; + + uint8_t const dir = tu_edpt_dir(ep_addr); + uint8_t const instance = get_instance_id_by_epaddr(dev_addr, ep_addr); + hidh_interface_t* hid_itf = get_instance(dev_addr, instance); + + if ( dir == TUSB_DIR_IN ) + { + TU_LOG2(" Get Report callback (%u, %u)\r\n", dev_addr, instance); + TU_LOG3_MEM(hid_itf->epin_buf, xferred_bytes, 2); + tuh_hid_report_received_cb(dev_addr, instance, hid_itf->epin_buf, (uint16_t) xferred_bytes); + }else + { + if (tuh_hid_report_sent_cb) tuh_hid_report_sent_cb(dev_addr, instance, hid_itf->epout_buf, (uint16_t) xferred_bytes); + } + + return true; +} + +void hidh_close(uint8_t dev_addr) +{ + TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX, ); + + hidh_device_t* hid_dev = get_dev(dev_addr); + + if (tuh_hid_umount_cb) + { + for (uint8_t inst = 0; inst < hid_dev->inst_count; inst++ ) tuh_hid_umount_cb(dev_addr, inst); + } + + tu_memclr(hid_dev, sizeof(hidh_device_t)); +} + +//--------------------------------------------------------------------+ +// Enumeration +//--------------------------------------------------------------------+ + +bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len) +{ + (void) rhport; + (void) max_len; + + TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass); + + TU_LOG2("[%u] HID opening Interface %u\r\n", dev_addr, desc_itf->bInterfaceNumber); + + // len = interface + hid + n*endpoints + uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + + desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t)); + TU_ASSERT(max_len >= drv_len); + + uint8_t const *p_desc = (uint8_t const *) desc_itf; + + //------------- HID descriptor -------------// + p_desc = tu_desc_next(p_desc); + tusb_hid_descriptor_hid_t const *desc_hid = (tusb_hid_descriptor_hid_t const *) p_desc; + TU_ASSERT(HID_DESC_TYPE_HID == desc_hid->bDescriptorType); + + // not enough interface, try to increase CFG_TUH_HID + // TODO multiple devices + hidh_device_t* hid_dev = get_dev(dev_addr); + TU_ASSERT(hid_dev->inst_count < CFG_TUH_HID, 0); + + hidh_interface_t* hid_itf = get_instance(dev_addr, hid_dev->inst_count); + + //------------- Endpoint Descriptors -------------// + p_desc = tu_desc_next(p_desc); + tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc; + + for(int i = 0; i < desc_itf->bNumEndpoints; i++) + { + TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType); + TU_ASSERT( tuh_edpt_open(dev_addr, desc_ep) ); + + if(tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) + { + hid_itf->ep_in = desc_ep->bEndpointAddress; + hid_itf->epin_size = tu_edpt_packet_size(desc_ep); + } + else + { + hid_itf->ep_out = desc_ep->bEndpointAddress; + hid_itf->epout_size = tu_edpt_packet_size(desc_ep); + } + + p_desc = tu_desc_next(p_desc); + desc_ep = (tusb_desc_endpoint_t const *) p_desc; + } + + hid_dev->inst_count++; + + hid_itf->itf_num = desc_itf->bInterfaceNumber; + + // Assume bNumDescriptors = 1 + hid_itf->report_desc_type = desc_hid->bReportType; + hid_itf->report_desc_len = tu_unaligned_read16(&desc_hid->wReportLength); + + // Per HID Specs: default is Report protocol, though we will force Boot protocol when set_config + hid_itf->protocol_mode = HID_PROTOCOL_BOOT; + if ( HID_SUBCLASS_BOOT == desc_itf->bInterfaceSubClass ) hid_itf->itf_protocol = desc_itf->bInterfaceProtocol; + + return true; +} + +//--------------------------------------------------------------------+ +// Set Configure +//--------------------------------------------------------------------+ + +enum { + CONFG_SET_IDLE, + CONFIG_SET_PROTOCOL, + CONFIG_GET_REPORT_DESC, + CONFIG_COMPLETE +}; + +static void config_driver_mount_complete(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len); +static void process_set_config(tuh_xfer_t* xfer); + +bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num) +{ + tusb_control_request_t request; + request.wIndex = tu_htole16((uint16_t) itf_num); + + tuh_xfer_t xfer; + xfer.daddr = dev_addr; + xfer.result = XFER_RESULT_SUCCESS; + xfer.setup = &request; + xfer.user_data = CONFG_SET_IDLE; + + // fake request to kick-off the set config process + process_set_config(&xfer); + + return true; +} + +static void process_set_config(tuh_xfer_t* xfer) +{ + // Stall is a valid response for SET_IDLE, therefore we could ignore its result + if ( xfer->setup->bRequest != HID_REQ_CONTROL_SET_IDLE ) + { + TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, ); + } + + uintptr_t const state = xfer->user_data; + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t const daddr = xfer->daddr; + + uint8_t const instance = get_instance_id_by_itfnum(daddr, itf_num); + hidh_interface_t* hid_itf = get_instance(daddr, instance); + + switch(state) + { + case CONFG_SET_IDLE: + { + // Idle rate = 0 mean only report when there is changes + const uint16_t idle_rate = 0; + const uintptr_t next_state = (hid_itf->itf_protocol != HID_ITF_PROTOCOL_NONE) ? CONFIG_SET_PROTOCOL : CONFIG_GET_REPORT_DESC; + _hidh_set_idle(daddr, itf_num, idle_rate, process_set_config, next_state); + } + break; + + case CONFIG_SET_PROTOCOL: + _hidh_set_protocol(daddr, hid_itf->itf_num, HID_PROTOCOL_BOOT, process_set_config, CONFIG_GET_REPORT_DESC); + break; + + case CONFIG_GET_REPORT_DESC: + // Get Report Descriptor if possible + // using usbh enumeration buffer since report descriptor can be very long + if( hid_itf->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE ) + { + TU_LOG2("HID Skip Report Descriptor since it is too large %u bytes\r\n", hid_itf->report_desc_len); + + // Driver is mounted without report descriptor + config_driver_mount_complete(daddr, instance, NULL, 0); + }else + { + tuh_descriptor_get_hid_report(daddr, itf_num, hid_itf->report_desc_type, 0, usbh_get_enum_buf(), hid_itf->report_desc_len, process_set_config, CONFIG_COMPLETE); + } + break; + + case CONFIG_COMPLETE: + { + uint8_t const* desc_report = usbh_get_enum_buf(); + uint16_t const desc_len = tu_le16toh(xfer->setup->wLength); + + config_driver_mount_complete(daddr, instance, desc_report, desc_len); + } + break; + + default: break; + } +} + +static void config_driver_mount_complete(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) +{ + hidh_interface_t* hid_itf = get_instance(dev_addr, instance); + + // enumeration is complete + tuh_hid_mount_cb(dev_addr, instance, desc_report, desc_len); + + // notify usbh that driver enumeration is complete + usbh_driver_set_config_complete(dev_addr, hid_itf->itf_num); +} + +//--------------------------------------------------------------------+ +// Report Descriptor Parser +//--------------------------------------------------------------------+ + +uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr, uint8_t arr_count, uint8_t const* desc_report, uint16_t desc_len) +{ + // Report Item 6.2.2.2 USB HID 1.11 + union TU_ATTR_PACKED + { + uint8_t byte; + struct TU_ATTR_PACKED + { + uint8_t size : 2; + uint8_t type : 2; + uint8_t tag : 4; + }; + } header; + + tu_memclr(report_info_arr, arr_count*sizeof(tuh_hid_report_info_t)); + + uint8_t report_num = 0; + tuh_hid_report_info_t* info = report_info_arr; + + // current parsed report count & size from descriptor +// uint8_t ri_report_count = 0; +// uint8_t ri_report_size = 0; + + uint8_t ri_collection_depth = 0; + + while(desc_len && report_num < arr_count) + { + header.byte = *desc_report++; + desc_len--; + + uint8_t const tag = header.tag; + uint8_t const type = header.type; + uint8_t const size = header.size; + + uint8_t const data8 = desc_report[0]; + + TU_LOG(3, "tag = %d, type = %d, size = %d, data = ", tag, type, size); + for(uint32_t i=0; iusage_page, desc_report, size); + break; + + case RI_GLOBAL_LOGICAL_MIN : break; + case RI_GLOBAL_LOGICAL_MAX : break; + case RI_GLOBAL_PHYSICAL_MIN : break; + case RI_GLOBAL_PHYSICAL_MAX : break; + + case RI_GLOBAL_REPORT_ID: + info->report_id = data8; + break; + + case RI_GLOBAL_REPORT_SIZE: +// ri_report_size = data8; + break; + + case RI_GLOBAL_REPORT_COUNT: +// ri_report_count = data8; + break; + + case RI_GLOBAL_UNIT_EXPONENT : break; + case RI_GLOBAL_UNIT : break; + case RI_GLOBAL_PUSH : break; + case RI_GLOBAL_POP : break; + + default: break; + } + break; + + case RI_TYPE_LOCAL: + switch(tag) + { + case RI_LOCAL_USAGE: + // only take in account the "usage" before starting REPORT ID + if ( ri_collection_depth == 0 ) info->usage = data8; + break; + + case RI_LOCAL_USAGE_MIN : break; + case RI_LOCAL_USAGE_MAX : break; + case RI_LOCAL_DESIGNATOR_INDEX : break; + case RI_LOCAL_DESIGNATOR_MIN : break; + case RI_LOCAL_DESIGNATOR_MAX : break; + case RI_LOCAL_STRING_INDEX : break; + case RI_LOCAL_STRING_MIN : break; + case RI_LOCAL_STRING_MAX : break; + case RI_LOCAL_DELIMITER : break; + default: break; + } + break; + + // error + default: break; + } + + desc_report += size; + desc_len -= size; + } + + for ( uint8_t i = 0; i < report_num; i++ ) + { + info = report_info_arr+i; + TU_LOG2("%u: id = %u, usage_page = %u, usage = %u\r\n", i, info->report_id, info->usage_page, info->usage); + } + + return report_num; +} + +//--------------------------------------------------------------------+ +// Helper +//--------------------------------------------------------------------+ + +// Get Device by address +TU_ATTR_ALWAYS_INLINE static inline hidh_device_t* get_dev(uint8_t dev_addr) +{ + return &_hidh_dev[dev_addr-1]; +} + +// Get Interface by instance number +TU_ATTR_ALWAYS_INLINE static inline hidh_interface_t* get_instance(uint8_t dev_addr, uint8_t instance) +{ + return &_hidh_dev[dev_addr-1].instances[instance]; +} + +// Get instance ID by interface number +static uint8_t get_instance_id_by_itfnum(uint8_t dev_addr, uint8_t itf) +{ + for ( uint8_t inst = 0; inst < CFG_TUH_HID; inst++ ) + { + hidh_interface_t *hid = get_instance(dev_addr, inst); + + if ( (hid->itf_num == itf) && (hid->ep_in || hid->ep_out) ) return inst; + } + + return 0xff; +} + +// Get instance ID by endpoint address +static uint8_t get_instance_id_by_epaddr(uint8_t dev_addr, uint8_t ep_addr) +{ + for ( uint8_t inst = 0; inst < CFG_TUH_HID; inst++ ) + { + hidh_interface_t *hid = get_instance(dev_addr, inst); + + if ( (ep_addr == hid->ep_in) || ( ep_addr == hid->ep_out) ) return inst; + } + + return 0xff; +} + +#endif diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/class/hid/hid_host.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/class/hid/hid_host.h new file mode 100644 index 0000000000..ffc601d772 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/class/hid/hid_host.h @@ -0,0 +1,152 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_HID_HOST_H_ +#define _TUSB_HID_HOST_H_ + +#include "hid.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Class Driver Configuration +//--------------------------------------------------------------------+ + +// TODO Highspeed interrupt can be up to 512 bytes +#ifndef CFG_TUH_HID_EPIN_BUFSIZE +#define CFG_TUH_HID_EPIN_BUFSIZE 64 +#endif + +#ifndef CFG_TUH_HID_EPOUT_BUFSIZE +#define CFG_TUH_HID_EPOUT_BUFSIZE 64 +#endif + + +typedef struct +{ + uint8_t report_id; + uint8_t usage; + uint16_t usage_page; + + // TODO still use the endpoint size for now +// uint8_t in_len; // length of IN report +// uint8_t out_len; // length of OUT report +} tuh_hid_report_info_t; + +//--------------------------------------------------------------------+ +// Interface API +//--------------------------------------------------------------------+ + +// Get the number of HID instances +uint8_t tuh_hid_instance_count(uint8_t dev_addr); + +// Check if HID instance is mounted +bool tuh_hid_mounted(uint8_t dev_addr, uint8_t instance); + +// Get interface supported protocol (bInterfaceProtocol) check out hid_interface_protocol_enum_t for possible values +uint8_t tuh_hid_interface_protocol(uint8_t dev_addr, uint8_t instance); + +// Parse report descriptor into array of report_info struct and return number of reports. +// For complicated report, application should write its own parser. +uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr, uint8_t arr_count, uint8_t const* desc_report, uint16_t desc_len) TU_ATTR_UNUSED; + +//--------------------------------------------------------------------+ +// Control Endpoint API +//--------------------------------------------------------------------+ + +// Get current protocol: HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1) +// Note: Device will be initialized in Boot protocol for simplicity. +// Application can use set_protocol() to switch back to Report protocol. +uint8_t tuh_hid_get_protocol(uint8_t dev_addr, uint8_t instance); + +// Set protocol to HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1) +// This function is only supported by Boot interface (tuh_n_hid_interface_protocol() != NONE) +bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t instance, uint8_t protocol); + +// Set Report using control endpoint +// report_type is either Input, Output or Feature, (value from hid_report_type_t) +bool tuh_hid_set_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, void* report, uint16_t len); + +//--------------------------------------------------------------------+ +// Interrupt Endpoint API +//--------------------------------------------------------------------+ + +// Check if the interface is ready to use +//bool tuh_n_hid_n_ready(uint8_t dev_addr, uint8_t instance); + +// Try to receive next report on Interrupt Endpoint. Immediately return +// - true If succeeded, tuh_hid_report_received_cb() callback will be invoked when report is available +// - false if failed to queue the transfer e.g endpoint is busy +bool tuh_hid_receive_report(uint8_t dev_addr, uint8_t instance); + +// Send report using interrupt endpoint +// If report_id > 0 (composite), it will be sent as 1st byte, then report contents. Otherwise only report content is sent. +//void tuh_hid_send_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t const* report, uint16_t len); + +//--------------------------------------------------------------------+ +// Callbacks (Weak is optional) +//--------------------------------------------------------------------+ + +// Invoked when device with hid interface is mounted +// Report descriptor is also available for use. tuh_hid_parse_report_descriptor() +// can be used to parse common/simple enough descriptor. +// Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped +// therefore report_desc = NULL, desc_len = 0 +void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report_desc, uint16_t desc_len); + +// Invoked when device with hid interface is un-mounted +TU_ATTR_WEAK void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance); + +// Invoked when received report from device via interrupt endpoint +// Note: if there is report ID (composite), it is 1st byte of report +void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len); + +// Invoked when sent report to device successfully via interrupt endpoint +TU_ATTR_WEAK void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len); + +// Invoked when Sent Report to device via either control endpoint +// len = 0 indicate there is error in the transfer e.g stalled response +TU_ATTR_WEAK void tuh_hid_set_report_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len); + +// Invoked when Set Protocol request is complete +TU_ATTR_WEAK void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t protocol); + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void hidh_init (void); +bool hidh_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len); +bool hidh_set_config (uint8_t dev_addr, uint8_t itf_num); +bool hidh_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); +void hidh_close (uint8_t dev_addr); + +#ifdef __cplusplus +} +#endif + +#endif /* _TUSB_HID_HOST_H_ */ diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_common.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_common.h new file mode 100644 index 0000000000..270339cca4 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_common.h @@ -0,0 +1,281 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_COMMON_H_ +#define _TUSB_COMMON_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Macros Helper +//--------------------------------------------------------------------+ +#define TU_ARRAY_SIZE(_arr) ( sizeof(_arr) / sizeof(_arr[0]) ) +#define TU_MIN(_x, _y) ( ( (_x) < (_y) ) ? (_x) : (_y) ) +#define TU_MAX(_x, _y) ( ( (_x) > (_y) ) ? (_x) : (_y) ) + +#define TU_U16(_high, _low) ((uint16_t) (((_high) << 8) | (_low))) +#define TU_U16_HIGH(_u16) ((uint8_t) (((_u16) >> 8) & 0x00ff)) +#define TU_U16_LOW(_u16) ((uint8_t) ((_u16) & 0x00ff)) +#define U16_TO_U8S_BE(_u16) TU_U16_HIGH(_u16), TU_U16_LOW(_u16) +#define U16_TO_U8S_LE(_u16) TU_U16_LOW(_u16), TU_U16_HIGH(_u16) + +#define TU_U32_BYTE3(_u32) ((uint8_t) ((((uint32_t) _u32) >> 24) & 0x000000ff)) // MSB +#define TU_U32_BYTE2(_u32) ((uint8_t) ((((uint32_t) _u32) >> 16) & 0x000000ff)) +#define TU_U32_BYTE1(_u32) ((uint8_t) ((((uint32_t) _u32) >> 8) & 0x000000ff)) +#define TU_U32_BYTE0(_u32) ((uint8_t) (((uint32_t) _u32) & 0x000000ff)) // LSB + +#define U32_TO_U8S_BE(_u32) TU_U32_BYTE3(_u32), TU_U32_BYTE2(_u32), TU_U32_BYTE1(_u32), TU_U32_BYTE0(_u32) +#define U32_TO_U8S_LE(_u32) TU_U32_BYTE0(_u32), TU_U32_BYTE1(_u32), TU_U32_BYTE2(_u32), TU_U32_BYTE3(_u32) + +#define TU_BIT(n) (1UL << (n)) +#define TU_GENMASK(h, l) ( (UINT32_MAX << (l)) & (UINT32_MAX >> (31 - (h))) ) + +//--------------------------------------------------------------------+ +// Includes +//--------------------------------------------------------------------+ + +// Standard Headers +#include +#include +#include +#include +#include + +// Tinyusb Common Headers +#include "tusb_option.h" +#include "tusb_compiler.h" +#include "tusb_verify.h" +#include "tusb_types.h" +#include "tusb_debug.h" + +#include "tusb_timeout.h" // TODO remove + +//--------------------------------------------------------------------+ +// Optional API implemented by application if needed +// TODO move to a more ovious place/file +//--------------------------------------------------------------------+ + +// flush data cache +TU_ATTR_WEAK extern void tusb_app_dcache_flush(uintptr_t addr, uint32_t data_size); + +// invalidate data cache +TU_ATTR_WEAK extern void tusb_app_dcache_invalidate(uintptr_t addr, uint32_t data_size); + +// Optional physical <-> virtual address translation +TU_ATTR_WEAK extern void* tusb_app_virt_to_phys(void *virt_addr); +TU_ATTR_WEAK extern void* tusb_app_phys_to_virt(void *phys_addr); + +//--------------------------------------------------------------------+ +// Internal Inline Functions +//--------------------------------------------------------------------+ + +//------------- Mem -------------// +#define tu_memclr(buffer, size) memset((buffer), 0, (size)) +#define tu_varclr(_var) tu_memclr(_var, sizeof(*(_var))) + +//------------- Bytes -------------// +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_u32(uint8_t b3, uint8_t b2, uint8_t b1, uint8_t b0) +{ + return ( ((uint32_t) b3) << 24) | ( ((uint32_t) b2) << 16) | ( ((uint32_t) b1) << 8) | b0; +} + +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_u16(uint8_t high, uint8_t low) +{ + return (uint16_t) ((((uint16_t) high) << 8) | low); +} + +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte3(uint32_t ui32) { return TU_U32_BYTE3(ui32); } +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte2(uint32_t ui32) { return TU_U32_BYTE2(ui32); } +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte1(uint32_t ui32) { return TU_U32_BYTE1(ui32); } +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte0(uint32_t ui32) { return TU_U32_BYTE0(ui32); } + +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_u32_high16(uint32_t ui32) { return (uint16_t) (ui32 >> 16); } +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_u32_low16 (uint32_t ui32) { return (uint16_t) (ui32 & 0x0000ffffu); } + +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u16_high(uint16_t ui16) { return TU_U16_HIGH(ui16); } +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u16_low (uint16_t ui16) { return TU_U16_LOW(ui16); } + +//------------- Bits -------------// +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_bit_set (uint32_t value, uint8_t pos) { return value | TU_BIT(pos); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_bit_clear(uint32_t value, uint8_t pos) { return value & (~TU_BIT(pos)); } +TU_ATTR_ALWAYS_INLINE static inline bool tu_bit_test (uint32_t value, uint8_t pos) { return (value & TU_BIT(pos)) ? true : false; } + +//------------- Min -------------// +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_min8 (uint8_t x, uint8_t y ) { return (x < y) ? x : y; } +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_min16 (uint16_t x, uint16_t y) { return (x < y) ? x : y; } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_min32 (uint32_t x, uint32_t y) { return (x < y) ? x : y; } + +//------------- Max -------------// +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_max8 (uint8_t x, uint8_t y ) { return (x > y) ? x : y; } +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_max16 (uint16_t x, uint16_t y) { return (x > y) ? x : y; } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_max32 (uint32_t x, uint32_t y) { return (x > y) ? x : y; } + +//------------- Align -------------// +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align(uint32_t value, uint32_t alignment) +{ + return value & ((uint32_t) ~(alignment-1)); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align16 (uint32_t value) { return (value & 0xFFFFFFF0UL); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align32 (uint32_t value) { return (value & 0xFFFFFFE0UL); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align4k (uint32_t value) { return (value & 0xFFFFF000UL); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_offset4k(uint32_t value) { return (value & 0xFFFUL); } + +//------------- Mathematics -------------// +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_div_ceil(uint32_t v, uint32_t d) { return (v + d -1)/d; } + +// log2 of a value is its MSB's position +// TODO use clz TODO remove +static inline uint8_t tu_log2(uint32_t value) +{ + uint8_t result = 0; + while (value >>= 1) { result++; } + return result; +} + +//static inline uint8_t tu_log2(uint32_t value) +//{ +// return sizeof(uint32_t) * CHAR_BIT - __builtin_clz(x) - 1; +//} + +static inline bool tu_is_power_of_two(uint32_t value) +{ + return (value != 0) && ((value & (value - 1)) == 0); +} + +//------------- Unaligned Access -------------// +#if TUP_ARCH_STRICT_ALIGN + +// Rely on compiler to generate correct code for unaligned access +typedef struct { uint16_t val; } TU_ATTR_PACKED tu_unaligned_uint16_t; +typedef struct { uint32_t val; } TU_ATTR_PACKED tu_unaligned_uint32_t; + +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_unaligned_read32(const void* mem) +{ + tu_unaligned_uint32_t const* ua32 = (tu_unaligned_uint32_t const*) mem; + return ua32->val; +} + +TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32(void* mem, uint32_t value) +{ + tu_unaligned_uint32_t* ua32 = (tu_unaligned_uint32_t*) mem; + ua32->val = value; +} + +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16(const void* mem) +{ + tu_unaligned_uint16_t const* ua16 = (tu_unaligned_uint16_t const*) mem; + return ua16->val; +} + +TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void* mem, uint16_t value) +{ + tu_unaligned_uint16_t* ua16 = (tu_unaligned_uint16_t*) mem; + ua16->val = value; +} + +#elif TUP_MCU_STRICT_ALIGN + +// MCU such as LPC_IP3511 Highspeed cannot access unaligned memory on USB_RAM although it is ARM M4. +// We have to manually pick up bytes since tu_unaligned_uint32_t will still generate unaligned code +// NOTE: volatile cast to memory to prevent compiler to optimize and generate unaligned code +// TODO Big Endian may need minor changes +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_unaligned_read32(const void* mem) +{ + volatile uint8_t const* buf8 = (uint8_t const*) mem; + return tu_u32(buf8[3], buf8[2], buf8[1], buf8[0]); +} + +TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32(void* mem, uint32_t value) +{ + volatile uint8_t* buf8 = (uint8_t*) mem; + buf8[0] = tu_u32_byte0(value); + buf8[1] = tu_u32_byte1(value); + buf8[2] = tu_u32_byte2(value); + buf8[3] = tu_u32_byte3(value); +} + +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16(const void* mem) +{ + volatile uint8_t const* buf8 = (uint8_t const*) mem; + return tu_u16(buf8[1], buf8[0]); +} + +TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void* mem, uint16_t value) +{ + volatile uint8_t* buf8 = (uint8_t*) mem; + buf8[0] = tu_u16_low(value); + buf8[1] = tu_u16_high(value); +} + + +#else + +// MCU that could access unaligned memory natively +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_unaligned_read32 (const void* mem) { return *((uint32_t const *) mem); } +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16 (const void* mem) { return *((uint16_t const *) mem); } + +TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32 (void* mem, uint32_t value ) { *((uint32_t*) mem) = value; } +TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16 (void* mem, uint16_t value ) { *((uint16_t*) mem) = value; } + +#endif + +// To be removed +//------------- Binary constant -------------// +#if defined(__GNUC__) && !defined(__CC_ARM) + +#define TU_BIN8(x) ((uint8_t) (0b##x)) +#define TU_BIN16(b1, b2) ((uint16_t) (0b##b1##b2)) +#define TU_BIN32(b1, b2, b3, b4) ((uint32_t) (0b##b1##b2##b3##b4)) + +#else + +// internal macro of B8, B16, B32 +#define _B8__(x) (((x&0x0000000FUL)?1:0) \ + +((x&0x000000F0UL)?2:0) \ + +((x&0x00000F00UL)?4:0) \ + +((x&0x0000F000UL)?8:0) \ + +((x&0x000F0000UL)?16:0) \ + +((x&0x00F00000UL)?32:0) \ + +((x&0x0F000000UL)?64:0) \ + +((x&0xF0000000UL)?128:0)) + +#define TU_BIN8(d) ((uint8_t) _B8__(0x##d##UL)) +#define TU_BIN16(dmsb,dlsb) (((uint16_t)TU_BIN8(dmsb)<<8) + TU_BIN8(dlsb)) +#define TU_BIN32(dmsb,db2,db3,dlsb) \ + (((uint32_t)TU_BIN8(dmsb)<<24) \ + + ((uint32_t)TU_BIN8(db2)<<16) \ + + ((uint32_t)TU_BIN8(db3)<<8) \ + + TU_BIN8(dlsb)) +#endif + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_COMMON_H_ */ diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_compiler.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_compiler.h new file mode 100644 index 0000000000..30478ba6d3 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_compiler.h @@ -0,0 +1,281 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +/** \ingroup Group_Common + * \defgroup Group_Compiler Compiler + * \brief Group_Compiler brief + * @{ */ + +#ifndef _TUSB_COMPILER_H_ +#define _TUSB_COMPILER_H_ + +#define TU_TOKEN(x) x +#define TU_STRING(x) #x ///< stringify without expand +#define TU_XSTRING(x) TU_STRING(x) ///< expand then stringify + +#define TU_STRCAT(a, b) a##b ///< concat without expand +#define TU_STRCAT3(a, b, c) a##b##c ///< concat without expand + +#define TU_XSTRCAT(a, b) TU_STRCAT(a, b) ///< expand then concat +#define TU_XSTRCAT3(a, b, c) TU_STRCAT3(a, b, c) ///< expand then concat 3 tokens + +#define TU_INCLUDE_PATH(_dir,_file) TU_XSTRING( TU_TOKEN(_dir)TU_TOKEN(_file) ) + +#if defined __COUNTER__ && __COUNTER__ != __COUNTER__ + #define _TU_COUNTER_ __COUNTER__ +#else + #define _TU_COUNTER_ __LINE__ +#endif + +// Compile-time Assert +#if defined (__cplusplus) && __cplusplus >= 201103L + #define TU_VERIFY_STATIC static_assert +#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define TU_VERIFY_STATIC _Static_assert +#elif defined(__CCRX__) + #define TU_VERIFY_STATIC(const_expr, _mess) typedef char TU_XSTRCAT(Line, __LINE__)[(const_expr) ? 1 : 0]; +#else + #define TU_VERIFY_STATIC(const_expr, _mess) enum { TU_XSTRCAT(_verify_static_, _TU_COUNTER_) = 1/(!!(const_expr)) } +#endif + +/* --------------------- Fuzzing types -------------------------------------- */ +#ifdef _FUZZ + #define tu_static static __thread +#else + #define tu_static static +#endif + +// for declaration of reserved field, make use of _TU_COUNTER_ +#define TU_RESERVED TU_XSTRCAT(reserved, _TU_COUNTER_) + +#define TU_LITTLE_ENDIAN (0x12u) +#define TU_BIG_ENDIAN (0x21u) + +/*------------------------------------------------------------------*/ +/* Count number of arguments of __VA_ARGS__ + * - reference https://stackoverflow.com/questions/2124339/c-preprocessor-va-args-number-of-arguments + * - _GET_NTH_ARG() takes args >= N (64) but only expand to Nth one (64th) + * - _RSEQ_N() is reverse sequential to N to add padding to have + * Nth position is the same as the number of arguments + * - ##__VA_ARGS__ is used to deal with 0 paramerter (swallows comma) + *------------------------------------------------------------------*/ +#if !defined(__CCRX__) +#define TU_ARGS_NUM(...) _TU_NARG(_0, ##__VA_ARGS__, _RSEQ_N()) +#else +#define TU_ARGS_NUM(...) _TU_NARG(_0, __VA_ARGS__, _RSEQ_N()) +#endif + +#define _TU_NARG(...) _GET_NTH_ARG(__VA_ARGS__) +#define _GET_NTH_ARG( \ + _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ + _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ + _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ + _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ + _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ + _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ + _61,_62,_63,N,...) N +#define _RSEQ_N() \ + 62,61,60, \ + 59,58,57,56,55,54,53,52,51,50, \ + 49,48,47,46,45,44,43,42,41,40, \ + 39,38,37,36,35,34,33,32,31,30, \ + 29,28,27,26,25,24,23,22,21,20, \ + 19,18,17,16,15,14,13,12,11,10, \ + 9,8,7,6,5,4,3,2,1,0 + +// Apply an macro X to each of the arguments with an separated of choice +#define TU_ARGS_APPLY(_X, _s, ...) TU_XSTRCAT(_TU_ARGS_APPLY_, TU_ARGS_NUM(__VA_ARGS__))(_X, _s, __VA_ARGS__) + +#define _TU_ARGS_APPLY_1(_X, _s, _a1) _X(_a1) +#define _TU_ARGS_APPLY_2(_X, _s, _a1, _a2) _X(_a1) _s _X(_a2) +#define _TU_ARGS_APPLY_3(_X, _s, _a1, _a2, _a3) _X(_a1) _s _TU_ARGS_APPLY_2(_X, _s, _a2, _a3) +#define _TU_ARGS_APPLY_4(_X, _s, _a1, _a2, _a3, _a4) _X(_a1) _s _TU_ARGS_APPLY_3(_X, _s, _a2, _a3, _a4) +#define _TU_ARGS_APPLY_5(_X, _s, _a1, _a2, _a3, _a4, _a5) _X(_a1) _s _TU_ARGS_APPLY_4(_X, _s, _a2, _a3, _a4, _a5) +#define _TU_ARGS_APPLY_6(_X, _s, _a1, _a2, _a3, _a4, _a5, _a6) _X(_a1) _s _TU_ARGS_APPLY_5(_X, _s, _a2, _a3, _a4, _a5, _a6) +#define _TU_ARGS_APPLY_7(_X, _s, _a1, _a2, _a3, _a4, _a5, _a6, _a7) _X(_a1) _s _TU_ARGS_APPLY_6(_X, _s, _a2, _a3, _a4, _a5, _a6, _a7) +#define _TU_ARGS_APPLY_8(_X, _s, _a1, _a2, _a3, _a4, _a5, _a6, _a7, _a8) _X(_a1) _s _TU_ARGS_APPLY_7(_X, _s, _a2, _a3, _a4, _a5, _a6, _a7, _a8) + +//--------------------------------------------------------------------+ +// Compiler porting with Attribute and Endian +//--------------------------------------------------------------------+ + +// TODO refactor since __attribute__ is supported across many compiler +#if defined(__GNUC__) + #define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes))) + #define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name))) + #define TU_ATTR_PACKED __attribute__ ((packed)) + #define TU_ATTR_WEAK __attribute__ ((weak)) + #define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline)) + #define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used + #define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused + #define TU_ATTR_USED __attribute__ ((used)) // Function/Variable is meant to be used + + #define TU_ATTR_PACKED_BEGIN + #define TU_ATTR_PACKED_END + #define TU_ATTR_BIT_FIELD_ORDER_BEGIN + #define TU_ATTR_BIT_FIELD_ORDER_END + + #if __has_attribute(__fallthrough__) + #define TU_ATTR_FALLTHROUGH __attribute__((fallthrough)) + #else + #define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */ + #endif + + // Endian conversion use well-known host to network (big endian) naming + #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + #define TU_BYTE_ORDER TU_LITTLE_ENDIAN + #else + #define TU_BYTE_ORDER TU_BIG_ENDIAN + #endif + + #define TU_BSWAP16(u16) (__builtin_bswap16(u16)) + #define TU_BSWAP32(u32) (__builtin_bswap32(u32)) + + #ifndef __ARMCC_VERSION + // List of obsolete callback function that is renamed and should not be defined. + // Put it here since only gcc support this pragma + #pragma GCC poison tud_vendor_control_request_cb + #endif + +#elif defined(__TI_COMPILER_VERSION__) + #define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes))) + #define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name))) + #define TU_ATTR_PACKED __attribute__ ((packed)) + #define TU_ATTR_WEAK __attribute__ ((weak)) + #define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline)) + #define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used + #define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused + #define TU_ATTR_USED __attribute__ ((used)) + #define TU_ATTR_FALLTHROUGH __attribute__((fallthrough)) + + #define TU_ATTR_PACKED_BEGIN + #define TU_ATTR_PACKED_END + #define TU_ATTR_BIT_FIELD_ORDER_BEGIN + #define TU_ATTR_BIT_FIELD_ORDER_END + + // __BYTE_ORDER is defined in the TI ARM compiler, but not MSP430 (which is little endian) + #if ((__BYTE_ORDER__) == (__ORDER_LITTLE_ENDIAN__)) || defined(__MSP430__) + #define TU_BYTE_ORDER TU_LITTLE_ENDIAN + #else + #define TU_BYTE_ORDER TU_BIG_ENDIAN + #endif + + #define TU_BSWAP16(u16) (__builtin_bswap16(u16)) + #define TU_BSWAP32(u32) (__builtin_bswap32(u32)) + +#elif defined(__ICCARM__) + #include + #define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes))) + #define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name))) + #define TU_ATTR_PACKED __attribute__ ((packed)) + #define TU_ATTR_WEAK __attribute__ ((weak)) + #define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline)) + #define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used + #define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused + #define TU_ATTR_USED __attribute__ ((used)) // Function/Variable is meant to be used + #define TU_ATTR_FALLTHROUGH __attribute__((fallthrough)) + + #define TU_ATTR_PACKED_BEGIN + #define TU_ATTR_PACKED_END + #define TU_ATTR_BIT_FIELD_ORDER_BEGIN + #define TU_ATTR_BIT_FIELD_ORDER_END + + // Endian conversion use well-known host to network (big endian) naming + #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + #define TU_BYTE_ORDER TU_LITTLE_ENDIAN + #else + #define TU_BYTE_ORDER TU_BIG_ENDIAN + #endif + + #define TU_BSWAP16(u16) (__iar_builtin_REV16(u16)) + #define TU_BSWAP32(u32) (__iar_builtin_REV(u32)) + +#elif defined(__CCRX__) + #define TU_ATTR_ALIGNED(Bytes) + #define TU_ATTR_SECTION(sec_name) + #define TU_ATTR_PACKED + #define TU_ATTR_WEAK + #define TU_ATTR_ALWAYS_INLINE + #define TU_ATTR_DEPRECATED(mess) + #define TU_ATTR_UNUSED + #define TU_ATTR_USED + #define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */ + + #define TU_ATTR_PACKED_BEGIN _Pragma("pack") + #define TU_ATTR_PACKED_END _Pragma("packoption") + #define TU_ATTR_BIT_FIELD_ORDER_BEGIN _Pragma("bit_order right") + #define TU_ATTR_BIT_FIELD_ORDER_END _Pragma("bit_order") + + // Endian conversion use well-known host to network (big endian) naming + #if defined(__LIT) + #define TU_BYTE_ORDER TU_LITTLE_ENDIAN + #else + #define TU_BYTE_ORDER TU_BIG_ENDIAN + #endif + + #define TU_BSWAP16(u16) ((unsigned short)_builtin_revw((unsigned long)u16)) + #define TU_BSWAP32(u32) (_builtin_revl(u32)) + +#else + #error "Compiler attribute porting is required" +#endif + + +#if (TU_BYTE_ORDER == TU_LITTLE_ENDIAN) + + #define tu_htons(u16) (TU_BSWAP16(u16)) + #define tu_ntohs(u16) (TU_BSWAP16(u16)) + + #define tu_htonl(u32) (TU_BSWAP32(u32)) + #define tu_ntohl(u32) (TU_BSWAP32(u32)) + + #define tu_htole16(u16) (u16) + #define tu_le16toh(u16) (u16) + + #define tu_htole32(u32) (u32) + #define tu_le32toh(u32) (u32) + +#elif (TU_BYTE_ORDER == TU_BIG_ENDIAN) + + #define tu_htons(u16) (u16) + #define tu_ntohs(u16) (u16) + + #define tu_htonl(u32) (u32) + #define tu_ntohl(u32) (u32) + + #define tu_htole16(u16) (TU_BSWAP16(u16)) + #define tu_le16toh(u16) (TU_BSWAP16(u16)) + + #define tu_htole32(u32) (TU_BSWAP32(u32)) + #define tu_le32toh(u32) (TU_BSWAP32(u32)) + +#else + #error Byte order is undefined +#endif + +#endif /* _TUSB_COMPILER_H_ */ + +/// @} diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_debug.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_debug.h new file mode 100644 index 0000000000..82f6820438 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_debug.h @@ -0,0 +1,178 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022, Ha Thach (tinyusb.org) + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_DEBUG_H_ +#define _TUSB_DEBUG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Debug +//--------------------------------------------------------------------+ + +// CFG_TUSB_DEBUG for debugging +// 0 : no debug +// 1 : print error +// 2 : print warning +// 3 : print info +#if CFG_TUSB_DEBUG + +// Enum to String for debugging purposes +#if CFG_TUSB_DEBUG >= 2 +extern char const* const tu_str_speed[]; +extern char const* const tu_str_std_request[]; +#endif + +void tu_print_mem(void const *buf, uint32_t count, uint8_t indent); + +#ifdef CFG_TUSB_DEBUG_PRINTF + extern int CFG_TUSB_DEBUG_PRINTF(const char *format, ...); + #define tu_printf CFG_TUSB_DEBUG_PRINTF +#else + #define tu_printf printf +#endif + +static inline void tu_print_arr(uint8_t const* buf, uint32_t bufsize) +{ + for(uint32_t i=0; i= 2 + #define TU_LOG2 TU_LOG1 + #define TU_LOG2_MEM TU_LOG1_MEM + #define TU_LOG2_ARR TU_LOG1_ARR + #define TU_LOG2_PTR TU_LOG1_PTR + #define TU_LOG2_INT TU_LOG1_INT + #define TU_LOG2_HEX TU_LOG1_HEX +#endif + +// Log Level 3: Info +#if CFG_TUSB_DEBUG >= 3 + #define TU_LOG3 TU_LOG1 + #define TU_LOG3_MEM TU_LOG1_MEM + #define TU_LOG3_ARR TU_LOG1_ARR + #define TU_LOG3_PTR TU_LOG1_PTR + #define TU_LOG3_INT TU_LOG1_INT + #define TU_LOG3_HEX TU_LOG1_HEX +#endif + +typedef struct +{ + uint32_t key; + const char* data; +} tu_lookup_entry_t; + +typedef struct +{ + uint16_t count; + tu_lookup_entry_t const* items; +} tu_lookup_table_t; + +static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint32_t key) +{ + tu_static char not_found[11]; + + for(uint16_t i=0; icount; i++) + { + if (p_table->items[i].key == key) return p_table->items[i].data; + } + + // not found return the key value in hex + snprintf(not_found, sizeof(not_found), "0x%08lX", (unsigned long) key); + + return not_found; +} + +#endif // CFG_TUSB_DEBUG + +#ifndef TU_LOG + #define TU_LOG(n, ...) + #define TU_LOG_MEM(n, ...) + #define TU_LOG_PTR(n, ...) + #define TU_LOG_INT(n, ...) + #define TU_LOG_HEX(n, ...) + #define TU_LOG_LOCATION() + #define TU_LOG_FAILED() +#endif + +// TODO replace all TU_LOGn with TU_LOG(n) + +#define TU_LOG0(...) +#define TU_LOG0_MEM(...) +#define TU_LOG0_PTR(...) +#define TU_LOG0_INT(...) +#define TU_LOG0_HEX(...) + +#ifndef TU_LOG1 + #define TU_LOG1(...) + #define TU_LOG1_MEM(...) + #define TU_LOG1_PTR(...) + #define TU_LOG1_INT(...) + #define TU_LOG1_HEX(...) +#endif + +#ifndef TU_LOG2 + #define TU_LOG2(...) + #define TU_LOG2_MEM(...) + #define TU_LOG2_PTR(...) + #define TU_LOG2_INT(...) + #define TU_LOG2_HEX(...) +#endif + +#ifndef TU_LOG3 + #define TU_LOG3(...) + #define TU_LOG3_MEM(...) + #define TU_LOG3_PTR(...) + #define TU_LOG3_INT(...) + #define TU_LOG3_HEX(...) +#endif + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_DEBUG_H_ */ diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_fifo.c b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_fifo.c new file mode 100644 index 0000000000..a52c92267d --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_fifo.c @@ -0,0 +1,1065 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * Copyright (c) 2020 Reinhard Panhuber - rework to unmasked pointers + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +#include "osal/osal.h" +#include "tusb_fifo.h" + +#define TU_FIFO_DBG 0 + +// Suppress IAR warning +// Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this statement +#if defined(__ICCARM__) +#pragma diag_suppress = Pa082 +#endif + +#if OSAL_MUTEX_REQUIRED + +TU_ATTR_ALWAYS_INLINE static inline void _ff_lock(osal_mutex_t mutex) +{ + if (mutex) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER); +} + +TU_ATTR_ALWAYS_INLINE static inline void _ff_unlock(osal_mutex_t mutex) +{ + if (mutex) osal_mutex_unlock(mutex); +} + +#else + +#define _ff_lock(_mutex) +#define _ff_unlock(_mutex) + +#endif + +/** \enum tu_fifo_copy_mode_t + * \brief Write modes intended to allow special read and write functions to be able to + * copy data to and from USB hardware FIFOs as needed for e.g. STM32s and others + */ +typedef enum +{ + TU_FIFO_COPY_INC, ///< Copy from/to an increasing source/destination address - default mode + TU_FIFO_COPY_CST_FULL_WORDS, ///< Copy from/to a constant source/destination address - required for e.g. STM32 to write into USB hardware FIFO +} tu_fifo_copy_mode_t; + +bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable) +{ + // Limit index space to 2*depth - this allows for a fast "modulo" calculation + // but limits the maximum depth to 2^16/2 = 2^15 and buffer overflows are detectable + // only if overflow happens once (important for unsupervised DMA applications) + if (depth > 0x8000) return false; + + _ff_lock(f->mutex_wr); + _ff_lock(f->mutex_rd); + + f->buffer = (uint8_t*) buffer; + f->depth = depth; + f->item_size = (uint16_t) (item_size & 0x7FFF); + f->overwritable = overwritable; + f->rd_idx = 0; + f->wr_idx = 0; + + _ff_unlock(f->mutex_wr); + _ff_unlock(f->mutex_rd); + + return true; +} + +//--------------------------------------------------------------------+ +// Pull & Push +//--------------------------------------------------------------------+ + +// Intended to be used to read from hardware USB FIFO in e.g. STM32 where all data is read from a constant address +// Code adapted from dcd_synopsys.c +// TODO generalize with configurable 1 byte or 4 byte each read +static void _ff_push_const_addr(uint8_t * ff_buf, const void * app_buf, uint16_t len) +{ + volatile const uint32_t * reg_rx = (volatile const uint32_t *) app_buf; + + // Reading full available 32 bit words from const app address + uint16_t full_words = len >> 2; + while(full_words--) + { + tu_unaligned_write32(ff_buf, *reg_rx); + ff_buf += 4; + } + + // Read the remaining 1-3 bytes from const app address + uint8_t const bytes_rem = len & 0x03; + if ( bytes_rem ) + { + uint32_t tmp32 = *reg_rx; + memcpy(ff_buf, &tmp32, bytes_rem); + } +} + +// Intended to be used to write to hardware USB FIFO in e.g. STM32 +// where all data is written to a constant address in full word copies +static void _ff_pull_const_addr(void * app_buf, const uint8_t * ff_buf, uint16_t len) +{ + volatile uint32_t * reg_tx = (volatile uint32_t *) app_buf; + + // Write full available 32 bit words to const address + uint16_t full_words = len >> 2; + while(full_words--) + { + *reg_tx = tu_unaligned_read32(ff_buf); + ff_buf += 4; + } + + // Write the remaining 1-3 bytes into const address + uint8_t const bytes_rem = len & 0x03; + if ( bytes_rem ) + { + uint32_t tmp32 = 0; + memcpy(&tmp32, ff_buf, bytes_rem); + + *reg_tx = tmp32; + } +} + +// send one item to fifo WITHOUT updating write pointer +static inline void _ff_push(tu_fifo_t* f, void const * app_buf, uint16_t rel) +{ + memcpy(f->buffer + (rel * f->item_size), app_buf, f->item_size); +} + +// send n items to fifo WITHOUT updating write pointer +static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t wr_ptr, tu_fifo_copy_mode_t copy_mode) +{ + uint16_t const lin_count = f->depth - wr_ptr; + uint16_t const wrap_count = n - lin_count; + + uint16_t lin_bytes = lin_count * f->item_size; + uint16_t wrap_bytes = wrap_count * f->item_size; + + // current buffer of fifo + uint8_t* ff_buf = f->buffer + (wr_ptr * f->item_size); + + switch (copy_mode) + { + case TU_FIFO_COPY_INC: + if(n <= lin_count) + { + // Linear only + memcpy(ff_buf, app_buf, n*f->item_size); + } + else + { + // Wrap around + + // Write data to linear part of buffer + memcpy(ff_buf, app_buf, lin_bytes); + + // Write data wrapped around + // TU_ASSERT(nWrap_bytes <= f->depth, ); + memcpy(f->buffer, ((uint8_t const*) app_buf) + lin_bytes, wrap_bytes); + } + break; + + case TU_FIFO_COPY_CST_FULL_WORDS: + // Intended for hardware buffers from which it can be read word by word only + if(n <= lin_count) + { + // Linear only + _ff_push_const_addr(ff_buf, app_buf, n*f->item_size); + } + else + { + // Wrap around case + + // Write full words to linear part of buffer + uint16_t nLin_4n_bytes = lin_bytes & 0xFFFC; + _ff_push_const_addr(ff_buf, app_buf, nLin_4n_bytes); + ff_buf += nLin_4n_bytes; + + // There could be odd 1-3 bytes before the wrap-around boundary + uint8_t rem = lin_bytes & 0x03; + if (rem > 0) + { + volatile const uint32_t * rx_fifo = (volatile const uint32_t *) app_buf; + + uint8_t remrem = (uint8_t) tu_min16(wrap_bytes, 4-rem); + wrap_bytes -= remrem; + + uint32_t tmp32 = *rx_fifo; + uint8_t * src_u8 = ((uint8_t *) &tmp32); + + // Write 1-3 bytes before wrapped boundary + while(rem--) *ff_buf++ = *src_u8++; + + // Read more bytes to beginning to complete a word + ff_buf = f->buffer; + while(remrem--) *ff_buf++ = *src_u8++; + } + else + { + ff_buf = f->buffer; // wrap around to beginning + } + + // Write data wrapped part + if (wrap_bytes > 0) _ff_push_const_addr(ff_buf, app_buf, wrap_bytes); + } + break; + } +} + +// get one item from fifo WITHOUT updating read pointer +static inline void _ff_pull(tu_fifo_t* f, void * app_buf, uint16_t rel) +{ + memcpy(app_buf, f->buffer + (rel * f->item_size), f->item_size); +} + +// get n items from fifo WITHOUT updating read pointer +static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rd_ptr, tu_fifo_copy_mode_t copy_mode) +{ + uint16_t const lin_count = f->depth - rd_ptr; + uint16_t const wrap_count = n - lin_count; // only used if wrapped + + uint16_t lin_bytes = lin_count * f->item_size; + uint16_t wrap_bytes = wrap_count * f->item_size; + + // current buffer of fifo + uint8_t* ff_buf = f->buffer + (rd_ptr * f->item_size); + + switch (copy_mode) + { + case TU_FIFO_COPY_INC: + if ( n <= lin_count ) + { + // Linear only + memcpy(app_buf, ff_buf, n*f->item_size); + } + else + { + // Wrap around + + // Read data from linear part of buffer + memcpy(app_buf, ff_buf, lin_bytes); + + // Read data wrapped part + memcpy((uint8_t*) app_buf + lin_bytes, f->buffer, wrap_bytes); + } + break; + + case TU_FIFO_COPY_CST_FULL_WORDS: + if ( n <= lin_count ) + { + // Linear only + _ff_pull_const_addr(app_buf, ff_buf, n*f->item_size); + } + else + { + // Wrap around case + + // Read full words from linear part of buffer + uint16_t lin_4n_bytes = lin_bytes & 0xFFFC; + _ff_pull_const_addr(app_buf, ff_buf, lin_4n_bytes); + ff_buf += lin_4n_bytes; + + // There could be odd 1-3 bytes before the wrap-around boundary + uint8_t rem = lin_bytes & 0x03; + if (rem > 0) + { + volatile uint32_t * reg_tx = (volatile uint32_t *) app_buf; + + uint8_t remrem = (uint8_t) tu_min16(wrap_bytes, 4-rem); + wrap_bytes -= remrem; + + uint32_t tmp32=0; + uint8_t * dst_u8 = (uint8_t *)&tmp32; + + // Read 1-3 bytes before wrapped boundary + while(rem--) *dst_u8++ = *ff_buf++; + + // Read more bytes from beginning to complete a word + ff_buf = f->buffer; + while(remrem--) *dst_u8++ = *ff_buf++; + + *reg_tx = tmp32; + } + else + { + ff_buf = f->buffer; // wrap around to beginning + } + + // Read data wrapped part + if (wrap_bytes > 0) _ff_pull_const_addr(app_buf, ff_buf, wrap_bytes); + } + break; + + default: break; + } +} + +//--------------------------------------------------------------------+ +// Helper +//--------------------------------------------------------------------+ + +// return only the index difference and as such can be used to determine an overflow i.e overflowable count +TU_ATTR_ALWAYS_INLINE static inline +uint16_t _ff_count(uint16_t depth, uint16_t wr_idx, uint16_t rd_idx) +{ + // In case we have non-power of two depth we need a further modification + if (wr_idx >= rd_idx) + { + return (uint16_t) (wr_idx - rd_idx); + } else + { + return (uint16_t) (2*depth - (rd_idx - wr_idx)); + } +} + +// return remaining slot in fifo +TU_ATTR_ALWAYS_INLINE static inline +uint16_t _ff_remaining(uint16_t depth, uint16_t wr_idx, uint16_t rd_idx) +{ + uint16_t const count = _ff_count(depth, wr_idx, rd_idx); + return (depth > count) ? (depth - count) : 0; +} + +//--------------------------------------------------------------------+ +// Index Helper +//--------------------------------------------------------------------+ + +// Advance an absolute index +// "absolute" index is only in the range of [0..2*depth) +static uint16_t advance_index(uint16_t depth, uint16_t idx, uint16_t offset) +{ + // We limit the index space of p such that a correct wrap around happens + // Check for a wrap around or if we are in unused index space - This has to be checked first!! + // We are exploiting the wrap around to the correct index + uint16_t new_idx = (uint16_t) (idx + offset); + if ( (idx > new_idx) || (new_idx >= 2*depth) ) + { + uint16_t const non_used_index_space = (uint16_t) (UINT16_MAX - (2*depth-1)); + new_idx = (uint16_t) (new_idx + non_used_index_space); + } + + return new_idx; +} + +#if 0 // not used but +// Backward an absolute index +static uint16_t backward_index(uint16_t depth, uint16_t idx, uint16_t offset) +{ + // We limit the index space of p such that a correct wrap around happens + // Check for a wrap around or if we are in unused index space - This has to be checked first!! + // We are exploiting the wrap around to the correct index + uint16_t new_idx = (uint16_t) (idx - offset); + if ( (idx < new_idx) || (new_idx >= 2*depth) ) + { + uint16_t const non_used_index_space = (uint16_t) (UINT16_MAX - (2*depth-1)); + new_idx = (uint16_t) (new_idx - non_used_index_space); + } + + return new_idx; +} +#endif + +// index to pointer, simply an modulo with minus. +TU_ATTR_ALWAYS_INLINE static inline +uint16_t idx2ptr(uint16_t depth, uint16_t idx) +{ + // Only run at most 3 times since index is limit in the range of [0..2*depth) + while ( idx >= depth ) idx -= depth; + return idx; +} + +// Works on local copies of w +// When an overwritable fifo is overflowed, rd_idx will be re-index so that it forms +// an full fifo i.e _ff_count() = depth +TU_ATTR_ALWAYS_INLINE static inline +uint16_t _ff_correct_read_index(tu_fifo_t* f, uint16_t wr_idx) +{ + uint16_t rd_idx; + if ( wr_idx >= f->depth ) + { + rd_idx = wr_idx - f->depth; + }else + { + rd_idx = wr_idx + f->depth; + } + + f->rd_idx = rd_idx; + + return rd_idx; +} + +// Works on local copies of w and r +// Must be protected by mutexes since in case of an overflow read pointer gets modified +static bool _tu_fifo_peek(tu_fifo_t* f, void * p_buffer, uint16_t wr_idx, uint16_t rd_idx) +{ + uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx); + + // nothing to peek + if ( cnt == 0 ) return false; + + // Check overflow and correct if required + if ( cnt > f->depth ) + { + rd_idx = _ff_correct_read_index(f, wr_idx); + cnt = f->depth; + } + + uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); + + // Peek data + _ff_pull(f, p_buffer, rd_ptr); + + return true; +} + +// Works on local copies of w and r +// Must be protected by mutexes since in case of an overflow read pointer gets modified +static uint16_t _tu_fifo_peek_n(tu_fifo_t* f, void * p_buffer, uint16_t n, uint16_t wr_idx, uint16_t rd_idx, tu_fifo_copy_mode_t copy_mode) +{ + uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx); + + // nothing to peek + if ( cnt == 0 ) return 0; + + // Check overflow and correct if required + if ( cnt > f->depth ) + { + rd_idx = _ff_correct_read_index(f, wr_idx); + cnt = f->depth; + } + + // Check if we can read something at and after offset - if too less is available we read what remains + if ( cnt < n ) n = cnt; + + uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); + + // Peek data + _ff_pull_n(f, p_buffer, n, rd_ptr, copy_mode); + + return n; +} + +static uint16_t _tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n, tu_fifo_copy_mode_t copy_mode) +{ + if ( n == 0 ) return 0; + + _ff_lock(f->mutex_wr); + + uint16_t wr_idx = f->wr_idx; + uint16_t rd_idx = f->rd_idx; + + uint8_t const* buf8 = (uint8_t const*) data; + + TU_LOG(TU_FIFO_DBG, "rd = %3u, wr = %3u, count = %3u, remain = %3u, n = %3u: ", + rd_idx, wr_idx, _ff_count(f->depth, wr_idx, rd_idx), _ff_remaining(f->depth, wr_idx, rd_idx), n); + + if ( !f->overwritable ) + { + // limit up to full + uint16_t const remain = _ff_remaining(f->depth, wr_idx, rd_idx); + n = tu_min16(n, remain); + } + else + { + // In over-writable mode, fifo_write() is allowed even when fifo is full. In such case, + // oldest data in fifo i.e at read pointer data will be overwritten + // Note: we can modify read buffer contents but we must not modify the read index itself within a write function! + // Since it would end up in a race condition with read functions! + if ( n >= f->depth ) + { + // Only copy last part + if ( copy_mode == TU_FIFO_COPY_INC ) + { + buf8 += (n - f->depth) * f->item_size; + }else + { + // TODO should read from hw fifo to discard data, however reading an odd number could + // accidentally discard data. + } + + n = f->depth; + + // We start writing at the read pointer's position since we fill the whole buffer + wr_idx = rd_idx; + } + else + { + uint16_t const overflowable_count = _ff_count(f->depth, wr_idx, rd_idx); + if (overflowable_count + n >= 2*f->depth) + { + // Double overflowed + // Index is bigger than the allowed range [0,2*depth) + // re-position write index to have a full fifo after pushed + wr_idx = advance_index(f->depth, rd_idx, f->depth - n); + + // TODO we should also shift out n bytes from read index since we avoid changing rd index !! + // However memmove() is expensive due to actual copying + wrapping consideration. + // Also race condition could happen anyway if read() is invoke while moving result in corrupted memory + // currently deliberately not implemented --> result in incorrect data read back + }else + { + // normal + single overflowed: + // Index is in the range of [0,2*depth) and thus detect and recoverable. Recovering is handled in read() + // Therefore we just increase write index + // we will correct (re-position) read index later on in fifo_read() function + } + } + } + + if (n) + { + uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); + + TU_LOG(TU_FIFO_DBG, "actual_n = %u, wr_ptr = %u", n, wr_ptr); + + // Write data + _ff_push_n(f, buf8, n, wr_ptr, copy_mode); + + // Advance index + f->wr_idx = advance_index(f->depth, wr_idx, n); + + TU_LOG(TU_FIFO_DBG, "\tnew_wr = %u\n", f->wr_idx); + } + + _ff_unlock(f->mutex_wr); + + return n; +} + +static uint16_t _tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n, tu_fifo_copy_mode_t copy_mode) +{ + _ff_lock(f->mutex_rd); + + // Peek the data + // f->rd_idx might get modified in case of an overflow so we can not use a local variable + n = _tu_fifo_peek_n(f, buffer, n, f->wr_idx, f->rd_idx, copy_mode); + + // Advance read pointer + f->rd_idx = advance_index(f->depth, f->rd_idx, n); + + _ff_unlock(f->mutex_rd); + return n; +} + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + +/******************************************************************************/ +/*! + @brief Get number of items in FIFO. + + As this function only reads the read and write pointers once, this function is + reentrant and thus thread and ISR save without any mutexes. In case an + overflow occurred, this function return f.depth at maximum. Overflows are + checked and corrected for in the read functions! + + @param[in] f + Pointer to the FIFO buffer to manipulate + + @returns Number of items in FIFO + */ +/******************************************************************************/ +uint16_t tu_fifo_count(tu_fifo_t* f) +{ + return tu_min16(_ff_count(f->depth, f->wr_idx, f->rd_idx), f->depth); +} + +/******************************************************************************/ +/*! + @brief Check if FIFO is empty. + + As this function only reads the read and write pointers once, this function is + reentrant and thus thread and ISR save without any mutexes. + + @param[in] f + Pointer to the FIFO buffer to manipulate + + @returns Number of items in FIFO + */ +/******************************************************************************/ +bool tu_fifo_empty(tu_fifo_t* f) +{ + return f->wr_idx == f->rd_idx; +} + +/******************************************************************************/ +/*! + @brief Check if FIFO is full. + + As this function only reads the read and write pointers once, this function is + reentrant and thus thread and ISR save without any mutexes. + + @param[in] f + Pointer to the FIFO buffer to manipulate + + @returns Number of items in FIFO + */ +/******************************************************************************/ +bool tu_fifo_full(tu_fifo_t* f) +{ + return _ff_count(f->depth, f->wr_idx, f->rd_idx) >= f->depth; +} + +/******************************************************************************/ +/*! + @brief Get remaining space in FIFO. + + As this function only reads the read and write pointers once, this function is + reentrant and thus thread and ISR save without any mutexes. + + @param[in] f + Pointer to the FIFO buffer to manipulate + + @returns Number of items in FIFO + */ +/******************************************************************************/ +uint16_t tu_fifo_remaining(tu_fifo_t* f) +{ + return _ff_remaining(f->depth, f->wr_idx, f->rd_idx); +} + +/******************************************************************************/ +/*! + @brief Check if overflow happened. + + BE AWARE - THIS FUNCTION MIGHT NOT GIVE A CORRECT ANSWERE IN CASE WRITE POINTER "OVERFLOWS" + Only one overflow is allowed for this function to work e.g. if depth = 100, you must not + write more than 2*depth-1 items in one rush without updating write pointer. Otherwise + write pointer wraps and your pointer states are messed up. This can only happen if you + use DMAs, write functions do not allow such an error. Avoid such nasty things! + + All reading functions (read, peek) check for overflows and correct read pointer on their own such + that latest items are read. + If required (e.g. for DMA use) you can also correct the read pointer by + tu_fifo_correct_read_pointer(). + + @param[in] f + Pointer to the FIFO buffer to manipulate + + @returns True if overflow happened + */ +/******************************************************************************/ +bool tu_fifo_overflowed(tu_fifo_t* f) +{ + return _ff_count(f->depth, f->wr_idx, f->rd_idx) > f->depth; +} + +// Only use in case tu_fifo_overflow() returned true! +void tu_fifo_correct_read_pointer(tu_fifo_t* f) +{ + _ff_lock(f->mutex_rd); + _ff_correct_read_index(f, f->wr_idx); + _ff_unlock(f->mutex_rd); +} + +/******************************************************************************/ +/*! + @brief Read one element out of the buffer. + + This function will return the element located at the array index of the + read pointer, and then increment the read pointer index. + This function checks for an overflow and corrects read pointer if required. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] buffer + Pointer to the place holder for data read from the buffer + + @returns TRUE if the queue is not empty + */ +/******************************************************************************/ +bool tu_fifo_read(tu_fifo_t* f, void * buffer) +{ + _ff_lock(f->mutex_rd); + + // Peek the data + // f->rd_idx might get modified in case of an overflow so we can not use a local variable + bool ret = _tu_fifo_peek(f, buffer, f->wr_idx, f->rd_idx); + + // Advance pointer + f->rd_idx = advance_index(f->depth, f->rd_idx, ret); + + _ff_unlock(f->mutex_rd); + return ret; +} + +/******************************************************************************/ +/*! + @brief This function will read n elements from the array index specified by + the read pointer and increment the read index. + This function checks for an overflow and corrects read pointer if required. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] buffer + The pointer to data location + @param[in] n + Number of element that buffer can afford + + @returns number of items read from the FIFO + */ +/******************************************************************************/ +uint16_t tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n) +{ + return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_INC); +} + +uint16_t tu_fifo_read_n_const_addr_full_words(tu_fifo_t* f, void * buffer, uint16_t n) +{ + return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_CST_FULL_WORDS); +} + +/******************************************************************************/ +/*! + @brief Read one item without removing it from the FIFO. + This function checks for an overflow and corrects read pointer if required. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] p_buffer + Pointer to the place holder for data read from the buffer + + @returns TRUE if the queue is not empty + */ +/******************************************************************************/ +bool tu_fifo_peek(tu_fifo_t* f, void * p_buffer) +{ + _ff_lock(f->mutex_rd); + bool ret = _tu_fifo_peek(f, p_buffer, f->wr_idx, f->rd_idx); + _ff_unlock(f->mutex_rd); + return ret; +} + +/******************************************************************************/ +/*! + @brief Read n items without removing it from the FIFO + This function checks for an overflow and corrects read pointer if required. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] p_buffer + Pointer to the place holder for data read from the buffer + @param[in] n + Number of items to peek + + @returns Number of bytes written to p_buffer + */ +/******************************************************************************/ +uint16_t tu_fifo_peek_n(tu_fifo_t* f, void * p_buffer, uint16_t n) +{ + _ff_lock(f->mutex_rd); + uint16_t ret = _tu_fifo_peek_n(f, p_buffer, n, f->wr_idx, f->rd_idx, TU_FIFO_COPY_INC); + _ff_unlock(f->mutex_rd); + return ret; +} + +/******************************************************************************/ +/*! + @brief Write one element into the buffer. + + This function will write one element into the array index specified by + the write pointer and increment the write index. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] data + The byte to add to the FIFO + + @returns TRUE if the data was written to the FIFO (overwrittable + FIFO will always return TRUE) + */ +/******************************************************************************/ +bool tu_fifo_write(tu_fifo_t* f, const void * data) +{ + _ff_lock(f->mutex_wr); + + bool ret; + uint16_t const wr_idx = f->wr_idx; + + if ( tu_fifo_full(f) && !f->overwritable ) + { + ret = false; + }else + { + uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); + + // Write data + _ff_push(f, data, wr_ptr); + + // Advance pointer + f->wr_idx = advance_index(f->depth, wr_idx, 1); + + ret = true; + } + + _ff_unlock(f->mutex_wr); + + return ret; +} + +/******************************************************************************/ +/*! + @brief This function will write n elements into the array index specified by + the write pointer and increment the write index. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] data + The pointer to data to add to the FIFO + @param[in] count + Number of element + @return Number of written elements + */ +/******************************************************************************/ +uint16_t tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n) +{ + return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_INC); +} + +/******************************************************************************/ +/*! + @brief This function will write n elements into the array index specified by + the write pointer and increment the write index. The source address will + not be incremented which is useful for reading from registers. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] data + The pointer to data to add to the FIFO + @param[in] count + Number of element + @return Number of written elements + */ +/******************************************************************************/ +uint16_t tu_fifo_write_n_const_addr_full_words(tu_fifo_t* f, const void * data, uint16_t n) +{ + return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_CST_FULL_WORDS); +} + +/******************************************************************************/ +/*! + @brief Clear the fifo read and write pointers + + @param[in] f + Pointer to the FIFO buffer to manipulate + */ +/******************************************************************************/ +bool tu_fifo_clear(tu_fifo_t *f) +{ + _ff_lock(f->mutex_wr); + _ff_lock(f->mutex_rd); + + f->rd_idx = 0; + f->wr_idx = 0; + + _ff_unlock(f->mutex_wr); + _ff_unlock(f->mutex_rd); + return true; +} + +/******************************************************************************/ +/*! + @brief Change the fifo mode to overwritable or not overwritable + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] overwritable + Overwritable mode the fifo is set to + */ +/******************************************************************************/ +bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable) +{ + _ff_lock(f->mutex_wr); + _ff_lock(f->mutex_rd); + + f->overwritable = overwritable; + + _ff_unlock(f->mutex_wr); + _ff_unlock(f->mutex_rd); + + return true; +} + +/******************************************************************************/ +/*! + @brief Advance write pointer - intended to be used in combination with DMA. + It is possible to fill the FIFO by use of a DMA in circular mode. Within + DMA ISRs you may update the write pointer to be able to read from the FIFO. + As long as the DMA is the only process writing into the FIFO this is safe + to use. + + USE WITH CARE - WE DO NOT CONDUCT SAFETY CHECKS HERE! + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] n + Number of items the write pointer moves forward + */ +/******************************************************************************/ +void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n) +{ + f->wr_idx = advance_index(f->depth, f->wr_idx, n); +} + +/******************************************************************************/ +/*! + @brief Advance read pointer - intended to be used in combination with DMA. + It is possible to read from the FIFO by use of a DMA in linear mode. Within + DMA ISRs you may update the read pointer to be able to again write into the + FIFO. As long as the DMA is the only process reading from the FIFO this is + safe to use. + + USE WITH CARE - WE DO NOT CONDUCT SAFETY CHECKS HERE! + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] n + Number of items the read pointer moves forward + */ +/******************************************************************************/ +void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n) +{ + f->rd_idx = advance_index(f->depth, f->rd_idx, n); +} + +/******************************************************************************/ +/*! + @brief Get read info + + Returns the length and pointer from which bytes can be read in a linear manner. + This is of major interest for DMA transmissions. If returned length is zero the + corresponding pointer is invalid. + The read pointer does NOT get advanced, use tu_fifo_advance_read_pointer() to + do so! + @param[in] f + Pointer to FIFO + @param[out] *info + Pointer to struct which holds the desired infos + */ +/******************************************************************************/ +void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) +{ + // Operate on temporary values in case they change in between + uint16_t wr_idx = f->wr_idx; + uint16_t rd_idx = f->rd_idx; + + uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx); + + // Check overflow and correct if required - may happen in case a DMA wrote too fast + if (cnt > f->depth) + { + _ff_lock(f->mutex_rd); + rd_idx = _ff_correct_read_index(f, wr_idx); + _ff_unlock(f->mutex_rd); + + cnt = f->depth; + } + + // Check if fifo is empty + if (cnt == 0) + { + info->len_lin = 0; + info->len_wrap = 0; + info->ptr_lin = NULL; + info->ptr_wrap = NULL; + return; + } + + // Get relative pointers + uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); + uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); + + // Copy pointer to buffer to start reading from + info->ptr_lin = &f->buffer[rd_ptr]; + + // Check if there is a wrap around necessary + if (wr_ptr > rd_ptr) + { + // Non wrapping case + info->len_lin = cnt; + + info->len_wrap = 0; + info->ptr_wrap = NULL; + } + else + { + info->len_lin = f->depth - rd_ptr; // Also the case if FIFO was full + + info->len_wrap = cnt - info->len_lin; + info->ptr_wrap = f->buffer; + } +} + +/******************************************************************************/ +/*! + @brief Get linear write info + + Returns the length and pointer to which bytes can be written into FIFO in a linear manner. + This is of major interest for DMA transmissions not using circular mode. If a returned length is zero the + corresponding pointer is invalid. The returned lengths summed up are the currently free space in the FIFO. + The write pointer does NOT get advanced, use tu_fifo_advance_write_pointer() to do so! + TAKE CARE TO NOT OVERFLOW THE BUFFER MORE THAN TWO TIMES THE FIFO DEPTH - IT CAN NOT RECOVERE OTHERWISE! + @param[in] f + Pointer to FIFO + @param[out] *info + Pointer to struct which holds the desired infos + */ +/******************************************************************************/ +void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) +{ + uint16_t wr_idx = f->wr_idx; + uint16_t rd_idx = f->rd_idx; + uint16_t remain = _ff_remaining(f->depth, wr_idx, rd_idx); + + if (remain == 0) + { + info->len_lin = 0; + info->len_wrap = 0; + info->ptr_lin = NULL; + info->ptr_wrap = NULL; + return; + } + + // Get relative pointers + uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); + uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); + + // Copy pointer to buffer to start writing to + info->ptr_lin = &f->buffer[wr_ptr]; + + if (wr_ptr < rd_ptr) + { + // Non wrapping case + info->len_lin = rd_ptr-wr_ptr; + info->len_wrap = 0; + info->ptr_wrap = NULL; + } + else + { + info->len_lin = f->depth - wr_ptr; + info->len_wrap = remain - info->len_lin; // Remaining length - n already was limited to remain or FIFO depth + info->ptr_wrap = f->buffer; // Always start of buffer + } +} diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_fifo.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_fifo.h new file mode 100644 index 0000000000..2f60ec2f49 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_fifo.h @@ -0,0 +1,206 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * Copyright (c) 2020 Reinhard Panhuber - rework to unmasked pointers + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_FIFO_H_ +#define _TUSB_FIFO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// Due to the use of unmasked pointers, this FIFO does not suffer from losing +// one item slice. Furthermore, write and read operations are completely +// decoupled as write and read functions do not modify a common state. Henceforth, +// writing or reading from the FIFO within an ISR is safe as long as no other +// process (thread or ISR) interferes. +// Also, this FIFO is ready to be used in combination with a DMA as the write and +// read pointers can be updated from within a DMA ISR. Overflows are detectable +// within a certain number (see tu_fifo_overflow()). + +#include "common/tusb_common.h" +#include "osal/osal.h" + +// mutex is only needed for RTOS +// for OS None, we don't get preempted +#define CFG_FIFO_MUTEX OSAL_MUTEX_REQUIRED + +/* Write/Read index is always in the range of: + * 0 .. 2*depth-1 + * The extra window allow us to determine the fifo state of empty or full with only 2 indices + * Following are examples with depth = 3 + * + * - empty: W = R + * | + * ------------------------- + * | 0 | RW| 2 | 3 | 4 | 5 | + * + * - full 1: W > R + * | + * ------------------------- + * | 0 | R | 2 | 3 | W | 5 | + * + * - full 2: W < R + * | + * ------------------------- + * | 0 | 1 | W | 3 | 4 | R | + * + * - Number of items in the fifo can be determined in either cases: + * - case W >= R: Count = W - R + * - case W < R: Count = 2*depth - (R - W) + * + * In non-overwritable mode, computed Count (in above 2 cases) is at most equal to depth. + * However, in over-writable mode, write index can be repeatedly increased and count can be + * temporarily larger than depth (overflowed condition) e.g + * + * - Overflowed 1: write(3), write(1) + * In this case we will adjust Read index when read()/peek() is called so that count = depth. + * | + * ------------------------- + * | R | 1 | 2 | 3 | W | 5 | + * + * - Double Overflowed i.e index is out of allowed range [0,2*depth) + * This occurs when we continue to write after 1st overflowed to 2nd overflowed. e.g: + * write(3), write(1), write(2) + * This must be prevented since it will cause unrecoverable state, in above example + * if not handled the fifo will be empty instead of continue-to-be full. Since we must not modify + * read index in write() function, which cause race condition. We will re-position write index so that + * after data is written it is a full fifo i.e W = depth - R + * + * re-position W = 1 before write(2) + * Note: we should also move data from mem[3] to read index as well, but deliberately skipped here + * since it is an expensive operation !!! + * | + * ------------------------- + * | R | W | 2 | 3 | 4 | 5 | + * + * perform write(2), result is still a full fifo. + * + * | + * ------------------------- + * | R | 1 | 2 | W | 4 | 5 | + + */ +typedef struct +{ + uint8_t* buffer ; // buffer pointer + uint16_t depth ; // max items + + struct TU_ATTR_PACKED { + uint16_t item_size : 15; // size of each item + bool overwritable : 1 ; // ovwerwritable when full + }; + + volatile uint16_t wr_idx ; // write index + volatile uint16_t rd_idx ; // read index + +#if OSAL_MUTEX_REQUIRED + osal_mutex_t mutex_wr; + osal_mutex_t mutex_rd; +#endif + +} tu_fifo_t; + +typedef struct +{ + uint16_t len_lin ; ///< linear length in item size + uint16_t len_wrap ; ///< wrapped length in item size + void * ptr_lin ; ///< linear part start pointer + void * ptr_wrap ; ///< wrapped part start pointer +} tu_fifo_buffer_info_t; + +#define TU_FIFO_INIT(_buffer, _depth, _type, _overwritable) \ +{ \ + .buffer = _buffer, \ + .depth = _depth, \ + .item_size = sizeof(_type), \ + .overwritable = _overwritable, \ +} + +#define TU_FIFO_DEF(_name, _depth, _type, _overwritable) \ + uint8_t _name##_buf[_depth*sizeof(_type)]; \ + tu_fifo_t _name = TU_FIFO_INIT(_name##_buf, _depth, _type, _overwritable) + + +bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable); +bool tu_fifo_clear(tu_fifo_t *f); +bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable); + +#if OSAL_MUTEX_REQUIRED +TU_ATTR_ALWAYS_INLINE static inline +void tu_fifo_config_mutex(tu_fifo_t *f, osal_mutex_t wr_mutex, osal_mutex_t rd_mutex) +{ + f->mutex_wr = wr_mutex; + f->mutex_rd = rd_mutex; +} + +#else + +#define tu_fifo_config_mutex(_f, _wr_mutex, _rd_mutex) + +#endif + +bool tu_fifo_write (tu_fifo_t* f, void const * p_data); +uint16_t tu_fifo_write_n (tu_fifo_t* f, void const * p_data, uint16_t n); +uint16_t tu_fifo_write_n_const_addr_full_words (tu_fifo_t* f, const void * data, uint16_t n); + +bool tu_fifo_read (tu_fifo_t* f, void * p_buffer); +uint16_t tu_fifo_read_n (tu_fifo_t* f, void * p_buffer, uint16_t n); +uint16_t tu_fifo_read_n_const_addr_full_words (tu_fifo_t* f, void * buffer, uint16_t n); + +bool tu_fifo_peek (tu_fifo_t* f, void * p_buffer); +uint16_t tu_fifo_peek_n (tu_fifo_t* f, void * p_buffer, uint16_t n); + +uint16_t tu_fifo_count (tu_fifo_t* f); +uint16_t tu_fifo_remaining (tu_fifo_t* f); +bool tu_fifo_empty (tu_fifo_t* f); +bool tu_fifo_full (tu_fifo_t* f); +bool tu_fifo_overflowed (tu_fifo_t* f); +void tu_fifo_correct_read_pointer (tu_fifo_t* f); + +TU_ATTR_ALWAYS_INLINE static inline +uint16_t tu_fifo_depth(tu_fifo_t* f) +{ + return f->depth; +} + +// Pointer modifications intended to be used in combinations with DMAs. +// USE WITH CARE - NO SAFETY CHECKS CONDUCTED HERE! NOT MUTEX PROTECTED! +void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n); +void tu_fifo_advance_read_pointer (tu_fifo_t *f, uint16_t n); + +// If you want to read/write from/to the FIFO by use of a DMA, you may need to conduct two copies +// to handle a possible wrapping part. These functions deliver a pointer to start +// reading/writing from/to and a valid linear length along which no wrap occurs. +void tu_fifo_get_read_info (tu_fifo_t *f, tu_fifo_buffer_info_t *info); +void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info); + + +#ifdef __cplusplus +} +#endif + +#endif /* _TUSB_FIFO_H_ */ diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_mcu.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_mcu.h new file mode 100644 index 0000000000..1f27afa4e8 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_mcu.h @@ -0,0 +1,308 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021, Ha Thach (tinyusb.org) + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef TUSB_MCU_H_ +#define TUSB_MCU_H_ + +//--------------------------------------------------------------------+ +// Port/Platform Specific +// TUP stand for TinyUSB Port/Platform (can be renamed) +//--------------------------------------------------------------------+ + +//------------- Unaligned Memory Access -------------// + +// ARMv7+ (M3-M7, M23-M33) can access unaligned memory +#if (defined(__ARM_ARCH) && (__ARM_ARCH >= 7)) + #define TUP_ARCH_STRICT_ALIGN 0 +#else + #define TUP_ARCH_STRICT_ALIGN 1 +#endif + +/* USB Controller Attributes for Device, Host or MCU (both) + * - ENDPOINT_MAX: max (logical) number of endpoint + * - ENDPOINT_EXCLUSIVE_NUMBER: endpoint number with different direction IN and OUT aren't allowed, + * e.g EP1 OUT & EP1 IN cannot exist together + * - RHPORT_HIGHSPEED: support highspeed with on-chip PHY + */ + +//------------- NXP -------------// +#if TU_CHECK_MCU(OPT_MCU_LPC11UXX, OPT_MCU_LPC13XX, OPT_MCU_LPC15XX) + #define TUP_DCD_ENDPOINT_MAX 5 + +#elif TU_CHECK_MCU(OPT_MCU_LPC175X_6X, OPT_MCU_LPC177X_8X, OPT_MCU_LPC40XX) + #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_USBIP_OHCI + #define TUP_OHCI_RHPORTS 2 + +#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX) + // TODO USB0 has 6, USB1 has 4 + #define TUP_USBIP_CHIPIDEA_HS + #define TUP_USBIP_EHCI + + #define TUP_DCD_ENDPOINT_MAX 6 + #define TUP_RHPORT_HIGHSPEED 1 // Port0 HS, Port1 FS + +#elif TU_CHECK_MCU(OPT_MCU_LPC51UXX) + #define TUP_DCD_ENDPOINT_MAX 5 + +#elif TU_CHECK_MCU(OPT_MCU_LPC54XXX) + // TODO USB0 has 5, USB1 has 6 + #define TUP_DCD_ENDPOINT_MAX 6 + +#elif TU_CHECK_MCU(OPT_MCU_LPC55XX) + // TODO USB0 has 5, USB1 has 6 + #define TUP_DCD_ENDPOINT_MAX 6 + +#elif TU_CHECK_MCU(OPT_MCU_MIMXRT) + #define TUP_USBIP_CHIPIDEA_HS + #define TUP_USBIP_EHCI + + #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_RHPORT_HIGHSPEED 1 // Port0 HS, Port1 HS + +#elif TU_CHECK_MCU(OPT_MCU_MKL25ZXX, OPT_MCU_K32L2BXX) + #define TUP_DCD_ENDPOINT_MAX 16 + +#elif TU_CHECK_MCU(OPT_MCU_MM32F327X) + #define TUP_DCD_ENDPOINT_MAX 16 + +//------------- Nordic -------------// +#elif TU_CHECK_MCU(OPT_MCU_NRF5X) + // 8 CBI + 1 ISO + #define TUP_DCD_ENDPOINT_MAX 9 + +//------------- Microchip -------------// +#elif TU_CHECK_MCU(OPT_MCU_SAMD21, OPT_MCU_SAMD51, OPT_MCU_SAME5X) || \ + TU_CHECK_MCU(OPT_MCU_SAMD11, OPT_MCU_SAML21, OPT_MCU_SAML22) + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_SAMG) + #define TUP_DCD_ENDPOINT_MAX 6 + #define TUP_DCD_ENDPOINT_EXCLUSIVE_NUMBER + +#elif TU_CHECK_MCU(OPT_MCU_SAMX7X) + #define TUP_DCD_ENDPOINT_MAX 10 + #define TUP_RHPORT_HIGHSPEED 1 + #define TUP_DCD_ENDPOINT_EXCLUSIVE_NUMBER + +#elif TU_CHECK_MCU(OPT_MCU_PIC32MZ) + #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_EXCLUSIVE_NUMBER + +#elif TU_CHECK_MCU(OPT_MCU_PIC32MX, OPT_MCU_PIC32MM, OPT_MCU_PIC32MK) || \ + TU_CHECK_MCU(OPT_MCU_PIC24, OPT_MCU_DSPIC33) + #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_DCD_ENDPOINT_EXCLUSIVE_NUMBER + +//------------- ST -------------// +#elif TU_CHECK_MCU(OPT_MCU_STM32F0) + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_STM32F1) + #if defined (STM32F105x8) || defined (STM32F105xB) || defined (STM32F105xC) || \ + defined (STM32F107xB) || defined (STM32F107xC) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + + #define TUP_DCD_ENDPOINT_MAX 4 + #else + #define TUP_DCD_ENDPOINT_MAX 8 + #endif + +#elif TU_CHECK_MCU(OPT_MCU_STM32F2) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + + // FS has 4 ep, HS has 5 ep + #define TUP_DCD_ENDPOINT_MAX 6 + +#elif TU_CHECK_MCU(OPT_MCU_STM32F3) + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_STM32F4) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + + // For most mcu, FS has 4, HS has 6. TODO 446/469/479 HS has 9 + #define TUP_DCD_ENDPOINT_MAX 6 + +#elif TU_CHECK_MCU(OPT_MCU_STM32F7) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + + // FS has 6, HS has 9 + #define TUP_DCD_ENDPOINT_MAX 9 + + // MCU with on-chip HS Phy + #if defined(STM32F723xx) || defined(STM32F730xx) || defined(STM32F733xx) + #define TUP_RHPORT_HIGHSPEED 1 // Port0: FS, Port1: HS + #endif + +#elif TU_CHECK_MCU(OPT_MCU_STM32H7) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + + #define TUP_DCD_ENDPOINT_MAX 9 + +#elif TU_CHECK_MCU(OPT_MCU_STM32G4) + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_STM32L0, OPT_MCU_STM32L1) + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_STM32L4) + #if defined (STM32L475xx) || defined (STM32L476xx) || \ + defined (STM32L485xx) || defined (STM32L486xx) || defined (STM32L496xx) || \ + defined (STM32L4A6xx) || defined (STM32L4P5xx) || defined (STM32L4Q5xx) || \ + defined (STM32L4R5xx) || defined (STM32L4R7xx) || defined (STM32L4R9xx) || \ + defined (STM32L4S5xx) || defined (STM32L4S7xx) || defined (STM32L4S9xx) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + + #define TUP_DCD_ENDPOINT_MAX 6 + #else + #define TUP_DCD_ENDPOINT_MAX 8 + #endif + +#elif TU_CHECK_MCU(OPT_MCU_STM32WB) + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_STM32U5) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + #define TUP_DCD_ENDPOINT_MAX 6 + +//------------- Sony -------------// +#elif TU_CHECK_MCU(OPT_MCU_CXD56) + #define TUP_DCD_ENDPOINT_MAX 7 + #define TUP_RHPORT_HIGHSPEED 1 + #define TUP_DCD_ENDPOINT_EXCLUSIVE_NUMBER + +//------------- TI -------------// +#elif TU_CHECK_MCU(OPT_MCU_MSP430x5xx) + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_MSP432E4, OPT_MCU_TM4C123, OPT_MCU_TM4C129) + #define TUP_DCD_ENDPOINT_MAX 8 + +//------------- ValentyUSB -------------// +#elif TU_CHECK_MCU(OPT_MCU_VALENTYUSB_EPTRI) + #define TUP_DCD_ENDPOINT_MAX 16 + +//------------- Nuvoton -------------// +#elif TU_CHECK_MCU(OPT_MCU_NUC121, OPT_MCU_NUC126) + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_NUC120) + #define TUP_DCD_ENDPOINT_MAX 6 + +#elif TU_CHECK_MCU(OPT_MCU_NUC505) + #define TUP_DCD_ENDPOINT_MAX 12 + #define TUP_RHPORT_HIGHSPEED 1 + +//------------- Espressif -------------// +#elif TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) + #define TUP_USBIP_DWC2 + #define TUP_DCD_ENDPOINT_MAX 6 + +//------------- Dialog -------------// +#elif TU_CHECK_MCU(OPT_MCU_DA1469X) + #define TUP_DCD_ENDPOINT_MAX 4 + +//------------- Raspberry Pi -------------// +#elif TU_CHECK_MCU(OPT_MCU_RP2040) + #define TUP_DCD_ENDPOINT_MAX 16 + + #define TU_ATTR_FAST_FUNC __attribute__((section(".time_critical.tinyusb"))) + +//------------- Silabs -------------// +#elif TU_CHECK_MCU(OPT_MCU_EFM32GG) + #define TUP_USBIP_DWC2 + #define TUP_DCD_ENDPOINT_MAX 7 + +//------------- Renesas -------------// +#elif TU_CHECK_MCU(OPT_MCU_RX63X, OPT_MCU_RX65X, OPT_MCU_RX72N) + #define TUP_DCD_ENDPOINT_MAX 10 + +//------------- GigaDevice -------------// +#elif TU_CHECK_MCU(OPT_MCU_GD32VF103) + #define TUP_USBIP_DWC2 + #define TUP_DCD_ENDPOINT_MAX 4 + +//------------- Broadcom -------------// +#elif TU_CHECK_MCU(OPT_MCU_BCM2711, OPT_MCU_BCM2835, OPT_MCU_BCM2837) + #define TUP_USBIP_DWC2 + #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_RHPORT_HIGHSPEED 1 + +//------------- Broadcom -------------// +#elif TU_CHECK_MCU(OPT_MCU_XMC4000) + #define TUP_USBIP_DWC2 + #define TUP_DCD_ENDPOINT_MAX 8 + +//------------- BridgeTek -------------// +#elif TU_CHECK_MCU(OPT_MCU_FT90X) + #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_RHPORT_HIGHSPEED 1 + +#elif TU_CHECK_MCU(OPT_MCU_FT93X) + #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_RHPORT_HIGHSPEED 1 + +//------------ Allwinner -------------// +#elif TU_CHECK_MCU(OPT_MCU_F1C100S) + #define TUP_DCD_ENDPOINT_MAX 4 + +//------------- WCH -------------// +#elif TU_CHECK_MCU(OPT_MCU_CH32V307) + #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_RHPORT_HIGHSPEED 1 +#endif + +//--------------------------------------------------------------------+ +// Default Values +//--------------------------------------------------------------------+ + +#ifndef TUP_MCU_MULTIPLE_CORE +#define TUP_MCU_MULTIPLE_CORE 0 +#endif + +#ifndef TUP_DCD_ENDPOINT_MAX + #warning "TUP_DCD_ENDPOINT_MAX is not defined for this MCU, default to 8" + #define TUP_DCD_ENDPOINT_MAX 8 +#endif + +// Default to fullspeed if not defined +#ifndef TUP_RHPORT_HIGHSPEED + #define TUP_RHPORT_HIGHSPEED 0 +#endif + +// fast function, normally mean placing function in SRAM +#ifndef TU_ATTR_FAST_FUNC + #define TU_ATTR_FAST_FUNC +#endif + +#endif diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_private.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_private.h new file mode 100644 index 0000000000..d5541856cb --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_private.h @@ -0,0 +1,173 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022, Ha Thach (tinyusb.org) + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + + +#ifndef _TUSB_PRIVATE_H_ +#define _TUSB_PRIVATE_H_ + +// Internal Helper used by Host and Device Stack + +#ifdef __cplusplus + extern "C" { +#endif + +typedef struct TU_ATTR_PACKED +{ + volatile uint8_t busy : 1; + volatile uint8_t stalled : 1; + volatile uint8_t claimed : 1; +}tu_edpt_state_t; + +typedef struct { + bool is_host; // host or device most + union { + uint8_t daddr; + uint8_t rhport; + uint8_t hwid; + }; + uint8_t ep_addr; + uint8_t ep_speed; + + uint16_t ep_packetsize; + uint16_t ep_bufsize; + + // TODO xfer_fifo can skip this buffer + uint8_t* ep_buf; + + tu_fifo_t ff; + + // mutex: read if ep rx, write if e tx + OSAL_MUTEX_DEF(ff_mutex); + +}tu_edpt_stream_t; + +//--------------------------------------------------------------------+ +// Endpoint +//--------------------------------------------------------------------+ + +// Check if endpoint descriptor is valid per USB specs +bool tu_edpt_validate(tusb_desc_endpoint_t const * desc_ep, tusb_speed_t speed); + +// Bind all endpoint of a interface descriptor to class driver +void tu_edpt_bind_driver(uint8_t ep2drv[][2], tusb_desc_interface_t const* p_desc, uint16_t desc_len, uint8_t driver_id); + +// Calculate total length of n interfaces (depending on IAD) +uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf, uint8_t itf_count, uint16_t max_len); + +// Claim an endpoint with provided mutex +bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex); + +// Release an endpoint with provided mutex +bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex); + +//--------------------------------------------------------------------+ +// Endpoint Stream +//--------------------------------------------------------------------+ + +// Init an stream, should only be called once +bool tu_edpt_stream_init(tu_edpt_stream_t* s, bool is_host, bool is_tx, bool overwritable, + void* ff_buf, uint16_t ff_bufsize, uint8_t* ep_buf, uint16_t ep_bufsize); + +// Open an stream for an endpoint +// hwid is either device address (host mode) or rhport (device mode) +TU_ATTR_ALWAYS_INLINE static inline +void tu_edpt_stream_open(tu_edpt_stream_t* s, uint8_t hwid, tusb_desc_endpoint_t const *desc_ep) +{ + tu_fifo_clear(&s->ff); + s->hwid = hwid; + s->ep_addr = desc_ep->bEndpointAddress; + s->ep_packetsize = tu_edpt_packet_size(desc_ep); +} + +TU_ATTR_ALWAYS_INLINE static inline +void tu_edpt_stream_close(tu_edpt_stream_t* s) +{ + s->hwid = 0; + s->ep_addr = 0; +} + +// Clear fifo +TU_ATTR_ALWAYS_INLINE static inline +bool tu_edpt_stream_clear(tu_edpt_stream_t* s) +{ + return tu_fifo_clear(&s->ff); +} + +//--------------------------------------------------------------------+ +// Stream Write +//--------------------------------------------------------------------+ + +// Write to stream +uint32_t tu_edpt_stream_write(tu_edpt_stream_t* s, void const *buffer, uint32_t bufsize); + +// Start an usb transfer if endpoint is not busy +uint32_t tu_edpt_stream_write_xfer(tu_edpt_stream_t* s); + +// Start an zero-length packet if needed +bool tu_edpt_stream_write_zlp_if_needed(tu_edpt_stream_t* s, uint32_t last_xferred_bytes); + +// Get the number of bytes available for writing +TU_ATTR_ALWAYS_INLINE static inline +uint32_t tu_edpt_stream_write_available(tu_edpt_stream_t* s) +{ + return (uint32_t) tu_fifo_remaining(&s->ff); +} + +//--------------------------------------------------------------------+ +// Stream Read +//--------------------------------------------------------------------+ + +// Read from stream +uint32_t tu_edpt_stream_read(tu_edpt_stream_t* s, void* buffer, uint32_t bufsize); + +// Start an usb transfer if endpoint is not busy +uint32_t tu_edpt_stream_read_xfer(tu_edpt_stream_t* s); + +// Must be called in the transfer complete callback +TU_ATTR_ALWAYS_INLINE static inline +void tu_edpt_stream_read_xfer_complete(tu_edpt_stream_t* s, uint32_t xferred_bytes) +{ + tu_fifo_write_n(&s->ff, s->ep_buf, (uint16_t) xferred_bytes); +} + +// Get the number of bytes available for reading +TU_ATTR_ALWAYS_INLINE static inline +uint32_t tu_edpt_stream_read_available(tu_edpt_stream_t* s) +{ + return (uint32_t) tu_fifo_count(&s->ff); +} + +TU_ATTR_ALWAYS_INLINE static inline +bool tu_edpt_stream_peek(tu_edpt_stream_t* s, uint8_t* ch) +{ + return tu_fifo_peek(&s->ff, ch); +} + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_PRIVATE_H_ */ diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_timeout.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_timeout.h new file mode 100644 index 0000000000..ce53955f00 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_timeout.h @@ -0,0 +1,80 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +/** \ingroup Group_Common Common Files + * \defgroup Group_TimeoutTimer timeout timer + * @{ */ + +#ifndef _TUSB_TIMEOUT_H_ +#define _TUSB_TIMEOUT_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint32_t start; + uint32_t interval; +}tu_timeout_t; + +#if 0 + +extern uint32_t tusb_hal_millis(void); + +static inline void tu_timeout_set(tu_timeout_t* tt, uint32_t msec) +{ + tt->interval = msec; + tt->start = tusb_hal_millis(); +} + +static inline bool tu_timeout_expired(tu_timeout_t* tt) +{ + return ( tusb_hal_millis() - tt->start ) >= tt->interval; +} + +// For used with periodic event to prevent drift +static inline void tu_timeout_reset(tu_timeout_t* tt) +{ + tt->start += tt->interval; +} + +static inline void tu_timeout_restart(tu_timeout_t* tt) +{ + tt->start = tusb_hal_millis(); +} + +#endif + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_TIMEOUT_H_ */ + +/** @} */ diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_types.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_types.h new file mode 100644 index 0000000000..7266a525ec --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_types.h @@ -0,0 +1,581 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +/** \ingroup group_usb_definitions + * \defgroup USBDef_Type USB Types + * @{ */ + +#ifndef _TUSB_TYPES_H_ +#define _TUSB_TYPES_H_ + +#include +#include +#include "tusb_compiler.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/*------------------------------------------------------------------*/ +/* CONSTANTS + *------------------------------------------------------------------*/ + +/// defined base on EHCI specs value for Endpoint Speed +typedef enum +{ + TUSB_SPEED_FULL = 0, + TUSB_SPEED_LOW = 1, + TUSB_SPEED_HIGH = 2, + TUSB_SPEED_INVALID = 0xff, +}tusb_speed_t; + +/// defined base on USB Specs Endpoint's bmAttributes +typedef enum +{ + TUSB_XFER_CONTROL = 0 , + TUSB_XFER_ISOCHRONOUS , + TUSB_XFER_BULK , + TUSB_XFER_INTERRUPT +}tusb_xfer_type_t; + +typedef enum +{ + TUSB_DIR_OUT = 0, + TUSB_DIR_IN = 1, + + TUSB_DIR_IN_MASK = 0x80 +}tusb_dir_t; + +enum +{ + TUSB_EPSIZE_BULK_FS = 64, + TUSB_EPSIZE_BULK_HS= 512, + + TUSB_EPSIZE_ISO_FS_MAX = 1023, + TUSB_EPSIZE_ISO_HS_MAX = 1024, +}; + +/// Isochronous End Point Attributes +typedef enum +{ + TUSB_ISO_EP_ATT_NO_SYNC = 0x00, + TUSB_ISO_EP_ATT_ASYNCHRONOUS = 0x04, + TUSB_ISO_EP_ATT_ADAPTIVE = 0x08, + TUSB_ISO_EP_ATT_SYNCHRONOUS = 0x0C, + TUSB_ISO_EP_ATT_DATA = 0x00, ///< Data End Point + TUSB_ISO_EP_ATT_EXPLICIT_FB = 0x10, ///< Feedback End Point + TUSB_ISO_EP_ATT_IMPLICIT_FB = 0x20, ///< Data endpoint that also serves as an implicit feedback +}tusb_iso_ep_attribute_t; + +/// USB Descriptor Types +typedef enum +{ + TUSB_DESC_DEVICE = 0x01, + TUSB_DESC_CONFIGURATION = 0x02, + TUSB_DESC_STRING = 0x03, + TUSB_DESC_INTERFACE = 0x04, + TUSB_DESC_ENDPOINT = 0x05, + TUSB_DESC_DEVICE_QUALIFIER = 0x06, + TUSB_DESC_OTHER_SPEED_CONFIG = 0x07, + TUSB_DESC_INTERFACE_POWER = 0x08, + TUSB_DESC_OTG = 0x09, + TUSB_DESC_DEBUG = 0x0A, + TUSB_DESC_INTERFACE_ASSOCIATION = 0x0B, + + TUSB_DESC_BOS = 0x0F, + TUSB_DESC_DEVICE_CAPABILITY = 0x10, + + TUSB_DESC_FUNCTIONAL = 0x21, + + // Class Specific Descriptor + TUSB_DESC_CS_DEVICE = 0x21, + TUSB_DESC_CS_CONFIGURATION = 0x22, + TUSB_DESC_CS_STRING = 0x23, + TUSB_DESC_CS_INTERFACE = 0x24, + TUSB_DESC_CS_ENDPOINT = 0x25, + + TUSB_DESC_SUPERSPEED_ENDPOINT_COMPANION = 0x30, + TUSB_DESC_SUPERSPEED_ISO_ENDPOINT_COMPANION = 0x31 +}tusb_desc_type_t; + +typedef enum +{ + TUSB_REQ_GET_STATUS = 0 , + TUSB_REQ_CLEAR_FEATURE = 1 , + TUSB_REQ_RESERVED = 2 , + TUSB_REQ_SET_FEATURE = 3 , + TUSB_REQ_RESERVED2 = 4 , + TUSB_REQ_SET_ADDRESS = 5 , + TUSB_REQ_GET_DESCRIPTOR = 6 , + TUSB_REQ_SET_DESCRIPTOR = 7 , + TUSB_REQ_GET_CONFIGURATION = 8 , + TUSB_REQ_SET_CONFIGURATION = 9 , + TUSB_REQ_GET_INTERFACE = 10 , + TUSB_REQ_SET_INTERFACE = 11 , + TUSB_REQ_SYNCH_FRAME = 12 +}tusb_request_code_t; + +typedef enum +{ + TUSB_REQ_FEATURE_EDPT_HALT = 0, + TUSB_REQ_FEATURE_REMOTE_WAKEUP = 1, + TUSB_REQ_FEATURE_TEST_MODE = 2 +}tusb_request_feature_selector_t; + +typedef enum +{ + TUSB_REQ_TYPE_STANDARD = 0, + TUSB_REQ_TYPE_CLASS, + TUSB_REQ_TYPE_VENDOR, + TUSB_REQ_TYPE_INVALID +} tusb_request_type_t; + +typedef enum +{ + TUSB_REQ_RCPT_DEVICE =0, + TUSB_REQ_RCPT_INTERFACE, + TUSB_REQ_RCPT_ENDPOINT, + TUSB_REQ_RCPT_OTHER +} tusb_request_recipient_t; + +// https://www.usb.org/defined-class-codes +typedef enum +{ + TUSB_CLASS_UNSPECIFIED = 0 , + TUSB_CLASS_AUDIO = 1 , + TUSB_CLASS_CDC = 2 , + TUSB_CLASS_HID = 3 , + TUSB_CLASS_RESERVED_4 = 4 , + TUSB_CLASS_PHYSICAL = 5 , + TUSB_CLASS_IMAGE = 6 , + TUSB_CLASS_PRINTER = 7 , + TUSB_CLASS_MSC = 8 , + TUSB_CLASS_HUB = 9 , + TUSB_CLASS_CDC_DATA = 10 , + TUSB_CLASS_SMART_CARD = 11 , + TUSB_CLASS_RESERVED_12 = 12 , + TUSB_CLASS_CONTENT_SECURITY = 13 , + TUSB_CLASS_VIDEO = 14 , + TUSB_CLASS_PERSONAL_HEALTHCARE = 15 , + TUSB_CLASS_AUDIO_VIDEO = 16 , + + TUSB_CLASS_DIAGNOSTIC = 0xDC , + TUSB_CLASS_WIRELESS_CONTROLLER = 0xE0 , + TUSB_CLASS_MISC = 0xEF , + TUSB_CLASS_APPLICATION_SPECIFIC = 0xFE , + TUSB_CLASS_VENDOR_SPECIFIC = 0xFF +}tusb_class_code_t; + +typedef enum +{ + MISC_SUBCLASS_COMMON = 2 +}misc_subclass_type_t; + +typedef enum +{ + MISC_PROTOCOL_IAD = 1 +}misc_protocol_type_t; + +typedef enum +{ + APP_SUBCLASS_USBTMC = 0x03, + APP_SUBCLASS_DFU_RUNTIME = 0x01 +} app_subclass_type_t; + +typedef enum +{ + DEVICE_CAPABILITY_WIRELESS_USB = 0x01, + DEVICE_CAPABILITY_USB20_EXTENSION = 0x02, + DEVICE_CAPABILITY_SUPERSPEED_USB = 0x03, + DEVICE_CAPABILITY_CONTAINER_id = 0x04, + DEVICE_CAPABILITY_PLATFORM = 0x05, + DEVICE_CAPABILITY_POWER_DELIVERY = 0x06, + DEVICE_CAPABILITY_BATTERY_INFO = 0x07, + DEVICE_CAPABILITY_PD_CONSUMER_PORT = 0x08, + DEVICE_CAPABILITY_PD_PROVIDER_PORT = 0x09, + DEVICE_CAPABILITY_SUPERSPEED_PLUS = 0x0A, + DEVICE_CAPABILITY_PRECESION_TIME_MEASUREMENT = 0x0B, + DEVICE_CAPABILITY_WIRELESS_USB_EXT = 0x0C, + DEVICE_CAPABILITY_BILLBOARD = 0x0D, + DEVICE_CAPABILITY_AUTHENTICATION = 0x0E, + DEVICE_CAPABILITY_BILLBOARD_EX = 0x0F, + DEVICE_CAPABILITY_CONFIGURATION_SUMMARY = 0x10 +}device_capability_type_t; + +enum { + TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = TU_BIT(5), + TUSB_DESC_CONFIG_ATT_SELF_POWERED = TU_BIT(6), +}; + +#define TUSB_DESC_CONFIG_POWER_MA(x) ((x)/2) + +typedef enum +{ + XFER_RESULT_SUCCESS = 0, + XFER_RESULT_FAILED, + XFER_RESULT_STALLED, + XFER_RESULT_TIMEOUT, + XFER_RESULT_INVALID +}xfer_result_t; + +enum // TODO remove +{ + DESC_OFFSET_LEN = 0, + DESC_OFFSET_TYPE = 1 +}; + +enum +{ + INTERFACE_INVALID_NUMBER = 0xff +}; + +typedef enum +{ + MS_OS_20_SET_HEADER_DESCRIPTOR = 0x00, + MS_OS_20_SUBSET_HEADER_CONFIGURATION = 0x01, + MS_OS_20_SUBSET_HEADER_FUNCTION = 0x02, + MS_OS_20_FEATURE_COMPATBLE_ID = 0x03, + MS_OS_20_FEATURE_REG_PROPERTY = 0x04, + MS_OS_20_FEATURE_MIN_RESUME_TIME = 0x05, + MS_OS_20_FEATURE_MODEL_ID = 0x06, + MS_OS_20_FEATURE_CCGP_DEVICE = 0x07, + MS_OS_20_FEATURE_VENDOR_REVISION = 0x08 +} microsoft_os_20_type_t; + +enum +{ + CONTROL_STAGE_IDLE, + CONTROL_STAGE_SETUP, + CONTROL_STAGE_DATA, + CONTROL_STAGE_ACK +}; + +enum +{ + TUSB_INDEX_INVALID = 0xff +}; + +//--------------------------------------------------------------------+ +// USB Descriptors +//--------------------------------------------------------------------+ + +// Start of all packed definitions for compiler without per-type packed +TU_ATTR_PACKED_BEGIN +TU_ATTR_BIT_FIELD_ORDER_BEGIN + +/// USB Device Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< DEVICE Descriptor Type. + uint16_t bcdUSB ; ///< BUSB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H). This field identifies the release of the USB Specification with which the device and its descriptors are compliant. + + uint8_t bDeviceClass ; ///< Class code (assigned by the USB-IF). \li If this field is reset to zero, each interface within a configuration specifies its own class information and the various interfaces operate independently. \li If this field is set to a value between 1 and FEH, the device supports different class specifications on different interfaces and the interfaces may not operate independently. This value identifies the class definition used for the aggregate interfaces. \li If this field is set to FFH, the device class is vendor-specific. + uint8_t bDeviceSubClass ; ///< Subclass code (assigned by the USB-IF). These codes are qualified by the value of the bDeviceClass field. \li If the bDeviceClass field is reset to zero, this field must also be reset to zero. \li If the bDeviceClass field is not set to FFH, all values are reserved for assignment by the USB-IF. + uint8_t bDeviceProtocol ; ///< Protocol code (assigned by the USB-IF). These codes are qualified by the value of the bDeviceClass and the bDeviceSubClass fields. If a device supports class-specific protocols on a device basis as opposed to an interface basis, this code identifies the protocols that the device uses as defined by the specification of the device class. \li If this field is reset to zero, the device does not use class-specific protocols on a device basis. However, it may use classspecific protocols on an interface basis. \li If this field is set to FFH, the device uses a vendor-specific protocol on a device basis. + uint8_t bMaxPacketSize0 ; ///< Maximum packet size for endpoint zero (only 8, 16, 32, or 64 are valid). For HS devices is fixed to 64. + + uint16_t idVendor ; ///< Vendor ID (assigned by the USB-IF). + uint16_t idProduct ; ///< Product ID (assigned by the manufacturer). + uint16_t bcdDevice ; ///< Device release number in binary-coded decimal. + uint8_t iManufacturer ; ///< Index of string descriptor describing manufacturer. + uint8_t iProduct ; ///< Index of string descriptor describing product. + uint8_t iSerialNumber ; ///< Index of string descriptor describing the device's serial number. + + uint8_t bNumConfigurations ; ///< Number of possible configurations. +} tusb_desc_device_t; + +TU_VERIFY_STATIC( sizeof(tusb_desc_device_t) == 18, "size is not correct"); + +// USB Binary Device Object Store (BOS) Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes + uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type + uint16_t wTotalLength ; ///< Total length of data returned for this descriptor + uint8_t bNumDeviceCaps ; ///< Number of device capability descriptors in the BOS +} tusb_desc_bos_t; + +TU_VERIFY_STATIC( sizeof(tusb_desc_bos_t) == 5, "size is not correct"); + +/// USB Configuration Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes + uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type + uint16_t wTotalLength ; ///< Total length of data returned for this configuration. Includes the combined length of all descriptors (configuration, interface, endpoint, and class- or vendor-specific) returned for this configuration. + + uint8_t bNumInterfaces ; ///< Number of interfaces supported by this configuration + uint8_t bConfigurationValue ; ///< Value to use as an argument to the SetConfiguration() request to select this configuration. + uint8_t iConfiguration ; ///< Index of string descriptor describing this configuration + uint8_t bmAttributes ; ///< Configuration characteristics \n D7: Reserved (set to one)\n D6: Self-powered \n D5: Remote Wakeup \n D4...0: Reserved (reset to zero) \n D7 is reserved and must be set to one for historical reasons. \n A device configuration that uses power from the bus and a local source reports a non-zero value in bMaxPower to indicate the amount of bus power required and sets D6. The actual power source at runtime may be determined using the GetStatus(DEVICE) request (see USB 2.0 spec Section 9.4.5). \n If a device configuration supports remote wakeup, D5 is set to one. + uint8_t bMaxPower ; ///< Maximum power consumption of the USB device from the bus in this specific configuration when the device is fully operational. Expressed in 2 mA units (i.e., 50 = 100 mA). +} tusb_desc_configuration_t; + +TU_VERIFY_STATIC( sizeof(tusb_desc_configuration_t) == 9, "size is not correct"); + +/// USB Interface Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes + uint8_t bDescriptorType ; ///< INTERFACE Descriptor Type + + uint8_t bInterfaceNumber ; ///< Number of this interface. Zero-based value identifying the index in the array of concurrent interfaces supported by this configuration. + uint8_t bAlternateSetting ; ///< Value used to select this alternate setting for the interface identified in the prior field + uint8_t bNumEndpoints ; ///< Number of endpoints used by this interface (excluding endpoint zero). If this value is zero, this interface only uses the Default Control Pipe. + uint8_t bInterfaceClass ; ///< Class code (assigned by the USB-IF). \li A value of zero is reserved for future standardization. \li If this field is set to FFH, the interface class is vendor-specific. \li All other values are reserved for assignment by the USB-IF. + uint8_t bInterfaceSubClass ; ///< Subclass code (assigned by the USB-IF). \n These codes are qualified by the value of the bInterfaceClass field. \li If the bInterfaceClass field is reset to zero, this field must also be reset to zero. \li If the bInterfaceClass field is not set to FFH, all values are reserved for assignment by the USB-IF. + uint8_t bInterfaceProtocol ; ///< Protocol code (assigned by the USB). \n These codes are qualified by the value of the bInterfaceClass and the bInterfaceSubClass fields. If an interface supports class-specific requests, this code identifies the protocols that the device uses as defined by the specification of the device class. \li If this field is reset to zero, the device does not use a class-specific protocol on this interface. \li If this field is set to FFH, the device uses a vendor-specific protocol for this interface. + uint8_t iInterface ; ///< Index of string descriptor describing this interface +} tusb_desc_interface_t; + +TU_VERIFY_STATIC( sizeof(tusb_desc_interface_t) == 9, "size is not correct"); + +/// USB Endpoint Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; // Size of this descriptor in bytes + uint8_t bDescriptorType ; // ENDPOINT Descriptor Type + + uint8_t bEndpointAddress ; // The address of the endpoint + + struct TU_ATTR_PACKED { + uint8_t xfer : 2; // Control, ISO, Bulk, Interrupt + uint8_t sync : 2; // None, Asynchronous, Adaptive, Synchronous + uint8_t usage : 2; // Data, Feedback, Implicit feedback + uint8_t : 2; + } bmAttributes; + + uint16_t wMaxPacketSize ; // Bit 10..0 : max packet size, bit 12..11 additional transaction per highspeed micro-frame + uint8_t bInterval ; // Polling interval, in frames or microframes depending on the operating speed +} tusb_desc_endpoint_t; + +TU_VERIFY_STATIC( sizeof(tusb_desc_endpoint_t) == 7, "size is not correct"); + +/// USB Other Speed Configuration Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of descriptor + uint8_t bDescriptorType ; ///< Other_speed_Configuration Type + uint16_t wTotalLength ; ///< Total length of data returned + + uint8_t bNumInterfaces ; ///< Number of interfaces supported by this speed configuration + uint8_t bConfigurationValue ; ///< Value to use to select configuration + uint8_t iConfiguration ; ///< Index of string descriptor + uint8_t bmAttributes ; ///< Same as Configuration descriptor + uint8_t bMaxPower ; ///< Same as Configuration descriptor +} tusb_desc_other_speed_t; + +/// USB Device Qualifier Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of descriptor + uint8_t bDescriptorType ; ///< Device Qualifier Type + uint16_t bcdUSB ; ///< USB specification version number (e.g., 0200H for V2.00) + + uint8_t bDeviceClass ; ///< Class Code + uint8_t bDeviceSubClass ; ///< SubClass Code + uint8_t bDeviceProtocol ; ///< Protocol Code + + uint8_t bMaxPacketSize0 ; ///< Maximum packet size for other speed + uint8_t bNumConfigurations ; ///< Number of Other-speed Configurations + uint8_t bReserved ; ///< Reserved for future use, must be zero +} tusb_desc_device_qualifier_t; + +TU_VERIFY_STATIC( sizeof(tusb_desc_device_qualifier_t) == 10, "size is not correct"); + +/// USB Interface Association Descriptor (IAD ECN) +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of descriptor + uint8_t bDescriptorType ; ///< Other_speed_Configuration Type + + uint8_t bFirstInterface ; ///< Index of the first associated interface. + uint8_t bInterfaceCount ; ///< Total number of associated interfaces. + + uint8_t bFunctionClass ; ///< Interface class ID. + uint8_t bFunctionSubClass ; ///< Interface subclass ID. + uint8_t bFunctionProtocol ; ///< Interface protocol ID. + + uint8_t iFunction ; ///< Index of the string descriptor describing the interface association. +} tusb_desc_interface_assoc_t; + +// USB String Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes + uint8_t bDescriptorType ; ///< Descriptor Type + uint16_t unicode_string[]; +} tusb_desc_string_t; + +// USB Binary Device Object Store (BOS) +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength; + uint8_t bDescriptorType ; + uint8_t bDevCapabilityType; + uint8_t bReserved; + uint8_t PlatformCapabilityUUID[16]; + uint8_t CapabilityData[]; +} tusb_desc_bos_platform_t; + +// USB WebuSB URL Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bScheme; + char url[]; +} tusb_desc_webusb_url_t; + +// DFU Functional Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength; + uint8_t bDescriptorType; + + union { + struct TU_ATTR_PACKED { + uint8_t bitCanDnload : 1; + uint8_t bitCanUpload : 1; + uint8_t bitManifestationTolerant : 1; + uint8_t bitWillDetach : 1; + uint8_t reserved : 4; + } bmAttributes; + + uint8_t bAttributes; + }; + + uint16_t wDetachTimeOut; + uint16_t wTransferSize; + uint16_t bcdDFUVersion; +} tusb_desc_dfu_functional_t; + +/*------------------------------------------------------------------*/ +/* Types + *------------------------------------------------------------------*/ +typedef struct TU_ATTR_PACKED{ + union { + struct TU_ATTR_PACKED { + uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t. + uint8_t type : 2; ///< Request type tusb_request_type_t. + uint8_t direction : 1; ///< Direction type. tusb_dir_t + } bmRequestType_bit; + + uint8_t bmRequestType; + }; + + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} tusb_control_request_t; + +TU_VERIFY_STATIC( sizeof(tusb_control_request_t) == 8, "size is not correct"); + + +TU_ATTR_PACKED_END // End of all packed definitions +TU_ATTR_BIT_FIELD_ORDER_END + +//--------------------------------------------------------------------+ +// Endpoint helper +//--------------------------------------------------------------------+ + +// Get direction from Endpoint address +TU_ATTR_ALWAYS_INLINE static inline tusb_dir_t tu_edpt_dir(uint8_t addr) +{ + return (addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT; +} + +// Get Endpoint number from address +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_number(uint8_t addr) +{ + return (uint8_t)(addr & (~TUSB_DIR_IN_MASK)); +} + +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_addr(uint8_t num, uint8_t dir) +{ + return (uint8_t)(num | (dir ? TUSB_DIR_IN_MASK : 0)); +} + +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_edpt_packet_size(tusb_desc_endpoint_t const* desc_ep) +{ + return tu_le16toh(desc_ep->wMaxPacketSize) & TU_GENMASK(10, 0); +} + +#if CFG_TUSB_DEBUG +TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_dir_str(tusb_dir_t dir) +{ + tu_static const char *str[] = {"out", "in"}; + return str[dir]; +} + +TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_type_str(tusb_xfer_type_t t) +{ + tu_static const char *str[] = {"control", "isochronous", "bulk", "interrupt"}; + return str[t]; +} +#endif + +//--------------------------------------------------------------------+ +// Descriptor helper +//--------------------------------------------------------------------+ + +// return next descriptor +TU_ATTR_ALWAYS_INLINE static inline uint8_t const * tu_desc_next(void const* desc) +{ + uint8_t const* desc8 = (uint8_t const*) desc; + return desc8 + desc8[DESC_OFFSET_LEN]; +} + +// get descriptor type +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_type(void const* desc) +{ + return ((uint8_t const*) desc)[DESC_OFFSET_TYPE]; +} + +// get descriptor length +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_len(void const* desc) +{ + return ((uint8_t const*) desc)[DESC_OFFSET_LEN]; +} + +// find descriptor that match byte1 (type) +uint8_t const * tu_desc_find(uint8_t const* desc, uint8_t const* end, uint8_t byte1); + +// find descriptor that match byte1 (type) and byte2 +uint8_t const * tu_desc_find2(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2); + +// find descriptor that match byte1 (type) and byte2 +uint8_t const * tu_desc_find3(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2, uint8_t byte3); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_TYPES_H_ */ + +/** @} */ diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_verify.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_verify.h new file mode 100644 index 0000000000..a52a6d2696 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/common/tusb_verify.h @@ -0,0 +1,156 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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. + * + * This file is part of the TinyUSB stack. + */ +#ifndef TUSB_VERIFY_H_ +#define TUSB_VERIFY_H_ + +#include +#include +#include "tusb_option.h" +#include "tusb_compiler.h" + +/*------------------------------------------------------------------*/ +/* This file use an advanced macro technique to mimic the default parameter + * as C++ for the sake of code simplicity. Beware of a headache macro + * manipulation that you are told to stay away. + * + * This contains macros for both VERIFY and ASSERT: + * + * VERIFY: Used when there is an error condition which is not the + * fault of the MCU. For example, bounds checking on data + * sent to the micro over USB should use this function. + * Another example is checking for buffer overflows, where + * returning from the active function causes a NAK. + * + * ASSERT: Used for error conditions that are caused by MCU firmware + * bugs. This is used to discover bugs in the code more + * quickly. One example would be adding assertions in library + * function calls to confirm a function's (untainted) + * parameters are valid. + * + * The difference in behavior is that ASSERT triggers a breakpoint while + * verify does not. + * + * #define TU_VERIFY(cond) if(cond) return false; + * #define TU_VERIFY(cond,ret) if(cond) return ret; + * + * #define TU_VERIFY_HDLR(cond,handler) if(cond) {handler; return false;} + * #define TU_VERIFY_HDLR(cond,ret,handler) if(cond) {handler; return ret;} + * + * #define TU_ASSERT(cond) if(cond) {_MESS_FAILED(); TU_BREAKPOINT(), return false;} + * #define TU_ASSERT(cond,ret) if(cond) {_MESS_FAILED(); TU_BREAKPOINT(), return ret;} + * + *------------------------------------------------------------------*/ + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// TU_VERIFY Helper +//--------------------------------------------------------------------+ + +#if CFG_TUSB_DEBUG + #include + #define _MESS_FAILED() tu_printf("%s %d: ASSERT FAILED\r\n", __func__, __LINE__) +#else + #define _MESS_FAILED() do {} while (0) +#endif + +// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7, M33 +#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__) + #define TU_BREAKPOINT() do \ + { \ + volatile uint32_t* ARM_CM_DHCSR = ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \ + if ( (*ARM_CM_DHCSR) & 1UL ) __asm("BKPT #0\n"); /* Only halt mcu if debugger is attached */ \ + } while(0) + +#elif defined(__riscv) + #define TU_BREAKPOINT() do { __asm("ebreak\n"); } while(0) + +#elif defined(_mips) + #define TU_BREAKPOINT() do { __asm("sdbbp 0"); } while (0) + +#else + #define TU_BREAKPOINT() do {} while (0) +#endif + +/*------------------------------------------------------------------*/ +/* Macro Generator + *------------------------------------------------------------------*/ + +// Helper to implement optional parameter for TU_VERIFY Macro family +#define _GET_3RD_ARG(arg1, arg2, arg3, ...) arg3 +#define _GET_4TH_ARG(arg1, arg2, arg3, arg4, ...) arg4 + +/*------------- Generator for TU_VERIFY and TU_VERIFY_HDLR -------------*/ +#define TU_VERIFY_DEFINE(_cond, _handler, _ret) do \ +{ \ + if ( !(_cond) ) { _handler; return _ret; } \ +} while(0) + +/*------------------------------------------------------------------*/ +/* TU_VERIFY + * - TU_VERIFY_1ARGS : return false if failed + * - TU_VERIFY_2ARGS : return provided value if failed + *------------------------------------------------------------------*/ +#define TU_VERIFY_1ARGS(_cond) TU_VERIFY_DEFINE(_cond, , false) +#define TU_VERIFY_2ARGS(_cond, _ret) TU_VERIFY_DEFINE(_cond, , _ret) + +#define TU_VERIFY(...) _GET_3RD_ARG(__VA_ARGS__, TU_VERIFY_2ARGS, TU_VERIFY_1ARGS, UNUSED)(__VA_ARGS__) + + +/*------------------------------------------------------------------*/ +/* TU_VERIFY WITH HANDLER + * - TU_VERIFY_HDLR_2ARGS : execute handler, return false if failed + * - TU_VERIFY_HDLR_3ARGS : execute handler, return provided error if failed + *------------------------------------------------------------------*/ +#define TU_VERIFY_HDLR_2ARGS(_cond, _handler) TU_VERIFY_DEFINE(_cond, _handler, false) +#define TU_VERIFY_HDLR_3ARGS(_cond, _handler, _ret) TU_VERIFY_DEFINE(_cond, _handler, _ret) + +#define TU_VERIFY_HDLR(...) _GET_4TH_ARG(__VA_ARGS__, TU_VERIFY_HDLR_3ARGS, TU_VERIFY_HDLR_2ARGS,UNUSED)(__VA_ARGS__) + +/*------------------------------------------------------------------*/ +/* ASSERT + * basically TU_VERIFY with TU_BREAKPOINT() as handler + * - 1 arg : return false if failed + * - 2 arg : return error if failed + *------------------------------------------------------------------*/ +#define ASSERT_1ARGS(_cond) TU_VERIFY_DEFINE(_cond, _MESS_FAILED(); TU_BREAKPOINT(), false) +#define ASSERT_2ARGS(_cond, _ret) TU_VERIFY_DEFINE(_cond, _MESS_FAILED(); TU_BREAKPOINT(), _ret) + +#ifndef TU_ASSERT +#define TU_ASSERT(...) _GET_3RD_ARG(__VA_ARGS__, ASSERT_2ARGS, ASSERT_1ARGS,UNUSED)(__VA_ARGS__) +#endif + +/*------------------------------------------------------------------*/ +/* ASSERT HDLR + *------------------------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif /* TUSB_VERIFY_H_ */ diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/hcd.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/hcd.h new file mode 100644 index 0000000000..a400626e4f --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/hcd.h @@ -0,0 +1,223 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_HCD_H_ +#define _TUSB_HCD_H_ + +#include "common/tusb_common.h" +#include "osal/osal.h" +#include "common/tusb_fifo.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Configuration +//--------------------------------------------------------------------+ + +#ifndef CFG_TUH_ENDPOINT_MAX + #define CFG_TUH_ENDPOINT_MAX (CFG_TUH_HUB + CFG_TUH_HID*2 + CFG_TUH_MSC*2 + CFG_TUH_CDC*3) +// #ifdef TUP_HCD_ENDPOINT_MAX +// #define CFG_TUH_ENDPPOINT_MAX TUP_HCD_ENDPOINT_MAX +// #else +// #define +// #endif +#endif + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +typedef enum +{ + HCD_EVENT_DEVICE_ATTACH, + HCD_EVENT_DEVICE_REMOVE, + HCD_EVENT_XFER_COMPLETE, + + // Not an HCD event, just a convenient way to defer ISR function + USBH_EVENT_FUNC_CALL, + + HCD_EVENT_COUNT +} hcd_eventid_t; + +typedef struct +{ + uint8_t rhport; + uint8_t event_id; + uint8_t dev_addr; + + union + { + // Attach, Remove + struct { + uint8_t hub_addr; + uint8_t hub_port; + uint8_t speed; + } connection; + + // XFER_COMPLETE + struct { + uint8_t ep_addr; + uint8_t result; + uint32_t len; + } xfer_complete; + + // FUNC_CALL + struct { + void (*func) (void*); + void* param; + }func_call; + }; + +} hcd_event_t; + +typedef struct +{ + uint8_t rhport; + uint8_t hub_addr; + uint8_t hub_port; + uint8_t speed; +} hcd_devtree_info_t; + +//--------------------------------------------------------------------+ +// Controller API +//--------------------------------------------------------------------+ + +// optional hcd configuration, called by tuh_configure() +bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) TU_ATTR_WEAK; + +// Initialize controller to host mode +bool hcd_init(uint8_t rhport); + +// Interrupt Handler +void hcd_int_handler(uint8_t rhport); + +// Enable USB interrupt +void hcd_int_enable (uint8_t rhport); + +// Disable USB interrupt +void hcd_int_disable(uint8_t rhport); + +// Get frame number (1ms) +uint32_t hcd_frame_number(uint8_t rhport); + +//--------------------------------------------------------------------+ +// Port API +//--------------------------------------------------------------------+ + +// Get the current connect status of roothub port +bool hcd_port_connect_status(uint8_t rhport); + +// Reset USB bus on the port +void hcd_port_reset(uint8_t rhport); + +// TODO implement later +void hcd_port_reset_end(uint8_t rhport); + +// Get port link speed +tusb_speed_t hcd_port_speed_get(uint8_t rhport); + +// HCD closes all opened endpoints belong to this device +void hcd_device_close(uint8_t rhport, uint8_t dev_addr); + +//--------------------------------------------------------------------+ +// Endpoints API +//--------------------------------------------------------------------+ + +// Open an endpoint +bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc); + +// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked +bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen); + +// Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked +bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]); + +// clear stall, data toggle is also reset to DATA0 +bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr); + +//--------------------------------------------------------------------+ +// USBH implemented API +//--------------------------------------------------------------------+ + +// Get device tree information of a device +// USB device tree can be complicated and manged by USBH, this help HCD to retrieve +// needed topology info to carry out its work +extern void hcd_devtree_get_info(uint8_t dev_addr, hcd_devtree_info_t* devtree_info); + +//------------- Event API -------------// + +// Called by HCD to notify stack +extern void hcd_event_handler(hcd_event_t const* event, bool in_isr); + +// Helper to send device attach event +TU_ATTR_ALWAYS_INLINE static inline +void hcd_event_device_attach(uint8_t rhport, bool in_isr) +{ + hcd_event_t event; + event.rhport = rhport; + event.event_id = HCD_EVENT_DEVICE_ATTACH; + event.connection.hub_addr = 0; + event.connection.hub_port = 0; + hcd_event_handler(&event, in_isr); +} + +// Helper to send device removal event +TU_ATTR_ALWAYS_INLINE static inline +void hcd_event_device_remove(uint8_t rhport, bool in_isr) +{ + hcd_event_t event; + event.rhport = rhport; + event.event_id = HCD_EVENT_DEVICE_REMOVE; + event.connection.hub_addr = 0; + event.connection.hub_port = 0; + + hcd_event_handler(&event, in_isr); +} + +// Helper to send USB transfer event +TU_ATTR_ALWAYS_INLINE static inline +void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, uint32_t xferred_bytes, xfer_result_t result, bool in_isr) +{ + hcd_event_t event = + { + .rhport = 0, // TODO correct rhport + .event_id = HCD_EVENT_XFER_COMPLETE, + .dev_addr = dev_addr, + }; + event.xfer_complete.ep_addr = ep_addr; + event.xfer_complete.result = result; + event.xfer_complete.len = xferred_bytes; + + + hcd_event_handler(&event, in_isr); +} + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_HCD_H_ */ diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/hub.c b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/hub.c new file mode 100644 index 0000000000..3da5358b2a --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/hub.c @@ -0,0 +1,490 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if (CFG_TUH_ENABLED && CFG_TUH_HUB) + +#include "hcd.h" +#include "usbh.h" +#include "usbh_classdriver.h" +#include "hub.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +typedef struct +{ + uint8_t itf_num; + uint8_t ep_in; + uint8_t port_count; + uint8_t status_change; // data from status change interrupt endpoint + + hub_port_status_response_t port_status; + hub_status_response_t hub_status; +} hub_interface_t; + +CFG_TUSB_MEM_SECTION static hub_interface_t hub_data[CFG_TUH_HUB]; +CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t _hub_buffer[sizeof(descriptor_hub_desc_t)]; + +TU_ATTR_ALWAYS_INLINE +static inline hub_interface_t* get_itf(uint8_t dev_addr) +{ + return &hub_data[dev_addr-1-CFG_TUH_DEVICE_MAX]; +} + +#if CFG_TUSB_DEBUG >= 2 +static char const* const _hub_feature_str[] = +{ + [HUB_FEATURE_PORT_CONNECTION ] = "PORT_CONNECTION", + [HUB_FEATURE_PORT_ENABLE ] = "PORT_ENABLE", + [HUB_FEATURE_PORT_SUSPEND ] = "PORT_SUSPEND", + [HUB_FEATURE_PORT_OVER_CURRENT ] = "PORT_OVER_CURRENT", + [HUB_FEATURE_PORT_RESET ] = "PORT_RESET", + [HUB_FEATURE_PORT_POWER ] = "PORT_POWER", + [HUB_FEATURE_PORT_LOW_SPEED ] = "PORT_LOW_SPEED", + [HUB_FEATURE_PORT_CONNECTION_CHANGE ] = "PORT_CONNECTION_CHANGE", + [HUB_FEATURE_PORT_ENABLE_CHANGE ] = "PORT_ENABLE_CHANGE", + [HUB_FEATURE_PORT_SUSPEND_CHANGE ] = "PORT_SUSPEND_CHANGE", + [HUB_FEATURE_PORT_OVER_CURRENT_CHANGE ] = "PORT_OVER_CURRENT_CHANGE", + [HUB_FEATURE_PORT_RESET_CHANGE ] = "PORT_RESET_CHANGE", + [HUB_FEATURE_PORT_TEST ] = "PORT_TEST", + [HUB_FEATURE_PORT_INDICATOR ] = "PORT_INDICATOR", +}; +#endif + +//--------------------------------------------------------------------+ +// HUB +//--------------------------------------------------------------------+ +bool hub_port_clear_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + tusb_control_request_t const request = + { + .bmRequestType_bit = + { + .recipient = (hub_port == 0) ? TUSB_REQ_RCPT_DEVICE : TUSB_REQ_RCPT_OTHER, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_OUT + }, + .bRequest = HUB_REQUEST_CLEAR_FEATURE, + .wValue = feature, + .wIndex = hub_port, + .wLength = 0 + }; + + tuh_xfer_t xfer = + { + .daddr = hub_addr, + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = complete_cb, + .user_data = user_data + }; + + TU_LOG2("HUB Clear Feature: %s, addr = %u port = %u\r\n", _hub_feature_str[feature], hub_addr, hub_port); + TU_ASSERT( tuh_control_xfer(&xfer) ); + return true; +} + +bool hub_port_set_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + tusb_control_request_t const request = + { + .bmRequestType_bit = + { + .recipient = (hub_port == 0) ? TUSB_REQ_RCPT_DEVICE : TUSB_REQ_RCPT_OTHER, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_OUT + }, + .bRequest = HUB_REQUEST_SET_FEATURE, + .wValue = feature, + .wIndex = hub_port, + .wLength = 0 + }; + + tuh_xfer_t xfer = + { + .daddr = hub_addr, + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = complete_cb, + .user_data = user_data + }; + + TU_LOG2("HUB Set Feature: %s, addr = %u port = %u\r\n", _hub_feature_str[feature], hub_addr, hub_port); + TU_ASSERT( tuh_control_xfer(&xfer) ); + return true; +} + +bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + tusb_control_request_t const request = + { + .bmRequestType_bit = + { + .recipient = (hub_port == 0) ? TUSB_REQ_RCPT_DEVICE : TUSB_REQ_RCPT_OTHER, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_IN + }, + .bRequest = HUB_REQUEST_GET_STATUS, + .wValue = 0, + .wIndex = hub_port, + .wLength = 4 + }; + + tuh_xfer_t xfer = + { + .daddr = hub_addr, + .ep_addr = 0, + .setup = &request, + .buffer = resp, + .complete_cb = complete_cb, + .user_data = user_data + }; + + TU_LOG2("HUB Get Port Status: addr = %u port = %u\r\n", hub_addr, hub_port); + TU_VERIFY( tuh_control_xfer(&xfer) ); + return true; +} + +//--------------------------------------------------------------------+ +// CLASS-USBH API (don't require to verify parameters) +//--------------------------------------------------------------------+ +void hub_init(void) +{ + tu_memclr(hub_data, sizeof(hub_data)); +} + +bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) +{ + (void) rhport; + + TU_VERIFY(TUSB_CLASS_HUB == itf_desc->bInterfaceClass && + 0 == itf_desc->bInterfaceSubClass); + + // hub driver does not support multiple TT yet + TU_VERIFY(itf_desc->bInterfaceProtocol <= 1); + + // msc driver length is fixed + uint16_t const drv_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_desc_endpoint_t); + TU_ASSERT(drv_len <= max_len); + + //------------- Interrupt Status endpoint -------------// + tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); + + TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && + TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer, 0); + + TU_ASSERT(tuh_edpt_open(dev_addr, desc_ep)); + + hub_interface_t* p_hub = get_itf(dev_addr); + + p_hub->itf_num = itf_desc->bInterfaceNumber; + p_hub->ep_in = desc_ep->bEndpointAddress; + + return true; +} + +void hub_close(uint8_t dev_addr) +{ + TU_VERIFY(dev_addr > CFG_TUH_DEVICE_MAX, ); + hub_interface_t* p_hub = get_itf(dev_addr); + + if (p_hub->ep_in) tu_memclr(p_hub, sizeof( hub_interface_t)); +} + +bool hub_edpt_status_xfer(uint8_t dev_addr) +{ + hub_interface_t* hub_itf = get_itf(dev_addr); + return usbh_edpt_xfer(dev_addr, hub_itf->ep_in, &hub_itf->status_change, 1); +} + + +//--------------------------------------------------------------------+ +// Set Configure +//--------------------------------------------------------------------+ + +static void config_set_port_power (tuh_xfer_t* xfer); +static void config_port_power_complete (tuh_xfer_t* xfer); + +bool hub_set_config(uint8_t dev_addr, uint8_t itf_num) +{ + hub_interface_t* p_hub = get_itf(dev_addr); + TU_ASSERT(itf_num == p_hub->itf_num); + + // Get Hub Descriptor + tusb_control_request_t const request = + { + .bmRequestType_bit = + { + .recipient = TUSB_REQ_RCPT_DEVICE, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_IN + }, + .bRequest = HUB_REQUEST_GET_DESCRIPTOR, + .wValue = 0, + .wIndex = 0, + .wLength = sizeof(descriptor_hub_desc_t) + }; + + tuh_xfer_t xfer = + { + .daddr = dev_addr, + .ep_addr = 0, + .setup = &request, + .buffer = _hub_buffer, + .complete_cb = config_set_port_power, + .user_data = 0 + }; + + TU_ASSERT( tuh_control_xfer(&xfer) ); + + return true; +} + +static void config_set_port_power (tuh_xfer_t* xfer) +{ + TU_ASSERT(XFER_RESULT_SUCCESS == xfer->result, ); + + uint8_t const daddr = xfer->daddr; + hub_interface_t* p_hub = get_itf(daddr); + + // only use number of ports in hub descriptor + descriptor_hub_desc_t const* desc_hub = (descriptor_hub_desc_t const*) _hub_buffer; + p_hub->port_count = desc_hub->bNbrPorts; + + // May need to GET_STATUS + + // Set Port Power to be able to detect connection, starting with port 1 + uint8_t const hub_port = 1; + hub_port_set_feature(daddr, hub_port, HUB_FEATURE_PORT_POWER, config_port_power_complete, 0); +} + +static void config_port_power_complete (tuh_xfer_t* xfer) +{ + TU_ASSERT(XFER_RESULT_SUCCESS == xfer->result, ); + + uint8_t const daddr = xfer->daddr; + hub_interface_t* p_hub = get_itf(daddr); + + if (xfer->setup->wIndex == p_hub->port_count) + { + // All ports are power -> queue notification status endpoint and + // complete the SET CONFIGURATION + TU_ASSERT( usbh_edpt_xfer(daddr, p_hub->ep_in, &p_hub->status_change, 1), ); + + usbh_driver_set_config_complete(daddr, p_hub->itf_num); + }else + { + // power next port + uint8_t const hub_port = (uint8_t) (xfer->setup->wIndex + 1); + hub_port_set_feature(daddr, hub_port, HUB_FEATURE_PORT_POWER, config_port_power_complete, 0); + } +} + +//--------------------------------------------------------------------+ +// Connection Changes +//--------------------------------------------------------------------+ + +static void hub_port_get_status_complete (tuh_xfer_t* xfer); +static void hub_get_status_complete (tuh_xfer_t* xfer); +static void connection_clear_conn_change_complete (tuh_xfer_t* xfer); +static void connection_port_reset_complete (tuh_xfer_t* xfer); + +// callback as response of interrupt endpoint polling +bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) +{ + (void) xferred_bytes; // TODO can be more than 1 for hub with lots of ports + (void) ep_addr; + TU_ASSERT(result == XFER_RESULT_SUCCESS); + + hub_interface_t* p_hub = get_itf(dev_addr); + + TU_LOG2(" Hub Status Change = 0x%02X\r\n", p_hub->status_change); + + // Hub bit 0 is for the hub device events + if (tu_bit_test(p_hub->status_change, 0)) + { + if (hub_port_get_status(dev_addr, 0, &p_hub->hub_status, hub_get_status_complete, 0) == false) + { + //Hub status control transfer failed, retry + hub_edpt_status_xfer(dev_addr); + } + } + else + { + // Hub bits 1 to n are hub port events + for (uint8_t port=1; port <= p_hub->port_count; port++) + { + if ( tu_bit_test(p_hub->status_change, port) ) + { + if (hub_port_get_status(dev_addr, port, &p_hub->port_status, hub_port_get_status_complete, 0) == false) + { + //Hub status control transfer failed, retry + hub_edpt_status_xfer(dev_addr); + } + break; + } + } + } + + // NOTE: next status transfer is queued by usbh.c after handling this request + + return true; +} + +static void hub_clear_feature_complete_stub(tuh_xfer_t* xfer) +{ + TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, ); + hub_edpt_status_xfer(xfer->daddr); +} + +static void hub_get_status_complete (tuh_xfer_t* xfer) +{ + TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, ); + + uint8_t const daddr = xfer->daddr; + hub_interface_t* p_hub = get_itf(daddr); + uint8_t const port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + TU_ASSERT(port_num == 0 , ); + + TU_LOG2("HUB Got hub status, addr = %u, status = %04x\r\n", daddr, p_hub->hub_status.change.value); + + if (p_hub->hub_status.change.local_power_source) + { + TU_LOG2("HUB Local Power Change, addr = %u\r\n", daddr); + hub_port_clear_feature(daddr, port_num, HUB_FEATURE_HUB_LOCAL_POWER_CHANGE, hub_clear_feature_complete_stub, 0); + } + else if (p_hub->hub_status.change.over_current) + { + TU_LOG1("HUB Over Current, addr = %u\r\n", daddr); + hub_port_clear_feature(daddr, port_num, HUB_FEATURE_HUB_OVER_CURRENT_CHANGE, hub_clear_feature_complete_stub, 0); + } +} + +static void hub_port_get_status_complete (tuh_xfer_t* xfer) +{ + TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, ); + + uint8_t const daddr = xfer->daddr; + hub_interface_t* p_hub = get_itf(daddr); + uint8_t const port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + + // Connection change + if (p_hub->port_status.change.connection) + { + // Port is powered and enabled + //TU_VERIFY(port_status.status_current.port_power && port_status.status_current.port_enable, ); + + // Acknowledge Port Connection Change + hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_CONNECTION_CHANGE, connection_clear_conn_change_complete, 0); + }else + { + // Clear other port status change interrupts. TODO Not currently handled - just cleared. + if (p_hub->port_status.change.port_enable) + { + hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_ENABLE_CHANGE, hub_clear_feature_complete_stub, 0); + } + else if (p_hub->port_status.change.suspend) + { + hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_SUSPEND_CHANGE, hub_clear_feature_complete_stub, 0); + } + else if (p_hub->port_status.change.over_current) + { + hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_OVER_CURRENT_CHANGE, hub_clear_feature_complete_stub, 0); + } + else if (p_hub->port_status.change.reset) + { + hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_RESET_CHANGE, hub_clear_feature_complete_stub, 0); + } + // Other changes are: L1 state + // TODO clear change + + // prepare for next hub status + // TODO continue with status_change, or maybe we can do it again with status + hub_edpt_status_xfer(daddr); + } +} + +static void connection_clear_conn_change_complete (tuh_xfer_t* xfer) +{ + TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, ); + + uint8_t const daddr = xfer->daddr; + hub_interface_t* p_hub = get_itf(daddr); + uint8_t const port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + + if ( p_hub->port_status.status.connection ) + { + // Reset port if attach event + hub_port_reset(daddr, port_num, connection_port_reset_complete, 0); + }else + { + // submit detach event + hcd_event_t event = + { + .rhport = usbh_get_rhport(daddr), + .event_id = HCD_EVENT_DEVICE_REMOVE, + .connection = + { + .hub_addr = daddr, + .hub_port = port_num + } + }; + + hcd_event_handler(&event, false); + } +} + +static void connection_port_reset_complete (tuh_xfer_t* xfer) +{ + TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, ); + + uint8_t const daddr = xfer->daddr; + // hub_interface_t* p_hub = get_itf(daddr); + uint8_t const port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + + // submit attach event + hcd_event_t event = + { + .rhport = usbh_get_rhport(daddr), + .event_id = HCD_EVENT_DEVICE_ATTACH, + .connection = + { + .hub_addr = daddr, + .hub_port = port_num + } + }; + + hcd_event_handler(&event, false); +} + +#endif diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/hub.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/hub.h new file mode 100644 index 0000000000..390740e1fc --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/hub.h @@ -0,0 +1,219 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +/** \ingroup group_class + * \defgroup ClassDriver_Hub Hub (Host only) + * \details Like most PC's OS, Hub support is completely hidden from Application. In fact, application cannot determine whether + * a device is mounted directly via roothub or via a hub's port. All Hub-related procedures are performed and managed + * by tinyusb stack. Unless you are trying to develop the stack itself, there are nothing else can be used by Application. + * \note Due to my laziness, only 1-level of Hub is supported. In other way, the stack cannot mount a hub via another hub. + * @{ + */ + +#ifndef _TUSB_HUB_H_ +#define _TUSB_HUB_H_ + +#include "common/tusb_common.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//D1...D0: Logical Power Switching Mode +//00: Ganged power switching (all ports’power at +//once) +//01: Individual port power switching +//1X: Reserved. Used only on 1.0 compliant hubs +//that implement no power switching +//D2: Identifies a Compound Device +//0: Hub is not part of a compound device. +//1: Hub is part of a compound device. +//D4...D3: Over-current Protection Mode +//00: Global Over-current Protection. The hub +//reports over-current as a summation of all +//ports’current draw, without a breakdown of +//individual port over-current status. +//01: Individual Port Over-current Protection. The +//hub reports over-current on a per-port basis. +//Each port has an over-current status. +//1X: No Over-current Protection. This option is +//allowed only for bus-powered hubs that do not +//implement over-current protection. +// +//D6...D5: TT Think TIme +//00: TT requires at most 8 FS bit times of inter +//transaction gap on a full-/low-speed +//downstream bus. +//01: TT requires at most 16 FS bit times. +//10: TT requires at most 24 FS bit times. +//11: TT requires at most 32 FS bit times. +//D7: Port Indicators Supported +//0: Port Indicators are not supported on its +//downstream facing ports and the +//PORT_INDICATOR request has no effect. +//1: Port Indicators are supported on its +//downstream facing ports and the +//PORT_INDICATOR request controls the +//indicators. See Section 11.5.3. +//D15...D8: Reserved + +typedef struct TU_ATTR_PACKED{ + uint8_t bLength ; ///< Size of descriptor + uint8_t bDescriptorType ; ///< Other_speed_Configuration Type + uint8_t bNbrPorts; + uint16_t wHubCharacteristics; + uint8_t bPwrOn2PwrGood; + uint8_t bHubContrCurrent; + uint8_t DeviceRemovable; // bitmap each bit for a port (from bit1) + uint8_t PortPwrCtrlMask; // just for compatibility, should be 0xff +} descriptor_hub_desc_t; + +TU_VERIFY_STATIC( sizeof(descriptor_hub_desc_t) == 9, "size is not correct"); + +enum { + HUB_REQUEST_GET_STATUS = 0 , + HUB_REQUEST_CLEAR_FEATURE = 1 , + + HUB_REQUEST_SET_FEATURE = 3 , + + HUB_REQUEST_GET_DESCRIPTOR = 6 , + HUB_REQUEST_SET_DESCRIPTOR = 7 , + HUB_REQUEST_CLEAR_TT_BUFFER = 8 , + HUB_REQUEST_RESET_TT = 9 , + HUB_REQUEST_GET_TT_STATE = 10 , + HUB_REQUEST_STOP_TT = 11 +}; + +enum { + HUB_FEATURE_HUB_LOCAL_POWER_CHANGE = 0, + HUB_FEATURE_HUB_OVER_CURRENT_CHANGE +}; + +enum{ + HUB_FEATURE_PORT_CONNECTION = 0, + HUB_FEATURE_PORT_ENABLE = 1, + HUB_FEATURE_PORT_SUSPEND = 2, + HUB_FEATURE_PORT_OVER_CURRENT = 3, + HUB_FEATURE_PORT_RESET = 4, + + HUB_FEATURE_PORT_POWER = 8, + HUB_FEATURE_PORT_LOW_SPEED = 9, + + HUB_FEATURE_PORT_CONNECTION_CHANGE = 16, + HUB_FEATURE_PORT_ENABLE_CHANGE = 17, + HUB_FEATURE_PORT_SUSPEND_CHANGE = 18, + HUB_FEATURE_PORT_OVER_CURRENT_CHANGE = 19, + HUB_FEATURE_PORT_RESET_CHANGE = 20, + HUB_FEATURE_PORT_TEST = 21, + HUB_FEATURE_PORT_INDICATOR = 22 +}; + +// data in response of HUB_REQUEST_GET_STATUS, wIndex = 0 (hub) +typedef struct { + union{ + struct TU_ATTR_PACKED { + uint16_t local_power_source : 1; + uint16_t over_current : 1; + uint16_t : 14; + }; + + uint16_t value; + } status, change; +} hub_status_response_t; + +TU_VERIFY_STATIC( sizeof(hub_status_response_t) == 4, "size is not correct"); + +// data in response of HUB_REQUEST_GET_STATUS, wIndex = Port num +typedef struct { + union { + struct TU_ATTR_PACKED { + uint16_t connection : 1; + uint16_t port_enable : 1; + uint16_t suspend : 1; + uint16_t over_current : 1; + uint16_t reset : 1; + + uint16_t : 3; + uint16_t port_power : 1; + uint16_t low_speed : 1; + uint16_t high_speed : 1; + uint16_t port_test_mode : 1; + uint16_t port_indicator_control : 1; + uint16_t TU_RESERVED : 3; + }; + + uint16_t value; + } status, change; +} hub_port_status_response_t; + +TU_VERIFY_STATIC( sizeof(hub_port_status_response_t) == 4, "size is not correct"); + +// Clear feature +bool hub_port_clear_feature (uint8_t hub_addr, uint8_t hub_port, uint8_t feature, + tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +// Set feature +bool hub_port_set_feature (uint8_t hub_addr, uint8_t hub_port, uint8_t feature, + tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +// Get port status +bool hub_port_get_status (uint8_t hub_addr, uint8_t hub_port, void* resp, + tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +// Get status from Interrupt endpoint +bool hub_edpt_status_xfer(uint8_t dev_addr); + +// Reset a port +static inline bool hub_port_reset(uint8_t hub_addr, uint8_t hub_port, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + return hub_port_set_feature(hub_addr, hub_port, HUB_FEATURE_PORT_RESET, complete_cb, user_data); +} + +// Clear Reset Change +static inline bool hub_port_clear_reset_change(uint8_t hub_addr, uint8_t hub_port, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + return hub_port_clear_feature(hub_addr, hub_port, HUB_FEATURE_PORT_RESET_CHANGE, complete_cb, user_data); +} + + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void hub_init (void); +bool hub_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len); +bool hub_set_config (uint8_t dev_addr, uint8_t itf_num); +bool hub_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); +void hub_close (uint8_t dev_addr); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_HUB_H_ */ + +/** @} */ diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/usbh.c b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/usbh.c new file mode 100644 index 0000000000..12b0ed203c --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/usbh.c @@ -0,0 +1,1633 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if CFG_TUH_ENABLED + +#include "host/hcd.h" +#include "tusb.h" +#include "host/usbh_classdriver.h" +#include "hub.h" + +//--------------------------------------------------------------------+ +// USBH Configuration +//--------------------------------------------------------------------+ + +#ifndef CFG_TUH_TASK_QUEUE_SZ +#define CFG_TUH_TASK_QUEUE_SZ 16 +#endif + +#ifndef CFG_TUH_INTERFACE_MAX +#define CFG_TUH_INTERFACE_MAX 8 +#endif + +// Debug level, TUSB_CFG_DEBUG must be at least this level for debug message +#define USBH_DEBUG 2 + +#define TU_LOG_USBH(...) TU_LOG(USBH_DEBUG, __VA_ARGS__) + +//--------------------------------------------------------------------+ +// USBH-HCD common data structure +//--------------------------------------------------------------------+ + +// device0 struct must be strictly a subset of normal device struct +// TODO refactor later +typedef struct +{ + // port + uint8_t rhport; + uint8_t hub_addr; + uint8_t hub_port; + uint8_t speed; + + struct TU_ATTR_PACKED + { + volatile uint8_t connected : 1; + volatile uint8_t addressed : 1; + volatile uint8_t configured : 1; + volatile uint8_t suspended : 1; + }; + +} usbh_dev0_t; + +typedef struct { + // port, must be same layout as usbh_dev0_t + uint8_t rhport; + uint8_t hub_addr; + uint8_t hub_port; + uint8_t speed; + + // Device State + struct TU_ATTR_PACKED { + volatile uint8_t connected : 1; + volatile uint8_t addressed : 1; + volatile uint8_t configured : 1; + volatile uint8_t suspended : 1; + }; + + // Device Descriptor + uint8_t ep0_size; + + uint16_t vid; + uint16_t pid; + + uint8_t i_manufacturer; + uint8_t i_product; + uint8_t i_serial; + + // Configuration Descriptor + // uint8_t interface_count; // bNumInterfaces alias + + // Endpoint & Interface + uint8_t itf2drv[CFG_TUH_INTERFACE_MAX]; // map interface number to driver (0xff is invalid) + uint8_t ep2drv[CFG_TUH_ENDPOINT_MAX][2]; // map endpoint to driver ( 0xff is invalid ), can use only 4-bit each + + tu_edpt_state_t ep_status[CFG_TUH_ENDPOINT_MAX][2]; + +#if CFG_TUH_API_EDPT_XFER + // TODO array can be CFG_TUH_ENDPOINT_MAX-1 + struct { + tuh_xfer_cb_t complete_cb; + uintptr_t user_data; + }ep_callback[CFG_TUH_ENDPOINT_MAX][2]; +#endif + +} usbh_device_t; + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +// Invalid driver ID in itf2drv[] ep2drv[][] mapping +enum { DRVID_INVALID = 0xFFu }; +enum { CONTROLLER_INVALID = 0xFFu }; + +#if CFG_TUSB_DEBUG >= 2 + #define DRIVER_NAME(_name) .name = _name, +#else + #define DRIVER_NAME(_name) +#endif + +static usbh_class_driver_t const usbh_class_drivers[] = +{ + #if CFG_TUH_CDC + { + DRIVER_NAME("CDC") + .init = cdch_init, + .open = cdch_open, + .set_config = cdch_set_config, + .xfer_cb = cdch_xfer_cb, + .close = cdch_close + }, + #endif + + #if CFG_TUH_MSC + { + DRIVER_NAME("MSC") + .init = msch_init, + .open = msch_open, + .set_config = msch_set_config, + .xfer_cb = msch_xfer_cb, + .close = msch_close + }, + #endif + + #if CFG_TUH_HID + { + DRIVER_NAME("HID") + .init = hidh_init, + .open = hidh_open, + .set_config = hidh_set_config, + .xfer_cb = hidh_xfer_cb, + .close = hidh_close + }, + #endif + + #if CFG_TUH_HUB + { + DRIVER_NAME("HUB") + .init = hub_init, + .open = hub_open, + .set_config = hub_set_config, + .xfer_cb = hub_xfer_cb, + .close = hub_close + }, + #endif + + #if CFG_TUH_VENDOR + { + DRIVER_NAME("VENDOR") + .init = cush_init, + .open = cush_open_subtask, + .xfer_cb = cush_isr, + .close = cush_close + } + #endif +}; + +enum { USBH_CLASS_DRIVER_COUNT = TU_ARRAY_SIZE(usbh_class_drivers) }; + +enum { RESET_DELAY = 500 }; // 200 USB specs say only 50ms but many devices require much longer + +enum { CONFIG_NUM = 1 }; // default to use configuration 1 + + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ + +// sum of end device + hub +#define TOTAL_DEVICES (CFG_TUH_DEVICE_MAX + CFG_TUH_HUB) + +static uint8_t _usbh_controller = CONTROLLER_INVALID; + +// Device with address = 0 for enumeration +static usbh_dev0_t _dev0; + +// all devices excluding zero-address +// hub address start from CFG_TUH_DEVICE_MAX+1 +// TODO: hub can has its own simpler struct to save memory +CFG_TUSB_MEM_SECTION usbh_device_t _usbh_devices[TOTAL_DEVICES]; + +// Mutex for claiming endpoint +#if OSAL_MUTEX_REQUIRED + static osal_mutex_def_t _usbh_mutexdef; + static osal_mutex_t _usbh_mutex; +#else + #define _usbh_mutex NULL +#endif + +// Event queue +// usbh_int_set is used as mutex in OS NONE config +OSAL_QUEUE_DEF(usbh_int_set, _usbh_qdef, CFG_TUH_TASK_QUEUE_SZ, hcd_event_t); +static osal_queue_t _usbh_q; + +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN +static uint8_t _usbh_ctrl_buf[CFG_TUH_ENUMERATION_BUFSIZE]; + +// Control transfers: since most controllers do not support multiple control transfers +// on multiple devices concurrently and control transfers are not used much except for +// enumeration, we will only execute control transfers one at a time. +CFG_TUSB_MEM_SECTION struct +{ + tusb_control_request_t request TU_ATTR_ALIGNED(4); + uint8_t* buffer; + tuh_xfer_cb_t complete_cb; + uintptr_t user_data; + + uint8_t daddr; + volatile uint8_t stage; + volatile uint16_t actual_len; +}_ctrl_xfer; + +//------------- Helper Function -------------// + +TU_ATTR_ALWAYS_INLINE +static inline usbh_device_t* get_device(uint8_t dev_addr) +{ + TU_VERIFY(dev_addr > 0 && dev_addr <= TOTAL_DEVICES, NULL); + return &_usbh_devices[dev_addr-1]; +} + +static bool enum_new_device(hcd_event_t* event); +static void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port); +static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size); +static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); + +#if CFG_TUSB_OS == OPT_OS_NONE +// TODO rework time-related function later +void osal_task_delay(uint32_t msec) +{ + const uint32_t start = hcd_frame_number(_usbh_controller); + while ( ( hcd_frame_number(_usbh_controller) - start ) < msec ) {} +} +#endif + +//--------------------------------------------------------------------+ +// PUBLIC API (Parameter Verification is required) +//--------------------------------------------------------------------+ + +bool tuh_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) +{ + if (hcd_configure) + { + return hcd_configure(rhport, cfg_id, cfg_param); + }else + { + return false; + } +} + +bool tuh_mounted(uint8_t dev_addr) +{ + usbh_device_t* dev = get_device(dev_addr); + TU_VERIFY(dev); + return dev->configured; +} + +bool tuh_vid_pid_get(uint8_t dev_addr, uint16_t* vid, uint16_t* pid) +{ + *vid = *pid = 0; + + usbh_device_t const* dev = get_device(dev_addr); + TU_VERIFY(dev && dev->configured); + + *vid = dev->vid; + *pid = dev->pid; + + return true; +} + +tusb_speed_t tuh_speed_get (uint8_t dev_addr) +{ + usbh_device_t* dev = get_device(dev_addr); + return (tusb_speed_t) (dev ? get_device(dev_addr)->speed : _dev0.speed); +} + +static void clear_device(usbh_device_t* dev) +{ + tu_memclr(dev, sizeof(usbh_device_t)); + memset(dev->itf2drv, DRVID_INVALID, sizeof(dev->itf2drv)); // invalid mapping + memset(dev->ep2drv , DRVID_INVALID, sizeof(dev->ep2drv )); // invalid mapping +} + +bool tuh_inited(void) +{ + return _usbh_controller != CONTROLLER_INVALID; +} + +bool tuh_init(uint8_t controller_id) +{ + // skip if already initialized + if ( tuh_inited() ) return true; + + TU_LOG_USBH("USBH init on controller %u\r\n", controller_id); + TU_LOG_INT(USBH_DEBUG, sizeof(usbh_device_t)); + TU_LOG_INT(USBH_DEBUG, sizeof(hcd_event_t)); + TU_LOG_INT(USBH_DEBUG, sizeof(_ctrl_xfer)); + TU_LOG_INT(USBH_DEBUG, sizeof(tuh_xfer_t)); + TU_LOG_INT(USBH_DEBUG, sizeof(tu_fifo_t)); + TU_LOG_INT(USBH_DEBUG, sizeof(tu_edpt_stream_t)); + + // Event queue + _usbh_q = osal_queue_create( &_usbh_qdef ); + TU_ASSERT(_usbh_q != NULL); + +#if OSAL_MUTEX_REQUIRED + // Init mutex + _usbh_mutex = osal_mutex_create(&_usbh_mutexdef); + TU_ASSERT(_usbh_mutex); +#endif + + // Device + tu_memclr(&_dev0, sizeof(_dev0)); + tu_memclr(_usbh_devices, sizeof(_usbh_devices)); + tu_memclr(&_ctrl_xfer, sizeof(_ctrl_xfer)); + + for(uint8_t i=0; iep_status[epnum][ep_dir].busy = 0; + dev->ep_status[epnum][ep_dir].claimed = 0; + + if ( 0 == epnum ) + { + usbh_control_xfer_cb(event.dev_addr, ep_addr, event.xfer_complete.result, event.xfer_complete.len); + }else + { + uint8_t drv_id = dev->ep2drv[epnum][ep_dir]; + if(drv_id < USBH_CLASS_DRIVER_COUNT) + { + TU_LOG_USBH("%s xfer callback\r\n", usbh_class_drivers[drv_id].name); + usbh_class_drivers[drv_id].xfer_cb(event.dev_addr, ep_addr, event.xfer_complete.result, event.xfer_complete.len); + } + else + { +#if CFG_TUH_API_EDPT_XFER + tuh_xfer_cb_t complete_cb = dev->ep_callback[epnum][ep_dir].complete_cb; + if ( complete_cb ) + { + tuh_xfer_t xfer = + { + .daddr = event.dev_addr, + .ep_addr = ep_addr, + .result = event.xfer_complete.result, + .actual_len = event.xfer_complete.len, + .buflen = 0, // not available + .buffer = NULL, // not available + .complete_cb = complete_cb, + .user_data = dev->ep_callback[epnum][ep_dir].user_data + }; + + complete_cb(&xfer); + }else +#endif + { + // no driver/callback responsible for this transfer + TU_ASSERT(false, ); + } + + } + } + } + } + break; + + case USBH_EVENT_FUNC_CALL: + if ( event.func_call.func ) event.func_call.func(event.func_call.param); + break; + + default: break; + } + +#if CFG_TUSB_OS != OPT_OS_NONE && CFG_TUSB_OS != OPT_OS_PICO + // return if there is no more events, for application to run other background + if (osal_queue_empty(_usbh_q)) return; +#endif + } +} + +//--------------------------------------------------------------------+ +// Control transfer +//--------------------------------------------------------------------+ + +static void _control_blocking_complete_cb(tuh_xfer_t* xfer) +{ + // update result + *((xfer_result_t*) xfer->user_data) = xfer->result; +} + +// TODO timeout_ms is not supported yet +bool tuh_control_xfer (tuh_xfer_t* xfer) +{ + // EP0 with setup packet + TU_VERIFY(xfer->ep_addr == 0 && xfer->setup); + + // pre-check to help reducing mutex lock + TU_VERIFY(_ctrl_xfer.stage == CONTROL_STAGE_IDLE); + + uint8_t const daddr = xfer->daddr; + + (void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER); + + bool const is_idle = (_ctrl_xfer.stage == CONTROL_STAGE_IDLE); + if (is_idle) + { + _ctrl_xfer.stage = CONTROL_STAGE_SETUP; + _ctrl_xfer.daddr = daddr; + _ctrl_xfer.actual_len = 0; + + _ctrl_xfer.request = (*xfer->setup); + _ctrl_xfer.buffer = xfer->buffer; + _ctrl_xfer.complete_cb = xfer->complete_cb; + _ctrl_xfer.user_data = xfer->user_data; + } + + (void) osal_mutex_unlock(_usbh_mutex); + + TU_VERIFY(is_idle); + const uint8_t rhport = usbh_get_rhport(daddr); + + TU_LOG_USBH("[%u:%u] %s: ", rhport, daddr, + (xfer->setup->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && xfer->setup->bRequest <= TUSB_REQ_SYNCH_FRAME) ? + tu_str_std_request[xfer->setup->bRequest] : "Class Request"); + TU_LOG_PTR(USBH_DEBUG, xfer->setup); + TU_LOG_USBH("\r\n"); + + if (xfer->complete_cb) + { + TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t const*) &_ctrl_xfer.request) ); + }else + { + // blocking if complete callback is not provided + // change callback to internal blocking, and result as user argument + volatile xfer_result_t result = XFER_RESULT_INVALID; + + // use user_data to point to xfer_result_t + _ctrl_xfer.user_data = (uintptr_t) &result; + _ctrl_xfer.complete_cb = _control_blocking_complete_cb; + + TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t*) &_ctrl_xfer.request) ); + + while (result == XFER_RESULT_INVALID) + { + // only need to call task if not preempted RTOS + #if CFG_TUSB_OS == OPT_OS_NONE || CFG_TUSB_OS == OPT_OS_PICO + tuh_task(); + #endif + + // TODO probably some timeout to prevent hanged + } + + // update transfer result + xfer->result = result; + xfer->actual_len = _ctrl_xfer.actual_len; + } + + return true; +} + +TU_ATTR_ALWAYS_INLINE static inline void _set_control_xfer_stage(uint8_t stage) +{ + (void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER); + _ctrl_xfer.stage = stage; + (void) osal_mutex_unlock(_usbh_mutex); +} + +static void _xfer_complete(uint8_t daddr, xfer_result_t result) +{ + TU_LOG_USBH("\r\n"); + + // duplicate xfer since user can execute control transfer within callback + tusb_control_request_t const request = _ctrl_xfer.request; + tuh_xfer_t xfer_temp = + { + .daddr = daddr, + .ep_addr = 0, + .result = result, + .setup = &request, + .actual_len = (uint32_t) _ctrl_xfer.actual_len, + .buffer = _ctrl_xfer.buffer, + .complete_cb = _ctrl_xfer.complete_cb, + .user_data = _ctrl_xfer.user_data + }; + + _set_control_xfer_stage(CONTROL_STAGE_IDLE); + + if (xfer_temp.complete_cb) + { + xfer_temp.complete_cb(&xfer_temp); + } +} + +static bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) +{ + (void) ep_addr; + + const uint8_t rhport = usbh_get_rhport(dev_addr); + tusb_control_request_t const * request = &_ctrl_xfer.request; + + if (XFER_RESULT_SUCCESS != result) + { + TU_LOG1("[%u:%u] Control %s, xferred_bytes = %lu\r\n", rhport, dev_addr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED", xferred_bytes); + #if CFG_TUSB_DEBUG == 1 + TU_LOG1_PTR(request); + TU_LOG1("\r\n"); + #endif + + // terminate transfer if any stage failed + _xfer_complete(dev_addr, result); + }else + { + switch(_ctrl_xfer.stage) + { + case CONTROL_STAGE_SETUP: + if (request->wLength) + { + // DATA stage: initial data toggle is always 1 + _set_control_xfer_stage(CONTROL_STAGE_DATA); + TU_ASSERT( hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, request->bmRequestType_bit.direction), _ctrl_xfer.buffer, request->wLength) ); + return true; + } + TU_ATTR_FALLTHROUGH; + + case CONTROL_STAGE_DATA: + if (request->wLength) + { + TU_LOG_USBH("[%u:%u] Control data:\r\n", rhport, dev_addr); + TU_LOG_MEM(USBH_DEBUG, _ctrl_xfer.buffer, xferred_bytes, 2); + } + + _ctrl_xfer.actual_len = (uint16_t) xferred_bytes; + + // ACK stage: toggle is always 1 + _set_control_xfer_stage(CONTROL_STAGE_ACK); + TU_ASSERT( hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, 1-request->bmRequestType_bit.direction), NULL, 0) ); + break; + + case CONTROL_STAGE_ACK: + _xfer_complete(dev_addr, result); + break; + + default: return false; + } + } + + return true; +} + +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ + +bool tuh_edpt_xfer(tuh_xfer_t* xfer) +{ + uint8_t const daddr = xfer->daddr; + uint8_t const ep_addr = xfer->ep_addr; + + TU_VERIFY(daddr && ep_addr); + + TU_VERIFY(usbh_edpt_claim(daddr, ep_addr)); + + if ( !usbh_edpt_xfer_with_callback(daddr, ep_addr, xfer->buffer, (uint16_t) xfer->buflen, xfer->complete_cb, xfer->user_data) ) + { + usbh_edpt_release(daddr, ep_addr); + return false; + } + + return true; +} + +//--------------------------------------------------------------------+ +// USBH API For Class Driver +//--------------------------------------------------------------------+ + +uint8_t usbh_get_rhport(uint8_t dev_addr) +{ + usbh_device_t* dev = get_device(dev_addr); + return dev ? dev->rhport : _dev0.rhport; +} + +uint8_t* usbh_get_enum_buf(void) +{ + return _usbh_ctrl_buf; +} + +void usbh_int_set(bool enabled) +{ + // TODO all host controller if multiple is used + if (enabled) + { + hcd_int_enable(_usbh_controller); + }else + { + hcd_int_disable(_usbh_controller); + } +} + +//--------------------------------------------------------------------+ +// Endpoint API +//--------------------------------------------------------------------+ + +// TODO has some duplication code with device, refactor later +bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr) +{ + usbh_device_t* dev = get_device(dev_addr); + + // addr0 only use tuh_control_xfer + TU_ASSERT(dev); + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + return tu_edpt_claim(&dev->ep_status[epnum][dir], _usbh_mutex); +} + +// TODO has some duplication code with device, refactor later +bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr) +{ + usbh_device_t* dev = get_device(dev_addr); + + // addr0 only use tuh_control_xfer + TU_ASSERT(dev); + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + return tu_edpt_release(&dev->ep_status[epnum][dir], _usbh_mutex); +} + +// TODO has some duplication code with device, refactor later +bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + (void) complete_cb; + (void) user_data; + + usbh_device_t* dev = get_device(dev_addr); + TU_VERIFY(dev); + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + tu_edpt_state_t* ep_state = &dev->ep_status[epnum][dir]; + + TU_LOG_USBH(" Queue EP %02X with %u bytes ... ", ep_addr, total_bytes); + + // Attempt to transfer on a busy endpoint, sound like an race condition ! + TU_ASSERT(ep_state->busy == 0); + + // Set busy first since the actual transfer can be complete before hcd_edpt_xfer() + // could return and USBH task can preempt and clear the busy + ep_state->busy = 1; + +#if CFG_TUH_API_EDPT_XFER + dev->ep_callback[epnum][dir].complete_cb = complete_cb; + dev->ep_callback[epnum][dir].user_data = user_data; +#endif + + if ( hcd_edpt_xfer(dev->rhport, dev_addr, ep_addr, buffer, total_bytes) ) + { + TU_LOG_USBH("OK\r\n"); + return true; + }else + { + // HCD error, mark endpoint as ready to allow next transfer + ep_state->busy = 0; + ep_state->claimed = 0; + TU_LOG1("Failed\r\n"); + TU_BREAKPOINT(); + return false; + } +} + +static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size) +{ + TU_LOG_USBH("[%u:%u] Open EP0 with Size = %u\r\n", usbh_get_rhport(dev_addr), dev_addr, max_packet_size); + + tusb_desc_endpoint_t ep0_desc = + { + .bLength = sizeof(tusb_desc_endpoint_t), + .bDescriptorType = TUSB_DESC_ENDPOINT, + .bEndpointAddress = 0, + .bmAttributes = { .xfer = TUSB_XFER_CONTROL }, + .wMaxPacketSize = max_packet_size, + .bInterval = 0 + }; + + return hcd_edpt_open(usbh_get_rhport(dev_addr), dev_addr, &ep0_desc); +} + +bool tuh_edpt_open(uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep) +{ + TU_ASSERT( tu_edpt_validate(desc_ep, tuh_speed_get(dev_addr)) ); + + return hcd_edpt_open(usbh_get_rhport(dev_addr), dev_addr, desc_ep); +} + +bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr) +{ + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + usbh_device_t* dev = get_device(dev_addr); + TU_VERIFY(dev); + + return dev->ep_status[epnum][dir].busy; +} + +//--------------------------------------------------------------------+ +// HCD Event Handler +//--------------------------------------------------------------------+ + +void hcd_devtree_get_info(uint8_t dev_addr, hcd_devtree_info_t* devtree_info) +{ + usbh_device_t const* dev = get_device(dev_addr); + + if (dev) + { + devtree_info->rhport = dev->rhport; + devtree_info->hub_addr = dev->hub_addr; + devtree_info->hub_port = dev->hub_port; + devtree_info->speed = dev->speed; + }else + { + devtree_info->rhport = _dev0.rhport; + devtree_info->hub_addr = _dev0.hub_addr; + devtree_info->hub_port = _dev0.hub_port; + devtree_info->speed = _dev0.speed; + } +} + +TU_ATTR_FAST_FUNC void hcd_event_handler(hcd_event_t const* event, bool in_isr) +{ + switch (event->event_id) + { + default: + osal_queue_send(_usbh_q, event, in_isr); + break; + } +} + +//--------------------------------------------------------------------+ +// Descriptors Async +//--------------------------------------------------------------------+ + +// generic helper to get a descriptor +// if blocking, user_data could be pointed to xfer_result +static bool _get_descriptor(uint8_t daddr, uint8_t type, uint8_t index, uint16_t language_id, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + tusb_control_request_t const request = + { + .bmRequestType_bit = + { + .recipient = TUSB_REQ_RCPT_DEVICE, + .type = TUSB_REQ_TYPE_STANDARD, + .direction = TUSB_DIR_IN + }, + .bRequest = TUSB_REQ_GET_DESCRIPTOR, + .wValue = tu_htole16( TU_U16(type, index) ), + .wIndex = tu_htole16(language_id), + .wLength = tu_htole16(len) + }; + + tuh_xfer_t xfer = + { + .daddr = daddr, + .ep_addr = 0, + .setup = &request, + .buffer = buffer, + .complete_cb = complete_cb, + .user_data = user_data + }; + + bool const ret = tuh_control_xfer(&xfer); + + // if blocking, user_data could be pointed to xfer_result + if ( !complete_cb && user_data ) + { + *((xfer_result_t*) user_data) = xfer.result; + } + + return ret; +} + +bool tuh_descriptor_get(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + return _get_descriptor(daddr, type, index, 0x0000, buffer, len, complete_cb, user_data); +} + +bool tuh_descriptor_get_device(uint8_t daddr, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + len = tu_min16(len, sizeof(tusb_desc_device_t)); + return tuh_descriptor_get(daddr, TUSB_DESC_DEVICE, 0, buffer, len, complete_cb, user_data); +} + +bool tuh_descriptor_get_configuration(uint8_t daddr, uint8_t index, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + return tuh_descriptor_get(daddr, TUSB_DESC_CONFIGURATION, index, buffer, len, complete_cb, user_data); +} + +//------------- String Descriptor -------------// + +bool tuh_descriptor_get_string(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + return _get_descriptor(daddr, TUSB_DESC_STRING, index, language_id, buffer, len, complete_cb, user_data); +} + +// Get manufacturer string descriptor +bool tuh_descriptor_get_manufacturer_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + usbh_device_t const* dev = get_device(daddr); + TU_VERIFY(dev && dev->i_manufacturer); + return tuh_descriptor_get_string(daddr, dev->i_manufacturer, language_id, buffer, len, complete_cb, user_data); +} + +// Get product string descriptor +bool tuh_descriptor_get_product_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + usbh_device_t const* dev = get_device(daddr); + TU_VERIFY(dev && dev->i_product); + return tuh_descriptor_get_string(daddr, dev->i_product, language_id, buffer, len, complete_cb, user_data); +} + +// Get serial string descriptor +bool tuh_descriptor_get_serial_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + usbh_device_t const* dev = get_device(daddr); + TU_VERIFY(dev && dev->i_serial); + return tuh_descriptor_get_string(daddr, dev->i_serial, language_id, buffer, len, complete_cb, user_data); +} + +// Get HID report descriptor +// if blocking, user_data could be pointed to xfer_result +bool tuh_descriptor_get_hid_report(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + TU_LOG_USBH("HID Get Report Descriptor\r\n"); + tusb_control_request_t const request = + { + .bmRequestType_bit = + { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_STANDARD, + .direction = TUSB_DIR_IN + }, + .bRequest = TUSB_REQ_GET_DESCRIPTOR, + .wValue = tu_htole16(TU_U16(desc_type, index)), + .wIndex = tu_htole16((uint16_t) itf_num), + .wLength = len + }; + + tuh_xfer_t xfer = + { + .daddr = daddr, + .ep_addr = 0, + .setup = &request, + .buffer = buffer, + .complete_cb = complete_cb, + .user_data = user_data + }; + + bool const ret = tuh_control_xfer(&xfer); + + // if blocking, user_data could be pointed to xfer_result + if ( !complete_cb && user_data ) + { + *((xfer_result_t*) user_data) = xfer.result; + } + + return ret; +} + +bool tuh_configuration_set(uint8_t daddr, uint8_t config_num, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + TU_LOG_USBH("Set Configuration = %d\r\n", config_num); + + tusb_control_request_t const request = + { + .bmRequestType_bit = + { + .recipient = TUSB_REQ_RCPT_DEVICE, + .type = TUSB_REQ_TYPE_STANDARD, + .direction = TUSB_DIR_OUT + }, + .bRequest = TUSB_REQ_SET_CONFIGURATION, + .wValue = tu_htole16(config_num), + .wIndex = 0, + .wLength = 0 + }; + + tuh_xfer_t xfer = + { + .daddr = daddr, + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = complete_cb, + .user_data = user_data + }; + + return tuh_control_xfer(&xfer); +} + +//--------------------------------------------------------------------+ +// Descriptor Sync +//--------------------------------------------------------------------+ + +#define _CONTROL_SYNC_API(_async_func, ...) \ + xfer_result_t result = XFER_RESULT_INVALID;\ + TU_VERIFY(_async_func(__VA_ARGS__, NULL, (uintptr_t) &result), XFER_RESULT_TIMEOUT); \ + return (uint8_t) result + +uint8_t tuh_descriptor_get_sync(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len) +{ + _CONTROL_SYNC_API(tuh_descriptor_get, daddr, type, index, buffer, len); +} + +uint8_t tuh_descriptor_get_device_sync(uint8_t daddr, void* buffer, uint16_t len) +{ + _CONTROL_SYNC_API(tuh_descriptor_get_device, daddr, buffer, len); +} + +uint8_t tuh_descriptor_get_configuration_sync(uint8_t daddr, uint8_t index, void* buffer, uint16_t len) +{ + _CONTROL_SYNC_API(tuh_descriptor_get_configuration, daddr, index, buffer, len); +} + +uint8_t tuh_descriptor_get_hid_report_sync(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len) +{ + _CONTROL_SYNC_API(tuh_descriptor_get_hid_report, daddr, itf_num, desc_type, index, buffer, len); +} + +uint8_t tuh_descriptor_get_string_sync(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len) +{ + _CONTROL_SYNC_API(tuh_descriptor_get_string, daddr, index, language_id, buffer, len); +} + +uint8_t tuh_descriptor_get_manufacturer_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len) +{ + _CONTROL_SYNC_API(tuh_descriptor_get_manufacturer_string, daddr, language_id, buffer, len); +} + +uint8_t tuh_descriptor_get_product_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len) +{ + _CONTROL_SYNC_API(tuh_descriptor_get_product_string, daddr, language_id, buffer, len); +} + +uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len) +{ + _CONTROL_SYNC_API(tuh_descriptor_get_serial_string, daddr, language_id, buffer, len); +} + +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ + +TU_ATTR_ALWAYS_INLINE +static inline bool is_hub_addr(uint8_t daddr) +{ + return (CFG_TUH_HUB > 0) && (daddr > CFG_TUH_DEVICE_MAX); +} + +// a device unplugged from rhport:hub_addr:hub_port +static void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port) +{ + //------------- find the all devices (star-network) under port that is unplugged -------------// + // TODO mark as disconnected in ISR, also handle dev0 + for ( uint8_t dev_id = 0; dev_id < TU_ARRAY_SIZE(_usbh_devices); dev_id++ ) + { + usbh_device_t* dev = &_usbh_devices[dev_id]; + uint8_t const dev_addr = dev_id+1; + + // TODO Hub multiple level + if (dev->rhport == rhport && + (hub_addr == 0 || dev->hub_addr == hub_addr) && // hub_addr = 0 means roothub + (hub_port == 0 || dev->hub_port == hub_port) && // hub_port = 0 means all devices of downstream hub + dev->connected) + { + TU_LOG_USBH(" Address = %u\r\n", dev_addr); + + if (is_hub_addr(dev_addr)) + { + TU_LOG(USBH_DEBUG, "HUB address = %u is unmounted\r\n", dev_addr); + // If the device itself is a usb hub, unplug downstream devices. + // FIXME un-roll recursive calls to prevent potential stack overflow + process_device_unplugged(rhport, dev_addr, 0); + }else + { + // Invoke callback before closing driver + if (tuh_umount_cb) tuh_umount_cb(dev_addr); + } + + // Close class driver + for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++) + { + TU_LOG_USBH("%s close\r\n", usbh_class_drivers[drv_id].name); + usbh_class_drivers[drv_id].close(dev_addr); + } + + hcd_device_close(rhport, dev_addr); + clear_device(dev); + // abort on-going control xfer if any + if (_ctrl_xfer.daddr == dev_addr) _set_control_xfer_stage(CONTROL_STAGE_IDLE); + } + } +} + +//--------------------------------------------------------------------+ +// Enumeration Process +// is a lengthy process with a series of control transfer to configure +// newly attached device. +// NOTE: due to the shared _usbh_ctrl_buf, we must complete enumerating +// one device before enumerating another one. +//--------------------------------------------------------------------+ + +enum { + ENUM_IDLE, + ENUM_RESET_1, // 1st reset when attached + //ENUM_HUB_GET_STATUS_1, + ENUM_HUB_CLEAR_RESET_1, + ENUM_ADDR0_DEVICE_DESC, + ENUM_RESET_2, // 2nd reset before set address (not used) + ENUM_HUB_GET_STATUS_2, + ENUM_HUB_CLEAR_RESET_2, + ENUM_SET_ADDR, + + ENUM_GET_DEVICE_DESC, + ENUM_GET_9BYTE_CONFIG_DESC, + ENUM_GET_FULL_CONFIG_DESC, + ENUM_SET_CONFIG, + ENUM_CONFIG_DRIVER +}; + +static bool enum_request_set_addr(void); +static bool _parse_configuration_descriptor (uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg); +static void enum_full_complete(void); + +// process device enumeration +static void process_enumeration(tuh_xfer_t* xfer) +{ + // Retry a few times with transfers in enumeration since device can be unstable when starting up + enum { + ATTEMPT_COUNT_MAX = 3, + ATTEMPT_DELAY_MS = 100 + }; + static uint8_t failed_count = 0; + + if (XFER_RESULT_SUCCESS != xfer->result) + { + // retry if not reaching max attempt + if ( failed_count < ATTEMPT_COUNT_MAX ) + { + failed_count++; + osal_task_delay(ATTEMPT_DELAY_MS); // delay a bit + TU_ASSERT(tuh_control_xfer(xfer), ); + }else + { + enum_full_complete(); + } + return; + } + failed_count = 0; + + uint8_t const daddr = xfer->daddr; + uintptr_t const state = xfer->user_data; + + switch(state) + { +#if CFG_TUH_HUB + //case ENUM_HUB_GET_STATUS_1: break; + + case ENUM_HUB_CLEAR_RESET_1: + { + hub_port_status_response_t port_status; + memcpy(&port_status, _usbh_ctrl_buf, sizeof(hub_port_status_response_t)); + + if ( !port_status.status.connection ) + { + // device unplugged while delaying, nothing else to do + enum_full_complete(); + return; + } + + _dev0.speed = (port_status.status.high_speed) ? TUSB_SPEED_HIGH : + (port_status.status.low_speed ) ? TUSB_SPEED_LOW : TUSB_SPEED_FULL; + + // Acknowledge Port Reset Change + if (port_status.change.reset) + { + hub_port_clear_reset_change(_dev0.hub_addr, _dev0.hub_port, process_enumeration, ENUM_ADDR0_DEVICE_DESC); + } + } + break; + + case ENUM_HUB_GET_STATUS_2: + osal_task_delay(RESET_DELAY); + TU_ASSERT( hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, process_enumeration, ENUM_HUB_CLEAR_RESET_2), ); + break; + + case ENUM_HUB_CLEAR_RESET_2: + { + hub_port_status_response_t port_status; + memcpy(&port_status, _usbh_ctrl_buf, sizeof(hub_port_status_response_t)); + + // Acknowledge Port Reset Change if Reset Successful + if (port_status.change.reset) + { + TU_ASSERT( hub_port_clear_reset_change(_dev0.hub_addr, _dev0.hub_port, process_enumeration, ENUM_SET_ADDR), ); + } + } + break; +#endif + + case ENUM_ADDR0_DEVICE_DESC: + { + // TODO probably doesn't need to open/close each enumeration + uint8_t const addr0 = 0; + TU_ASSERT( usbh_edpt_control_open(addr0, 8), ); + + // Get first 8 bytes of device descriptor for Control Endpoint size + TU_LOG_USBH("Get 8 byte of Device Descriptor\r\n"); + TU_ASSERT(tuh_descriptor_get_device(addr0, _usbh_ctrl_buf, 8, process_enumeration, ENUM_SET_ADDR), ); + } + break; + +#if 0 + case ENUM_RESET_2: + // TODO not used by now, but may be needed for some devices !? + // Reset device again before Set Address + TU_LOG_USBH("Port reset2 \r\n"); + if (_dev0.hub_addr == 0) + { + // connected directly to roothub + hcd_port_reset( _dev0.rhport ); + osal_task_delay(RESET_DELAY); // TODO may not work for no-OS on MCU that require reset_end() since + // sof of controller may not running while resetting + hcd_port_reset_end(_dev0.rhport); + // TODO: fall through to SET ADDRESS, refactor later + } + #if CFG_TUH_HUB + else + { + // after RESET_DELAY the hub_port_reset() already complete + TU_ASSERT( hub_port_reset(_dev0.hub_addr, _dev0.hub_port, process_enumeration, ENUM_HUB_GET_STATUS_2), ); + break; + } + #endif + TU_ATTR_FALLTHROUGH; +#endif + + case ENUM_SET_ADDR: + enum_request_set_addr(); + break; + + case ENUM_GET_DEVICE_DESC: + { + uint8_t const new_addr = (uint8_t) tu_le16toh(xfer->setup->wValue); + + usbh_device_t* new_dev = get_device(new_addr); + TU_ASSERT(new_dev, ); + new_dev->addressed = 1; + + // Close device 0 + hcd_device_close(_dev0.rhport, 0); + + // open control pipe for new address + TU_ASSERT( usbh_edpt_control_open(new_addr, new_dev->ep0_size), ); + + // Get full device descriptor + TU_LOG_USBH("Get Device Descriptor\r\n"); + TU_ASSERT(tuh_descriptor_get_device(new_addr, _usbh_ctrl_buf, sizeof(tusb_desc_device_t), process_enumeration, ENUM_GET_9BYTE_CONFIG_DESC), ); + } + break; + + case ENUM_GET_9BYTE_CONFIG_DESC: + { + tusb_desc_device_t const * desc_device = (tusb_desc_device_t const*) _usbh_ctrl_buf; + usbh_device_t* dev = get_device(daddr); + TU_ASSERT(dev, ); + + dev->vid = desc_device->idVendor; + dev->pid = desc_device->idProduct; + dev->i_manufacturer = desc_device->iManufacturer; + dev->i_product = desc_device->iProduct; + dev->i_serial = desc_device->iSerialNumber; + + // if (tuh_attach_cb) tuh_attach_cb((tusb_desc_device_t*) _usbh_ctrl_buf); + + // Get 9-byte for total length + uint8_t const config_idx = CONFIG_NUM - 1; + TU_LOG_USBH("Get Configuration[0] Descriptor (9 bytes)\r\n"); + TU_ASSERT( tuh_descriptor_get_configuration(daddr, config_idx, _usbh_ctrl_buf, 9, process_enumeration, ENUM_GET_FULL_CONFIG_DESC), ); + } + break; + + case ENUM_GET_FULL_CONFIG_DESC: + { + uint8_t const * desc_config = _usbh_ctrl_buf; + + // Use offsetof to avoid pointer to the odd/misaligned address + uint16_t const total_len = tu_le16toh( tu_unaligned_read16(desc_config + offsetof(tusb_desc_configuration_t, wTotalLength)) ); + + // TODO not enough buffer to hold configuration descriptor + TU_ASSERT(total_len <= CFG_TUH_ENUMERATION_BUFSIZE, ); + + // Get full configuration descriptor + uint8_t const config_idx = CONFIG_NUM - 1; + TU_LOG_USBH("Get Configuration[0] Descriptor\r\n"); + TU_ASSERT( tuh_descriptor_get_configuration(daddr, config_idx, _usbh_ctrl_buf, total_len, process_enumeration, ENUM_SET_CONFIG), ); + } + break; + + case ENUM_SET_CONFIG: + // Parse configuration & set up drivers + // Driver open aren't allowed to make any usb transfer yet + TU_ASSERT( _parse_configuration_descriptor(daddr, (tusb_desc_configuration_t*) _usbh_ctrl_buf), ); + + TU_ASSERT( tuh_configuration_set(daddr, CONFIG_NUM, process_enumeration, ENUM_CONFIG_DRIVER), ); + break; + + case ENUM_CONFIG_DRIVER: + { + TU_LOG_USBH("Device configured\r\n"); + usbh_device_t* dev = get_device(daddr); + TU_ASSERT(dev, ); + + dev->configured = 1; + + // Start the Set Configuration process for interfaces (itf = DRVID_INVALID) + // Since driver can perform control transfer within its set_config, this is done asynchronously. + // The process continue with next interface when class driver complete its sequence with usbh_driver_set_config_complete() + // TODO use separated API instead of using DRVID_INVALID + usbh_driver_set_config_complete(daddr, DRVID_INVALID); + } + break; + + default: + // stop enumeration if unknown state + enum_full_complete(); + break; + } +} + +static bool enum_new_device(hcd_event_t* event) +{ + _dev0.rhport = event->rhport; + _dev0.hub_addr = event->connection.hub_addr; + _dev0.hub_port = event->connection.hub_port; + + if (_dev0.hub_addr == 0) + { + // connected/disconnected directly with roothub + // wait until device is stable TODO non blocking + hcd_port_reset(_dev0.rhport); + osal_task_delay(RESET_DELAY); // TODO may not work for no-OS on MCU that require reset_end() since + // sof of controller may not running while resetting + hcd_port_reset_end( _dev0.rhport); + + // device unplugged while delaying + if ( !hcd_port_connect_status(_dev0.rhport) ) return true; + + _dev0.speed = hcd_port_speed_get(_dev0.rhport ); + TU_LOG_USBH("%s Speed\r\n", tu_str_speed[_dev0.speed]); + + // fake transfer to kick-off the enumeration process + tuh_xfer_t xfer; + xfer.daddr = 0; + xfer.result = XFER_RESULT_SUCCESS; + xfer.user_data = ENUM_ADDR0_DEVICE_DESC; + + process_enumeration(&xfer); + + } +#if CFG_TUH_HUB + else + { + // connected/disconnected via external hub + // wait until device is stable + osal_task_delay(RESET_DELAY); + + // ENUM_HUB_GET_STATUS + //TU_ASSERT( hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, enum_hub_get_status0_complete, 0) ); + TU_ASSERT( hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, process_enumeration, ENUM_HUB_CLEAR_RESET_1) ); + } +#endif // hub + + return true; +} + +static uint8_t get_new_address(bool is_hub) +{ + uint8_t start; + uint8_t end; + if ( is_hub ) + { + start = CFG_TUH_DEVICE_MAX; + end = start + CFG_TUH_HUB; + }else + { + start = 0; + end = start + CFG_TUH_DEVICE_MAX; + } + + for ( uint8_t idx = start; idx < end; idx++) + { + if (!_usbh_devices[idx].connected) return (idx+1); + } + + return 0; // invalid address +} + +static bool enum_request_set_addr(void) +{ + tusb_desc_device_t const * desc_device = (tusb_desc_device_t const*) _usbh_ctrl_buf; + + // Get new address + uint8_t const new_addr = get_new_address(desc_device->bDeviceClass == TUSB_CLASS_HUB); + TU_ASSERT(new_addr != 0); + + TU_LOG_USBH("Set Address = %d\r\n", new_addr); + + usbh_device_t* new_dev = get_device(new_addr); + + new_dev->rhport = _dev0.rhport; + new_dev->hub_addr = _dev0.hub_addr; + new_dev->hub_port = _dev0.hub_port; + new_dev->speed = _dev0.speed; + new_dev->connected = 1; + new_dev->ep0_size = desc_device->bMaxPacketSize0; + + tusb_control_request_t const request = + { + .bmRequestType_bit = + { + .recipient = TUSB_REQ_RCPT_DEVICE, + .type = TUSB_REQ_TYPE_STANDARD, + .direction = TUSB_DIR_OUT + }, + .bRequest = TUSB_REQ_SET_ADDRESS, + .wValue = tu_htole16(new_addr), + .wIndex = 0, + .wLength = 0 + }; + + tuh_xfer_t xfer = + { + .daddr = 0, // dev0 + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = process_enumeration, + .user_data = ENUM_GET_DEVICE_DESC + }; + + TU_ASSERT( tuh_control_xfer(&xfer) ); + + return true; +} + +static bool _parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg) +{ + usbh_device_t* dev = get_device(dev_addr); + + uint16_t const total_len = tu_le16toh(desc_cfg->wTotalLength); + uint8_t const* desc_end = ((uint8_t const*) desc_cfg) + total_len; + uint8_t const* p_desc = tu_desc_next(desc_cfg); + + TU_LOG_USBH("Parsing Configuration descriptor (wTotalLength = %u)\r\n", total_len); + + // parse each interfaces + while( p_desc < desc_end ) + { + uint8_t assoc_itf_count = 1; + + // Class will always starts with Interface Association (if any) and then Interface descriptor + if ( TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc) ) + { + tusb_desc_interface_assoc_t const * desc_iad = (tusb_desc_interface_assoc_t const *) p_desc; + assoc_itf_count = desc_iad->bInterfaceCount; + + p_desc = tu_desc_next(p_desc); // next to Interface + + // IAD's first interface number and class should match with opened interface + //TU_ASSERT(desc_iad->bFirstInterface == desc_itf->bInterfaceNumber && + // desc_iad->bFunctionClass == desc_itf->bInterfaceClass); + } + + TU_ASSERT( TUSB_DESC_INTERFACE == tu_desc_type(p_desc) ); + tusb_desc_interface_t const* desc_itf = (tusb_desc_interface_t const*) p_desc; + +#if CFG_TUH_MIDI + // MIDI has 2 interfaces (Audio Control v1 + MIDIStreaming) but does not have IAD + // manually force associated count = 2 + if (1 == assoc_itf_count && + TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass && + AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass && + AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol) + { + assoc_itf_count = 2; + } +#endif + +#if CFG_TUH_CDC + // Some legacy CDC device does not use IAD but rather use device class as hint to combine 2 interfaces + // manually force associated count = 2 + if (1 == assoc_itf_count && + TUSB_CLASS_CDC == desc_itf->bInterfaceClass && + CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == desc_itf->bInterfaceSubClass) + { + assoc_itf_count = 2; + } +#endif + + uint16_t const drv_len = tu_desc_get_interface_total_len(desc_itf, assoc_itf_count, (uint16_t) (desc_end-p_desc)); + TU_ASSERT(drv_len >= sizeof(tusb_desc_interface_t)); + + // Find driver for this interface + for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++) + { + usbh_class_driver_t const * driver = &usbh_class_drivers[drv_id]; + + if ( driver->open(dev->rhport, dev_addr, desc_itf, drv_len) ) + { + // open successfully + TU_LOG_USBH(" %s opened\r\n", driver->name); + + // bind (associated) interfaces to found driver + for(uint8_t i=0; ibInterfaceNumber+i; + + // Interface number must not be used already + TU_ASSERT( DRVID_INVALID == dev->itf2drv[itf_num] ); + dev->itf2drv[itf_num] = drv_id; + } + + // bind all endpoints to found driver + tu_edpt_bind_driver(dev->ep2drv, desc_itf, drv_len, drv_id); + + break; // exit driver find loop + } + + if( drv_id >= USBH_CLASS_DRIVER_COUNT ) + { + TU_LOG(USBH_DEBUG, "Interface %u: class = %u subclass = %u protocol = %u is not supported\r\n", + desc_itf->bInterfaceNumber, desc_itf->bInterfaceClass, desc_itf->bInterfaceSubClass, desc_itf->bInterfaceProtocol); + } + } + + // next Interface or IAD descriptor + p_desc += drv_len; + } + + return true; +} + +void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num) +{ + usbh_device_t* dev = get_device(dev_addr); + + for(itf_num++; itf_num < CFG_TUH_INTERFACE_MAX; itf_num++) + { + // continue with next valid interface + // IAD binding interface such as CDCs should return itf_num + 1 when complete + // with usbh_driver_set_config_complete() + uint8_t const drv_id = dev->itf2drv[itf_num]; + if (drv_id != DRVID_INVALID) + { + usbh_class_driver_t const * driver = &usbh_class_drivers[drv_id]; + TU_LOG_USBH("%s set config: itf = %u\r\n", driver->name, itf_num); + driver->set_config(dev_addr, itf_num); + break; + } + } + + // all interface are configured + if (itf_num == CFG_TUH_INTERFACE_MAX) + { + enum_full_complete(); + + if (is_hub_addr(dev_addr)) + { + TU_LOG(USBH_DEBUG, "HUB address = %u is mounted\r\n", dev_addr); + }else + { + // Invoke callback if available + if (tuh_mount_cb) tuh_mount_cb(dev_addr); + } + } +} + +static void enum_full_complete(void) +{ +#if CFG_TUH_HUB + // get next hub status + if (_dev0.hub_addr) hub_edpt_status_xfer(_dev0.hub_addr); +#endif + +} + +#endif diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/usbh.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/usbh.h new file mode 100644 index 0000000000..37de7093c5 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/usbh.h @@ -0,0 +1,259 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_USBH_H_ +#define _TUSB_USBH_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include "common/tusb_common.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +// forward declaration +struct tuh_xfer_s; +typedef struct tuh_xfer_s tuh_xfer_t; + +typedef void (*tuh_xfer_cb_t)(tuh_xfer_t* xfer); + +// Note1: layout and order of this will be changed in near future +// it is advised to initialize it using member name +// Note2: not all field is available/meaningful in callback, +// some info is not saved by usbh to save SRAM +struct tuh_xfer_s +{ + uint8_t daddr; + uint8_t ep_addr; + uint8_t TU_RESERVED; // reserved + xfer_result_t result; + + uint32_t actual_len; // excluding setup packet + + union + { + tusb_control_request_t const* setup; // setup packet pointer if control transfer + uint32_t buflen; // expected length if not control transfer (not available in callback) + }; + + uint8_t* buffer; // not available in callback if not control transfer + tuh_xfer_cb_t complete_cb; + uintptr_t user_data; + + // uint32_t timeout_ms; // place holder, not supported yet +}; + +// ConfigID for tuh_config() +enum +{ + TUH_CFGID_RPI_PIO_USB_CONFIGURATION = OPT_MCU_RP2040 << 8 // cfg_param: pio_usb_configuration_t +}; + +//--------------------------------------------------------------------+ +// APPLICATION CALLBACK +//--------------------------------------------------------------------+ + +//TU_ATTR_WEAK uint8_t tuh_attach_cb (tusb_desc_device_t const *desc_device); + +// Invoked when a device is mounted (configured) +TU_ATTR_WEAK void tuh_mount_cb (uint8_t daddr); + +// Invoked when a device failed to mount during enumeration process +// TU_ATTR_WEAK void tuh_mount_failed_cb (uint8_t daddr); + +/// Invoked when a device is unmounted (detached) +TU_ATTR_WEAK void tuh_umount_cb(uint8_t daddr); + +//--------------------------------------------------------------------+ +// APPLICATION API +//--------------------------------------------------------------------+ + +// Configure host stack behavior with dynamic or port-specific parameters. +// Should be called before tuh_init() +// - cfg_id : configure ID (TBD) +// - cfg_param: configure data, structure depends on the ID +bool tuh_configure(uint8_t controller_id, uint32_t cfg_id, const void* cfg_param); + +// Init host stack +bool tuh_init(uint8_t controller_id); + +// Check if host stack is already initialized +bool tuh_inited(void); + +// Task function should be called in main/rtos loop, extended version of tuh_task() +// - timeout_ms: millisecond to wait, zero = no wait, 0xFFFFFFFF = wait forever +// - in_isr: if function is called in ISR +void tuh_task_ext(uint32_t timeout_ms, bool in_isr); + +// Task function should be called in main/rtos loop +TU_ATTR_ALWAYS_INLINE static inline +void tuh_task(void) +{ + tuh_task_ext(UINT32_MAX, false); +} + +#ifndef _TUSB_HCD_H_ +extern void hcd_int_handler(uint8_t rhport); +#endif + +// Interrupt handler, name alias to HCD +#define tuh_int_handler hcd_int_handler + +bool tuh_vid_pid_get(uint8_t daddr, uint16_t* vid, uint16_t* pid); + +tusb_speed_t tuh_speed_get(uint8_t daddr); + +// Check if device is connected and configured +bool tuh_mounted(uint8_t daddr); + +// Check if device is suspended +TU_ATTR_ALWAYS_INLINE static inline +bool tuh_suspended(uint8_t daddr) +{ + // TODO implement suspend & resume on host + (void) daddr; + return false; +} + +// Check if device is ready to communicate with +TU_ATTR_ALWAYS_INLINE static inline +bool tuh_ready(uint8_t daddr) +{ + return tuh_mounted(daddr) && !tuh_suspended(daddr); +} + +//--------------------------------------------------------------------+ +// Transfer API +//--------------------------------------------------------------------+ + +// Submit a control transfer +// - async: complete callback invoked when finished. +// - sync : blocking if complete callback is NULL. +bool tuh_control_xfer(tuh_xfer_t* xfer); + +// Submit a bulk/interrupt transfer +// - async: complete callback invoked when finished. +// - sync : blocking if complete callback is NULL. +bool tuh_edpt_xfer(tuh_xfer_t* xfer); + +// Open an non-control endpoint +bool tuh_edpt_open(uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep); + +// Set Configuration (control transfer) +// config_num = 0 will un-configure device. Note: config_num = config_descriptor_index + 1 +// true on success, false if there is on-going control transfer or incorrect parameters +bool tuh_configuration_set(uint8_t daddr, uint8_t config_num, + tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +//--------------------------------------------------------------------+ +// Descriptors Asynchronous (non-blocking) +//--------------------------------------------------------------------+ + +// Get an descriptor (control transfer) +// true on success, false if there is on-going control transfer or incorrect parameters +bool tuh_descriptor_get(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +// Get device descriptor (control transfer) +// true on success, false if there is on-going control transfer or incorrect parameters +bool tuh_descriptor_get_device(uint8_t daddr, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +// Get configuration descriptor (control transfer) +// true on success, false if there is on-going control transfer or incorrect parameters +bool tuh_descriptor_get_configuration(uint8_t daddr, uint8_t index, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +// Get HID report descriptor (control transfer) +// true on success, false if there is on-going control transfer or incorrect parameters +bool tuh_descriptor_get_hid_report(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +// Get string descriptor (control transfer) +// true on success, false if there is on-going control transfer or incorrect parameters +// Blocking if complete callback is NULL, in this case 'user_data' must contain xfer_result_t variable +bool tuh_descriptor_get_string(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +// Get manufacturer string descriptor (control transfer) +// true on success, false if there is on-going control transfer or incorrect parameters +bool tuh_descriptor_get_manufacturer_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +// Get product string descriptor (control transfer) +// true on success, false if there is on-going control transfer or incorrect parameters +bool tuh_descriptor_get_product_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +// Get serial string descriptor (control transfer) +// true on success, false if there is on-going control transfer or incorrect parameters +bool tuh_descriptor_get_serial_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, + tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +//--------------------------------------------------------------------+ +// Descriptors Synchronous (blocking) +//--------------------------------------------------------------------+ + +// Sync (blocking) version of tuh_descriptor_get() +// return transfer result +uint8_t tuh_descriptor_get_sync(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len); + +// Sync (blocking) version of tuh_descriptor_get_device() +// return transfer result +uint8_t tuh_descriptor_get_device_sync(uint8_t daddr, void* buffer, uint16_t len); + +// Sync (blocking) version of tuh_descriptor_get_configuration() +// return transfer result +uint8_t tuh_descriptor_get_configuration_sync(uint8_t daddr, uint8_t index, void* buffer, uint16_t len); + +// Sync (blocking) version of tuh_descriptor_get_hid_report() +// return transfer result +uint8_t tuh_descriptor_get_hid_report_sync(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len); + +// Sync (blocking) version of tuh_descriptor_get_string() +// return transfer result +uint8_t tuh_descriptor_get_string_sync(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len); + +// Sync (blocking) version of tuh_descriptor_get_manufacturer_string() +// return transfer result +uint8_t tuh_descriptor_get_manufacturer_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len); + +// Sync (blocking) version of tuh_descriptor_get_product_string() +// return transfer result +uint8_t tuh_descriptor_get_product_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len); + +// Sync (blocking) version of tuh_descriptor_get_serial_string() +// return transfer result +uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/usbh_classdriver.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/usbh_classdriver.h new file mode 100644 index 0000000000..be9811641e --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/host/usbh_classdriver.h @@ -0,0 +1,96 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021, Ha Thach (tinyusb.org) + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_USBH_CLASSDRIVER_H_ +#define _TUSB_USBH_CLASSDRIVER_H_ + +#include "osal/osal.h" +#include "common/tusb_fifo.h" +#include "common/tusb_private.h" + +#ifdef __cplusplus + extern "C" { +#endif + +enum { + USBH_EPSIZE_BULK_MAX = (TUH_OPT_HIGH_SPEED ? TUSB_EPSIZE_BULK_HS : TUSB_EPSIZE_BULK_FS) +}; + +//--------------------------------------------------------------------+ +// Class Driver API +//--------------------------------------------------------------------+ + +typedef struct { + #if CFG_TUSB_DEBUG >= 2 + char const* name; + #endif + + void (* const init )(void); + bool (* const open )(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const * itf_desc, uint16_t max_len); + bool (* const set_config )(uint8_t dev_addr, uint8_t itf_num); + bool (* const xfer_cb )(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); + void (* const close )(uint8_t dev_addr); +} usbh_class_driver_t; + +// Call by class driver to tell USBH that it has complete the enumeration +void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num); + +uint8_t usbh_get_rhport(uint8_t dev_addr); + +uint8_t* usbh_get_enum_buf(void); + +void usbh_int_set(bool enabled); + +//--------------------------------------------------------------------+ +// USBH Endpoint API +//--------------------------------------------------------------------+ + +// Submit a usb transfer with callback support, require CFG_TUH_API_EDPT_XFER +bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes, + tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +TU_ATTR_ALWAYS_INLINE +static inline bool usbh_edpt_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) +{ + return usbh_edpt_xfer_with_callback(dev_addr, ep_addr, buffer, total_bytes, NULL, 0); +} + + +// Claim an endpoint before submitting a transfer. +// If caller does not make any transfer, it must release endpoint for others. +bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr); + +// Release claimed endpoint without submitting a transfer +bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr); + +// Check if endpoint transferring is complete +bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/osal/osal.h b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/osal/osal.h new file mode 100644 index 0000000000..afa3826fc4 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/osal/osal.h @@ -0,0 +1,96 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_OSAL_H_ +#define _TUSB_OSAL_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include "common/tusb_common.h" + +typedef void (*osal_task_func_t)( void * ); + +// Timeout +#define OSAL_TIMEOUT_NOTIMEOUT (0) // Return immediately +#define OSAL_TIMEOUT_NORMAL (10) // Default timeout +#define OSAL_TIMEOUT_WAIT_FOREVER (UINT32_MAX) // Wait forever +#define OSAL_TIMEOUT_CONTROL_XFER OSAL_TIMEOUT_WAIT_FOREVER + +// Mutex is required when using a preempted RTOS or MCU has multiple cores +#if (CFG_TUSB_OS == OPT_OS_NONE) && !TUP_MCU_MULTIPLE_CORE + #define OSAL_MUTEX_REQUIRED 0 + #define OSAL_MUTEX_DEF(_name) uint8_t :0 +#else + #define OSAL_MUTEX_REQUIRED 1 + #define OSAL_MUTEX_DEF(_name) osal_mutex_def_t _name +#endif + +// OS thin implementation +#if CFG_TUSB_OS == OPT_OS_NONE + #include "osal_none.h" +#elif CFG_TUSB_OS == OPT_OS_FREERTOS + #include "osal_freertos.h" +#elif CFG_TUSB_OS == OPT_OS_MYNEWT + #include "osal_mynewt.h" +#elif CFG_TUSB_OS == OPT_OS_PICO + #include "osal_pico.h" +#elif CFG_TUSB_OS == OPT_OS_RTTHREAD + #include "osal_rtthread.h" +#elif CFG_TUSB_OS == OPT_OS_RTX4 + #include "osal_rtx4.h" +#elif CFG_TUSB_OS == OPT_OS_CUSTOM + #include "tusb_os_custom.h" // implemented by application +#else + #error OS is not supported yet +#endif + +//--------------------------------------------------------------------+ +// OSAL Porting API +// Should be implemented as static inline function in osal_port.h header +/* + osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef); + bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr); + bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec); + void osal_semaphore_reset(osal_semaphore_t sem_hdl); // TODO removed + + osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef); + bool osal_mutex_lock (osal_mutex_t sem_hdl, uint32_t msec); + bool osal_mutex_unlock(osal_mutex_t mutex_hdl); + + osal_queue_t osal_queue_create(osal_queue_def_t* qdef); + bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec); + bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr); + bool osal_queue_empty(osal_queue_t qhdl); +*/ +//--------------------------------------------------------------------+ + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_OSAL_H_ */ diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c new file mode 100644 index 0000000000..58b153ac3e --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c @@ -0,0 +1,225 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 Ha Thach (tinyusb.org) + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if CFG_TUH_ENABLED && (CFG_TUSB_MCU == OPT_MCU_RP2040) && CFG_TUH_RPI_PIO_USB + +#include "pico.h" +#include "pio_usb.h" +#include "pio_usb_ll.h" + +//--------------------------------------------------------------------+ +// INCLUDE +//--------------------------------------------------------------------+ +#include "osal/osal.h" + +#include "host/hcd.h" +#include "host/usbh.h" + +#define RHPORT_OFFSET 1 +#define RHPORT_PIO(_x) ((_x)-RHPORT_OFFSET) + +static pio_usb_configuration_t pio_host_cfg = PIO_USB_DEFAULT_CONFIG; + +//--------------------------------------------------------------------+ +// HCD API +//--------------------------------------------------------------------+ +bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) +{ + (void) rhport; + TU_VERIFY(cfg_id == TUH_CFGID_RPI_PIO_USB_CONFIGURATION); + memcpy(&pio_host_cfg, cfg_param, sizeof(pio_usb_configuration_t)); + return true; +} + +bool hcd_init(uint8_t rhport) +{ + (void) rhport; + + // To run USB SOF interrupt in core1, call this init in core1 + pio_usb_host_init(&pio_host_cfg); + + return true; +} + +void hcd_port_reset(uint8_t rhport) +{ + uint8_t const pio_rhport = RHPORT_PIO(rhport); + pio_usb_host_port_reset_start(pio_rhport); +} + +void hcd_port_reset_end(uint8_t rhport) +{ + uint8_t const pio_rhport = RHPORT_PIO(rhport); + pio_usb_host_port_reset_end(pio_rhport); +} + +bool hcd_port_connect_status(uint8_t rhport) +{ + uint8_t const pio_rhport = RHPORT_PIO(rhport); + + root_port_t *root = PIO_USB_ROOT_PORT(pio_rhport); + port_pin_status_t line_state = pio_usb_bus_get_line_state(root); + + return line_state != PORT_PIN_SE0; +} + +tusb_speed_t hcd_port_speed_get(uint8_t rhport) +{ + // TODO determine link speed + uint8_t const pio_rhport = RHPORT_PIO(rhport); + return PIO_USB_ROOT_PORT(pio_rhport)->is_fullspeed ? TUSB_SPEED_FULL : TUSB_SPEED_LOW; +} + +// Close all opened endpoint belong to this device +void hcd_device_close(uint8_t rhport, uint8_t dev_addr) +{ + uint8_t const pio_rhport = RHPORT_PIO(rhport); + pio_usb_host_close_device(pio_rhport, dev_addr); +} + +uint32_t hcd_frame_number(uint8_t rhport) +{ + (void) rhport; + return 0; +} + +void hcd_int_enable(uint8_t rhport) +{ + (void) rhport; +} + +void hcd_int_disable(uint8_t rhport) +{ + (void) rhport; +} + +//--------------------------------------------------------------------+ +// Endpoint API +//--------------------------------------------------------------------+ + +bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep) +{ + hcd_devtree_info_t dev_tree; + hcd_devtree_get_info(dev_addr, &dev_tree); + bool const need_pre = (dev_tree.hub_addr && dev_tree.speed == TUSB_SPEED_LOW); + + uint8_t const pio_rhport = RHPORT_PIO(rhport); + return pio_usb_host_endpoint_open(pio_rhport, dev_addr, (uint8_t const*) desc_ep, need_pre); +} + +bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) +{ + uint8_t const pio_rhport = RHPORT_PIO(rhport); + return pio_usb_host_endpoint_transfer(pio_rhport, dev_addr, ep_addr, buffer, buflen); +} + +bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) +{ + uint8_t const pio_rhport = RHPORT_PIO(rhport); + return pio_usb_host_send_setup(pio_rhport, dev_addr, setup_packet); +} + +//bool hcd_edpt_busy(uint8_t dev_addr, uint8_t ep_addr) +//{ +// // EPX is shared, so multiple device addresses and endpoint addresses share that +// // so if any transfer is active on epx, we are busy. Interrupt endpoints have their own +// // EPX so ep->active will only be busy if there is a pending transfer on that interrupt endpoint +// // on that device +// pico_trace("hcd_edpt_busy dev addr %d ep_addr 0x%x\n", dev_addr, ep_addr); +// struct hw_endpoint *ep = get_dev_ep(dev_addr, ep_addr); +// assert(ep); +// bool busy = ep->active; +// pico_trace("busy == %d\n", busy); +// return busy; +//} + +bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr) +{ + (void) dev_addr; + (void) ep_addr; + + return true; +} + +static void __no_inline_not_in_flash_func(handle_endpoint_irq)(root_port_t* rport, xfer_result_t result, volatile uint32_t* ep_reg) +{ + (void) rport; + const uint32_t ep_all = *ep_reg; + + for(uint8_t ep_idx = 0; ep_idx < PIO_USB_EP_POOL_CNT; ep_idx++) + { + uint32_t const mask = (1u << ep_idx); + + if (ep_all & mask) + { + endpoint_t* ep = PIO_USB_ENDPOINT(ep_idx); + hcd_event_xfer_complete(ep->dev_addr, ep->ep_num, ep->actual_len, result, true); + } + } + + // clear all + (*ep_reg) &= ~ep_all; +} + +// IRQ Handler +void __no_inline_not_in_flash_func(pio_usb_host_irq_handler)(uint8_t root_id) +{ + uint8_t const tu_rhport = root_id + 1; + root_port_t* rport = PIO_USB_ROOT_PORT(root_id); + uint32_t const ints = rport->ints; + + if ( ints & PIO_USB_INTS_CONNECT_BITS ) + { + hcd_event_device_attach(tu_rhport, true); + } + + if ( ints & PIO_USB_INTS_DISCONNECT_BITS ) + { + hcd_event_device_remove(tu_rhport, true); + } + + if ( ints & PIO_USB_INTS_ENDPOINT_COMPLETE_BITS ) + { + handle_endpoint_irq(rport, XFER_RESULT_SUCCESS, &rport->ep_complete); + } + + if ( ints & PIO_USB_INTS_ENDPOINT_STALLED_BITS ) + { + handle_endpoint_irq(rport, XFER_RESULT_STALLED, &rport->ep_stalled); + } + + if ( ints & PIO_USB_INTS_ENDPOINT_ERROR_BITS ) + { + handle_endpoint_irq(rport, XFER_RESULT_FAILED, &rport->ep_error); + } + + // clear all + rport->ints &= ~ints; +} + +#endif diff --git a/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/tusb.c b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/tusb.c new file mode 100644 index 0000000000..44b34b0e67 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/tusb.c @@ -0,0 +1,523 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if CFG_TUH_ENABLED || CFG_TUD_ENABLED + +#include "tusb.h" +#include "common/tusb_private.h" + +#if CFG_TUD_ENABLED +#include "device/usbd_pvt.h" +#endif + +#if CFG_TUH_ENABLED +#include "host/usbh_classdriver.h" +#endif + +//--------------------------------------------------------------------+ +// Public API +//--------------------------------------------------------------------+ + +bool tusb_init(void) +{ +#if CFG_TUD_ENABLED && defined(TUD_OPT_RHPORT) + // init device stack CFG_TUSB_RHPORTx_MODE must be defined + TU_ASSERT ( tud_init(TUD_OPT_RHPORT) ); +#endif + +#if CFG_TUH_ENABLED && defined(TUH_OPT_RHPORT) + // init host stack CFG_TUSB_RHPORTx_MODE must be defined + TU_ASSERT( tuh_init(TUH_OPT_RHPORT) ); +#endif + + return true; +} + +bool tusb_inited(void) +{ + bool ret = false; + +#if CFG_TUD_ENABLED + ret = ret || tud_inited(); +#endif + +#if CFG_TUH_ENABLED + ret = ret || tuh_inited(); +#endif + + return ret; +} + +//--------------------------------------------------------------------+ +// Descriptor helper +//--------------------------------------------------------------------+ + +uint8_t const * tu_desc_find(uint8_t const* desc, uint8_t const* end, uint8_t byte1) +{ + while(desc+1 < end) + { + if ( desc[1] == byte1 ) return desc; + desc += desc[DESC_OFFSET_LEN]; + } + return NULL; +} + +uint8_t const * tu_desc_find2(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2) +{ + while(desc+2 < end) + { + if ( desc[1] == byte1 && desc[2] == byte2) return desc; + desc += desc[DESC_OFFSET_LEN]; + } + return NULL; +} + +uint8_t const * tu_desc_find3(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2, uint8_t byte3) +{ + while(desc+3 < end) + { + if (desc[1] == byte1 && desc[2] == byte2 && desc[3] == byte3) return desc; + desc += desc[DESC_OFFSET_LEN]; + } + return NULL; +} + + +//--------------------------------------------------------------------+ +// Endpoint Helper for both Host and Device stack +//--------------------------------------------------------------------+ + +bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex) +{ + (void) mutex; + + // pre-check to help reducing mutex lock + TU_VERIFY((ep_state->busy == 0) && (ep_state->claimed == 0)); + (void) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER); + + // can only claim the endpoint if it is not busy and not claimed yet. + bool const available = (ep_state->busy == 0) && (ep_state->claimed == 0); + if (available) + { + ep_state->claimed = 1; + } + + (void) osal_mutex_unlock(mutex); + + return available; +} + +bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex) +{ + (void) mutex; + + (void) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER); + + // can only release the endpoint if it is claimed and not busy + bool const ret = (ep_state->claimed == 1) && (ep_state->busy == 0); + if (ret) + { + ep_state->claimed = 0; + } + + (void) osal_mutex_unlock(mutex); + + return ret; +} + +bool tu_edpt_validate(tusb_desc_endpoint_t const * desc_ep, tusb_speed_t speed) +{ + uint16_t const max_packet_size = tu_edpt_packet_size(desc_ep); + TU_LOG2(" Open EP %02X with Size = %u\r\n", desc_ep->bEndpointAddress, max_packet_size); + + switch (desc_ep->bmAttributes.xfer) + { + case TUSB_XFER_ISOCHRONOUS: + { + uint16_t const spec_size = (speed == TUSB_SPEED_HIGH ? 1024 : 1023); + TU_ASSERT(max_packet_size <= spec_size); + } + break; + + case TUSB_XFER_BULK: + if (speed == TUSB_SPEED_HIGH) + { + // Bulk highspeed must be EXACTLY 512 + TU_ASSERT(max_packet_size == 512); + }else + { + // TODO Bulk fullspeed can only be 8, 16, 32, 64 + TU_ASSERT(max_packet_size <= 64); + } + break; + + case TUSB_XFER_INTERRUPT: + { + uint16_t const spec_size = (speed == TUSB_SPEED_HIGH ? 1024 : 64); + TU_ASSERT(max_packet_size <= spec_size); + } + break; + + default: return false; + } + + return true; +} + +void tu_edpt_bind_driver(uint8_t ep2drv[][2], tusb_desc_interface_t const* desc_itf, uint16_t desc_len, uint8_t driver_id) +{ + uint8_t const* p_desc = (uint8_t const*) desc_itf; + uint8_t const* desc_end = p_desc + desc_len; + + while( p_desc < desc_end ) + { + if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) ) + { + uint8_t const ep_addr = ((tusb_desc_endpoint_t const*) p_desc)->bEndpointAddress; + + TU_LOG(2, " Bind EP %02x to driver id %u\r\n", ep_addr, driver_id); + ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)] = driver_id; + } + + p_desc = tu_desc_next(p_desc); + } +} + +uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf, uint8_t itf_count, uint16_t max_len) +{ + uint8_t const* p_desc = (uint8_t const*) desc_itf; + uint16_t len = 0; + + while (itf_count--) + { + // Next on interface desc + len += tu_desc_len(desc_itf); + p_desc = tu_desc_next(p_desc); + + while (len < max_len) + { + // return on IAD regardless of itf count + if ( tu_desc_type(p_desc) == TUSB_DESC_INTERFACE_ASSOCIATION ) return len; + + if ( (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) && + ((tusb_desc_interface_t const*) p_desc)->bAlternateSetting == 0 ) + { + break; + } + + len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + } + + return len; +} + +//--------------------------------------------------------------------+ +// Endpoint Stream Helper for both Host and Device stack +//--------------------------------------------------------------------+ + +bool tu_edpt_stream_init(tu_edpt_stream_t* s, bool is_host, bool is_tx, bool overwritable, + void* ff_buf, uint16_t ff_bufsize, uint8_t* ep_buf, uint16_t ep_bufsize) +{ + osal_mutex_t new_mutex = osal_mutex_create(&s->ff_mutex); + (void) new_mutex; + (void) is_tx; + + s->is_host = is_host; + tu_fifo_config(&s->ff, ff_buf, ff_bufsize, 1, overwritable); + tu_fifo_config_mutex(&s->ff, is_tx ? new_mutex : NULL, is_tx ? NULL : new_mutex); + + s->ep_buf = ep_buf; + s->ep_bufsize = ep_bufsize; + + return true; +} + +TU_ATTR_ALWAYS_INLINE static inline +bool stream_claim(tu_edpt_stream_t* s) +{ + if (s->is_host) + { + #if CFG_TUH_ENABLED + return usbh_edpt_claim(s->daddr, s->ep_addr); + #endif + }else + { + #if CFG_TUD_ENABLED + return usbd_edpt_claim(s->rhport, s->ep_addr); + #endif + } + + return false; +} + +TU_ATTR_ALWAYS_INLINE static inline +bool stream_xfer(tu_edpt_stream_t* s, uint16_t count) +{ + if (s->is_host) + { + #if CFG_TUH_ENABLED + return usbh_edpt_xfer(s->daddr, s->ep_addr, count ? s->ep_buf : NULL, count); + #endif + }else + { + #if CFG_TUD_ENABLED + return usbd_edpt_xfer(s->rhport, s->ep_addr, count ? s->ep_buf : NULL, count); + #endif + } + + return false; +} + +TU_ATTR_ALWAYS_INLINE static inline +bool stream_release(tu_edpt_stream_t* s) +{ + if (s->is_host) + { + #if CFG_TUH_ENABLED + return usbh_edpt_release(s->daddr, s->ep_addr); + #endif + }else + { + #if CFG_TUD_ENABLED + return usbd_edpt_release(s->rhport, s->ep_addr); + #endif + } + + return false; +} + +//--------------------------------------------------------------------+ +// Stream Write +//--------------------------------------------------------------------+ + +bool tu_edpt_stream_write_zlp_if_needed(tu_edpt_stream_t* s, uint32_t last_xferred_bytes) +{ + // ZLP condition: no pending data, last transferred bytes is multiple of packet size + TU_VERIFY( !tu_fifo_count(&s->ff) && last_xferred_bytes && (0 == (last_xferred_bytes & (s->ep_packetsize-1))) ); + + TU_VERIFY( stream_claim(s) ); + TU_ASSERT( stream_xfer(s, 0) ); + + return true; +} + +uint32_t tu_edpt_stream_write_xfer(tu_edpt_stream_t* s) +{ + // skip if no data + TU_VERIFY( tu_fifo_count(&s->ff), 0 ); + + // Claim the endpoint + TU_VERIFY( stream_claim(s), 0 ); + + // Pull data from FIFO -> EP buf + uint16_t const count = tu_fifo_read_n(&s->ff, s->ep_buf, s->ep_bufsize); + + if ( count ) + { + TU_ASSERT( stream_xfer(s, count), 0 ); + return count; + }else + { + // Release endpoint since we don't make any transfer + // Note: data is dropped if terminal is not connected + stream_release(s); + return 0; + } +} + +uint32_t tu_edpt_stream_write(tu_edpt_stream_t* s, void const *buffer, uint32_t bufsize) +{ + TU_VERIFY(bufsize); // TODO support ZLP + + uint16_t ret = tu_fifo_write_n(&s->ff, buffer, (uint16_t) bufsize); + + // flush if fifo has more than packet size or + // in rare case: fifo depth is configured too small (which never reach packet size) + if ( (tu_fifo_count(&s->ff) >= s->ep_packetsize) || (tu_fifo_depth(&s->ff) < s->ep_packetsize) ) + { + tu_edpt_stream_write_xfer(s); + } + + return ret; +} + +//--------------------------------------------------------------------+ +// Stream Read +//--------------------------------------------------------------------+ + +uint32_t tu_edpt_stream_read_xfer(tu_edpt_stream_t* s) +{ + uint16_t available = tu_fifo_remaining(&s->ff); + + // Prepare for incoming data but only allow what we can store in the ring buffer. + // TODO Actually we can still carry out the transfer, keeping count of received bytes + // and slowly move it to the FIFO when read(). + // This pre-check reduces endpoint claiming + TU_VERIFY(available >= s->ep_packetsize); + + // claim endpoint + TU_VERIFY(stream_claim(s), 0); + + // get available again since fifo can be changed before endpoint is claimed + available = tu_fifo_remaining(&s->ff); + + if ( available >= s->ep_packetsize ) + { + // multiple of packet size limit by ep bufsize + uint16_t count = (uint16_t) (available & ~(s->ep_packetsize -1)); + count = tu_min16(count, s->ep_bufsize); + + TU_ASSERT( stream_xfer(s, count), 0 ); + + return count; + }else + { + // Release endpoint since we don't make any transfer + stream_release(s); + return 0; + } +} + +uint32_t tu_edpt_stream_read(tu_edpt_stream_t* s, void* buffer, uint32_t bufsize) +{ + uint32_t num_read = tu_fifo_read_n(&s->ff, buffer, (uint16_t) bufsize); + tu_edpt_stream_read_xfer(s); + return num_read; +} + +//--------------------------------------------------------------------+ +// Debug +//--------------------------------------------------------------------+ + +#if CFG_TUSB_DEBUG +#include + +#if CFG_TUSB_DEBUG >= 2 + +char const* const tu_str_speed[] = { "Full", "Low", "High" }; +char const* const tu_str_std_request[] = +{ + "Get Status" , + "Clear Feature" , + "Reserved" , + "Set Feature" , + "Reserved" , + "Set Address" , + "Get Descriptor" , + "Set Descriptor" , + "Get Configuration" , + "Set Configuration" , + "Get Interface" , + "Set Interface" , + "Synch Frame" +}; + +#endif + +static void dump_str_line(uint8_t const* buf, uint16_t count) +{ + tu_printf(" |"); + + // each line is 16 bytes + for(uint16_t i=0; i 64 + #error Control Endpoint Max Packet Size cannot be larger than 64 +#endif + +// To avoid GCC compiler warnings when -pedantic option is used (strict ISO C) +typedef int make_iso_compilers_happy; + +#endif /* _TUSB_OPTION_H_ */ + +/** @} */ diff --git a/keyboards/sekigon/keyboard_quantizer/mini/matrix.c b/keyboards/sekigon/keyboard_quantizer/mini/matrix.c new file mode 100644 index 0000000000..4d748bc20c --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/matrix.c @@ -0,0 +1,163 @@ +// Copyright 2023 sekigon-gonnoc +// SPDX-License-Identifier: GPL-2.0-or-later + +#include QMK_KEYBOARD_H + +#include "print.h" +#include "tusb.h" +#include "pio_usb_ll.h" + +static uint8_t kbd_addr; +static uint8_t kbd_instance; +static int32_t led_count = -1; +static uint8_t hid_report_buffer[64]; +static volatile uint8_t hid_report_size; +static uint8_t hid_instance; +static bool hid_disconnect_flag; +static uint8_t pre_keyreport[8]; +#define LED_BLINK_TIME_MS 50 +#define KQ_PIN_LED 7 +#define MATRIX_MODIFIER_ROW 21 + +extern void busy_wait_us(uint64_t delay_us); +static bool send_led_report(uint8_t* leds); + +void matrix_init_custom(void) { + // Configure LED pin + setPinOutput(KQ_PIN_LED); + writePinHigh(KQ_PIN_LED); +} + +bool matrix_scan_custom(matrix_row_t current_matrix[]) { + bool matrix_has_changed = false; + + // If keyboard is disconnected, clear matrix + if (hid_disconnect_flag) { + for (uint8_t rowIdx = 0; rowIdx < MATRIX_ROWS; rowIdx++) { + if (current_matrix[rowIdx] != 0) { + matrix_has_changed = true; + current_matrix[rowIdx] = 0; + } + } + + hid_disconnect_flag = false; + + return matrix_has_changed; + } + + // If keyboard report is received, apply it to matrix + if (hid_report_size > 0) { + hid_report_size = 0; + dprintf("%02x %02x %02x %02x %02x %02x %02x %02x\n", hid_report_buffer[0], hid_report_buffer[1], hid_report_buffer[2], hid_report_buffer[3], hid_report_buffer[4], hid_report_buffer[5], hid_report_buffer[6], hid_report_buffer[7]); + if (memcmp(pre_keyreport, hid_report_buffer, 8) == 0) { + // no change + matrix_has_changed = false; + + return matrix_has_changed; + } else { + matrix_has_changed = true; + memcpy(pre_keyreport, hid_report_buffer, 8); + } + + // clear all bit + for (uint8_t rowIdx = 0; rowIdx < MATRIX_ROWS; rowIdx++) { + current_matrix[rowIdx] = 0; + } + + // set bits + for (uint8_t keyIdx = 0; keyIdx < 6; keyIdx++) { + uint8_t key = hid_report_buffer[keyIdx + 2]; + uint8_t rowIdx = key / (sizeof(uint8_t) * 8); + uint8_t colIdx = key - rowIdx * (sizeof(uint8_t) * 8); + current_matrix[rowIdx] |= (1 << colIdx); + } + + // modifier bits + current_matrix[MATRIX_MODIFIER_ROW] = hid_report_buffer[0]; + return matrix_has_changed; + } else { + return false; + } + + return matrix_has_changed; +} + +void housekeeping_task_kb(void) { + // Control keyboard indicator LED + static uint8_t keyboard_led; + if (keyboard_led != host_keyboard_leds()) { + uint8_t led_backup = keyboard_led; + keyboard_led = host_keyboard_leds(); + if (!send_led_report(&keyboard_led)) { + keyboard_led = led_backup; + } + } + + // Blink LED when USB reports are received + if (led_count >= 0) { + if (timer_elapsed(led_count) < LED_BLINK_TIME_MS) { + writePinLow(KQ_PIN_LED); + } else if (timer_elapsed(led_count) < 2 * LED_BLINK_TIME_MS) { + writePinHigh(KQ_PIN_LED); + } else { + led_count = -1; + } + } +} + +static bool send_led_report(uint8_t* leds) { + if (kbd_addr != 0) { + return tuh_hid_set_report(kbd_addr, kbd_instance, 0, HID_REPORT_TYPE_OUTPUT, leds, sizeof(*leds)); + } + + return false; +} + +void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) { + dprintf("HID mounted:%d:%d\n", dev_addr, instance); + + if (led_count < 0) { + led_count = timer_read(); + } + + for (int instance = 0; instance < tuh_hid_instance_count(dev_addr); instance++) { + tuh_hid_receive_report(dev_addr, instance); + + uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); + if (itf_protocol == HID_ITF_PROTOCOL_KEYBOARD) { + kbd_addr = dev_addr; + kbd_instance = instance; + } + } +} + +void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) { + dprintf("HID unmounted:%d:%d\n", dev_addr, instance); + memset(pre_keyreport, 0, sizeof(pre_keyreport)); + hid_disconnect_flag = true; + kbd_addr = 0; + kbd_instance = 0; +} + +void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) { + dprintf("Report received\n"); + if (led_count < 0) { + led_count = timer_read(); + } + + if (len > 0 && instance == kbd_instance) { + int cnt = 0; + while (hid_report_size > 0 && cnt++ < 50000) { + busy_wait_us(1); + continue; + } + + hid_instance = instance; + memcpy(hid_report_buffer, report, len); + __DSB(); + // hid_report_size is used as trigger of report parser + hid_report_size = len; + } + + tuh_hid_receive_report(dev_addr, instance); +} diff --git a/keyboards/sekigon/keyboard_quantizer/mini/mcuconf.h b/keyboards/sekigon/keyboard_quantizer/mini/mcuconf.h new file mode 100644 index 0000000000..533213ecf8 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/mcuconf.h @@ -0,0 +1,9 @@ +// Copyright 2023 sekigon-gonnoc +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include_next + +#undef RP_CORE1_START +#define RP_CORE1_START TRUE \ No newline at end of file diff --git a/keyboards/sekigon/keyboard_quantizer/mini/mini.c b/keyboards/sekigon/keyboard_quantizer/mini/mini.c new file mode 100644 index 0000000000..8e56a34b8d --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/mini.c @@ -0,0 +1,20 @@ +// Copyright 2023 sekigon-gonnoc +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "keyboard.h" +#include "pico/stdlib.h" +#include "bootloader.h" +#include "debug.h" + +void keyboard_pre_init_kb(void) { + set_sys_clock_khz(120000, true); +} + +void virtser_recv(uint8_t c) { + if (c == 'b') { + bootloader_jump(); + } else if (c == 'd') { + debug_enable = !debug_enable; + uprintf("Debug %s\n", debug_enable ? "enabled" : "disabled"); + } +} diff --git a/keyboards/sekigon/keyboard_quantizer/mini/readme.md b/keyboards/sekigon/keyboard_quantizer/mini/readme.md new file mode 100644 index 0000000000..6a0036566d --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/readme.md @@ -0,0 +1,26 @@ +# Keyboard Quantizer Mini + +![sekigon/keyboard_quantizer/mini](https://i.gyazo.com/aca68f4592ff92eea99a55d710bd2ae3.jpg) + +*A keyboard converter powered by QMK* + +* Keyboard Maintainer: [sekigon-gonnoc](https://github.com/sekigon-gonnoc) +* Hardware Supported: The hardware of [Keyboard Qauntizer](https://github.com/sekigon-gonnoc/keyboard-quantizer-doc) +* Hardware Availability: Available at [BOOTH](https://nogikes.booth.pm/items/2256612) + +Make example for this keyboard (after setting up your build environment): + + make sekigon/keyboard_quantizer/mini:default + +Flashing example for this keyboard: + + make sekigon/keyboard_quantizer/mini:default:flash + +See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs). + +## Bootloader + +Enter the bootloader in 2 ways: + +* **Keycode in layout**: Press the key mapped to `QK_BOOT` if it is available +* **Virtser**: Send `b` to the device through virtser diff --git a/keyboards/sekigon/keyboard_quantizer/mini/rules.mk b/keyboards/sekigon/keyboard_quantizer/mini/rules.mk new file mode 100644 index 0000000000..710fe0b4f4 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/rules.mk @@ -0,0 +1,27 @@ +SRC += matrix.c c1_main.c c1_usbh.c tusb_os_custom.c +CUSTOM_MATRIX = lite + +OPT_DEFS += -DCRT0_EXTRA_CORES_NUMBER=1 + +SRC += lib/Pico-PIO-USB/src/pio_usb.c +SRC += lib/Pico-PIO-USB/src/pio_usb_host.c +SRC += lib/Pico-PIO-USB/src/usb_crc.c +VPATH += keyboards/sekigon/keyboard_quantizer/mini/lib/Pico-PIO-USB/src + +SRC += lib/tinyusb/src/tusb.c +SRC += lib/tinyusb/src/common/tusb_fifo.c +SRC += lib/tinyusb/src/host/usbh.c +SRC += lib/tinyusb/src/host/hub.c +SRC += lib/tinyusb/src/class/hid/hid_host.c +SRC += lib/tinyusb/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c +VPATH += keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src + +SRC += lib/pico-sdk/src/rp2_common/hardware_dma/dma.c +SRC += lib/pico-sdk/src/host/pico_stdlib/stdlib.c +VPATH += lib/pico-sdk/src/rp2_common/hardware_dma/include +VPATH += lib/pico-sdk/src/rp2_common/hardware_uart/include +VPATH += lib/pico-sdk/src/rp2_common/pico_stdio/include +VPATH += lib/pico-sdk/src/common/pico_stdlib/include +VPATH += lib/pico-sdk/src/common/pico_time/include +VPATH += lib/pico-sdk/src/common/pico_sync/include +VPATH += lib/pico-sdk/src/common/pico_util/include diff --git a/keyboards/sekigon/keyboard_quantizer/mini/tusb_config.h b/keyboards/sekigon/keyboard_quantizer/mini/tusb_config.h new file mode 100644 index 0000000000..ef923171b9 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/tusb_config.h @@ -0,0 +1,85 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * Copyright (c) 2023 sekigon-gonnoc + * + * 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 _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +#define CFG_TUSB_OS OPT_OS_CUSTOM +#define CFG_TUSB_MCU OPT_MCU_RP2040 + +// Disable device stack +#define CFG_TUD_ENABLED 0 + +// Enable host stack with pio-usb if Pico-PIO-USB library is available +#define CFG_TUH_ENABLED 1 +#define CFG_TUH_RPI_PIO_USB 1 + +// CFG_TUSB_DEBUG is defined by compiler in DEBUG build +// #define CFG_TUSB_DEBUG 0 + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// HOST CONFIGURATION +//-------------------------------------------------------------------- + +// Size of buffer to hold descriptors and other data used for enumeration +#define CFG_TUH_ENUMERATION_BUFSIZE 256 + +#define CFG_TUH_HUB 1 +// max device support (excluding hub device) +#define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB ? 4 : 1) // hub typically has 4 ports + +#define CFG_TUH_HID 4 +#define CFG_TUH_HID_EPIN_BUFSIZE 64 +#define CFG_TUH_HID_EPOUT_BUFSIZE 64 + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/keyboards/sekigon/keyboard_quantizer/mini/tusb_os_custom.c b/keyboards/sekigon/keyboard_quantizer/mini/tusb_os_custom.c new file mode 100644 index 0000000000..a591491b90 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/tusb_os_custom.c @@ -0,0 +1,24 @@ +// Copyright 2023 sekigon-gonnoc +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ch.h" +#include "tusb_os_custom.h" + +void osal_task_delay(uint32_t msec) { + chThdSleepMilliseconds(msec); +} + +osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef) { + chMtxObjectInit((mutex_t*)mdef); + return mdef; +} + +bool osal_mutex_lock(osal_mutex_t mutex_hdl, uint32_t msec) { + chMtxLock((mutex_t*)mutex_hdl); + return true; +} + +bool osal_mutex_unlock(osal_mutex_t mutex_hdl) { + chMtxUnlock((mutex_t*)mutex_hdl); + return true; +} diff --git a/keyboards/sekigon/keyboard_quantizer/mini/tusb_os_custom.h b/keyboards/sekigon/keyboard_quantizer/mini/tusb_os_custom.h new file mode 100644 index 0000000000..f604ab7418 --- /dev/null +++ b/keyboards/sekigon/keyboard_quantizer/mini/tusb_os_custom.h @@ -0,0 +1,128 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * Copyright (c) 2023 sekigon-gonnoc + * + * 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. + * + * This file is part of the TinyUSB stack. + */ +#pragma once + +#ifdef __cplusplus + extern "C" { +#endif + + typedef struct { + struct { + void *prev; + void *next; + } queue_t; + void* owner; + void* next; + uint32_t cnt; + } chtemp_mutex_t; + +typedef chtemp_mutex_t osal_mutex_def_t, *osal_mutex_t; +void osal_task_delay(uint32_t msec); +osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef); +bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec); +bool osal_mutex_unlock(osal_mutex_t mutex_hdl); + +//--------------------------------------------------------------------+ +// QUEUE API +//--------------------------------------------------------------------+ + +#include "common/tusb_fifo.h" + +typedef struct +{ + tu_fifo_t ff; + chtemp_mutex_t mutex; +} osal_queue_def_t; + +typedef osal_queue_def_t* osal_queue_t; + +static inline void _osal_q_lock(osal_queue_t qhdl) { + osal_mutex_lock(&qhdl->mutex,0); +} +static inline void _osal_q_unlock(osal_queue_t qhdl){ + osal_mutex_unlock(&qhdl->mutex); +} + +// role device/host is used by OS NONE for mutex (disable usb isr) only +#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \ + uint8_t _name##_buf[_depth*sizeof(_type)]; \ + osal_queue_def_t _name = { \ + .ff = TU_FIFO_INIT(_name##_buf, _depth, _type, false) \ + } + +TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) +{ + tu_fifo_clear(&qdef->ff); + osal_mutex_create(&qdef->mutex); + return (osal_queue_t) qdef; +} + +TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec) +{ + (void) msec; // not used, always behave as msec = 0 + + // TODO: revisit... docs say that mutexes are never used from IRQ context, + // however osal_queue_recieve may be. therefore my assumption is that + // the fifo mutex is not populated for queues used from an IRQ context + //assert(!qhdl->ff.mutex); + + _osal_q_lock(qhdl); + bool success = tu_fifo_read(&qhdl->ff, data); + _osal_q_unlock(qhdl); + + return success; +} + +TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr) +{ + // TODO: revisit... docs say that mutexes are never used from IRQ context, + // however osal_queue_recieve may be. therefore my assumption is that + // the fifo mutex is not populated for queues used from an IRQ context + //assert(!qhdl->ff.mutex); + (void) in_isr; + + _osal_q_lock(qhdl); + bool success = tu_fifo_write(&qhdl->ff, data); + _osal_q_unlock(qhdl); + + TU_ASSERT(success); + + return success; +} + +TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl) +{ + // TODO: revisit; whether this is true or not currently, tu_fifo_empty is a single + // volatile read. + + // Skip queue lock/unlock since this function is primarily called + // with interrupt disabled before going into low power mode + return tu_fifo_empty(&qhdl->ff); +} +#ifdef __cplusplus + } +#endif \ No newline at end of file