Skip to content

Commit e1cac73

Browse files
authored
Merge pull request #250 from TheRedfoox/main
Enables support for multiple keyboard devices
2 parents 6ef3d7b + 727007f commit e1cac73

File tree

7 files changed

+137
-8
lines changed

7 files changed

+137
-8
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
build
2+
3+
.idea/

src/handlers.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,17 @@ void config_enable_hotkey_handler(device_t *state, hid_keyboard_report_t *report
150150

151151
/* Function handles received keypresses from the other board */
152152
void handle_keyboard_uart_msg(uart_packet_t *packet, device_t *state) {
153-
queue_kbd_report((hid_keyboard_report_t *)packet->data, state);
153+
hid_keyboard_report_t *report = (hid_keyboard_report_t *)packet->data;
154+
155+
/* Update the keyboard state for the remote device (using MAX_DEVICES-1 as the index) */
156+
update_kbd_state(state, report, MAX_DEVICES-1);
157+
158+
/* Create a combined report from all device states */
159+
hid_keyboard_report_t combined_report;
160+
combine_kbd_states(state, &combined_report);
161+
162+
/* Queue the combined report */
163+
queue_kbd_report(&combined_report, state);
154164
state->last_activity[BOARD_ROLE] = time_us_64();
155165
}
156166

src/include/keyboard.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ int32_t extract_kbd_data(uint8_t *, int, uint8_t, hid_interface_t *, hid_keyboa
2727

2828
bool check_specific_hotkey(hotkey_combo_t, const hid_keyboard_report_t *);
2929

30+
/*==============================================================================
31+
* Keyboard State Management
32+
*==============================================================================*/
33+
void update_kbd_state(device_t *, hid_keyboard_report_t *, uint8_t);
34+
void combine_kbd_states(device_t *, hid_keyboard_report_t *);
35+
3036
/*==============================================================================
3137
* Keyboard Report Processing
3238
*==============================================================================*/

src/include/structs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ typedef struct {
100100
uint8_t active_output; // Currently selected output (0 = A, 1 = B)
101101
uint8_t board_role; // Which board are we running on? (0 = A, 1 = B, etc.)
102102

103+
// Track keyboard state for each device
104+
hid_keyboard_report_t kbd_states[MAX_DEVICES]; // Store keyboard state for each device
105+
uint8_t kbd_device_count; // Number of active keyboard devices
106+
103107
int16_t pointer_x; // Store and update the location of our mouse pointer
104108
int16_t pointer_y;
105109
int16_t mouse_buttons; // Store and update the state of mouse buttons

src/keyboard.c

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,55 @@ hotkey_combo_t *check_all_hotkeys(hid_keyboard_report_t *report, device_t *state
143143
return NULL;
144144
}
145145

146+
/* ==================================================== *
147+
* Keyboard State Management
148+
* ==================================================== */
149+
150+
/* Update the keyboard state for a specific device */
151+
void update_kbd_state(device_t *state, hid_keyboard_report_t *report, uint8_t device_idx) {
152+
/* Ensure device_idx is within bounds */
153+
if (device_idx >= MAX_DEVICES)
154+
return;
155+
156+
/* Ensure local devices never use the last slot, which is reserved for the remote device */
157+
if (device_idx == MAX_DEVICES-1 && device_idx != 0) {
158+
/* Use the previous slot instead */
159+
device_idx = MAX_DEVICES-2;
160+
}
161+
162+
/* Update the keyboard state for this device */
163+
memcpy(&state->kbd_states[device_idx], report, sizeof(hid_keyboard_report_t));
164+
165+
/* Ensure kbd_device_count is at least device_idx + 1 */
166+
if (state->kbd_device_count <= device_idx)
167+
state->kbd_device_count = device_idx + 1;
168+
}
169+
170+
/* Combine keyboard states from all devices into a single report */
171+
void combine_kbd_states(device_t *state, hid_keyboard_report_t *combined_report) {
172+
/* Initialize combined report */
173+
memset(combined_report, 0, sizeof(hid_keyboard_report_t));
174+
175+
/* Combine modifiers and keys from all devices */
176+
for (uint8_t i = 0; i < state->kbd_device_count; i++) {
177+
/* Combine modifiers with OR operation */
178+
combined_report->modifier |= state->kbd_states[i].modifier;
179+
180+
/* Add keys from this device to the combined report */
181+
for (uint8_t j = 0; j < KEYS_IN_USB_REPORT; j++) {
182+
if (state->kbd_states[i].keycode[j] != 0) {
183+
/* Find an empty slot in the combined report */
184+
for (uint8_t k = 0; k < KEYS_IN_USB_REPORT; k++) {
185+
if (combined_report->keycode[k] == 0) {
186+
combined_report->keycode[k] = state->kbd_states[i].keycode[j];
187+
break;
188+
}
189+
}
190+
}
191+
}
192+
}
193+
}
194+
146195
/* ==================================================== *
147196
* Keyboard Queue Section
148197
* ==================================================== */
@@ -184,16 +233,29 @@ void queue_kbd_report(hid_keyboard_report_t *report, device_t *state) {
184233

185234
void release_all_keys(device_t *state) {
186235
static hid_keyboard_report_t no_keys_pressed_report = {0, 0, {0}};
236+
237+
/* Clear keyboard states for all devices */
238+
for (uint8_t i = 0; i < state->kbd_device_count; i++) {
239+
memset(&state->kbd_states[i], 0, sizeof(hid_keyboard_report_t));
240+
}
241+
242+
/* Send a report with no keys pressed */
187243
queue_try_add(&state->kbd_queue, &no_keys_pressed_report);
188244
}
189245

190246
/* If keys need to go locally, queue packet to kbd queue, else send them through UART */
191247
void send_key(hid_keyboard_report_t *report, device_t *state) {
248+
/* Create a combined report from all device states */
249+
hid_keyboard_report_t combined_report;
250+
combine_kbd_states(state, &combined_report);
251+
192252
if (CURRENT_BOARD_IS_ACTIVE_OUTPUT) {
193-
queue_kbd_report(report, state);
253+
/* Queue the combined report */
254+
queue_kbd_report(&combined_report, state);
194255
state->last_activity[BOARD_ROLE] = time_us_64();
195256
} else {
196-
queue_packet((uint8_t *)report, KEYBOARD_REPORT_MSG, KBD_REPORT_LENGTH);
257+
/* Send the combined report to ensure all keys are included */
258+
queue_packet((uint8_t *)&combined_report, KEYBOARD_REPORT_MSG, KBD_REPORT_LENGTH);
197259
}
198260
}
199261

@@ -235,6 +297,9 @@ void process_keyboard_report(uint8_t *raw_report, int length, uint8_t itf, hid_i
235297

236298
extract_kbd_data(raw_report, length, itf, iface, &new_report);
237299

300+
/* Update the keyboard state for this device */
301+
update_kbd_state(state, &new_report, itf);
302+
238303
/* Check if any hotkey was pressed */
239304
hotkey = check_all_hotkeys(&new_report, state);
240305

@@ -259,6 +324,7 @@ void process_keyboard_report(uint8_t *raw_report, int length, uint8_t itf, hid_i
259324
void process_consumer_report(uint8_t *raw_report, int length, uint8_t itf, hid_interface_t *iface) {
260325
uint8_t new_report[CONSUMER_CONTROL_LENGTH] = {0};
261326
uint16_t *report_ptr = (uint16_t *)new_report;
327+
device_t *state = &global_state;
262328

263329
/* If consumer control is variable, read the values from cc_array and send as array. */
264330
if (iface->consumer.is_variable) {
@@ -276,12 +342,21 @@ void process_consumer_report(uint8_t *raw_report, int length, uint8_t itf, hid_i
276342
new_report[i] = raw_report[i + 1];
277343
}
278344

279-
send_consumer_control(new_report, &global_state);
345+
if (CURRENT_BOARD_IS_ACTIVE_OUTPUT) {
346+
send_consumer_control(new_report, state);
347+
} else {
348+
queue_packet((uint8_t *)new_report, CONSUMER_CONTROL_MSG, CONSUMER_CONTROL_LENGTH);
349+
}
280350
}
281351

282352
void process_system_report(uint8_t *raw_report, int length, uint8_t itf, hid_interface_t *iface) {
283353
uint16_t new_report = raw_report[1];
284354
uint8_t *report_ptr = (uint8_t *)&new_report;
355+
device_t *state = &global_state;
285356

286-
send_system_control(report_ptr, &global_state);
357+
if (CURRENT_BOARD_IS_ACTIVE_OUTPUT) {
358+
send_system_control(report_ptr, state);
359+
} else {
360+
queue_packet(report_ptr, SYSTEM_CONTROL_MSG, SYSTEM_CONTROL_LENGTH);
361+
}
287362
}

src/setup.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,10 @@ void initial_setup(device_t *state) {
231231
queue_init(&state->kbd_queue, sizeof(hid_keyboard_report_t), KBD_QUEUE_LENGTH);
232232
queue_init(&state->mouse_queue, sizeof(mouse_report_t), MOUSE_QUEUE_LENGTH);
233233

234+
/* Initialize keyboard states for all devices */
235+
memset(state->kbd_states, 0, sizeof(state->kbd_states));
236+
state->kbd_device_count = 0;
237+
234238
/* Initialize generic HID packet queue */
235239
queue_init(&state->hid_queue_out, sizeof(hid_generic_pkt_t), HID_QUEUE_LENGTH);
236240

src/usb.c

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,34 @@ void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t cons
188188
if (instance >= MAX_INTERFACES)
189189
return;
190190

191+
/* Calculate a device index that distinguishes between different devices
192+
while staying within the bounds of MAX_DEVICES.
193+
194+
Device index assignment:
195+
- 0: Primary keyboard (the one set in tuh_hid_mount_cb)
196+
- 1: Mouse devices
197+
- MAX_DEVICES-2: Secondary keyboards (e.g., wireless keyboard through unified dongle)
198+
- (dev_addr-1) % (MAX_DEVICES-1): Other devices
199+
200+
Note: Slot MAX_DEVICES-1 is reserved for the remote device (used in handle_keyboard_uart_msg) */
201+
uint8_t device_idx;
202+
203+
if (itf_protocol == HID_ITF_PROTOCOL_KEYBOARD) {
204+
if (dev_addr == global_state.kbd_dev_addr && instance == global_state.kbd_instance) {
205+
/* Primary keyboard */
206+
device_idx = 0;
207+
} else {
208+
/* Secondary keyboard (e.g., wireless keyboard through unified dongle) */
209+
device_idx = (MAX_DEVICES - 2);
210+
}
211+
} else if (itf_protocol == HID_ITF_PROTOCOL_MOUSE) {
212+
/* Mouse devices */
213+
device_idx = 1;
214+
} else {
215+
/* Other devices */
216+
device_idx = (dev_addr - 1) % (MAX_DEVICES - 1);
217+
}
218+
191219
if (iface->uses_report_id || itf_protocol == HID_ITF_PROTOCOL_NONE) {
192220
uint8_t report_id = 0;
193221

@@ -198,14 +226,14 @@ void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t cons
198226
process_report_f receiver = iface->report_handler[report_id];
199227

200228
if (receiver != NULL)
201-
receiver((uint8_t *)report, len, itf_protocol, iface);
229+
receiver((uint8_t *)report, len, device_idx, iface);
202230
}
203231
}
204232
else if (itf_protocol == HID_ITF_PROTOCOL_KEYBOARD) {
205-
process_keyboard_report((uint8_t *)report, len, itf_protocol, iface);
233+
process_keyboard_report((uint8_t *)report, len, device_idx, iface);
206234
}
207235
else if (itf_protocol == HID_ITF_PROTOCOL_MOUSE) {
208-
process_mouse_report((uint8_t *)report, len, itf_protocol, iface);
236+
process_mouse_report((uint8_t *)report, len, device_idx, iface);
209237
}
210238

211239
/* Continue requesting reports */

0 commit comments

Comments
 (0)