[Core] Refactor ChibiOS USB endpoints to be fully async (#21656)

This commit is contained in:
Stefan Kerkmann 2024-02-28 12:00:27 +01:00 committed by GitHub
parent b43f6cb7ef
commit 0e02b0c41e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 1311 additions and 1134 deletions

View File

@ -192,15 +192,18 @@ void protocol_pre_task(void) {
/* Remote wakeup */
if ((USB_DRIVER.status & USB_GETSTATUS_REMOTE_WAKEUP_ENABLED) && suspend_wakeup_condition()) {
usbWakeupHost(&USB_DRIVER);
restart_usb_driver(&USB_DRIVER);
# if USB_SUSPEND_WAKEUP_DELAY > 0
// Some hubs, kvm switches, and monitors do
// weird things, with USB device state bouncing
// around wildly on wakeup, yielding race
// conditions that can corrupt the keyboard state.
//
// Pause for a while to let things settle...
wait_ms(USB_SUSPEND_WAKEUP_DELAY);
# endif
}
}
/* Woken up */
// variables has been already cleared by the wakeup hook
send_keyboard_report();
# ifdef MOUSEKEY_ENABLE
mousekey_send();
# endif /* MOUSEKEY_ENABLE */
}
#endif
}
@ -218,4 +221,5 @@ void protocol_post_task(void) {
#ifdef RAW_ENABLE
raw_hid_task();
#endif
usb_idle_task();
}

View File

@ -6,6 +6,8 @@ SRC += $(CHIBIOS_DIR)/usb_main.c
SRC += $(CHIBIOS_DIR)/chibios.c
SRC += usb_descriptor.c
SRC += $(CHIBIOS_DIR)/usb_driver.c
SRC += $(CHIBIOS_DIR)/usb_endpoints.c
SRC += $(CHIBIOS_DIR)/usb_report_handling.c
SRC += $(CHIBIOS_DIR)/usb_util.c
SRC += $(LIBSRC)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,152 @@
// Copyright 2023 Stefan Kerkmann (@KarlK90)
// SPDX-License-Identifier: GPL-3.0-or-later
#include <ch.h>
#include <hal.h>
#include "usb_main.h"
#include "usb_driver.h"
#include "usb_endpoints.h"
#include "report.h"
usb_endpoint_in_t usb_endpoints_in[USB_ENDPOINT_IN_COUNT] = {
// clang-format off
#if defined(SHARED_EP_ENABLE)
[USB_ENDPOINT_IN_SHARED] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, SHARED_EPSIZE, SHARED_IN_EPNUM, SHARED_IN_CAPACITY, NULL,
QMK_USB_REPORT_STORAGE(
&usb_shared_get_report,
&usb_shared_set_report,
&usb_shared_reset_report,
&usb_shared_get_idle_rate,
&usb_shared_set_idle_rate,
&usb_shared_idle_timer_elapsed,
(REPORT_ID_COUNT + 1),
#if defined(KEYBOARD_SHARED_EP)
QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_KEYBOARD, sizeof(report_keyboard_t)),
#endif
#if defined(MOUSE_SHARED_EP)
QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_MOUSE, sizeof(report_mouse_t)),
#endif
#if defined(EXTRAKEY_ENABLE)
QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_SYSTEM, sizeof(report_extra_t)),
QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_CONSUMER, sizeof(report_extra_t)),
#endif
#if defined(PROGRAMMABLE_BUTTON_ENABLE)
QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_PROGRAMMABLE_BUTTON, sizeof(report_programmable_button_t)),
#endif
#if defined(NKRO_ENABLE)
QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_NKRO, sizeof(report_nkro_t)),
#endif
#if defined(JOYSTICK_SHARED_EP)
QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_JOYSTICK, sizeof(report_joystick_t)),
#endif
#if defined(DIGITIZER_SHARED_EP)
QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_DIGITIZER, sizeof(report_digitizer_t)),
#endif
)
),
#endif
// clang-format on
#if !defined(KEYBOARD_SHARED_EP)
[USB_ENDPOINT_IN_KEYBOARD] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, KEYBOARD_EPSIZE, KEYBOARD_IN_EPNUM, KEYBOARD_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(sizeof(report_keyboard_t))),
#endif
#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
[USB_ENDPOINT_IN_MOUSE] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, MOUSE_EPSIZE, MOUSE_IN_EPNUM, MOUSE_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(sizeof(report_mouse_t))),
#endif
#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP)
[USB_ENDPOINT_IN_JOYSTICK] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, JOYSTICK_EPSIZE, JOYSTICK_IN_EPNUM, JOYSTICK_IN_CAPACITY, QMK_USB_REPORT_STORAGE_DEFAULT(sizeof(report_joystick_t))),
#endif
#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)
[USB_ENDPOINT_IN_JOYSTICK] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, DIGITIZER_EPSIZE, DIGITIZER_IN_EPNUM, DIGITIZER_IN_CAPACITY, QMK_USB_REPORT_STORAGE_DEFAULT(sizeof(report_digitizer_t))),
#endif
#if defined(CONSOLE_ENABLE)
# if defined(USB_ENDPOINTS_ARE_REORDERABLE)
[USB_ENDPOINT_IN_CONSOLE] = QMK_USB_ENDPOINT_IN_SHARED(USB_EP_MODE_TYPE_INTR, CONSOLE_EPSIZE, CONSOLE_IN_EPNUM, CONSOLE_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(CONSOLE_EPSIZE)),
# else
[USB_ENDPOINT_IN_CONSOLE] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, CONSOLE_EPSIZE, CONSOLE_IN_EPNUM, CONSOLE_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(CONSOLE_EPSIZE)),
# endif
#endif
#if defined(RAW_ENABLE)
# if defined(USB_ENDPOINTS_ARE_REORDERABLE)
[USB_ENDPOINT_IN_RAW] = QMK_USB_ENDPOINT_IN_SHARED(USB_EP_MODE_TYPE_INTR, RAW_EPSIZE, RAW_IN_EPNUM, RAW_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(RAW_EPSIZE)),
# else
[USB_ENDPOINT_IN_RAW] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, RAW_EPSIZE, RAW_IN_EPNUM, RAW_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(RAW_EPSIZE)),
# endif
#endif
#if defined(MIDI_ENABLE)
# if defined(USB_ENDPOINTS_ARE_REORDERABLE)
[USB_ENDPOINT_IN_MIDI] = QMK_USB_ENDPOINT_IN_SHARED(USB_EP_MODE_TYPE_BULK, MIDI_STREAM_EPSIZE, MIDI_STREAM_IN_EPNUM, MIDI_STREAM_IN_CAPACITY, NULL, NULL),
# else
[USB_ENDPOINT_IN_MIDI] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_BULK, MIDI_STREAM_EPSIZE, MIDI_STREAM_IN_EPNUM, MIDI_STREAM_IN_CAPACITY, NULL, NULL),
# endif
#endif
#if defined(VIRTSER_ENABLE)
# if defined(USB_ENDPOINTS_ARE_REORDERABLE)
[USB_ENDPOINT_IN_CDC_DATA] = QMK_USB_ENDPOINT_IN_SHARED(USB_EP_MODE_TYPE_BULK, CDC_EPSIZE, CDC_IN_EPNUM, CDC_IN_CAPACITY, virtser_usb_request_cb, NULL),
# else
[USB_ENDPOINT_IN_CDC_DATA] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_BULK, CDC_EPSIZE, CDC_IN_EPNUM, CDC_IN_CAPACITY, virtser_usb_request_cb, NULL),
# endif
[USB_ENDPOINT_IN_CDC_SIGNALING] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, CDC_NOTIFICATION_EPSIZE, CDC_NOTIFICATION_EPNUM, CDC_SIGNALING_DUMMY_CAPACITY, NULL, NULL),
#endif
};
usb_endpoint_in_lut_t usb_endpoint_interface_lut[TOTAL_INTERFACES] = {
#if !defined(KEYBOARD_SHARED_EP)
[KEYBOARD_INTERFACE] = USB_ENDPOINT_IN_KEYBOARD,
#endif
#if defined(RAW_ENABLE)
[RAW_INTERFACE] = USB_ENDPOINT_IN_RAW,
#endif
#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
[MOUSE_INTERFACE] = USB_ENDPOINT_IN_MOUSE,
#endif
#if defined(SHARED_EP_ENABLE)
[SHARED_INTERFACE] = USB_ENDPOINT_IN_SHARED,
#endif
#if defined(CONSOLE_ENABLE)
[CONSOLE_INTERFACE] = USB_ENDPOINT_IN_CONSOLE,
#endif
#if defined(MIDI_ENABLE)
[AS_INTERFACE] = USB_ENDPOINT_IN_MIDI,
#endif
#if defined(VIRTSER_ENABLE)
[CCI_INTERFACE] = USB_ENDPOINT_IN_CDC_SIGNALING,
[CDI_INTERFACE] = USB_ENDPOINT_IN_CDC_DATA,
#endif
#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP)
[JOYSTICK_INTERFACE] = USB_ENDPOINT_IN_JOYSTICK,
#endif
#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)
[DIGITIZER_INTERFACE] = USB_ENDPOINT_IN_DIGITIZER,
#endif
};
usb_endpoint_out_t usb_endpoints_out[USB_ENDPOINT_OUT_COUNT] = {
#if defined(RAW_ENABLE)
[USB_ENDPOINT_OUT_RAW] = QMK_USB_ENDPOINT_OUT(USB_EP_MODE_TYPE_INTR, RAW_EPSIZE, RAW_OUT_EPNUM, RAW_OUT_CAPACITY),
#endif
#if defined(MIDI_ENABLE)
[USB_ENDPOINT_OUT_MIDI] = QMK_USB_ENDPOINT_OUT(USB_EP_MODE_TYPE_BULK, MIDI_STREAM_EPSIZE, MIDI_STREAM_OUT_EPNUM, MIDI_STREAM_OUT_CAPACITY),
#endif
#if defined(VIRTSER_ENABLE)
[USB_ENDPOINT_OUT_CDC_DATA] = QMK_USB_ENDPOINT_OUT(USB_EP_MODE_TYPE_BULK, CDC_EPSIZE, CDC_OUT_EPNUM, CDC_OUT_CAPACITY),
#endif
};

View File

@ -0,0 +1,137 @@
// Copyright 2023 Stefan Kerkmann (@KarlK90)
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "usb_descriptor.h"
#if !defined(USB_DEFAULT_BUFFER_CAPACITY)
# define USB_DEFAULT_BUFFER_CAPACITY 4
#endif
#if !defined(KEYBOARD_IN_CAPACITY)
# define KEYBOARD_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
#endif
#if !defined(SHARED_IN_CAPACITY)
# define SHARED_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
#endif
#if !defined(MOUSE_IN_CAPACITY)
# define MOUSE_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
#endif
#if !defined(JOYSTICK_IN_CAPACITY)
# define JOYSTICK_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
#endif
#if !defined(DIGITIZER_IN_CAPACITY)
# define DIGITIZER_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
#endif
#if !defined(CONSOLE_IN_CAPACITY)
# define CONSOLE_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
#endif
#if !defined(CONSOLE_OUT_CAPACITY)
# define CONSOLE_OUT_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
#endif
#if !defined(RAW_IN_CAPACITY)
# define RAW_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
#endif
#if !defined(RAW_OUT_CAPACITY)
# define RAW_OUT_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
#endif
#if !defined(MIDI_STREAM_IN_CAPACITY)
# define MIDI_STREAM_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
#endif
#if !defined(MIDI_STREAM_OUT_CAPACITY)
# define MIDI_STREAM_OUT_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
#endif
#if !defined(CDC_IN_CAPACITY)
# define CDC_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
#endif
#if !defined(CDC_OUT_CAPACITY)
# define CDC_OUT_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
#endif
#define CDC_SIGNALING_DUMMY_CAPACITY 1
typedef enum {
#if defined(SHARED_EP_ENABLE)
USB_ENDPOINT_IN_SHARED,
#endif
#if !defined(KEYBOARD_SHARED_EP)
USB_ENDPOINT_IN_KEYBOARD,
#endif
#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
USB_ENDPOINT_IN_MOUSE,
#endif
#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP)
USB_ENDPOINT_IN_JOYSTICK,
#endif
#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)
USB_ENDPOINT_IN_DIGITIZER,
#endif
#if defined(CONSOLE_ENABLE)
USB_ENDPOINT_IN_CONSOLE,
#endif
#if defined(RAW_ENABLE)
USB_ENDPOINT_IN_RAW,
#endif
#if defined(MIDI_ENABLE)
USB_ENDPOINT_IN_MIDI,
#endif
#if defined(VIRTSER_ENABLE)
USB_ENDPOINT_IN_CDC_DATA,
USB_ENDPOINT_IN_CDC_SIGNALING,
#endif
USB_ENDPOINT_IN_COUNT,
/* All non shared endpoints have to be consequtive numbers starting from 0, so
* that they can be used as array indices. The shared endpoints all point to
* the same endpoint so they have to be defined last to not reset the enum
* counter. */
#if defined(SHARED_EP_ENABLE)
# if defined(KEYBOARD_SHARED_EP)
USB_ENDPOINT_IN_KEYBOARD = USB_ENDPOINT_IN_SHARED,
# endif
# if defined(MOUSE_SHARED_EP)
USB_ENDPOINT_IN_MOUSE = USB_ENDPOINT_IN_SHARED,
# endif
# if defined(JOYSTICK_SHARED_EP)
USB_ENDPOINT_IN_JOYSTICK = USB_ENDPOINT_IN_SHARED,
# endif
# if defined(DIGITIZER_SHARED_EP)
USB_ENDPOINT_IN_DIGITIZER = USB_ENDPOINT_IN_SHARED,
# endif
#endif
} usb_endpoint_in_lut_t;
#define IS_VALID_USB_ENDPOINT_IN_LUT(i) ((i) >= 0 && (i) < USB_ENDPOINT_IN_COUNT)
usb_endpoint_in_lut_t usb_endpoint_interface_lut[TOTAL_INTERFACES];
typedef enum {
#if defined(RAW_ENABLE)
USB_ENDPOINT_OUT_RAW,
#endif
#if defined(MIDI_ENABLE)
USB_ENDPOINT_OUT_MIDI,
#endif
#if defined(VIRTSER_ENABLE)
USB_ENDPOINT_OUT_CDC_DATA,
#endif
USB_ENDPOINT_OUT_COUNT,
} usb_endpoint_out_lut_t;

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +1,21 @@
/*
* (c) 2015 flabberast <s3+flabbergast@sdfeu.org>
*
* Based on the following work:
* - Guillaume Duc's raw hid example (MIT License)
* https://github.com/guiduc/usb-hid-chibios-example
* - PJRC Teensy examples (MIT License)
* https://www.pjrc.com/teensy/usb_keyboard.html
* - hasu's TMK keyboard code (GPL v2 and some code Modified BSD)
* https://github.com/tmk/tmk_keyboard/
* - ChibiOS demo code (Apache 2.0 License)
* http://www.chibios.org
*
* Since some GPL'd code is used, this work is licensed under
* GPL v2 or later.
*/
// Copyright 2023 Stefan Kerkmann (@KarlK90)
// Copyright 2020 Ryan (@fauxpark)
// Copyright 2020 Joel Challis (@zvecr)
// Copyright 2018 James Laird-Wah
// Copyright 2016 Fredizzimo
// Copyright 2016 Giovanni Di Sirio
// SPDX-License-Identifier: GPL-3.0-or-later OR Apache-2.0
#pragma once
#include <ch.h>
#include <hal.h>
#include "usb_device_state.h"
#include "usb_descriptor.h"
#include "usb_driver.h"
#include "usb_endpoints.h"
/* -------------------------
* General USB driver header
* -------------------------
@ -36,6 +32,8 @@ void init_usb_driver(USBDriver *usbp);
/* Restart the USB driver and bus */
void restart_usb_driver(USBDriver *usbp);
bool send_report(usb_endpoint_in_lut_t endpoint, void *report, size_t size);
/* ---------------
* USB Event queue
* ---------------
@ -58,3 +56,14 @@ void usb_event_queue_task(void);
int8_t sendchar(uint8_t c);
#endif /* CONSOLE_ENABLE */
/* --------------
* Virtser header
* --------------
*/
#if defined(VIRTSER_ENABLE)
bool virtser_usb_request_cb(USBDriver *usbp);
#endif

View File

@ -0,0 +1,296 @@
// Copyright 2023 Stefan Kerkmann (@KarlK90)
// SPDX-License-Identifier: GPL-3.0-or-later
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "usb_report_handling.h"
#include "usb_endpoints.h"
#include "usb_main.h"
#include "usb_types.h"
#include "usb_driver.h"
#include "report.h"
extern usb_endpoint_in_t usb_endpoints_in[USB_ENDPOINT_IN_COUNT];
extern usb_endpoint_in_lut_t usb_endpoint_interface_lut[TOTAL_INTERFACES];
void usb_set_report(usb_fs_report_t **reports, const uint8_t *data, size_t length) {
if (*reports == NULL) {
return;
}
(*reports)->last_report = chVTGetSystemTimeX();
(*reports)->length = length;
memcpy(&(*reports)->data, data, length);
}
void usb_get_report(usb_fs_report_t **reports, uint8_t report_id, usb_fs_report_t *report) {
(void)report_id;
if (*reports == NULL) {
return;
}
report->length = (*reports)->length;
memcpy(&report->data, &(*reports)->data, report->length);
}
void usb_reset_report(usb_fs_report_t **reports) {
if (*reports == NULL) {
return;
}
memset(&(*reports)->data, 0, (*reports)->length);
(*reports)->idle_rate = 0;
(*reports)->last_report = 0;
}
void usb_shared_set_report(usb_fs_report_t **reports, const uint8_t *data, size_t length) {
uint8_t report_id = data[0];
if (report_id > REPORT_ID_COUNT || reports[report_id] == NULL) {
return;
}
reports[report_id]->last_report = chVTGetSystemTimeX();
reports[report_id]->length = length;
memcpy(&reports[report_id]->data, data, length);
}
void usb_shared_get_report(usb_fs_report_t **reports, uint8_t report_id, usb_fs_report_t *report) {
if (report_id > REPORT_ID_COUNT || reports[report_id] == NULL) {
return;
}
report->length = reports[report_id]->length;
memcpy(&report->data, &reports[report_id]->data, report->length);
}
void usb_shared_reset_report(usb_fs_report_t **reports) {
for (int i = 0; i <= REPORT_ID_COUNT; i++) {
if (reports[i] == NULL) {
continue;
}
memset(&reports[i]->data, 0, reports[i]->length);
reports[i]->idle_rate = 0;
reports[i]->last_report = 0;
}
}
bool usb_get_report_cb(USBDriver *driver) {
usb_control_request_t *setup = (usb_control_request_t *)driver->setup;
uint8_t interface = setup->wIndex;
uint8_t report_id = setup->wValue.lbyte;
static usb_fs_report_t report;
if (!IS_VALID_INTERFACE(interface) || !IS_VALID_REPORT_ID(report_id)) {
return false;
}
usb_endpoint_in_lut_t ep = usb_endpoint_interface_lut[interface];
if (!IS_VALID_USB_ENDPOINT_IN_LUT(ep)) {
return false;
}
usb_report_storage_t *report_storage = usb_endpoints_in[ep].report_storage;
if (report_storage == NULL) {
return false;
}
report_storage->get_report(report_storage->reports, report_id, &report);
usbSetupTransfer(driver, (uint8_t *)report.data, report.length, NULL);
return true;
}
static bool run_idle_task = false;
void usb_set_idle_rate(usb_fs_report_t **reports, uint8_t report_id, uint8_t idle_rate) {
(void)report_id;
if (*reports == NULL) {
return;
}
(*reports)->idle_rate = idle_rate * 4;
run_idle_task |= idle_rate != 0;
}
uint8_t usb_get_idle_rate(usb_fs_report_t **reports, uint8_t report_id) {
(void)report_id;
if (*reports == NULL) {
return 0;
}
return (*reports)->idle_rate / 4;
}
bool usb_idle_timer_elapsed(usb_fs_report_t **reports, uint8_t report_id) {
(void)report_id;
if (*reports == NULL) {
return false;
}
osalSysLock();
time_msecs_t idle_rate = (*reports)->idle_rate;
systime_t last_report = (*reports)->last_report;
osalSysUnlock();
if (idle_rate == 0) {
return false;
}
return chTimeI2MS(chVTTimeElapsedSinceX(last_report)) >= idle_rate;
}
void usb_shared_set_idle_rate(usb_fs_report_t **reports, uint8_t report_id, uint8_t idle_rate) {
// USB spec demands that a report_id of 0 would set the idle rate for all
// reports of that endpoint, but this can easily lead to resource
// exhaustion, therefore we deliberalty break the spec at this point.
if (report_id == 0 || report_id > REPORT_ID_COUNT || reports[report_id] == NULL) {
return;
}
reports[report_id]->idle_rate = idle_rate * 4;
run_idle_task |= idle_rate != 0;
}
uint8_t usb_shared_get_idle_rate(usb_fs_report_t **reports, uint8_t report_id) {
if (report_id > REPORT_ID_COUNT || reports[report_id] == NULL) {
return 0;
}
return reports[report_id]->idle_rate / 4;
}
bool usb_shared_idle_timer_elapsed(usb_fs_report_t **reports, uint8_t report_id) {
if (report_id > REPORT_ID_COUNT || reports[report_id] == NULL) {
return false;
}
osalSysLock();
time_msecs_t idle_rate = reports[report_id]->idle_rate;
systime_t last_report = reports[report_id]->last_report;
osalSysUnlock();
if (idle_rate == 0) {
return false;
}
return chTimeI2MS(chVTTimeElapsedSinceX(last_report)) >= idle_rate;
}
void usb_idle_task(void) {
if (!run_idle_task) {
return;
}
static usb_fs_report_t report;
bool non_zero_idle_rate_found = false;
for (int ep = 0; ep < USB_ENDPOINT_IN_COUNT; ep++) {
usb_report_storage_t *report_storage = usb_endpoints_in[ep].report_storage;
if (report_storage == NULL) {
continue;
}
#if defined(SHARED_EP_ENABLE)
if (ep == USB_ENDPOINT_IN_SHARED) {
for (int report_id = 1; report_id <= REPORT_ID_COUNT; report_id++) {
osalSysLock();
non_zero_idle_rate_found |= report_storage->get_idle(report_storage->reports, report_id) != 0;
osalSysUnlock();
if (usb_endpoint_in_is_inactive(&usb_endpoints_in[ep]) && report_storage->idle_timer_elasped(report_storage->reports, report_id)) {
osalSysLock();
report_storage->get_report(report_storage->reports, report_id, &report);
osalSysUnlock();
send_report(ep, &report.data, report.length);
}
}
continue;
}
#endif
osalSysLock();
non_zero_idle_rate_found |= report_storage->get_idle(report_storage->reports, 0) != 0;
osalSysUnlock();
if (usb_endpoint_in_is_inactive(&usb_endpoints_in[ep]) && report_storage->idle_timer_elasped(report_storage->reports, 0)) {
osalSysLock();
report_storage->get_report(report_storage->reports, 0, &report);
osalSysUnlock();
send_report(ep, &report.data, report.length);
}
}
run_idle_task = non_zero_idle_rate_found;
}
bool usb_get_idle_cb(USBDriver *driver) {
usb_control_request_t *setup = (usb_control_request_t *)driver->setup;
uint8_t interface = setup->wIndex;
uint8_t report_id = setup->wValue.lbyte;
static uint8_t _Alignas(4) idle_rate;
if (!IS_VALID_INTERFACE(interface) || !IS_VALID_REPORT_ID(report_id)) {
return false;
}
usb_endpoint_in_lut_t ep = usb_endpoint_interface_lut[interface];
if (!IS_VALID_USB_ENDPOINT_IN_LUT(ep)) {
return false;
}
usb_report_storage_t *report_storage = usb_endpoints_in[ep].report_storage;
if (report_storage == NULL) {
return false;
}
idle_rate = report_storage->get_idle(report_storage->reports, report_id);
usbSetupTransfer(driver, &idle_rate, 1, NULL);
return true;
}
bool usb_set_idle_cb(USBDriver *driver) {
usb_control_request_t *setup = (usb_control_request_t *)driver->setup;
uint8_t interface = setup->wIndex;
uint8_t report_id = setup->wValue.lbyte;
uint8_t idle_rate = setup->wValue.hbyte;
if (!IS_VALID_INTERFACE(interface) || !IS_VALID_REPORT_ID(report_id)) {
return false;
}
usb_endpoint_in_lut_t ep = usb_endpoint_interface_lut[interface];
if (!IS_VALID_USB_ENDPOINT_IN_LUT(ep)) {
return false;
}
usb_report_storage_t *report_storage = usb_endpoints_in[ep].report_storage;
if (report_storage == NULL) {
return false;
}
report_storage->set_idle(report_storage->reports, report_id, idle_rate);
usbSetupTransfer(driver, NULL, 0, NULL);
return true;
}

View File

@ -0,0 +1,77 @@
// Copyright 2023 Stefan Kerkmann (@KarlK90)
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <ch.h>
#include <hal.h>
#include <stdint.h>
#include "timer.h"
typedef struct {
time_msecs_t idle_rate;
systime_t last_report;
uint8_t data[64];
size_t length;
} usb_fs_report_t;
typedef struct {
usb_fs_report_t **reports;
const void (*get_report)(usb_fs_report_t **, uint8_t, usb_fs_report_t *);
const void (*set_report)(usb_fs_report_t **, const uint8_t *, size_t);
const void (*reset_report)(usb_fs_report_t **);
const void (*set_idle)(usb_fs_report_t **, uint8_t, uint8_t);
const uint8_t (*get_idle)(usb_fs_report_t **, uint8_t);
const bool (*idle_timer_elasped)(usb_fs_report_t **, uint8_t);
} usb_report_storage_t;
#define QMK_USB_REPORT_STROAGE_ENTRY(_report_id, _report_size) [_report_id] = &((usb_fs_report_t){.data = {[0] = _report_id}, .length = _report_size})
#define QMK_USB_REPORT_STORAGE(_get_report, _set_report, _reset_report, _get_idle, _set_idle, _idle_timer_elasped, _report_count, _reports...) \
&((usb_report_storage_t){ \
.reports = (_Alignas(4) usb_fs_report_t *[_report_count]){_reports}, \
.get_report = _get_report, \
.set_report = _set_report, \
.reset_report = _reset_report, \
.get_idle = _get_idle, \
.set_idle = _set_idle, \
.idle_timer_elasped = _idle_timer_elasped, \
})
#define QMK_USB_REPORT_STORAGE_DEFAULT(_report_length) \
QMK_USB_REPORT_STORAGE(&usb_get_report, /* _get_report */ \
&usb_set_report, /* _set_report */ \
&usb_reset_report, /* _reset_report */ \
&usb_get_idle_rate, /* _get_idle */ \
&usb_set_idle_rate, /* _set_idle */ \
&usb_idle_timer_elapsed, /* _idle_timer_elasped */ \
1, /* _report_count */ \
QMK_USB_REPORT_STROAGE_ENTRY(0, _report_length))
// USB HID SET_REPORT and GET_REPORT handling functions
void usb_set_report(usb_fs_report_t **reports, const uint8_t *data, size_t length);
void usb_shared_set_report(usb_fs_report_t **reports, const uint8_t *data, size_t length);
void usb_get_report(usb_fs_report_t **reports, uint8_t report_id, usb_fs_report_t *report);
void usb_shared_get_report(usb_fs_report_t **reports, uint8_t report_id, usb_fs_report_t *report);
void usb_reset_report(usb_fs_report_t **reports);
void usb_shared_reset_report(usb_fs_report_t **reports);
bool usb_get_report_cb(USBDriver *driver);
// USB HID SET_IDLE and GET_IDLE handling functions
void usb_idle_task(void);
void usb_set_idle_rate(usb_fs_report_t **timers, uint8_t report_id, uint8_t idle_rate);
void usb_shared_set_idle_rate(usb_fs_report_t **timers, uint8_t report_id, uint8_t idle_rate);
uint8_t usb_get_idle_rate(usb_fs_report_t **timers, uint8_t report_id);
uint8_t usb_shared_get_idle_rate(usb_fs_report_t **timers, uint8_t report_id);
bool usb_idle_timer_elapsed(usb_fs_report_t **timers, uint8_t report_id);
bool usb_shared_idle_timer_elapsed(usb_fs_report_t **timers, uint8_t report_id);
bool usb_get_idle_cb(USBDriver *driver);
bool usb_set_idle_cb(USBDriver *driver);

View File

@ -29,7 +29,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// clang-format off
/* HID report IDs */
enum hid_report_ids {
enum hid_report_ids {
REPORT_ID_ALL = 0,
REPORT_ID_KEYBOARD = 1,
REPORT_ID_MOUSE,
REPORT_ID_SYSTEM,
@ -37,9 +38,12 @@ enum hid_report_ids {
REPORT_ID_PROGRAMMABLE_BUTTON,
REPORT_ID_NKRO,
REPORT_ID_JOYSTICK,
REPORT_ID_DIGITIZER
REPORT_ID_DIGITIZER,
REPORT_ID_COUNT = REPORT_ID_DIGITIZER
};
#define IS_VALID_REPORT_ID(id) ((id) >= REPORT_ID_ALL && (id) <= REPORT_ID_COUNT)
/* Mouse buttons */
#define MOUSE_BTN_MASK(n) (1 << (n))
enum mouse_buttons {

View File

@ -196,6 +196,8 @@ enum usb_interfaces {
TOTAL_INTERFACES
};
#define IS_VALID_INTERFACE(i) ((i) >= 0 && (i) < TOTAL_INTERFACES)
#define NEXT_EPNUM __COUNTER__
/*