Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(hid) Basic support for generic desktop usage page #2473

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions app/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,16 @@ config ZMK_HID_SEPARATE_MOD_RELEASE_REPORT
Send a separate release event for the modifiers, to make sure the release
of the modifier doesn't get recognized before the actual key's release event.

config ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC
bool "Basic Generic Desktop HID Usage Support"
help
Enable only 15 Generic Desktop ID values, "Power Down", "Sleep", "Wake up", ...,
"Warm Restart" to be sent to hosts.

config ZMK_BLE_GENERIC_DESKTOP_REPORT_QUEUE_SIZE
int "Max number of Generic Desktop HID reports to queue for sending over BLE"
default 3

menu "Output Types"

config ZMK_USB
Expand Down
1 change: 1 addition & 0 deletions app/include/dt-bindings/zmk/hid_usage.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
#define HID_USAGE_GD_APPLICATION_DEBUGGER_BREAK (0xA6) // OSC
#define HID_USAGE_GD_SYSTEM_SPEAKER_MUTE (0xA7) // OSC
#define HID_USAGE_GD_SYSTEM_HIBERNATE (0xA8) // OSC
#define HID_USAGE_GD_SYSTEM_MICROPHONE_MUTE (0xA9) // OOC
#define HID_USAGE_GD_SYSTEM_DISPLAY_INVERT (0xB0) // OSC
#define HID_USAGE_GD_SYSTEM_DISPLAY_INTERNAL (0xB1) // OSC
#define HID_USAGE_GD_SYSTEM_DISPLAY_EXTERNAL (0xB2) // OSC
Expand Down
48 changes: 48 additions & 0 deletions app/include/dt-bindings/zmk/keys.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,54 @@
#define SYSTEM_WAKE_UP (ZMK_HID_USAGE(HID_USAGE_GD, HID_USAGE_GD_SYSTEM_WAKE_UP))
#define SYS_WAKE (SYSTEM_WAKE_UP)

/* System Context Menu */
#define SYSTEM_CONTEXT_MENU (ZMK_HID_USAGE(HID_USAGE_GD, HID_USAGE_GD_SYSTEM_CONTEXT_MENU))
#define SYS_CONTEXT_MENU (SYSTEM_CONTEXT_MENU)

/* System Main Menu */
#define SYSTEM_MAIN_MENU (ZMK_HID_USAGE(HID_USAGE_GD, HID_USAGE_GD_SYSTEM_MAIN_MENU))
#define SYS_MAIN_MENU (SYSTEM_MAIN_MENU)

/* System App Menu */
#define SYSTEM_APP_MENU (ZMK_HID_USAGE(HID_USAGE_GD, HID_USAGE_GD_SYSTEM_APP_MENU))
#define SYS_APP_MENU (SYSTEM_APP_MENU)

/* System Menu Help */
#define SYSTEM_MENU_HELP (ZMK_HID_USAGE(HID_USAGE_GD, HID_USAGE_GD_SYSTEM_MENU_HELP))
#define SYS_MENU_HELP (SYSTEM_MENU_HELP)

/* System Menu Exit */
#define SYSTEM_MENU_EXIT (ZMK_HID_USAGE(HID_USAGE_GD, HID_USAGE_GD_SYSTEM_MENU_EXIT))
#define SYS_MENU_EXIT (SYSTEM_MENU_EXIT)

/* System Menu Select */
#define SYSTEM_MENU_SELECT (ZMK_HID_USAGE(HID_USAGE_GD, HID_USAGE_GD_SYSTEM_MENU_SELECT))
#define SYS_MENU_SELECT (SYSTEM_MENU_SELECT)

/* System Main Right */
#define SYSTEM_MENU_RIGHT (ZMK_HID_USAGE(HID_USAGE_GD, HID_USAGE_GD_SYSTEM_MENU_RIGHT))
#define SYS_MENU_RIGHT (SYSTEM_MENU_RIGHT)

/* System Menu Left */
#define SYSTEM_MENU_LEFT (ZMK_HID_USAGE(HID_USAGE_GD, HID_USAGE_GD_SYSTEM_MENU_LEFT))
#define SYS_MENU_LEFT (SYSTEM_MENU_LEFT)

/* System Main Up */
#define SYSTEM_MENU_UP (ZMK_HID_USAGE(HID_USAGE_GD, HID_USAGE_GD_SYSTEM_MENU_UP))
#define SYS_MENU_UP (SYSTEM_MENU_UP)

/* System Menu Down */
#define SYSTEM_MENU_DOWN (ZMK_HID_USAGE(HID_USAGE_GD, HID_USAGE_GD_SYSTEM_MENU_DOWN))
#define SYS_MENU_DOWN (SYSTEM_MENU_DOWN)

/* System Cold Restart */
#define SYSTEM_COLD_RESTART (ZMK_HID_USAGE(HID_USAGE_GD, HID_USAGE_GD_SYSTEM_COLD_RESTART))
#define SYS_COLD_RESTART (SYSTEM_COLD_RESTART)

/* System Warm Restart */
#define SYSTEM_WARM_RESTART (ZMK_HID_USAGE(HID_USAGE_GD, HID_USAGE_GD_SYSTEM_WARM_RESTART))
#define SYS_WARM_RESTART (SYSTEM_WARM_RESTART)

/* Keyboard a and A */
#define A (ZMK_HID_USAGE(HID_USAGE_KEY, HID_USAGE_KEY_KEYBOARD_A))

Expand Down
61 changes: 61 additions & 0 deletions app/include/zmk/hid.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
#define ZMK_HID_REPORT_ID_LEDS 0x01
#define ZMK_HID_REPORT_ID_CONSUMER 0x02
#define ZMK_HID_REPORT_ID_MOUSE 0x03
#define ZMK_HID_REPORT_ID_GENERIC_DESKTOP 0x04

static const uint8_t zmk_hid_report_desc[] = {
HID_USAGE_PAGE(HID_USAGE_GEN_DESKTOP),
Expand Down Expand Up @@ -193,6 +194,47 @@ static const uint8_t zmk_hid_report_desc[] = {
HID_END_COLLECTION,
HID_END_COLLECTION,
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)

#if IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)
HID_USAGE_PAGE(HID_USAGE_GEN_DESKTOP),
HID_USAGE(HID_USAGE_GD_SYSTEM_CONTROL),
HID_COLLECTION(HID_COLLECTION_APPLICATION),
HID_REPORT_ID(ZMK_HID_REPORT_ID_GENERIC_DESKTOP),

// System Power down (0x81) to System Menu select (0x89), OSC, 9 bits
HID_USAGE_MIN8(HID_USAGE_GD_SYSTEM_POWER_DOWN),
HID_USAGE_MAX8(HID_USAGE_GD_SYSTEM_MENU_SELECT),
HID_LOGICAL_MIN8(0x00),
HID_LOGICAL_MAX8(0x01),
HID_REPORT_SIZE(0x01),
HID_REPORT_COUNT(0x09),
HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_REL),

// System Menu Right (0x8A) to System Menu Down (0x8D), RTC, 4 bits
HID_USAGE_MIN8(HID_USAGE_GD_SYSTEM_MENU_RIGHT),
HID_USAGE_MAX8(HID_USAGE_GD_SYSTEM_MENU_DOWN),
HID_LOGICAL_MIN8(0x00),
HID_LOGICAL_MAX8(0x01),
HID_REPORT_SIZE(0x01),
HID_REPORT_COUNT(0x04),
HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS),

// System Cold Restart (0x8E) & System Warm Restart (0x8F), OSC, 2 bits
HID_USAGE_MIN8(HID_USAGE_GD_SYSTEM_COLD_RESTART),
HID_USAGE_MAX8(HID_USAGE_GD_SYSTEM_WARM_RESTART),
HID_LOGICAL_MIN8(0x00),
HID_LOGICAL_MAX8(0x01),
HID_REPORT_SIZE(0x01),
HID_REPORT_COUNT(0x02),
HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_REL),

// 1 bit padding
HID_REPORT_SIZE(0x01),
HID_REPORT_COUNT(0x1),
HID_INPUT(ZMK_HID_MAIN_VAL_CONST | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS),

HID_END_COLLECTION,
#endif // IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)
};

#if IS_ENABLED(CONFIG_ZMK_USB_BOOT)
Expand Down Expand Up @@ -270,6 +312,17 @@ struct zmk_hid_mouse_report {

#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)

#if IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)
struct zmk_hid_generic_desktop_report_body {
uint8_t keys[2];
} __packed;

struct zmk_hid_generic_desktop_report {
uint8_t report_id;
struct zmk_hid_generic_desktop_report_body body;
} __packed;
#endif // IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)

zmk_mod_flags_t zmk_hid_get_explicit_mods(void);
int zmk_hid_register_mod(zmk_mod_t modifier);
int zmk_hid_unregister_mod(zmk_mod_t modifier);
Expand Down Expand Up @@ -304,9 +357,17 @@ int zmk_hid_mouse_buttons_release(zmk_mouse_button_flags_t buttons);
void zmk_hid_mouse_clear(void);
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)

#if IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)
void zmk_hid_generic_desktop_clear(void);
#endif // IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)

struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report(void);
struct zmk_hid_consumer_report *zmk_hid_get_consumer_report(void);

#if IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)
struct zmk_hid_generic_desktop_report *zmk_hid_get_generic_desktop_report(void);
#endif // IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)

#if IS_ENABLED(CONFIG_ZMK_USB_BOOT)
zmk_hid_boot_report_t *zmk_hid_get_boot_report();
#endif
Expand Down
4 changes: 4 additions & 0 deletions app/include/zmk/hog.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
int zmk_hog_send_keyboard_report(struct zmk_hid_keyboard_report_body *body);
int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *body);

#if IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)
int zmk_hog_send_generic_desktop_report(struct zmk_hid_generic_desktop_report_body *body);
#endif // IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)

#if IS_ENABLED(CONFIG_ZMK_MOUSE)
int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *body);
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
3 changes: 3 additions & 0 deletions app/include/zmk/usb_hid.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@

int zmk_usb_hid_send_keyboard_report(void);
int zmk_usb_hid_send_consumer_report(void);
#if IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)
int zmk_usb_hid_send_generic_desktop_report(void);
#endif // IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
int zmk_usb_hid_send_mouse_report(void);
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
Expand Down
45 changes: 45 additions & 0 deletions app/src/endpoints.c
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,43 @@ static int send_consumer_report(void) {
return -ENOTSUP;
}

#if IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)
static int send_generic_desktop_report(void) {
switch (current_instance.transport) {
case ZMK_TRANSPORT_USB: {
#if IS_ENABLED(CONFIG_ZMK_USB)
int err = zmk_usb_hid_send_generic_desktop_report();
if (err) {
LOG_ERR("FAILED TO SEND OVER USB: %d", err);
}
return err;
#else
LOG_ERR("USB endpoint is not supported");
return -ENOTSUP;
#endif /* IS_ENABLED(CONFIG_ZMK_USB) */
}

case ZMK_TRANSPORT_BLE: {
#if IS_ENABLED(CONFIG_ZMK_BLE)
struct zmk_hid_generic_desktop_report *generic_desktop_report =
zmk_hid_get_generic_desktop_report();
int err = zmk_hog_send_generic_desktop_report(&generic_desktop_report->body);
if (err) {
LOG_ERR("FAILED TO SEND OVER HOG: %d", err);
}
return err;
#else
LOG_ERR("BLE HOG endpoint is not supported");
return -ENOTSUP;
#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */
}
}

LOG_ERR("Unhandled endpoint transport %d", current_instance.transport);
return -ENOTSUP;
}
#endif // IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)

int zmk_endpoints_send_report(uint16_t usage_page) {

LOG_DBG("usage page 0x%02X", usage_page);
Expand All @@ -197,6 +234,11 @@ int zmk_endpoints_send_report(uint16_t usage_page) {

case HID_USAGE_CONSUMER:
return send_consumer_report();

#if IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)
case HID_USAGE_GD:
return send_generic_desktop_report();
#endif // IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)
}

LOG_ERR("Unsupported usage page %d", usage_page);
Expand Down Expand Up @@ -337,6 +379,9 @@ void zmk_endpoints_clear_current(void) {
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
zmk_hid_mouse_clear();
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
#if IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)
zmk_hid_generic_desktop_clear();
#endif // IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)

zmk_endpoints_send_report(HID_USAGE_KEY);
zmk_endpoints_send_report(HID_USAGE_CONSUMER);
Expand Down
70 changes: 70 additions & 0 deletions app/src/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ static struct zmk_hid_mouse_report mouse_report = {.report_id = ZMK_HID_REPORT_I

#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)

#if IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)

static struct zmk_hid_generic_desktop_report generic_desktop_report = {
.report_id = ZMK_HID_REPORT_ID_GENERIC_DESKTOP, .body = {.keys = {0}}};

#endif // IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)

// Keep track of how often a modifier was pressed.
// Only release the modifier if the count is 0.
static int explicit_modifier_counts[8] = {0, 0, 0, 0, 0, 0, 0, 0};
Expand Down Expand Up @@ -340,12 +347,61 @@ bool zmk_hid_consumer_is_pressed(zmk_key_t key) {
return false;
}

#if IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)
#define TOGGLE_GENERIC_DESKTOP(code, val) \
WRITE_BIT(generic_desktop_report.body.keys[code / 8], code % 8, val)

int32_t zmk_hid_generic_desktop_code_to_offset(zmk_key_t code) {
if (HID_USAGE_GD_SYSTEM_POWER_DOWN <= code && code <= HID_USAGE_GD_SYSTEM_WARM_RESTART) {
return code - HID_USAGE_GD_SYSTEM_POWER_DOWN;
}

LOG_ERR("Unhandled generic desktop code 0x%02X", code);
return -ENOTSUP;
}

int zmk_hid_generic_desktop_press(zmk_key_t code) {
int32_t offset = zmk_hid_generic_desktop_code_to_offset(code);
if (offset == -ENOTSUP) {
return -ENOTSUP;
}
TOGGLE_GENERIC_DESKTOP(offset, 1);
return 0;
}

int zmk_hid_generic_desktop_release(zmk_key_t code) {
int32_t offset = zmk_hid_generic_desktop_code_to_offset(code);
if (offset == -ENOTSUP) {
return -ENOTSUP;
}
TOGGLE_GENERIC_DESKTOP(offset, 0);
return 0;
}

void zmk_hid_generic_desktop_clear(void) {
memset(&generic_desktop_report.body, 0, sizeof(generic_desktop_report.body));
}

bool zmk_hid_generic_desktop_is_pressed(zmk_key_t code) {
int32_t offset = zmk_hid_generic_desktop_code_to_offset(code);
if (offset == -ENOTSUP) {
return false;
}
return generic_desktop_report.body.keys[offset / 8] & (1 << (offset % 8));
}

#endif // IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)

int zmk_hid_press(uint32_t usage) {
switch (ZMK_HID_USAGE_PAGE(usage)) {
case HID_USAGE_KEY:
return zmk_hid_keyboard_press(ZMK_HID_USAGE_ID(usage));
case HID_USAGE_CONSUMER:
return zmk_hid_consumer_press(ZMK_HID_USAGE_ID(usage));
#if IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)
case HID_USAGE_GD:
return zmk_hid_generic_desktop_press(ZMK_HID_USAGE_ID(usage));
#endif // IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)
}
return -EINVAL;
}
Expand All @@ -356,6 +412,10 @@ int zmk_hid_release(uint32_t usage) {
return zmk_hid_keyboard_release(ZMK_HID_USAGE_ID(usage));
case HID_USAGE_CONSUMER:
return zmk_hid_consumer_release(ZMK_HID_USAGE_ID(usage));
#if IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)
case HID_USAGE_GD:
return zmk_hid_generic_desktop_release(ZMK_HID_USAGE_ID(usage));
#endif // IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)
}
return -EINVAL;
}
Expand All @@ -366,6 +426,10 @@ bool zmk_hid_is_pressed(uint32_t usage) {
return zmk_hid_keyboard_is_pressed(ZMK_HID_USAGE_ID(usage));
case HID_USAGE_CONSUMER:
return zmk_hid_consumer_is_pressed(ZMK_HID_USAGE_ID(usage));
#if IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)
case HID_USAGE_GD:
return zmk_hid_generic_desktop_is_pressed(ZMK_HID_USAGE_ID(usage));
#endif // IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)
}
return false;
}
Expand Down Expand Up @@ -450,3 +514,9 @@ struct zmk_hid_mouse_report *zmk_hid_get_mouse_report(void) {
}

#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)

#if IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)
struct zmk_hid_generic_desktop_report *zmk_hid_get_generic_desktop_report(void) {
return &generic_desktop_report;
}
#endif // IS_ENABLED(CONFIG_ZMK_HID_GENERIC_DESKTOP_USAGES_BASIC)
Loading