1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
|
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <zmk/ble.h>
#include <zmk/endpoints.h>
#include <zmk/hid.h>
#include <zmk/usb.h>
#include <zmk/hog.h>
#include <zmk/event-manager.h>
#include <zmk/events/ble-active-profile-changed.h>
#include <zmk/events/usb-conn-state-changed.h>
#include <logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
enum endpoint {
ENDPOINT_USB,
ENDPOINT_BLE,
};
#define DEFAULT_ENDPOINT COND_CODE_1(IS_ENABLED(CONFIG_ZMK_BLE), (ENDPOINT_BLE), (ENDPOINT_USB))
static enum endpoint current_endpoint = DEFAULT_ENDPOINT;
static int send_keypad_report() {
struct zmk_hid_keypad_report *keypad_report = zmk_hid_get_keypad_report();
switch (current_endpoint) {
#if IS_ENABLED(CONFIG_ZMK_USB)
case ENDPOINT_USB: {
int err = zmk_usb_hid_send_report((u8_t *)keypad_report, sizeof(*keypad_report));
if (err) {
LOG_ERR("FAILED TO SEND OVER USB: %d", err);
}
return err;
}
#endif /* IS_ENABLED(CONFIG_ZMK_USB) */
#if IS_ENABLED(CONFIG_ZMK_BLE)
case ENDPOINT_BLE: {
int err = zmk_hog_send_keypad_report(&keypad_report->body);
if (err) {
LOG_ERR("FAILED TO SEND OVER HOG: %d", err);
}
return err;
}
#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */
default:
LOG_ERR("Unsupported endpoint %d", current_endpoint);
return -ENOTSUP;
}
}
static int send_consumer_report() {
struct zmk_hid_consumer_report *consumer_report = zmk_hid_get_consumer_report();
switch (current_endpoint) {
#if IS_ENABLED(CONFIG_ZMK_USB)
case ENDPOINT_USB: {
int err = zmk_usb_hid_send_report((u8_t *)consumer_report, sizeof(*consumer_report));
if (err) {
LOG_ERR("FAILED TO SEND OVER USB: %d", err);
}
return err;
}
#endif /* IS_ENABLED(CONFIG_ZMK_USB) */
#if IS_ENABLED(CONFIG_ZMK_BLE)
case ENDPOINT_BLE: {
int err = zmk_hog_send_consumer_report(&consumer_report->body);
if (err) {
LOG_ERR("FAILED TO SEND OVER HOG: %d", err);
}
return err;
}
#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */
default:
LOG_ERR("Unsupported endpoint %d", current_endpoint);
return -ENOTSUP;
}
}
int zmk_endpoints_send_report(u8_t usage_page) {
LOG_DBG("usage page 0x%02X", usage_page);
switch (usage_page) {
case USAGE_KEYPAD:
return send_keypad_report();
case USAGE_CONSUMER:
return send_consumer_report();
default:
LOG_ERR("Unsupported usage page %d", usage_page);
return -ENOTSUP;
}
}
static bool is_usb_ready() {
#if IS_ENABLED(CONFIG_ZMK_USB)
return zmk_usb_is_hid_ready();
#else
return false;
#endif
}
static bool is_ble_ready() {
#if IS_ENABLED(CONFIG_ZMK_BLE)
return zmk_ble_active_profile_is_connected();
#else
return false;
#endif
}
static enum endpoint get_selected_endpoint() {
if (is_ble_ready()) {
if (is_usb_ready()) {
LOG_DBG("Both endpoints are ready.");
// TODO: add user setting to control this
return ENDPOINT_USB;
}
LOG_DBG("Only BLE is ready.");
return ENDPOINT_BLE;
}
if (is_usb_ready()) {
LOG_DBG("Only USB is ready.");
return ENDPOINT_USB;
}
LOG_DBG("No endpoints are ready.");
return DEFAULT_ENDPOINT;
}
static void disconnect_current_endpoint() {
zmk_hid_keypad_clear();
zmk_hid_consumer_clear();
zmk_endpoints_send_report(USAGE_KEYPAD);
zmk_endpoints_send_report(USAGE_CONSUMER);
}
static int endpoint_listener(const struct zmk_event_header *eh) {
enum endpoint new_endpoint = get_selected_endpoint();
if (new_endpoint != current_endpoint) {
/* Cancel all current keypresses so keys don't stay held on the old endpoint. */
disconnect_current_endpoint();
current_endpoint = new_endpoint;
LOG_INF("Endpoint changed: %d", current_endpoint);
}
return 0;
}
ZMK_LISTENER(endpoint_listener, endpoint_listener);
#if IS_ENABLED(CONFIG_ZMK_USB)
ZMK_SUBSCRIPTION(endpoint_listener, usb_conn_state_changed);
#endif
#if IS_ENABLED(CONFIG_ZMK_BLE)
ZMK_SUBSCRIPTION(endpoint_listener, ble_active_profile_changed);
#endif
|