diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..e70f291 --- /dev/null +++ b/.clang-format @@ -0,0 +1,23 @@ +BasedOnStyle: LLVM +BinPackParameters: 'false' +BinPackArguments: 'false' +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: 'true' +AlignConsecutiveAssignments: 'true' +AlignConsecutiveDeclarations: 'false' +AlignEscapedNewlines: Left +AlignOperands: 'true' +AlignTrailingComments: 'true' +AllowAllArgumentsOnNextLine: 'true' +AllowShortFunctionsOnASingleLine: 'false' +BreakBeforeBinaryOperators: 'All' +ColumnLimit: '110' +IndentWidth: '4' +IndentCaseLabels: 'true' +IndentWrappedFunctionNames: 'false' +KeepEmptyLinesAtTheStartOfBlocks: 'true' +MaxEmptyLinesToKeep: '2' +PointerAlignment: Right +ReflowComments: 'true' +Standard: Cpp11 +UseTab: Never diff --git a/binaries/board_A.uf2 b/binaries/board_A.uf2 index fa09221..234d2ca 100644 Binary files a/binaries/board_A.uf2 and b/binaries/board_A.uf2 differ diff --git a/binaries/board_B.uf2 b/binaries/board_B.uf2 index 546f5fc..3cd3995 100644 Binary files a/binaries/board_B.uf2 and b/binaries/board_B.uf2 differ diff --git a/src/handlers.c b/src/handlers.c index 55db6da..3566688 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -231,6 +231,11 @@ void handle_output_config_msg(uart_packet_t *packet, device_t *state) { save_config(state); } +/* Process consumer control keyboard message. Send immediately, w/o queing */ +void handle_consumer_control_msg(uart_packet_t *packet, device_t *state) { + tud_hid_n_report(0, REPORT_ID_CONSUMER, &packet->data[0], CONSUMER_CONTROL_LENGTH); +} + /**==================================================== * * ============== Output Switch Routines ============ * * ==================================================== */ diff --git a/src/hid_parser.h b/src/hid_parser.h index f0d62be..45324e9 100644 --- a/src/hid_parser.h +++ b/src/hid_parser.h @@ -74,6 +74,14 @@ typedef struct { bool uses_report_id; } mouse_t; +/* Defines information about HID report format for the keyboard. */ +typedef struct { + uint8_t keyboard_report_id; + uint8_t consumer_report_id; + uint8_t system_report_id; + uint8_t protocol; +} keyboard_t; + /* For each element type we're interested in there is an entry in an array of these, defining its usage and in case matched, where to store the data. */ diff --git a/src/keyboard.c b/src/keyboard.c index a234fa0..47def28 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -181,6 +181,16 @@ void send_key(hid_keyboard_report_t *report, device_t *state) { } } +/* Decide if consumer control reports go local or to the other board */ +void send_consumer_control(uint8_t *raw_report, device_t *state) { + if (CURRENT_BOARD_IS_ACTIVE_OUTPUT) { + tud_hid_n_report(0, REPORT_ID_CONSUMER, raw_report, CONSUMER_CONTROL_LENGTH); + state->last_activity[BOARD_ROLE] = time_us_64(); + } else { + send_packet((uint8_t *)raw_report, CONSUMER_CONTROL_MSG, CONSUMER_CONTROL_LENGTH); + } +} + /* ==================================================== * * Parse and interpret the keys pressed on the keyboard * ==================================================== */ @@ -218,3 +228,20 @@ void process_keyboard_report(uint8_t *raw_report, int length, device_t *state) { /* This method will decide if the key gets queued locally or sent through UART */ send_key(keyboard_report, state); } + +void process_consumer_report(uint8_t *raw_report, int length, device_t *state) { + uint8_t new_report[CONSUMER_CONTROL_LENGTH] = {0}; + + /* We expect length not to be zero or bail out */ + if (!length) + return; + + /* Consumer control report ID rewrite and forward */ + if (raw_report[0] && raw_report[0] == global_state.kbd_dev.consumer_report_id) { + for (int i = 0; i < length - 1 || i < CONSUMER_CONTROL_LENGTH; i++) { + new_report[i] = raw_report[i + 1]; + } + + send_consumer_control(new_report, &global_state); + } +} \ No newline at end of file diff --git a/src/main.h b/src/main.h index fd07b04..dbd9c30 100644 --- a/src/main.h +++ b/src/main.h @@ -109,6 +109,7 @@ enum packet_type_e { SWAP_OUTPUTS_MSG = 12, HEARTBEAT_MSG = 13, OUTPUT_CONFIG_MSG = 14, + CONSUMER_CONTROL_MSG = 15, }; /* @@ -142,9 +143,10 @@ typedef struct { #define KBD_QUEUE_LENGTH 128 #define MOUSE_QUEUE_LENGTH 2048 -#define KEYS_IN_USB_REPORT 6 -#define KBD_REPORT_LENGTH 8 -#define MOUSE_REPORT_LENGTH 7 +#define KEYS_IN_USB_REPORT 6 +#define KBD_REPORT_LENGTH 8 +#define MOUSE_REPORT_LENGTH 7 +#define CONSUMER_CONTROL_LENGTH 4 /********* Screen **********/ #define MIN_SCREEN_COORD 0 @@ -260,6 +262,7 @@ typedef struct { config_t config; // Device configuration, loaded from flash or defaults used mouse_t mouse_dev; // Mouse device specifics, e.g. stores locations for keys in report + keyboard_t kbd_dev; // Keyboard device specifics, like report IDs queue_t kbd_queue; // Queue that stores keyboard reports queue_t mouse_queue; // Queue that stores mouse reports @@ -288,11 +291,13 @@ void core1_main(void); /********* Keyboard **********/ bool check_specific_hotkey(hotkey_combo_t, const hid_keyboard_report_t *); void process_keyboard_report(uint8_t *, int, device_t *); +void process_consumer_report(uint8_t *, int, device_t *); void release_all_keys(device_t *); void queue_kbd_report(hid_keyboard_report_t *, device_t *); void process_kbd_queue_task(device_t *); void send_key(hid_keyboard_report_t *, device_t *); bool key_in_report(uint8_t, const hid_keyboard_report_t *); +void send_consumer_control(uint8_t *, device_t *); /********* Mouse **********/ bool tud_mouse_report(uint8_t mode, uint8_t buttons, int16_t x, int16_t y, int8_t wheel); @@ -355,6 +360,7 @@ void handle_flash_led_msg(uart_packet_t *, device_t *); void handle_fw_upgrade_msg(uart_packet_t *, device_t *); void handle_wipe_config_msg(uart_packet_t *, device_t *); void handle_screensaver_msg(uart_packet_t *, device_t *); +void handle_consumer_control_msg(uart_packet_t *, device_t *); void switch_output(device_t *, uint8_t); diff --git a/src/mouse.c b/src/mouse.c index 75ae968..07b8318 100644 --- a/src/mouse.c +++ b/src/mouse.c @@ -139,9 +139,9 @@ void switch_desktop(device_t *state, output_t *output, int new_index, int direct switch (output->os) { case MACOS: - /* Once doesn't seem reliable enough, do it twice */ - output_mouse_report(&move_relative_one, state); - output_mouse_report(&move_relative_one, state); + /* Once isn't reliable enough, but repeating it does the trick */ + for (int move_cnt=0; move_cnt<5; move_cnt++) + output_mouse_report(&move_relative_one, state); break; case WINDOWS: diff --git a/src/uart.c b/src/uart.c index e2dc8fb..3f7b7ba 100644 --- a/src/uart.c +++ b/src/uart.c @@ -57,6 +57,7 @@ const uart_handler_t uart_handler[] = { {.type = SCREENSAVER_MSG, .handler = handle_screensaver_msg}, {.type = WIPE_CONFIG_MSG, .handler = handle_wipe_config_msg}, {.type = OUTPUT_CONFIG_MSG, .handler = handle_output_config_msg}, + {.type = CONSUMER_CONTROL_MSG, .handler = handle_consumer_control_msg}, }; void process_packet(uart_packet_t *packet, device_t *state) { diff --git a/src/usb.c b/src/usb.c index 834dc9e..1027198 100644 --- a/src/usb.c +++ b/src/usb.c @@ -87,6 +87,7 @@ void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) { switch (itf_protocol) { case HID_ITF_PROTOCOL_KEYBOARD: global_state.keyboard_connected = false; + memset(&global_state.kbd_dev, 0, sizeof(global_state.kbd_dev)); break; case HID_ITF_PROTOCOL_MOUSE: @@ -99,7 +100,9 @@ void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) { } void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_report, uint16_t desc_len) { - uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); + uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); + tuh_hid_report_info_t report_info[MAX_REPORT_ITEMS] = {0}; + tuh_hid_report_info_t *info; switch (itf_protocol) { case HID_ITF_PROTOCOL_KEYBOARD: @@ -120,13 +123,25 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_re at least we get all the information we need (looking at you, mouse wheel) */ if (tuh_hid_get_protocol(dev_addr, instance) == HID_PROTOCOL_BOOT) { tuh_hid_set_protocol(dev_addr, instance, HID_PROTOCOL_REPORT); - } + } global_state.mouse_dev.protocol = tuh_hid_get_protocol(dev_addr, instance); parse_report_descriptor(&global_state.mouse_dev, MAX_REPORTS, desc_report, desc_len); global_state.mouse_connected = true; break; + + case HID_ITF_PROTOCOL_NONE: + uint8_t num_parsed + = tuh_hid_parse_report_descriptor(&report_info[0], MAX_REPORT_ITEMS, desc_report, desc_len); + + for (int report_num = 0; report_num < num_parsed; report_num++) { + info = &report_info[report_num]; + + if (info->usage == HID_USAGE_CONSUMER_CONTROL && info->usage_page == HID_USAGE_PAGE_CONSUMER) + global_state.kbd_dev.consumer_report_id = info->report_id; + } + break; } /* Flash local led to indicate a device was connected */ blink_led(&global_state); @@ -134,7 +149,7 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_re /* Also signal the other board to flash LED, to enable easy verification if serial works */ send_value(ENABLE, FLASH_LED_MSG); - /* Kick off the report querying */ + /* Kick off the report querying */ tuh_hid_receive_report(dev_addr, instance); } @@ -150,6 +165,10 @@ void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t cons case HID_ITF_PROTOCOL_MOUSE: process_mouse_report((uint8_t *)report, len, &global_state); break; + + case HID_ITF_PROTOCOL_NONE: + process_consumer_report((uint8_t *)report, len, &global_state); + break; } /* Continue requesting reports */ diff --git a/src/usb_descriptors.c b/src/usb_descriptors.c index ba2addc..7baac05 100644 --- a/src/usb_descriptors.c +++ b/src/usb_descriptors.c @@ -62,7 +62,9 @@ uint8_t const *tud_descriptor_device_cb(void) { // Relative mouse is used to overcome limitations of multiple desktops on MacOS and Windows uint8_t const desc_hid_report[] = {TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID_KEYBOARD)), - TUD_HID_REPORT_DESC_ABSMOUSE(HID_REPORT_ID(REPORT_ID_MOUSE))}; + TUD_HID_REPORT_DESC_ABSMOUSE(HID_REPORT_ID(REPORT_ID_MOUSE)), + TUD_HID_REPORT_DESC_CONSUMER_CTRL(HID_REPORT_ID(REPORT_ID_CONSUMER)) + }; uint8_t const desc_hid_report_relmouse[] = {TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(REPORT_ID_RELMOUSE))}; diff --git a/src/usb_descriptors.h b/src/usb_descriptors.h index 197373b..1571e7f 100644 --- a/src/usb_descriptors.h +++ b/src/usb_descriptors.h @@ -29,7 +29,8 @@ enum { REPORT_ID_KEYBOARD = 1, REPORT_ID_MOUSE, - REPORT_ID_COUNT + REPORT_ID_COUNT, + REPORT_ID_CONSUMER }; enum @@ -86,4 +87,20 @@ HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\ HID_COLLECTION_END , \ HID_COLLECTION_END \ +// Consumer Control Report Descriptor Template +#define TUD_HID_REPORT_DESC_CONSUMER_CTRL(...) \ + HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ) ,\ + HID_USAGE ( HID_USAGE_CONSUMER_CONTROL ) ,\ + HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\ + /* Report ID if any */\ + __VA_ARGS__ \ + HID_LOGICAL_MIN ( 0x01 ) ,\ + HID_LOGICAL_MAX_N( 0x0FFF, 2 ) ,\ + HID_USAGE_MIN ( 0x01 ) ,\ + HID_USAGE_MAX_N ( 0x0FFF, 2 ) ,\ + HID_REPORT_SIZE ( 16 ) ,\ + HID_REPORT_COUNT ( 2 ) ,\ + HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\ + HID_COLLECTION_END \ + #endif /* USB_DESCRIPTORS_H_ */