Harden firmware state and transfer handling
This commit is contained in:
+124
-13
@@ -1,6 +1,7 @@
|
||||
#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).
|
||||
@@ -67,30 +68,140 @@ struct ConsumerReport {
|
||||
uint16_t usage;
|
||||
};
|
||||
|
||||
void usb_hid_init() {}
|
||||
static uint8_t s_key_refcount[256] = {};
|
||||
static uint8_t s_modifier_refcount[8] = {};
|
||||
|
||||
void usb_hid_send_key(uint8_t keycode, uint8_t modifier)
|
||||
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 = {};
|
||||
report.modifier = modifier;
|
||||
report.keycodes[0] = keycode;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
void usb_hid_release_key()
|
||||
static void send_consumer_state()
|
||||
{
|
||||
KeyboardReport report = {};
|
||||
HID().SendReport(HID_REPORT_ID_KEYBOARD, &report, sizeof(report));
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void usb_hid_send_consumer(uint16_t usage)
|
||||
{
|
||||
ConsumerReport report = { usage };
|
||||
HID().SendReport(HID_REPORT_ID_CONSUMER, &report, sizeof(report));
|
||||
}
|
||||
|
||||
void usb_hid_release_consumer()
|
||||
void usb_hid_init()
|
||||
{
|
||||
ConsumerReport report = { 0 };
|
||||
HID().SendReport(HID_REPORT_ID_CONSUMER, &report, sizeof(report));
|
||||
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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user