Files
VersaMCU/src/hal/usb_hid.cpp
T

208 lines
6.3 KiB
C++

#include "usb_hid.h"
#include <Arduino.h>
#include <HID.h>
#include <string.h>
// ── HID Report Descriptor: Keyboard + Consumer Control ───────────────────────
// Host-Kommunikation außerhalb von HID läuft separat über USB CDC (SerialUSB).
static const uint8_t k_hid_descriptor[] = {
// ── Report ID 1: Keyboard ─────────────────────────────────────────────────
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x85, HID_REPORT_ID_KEYBOARD,
0x05, 0x07, // Usage Page (Key Codes)
0x19, 0xE0, // Usage Minimum (Left Ctrl)
0x29, 0xE7, // Usage Maximum (Right GUI)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1 Bit)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data, Variable, Absolute)
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8 Bit)
0x81, 0x01, // Input (Constant)
0x95, 0x06, // Report Count (6)
0x75, 0x08, // Report Size (8 Bit)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x65, // Logical Maximum (101)
0x05, 0x07, // Usage Page (Key Codes)
0x19, 0x00, // Usage Minimum (0)
0x29, 0x65, // Usage Maximum (101)
0x81, 0x00, // Input (Data, Array)
0xC0, // End Collection
// ── Report ID 2: Consumer Control ─────────────────────────────────────────
0x05, 0x0C, // Usage Page (Consumer Devices)
0x09, 0x01, // Usage (Consumer Control)
0xA1, 0x01, // Collection (Application)
0x85, HID_REPORT_ID_CONSUMER,
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x03, // Logical Maximum (1023)
0x19, 0x00, // Usage Minimum (0)
0x2A, 0xFF, 0x03, // Usage Maximum (1023)
0x75, 0x10, // Report Size (16 Bit)
0x95, 0x01, // Report Count (1)
0x81, 0x00, // Input (Data, Array)
0xC0, // End Collection
};
namespace {
struct HIDRegistrar {
HIDSubDescriptor node;
HIDRegistrar() : node(k_hid_descriptor, sizeof(k_hid_descriptor)) {
HID().AppendDescriptor(&node);
}
} s_hid_registrar;
}
struct KeyboardReport {
uint8_t modifier;
uint8_t reserved;
uint8_t keycodes[6];
};
struct ConsumerReport {
uint16_t usage;
};
static uint8_t s_key_refcount[256] = {};
static uint8_t s_modifier_refcount[8] = {};
struct ConsumerState {
uint16_t usage;
uint8_t refcount;
uint32_t order;
};
static constexpr uint8_t CONSUMER_STATE_SLOTS = 8;
static ConsumerState s_consumer_state[CONSUMER_STATE_SLOTS] = {};
static uint32_t s_consumer_order = 0;
static void send_keyboard_state()
{
KeyboardReport report = {};
for (uint8_t bit = 0; bit < 8; bit++) {
if (s_modifier_refcount[bit] > 0)
report.modifier |= static_cast<uint8_t>(1u << bit);
}
uint8_t out = 0;
for (uint16_t key = 1; key < 256 && out < 6; key++) {
if (s_key_refcount[key] > 0)
report.keycodes[out++] = static_cast<uint8_t>(key);
}
HID().SendReport(HID_REPORT_ID_KEYBOARD, &report, sizeof(report));
}
static void send_consumer_state()
{
uint16_t usage = 0;
uint32_t newest = 0;
for (uint8_t i = 0; i < CONSUMER_STATE_SLOTS; i++) {
if (s_consumer_state[i].refcount > 0 &&
s_consumer_state[i].order >= newest)
{
newest = s_consumer_state[i].order;
usage = s_consumer_state[i].usage;
}
}
ConsumerReport report = { usage };
HID().SendReport(HID_REPORT_ID_CONSUMER, &report, sizeof(report));
}
void usb_hid_init()
{
memset(s_key_refcount, 0, sizeof(s_key_refcount));
memset(s_modifier_refcount, 0, sizeof(s_modifier_refcount));
memset(s_consumer_state, 0, sizeof(s_consumer_state));
s_consumer_order = 0;
}
void usb_hid_send_key(uint8_t keycode, uint8_t modifier)
{
if (keycode != 0 && s_key_refcount[keycode] < 0xFF)
s_key_refcount[keycode]++;
for (uint8_t bit = 0; bit < 8; bit++) {
if ((modifier & (1u << bit)) != 0 && s_modifier_refcount[bit] < 0xFF)
s_modifier_refcount[bit]++;
}
send_keyboard_state();
}
void usb_hid_release_key(uint8_t keycode, uint8_t modifier)
{
if (keycode != 0 && s_key_refcount[keycode] > 0)
s_key_refcount[keycode]--;
for (uint8_t bit = 0; bit < 8; bit++) {
if ((modifier & (1u << bit)) != 0 && s_modifier_refcount[bit] > 0)
s_modifier_refcount[bit]--;
}
send_keyboard_state();
}
void usb_hid_release_all_keys()
{
memset(s_key_refcount, 0, sizeof(s_key_refcount));
memset(s_modifier_refcount, 0, sizeof(s_modifier_refcount));
send_keyboard_state();
}
void usb_hid_send_consumer(uint16_t usage)
{
ConsumerState* free_slot = nullptr;
for (uint8_t i = 0; i < CONSUMER_STATE_SLOTS; i++) {
ConsumerState& state = s_consumer_state[i];
if (state.refcount > 0 && state.usage == usage) {
if (state.refcount < 0xFF) state.refcount++;
state.order = ++s_consumer_order;
send_consumer_state();
return;
}
if (state.refcount == 0 && free_slot == nullptr)
free_slot = &state;
}
if (free_slot != nullptr) {
free_slot->usage = usage;
free_slot->refcount = 1;
free_slot->order = ++s_consumer_order;
}
send_consumer_state();
}
void usb_hid_release_consumer(uint16_t usage)
{
for (uint8_t i = 0; i < CONSUMER_STATE_SLOTS; i++) {
ConsumerState& state = s_consumer_state[i];
if (state.refcount > 0 && state.usage == usage) {
state.refcount--;
if (state.refcount == 0) {
state.usage = 0;
state.order = 0;
}
break;
}
}
send_consumer_state();
}
void usb_hid_release_all_consumers()
{
memset(s_consumer_state, 0, sizeof(s_consumer_state));
send_consumer_state();
}