forked from kofal.net/zmk
feat(usb): Add boot protocol support
* USB boot protocol support * Use a single definition of a boot report, used for regular reports in non-6KRO, and for rollover in all branches. * Handle gaps in the zmk report when producing a boot report in HKRO mode. For .example, if it was 8KRO, it would be possible to have the state 0 0 0 0 0 0 0 17 (by pressing 8 keys, and letting go of the first 7). Copying the first 6 bytes would not show up the single pressed key. * Disable usb status change and callback on SOF events: SOF events were introduced by the boot protocol changes, and required internally by Zephyr's idle support, but are unused within ZMK itself. Ignore them in the usb status callback. --------- Co-authored-by: Andrew Childs <lorne@cons.org.nz>
This commit is contained in:
@@ -23,11 +23,75 @@ static K_SEM_DEFINE(hid_sem, 1, 1);
|
||||
|
||||
static void in_ready_cb(const struct device *dev) { k_sem_give(&hid_sem); }
|
||||
|
||||
#define HID_GET_REPORT_TYPE_MASK 0xff00
|
||||
#define HID_GET_REPORT_ID_MASK 0x00ff
|
||||
|
||||
#define HID_REPORT_TYPE_INPUT 0x100
|
||||
#define HID_REPORT_TYPE_OUTPUT 0x200
|
||||
#define HID_REPORT_TYPE_FEATURE 0x300
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_USB_BOOT)
|
||||
static uint8_t hid_protocol = HID_PROTOCOL_REPORT;
|
||||
|
||||
static void set_proto_cb(const struct device *dev, uint8_t protocol) { hid_protocol = protocol; }
|
||||
|
||||
void zmk_usb_hid_set_protocol(uint8_t protocol) { hid_protocol = protocol; }
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_USB_BOOT) */
|
||||
|
||||
static uint8_t *get_keyboard_report(size_t *len) {
|
||||
if (hid_protocol == HID_PROTOCOL_REPORT) {
|
||||
struct zmk_hid_keyboard_report *report = zmk_hid_get_keyboard_report();
|
||||
*len = sizeof(*report);
|
||||
return (uint8_t *)report;
|
||||
}
|
||||
#if IS_ENABLED(CONFIG_ZMK_USB_BOOT)
|
||||
zmk_hid_boot_report_t *boot_report = zmk_hid_get_boot_report();
|
||||
*len = sizeof(*boot_report);
|
||||
return (uint8_t *)boot_report;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int get_report_cb(const struct device *dev, struct usb_setup_packet *setup, int32_t *len,
|
||||
uint8_t **data) {
|
||||
|
||||
/*
|
||||
* 7.2.1 of the HID v1.11 spec is unclear about handling requests for reports that do not exist
|
||||
* For requested reports that aren't input reports, return -ENOTSUP like the Zephyr subsys does
|
||||
*/
|
||||
if ((setup->wValue & HID_GET_REPORT_TYPE_MASK) != HID_REPORT_TYPE_INPUT) {
|
||||
LOG_ERR("Unsupported report type %d requested", (setup->wValue & HID_GET_REPORT_TYPE_MASK)
|
||||
<< 8);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
switch (setup->wValue & HID_GET_REPORT_ID_MASK) {
|
||||
case ZMK_HID_REPORT_ID_KEYBOARD: {
|
||||
*data = get_keyboard_report(len);
|
||||
break;
|
||||
}
|
||||
case ZMK_HID_REPORT_ID_CONSUMER: {
|
||||
struct zmk_hid_consumer_report *report = zmk_hid_get_consumer_report();
|
||||
*data = (uint8_t *)report;
|
||||
*len = sizeof(*report);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_ERR("Invalid report ID %d requested", setup->wValue & HID_GET_REPORT_ID_MASK);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hid_ops ops = {
|
||||
#if IS_ENABLED(CONFIG_ZMK_USB_BOOT)
|
||||
.protocol_change = set_proto_cb,
|
||||
#endif
|
||||
.int_in_ready = in_ready_cb,
|
||||
.get_report = get_report_cb,
|
||||
};
|
||||
|
||||
int zmk_usb_hid_send_report(const uint8_t *report, size_t len) {
|
||||
static int zmk_usb_hid_send_report(const uint8_t *report, size_t len) {
|
||||
switch (zmk_usb_get_status()) {
|
||||
case USB_DC_SUSPEND:
|
||||
return usb_wakeup_request();
|
||||
@@ -48,6 +112,23 @@ int zmk_usb_hid_send_report(const uint8_t *report, size_t len) {
|
||||
}
|
||||
}
|
||||
|
||||
int zmk_usb_hid_send_keyboard_report() {
|
||||
size_t len;
|
||||
uint8_t *report = get_keyboard_report(&len);
|
||||
return zmk_usb_hid_send_report(report, len);
|
||||
}
|
||||
|
||||
int zmk_usb_hid_send_consumer_report() {
|
||||
#if IS_ENABLED(CONFIG_ZMK_USB_BOOT)
|
||||
if (hid_protocol == HID_PROTOCOL_BOOT) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_USB_BOOT) */
|
||||
|
||||
struct zmk_hid_consumer_report *report = zmk_hid_get_consumer_report();
|
||||
return zmk_usb_hid_send_report((uint8_t *)report, sizeof(*report));
|
||||
}
|
||||
|
||||
static int zmk_usb_hid_init(const struct device *_arg) {
|
||||
hid_dev = device_get_binding("HID_0");
|
||||
if (hid_dev == NULL) {
|
||||
@@ -56,6 +137,11 @@ static int zmk_usb_hid_init(const struct device *_arg) {
|
||||
}
|
||||
|
||||
usb_hid_register_device(hid_dev, zmk_hid_report_desc, sizeof(zmk_hid_report_desc), &ops);
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_USB_BOOT)
|
||||
usb_hid_set_proto_code(hid_dev, HID_BOOT_IFACE_CODE_KEYBOARD);
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_USB_BOOT) */
|
||||
|
||||
usb_hid_init(hid_dev);
|
||||
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user