remote_kb.c (4968B)
1 /* Copyright 2021 Jay Greco 2 * 3 * This program is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published by 5 * the Free Software Foundation, either version 2 of the License, or 6 * (at your option) any later version. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16 17 /* 18 Remote keyboard is an experimental feature that allows for connecting another 19 keyboard, macropad, numpad, or accessory without requiring an additional USB connection. 20 The "remote keyboard" forwards its keystrokes using UART serial over TRRS. Dynamic VUSB 21 detect allows the keyboard automatically switch to host or remote mode depending on 22 which is connected to the USB port. 23 24 Possible functionality includes the ability to send data from the host to the remote using 25 a reverse link, allowing for LED sync, configuration, and more data sharing between devices. 26 This will require a new communication protocol, as the current one is limited. 27 */ 28 29 #include "remote_kb.h" 30 #include "quantum.h" 31 #include "uart.h" 32 #include "wait.h" 33 #include "debug.h" 34 35 uint8_t 36 msg[UART_MSG_LEN], 37 msg_idx = 0; 38 39 bool 40 is_host = true; 41 42 // Private functions 43 44 static bool vbus_detect(void) { 45 #if defined(__AVR_ATmega32U4__) 46 //returns true if VBUS is present, false otherwise. 47 USBCON |= (1 << OTGPADE); //enables VBUS pad 48 _delay_us(10); 49 return (USBSTA & (1<<VBUS)); //checks state of VBUS 50 #else 51 #error vbus_detect is not implemented for this architecure! 52 #endif 53 } 54 55 static uint8_t chksum8(const unsigned char *buf, size_t len) { 56 unsigned int sum; 57 for (sum = 0 ; len != 0 ; len--) 58 sum += *(buf++); 59 return (uint8_t)sum; 60 } 61 62 static void send_msg(uint16_t keycode, bool pressed) { 63 msg[IDX_PREAMBLE] = UART_PREAMBLE; 64 msg[IDX_KCLSB] = (keycode & 0xFF); 65 msg[IDX_KCMSB] = (keycode >> 8) & 0xFF; 66 msg[IDX_PRESSED] = pressed; 67 msg[IDX_CHECKSUM] = chksum8(msg, UART_MSG_LEN-1); 68 uart_transmit(msg, UART_MSG_LEN); 69 } 70 71 static inline void print_message_buffer(void) { 72 for (int i=0; i<UART_MSG_LEN; i++) { 73 dprintf("msg[%u]: 0x%02X\n", i, msg[i]); 74 } 75 } 76 77 static void process_uart(void) { 78 uint8_t chksum = chksum8(msg, UART_MSG_LEN-1); 79 if (msg[IDX_PREAMBLE] != UART_PREAMBLE || msg[IDX_CHECKSUM] != chksum) { 80 dprintf("UART checksum mismatch!\n"); 81 print_message_buffer(); 82 dprintf("calc checksum: 0x%02X\n", chksum); 83 } else { 84 uint16_t keycode = (uint16_t)msg[IDX_KCLSB] | ((uint16_t)msg[IDX_KCMSB] << 8); 85 bool pressed = (bool)msg[IDX_PRESSED]; 86 if (IS_RM_KC(keycode)) { 87 keyrecord_t record; 88 record.event.pressed = pressed; 89 if (pressed) dprintf("Remote macro: press [%u]\n", keycode); 90 else dprintf("Remote macro: release [%u]\n", keycode); 91 process_record_user(keycode, &record); 92 } else { 93 if (pressed) { 94 dprintf("Remote: press [%u]\n", keycode); 95 register_code(keycode); 96 } else { 97 dprintf("Remote: release [%u]\n", keycode); 98 unregister_code(keycode); 99 } 100 } 101 } 102 } 103 104 static void get_msg(void) { 105 while (uart_available()) { 106 msg[msg_idx] = uart_read(); 107 dprintf("idx: %u, recv: 0x%002X\n", msg_idx, msg[msg_idx]); 108 if (msg_idx == 0 && (msg[msg_idx] != UART_PREAMBLE)) { 109 dprintf("Byte sync error!\n"); 110 msg_idx = 0; 111 } else if (msg_idx == (UART_MSG_LEN-1)) { 112 process_uart(); 113 msg_idx = 0; 114 break; 115 } else { 116 msg_idx++; 117 } 118 } 119 } 120 121 static void handle_host_incoming(void) { 122 get_msg(); 123 } 124 125 static void handle_host_outgoing(void) { 126 // for future reverse link use 127 } 128 129 static void handle_remote_incoming(void) { 130 // for future reverse link use 131 } 132 133 static void handle_remote_outgoing(uint16_t keycode, keyrecord_t *record) { 134 if (IS_HID_KC(keycode) || IS_RM_KC(keycode)) { 135 dprintf("Remote: send [%u]\n", keycode); 136 send_msg(keycode, record->event.pressed); 137 } 138 } 139 140 // Public functions 141 142 void matrix_init_remote_kb(void) { 143 uart_init(SERIAL_UART_BAUD); 144 is_host = vbus_detect(); 145 } 146 147 void process_record_remote_kb(uint16_t keycode, keyrecord_t *record) { 148 #if defined (KEYBOARD_HOST) 149 handle_host_outgoing(); 150 151 #elif defined(KEYBOARD_REMOTE) 152 handle_remote_outgoing(keycode, record); 153 154 #else //auto check with VBUS 155 if (is_host) { 156 handle_host_outgoing(); 157 } 158 else { 159 handle_remote_outgoing(keycode, record); 160 } 161 #endif 162 } 163 164 void matrix_scan_remote_kb(void) { 165 #if defined(KEYBOARD_HOST) 166 handle_host_incoming(); 167 168 #elif defined (KEYBOARD_REMOTE) 169 handle_remote_incoming(); 170 171 #else //auto check with VBUS 172 if (is_host) { 173 handle_host_incoming(); 174 } 175 else { 176 handle_remote_incoming(); 177 } 178 #endif 179 }