From c1b410d120770eb23f17c51cbbc211d5f8291a37 Mon Sep 17 00:00:00 2001 From: Frederik Walk Date: Sun, 3 Dec 2023 11:27:19 +0100 Subject: [PATCH] Add Keyboard Mode --- README.md | 1 + include/usb/hid_driver.h | 1 + include/usb/hid_keyboard_driver.h | 34 ++++++++++++ include/usb/usb_driver.h | 2 + include/utils/InputState.h | 2 + include/utils/Menu.h | 1 + src/peripherals/Display.cpp | 2 + src/usb/hid_driver.c | 5 ++ src/usb/hid_keyboard_driver.c | 89 +++++++++++++++++++++++++++++++ src/usb/usb_driver.c | 9 ++++ src/utils/InputState.cpp | 41 +++++++++++++- src/utils/Menu.cpp | 7 ++- 12 files changed, 191 insertions(+), 3 deletions(-) create mode 100644 include/usb/hid_keyboard_driver.h create mode 100644 src/usb/hid_keyboard_driver.c diff --git a/README.md b/README.md index 8ebb8fa..552c3bc 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ The firmware is pretty much tailored to this specific use case, if you are looki - Dualshock 3 - Switch Pro Controller - XInput + - Keyboard - MIDI - Debug mode (will output current state via USB serial) - Additional buttons via external i2c GPIO expander diff --git a/include/usb/hid_driver.h b/include/usb/hid_driver.h index 4495dfb..4bcbab9 100644 --- a/include/usb/hid_driver.h +++ b/include/usb/hid_driver.h @@ -1,6 +1,7 @@ #ifndef _USB_HID_DRIVER_H_ #define _USB_HID_DRIVER_H_ +#include "usb/hid_keyboard_driver.h" #include "usb/hid_ps3_driver.h" #include "usb/hid_ps4_driver.h" #include "usb/hid_switch_driver.h" diff --git a/include/usb/hid_keyboard_driver.h b/include/usb/hid_keyboard_driver.h new file mode 100644 index 0000000..270802b --- /dev/null +++ b/include/usb/hid_keyboard_driver.h @@ -0,0 +1,34 @@ +#ifndef _USB_HID_KEYBOARD_DRIVER_H_ +#define _USB_HID_KEYBOARD_DRIVER_H_ + +#include "usb/usb_driver.h" + +#include "device/usbd_pvt.h" + +#include + +#define USBD_KEYBOARD_NAME "Keyboard Mode" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct __attribute((packed, aligned(1))) { + uint8_t keycodes[32]; +} hid_nkro_keyboard_report_t; + +extern const tusb_desc_device_t keyboard_desc_device; +extern const uint8_t keyboard_desc_cfg[]; +extern const uint8_t keyboard_desc_hid_report[]; + +bool send_hid_keyboard_report(usb_report_t report); +uint16_t hid_keyboard_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, + uint16_t reqlen); +void hid_keyboard_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, + uint16_t bufsize); + +#ifdef __cplusplus +} +#endif + +#endif // _USB_HID_KEYBOARD_DRIVER_H_ \ No newline at end of file diff --git a/include/usb/usb_driver.h b/include/usb/usb_driver.h index 16eb2d3..fbd2a11 100644 --- a/include/usb/usb_driver.h +++ b/include/usb/usb_driver.h @@ -20,6 +20,7 @@ typedef enum { USB_MODE_DUALSHOCK3, USB_MODE_PS4_TATACON, USB_MODE_DUALSHOCK4, + USB_MODE_KEYBOARD, USB_MODE_XBOX360, USB_MODE_MIDI, USB_MODE_DEBUG, @@ -34,6 +35,7 @@ enum { USBD_STR_SWITCH, USBD_STR_PS3, USBD_STR_PS4, + USBD_STR_KEYBOARD, USBD_STR_XINPUT, USBD_STR_MIDI, USBD_STR_RPI_RESET, diff --git a/include/utils/InputState.h b/include/utils/InputState.h index fe88e90..7d24f34 100644 --- a/include/utils/InputState.h +++ b/include/utils/InputState.h @@ -45,6 +45,7 @@ struct InputState { hid_switch_report_t m_switch_report; hid_ps3_report_t m_ps3_report; hid_ps4_report_t m_ps4_report; + hid_nkro_keyboard_report_t m_keyboard_report; xinput_report_t m_xinput_report; midi_report_t m_midi_report; std::string m_debug_report; @@ -52,6 +53,7 @@ struct InputState { usb_report_t getSwitchReport(); usb_report_t getPS3InputReport(); usb_report_t getPS4InputReport(); + usb_report_t getKeyboardReport(); usb_report_t getXinputReport(); usb_report_t getMidiReport(); usb_report_t getDebugReport(); diff --git a/include/utils/Menu.h b/include/utils/Menu.h index 1d48de2..59abcc8 100644 --- a/include/utils/Menu.h +++ b/include/utils/Menu.h @@ -65,6 +65,7 @@ class Menu { ChangeUsbModeDS3, ChangeUsbModePS4Tatacon, ChangeUsbModeDS4, + ChangeUsbModeKeyboard, ChangeUsbModeXbox360, ChangeUsbModeMidi, ChangeUsbModeDebug, diff --git a/src/peripherals/Display.cpp b/src/peripherals/Display.cpp index 854df2f..1e14141 100644 --- a/src/peripherals/Display.cpp +++ b/src/peripherals/Display.cpp @@ -40,6 +40,8 @@ static std::string modeToString(usb_mode_t mode) { return "PS4 Tatacon"; case USB_MODE_DUALSHOCK4: return "Dualshock 4"; + case USB_MODE_KEYBOARD: + return "Keyboard"; case USB_MODE_XBOX360: return "Xbox 360"; case USB_MODE_MIDI: diff --git a/src/usb/hid_driver.c b/src/usb/hid_driver.c index a92ca16..0494ad9 100644 --- a/src/usb/hid_driver.c +++ b/src/usb/hid_driver.c @@ -16,6 +16,8 @@ uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t case USB_MODE_PS4_TATACON: case USB_MODE_DUALSHOCK4: return hid_ps4_get_report_cb(itf, report_id, report_type, buffer, reqlen); + case USB_MODE_KEYBOARD: + return hid_keyboard_get_report_cb(itf, report_id, report_type, buffer, reqlen); default: } @@ -36,6 +38,9 @@ void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t rep case USB_MODE_DUALSHOCK4: hid_ps4_set_report_cb(itf, report_id, report_type, buffer, bufsize); break; + case USB_MODE_KEYBOARD: + hid_keyboard_set_report_cb(itf, report_id, report_type, buffer, bufsize); + break; default: } } diff --git a/src/usb/hid_keyboard_driver.c b/src/usb/hid_keyboard_driver.c new file mode 100644 index 0000000..3b1f5b9 --- /dev/null +++ b/src/usb/hid_keyboard_driver.c @@ -0,0 +1,89 @@ +#include "usb/hid_keyboard_driver.h" +#include "usb/usb_driver.h" + +#include "class/hid/hid_device.h" +#include "pico/unique_id.h" + +#include "tusb.h" + +const tusb_desc_device_t keyboard_desc_device = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = TUSB_CLASS_UNSPECIFIED, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .idVendor = 0x1209, + .idProduct = 0x3901, + .bcdDevice = 0x0100, + .iManufacturer = USBD_STR_MANUFACTURER, + .iProduct = USBD_STR_PRODUCT, + .iSerialNumber = USBD_STR_SERIAL, + .bNumConfigurations = 0x01, +}; + +enum { + USBD_ITF_HID, + USBD_ITF_MAX, +}; + +const uint8_t keyboard_desc_hid_report[] = { + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x09, 0x06, // Usage (Keyboard) + 0xA1, 0x01, // Collection (Application) + 0x85, 0x01, // Report ID (1) + 0x05, 0x07, // Usage Page (Kbrd/Keypad) + 0x19, 0x00, // Usage Minimum (0x00) + 0x2A, 0xFF, 0x00, // Usage Maximum (0xFF) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x96, 0x00, 0x01, // Report Count (256) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0xC0, // End Collection +}; + +#define USBD_KEYBOARD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN) +uint8_t const keyboard_desc_cfg[] = { + TUD_CONFIG_DESCRIPTOR(0x01, USBD_ITF_MAX, USBD_STR_LANGUAGE, USBD_KEYBOARD_DESC_LEN, + TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, USBD_MAX_POWER_MAX), + TUD_HID_DESCRIPTOR(USBD_ITF_HID, USBD_STR_KEYBOARD, HID_ITF_PROTOCOL_KEYBOARD, sizeof(keyboard_desc_hid_report), + 0x81, CFG_TUD_HID_EP_BUFSIZE, 1), +}; + +static hid_keyboard_report_t last_report = {}; + +bool send_hid_keyboard_report(usb_report_t report) { + bool result = false; + + if (tud_hid_ready()) { + result = tud_hid_report(0x01, report.data, report.size); + } + + memcpy(&last_report, report.data, tu_min16(report.size, sizeof(hid_nkro_keyboard_report_t))); + + return result; +} + +uint16_t hid_keyboard_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, + uint16_t reqlen) { + (void)itf; + (void)report_id; + (void)reqlen; + + if (report_type == HID_REPORT_TYPE_INPUT) { + memcpy(buffer, &last_report, sizeof(hid_keyboard_report_t)); + return sizeof(hid_keyboard_report_t); + } + return 0; +} + +void hid_keyboard_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, + uint16_t bufsize) { + (void)itf; + (void)report_id; + (void)report_type; + (void)buffer; + (void)bufsize; +} diff --git a/src/usb/usb_driver.c b/src/usb/usb_driver.c index 0b291bc..c14476b 100644 --- a/src/usb/usb_driver.c +++ b/src/usb/usb_driver.c @@ -26,6 +26,7 @@ char *const usbd_desc_str[] = { [USBD_STR_SWITCH] = USBD_SWITCH_NAME, // [USBD_STR_PS3] = USBD_PS3_NAME, // [USBD_STR_PS4] = USBD_PS4_NAME, // + [USBD_STR_KEYBOARD] = USBD_KEYBOARD_NAME, // [USBD_STR_XINPUT] = USBD_XINPUT_NAME, // [USBD_STR_MIDI] = USBD_MIDI_NAME, // [USBD_STR_CDC] = USBD_DEBUG_CDC_NAME, // @@ -76,6 +77,14 @@ void usb_driver_init(usb_mode_t mode) { usbd_send_report = send_hid_ps4_report; usbd_receive_report = NULL; break; + case USB_MODE_KEYBOARD: + usbd_desc_device = &keyboard_desc_device; + usbd_desc_cfg = keyboard_desc_cfg; + usbd_desc_hid_report = keyboard_desc_hid_report; + usbd_app_driver = &hid_app_driver; + usbd_send_report = send_hid_keyboard_report; + usbd_receive_report = NULL; + break; case USB_MODE_XBOX360: usbd_desc_device = &xinput_desc_device; usbd_desc_cfg = xinput_desc_cfg; diff --git a/src/utils/InputState.cpp b/src/utils/InputState.cpp index 0c65f7a..3f036cb 100644 --- a/src/utils/InputState.cpp +++ b/src/utils/InputState.cpp @@ -9,7 +9,7 @@ InputState::InputState() : drum({{false, 0}, {false, 0}, {false, 0}, {false, 0}}), controller( {{false, false, false, false}, {false, false, false, false, false, false, false, false, false, false}}), - m_switch_report({}), m_ps3_report({}), m_ps4_report({}), + m_switch_report({}), m_ps3_report({}), m_ps4_report({}), m_keyboard_report({}), m_xinput_report({0x00, sizeof(xinput_report_t), 0, 0, 0, 0, 0, 0, 0, 0, {}}), m_midi_report({{false, false, false, false}, {0, 0, 0, 0}}) {} @@ -23,6 +23,8 @@ usb_report_t InputState::getReport(usb_mode_t mode) { case USB_MODE_PS4_TATACON: case USB_MODE_DUALSHOCK4: return getPS4InputReport(); + case USB_MODE_KEYBOARD: + return getKeyboardReport(); case USB_MODE_XBOX360: return getXinputReport(); case USB_MODE_MIDI: @@ -173,7 +175,7 @@ usb_report_t InputState::getPS4InputReport() { m_ps4_report.battery = 0 | (1 << 4) | 11; // Cable connected and fully charged m_ps4_report.peripheral = 0x01; m_ps4_report.touch_report_count = 0; - + // This method actually gets called more often than the report is sent, // so counters are not consecutive ... let's see if this turns out to // be a problem. @@ -185,6 +187,41 @@ usb_report_t InputState::getPS4InputReport() { return {(uint8_t *)&m_ps4_report, sizeof(hid_ps4_report_t)}; } +usb_report_t InputState::getKeyboardReport() { + m_keyboard_report = {.keycodes = {0}}; + + auto set_key = [&](const bool input, const uint8_t keycode) { + if (input) { + m_keyboard_report.keycodes[keycode / 8] |= 1 << (keycode % 8); + } + }; + + set_key(drum.ka_left.triggered, HID_KEY_D); + set_key(drum.don_left.triggered, HID_KEY_F); + set_key(drum.don_right.triggered, HID_KEY_J); + set_key(drum.ka_right.triggered, HID_KEY_K); + + set_key(controller.dpad.up, HID_KEY_ARROW_UP); + set_key(controller.dpad.down, HID_KEY_ARROW_DOWN); + set_key(controller.dpad.left, HID_KEY_ARROW_LEFT); + set_key(controller.dpad.right, HID_KEY_ARROW_RIGHT); + + set_key(controller.buttons.north, HID_KEY_L); + set_key(controller.buttons.east, HID_KEY_BACKSPACE); + set_key(controller.buttons.south, HID_KEY_ENTER); + set_key(controller.buttons.west, HID_KEY_P); + + set_key(controller.buttons.l, HID_KEY_Q); + set_key(controller.buttons.r, HID_KEY_E); + + set_key(controller.buttons.start, HID_KEY_ESCAPE); + set_key(controller.buttons.select, HID_KEY_TAB); + // set_key(controller.buttons.home, ); + // set_key(controller.buttons.share, ); + + return {(uint8_t *)&m_keyboard_report, sizeof(hid_nkro_keyboard_report_t)}; +} + usb_report_t InputState::getXinputReport() { m_xinput_report.buttons1 = 0 // | (controller.dpad.up ? (1 << 0) : 0) // Dpad Up diff --git a/src/utils/Menu.cpp b/src/utils/Menu.cpp index 679dde4..92c6382 100644 --- a/src/utils/Menu.cpp +++ b/src/utils/Menu.cpp @@ -9,7 +9,7 @@ const std::map Menu::descriptors = { {{"Mode", Menu::Descriptor::Action::GotoPageDeviceMode}, // {"Brightness", Menu::Descriptor::Action::GotoPageLedBrightness}, // {"Sensitvty", Menu::Descriptor::Action::GotoPageTriggerThreshold}, // - {"DebnceDly", Menu::Descriptor::Action::GotoPageDebounceDelay}, // + {"DebnceDly", Menu::Descriptor::Action::GotoPageDebounceDelay}, // {"Reset", Menu::Descriptor::Action::GotoPageReset}, // {"BOOTSEL", Menu::Descriptor::Action::GotoPageBootsel}}, // 0}}, // @@ -22,6 +22,7 @@ const std::map Menu::descriptors = { {"Dualshock3", Menu::Descriptor::Action::ChangeUsbModeDS3}, // {"PS4 Tata", Menu::Descriptor::Action::ChangeUsbModePS4Tatacon}, // {"Dualshock4", Menu::Descriptor::Action::ChangeUsbModeDS4}, // + {"Keyboard", Menu::Descriptor::Action::ChangeUsbModeKeyboard}, // {"Xbox 360", Menu::Descriptor::Action::ChangeUsbModeXbox360}, // {"MIDI", Menu::Descriptor::Action::ChangeUsbModeMidi}, // {"Debug", Menu::Descriptor::Action::ChangeUsbModeDebug}}, // @@ -286,6 +287,10 @@ void Menu::performSelectionAction(Menu::Descriptor::Action action) { m_store->setUsbMode(USB_MODE_DUALSHOCK4); gotoParent(); break; + case Descriptor::Action::ChangeUsbModeKeyboard: + m_store->setUsbMode(USB_MODE_KEYBOARD); + gotoParent(); + break; case Descriptor::Action::ChangeUsbModeXbox360: m_store->setUsbMode(USB_MODE_XBOX360); gotoParent();