From 8de6c85b7464fdc3518648242ce16142e30ed73d Mon Sep 17 00:00:00 2001 From: Pete Johanson Date: Tue, 2 Jun 2020 11:23:22 -0400 Subject: [PATCH] Implement a basic set of consumer page keycodes. --- README.md | 3 +- .../keymaps/default/keymap.overlay | 13 ++- include/dt-bindings/zmk/keys.h | 22 ++++- include/zmk/endpoints.h | 3 +- include/zmk/hid.h | 93 ++++++++++++++++++- include/zmk/hog.h | 3 +- include/zmk/usb_hid.h | 2 +- src/endpoints.c | 58 +++++++++--- src/handlers.c | 2 +- src/hid.c | 93 +++++++++++++------ src/hog.c | 32 ++++++- src/keymap.c | 7 +- src/usb_hid.c | 4 +- 13 files changed, 267 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index ae2bf502..a52e1583 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,7 @@ Basic WIP website to learn more: https://zmk.netlify.app/ # Missing Features -- Consumer Key Support (play/pause, etc) -- Mod Tap +- Layer Tap - One Shot - Shell over BLE? - Split support diff --git a/boards/shields/petejohanson_handwire/keymaps/default/keymap.overlay b/boards/shields/petejohanson_handwire/keymaps/default/keymap.overlay index 863e0341..d87363f9 100644 --- a/boards/shields/petejohanson_handwire/keymaps/default/keymap.overlay +++ b/boards/shields/petejohanson_handwire/keymaps/default/keymap.overlay @@ -18,19 +18,24 @@ label = "DEFAULT"; keys = < - KC_A MT(MOD_LSFT, KC_B) - CC_RAIS CC_LOWR + KC_A MT(MOD_LSFT, KC_B) ZC_NO ZC_NO + CC_RAIS CC_LOWR ZC_NO ZC_NO >; }; lower: layer_1 { label = "LOWER"; - keys = ; + keys = < + KC_MPLY KC_MNXT ZC_NO ZC_NO + ZC_TRNS ZC_TRNS ZC_NO ZC_NO + >; }; raise: layer_2 { label = "RAISE"; - keys = ; + keys = < + KC_C KC_D ZC_NO ZC_NO + ZC_TRNS ZC_TRNS ZC_NO ZC_NO>; }; }; }; diff --git a/include/dt-bindings/zmk/keys.h b/include/dt-bindings/zmk/keys.h index 027eb92c..c91d27b1 100644 --- a/include/dt-bindings/zmk/keys.h +++ b/include/dt-bindings/zmk/keys.h @@ -99,10 +99,24 @@ #define KC_RALT 0xE6 #define KC_RGUI 0xE7 -#define ZC_TRNS 0xF0 -#define ZC_NO 0xF1 +#define KC_VOLU 0x80 +#define KC_VOLD 0x81 -#define ZC_CSTM(n) (0xFF + n) +/* The following are select consumer page usages */ + +#define KC_MNXT 0x100 +#define KC_MPRV 0x101 +#define KC_MSTP 0x102 +#define KC_MJCT 0x103 +#define KC_MPLY 0x104 +#define KC_MMUT 0x105 +#define KC_MVLU 0x106 +#define KC_MVLD 0x107 + +#define ZC_TRNS (0xFFFF) +#define ZC_NO (0xFFFF - 1) + +#define ZC_CSTM(n) (0xFFF + n) #define MOD_LCTL (1 << 0x00) #define MOD_LSFT (1 << 0x01) @@ -119,6 +133,8 @@ #define ZK_KEY(a) (a & 0xFFFF) #define ZK_MODS(a) ((a >> 16) & 0xFF) +#define ZK_IS_CONSUMER(k) (ZK_KEY(k) >= 0x100) + #define ZMK_ACTION_KEY 0x01 #define ZMK_ACTION_MOD_TAP 0x01 #define ZMK_ACTION_ONE_SHOT 0x02 diff --git a/include/zmk/endpoints.h b/include/zmk/endpoints.h index 666f00da..255ca54d 100644 --- a/include/zmk/endpoints.h +++ b/include/zmk/endpoints.h @@ -1,7 +1,8 @@ #pragma once #include +#include int zmk_endpoints_init(); -int zmk_endpoints_send_report(); +int zmk_endpoints_send_report(enum zmk_hid_report_changes changes); int zmk_endpoints_send_key_event(struct zmk_key_event key_event); diff --git a/include/zmk/hid.h b/include/zmk/hid.h index 3767b783..7e3560d5 100644 --- a/include/zmk/hid.h +++ b/include/zmk/hid.h @@ -7,6 +7,8 @@ #include +#define COLLECTION_REPORT 0x03 + #define ZMK_HID_MAX_KEYCODE KC_APP static const u8_t zmk_hid_report_desc[] = { @@ -54,7 +56,7 @@ static const u8_t zmk_hid_report_desc[] = { /* LOGICAL_MINIMUM (0) */ HID_GI_LOGICAL_MIN(1), 0x00, - /* LOGICAL_MAXIMUM (101) */ + /* LOGICAL_MAXIMUM (1) */ HID_GI_LOGICAL_MAX(1), 0x01, /* USAGE_MINIMUM (Reserved) */ @@ -86,6 +88,62 @@ static const u8_t zmk_hid_report_desc[] = { 0x03, /* END_COLLECTION */ HID_MI_COLLECTION_END, + /* USAGE_PAGE (Consumer) */ + HID_GI_USAGE_PAGE, + 0x0C, + /* USAGE (Consumer Control) */ + HID_LI_USAGE, + 0x01, + /* Consumer Page */ + HID_MI_COLLECTION, + COLLECTION_APPLICATION, + /* REPORT ID (1) */ + HID_GI_REPORT_ID, + 0x02, + /* USAGE_PAGE (Consumer) */ + HID_GI_USAGE_PAGE, + 0x0C, + /* LOGICAL_MINIMUM (0) */ + HID_GI_LOGICAL_MIN(1), + 0x00, + /* LOGICAL_MAXIMUM (1) */ + HID_GI_LOGICAL_MAX(1), + 0x01, + /* USAGE (Scan Next Track) */ + HID_LI_USAGE, + 0xB5, + /* USAGE (Scan Previous Track) */ + HID_LI_USAGE, + 0xB6, + /* USAGE (Stop) */ + HID_LI_USAGE, + 0xB7, + /* USAGE (Eject) */ + HID_LI_USAGE, + 0xB8, + /* USAGE (Media Play/Pause) */ + HID_LI_USAGE, + 0xCD, + /* USAGE (Mute) */ + HID_LI_USAGE, + 0xE2, + /* USAGE (Volume Increment) */ + HID_LI_USAGE, + 0xE9, + /* USAGE (Volume Decrement) */ + HID_LI_USAGE, + 0xEA, + /* INPUT (Data,Ary,Abs) */ + /* REPORT_SIZE (1) */ + HID_GI_REPORT_SIZE, + 0x01, + /* REPORT_COUNT (8) */ + HID_GI_REPORT_COUNT, + 0x08, + HID_MI_INPUT, + 0x02, + /* END COLLECTION */ + HID_MI_COLLECTION_END, }; // struct zmk_hid_boot_report @@ -95,17 +153,42 @@ static const u8_t zmk_hid_report_desc[] = { // u8_t keys[6]; // } __packed; -struct zmk_hid_report +struct zmk_hid_keypad_report_body { zmk_mod_flags modifiers; u8_t keys[13]; } __packed; +struct zmk_hid_keypad_report +{ + u8_t report_id; + struct zmk_hid_keypad_report_body body; +} __packed; + +struct zmk_hid_consumer_report_body +{ + u8_t keys; +} __packed; + +struct zmk_hid_consumer_report +{ + u8_t report_id; + struct zmk_hid_consumer_report_body body; +} __packed; + +enum zmk_hid_report_changes +{ + None = 0x00, + Keypad = (0x01 << 0x00), + Consumer = (0x01 << 0x01) +}; + int zmk_hid_register_mod(zmk_mod modifier); int zmk_hid_unregister_mod(zmk_mod modifier); int zmk_hid_register_mods(zmk_mod_flags modifiers); int zmk_hid_unregister_mods(zmk_mod_flags modifiers); -int zmk_hid_press_key(zmk_key key); -int zmk_hid_release_key(zmk_key key); +enum zmk_hid_report_changes zmk_hid_press_key(zmk_key key); +enum zmk_hid_report_changes zmk_hid_release_key(zmk_key key); -struct zmk_hid_report *zmk_hid_get_report(); +struct zmk_hid_keypad_report *zmk_hid_get_keypad_report(); +struct zmk_hid_consumer_report *zmk_hid_get_consumer_report(); diff --git a/include/zmk/hog.h b/include/zmk/hog.h index a48fa989..e2f976a2 100644 --- a/include/zmk/hog.h +++ b/include/zmk/hog.h @@ -6,4 +6,5 @@ int zmk_hog_init(); -int zmk_hog_send_report(struct zmk_hid_report *report); +int zmk_hog_send_keypad_report(struct zmk_hid_keypad_report_body *body); +int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *body); diff --git a/include/zmk/usb_hid.h b/include/zmk/usb_hid.h index 893e7e48..5d7c744a 100644 --- a/include/zmk/usb_hid.h +++ b/include/zmk/usb_hid.h @@ -9,6 +9,6 @@ int zmk_usb_hid_init(); -int zmk_usb_hid_send_report(const struct zmk_hid_report *report); +int zmk_usb_hid_send_report(u8_t *report, size_t len); #endif diff --git a/src/endpoints.c b/src/endpoints.c index 1f2c94df..f46d42df 100644 --- a/src/endpoints.c +++ b/src/endpoints.c @@ -35,41 +35,71 @@ int zmk_endpoints_init() return 0; } -int zmk_endpoints_send_report() +int zmk_endpoints_send_report(enum zmk_hid_report_changes report_type) { int err; - struct zmk_hid_report *report = zmk_hid_get_report(); + struct zmk_hid_keypad_report *keypad_report; + struct zmk_hid_consumer_report *consumer_report; + switch (report_type) + { + case Keypad: + keypad_report = zmk_hid_get_keypad_report(); +#ifdef CONFIG_ZMK_USB + if (zmk_usb_hid_send_report((u8_t *)keypad_report, sizeof(struct zmk_hid_keypad_report)) != 0) + { + LOG_DBG("USB Send Failed"); + } +#endif /* CONFIG_ZMK_USB */ +#ifdef CONFIG_ZMK_BLE + err = zmk_hog_send_keypad_report(&keypad_report->body); + if (err) + { + LOG_ERR("FAILED TO SEND OVER HOG: %d", err); + } +#endif /* CONFIG_ZMK_BLE */ + + break; + case Consumer: + consumer_report = zmk_hid_get_consumer_report(); #ifdef CONFIG_ZMK_USB - if (zmk_usb_hid_send_report(report) != 0) - { - LOG_DBG("USB Send Failed"); - } + if (zmk_usb_hid_send_report((u8_t *)consumer_report, sizeof(struct zmk_hid_consumer_report)) != 0) + { + LOG_DBG("USB Send Failed"); + } #endif /* CONFIG_ZMK_USB */ #ifdef CONFIG_ZMK_BLE - err = zmk_hog_send_report(report); - if (err) - { - LOG_ERR("FAILED TO SEND OVER HOG: %d", err); - } + err = zmk_hog_send_consumer_report(&consumer_report->body); + if (err) + { + LOG_ERR("FAILED TO SEND OVER HOG: %d", err); + } #endif /* CONFIG_ZMK_BLE */ + break; + default: + LOG_ERR("Unknown report change type %d", report_type); + return -EINVAL; + } + return 0; } int zmk_endpoints_send_key_event(struct zmk_key_event key_event) { + enum zmk_hid_report_changes changes; + LOG_DBG("key %d, state %d\n", key_event.key, key_event.pressed); if (key_event.pressed) { - zmk_hid_press_key(key_event.key); + changes = zmk_hid_press_key(key_event.key); } else { - zmk_hid_release_key(key_event.key); + changes = zmk_hid_release_key(key_event.key); } - return zmk_endpoints_send_report(); + return zmk_endpoints_send_report(changes); } diff --git a/src/handlers.c b/src/handlers.c index ff969b18..90a08676 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -46,7 +46,7 @@ bool zmk_handle_action(zmk_action action, struct zmk_key_event *key_event) else { // Since not sending a keycode, at least send the report w/ the mod removed - zmk_endpoints_send_report(); + zmk_endpoints_send_report(Keypad); } } break; diff --git a/src/hid.c b/src/hid.c index 35dcdac9..b3d47cf9 100644 --- a/src/hid.c +++ b/src/hid.c @@ -1,15 +1,25 @@ -#include +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); -static struct zmk_hid_report report = { - .modifiers = 0, - .keys = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; +#include -#define _TOGGLE_MOD(mod, state) \ - if (modifier > MOD_RGUI) \ - { \ - return -EINVAL; \ - } \ - WRITE_BIT(report.modifiers, mod, state); \ +static struct zmk_hid_keypad_report kp_report = { + .report_id = 1, + .body = { + .modifiers = 0, + .keys = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}}; + +static struct zmk_hid_consumer_report consumer_report = { + .report_id = 2, + .body = { + .keys = 0x00}}; + +#define _TOGGLE_MOD(mod, state) \ + if (modifier > MOD_RGUI) \ + { \ + return -EINVAL; \ + } \ + WRITE_BIT(kp_report.body.modifiers, mod, state); \ return 0; int zmk_hid_register_mod(zmk_mod modifier) @@ -23,13 +33,13 @@ int zmk_hid_unregister_mod(zmk_mod modifier) int zmk_hid_register_mods(zmk_mod_flags modifiers) { - report.modifiers |= modifiers; + kp_report.body.modifiers |= modifiers; return 0; } int zmk_hid_unregister_mods(zmk_mod_flags modifiers) { - report.modifiers &= ~modifiers; + kp_report.body.modifiers &= ~modifiers; return 0; } @@ -40,56 +50,81 @@ int zmk_hid_unregister_mods(zmk_mod_flags modifiers) #define TOGGLE_BOOT_KEY(match, val) \ for (int idx = 0; idx < MAX_KEYS; idx++) \ { \ - if (report.boot.keys[idx + KEY_OFFSET] != match) \ + if (kp_report.boot.keys[idx + KEY_OFFSET] != match) \ { \ continue; \ } \ - report.boot.keys[idx + KEY_OFFSET] = val; \ + kp_report.boot.keys[idx + KEY_OFFSET] = val; \ break; \ } */ -#define TOGGLE_KEY(code, val) WRITE_BIT(report.keys[code / 8], code % 8, val) +#define TOGGLE_KEY(code, val) WRITE_BIT(kp_report.body.keys[code / 8], code % 8, val) + +#define TOGGLE_CONSUMER(key, state) \ + WRITE_BIT(consumer_report.body.keys, (key - 0x100), state); -int zmk_hid_press_key(zmk_key code) +enum zmk_hid_report_changes zmk_hid_press_key(zmk_key code) { if (code >= KC_LCTL && code <= KC_RGUI) { return zmk_hid_register_mod(code - KC_LCTL); } - if (code > ZMK_HID_MAX_KEYCODE) + if (ZK_IS_CONSUMER(code)) { - return -EINVAL; + LOG_DBG("Toggling a consumer key!"); + TOGGLE_CONSUMER(code, true); + return Consumer; } + else + { + if (code > ZMK_HID_MAX_KEYCODE) + { + return -EINVAL; + } - // TOGGLE_BOOT_KEY(0U, code); + // TOGGLE_BOOT_KEY(0U, code); - TOGGLE_KEY(code, true); + TOGGLE_KEY(code, true); - return 0; + return Keypad; + } }; -int zmk_hid_release_key(zmk_key code) +enum zmk_hid_report_changes zmk_hid_release_key(zmk_key code) { if (code >= KC_LCTL && code <= KC_RGUI) { return zmk_hid_unregister_mod(code - KC_LCTL); } - if (code > ZMK_HID_MAX_KEYCODE) + if (ZK_IS_CONSUMER(code)) { - return -EINVAL; + TOGGLE_CONSUMER(code, false); + return Consumer; } + else + { + if (code > ZMK_HID_MAX_KEYCODE) + { + return -EINVAL; + } - // TOGGLE_BOOT_KEY(code, 0U); + // TOGGLE_BOOT_KEY(0U, code); - TOGGLE_KEY(code, false); + TOGGLE_KEY(code, false); - return 0; + return Keypad; + } }; -struct zmk_hid_report *zmk_hid_get_report() +struct zmk_hid_keypad_report *zmk_hid_get_keypad_report() +{ + return &kp_report; +} + +struct zmk_hid_consumer_report *zmk_hid_get_consumer_report() { - return &report; + return &consumer_report; } diff --git a/src/hog.c b/src/hog.c index 67ccf5b6..087af423 100644 --- a/src/hog.c +++ b/src/hog.c @@ -49,6 +49,11 @@ static struct hids_report input = { .type = HIDS_INPUT, }; +static struct hids_report consumer_input = { + .id = 0x02, + .type = HIDS_INPUT, +}; + static bool host_requests_notification = false; static u8_t ctrl_point; // static u8_t proto_mode; @@ -70,8 +75,14 @@ static ssize_t read_hids_report_map(struct bt_conn *conn, const struct bt_gatt_a static ssize_t read_hids_input_report(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset) { - struct zmk_hid_report *report = zmk_hid_get_report(); - return bt_gatt_attr_read(conn, attr, buf, len, offset, report, sizeof(struct zmk_hid_report)); + struct zmk_hid_keypad_report_body *report_body = &zmk_hid_get_keypad_report()->body; + return bt_gatt_attr_read(conn, attr, buf, len, offset, report_body, sizeof(struct zmk_hid_keypad_report_body)); +} + +static ssize_t read_hids_consumer_input_report(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset) +{ + struct zmk_hid_consumer_report_body *report_body = &zmk_hid_get_consumer_report()->body; + return bt_gatt_attr_read(conn, attr, buf, len, offset, report_body, sizeof(struct zmk_hid_consumer_report_body)); } // static ssize_t write_proto_mode(struct bt_conn *conn, @@ -123,12 +134,25 @@ BT_GATT_SERVICE_DEFINE(hog_svc, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT), BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ, read_hids_report_ref, NULL, &input), + BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT, + BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, + BT_GATT_PERM_READ_ENCRYPT, + read_hids_consumer_input_report, NULL, NULL), + BT_GATT_CCC(input_ccc_changed, + BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT), + BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ, + read_hids_report_ref, NULL, &consumer_input), BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_CTRL_POINT, BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE, NULL, write_ctrl_point, &ctrl_point)); -int zmk_hog_send_report(struct zmk_hid_report *report) +int zmk_hog_send_keypad_report(struct zmk_hid_keypad_report_body *report) +{ + return bt_gatt_notify(NULL, &hog_svc.attrs[5], report, sizeof(struct zmk_hid_keypad_report_body)); +}; + +int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *report) { - return bt_gatt_notify(NULL, &hog_svc.attrs[5], report, sizeof(struct zmk_hid_report)); + return bt_gatt_notify(NULL, &hog_svc.attrs[10], report, sizeof(struct zmk_hid_consumer_report_body)); }; diff --git a/src/keymap.c b/src/keymap.c index df7e715b..569a2cc7 100644 --- a/src/keymap.c +++ b/src/keymap.c @@ -1,4 +1,6 @@ +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include static u32_t zmk_keymap_layer_state = 0; @@ -55,7 +57,10 @@ zmk_key zmk_keymap_keycode_from_position(u32_t row, u32_t column) { if ((zmk_keymap_layer_state & BIT(layer)) == BIT(layer) || layer == zmk_keymap_layer_default) { - zmk_key key = zmk_keymap[layer][(row * ZMK_MATRIX_COLS) + column]; + u8_t key_index = (row * ZMK_MATRIX_COLS) + column; + LOG_DBG("Getting key at index %d", key_index); + + zmk_key key = zmk_keymap[layer][key_index]; if (key == ZC_TRNS) { continue; diff --git a/src/usb_hid.c b/src/usb_hid.c index d7594a12..2862d566 100644 --- a/src/usb_hid.c +++ b/src/usb_hid.c @@ -14,14 +14,14 @@ static enum usb_dc_status_code usb_status; static struct device *hid_dev; -int zmk_usb_hid_send_report(const struct zmk_hid_report *report) +int zmk_usb_hid_send_report(const u8_t *report, size_t len) { if (usb_status == USB_DC_SUSPEND) { return usb_wakeup_request(); } - return hid_int_ep_write(hid_dev, (u8_t *)report, sizeof(struct zmk_hid_report), NULL); + return hid_int_ep_write(hid_dev, report, len, NULL); } void usb_hid_status_cb(enum usb_dc_status_code status, const u8_t *params)