Add Keyboard Mode

This commit is contained in:
Frederik Walk 2023-12-03 11:27:19 +01:00
parent 75bcf4dd96
commit c1b410d120
12 changed files with 191 additions and 3 deletions

View File

@ -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

View File

@ -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"

View File

@ -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 <stdint.h>
#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_

View File

@ -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,

View File

@ -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();

View File

@ -65,6 +65,7 @@ class Menu {
ChangeUsbModeDS3,
ChangeUsbModePS4Tatacon,
ChangeUsbModeDS4,
ChangeUsbModeKeyboard,
ChangeUsbModeXbox360,
ChangeUsbModeMidi,
ChangeUsbModeDebug,

View File

@ -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:

View File

@ -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:
}
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -9,7 +9,7 @@ const std::map<Menu::Page, const Menu::Descriptor> 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::Page, const Menu::Descriptor> 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();