split.c (7034B)
1#include "split.h" 2#include "util.h" 3#include "keymat.h" 4#include "uart_rx.pio.h" 5#include "uart_tx.pio.h" 6 7#include "device/usbd.h" 8#include "hardware/pio.h" 9#include "hardware/irq.h" 10#include "hardware/gpio.h" 11#include "hardware/regs/intctrl.h" 12#include "hardware/clocks.h" 13 14#include <stdint.h> 15 16#define UART_AWAIT_TIMEOUT_US 1000 17#define UART_RECV_TIMEOUT_US 200 18#define UART_SEND_TIMEOUT_US 200 19 20#define UART_BAUD 115200 21 22#define CMD_START 0x8a 23 24#if SPLIT_SIDE == LEFT 25#define UART_TX_PIN 0 26#define UART_RX_PIN 1 27#elif SPLIT_SIDE == RIGHT 28#define UART_TX_PIN 1 29#define UART_RX_PIN 0 30#else 31#error "SPLIT_SIDE not set" 32#endif 33 34enum { 35 CMD_SCAN_KEYMAT_REQ = 0x80, 36 CMD_SCAN_KEYMAT_RESP, 37 CMD_SLAVE_WARN 38}; 39 40static uint uart_tx_sm; 41static uint uart_tx_sm_offset; 42 43static uint uart_rx_sm; 44static uint uart_rx_sm_offset; 45 46static uint32_t halfmat; 47 48int split_role; 49 50static void 51irq_rx(void) 52{ 53 if (pio_interrupt_get(pio0, 0)) { 54 DEBUG(LOG_SPLIT, "UART RX ERR"); 55 pio_interrupt_clear(pio0, 0); 56 } 57} 58 59static void 60uart_tx_sm_init(void) 61{ 62 pio_sm_config config; 63 64 uart_tx_sm = claim_unused_sm(pio0); 65 uart_tx_sm_offset = pio_add_program(pio0, &uart_tx_program); 66 67 config = uart_tx_program_get_default_config(uart_tx_sm_offset); 68 sm_config_set_out_shift(&config, true, false, 32); 69 sm_config_set_out_pins(&config, UART_TX_PIN, 1); 70 sm_config_set_sideset_pins(&config, UART_TX_PIN); 71 sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_TX); 72 sm_config_set_clkdiv(&config, 73 ((float) clock_get_hz(clk_sys)) / (8 * UART_BAUD)); 74 75 pio_sm_init(pio0, uart_tx_sm, uart_tx_sm_offset, &config); 76 pio_sm_set_enabled(pio0, uart_tx_sm, false); 77} 78 79static void 80uart_rx_sm_init(void) 81{ 82 pio_sm_config config; 83 84 uart_rx_sm = claim_unused_sm(pio0); 85 uart_rx_sm_offset = pio_add_program(pio0, &uart_rx_program); 86 87 config = uart_rx_program_get_default_config(uart_rx_sm_offset); 88 sm_config_set_in_pins(&config, UART_RX_PIN); 89 sm_config_set_jmp_pin(&config, UART_RX_PIN); 90 sm_config_set_in_shift(&config, true, false, 32); 91 sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_RX); 92 sm_config_set_clkdiv(&config, 93 ((float) clock_get_hz(clk_sys)) / (8 * UART_BAUD)); 94 95 pio_sm_init(pio0, uart_rx_sm, uart_rx_sm_offset, &config); 96 pio_sm_set_enabled(pio0, uart_rx_sm, false); 97} 98 99static void 100uart_full_init(void) 101{ 102 pio_gpio_init(pio0, UART_RX_PIN); 103 pio_sm_set_pins_with_mask(pio0, uart_rx_sm, 1U, 1U << UART_RX_PIN); 104 pio_sm_set_consecutive_pindirs(pio0, uart_rx_sm, UART_RX_PIN, 1, false); 105 106 pio_gpio_init(pio0, UART_TX_PIN); 107 gpio_set_slew_rate(UART_TX_PIN, GPIO_SLEW_RATE_FAST); 108 gpio_set_drive_strength(UART_TX_PIN, GPIO_DRIVE_STRENGTH_4MA); 109 pio_sm_set_pins_with_mask(pio0, uart_tx_sm, 1U, 1U << UART_TX_PIN); 110 pio_sm_set_consecutive_pindirs(pio0, uart_tx_sm, UART_TX_PIN, 1, true); 111 112 uart_rx_sm_init(); 113 uart_tx_sm_init(); 114 115 pio_set_irq0_source_enabled(pio0, 116 pis_sm0_rx_fifo_not_empty + uart_rx_sm, false); 117 pio_set_irq0_source_enabled(pio0, 118 pis_sm0_tx_fifo_not_full + uart_tx_sm, false); 119 pio_set_irq0_source_enabled(pio0, pis_interrupt0, true); 120 121 irq_set_priority(PIO0_IRQ_0, PICO_HIGHEST_IRQ_PRIORITY); 122 irq_set_exclusive_handler(PIO0_IRQ_0, irq_rx); 123 irq_set_enabled(PIO0_IRQ_0, true); 124 125 pio_sm_set_enabled(pio0, uart_rx_sm, true); 126 pio_sm_set_enabled(pio0, uart_tx_sm, true); 127} 128 129static bool 130uart_await_rx(uint64_t timeout_us) 131{ 132 uint64_t start_us; 133 bool empty; 134 135 if (!pio_sm_is_rx_fifo_empty(pio0, uart_rx_sm)) 136 return true; 137 138 start_us = board_micros(); 139 do { 140 tud_task(); 141 empty = pio_sm_is_rx_fifo_empty(pio0, uart_rx_sm); 142 } while (empty && board_micros() < start_us + timeout_us); 143 144 return !empty; 145} 146 147static bool 148uart_await_tx(uint64_t timeout_us) 149{ 150 uint64_t start_us; 151 bool full; 152 153 if (!pio_sm_is_tx_fifo_full(pio0, uart_tx_sm)) 154 return true; 155 156 start_us = board_micros(); 157 do { 158 tud_task(); 159 full = pio_sm_is_tx_fifo_full(pio0, uart_tx_sm); 160 } while (full && board_micros() < start_us + timeout_us); 161 162 return !full; 163} 164 165static uint8_t 166uart_rx_byte(void) 167{ 168 return *(uint8_t*)((uintptr_t)&pio0->rxf[uart_rx_sm] + 3); 169} 170 171static void 172uart_tx_byte(uint8_t c) 173{ 174 pio_sm_put(pio0, uart_tx_sm, c); 175} 176 177static uint 178uart_recv(uint8_t *data, uint len) 179{ 180 uint recv; 181 182 for (recv = 0; recv < len; recv++) { 183 if (!uart_await_rx(UART_RECV_TIMEOUT_US)) 184 break; 185 *data++ = uart_rx_byte(); 186 } 187 188 return recv; 189} 190 191static uint 192uart_recv_str(uint8_t *data, uint max) 193{ 194 uint recv; 195 196 for (recv = 0; recv < max; recv++) { 197 if (!uart_await_rx(UART_RECV_TIMEOUT_US)) 198 break; 199 *data++ = uart_rx_byte(); 200 if (!*data) break; 201 } 202 203 return recv; 204} 205 206static uint 207uart_send(const uint8_t *data, uint len) 208{ 209 uint sent; 210 211 for (sent = 0; sent < len; sent++) { 212 if (!uart_await_tx(UART_SEND_TIMEOUT_US)) 213 break; 214 uart_tx_byte(*data++); 215 } 216 217 return sent; 218} 219 220static int 221handle_cmd(void) 222{ 223 static uint8_t msgbuf[128]; 224 uint8_t cmd, start; 225 uint len; 226 227 start = uart_rx_byte(); 228 if (start != CMD_START) { 229 WARN(LOG_SPLIT, "Got bad start byte: %02u", start); 230 return -1; 231 } 232 233 if (!uart_recv(&cmd, 1)) { 234 WARN(LOG_SPLIT, "Got start byte without command"); 235 return -1; 236 } 237 238 switch (cmd) { 239 case CMD_SCAN_KEYMAT_REQ: 240 if (split_role != SLAVE) { 241 WARN(LOG_SPLIT, "Got SCAN_KEYMAT_REQ as master"); 242 return -1; 243 } 244 break; 245 case CMD_SCAN_KEYMAT_RESP: 246 if (split_role != MASTER) { 247 WARN(LOG_SPLIT, "Got SCAN_KEYMAT_RESP as slave"); 248 return -1; 249 } 250 if (uart_recv((uint8_t *) &halfmat, 4) != 4) { 251 WARN(LOG_SPLIT, "Incomplete matrix received"); 252 return -1; 253 } 254 break; 255 case CMD_SLAVE_WARN: 256 if (split_role != MASTER) { 257 WARN(LOG_SPLIT, "Got SLAVE_WARN as slave"); 258 return -1; 259 } 260 len = uart_recv_str(msgbuf, sizeof(msgbuf)-1); 261 msgbuf[len] = '\0'; 262 WARN(LOG_SPLIT, "SLAVE: %s\n", msgbuf); 263 break; 264 default: 265 WARN(LOG_SPLIT, "Unknown uart cmd: %i", cmd); 266 return -1; 267 } 268 269 return cmd; 270} 271 272static bool 273send_cmd(uint8_t cmd) 274{ 275 uint8_t buf[2]; 276 277 buf[0] = CMD_START; 278 buf[1] = cmd; 279 280 return uart_send(buf, 2) == 2; 281} 282 283void 284split_init(void) 285{ 286 uart_full_init(); 287#ifdef SPLIT_ROLE 288 split_role = SPLIT_ROLE; 289#else 290 split_role = SLAVE; 291#endif 292} 293 294static void 295split_task_master(void) 296{ 297 int cmd; 298 299 keymat_scan(); 300 301 if (uart_await_rx(UART_AWAIT_TIMEOUT_US)) { 302 if ((cmd = handle_cmd()) == CMD_SCAN_KEYMAT_RESP) { 303 keymat_decode_half(SPLIT_OPP(SPLIT_SIDE), halfmat); 304 } else { 305 WARN(LOG_SPLIT, "Got unexpected command %02x", cmd); 306 } 307 } 308 309 keymat_debug(); 310} 311 312static void 313split_task_slave(void) 314{ 315 if (keymat_scan()) { 316 DEBUG(LOG_SPLIT, "Sending SCAN_KEYMAT_RESP"); 317 halfmat = keymat_encode_half(SPLIT_SIDE); 318 if (!send_cmd(CMD_SCAN_KEYMAT_RESP) 319 || !uart_send((uint8_t *) &halfmat, 4)) { 320 WARN(LOG_SPLIT, 321 "UART send SCAN_KEYMAT_RESP failed"); 322 } 323 } 324} 325 326void 327split_task(void) 328{ 329 if (split_role == MASTER) { 330 split_task_master(); 331 } else { 332 split_task_slave(); 333 } 334} 335 336void 337split_warn_master(const char *msg) 338{ 339 uint32_t len; 340 341 if (!send_cmd(CMD_SLAVE_WARN)) { 342 WARN(LOG_SPLIT, "UART send SLAVE_WARN failed"); 343 return; 344 } 345 346 len = strlen(msg) + 1; 347 if (uart_send((const uint8_t *) msg, len) != len) 348 WARN(LOG_SPLIT, "UART send warning failed"); 349}