summaryrefslogtreecommitdiffstats
path: root/keyboards/nullbitsco/common/remote_kb.c
blob: 89cbf9a92a9d26ae2acaf242de673a56e5e610f1 (plain)
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
168
169
170
171
172
173
174
175
176
177
178
/* Copyright 2021 Jay Greco
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
Remote keyboard is an experimental feature that allows for connecting another
keyboard, macropad, numpad, or accessory without requiring an additional USB connection.
The "remote keyboard" forwards its keystrokes using UART serial over TRRS. Dynamic VUSB 
detect allows the keyboard automatically switch to host or remote mode depending on
which is connected to the USB port.

Possible functionality includes the ability to send data from the host to the remote using
a reverse link, allowing for LED sync, configuration, and more data sharing between devices.
This will require a new communication protocol, as the current one is limited.
*/

#include "remote_kb.h"
#include "uart.h"

uint8_t
 msg[UART_MSG_LEN],
 msg_idx = 0;

bool
 is_host = true;

// Private functions

static bool vbus_detect(void) {
  #if defined(__AVR_ATmega32U4__)
    //returns true if VBUS is present, false otherwise.
    USBCON |= (1 << OTGPADE); //enables VBUS pad
    _delay_us(10);
    return (USBSTA & (1<<VBUS));  //checks state of VBUS
  #else
    #error vbus_detect is not implemented for this architecure!
  #endif
}

static uint8_t chksum8(const unsigned char *buf, size_t len) {
  unsigned int sum;
  for (sum = 0 ; len != 0 ; len--)
    sum += *(buf++);
  return (uint8_t)sum;
}

static void send_msg(uint16_t keycode, bool pressed) {
  msg[IDX_PREAMBLE] = UART_PREAMBLE;
  msg[IDX_KCLSB] = (keycode & 0xFF);
  msg[IDX_KCMSB] = (keycode >> 8) & 0xFF;
  msg[IDX_PRESSED] = pressed;
  msg[IDX_CHECKSUM] = chksum8(msg, UART_MSG_LEN-1);

  for (int i=0; i<UART_MSG_LEN; i++) {
    uart_putchar(msg[i]);
  }
}

static void print_message_buffer(void) {
  for (int i=0; i<UART_MSG_LEN; i++) {
    dprintf("msg[%u]: %u\n", i, msg[i]);
  }
}

static void process_uart(void) {
  uint8_t chksum = chksum8(msg, UART_MSG_LEN-1);
  if (msg[IDX_PREAMBLE] != UART_PREAMBLE || msg[IDX_CHECKSUM] != chksum) {
     dprintf("UART checksum mismatch!\n");
     print_message_buffer();
     dprintf("calc checksum: %u\n", chksum);
  } else {
    uint16_t keycode = (uint16_t)msg[IDX_KCLSB] | ((uint16_t)msg[IDX_KCMSB] << 8);
    bool pressed = (bool)msg[IDX_PRESSED];
    if (IS_RM_KC(keycode)) {
      keyrecord_t record;
      record.event.pressed = pressed;
      if (pressed) dprintf("Remote macro: press [%u]\n", keycode);
      else dprintf("Remote macro: release [%u]\n", keycode);
      process_record_user(keycode, &record);
    } else {
      if (pressed) {
        dprintf("Remote: press [%u]\n", keycode);
        register_code(keycode);
    } else {
        dprintf("Remote: release [%u]\n", keycode);
        unregister_code(keycode);
      }
    }
  }
}

static void get_msg(void) {
  while (uart_available()) {
    msg[msg_idx] = uart_getchar();
    dprintf("idx: %u, recv: %u\n", msg_idx, msg[msg_idx]);
    if (msg_idx == 0 && (msg[msg_idx] != UART_PREAMBLE)) {
      dprintf("Byte sync error!\n");
      msg_idx = 0;
    } else if (msg_idx == (UART_MSG_LEN-1)) {
      process_uart();
      msg_idx = 0;
    } else {
      msg_idx++;
    }
  }
}

static void handle_host_incoming(void) {
  get_msg();
}

static void handle_host_outgoing(void) {
  // for future reverse link use
}

static void handle_remote_incoming(void) {
  // for future reverse link use
}

static void handle_remote_outgoing(uint16_t keycode, keyrecord_t *record) {
  if (IS_HID_KC(keycode) || IS_RM_KC(keycode)) {
    dprintf("Remote: send [%u]\n", keycode);
    send_msg(keycode, record->event.pressed);
  }
}

// Public functions

void matrix_init_remote_kb(void) {
  uart_init(SERIAL_UART_BAUD);
  is_host = vbus_detect();
}

void process_record_remote_kb(uint16_t keycode, keyrecord_t *record) {
  #if defined (KEYBOARD_HOST)
  handle_host_outgoing();

  #elif defined(KEYBOARD_REMOTE)
  handle_remote_outgoing(keycode, record);

  #else //auto check with VBUS
  if (is_host) {
    handle_host_outgoing();
  }
  else {
    handle_remote_outgoing(keycode, record);
  }
  #endif
}

void matrix_scan_remote_kb(void) {
  #if defined(KEYBOARD_HOST)
  handle_host_incoming();

  #elif defined (KEYBOARD_REMOTE)
  handle_remote_incoming();

  #else //auto check with VBUS
  if (is_host) {
    handle_host_incoming();
  }
  else {
    handle_remote_incoming();
  }
  #endif
}