qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

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 }