208 lines
6.3 KiB
C++
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();
|
|
}
|