[Keyboard] !!! USB HOST !!! Quantizer Mini (20999)
This commit is contained in:
parent
7ca90e1d27
commit
822093fa8d
8
keyboards/sekigon/keyboard_quantizer/mini/c1.h
Normal file
8
keyboards/sekigon/keyboard_quantizer/mini/c1.h
Normal file
@ -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);
|
48
keyboards/sekigon/keyboard_quantizer/mini/c1_main.c
Normal file
48
keyboards/sekigon/keyboard_quantizer/mini/c1_main.c
Normal file
@ -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);
|
||||
}
|
24
keyboards/sekigon/keyboard_quantizer/mini/c1_usbh.c
Normal file
24
keyboards/sekigon/keyboard_quantizer/mini/c1_usbh.c
Normal file
@ -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();
|
||||
}
|
20
keyboards/sekigon/keyboard_quantizer/mini/config.h
Normal file
20
keyboards/sekigon/keyboard_quantizer/mini/config.h
Normal file
@ -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
|
259
keyboards/sekigon/keyboard_quantizer/mini/info.json
Normal file
259
keyboards/sekigon/keyboard_quantizer/mini/info.json
Normal file
@ -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 }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -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"
|
||||
]
|
||||
]
|
||||
}
|
@ -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.
|
@ -0,0 +1,466 @@
|
||||
/**
|
||||
* Copyright (c) 2021 sekigon-gonnoc
|
||||
*/
|
||||
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize("-O3")
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h> // 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<<c->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
|
@ -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
|
@ -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
|
@ -0,0 +1,498 @@
|
||||
/**
|
||||
* Copyright (c) 2021 sekigon-gonnoc
|
||||
* Ha Thach (thach@tinyusb.org)
|
||||
*/
|
||||
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize("-O3")
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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
|
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
// Copyright 2021 sekigon-gonnoc
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#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;
|
||||
}
|
@ -0,0 +1,342 @@
|
||||
// Copyright 2021 sekigon-gonnoc
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#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;
|
@ -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);
|
||||
}
|
||||
|
||||
%}
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
%}
|
@ -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
|
||||
|
@ -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.
|
File diff suppressed because it is too large
Load Diff
@ -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; i<size; i++) TU_LOG(3, "%02X ", desc_report[i]);
|
||||
TU_LOG(3, "\r\n");
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case RI_TYPE_MAIN:
|
||||
switch (tag)
|
||||
{
|
||||
case RI_MAIN_INPUT: break;
|
||||
case RI_MAIN_OUTPUT: break;
|
||||
case RI_MAIN_FEATURE: break;
|
||||
|
||||
case RI_MAIN_COLLECTION:
|
||||
ri_collection_depth++;
|
||||
break;
|
||||
|
||||
case RI_MAIN_COLLECTION_END:
|
||||
ri_collection_depth--;
|
||||
if (ri_collection_depth == 0)
|
||||
{
|
||||
info++;
|
||||
report_num++;
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
|
||||
case RI_TYPE_GLOBAL:
|
||||
switch(tag)
|
||||
{
|
||||
case RI_GLOBAL_USAGE_PAGE:
|
||||
// only take in account the "usage page" before REPORT ID
|
||||
if ( ri_collection_depth == 0 ) memcpy(&info->usage_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
|
@ -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_ */
|
@ -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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// 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_ */
|
@ -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 <intrinsics.h>
|
||||
#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_ */
|
||||
|
||||
/// @}
|
@ -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<bufsize; i++) tu_printf("%02X ", buf[i]);
|
||||
}
|
||||
|
||||
// Log with Level
|
||||
#define TU_LOG(n, ...) TU_XSTRCAT(TU_LOG, n)(__VA_ARGS__)
|
||||
#define TU_LOG_MEM(n, ...) TU_XSTRCAT3(TU_LOG, n, _MEM)(__VA_ARGS__)
|
||||
#define TU_LOG_ARR(n, ...) TU_XSTRCAT3(TU_LOG, n, _ARR)(__VA_ARGS__)
|
||||
#define TU_LOG_PTR(n, ...) TU_XSTRCAT3(TU_LOG, n, _PTR)(__VA_ARGS__)
|
||||
#define TU_LOG_INT(n, ...) TU_XSTRCAT3(TU_LOG, n, _INT)(__VA_ARGS__)
|
||||
#define TU_LOG_HEX(n, ...) TU_XSTRCAT3(TU_LOG, n, _HEX)(__VA_ARGS__)
|
||||
#define TU_LOG_LOCATION() tu_printf("%s: %d:\r\n", __PRETTY_FUNCTION__, __LINE__)
|
||||
#define TU_LOG_FAILED() tu_printf("%s: %d: Failed\r\n", __PRETTY_FUNCTION__, __LINE__)
|
||||
|
||||
// Log Level 1: Error
|
||||
#define TU_LOG1 tu_printf
|
||||
#define TU_LOG1_MEM tu_print_mem
|
||||
#define TU_LOG1_ARR(_x, _n) tu_print_arr((uint8_t const*)(_x), _n)
|
||||
#define TU_LOG1_PTR(_x) tu_print_arr((uint8_t const*)(_x), sizeof(*(_x)))
|
||||
#define TU_LOG1_INT(_x) tu_printf(#_x " = %ld\r\n", (unsigned long) (_x) )
|
||||
#define TU_LOG1_HEX(_x) tu_printf(#_x " = %lX\r\n", (unsigned long) (_x) )
|
||||
|
||||
// Log Level 2: Warn
|
||||
#if CFG_TUSB_DEBUG >= 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; i<p_table->count; 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_ */
|
File diff suppressed because it is too large
Load Diff
@ -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_ */
|
@ -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
|
@ -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_ */
|
@ -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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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_ */
|
||||
|
||||
/** @} */
|
@ -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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#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_ */
|
||||
|
||||
/** @} */
|
@ -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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#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 <stdio.h>
|
||||
#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_ */
|
@ -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_ */
|
@ -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
|
@ -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_ */
|
||||
|
||||
/** @} */
|
File diff suppressed because it is too large
Load Diff
@ -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
|
@ -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
|
@ -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_ */
|
@ -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
|
523
keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/tusb.c
Normal file
523
keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/tusb.c
Normal file
@ -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 <ctype.h>
|
||||
|
||||
#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<count; i++)
|
||||
{
|
||||
const char ch = buf[i];
|
||||
tu_printf("%c", isprint(ch) ? ch : '.');
|
||||
}
|
||||
|
||||
tu_printf("|\r\n");
|
||||
}
|
||||
|
||||
/* Print out memory contents
|
||||
* - buf : buffer
|
||||
* - count : number of item
|
||||
* - indent: prefix spaces on every line
|
||||
*/
|
||||
void tu_print_mem(void const *buf, uint32_t count, uint8_t indent)
|
||||
{
|
||||
uint8_t const size = 1; // fixed 1 byte for now
|
||||
|
||||
if ( !buf || !count )
|
||||
{
|
||||
tu_printf("NULL\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t const *buf8 = (uint8_t const *) buf;
|
||||
|
||||
char format[] = "%00X";
|
||||
format[2] += 2*size;
|
||||
|
||||
const uint8_t item_per_line = 16 / size;
|
||||
|
||||
for(unsigned int i=0; i<count; i++)
|
||||
{
|
||||
unsigned int value=0;
|
||||
|
||||
if ( i%item_per_line == 0 )
|
||||
{
|
||||
// Print Ascii
|
||||
if ( i != 0 )
|
||||
{
|
||||
dump_str_line(buf8-16, 16);
|
||||
}
|
||||
|
||||
for(uint8_t s=0; s < indent; s++) tu_printf(" ");
|
||||
|
||||
// print offset or absolute address
|
||||
tu_printf("%04X: ", 16*i/item_per_line);
|
||||
}
|
||||
|
||||
memcpy(&value, buf8, size);
|
||||
buf8 += size;
|
||||
|
||||
tu_printf(" ");
|
||||
tu_printf(format, value);
|
||||
}
|
||||
|
||||
// fill up last row to 16 for printing ascii
|
||||
const uint32_t remain = count%16;
|
||||
uint8_t nback = (uint8_t)(remain ? remain : 16);
|
||||
|
||||
if ( remain )
|
||||
{
|
||||
for(uint32_t i=0; i< 16-remain; i++)
|
||||
{
|
||||
tu_printf(" ");
|
||||
for(int j=0; j<2*size; j++) tu_printf(" ");
|
||||
}
|
||||
}
|
||||
|
||||
dump_str_line(buf8-nback, nback);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // host or device enabled
|
138
keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/tusb.h
Normal file
138
keyboards/sekigon/keyboard_quantizer/mini/lib/tinyusb/src/tusb.h
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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_H_
|
||||
#define _TUSB_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INCLUDE
|
||||
//--------------------------------------------------------------------+
|
||||
#include "common/tusb_common.h"
|
||||
#include "osal/osal.h"
|
||||
#include "common/tusb_fifo.h"
|
||||
|
||||
#include "class/hid/hid.h"
|
||||
|
||||
//------------- HOST -------------//
|
||||
#if CFG_TUH_ENABLED
|
||||
#include "host/usbh.h"
|
||||
|
||||
#if CFG_TUH_HID
|
||||
#include "class/hid/hid_host.h"
|
||||
#endif
|
||||
|
||||
#if CFG_TUH_MSC
|
||||
#include "class/msc/msc_host.h"
|
||||
#endif
|
||||
|
||||
#if CFG_TUH_CDC
|
||||
#include "class/cdc/cdc_host.h"
|
||||
#endif
|
||||
|
||||
#if CFG_TUH_VENDOR
|
||||
#include "class/vendor/vendor_host.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//------------- DEVICE -------------//
|
||||
#if CFG_TUD_ENABLED
|
||||
#include "device/usbd.h"
|
||||
|
||||
#if CFG_TUD_HID
|
||||
#include "class/hid/hid_device.h"
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_CDC
|
||||
#include "class/cdc/cdc_device.h"
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_MSC
|
||||
#include "class/msc/msc_device.h"
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_AUDIO
|
||||
#include "class/audio/audio_device.h"
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VIDEO
|
||||
#include "class/video/video_device.h"
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_MIDI
|
||||
#include "class/midi/midi_device.h"
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR
|
||||
#include "class/vendor/vendor_device.h"
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_USBTMC
|
||||
#include "class/usbtmc/usbtmc_device.h"
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_DFU_RUNTIME
|
||||
#include "class/dfu/dfu_rt_device.h"
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_DFU
|
||||
#include "class/dfu/dfu_device.h"
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_ECM_RNDIS || CFG_TUD_NCM
|
||||
#include "class/net/net_device.h"
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_BTH
|
||||
#include "class/bth/bth_device.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// APPLICATION API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Initialize device/host stack
|
||||
// Note: when using with RTOS, this should be called after scheduler/kernel is started.
|
||||
// Otherwise it could cause kernel issue since USB IRQ handler does use RTOS queue API.
|
||||
bool tusb_init(void);
|
||||
|
||||
// Check if stack is initialized
|
||||
bool tusb_inited(void);
|
||||
|
||||
// TODO
|
||||
// bool tusb_teardown(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_H_ */
|
@ -0,0 +1,440 @@
|
||||
/*
|
||||
* 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_OPTION_H_
|
||||
#define _TUSB_OPTION_H_
|
||||
|
||||
#include "common/tusb_compiler.h"
|
||||
|
||||
#define TUSB_VERSION_MAJOR 0
|
||||
#define TUSB_VERSION_MINOR 15
|
||||
#define TUSB_VERSION_REVISION 0
|
||||
#define TUSB_VERSION_STRING TU_STRING(TUSB_VERSION_MAJOR) "." TU_STRING(TUSB_VERSION_MINOR) "." TU_STRING(TUSB_VERSION_REVISION)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Supported MCUs
|
||||
// CFG_TUSB_MCU must be defined to one of following value
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#define OPT_MCU_NONE 0
|
||||
|
||||
// LPC
|
||||
#define OPT_MCU_LPC11UXX 1 ///< NXP LPC11Uxx
|
||||
#define OPT_MCU_LPC13XX 2 ///< NXP LPC13xx
|
||||
#define OPT_MCU_LPC15XX 3 ///< NXP LPC15xx
|
||||
#define OPT_MCU_LPC175X_6X 4 ///< NXP LPC175x, LPC176x
|
||||
#define OPT_MCU_LPC177X_8X 5 ///< NXP LPC177x, LPC178x
|
||||
#define OPT_MCU_LPC18XX 6 ///< NXP LPC18xx
|
||||
#define OPT_MCU_LPC40XX 7 ///< NXP LPC40xx
|
||||
#define OPT_MCU_LPC43XX 8 ///< NXP LPC43xx
|
||||
#define OPT_MCU_LPC51UXX 9 ///< NXP LPC51U6x
|
||||
#define OPT_MCU_LPC54XXX 10 ///< NXP LPC54xxx
|
||||
#define OPT_MCU_LPC55XX 11 ///< NXP LPC55xx
|
||||
|
||||
// NRF
|
||||
#define OPT_MCU_NRF5X 100 ///< Nordic nRF5x series
|
||||
|
||||
// SAM
|
||||
#define OPT_MCU_SAMD21 200 ///< MicroChip SAMD21
|
||||
#define OPT_MCU_SAMD51 201 ///< MicroChip SAMD51
|
||||
#define OPT_MCU_SAMG 202 ///< MicroChip SAMDG series
|
||||
#define OPT_MCU_SAME5X 203 ///< MicroChip SAM E5x
|
||||
#define OPT_MCU_SAMD11 204 ///< MicroChip SAMD11
|
||||
#define OPT_MCU_SAML22 205 ///< MicroChip SAML22
|
||||
#define OPT_MCU_SAML21 206 ///< MicroChip SAML21
|
||||
#define OPT_MCU_SAMX7X 207 ///< MicroChip SAME70, S70, V70, V71 family
|
||||
|
||||
// STM32
|
||||
#define OPT_MCU_STM32F0 300 ///< ST F0
|
||||
#define OPT_MCU_STM32F1 301 ///< ST F1
|
||||
#define OPT_MCU_STM32F2 302 ///< ST F2
|
||||
#define OPT_MCU_STM32F3 303 ///< ST F3
|
||||
#define OPT_MCU_STM32F4 304 ///< ST F4
|
||||
#define OPT_MCU_STM32F7 305 ///< ST F7
|
||||
#define OPT_MCU_STM32H7 306 ///< ST H7
|
||||
#define OPT_MCU_STM32L1 308 ///< ST L1
|
||||
#define OPT_MCU_STM32L0 307 ///< ST L0
|
||||
#define OPT_MCU_STM32L4 309 ///< ST L4
|
||||
#define OPT_MCU_STM32G0 310 ///< ST G0
|
||||
#define OPT_MCU_STM32G4 311 ///< ST G4
|
||||
#define OPT_MCU_STM32WB 312 ///< ST WB
|
||||
#define OPT_MCU_STM32U5 313 ///< ST U5
|
||||
|
||||
// Sony
|
||||
#define OPT_MCU_CXD56 400 ///< SONY CXD56
|
||||
|
||||
// TI
|
||||
#define OPT_MCU_MSP430x5xx 500 ///< TI MSP430x5xx
|
||||
#define OPT_MCU_MSP432E4 510 ///< TI MSP432E4xx
|
||||
#define OPT_MCU_TM4C123 511 ///< TI Tiva-C 123x
|
||||
#define OPT_MCU_TM4C129 512 ///< TI Tiva-C 129x
|
||||
|
||||
// ValentyUSB eptri
|
||||
#define OPT_MCU_VALENTYUSB_EPTRI 600 ///< Fomu eptri config
|
||||
|
||||
// NXP iMX RT
|
||||
#define OPT_MCU_MIMXRT 700 ///< NXP iMX RT Series
|
||||
#define OPT_MCU_MIMXRT10XX OPT_MCU_MIMXRT ///< RT10xx
|
||||
#define OPT_MCU_MIMXRT11XX OPT_MCU_MIMXRT ///< RT11xx
|
||||
|
||||
// Nuvoton
|
||||
#define OPT_MCU_NUC121 800
|
||||
#define OPT_MCU_NUC126 801
|
||||
#define OPT_MCU_NUC120 802
|
||||
#define OPT_MCU_NUC505 803
|
||||
|
||||
// Espressif
|
||||
#define OPT_MCU_ESP32S2 900 ///< Espressif ESP32-S2
|
||||
#define OPT_MCU_ESP32S3 901 ///< Espressif ESP32-S3
|
||||
|
||||
// Dialog
|
||||
#define OPT_MCU_DA1469X 1000 ///< Dialog Semiconductor DA1469x
|
||||
|
||||
// Raspberry Pi
|
||||
#define OPT_MCU_RP2040 1100 ///< Raspberry Pi RP2040
|
||||
|
||||
// NXP Kinetis
|
||||
#define OPT_MCU_MKL25ZXX 1200 ///< NXP MKL25Zxx
|
||||
#define OPT_MCU_K32L2BXX 1201 ///< NXP K32L2Bxx
|
||||
|
||||
// Silabs
|
||||
#define OPT_MCU_EFM32GG 1300 ///< Silabs EFM32GG
|
||||
|
||||
// Renesas RX
|
||||
#define OPT_MCU_RX63X 1400 ///< Renesas RX63N/631
|
||||
#define OPT_MCU_RX65X 1401 ///< Renesas RX65N/RX651
|
||||
#define OPT_MCU_RX72N 1402 ///< Renesas RX72N
|
||||
|
||||
// Mind Motion
|
||||
#define OPT_MCU_MM32F327X 1500 ///< Mind Motion MM32F327
|
||||
|
||||
// GigaDevice
|
||||
#define OPT_MCU_GD32VF103 1600 ///< GigaDevice GD32VF103
|
||||
|
||||
// Broadcom
|
||||
#define OPT_MCU_BCM2711 1700 ///< Broadcom BCM2711
|
||||
#define OPT_MCU_BCM2835 1701 ///< Broadcom BCM2835
|
||||
#define OPT_MCU_BCM2837 1702 ///< Broadcom BCM2837
|
||||
|
||||
// Infineon
|
||||
#define OPT_MCU_XMC4000 1800 ///< Infineon XMC4000
|
||||
|
||||
// PIC
|
||||
#define OPT_MCU_PIC32MZ 1900 ///< MicroChip PIC32MZ family
|
||||
#define OPT_MCU_PIC32MM 1901 ///< MicroChip PIC32MM family
|
||||
#define OPT_MCU_PIC32MX 1902 ///< MicroChip PIC32MX family
|
||||
#define OPT_MCU_PIC32MK 1903 ///< MicroChip PIC32MK family
|
||||
#define OPT_MCU_PIC24 1910 ///< MicroChip PIC24 family
|
||||
#define OPT_MCU_DSPIC33 1911 ///< MicroChip DSPIC33 family
|
||||
|
||||
// BridgeTek
|
||||
#define OPT_MCU_FT90X 2000 ///< BridgeTek FT90x
|
||||
#define OPT_MCU_FT93X 2001 ///< BridgeTek FT93x
|
||||
|
||||
// Allwinner
|
||||
#define OPT_MCU_F1C100S 2100 ///< Allwinner F1C100s family
|
||||
|
||||
// WCH
|
||||
#define OPT_MCU_CH32V307 2200 ///< WCH CH32V307
|
||||
|
||||
// Helper to check if configured MCU is one of listed
|
||||
// Apply _TU_CHECK_MCU with || as separator to list of input
|
||||
#define _TU_CHECK_MCU(_m) (CFG_TUSB_MCU == _m)
|
||||
#define TU_CHECK_MCU(...) (TU_ARGS_APPLY(_TU_CHECK_MCU, ||, __VA_ARGS__))
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Supported OS
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#define OPT_OS_NONE 1 ///< No RTOS
|
||||
#define OPT_OS_FREERTOS 2 ///< FreeRTOS
|
||||
#define OPT_OS_MYNEWT 3 ///< Mynewt OS
|
||||
#define OPT_OS_CUSTOM 4 ///< Custom OS is implemented by application
|
||||
#define OPT_OS_PICO 5 ///< Raspberry Pi Pico SDK
|
||||
#define OPT_OS_RTTHREAD 6 ///< RT-Thread
|
||||
#define OPT_OS_RTX4 7 ///< Keil RTX 4
|
||||
|
||||
// Allow to use command line to change the config name/location
|
||||
#ifdef CFG_TUSB_CONFIG_FILE
|
||||
#include CFG_TUSB_CONFIG_FILE
|
||||
#else
|
||||
#include "tusb_config.h"
|
||||
#endif
|
||||
|
||||
#include "common/tusb_mcu.h"
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// RootHub Mode Configuration
|
||||
// CFG_TUSB_RHPORTx_MODE contains operation mode and speed for that port
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// Low byte is operational mode
|
||||
#define OPT_MODE_NONE 0x0000 ///< Disabled
|
||||
#define OPT_MODE_DEVICE 0x0001 ///< Device Mode
|
||||
#define OPT_MODE_HOST 0x0002 ///< Host Mode
|
||||
|
||||
// High byte is max operational speed (corresponding to tusb_speed_t)
|
||||
#define OPT_MODE_DEFAULT_SPEED 0x0000 ///< Default (max) speed supported by MCU
|
||||
#define OPT_MODE_LOW_SPEED 0x0100 ///< Low Speed
|
||||
#define OPT_MODE_FULL_SPEED 0x0200 ///< Full Speed
|
||||
#define OPT_MODE_HIGH_SPEED 0x0400 ///< High Speed
|
||||
#define OPT_MODE_SPEED_MASK 0xff00
|
||||
|
||||
//------------- Roothub as Device -------------//
|
||||
|
||||
#if defined(CFG_TUSB_RHPORT0_MODE) && ((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_DEVICE)
|
||||
#define TUD_RHPORT_MODE (CFG_TUSB_RHPORT0_MODE)
|
||||
#define TUD_OPT_RHPORT 0
|
||||
#elif defined(CFG_TUSB_RHPORT1_MODE) && ((CFG_TUSB_RHPORT1_MODE) & OPT_MODE_DEVICE)
|
||||
#define TUD_RHPORT_MODE (CFG_TUSB_RHPORT1_MODE)
|
||||
#define TUD_OPT_RHPORT 1
|
||||
#else
|
||||
#define TUD_RHPORT_MODE OPT_MODE_NONE
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_ENABLED
|
||||
// fallback to use CFG_TUSB_RHPORTx_MODE
|
||||
#define CFG_TUD_ENABLED (TUD_RHPORT_MODE & OPT_MODE_DEVICE)
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_MAX_SPEED
|
||||
// fallback to use CFG_TUSB_RHPORTx_MODE
|
||||
#define CFG_TUD_MAX_SPEED (TUD_RHPORT_MODE & OPT_MODE_SPEED_MASK)
|
||||
#endif
|
||||
|
||||
// For backward compatible
|
||||
#define TUSB_OPT_DEVICE_ENABLED CFG_TUD_ENABLED
|
||||
|
||||
// highspeed support indicator
|
||||
#define TUD_OPT_HIGH_SPEED (CFG_TUD_MAX_SPEED ? (CFG_TUD_MAX_SPEED & OPT_MODE_HIGH_SPEED) : TUP_RHPORT_HIGHSPEED)
|
||||
|
||||
//------------- Roothub as Host -------------//
|
||||
|
||||
#if defined(CFG_TUSB_RHPORT0_MODE) && ((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_HOST)
|
||||
#define TUH_RHPORT_MODE (CFG_TUSB_RHPORT0_MODE)
|
||||
#define TUH_OPT_RHPORT 0
|
||||
#elif defined(CFG_TUSB_RHPORT1_MODE) && ((CFG_TUSB_RHPORT1_MODE) & OPT_MODE_HOST)
|
||||
#define TUH_RHPORT_MODE (CFG_TUSB_RHPORT1_MODE)
|
||||
#define TUH_OPT_RHPORT 1
|
||||
#else
|
||||
#define TUH_RHPORT_MODE OPT_MODE_NONE
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_ENABLED
|
||||
// fallback to use CFG_TUSB_RHPORTx_MODE
|
||||
#define CFG_TUH_ENABLED (TUH_RHPORT_MODE & OPT_MODE_HOST)
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_MAX_SPEED
|
||||
// fallback to use CFG_TUSB_RHPORTx_MODE
|
||||
#define CFG_TUH_MAX_SPEED (TUH_RHPORT_MODE & OPT_MODE_SPEED_MASK)
|
||||
#endif
|
||||
|
||||
// For backward compatible
|
||||
#define TUSB_OPT_HOST_ENABLED CFG_TUH_ENABLED
|
||||
|
||||
// highspeed support indicator
|
||||
#define TUH_OPT_HIGH_SPEED (CFG_TUH_MAX_SPEED ? (CFG_TUH_MAX_SPEED & OPT_MODE_HIGH_SPEED) : TUP_RHPORT_HIGHSPEED)
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// TODO move later
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// TUP_MCU_STRICT_ALIGN will overwrite TUP_ARCH_STRICT_ALIGN.
|
||||
// In case TUP_MCU_STRICT_ALIGN = 1 and TUP_ARCH_STRICT_ALIGN =0, we will not reply on compiler
|
||||
// to generate unaligned access code.
|
||||
// LPC_IP3511 Highspeed cannot access unaligned memory on USB_RAM
|
||||
#if TUD_OPT_HIGH_SPEED && (CFG_TUSB_MCU == OPT_MCU_LPC54XXX || CFG_TUSB_MCU == OPT_MCU_LPC55XX)
|
||||
#define TUP_MCU_STRICT_ALIGN 1
|
||||
#else
|
||||
#define TUP_MCU_STRICT_ALIGN 0
|
||||
#endif
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Common Options (Default)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Debug enable to print out error message
|
||||
#ifndef CFG_TUSB_DEBUG
|
||||
#define CFG_TUSB_DEBUG 0
|
||||
#endif
|
||||
|
||||
// place data in accessible RAM for usb controller
|
||||
#ifndef CFG_TUSB_MEM_SECTION
|
||||
#define CFG_TUSB_MEM_SECTION
|
||||
#endif
|
||||
|
||||
// alignment requirement of buffer used for endpoint transferring
|
||||
#ifndef CFG_TUSB_MEM_ALIGN
|
||||
#define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4)
|
||||
#endif
|
||||
|
||||
// OS selection
|
||||
#ifndef CFG_TUSB_OS
|
||||
#define CFG_TUSB_OS OPT_OS_NONE
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_OS_INC_PATH
|
||||
#define CFG_TUSB_OS_INC_PATH
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Device Options (Default)
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#ifndef CFG_TUD_ENDPOINT0_SIZE
|
||||
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_INTERFACE_MAX
|
||||
#define CFG_TUD_INTERFACE_MAX 16
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_CDC
|
||||
#define CFG_TUD_CDC 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_MSC
|
||||
#define CFG_TUD_MSC 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_HID
|
||||
#define CFG_TUD_HID 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_AUDIO
|
||||
#define CFG_TUD_AUDIO 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_VIDEO
|
||||
#define CFG_TUD_VIDEO 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_MIDI
|
||||
#define CFG_TUD_MIDI 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_VENDOR
|
||||
#define CFG_TUD_VENDOR 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_USBTMC
|
||||
#define CFG_TUD_USBTMC 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_DFU_RUNTIME
|
||||
#define CFG_TUD_DFU_RUNTIME 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_DFU
|
||||
#define CFG_TUD_DFU 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_BTH
|
||||
#define CFG_TUD_BTH 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_ECM_RNDIS
|
||||
#ifdef CFG_TUD_NET
|
||||
#warning "CFG_TUD_NET is renamed to CFG_TUD_ECM_RNDIS"
|
||||
#define CFG_TUD_ECM_RNDIS CFG_TUD_NET
|
||||
#else
|
||||
#define CFG_TUD_ECM_RNDIS 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_NCM
|
||||
#define CFG_TUD_NCM 0
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Host Options (Default)
|
||||
//--------------------------------------------------------------------
|
||||
#if CFG_TUH_ENABLED
|
||||
#ifndef CFG_TUH_DEVICE_MAX
|
||||
#define CFG_TUH_DEVICE_MAX 1
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_ENUMERATION_BUFSIZE
|
||||
#define CFG_TUH_ENUMERATION_BUFSIZE 256
|
||||
#endif
|
||||
#endif // CFG_TUH_ENABLED
|
||||
|
||||
//------------- CLASS -------------//
|
||||
|
||||
#ifndef CFG_TUH_HUB
|
||||
#define CFG_TUH_HUB 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_CDC
|
||||
#define CFG_TUH_CDC 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_HID
|
||||
#define CFG_TUH_HID 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_MIDI
|
||||
#define CFG_TUH_MIDI 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_MSC
|
||||
#define CFG_TUH_MSC 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_VENDOR
|
||||
#define CFG_TUH_VENDOR 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_API_EDPT_XFER
|
||||
#define CFG_TUH_API_EDPT_XFER 0
|
||||
#endif
|
||||
|
||||
// Enable PIO-USB software host controller
|
||||
#ifndef CFG_TUH_RPI_PIO_USB
|
||||
#define CFG_TUH_RPI_PIO_USB 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_RPI_PIO_USB
|
||||
#define CFG_TUD_RPI_PIO_USB 0
|
||||
#endif
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Configuration Validation
|
||||
//------------------------------------------------------------------
|
||||
#if CFG_TUD_ENDPOINT0_SIZE > 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_ */
|
||||
|
||||
/** @} */
|
163
keyboards/sekigon/keyboard_quantizer/mini/matrix.c
Normal file
163
keyboards/sekigon/keyboard_quantizer/mini/matrix.c
Normal file
@ -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);
|
||||
}
|
9
keyboards/sekigon/keyboard_quantizer/mini/mcuconf.h
Normal file
9
keyboards/sekigon/keyboard_quantizer/mini/mcuconf.h
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2023 sekigon-gonnoc
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include_next <mcuconf.h>
|
||||
|
||||
#undef RP_CORE1_START
|
||||
#define RP_CORE1_START TRUE
|
20
keyboards/sekigon/keyboard_quantizer/mini/mini.c
Normal file
20
keyboards/sekigon/keyboard_quantizer/mini/mini.c
Normal file
@ -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");
|
||||
}
|
||||
}
|
26
keyboards/sekigon/keyboard_quantizer/mini/readme.md
Normal file
26
keyboards/sekigon/keyboard_quantizer/mini/readme.md
Normal file
@ -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
|
27
keyboards/sekigon/keyboard_quantizer/mini/rules.mk
Normal file
27
keyboards/sekigon/keyboard_quantizer/mini/rules.mk
Normal file
@ -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
|
85
keyboards/sekigon/keyboard_quantizer/mini/tusb_config.h
Normal file
85
keyboards/sekigon/keyboard_quantizer/mini/tusb_config.h
Normal file
@ -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_ */
|
24
keyboards/sekigon/keyboard_quantizer/mini/tusb_os_custom.c
Normal file
24
keyboards/sekigon/keyboard_quantizer/mini/tusb_os_custom.c
Normal file
@ -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;
|
||||
}
|
128
keyboards/sekigon/keyboard_quantizer/mini/tusb_os_custom.h
Normal file
128
keyboards/sekigon/keyboard_quantizer/mini/tusb_os_custom.h
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user