/* * Copyright (C) 2023 dweller@cabin.digital * SPDX-License-Identifier: BSD-3-Clause-Clear */ /* TODO: debug and release (-Os) builds! Fucking cmake... */ #include "bits.h" #include #include #include #include #include #include "pico/stdlib.h" #include "pico/multicore.h" #include "pico/util/queue.h" #include "hardware/gpio.h" #include "hardware/pwm.h" #include "hardware/clocks.h" #include "hardware/uart.h" #include "bsp/board.h" #include "tusb.h" #include "uart_tx.pio.h" /* Compilation Config */ #define _VERBOSE 2 /* 0 == off, 1 == info, 2 == debug */ #define _SCROLL_WHEEL_AS_ARROWS 1 /* TODO: move to lib.h */ #define lengthof(arr) (sizeof(arr) / sizeof(arr[0])) #define bit(n) (1 << (n)) /* tusb */ #define MAX_REPORT 8 /* board cfg */ #define UART_KBD_ID uart1 #define UART_KBD_BAUR 1200 #define UART_KBD_DATA_BITS 8 #define UART_KBD_STOP_BITS 1 #define UART_KBD_PARITY UART_PARITY_NONE #define UART_MOUS_BAUD 1200 #define PIN_UART_KBD_TX 4 #define PIN_UART_KBD_RX 5 #define PIN_UART_MOUS_TX 3 #define PIN_LVL_OE 2 #define PIN_BZZ 14 #define SMP_INIT_FLAG 0xDEADC0DE /* Sun5 PC->KBD commands */ #define SUN5_KBD_CMD_RESET 0x01 #define SUN5_KBD_CMD_BELL_ON 0x02 #define SUN5_KBD_CMD_BELL_OFF 0x03 #define SUN5_KBD_CMD_CLICK_ON 0x0A #define SUN5_KBD_CMD_CLICK_OFF 0x0B #define SUN5_KBD_CMD_LED 0x0E #define SUN5_KBD_CMD_LAYOUT 0x0F /* Sun5 KBD->PC commands/responses */ #define SUN5_KBD_CMD_IDLE 0x7F #define SUN5_KBD_CMD_LAYOUT_R 0xFE #define SUN5_KBD_CMD_RESET_R 0xFF #define SUN5_LED_NUM_LOCK bit(0) #define SUN5_LED_COMPOSE bit(1) #define SUN5_LED_SCR_LOCK bit(2) #define SUN5_LED_CAPS_LOCK bit(3) #define SUN5_KBD_LAYOUT_CA_FR 0x03 #define SUN5_KBD_LAYOUT_DK 0x04 #define SUN5_KBD_LAYOUT_FR_BE 0x02 #define SUN5_KBD_LAYOUT_DE 0x04 #define SUN5_KBD_LAYOUT_IT 0x06 #define SUN5_KBD_LAYOUT_JP 0x20 #define SUN5_KBD_LAYOUT_NL 0x07 #define SUN5_KBD_LAYOUT_NO 0x08 #define SUN5_KBD_LAYOUT_PT 0x09 #define SUN5_KBD_LAYOUT_ES_LA 0x0A #define SUN5_KBD_LAYOUT_SE_FI 0x0B #define SUN5_KBD_LAYOUT_CH_FR 0x0C #define SUN5_KBD_LAYOUT_CH_DE 0x0D #define SUN5_KBD_LAYOUT_UK 0x0E #define SUN5_KBD_LAYOUT_US 0x00 #define SUN5_KEY_US_CONTROL 0x4C #define SUN5_KEY_US_SHIFT 0x6E #define SUN5_KEY_US_ALT 0x13 #define SUN5_KEY_US_LTRIG 0x78 #define SUN5_KEY_US_RTRIG 0x7A #define SUN5_KEY_US_COMPOSE 0x43 #define SUN5_KEY_US_GRAPH 0x0D /* Shifted: Alt */ #define USB_BOOT_KEY_LCTRL 0xE0 #define USB_BOOT_KEY_LSHFT 0xE1 #define USB_BOOT_KEY_LALT 0xE2 #define USB_BOOT_KEY_LGUI 0xE3 #define USB_BOOT_KEY_RCTRL 0xE4 #define USB_BOOT_KEY_RSHFT 0xE5 #define USB_BOOT_KEY_RALT 0xE6 #define USB_BOOT_KEY_RGUI 0xE7 enum { SND_TYPE_CLICK = 0, SND_TYPE_BEEP, } snd_packet_type; typedef union { /* FIXME: screw this, not only is 7 bits not enough for a long beep (although u can just 0 it * for infinite (until next 0ms packet) time. bitfields are not in C89 and don't have * defined memory layout. So maybe just make the packet bigger? */ struct { unsigned type : 1; unsigned ms : 7; }; u8 raw; } snd_packet; typedef struct { bool valid; u8 address; u8 instance; } usb_kbd_dev; /* see: "Mouse Systems Optical Mouse Technical Reference Manual" pages 4,5 * P/N 300771-001 */ typedef struct msys_frame_s { u8 button; /* 10000LMR - Left, Middle, Right, 0 = depressed */ s8 dx, dy; /* relative change */ s8 dxp, dyp; /* relative change after dx, dy were sent */ } msys_frame; static PIO uart_pio; static u32 uart_pio_sm; #define USB_KBD_MAX_DEVS 8 static usb_kbd_dev usb_kbd_devs[USB_KBD_MAX_DEVS] = {0}; static bool kbd_inited = false; static bool kbd_click = true; static u8 kbd_status = 0; static const u8 kbd_layout = SUN5_KBD_LAYOUT_US; /* TODO: HID Local code? */ static u32 pwm_slice; static queue_t qsnd; static struct { u8 report_count; tuh_hid_report_info_t report_info[MAX_REPORT]; } hid_info[CFG_TUH_HID]; /* refs: - USB HID Usage Tables, Chapter 10 "Keyboard/Keypad Page (0x07) * - SPARC Keyboard Specification Version 1, Chapter 3.1 Scan Code */ /* TODO: TEST ME */ static const u8 usb2sun5_us[0xE0] = { /* reserved */ 0, /* rollover */ 0, /* POST fail */ 0, /* err:undef */ 0, /* a b c d e f g h i j k l m*/ 0x4D, 0x68, 0x66, 0x4F, 0x38, 0x50, 0x51, 0x52, 0x3D, 0x53, 0x54, 0x55,0x6A, /* n o p q r s t u v w x y z*/ 0x69, 0x3E, 0x3F, 0x36, 0x39, 0x4E, 0x3A, 0x3C, 0x67, 0x37, 0x65, 0x3B,0x64, /* 1 2 3 4 5 6 7 8 9 0 */ 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* return */ /*0x5A,*/ 0x59, /* Enter vs Return, the former doesn't work in OpenBoot */ /* escape */ 0x1D, /* bckspc */ 0x2B, /* tab */ 0x35, /* space */ 0x79, /* - = [ ] \ ??? ; ' ` , . / */ 0x28, 0x29, 0x40, 0x41, 0x58, 0 , 0x56, 0x57, 0x2A, 0x6B, 0x6C, 0x6D, /* caps */ 0x77, /*f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12*/ 0x05, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10, 0x11, 0x12, 0x07, 0x09, 0x0B, /* prt sc */ 0x16, /* scroll */ 0x17, /* XXX: needs to be shifted, default is actually break */ /* pause */ 0x15, /* insert */ 0x5E, /* home */ 0x44, /* XXX: keypad dup */ /* pg up */ 0x46, /* XXX: keypad dup */ /* delete */ 0x32, /*0x42, XXX: acts as backsp*/ /* end */ 0x70, /* XXX: keypad dup */ /* pg dwn */ 0x72, /* arrows: r l d u */ 0x5D, 0x5B, 0x71, 0x45, /* XXX: keypad dup */ /* num lk */ 0x62, /* keypad */ /* / * - + RET */ /*0x2E, 0x2F, 0x47, 0x7D, 0x59,*/ /* 1 2 3 */ /*0x70, 0x71, 0x72,*/ /* 4 5 6 */ /*0x5B, 0x77, 0x78,*/ /* 7 8 9 */ /*0x44, 0x45, 0x46,*/ /* 0 . */ /*0x5E, 0x32,*/ /* Reuse keypad as specials on Sun since I don't use * numpad anyways */ /* COMPOSE GRAPH - + RET */ 0x43, 0x0D, 0x47, 0x7D, 0x59, /* AGAIN FIND CUT */ 0x03, 0x5F, 0x61, /* COPY OPEN PASTE */ 0x33, 0x48, 0x49, /* PROPS UNDO FRONT*/ 0x19, 0x1A, 0x31, /* HELP STOP */ 0x76, 0x01, /* I don't care about the rest */ }; static int log_info(char* fmt, ...) { #if _VERBOSE >= 1 va_list ap; va_start(ap, fmt); return vprintf(fmt, ap); va_end(ap); #else return 0; #endif } static int log_dbg(char* fmt, ...) { #if _VERBOSE >= 2 va_list ap; va_start(ap, fmt); return vprintf(fmt, ap); va_end(ap); #else return 0; #endif } static inline void snd_click(void) { queue_try_add(&qsnd, &(snd_packet){.type = SND_TYPE_CLICK, .ms = 0}); } static inline void snd_beep(u8 ms) { queue_try_add(&qsnd, &(snd_packet){.type = SND_TYPE_BEEP, .ms = ms & 0x7F}); } static void sun5_kbd_handle_cmd(char c) { switch(c) { case SUN5_KBD_CMD_RESET: { const u8 payload[] = {SUN5_KBD_CMD_RESET_R, 0x04, 0x7F}; // ok uart_write_blocking(UART_KBD_ID, payload, lengthof(payload)); log_info("KBD: reset\n"); kbd_inited = true; } break; case SUN5_KBD_CMD_BELL_ON: snd_beep(0); log_info("KBD: bell on\n"); break; case SUN5_KBD_CMD_BELL_OFF: snd_beep(0); log_info("KBD: bell off\n"); break; case SUN5_KBD_CMD_CLICK_ON: kbd_click = true; log_info("KBD: click on\n"); break; case SUN5_KBD_CMD_CLICK_OFF: kbd_click = false; log_info("KBD: click off\n"); break; case SUN5_KBD_CMD_LED: { u8 in; uart_read_blocking(UART_KBD_ID, &in, 1); if(in & SUN5_LED_NUM_LOCK) kbd_status |= KEYBOARD_LED_NUMLOCK; else kbd_status &= ~KEYBOARD_LED_NUMLOCK; if(in & SUN5_LED_COMPOSE) kbd_status |= KEYBOARD_LED_COMPOSE; else kbd_status &= ~KEYBOARD_LED_COMPOSE; if(in & SUN5_LED_SCR_LOCK) kbd_status |= KEYBOARD_LED_SCROLLLOCK; else kbd_status &= ~KEYBOARD_LED_SCROLLLOCK; if(in & SUN5_LED_CAPS_LOCK) kbd_status |= KEYBOARD_LED_CAPSLOCK; else kbd_status &= ~KEYBOARD_LED_CAPSLOCK; /* broadcast to all kbds */ for(u32 i = 0; i < USB_KBD_MAX_DEVS; i++) { if(!usb_kbd_devs[i].valid) continue; log_dbg("KBD: setting USB leds to @%d inst %d\n", usb_kbd_devs[i].address, usb_kbd_devs[i].instance); tuh_hid_set_report(usb_kbd_devs[i].address, usb_kbd_devs[i].instance, 0, HID_REPORT_TYPE_OUTPUT, &kbd_status, sizeof(kbd_status)); /* XXX: tinyusb expects me to know not to clobber something * internal to them without providing docs. Hence: */ sleep_ms(50); } log_info("KBD: LEDs - %c%c%c%c\n", in & SUN5_LED_NUM_LOCK ? 'N' : ' ', in & SUN5_LED_COMPOSE ? 'P' : ' ', in & SUN5_LED_SCR_LOCK ? 'S' : ' ', in & SUN5_LED_CAPS_LOCK ? 'C' : ' '); } break; case SUN5_KBD_CMD_LAYOUT: { const u8 payload[] = {SUN5_KBD_CMD_LAYOUT_R, kbd_layout}; uart_write_blocking(UART_KBD_ID, payload, lengthof(payload)); log_info("KBD: layout req\n"); } break; default: log_info("KBD: !!UNKNOWN CMD!! %x\n", c); break; } } void core1_entry(void) { multicore_fifo_push_blocking(SMP_INIT_FLAG); u32 g = multicore_fifo_pop_blocking(); if(g != SMP_INIT_FLAG) log_info("core1: bro the other core is weird\n"); log_info("core1: up\n"); bool beep_on = false; while(true) { snd_packet p; queue_remove_blocking(&qsnd, &p); if((p.type == SND_TYPE_CLICK) && !beep_on) { for(int i = 5; i > 0; i--) { const u8 div = 1; const u16 wrap = 31250 / i; pwm_set_clkdiv(pwm_slice, div); pwm_set_wrap(pwm_slice, wrap); pwm_set_chan_level(pwm_slice, PWM_CHAN_A, wrap / 2); pwm_set_enabled(pwm_slice, true); sleep_ms(5); pwm_set_enabled(pwm_slice, false); } } else if(p.type == SND_TYPE_BEEP) { if(beep_on) { pwm_set_enabled(pwm_slice, false); beep_on = false; } else { const u8 div = 5; const u16 wrap = 59510; pwm_set_clkdiv(pwm_slice, div); pwm_set_wrap(pwm_slice, wrap); pwm_set_chan_level(pwm_slice, PWM_CHAN_A, wrap / 2); beep_on = true; pwm_set_enabled(pwm_slice, true); if(p.ms > 0) { sleep_ms(p.ms); pwm_set_enabled(pwm_slice, false); beep_on = false; } } } } } int main(void) { u32 uart_pio_offset = 0; board_init(); tusb_init(); stdio_init_all(); sleep_ms(10); /* TODO: actual version */ log_info("USB2Sun5 v0.0.0a\n"); log_info("by dweller@cabin.digital\n"); log_info("------------------------\n\n"); fflush(stdout); tuh_init(BOARD_TUH_RHPORT); log_info("USB: init\n"); gpio_init(PIN_LVL_OE); gpio_set_dir(PIN_LVL_OE, GPIO_OUT); gpio_put(PIN_LVL_OE, true); log_info("LVL: init\n"); uart_init(UART_KBD_ID, UART_KBD_BAUR); gpio_set_function(PIN_UART_KBD_TX, GPIO_FUNC_UART); gpio_set_function(PIN_UART_KBD_RX, GPIO_FUNC_UART); uart_set_hw_flow(UART_KBD_ID, false, false); uart_set_format(UART_KBD_ID, UART_KBD_DATA_BITS, UART_KBD_STOP_BITS, UART_KBD_PARITY); uart_set_fifo_enabled(UART_KBD_ID, true); log_info("UART1(KBD): init\n"); uart_pio = pio0; uart_pio_offset = pio_add_program(uart_pio, &uart_tx_program); uart_tx_program_init(uart_pio, uart_pio_sm, uart_pio_offset, PIN_UART_MOUS_TX, UART_MOUS_BAUD); log_info("UART PIO(MOUS): init\n"); gpio_init(PIN_BZZ); gpio_set_function(PIN_BZZ, GPIO_FUNC_PWM); pwm_slice = pwm_gpio_to_slice_num(PIN_BZZ); pwm_set_clkdiv_mode(pwm_slice, PWM_DIV_FREE_RUNNING); pwm_set_counter(pwm_slice, 0); pwm_set_enabled(pwm_slice, false); log_info("PWM: buzzer init\n"); queue_init(&qsnd, sizeof(snd_packet), 32); multicore_launch_core1(core1_entry); u32 g = multicore_fifo_pop_blocking(); if(g != SMP_INIT_FLAG) printf("core0: bro the other core is weird\n"); else multicore_fifo_push_blocking(SMP_INIT_FLAG); log_info("core0: core1 init\n"); snd_beep(120); log_info("Entering main loop...\n"); while(true) { tuh_task(); if(uart_is_readable(UART_KBD_ID)) { char c = uart_getc(UART_KBD_ID); sun5_kbd_handle_cmd(c); } } return 0; } /* OOF */ static inline void process_kbd_mods(u8 mods, u8* prev, u8 brk) { /* TODO: loop through bits and use bit position as a index into LUT */ if(mods & KEYBOARD_MODIFIER_LEFTCTRL) { if(!(*prev & KEYBOARD_MODIFIER_LEFTCTRL)) uart_putc_raw(UART_KBD_ID, SUN5_KEY_US_CONTROL | brk); else *prev &= ~KEYBOARD_MODIFIER_LEFTCTRL; } if(mods & KEYBOARD_MODIFIER_LEFTSHIFT) { if(!(*prev & KEYBOARD_MODIFIER_LEFTSHIFT)) uart_putc_raw(UART_KBD_ID, SUN5_KEY_US_SHIFT | brk); else *prev &= ~KEYBOARD_MODIFIER_LEFTSHIFT; } if(mods & KEYBOARD_MODIFIER_LEFTALT) { if(!(*prev & KEYBOARD_MODIFIER_LEFTALT)) uart_putc_raw(UART_KBD_ID, SUN5_KEY_US_ALT | brk); else *prev &= ~KEYBOARD_MODIFIER_LEFTALT; } if(mods & KEYBOARD_MODIFIER_LEFTGUI) { if(!(*prev & KEYBOARD_MODIFIER_LEFTGUI)) uart_putc_raw(UART_KBD_ID, SUN5_KEY_US_LTRIG | brk); else *prev &= ~KEYBOARD_MODIFIER_LEFTGUI; } if(mods & KEYBOARD_MODIFIER_RIGHTCTRL) { if(!(*prev & KEYBOARD_MODIFIER_RIGHTCTRL)) uart_putc_raw(UART_KBD_ID, SUN5_KEY_US_CONTROL | brk); else *prev &= ~KEYBOARD_MODIFIER_RIGHTCTRL; } if(mods & KEYBOARD_MODIFIER_RIGHTSHIFT) { if(!(*prev & KEYBOARD_MODIFIER_RIGHTSHIFT)) uart_putc_raw(UART_KBD_ID, SUN5_KEY_US_SHIFT | brk); else *prev &= ~KEYBOARD_MODIFIER_RIGHTSHIFT; } if(mods & KEYBOARD_MODIFIER_RIGHTALT) { if(!(*prev & KEYBOARD_MODIFIER_RIGHTALT)) uart_putc_raw(UART_KBD_ID, SUN5_KEY_US_ALT | brk); else *prev &= ~KEYBOARD_MODIFIER_RIGHTALT; } if(mods & KEYBOARD_MODIFIER_RIGHTGUI) { if(!(*prev & KEYBOARD_MODIFIER_RIGHTGUI)) uart_putc_raw(UART_KBD_ID, SUN5_KEY_US_RTRIG | brk); else *prev &= ~KEYBOARD_MODIFIER_RIGHTGUI; } } static void process_kbd_report(hid_keyboard_report_t const* report) { bool found; u8 i, j, sun_key, dummy = 0; static hid_keyboard_report_t prev = {0}; process_kbd_mods(report->modifier, &prev.modifier, 0); prev.modifier &= ~report->modifier; process_kbd_mods(prev.modifier, &dummy, 0x80); for(i = 0; i < 6; i++) { if(report->keycode[i]) { found = false; for(j = 0; j < 6; j++) { if(report->keycode[i] == prev.keycode[j]) { prev.keycode[j] = 0; found = true; break; } } if(found) continue; else if(report->keycode[i] < sizeof(usb2sun5_us)) { if(kbd_click) snd_click(); sun_key = usb2sun5_us[report->keycode[i]]; if(sun_key) uart_putc_raw(UART_KBD_ID, sun_key); } } } for(i = 0; i < 6; i++) { if(prev.keycode[i]) { if(prev.keycode[i] < sizeof(usb2sun5_us)) { sun_key = usb2sun5_us[prev.keycode[i]]; if(sun_key) uart_putc_raw(UART_KBD_ID, sun_key | 0x80); } } } log_dbg("USB: KBD report: %x [%x, %x, %x, %x, %x, %x]\n", report->modifier, report->keycode[0], report->keycode[1], report->keycode[2], report->keycode[3], report->keycode[4], report->keycode[5] ); prev = *report; } static void process_mouse_report(hid_mouse_report_t const* report) { s8 wheel = 0; u8 scroll_key = 0; msys_frame frame; /* see: tinyusb/src/class/hid/hid.h:306 */ frame.button = 0x80 | ((~report->buttons & MOUSE_BUTTON_LEFT) << 2) | ((~report->buttons & MOUSE_BUTTON_RIGHT) >> 1) | ((~report->buttons & MOUSE_BUTTON_MIDDLE) >> 1) ; frame.dx = report->x; frame.dy = -report->y; frame.dxp = frame.dyp = 0; uart_tx_write_blocking(uart_pio, uart_pio_sm, (u8*)&frame, sizeof(frame)); #if _SCROLL_WHEEL_AS_ARROWS == 1 wheel = report->wheel; if(wheel > 0) scroll_key = 0x45; /* Up Arrow */ else if(wheel < 0) { scroll_key = 0x71; /* Down Arrow */ wheel = -wheel; } if(scroll_key) { u8 i = 0; for(; i < wheel; i++) { uart_putc_raw(UART_KBD_ID, scroll_key); uart_putc_raw(UART_KBD_ID, scroll_key | 0x80); } } #endif log_dbg("USB: MOUS report: [%c%c%c, %d:%d, %d]\n", report->buttons & MOUSE_BUTTON_LEFT ? 'L' : '-', report->buttons & MOUSE_BUTTON_MIDDLE ? 'M' : '-', report->buttons & MOUSE_BUTTON_RIGHT ? 'R' : '-', report->x, report->y, report->wheel ); } static void process_generic_report(u8 dev_addr, u8 instance, u8 const* report, u16 len) { u8 i; const u8 rpt_count = hid_info[instance].report_count; tuh_hid_report_info_t* rpt_info_arr = hid_info[instance].report_info; tuh_hid_report_info_t* rpt_info = NULL; /* Simple vs composite report info retrieving */ if(rpt_count == 1 && rpt_info_arr[0].report_id == 0) rpt_info = &rpt_info_arr[0]; else { const u8 rpt_id = report[0]; for(i = 0; i < rpt_count; i++) { if(rpt_id == rpt_info_arr[i].report_id) { rpt_info = &rpt_info_arr[i]; break; } } report++; len--; } if(!rpt_info) { log_info("USB: Failed to process report from HID dev @ addr %d, " "instance %d,report. No report info.\n", dev_addr, instance ); return; } /* For complete list of Usage Page & Usage see tinyusb/src/class/hid/hid.h * For example: * - Keyboard : Desktop, Keyboard * - Mouse : Desktop, Mouse * - Gamepad : Desktop, Gamepad * - Consumer Control (Media Key) : Consumer, Consumer Control * - System Control (Power key) : Desktop, System Control * - Generic (vendor) : 0xFFxx, xx */ if(rpt_info->usage_page == HID_USAGE_PAGE_DESKTOP) { switch (rpt_info->usage) { case HID_USAGE_DESKTOP_KEYBOARD: /* XXX: Assume keyboard follow boot report layout */ process_kbd_report((hid_keyboard_report_t const*)report); return; case HID_USAGE_DESKTOP_MOUSE: /* XXX: Assume mouse follow boot report layout */ process_mouse_report((hid_mouse_report_t const*)report); return; default: break; } } log_info("USB: Unknown/unhandled report from HID dev @ addr %d, instance %d" "\n", dev_addr, instance); } void tuh_hid_mount_cb(u8 dev_addr, u8 instance, u8 const* desc_report, u16 desc_len) { u8 itf_protocol = 0; static const char* protocol_str[] = { "None", "Keyboard", "Mouse" }; log_info("USB: mounted HID dev @ addr %d, instance %d\n", dev_addr, instance); itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); log_info("USB: HID iface prot - %s\n", protocol_str[itf_protocol]); /* TODO: check if the protocol is boot * TODO: parse non-boot protocols */ if(itf_protocol == HID_ITF_PROTOCOL_KEYBOARD) { hid_info[instance].report_count = tuh_hid_parse_report_descriptor(hid_info[instance].report_info, MAX_REPORT, desc_report, desc_len); for(int i = 0; i < hid_info[instance].report_count; i++) { if((hid_info[instance].report_info[i].usage_page == HID_USAGE_PAGE_DESKTOP) && (hid_info[instance].report_info[i].usage == HID_USAGE_DESKTOP_KEYBOARD)) { u8 stored = 0; /* XXX: I wanted to handle multiple keyboards, but * that just caused XFER stalls, tinyusb asserts * and other USB related "fun". So I just grab the * first device and ignore the others. That said * I leave this code here in case I ever decide to fix * this * * (Could this be related to boot protocol?) */ for(u32 j = 0; j < /*USB_KBD_MAX_DEVS*/ 1; j++) { if(usb_kbd_devs[j].valid) { if((usb_kbd_devs[j].address == dev_addr) && (usb_kbd_devs[j].instance == instance)) { stored = 1; break; } else continue; } usb_kbd_devs[j].valid = true; usb_kbd_devs[j].address = dev_addr; usb_kbd_devs[j].instance = instance; stored = 2; break; } if(!stored) log_info("USB: WARN - no room for kbd in status array\n"); else if(stored == 1) log_info("USB: WARN - tried to add same kbd dev\n"); else log_info("USB: added KBD LED sink: @%d, inst %d\n", dev_addr, instance); } } log_info("USB: HID has %u reports\n", hid_info[instance].report_count); } if(!tuh_hid_receive_report(dev_addr, instance)) log_info("USB: ERROR - cannot request to receive report\n"); } void tuh_hid_umount_cb(u8 dev_addr, u8 instance) { for(u32 i = 0; i < USB_KBD_MAX_DEVS; i++) { if(!usb_kbd_devs[i].valid) continue; if((usb_kbd_devs[i].address == dev_addr) && (usb_kbd_devs[i].instance == instance)) usb_kbd_devs[i].valid = false; } log_info("USB: unmounted HID dev @ addr %d, instance %d\n", dev_addr, instance); } void tuh_hid_report_received_cb(u8 dev_addr, u8 instance, u8 const* report, u16 len) { u8 const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); switch(itf_protocol) { case HID_ITF_PROTOCOL_KEYBOARD: process_kbd_report((hid_keyboard_report_t const*)report); break; case HID_ITF_PROTOCOL_MOUSE: process_mouse_report((hid_mouse_report_t const*)report); break; default: process_generic_report(dev_addr, instance, report, len); break; } if(!tuh_hid_receive_report(dev_addr, instance)) log_info("USB: ERROR - cannot request to receive report\n"); } void tuh_mount_cb(u8 dev_addr) { log_info("USB: mounted dev %d\n", dev_addr); } void tuh_umount_cb(u8 dev_addr) { log_info("USB: unmounted dev %d\n", dev_addr); }