transport.c (6284B)
1 /* Copyright 2020 Dimitris Papavasiliou <dpapavas@protonmail.ch> 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 3 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 <https://www.gnu.org/licenses/>. 15 */ 16 17 #include "spi_master.h" 18 #include "split_util.h" 19 #include "transport.h" 20 #include "timer.h" 21 22 #include "lagrange.h" 23 24 struct led_context { 25 led_t led_state; 26 layer_state_t layer_state; 27 }; 28 29 uint8_t transceive(uint8_t b) { 30 for (SPDR = b ; !(SPSR & _BV(SPIF)) ; ); 31 return SPDR; 32 } 33 34 /* The SPI bus, doesn't have any form of protocol built in, so when 35 * the other side isn't present, any old noise on the line will appear 36 * as matrix data. To avoid interpreting data as keystrokes, we do a 37 * simple n-way (8-way here) handshake before each scan, where each 38 * side sends a prearranged sequence of bytes. */ 39 40 bool shake_hands(bool master) { 41 const uint8_t m = master ? 0xf8 : 0; 42 const uint8_t a = 0xa8 ^ m, b = 0x50 ^ m; 43 bool synchronized = true; 44 45 uint8_t i; 46 47 i = SPSR; 48 i = SPDR; 49 50 do { 51 /* Cycling the SS pin on each attempt is necessary, as it 52 * resets the AVR's SPI core and guarantees proper 53 * alignment. */ 54 55 if (master) { 56 gpio_write_pin_low(SPI_SS_PIN); 57 } 58 59 for (i = 0 ; i < 8 ; i += 1) { 60 if (transceive(a + i) != b + i) { 61 synchronized = false; 62 break; 63 } 64 } 65 66 if (master) { 67 gpio_write_pin_high(SPI_SS_PIN); 68 } 69 } while (i < 8); 70 71 return synchronized; 72 } 73 74 bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { 75 const struct led_context context = { 76 host_keyboard_led_state(), 77 layer_state 78 }; 79 80 uint8_t i; 81 82 /* We shake hands both before and after transmitting the matrix. 83 * Doing it before transmitting is necessary to ensure 84 * synchronization: Due to the master-slave nature of the SPI bus, 85 * the master calls the shots. If we just go ahead and start 86 * clocking bits, the slave side might be otherwise engaged at 87 * that moment, so we'll initially read zeros, or garbage. Then 88 * when the slave gets around to transmitting its matrix, we'll 89 * misinterpret the keys it sends, leading to spurious 90 * keypresses. */ 91 92 /* The handshake forces the master to wait for the slave to be 93 * ready to start transmitting. */ 94 95 do { 96 shake_hands(true); 97 98 /* Receive the matrix from the other side, while transmitting 99 * LED and layer states. */ 100 101 spi_start(SPI_SS_PIN, 0, 0, 4); 102 103 for (i = 0 ; i < sizeof(matrix_row_t[MATRIX_ROWS / 2]) ; i += 1) { 104 spi_status_t x; 105 106 x = spi_write(i < sizeof(struct led_context) ? 107 ((uint8_t *)&context)[i] : 0); 108 109 if (x == SPI_STATUS_TIMEOUT) { 110 return false; 111 } 112 113 ((uint8_t *)slave_matrix)[i] = (uint8_t)x; 114 } 115 116 spi_stop(); 117 118 /* In case of errors during the transmission, e.g. if the 119 * cable was disconnected and since there is no inherent 120 * error-checking protocol, we would simply interpret noise as 121 * data. */ 122 123 /* To avoid this, both sides shake hands after transmitting. 124 * If synchronization was lost during transmission, the (first) 125 * handshake will fail. In that case we go around and 126 * re-transmit. */ 127 128 } while (!shake_hands(true)); 129 130 return true; 131 } 132 133 void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { 134 static struct led_context context; 135 struct led_context new_context; 136 137 uint8_t i; 138 139 /* Do the reverse of master above. Note that timing is critical, 140 * so interrupts must be turned off. */ 141 142 cli(); 143 shake_hands(false); 144 145 do { 146 for (i = 0 ; i < sizeof(matrix_row_t[MATRIX_ROWS / 2]) ; i += 1) { 147 uint8_t b; 148 149 b = transceive(((uint8_t *)slave_matrix)[i]); 150 151 if (i < sizeof(struct led_context)) { 152 ((uint8_t *)&new_context)[i] = b; 153 } 154 } 155 } while (!shake_hands(false)); 156 157 sei(); 158 159 /* Update the layer and LED state if necessary. */ 160 161 if (!isLeftHand) { 162 if (context.led_state.raw != new_context.led_state.raw) { 163 context.led_state.raw = new_context.led_state.raw; 164 led_update_kb(context.led_state); 165 } 166 167 if (context.layer_state != new_context.layer_state) { 168 context.layer_state = new_context.layer_state; 169 layer_state_set_kb(context.layer_state); 170 } 171 } 172 } 173 174 void transport_master_init(void) { 175 /* We need to set the SS pin as output as the handshake logic 176 * above depends on it and the SPI master driver won't do it 177 * before we call spi_start(). */ 178 179 gpio_write_pin_high(SPI_SS_PIN); 180 gpio_set_pin_output(SPI_SS_PIN); 181 182 spi_init(); 183 184 shake_hands(true); 185 } 186 187 void transport_slave_init(void) { 188 /* The datasheet isn't very clear on whether the internal pull-up 189 * is selectable when the SS pin is used by the SPI slave, but 190 * experimentations shows that it is, at least on the ATMega32u4. 191 * We enable the pull-up to guard against the case where both 192 * halves end up as slaves. In that case the SS pin would 193 * otherwise be floating and free to fluctuate due to picked up 194 * noise, etc. When reading low it would make both halves think 195 * they're asserted making the MISO pin an output on both ends and 196 * leading to potential shorts. */ 197 198 gpio_set_pin_input_high(SPI_SS_PIN); 199 gpio_set_pin_input(SPI_SCK_PIN); 200 gpio_set_pin_input(SPI_MOSI_PIN); 201 gpio_set_pin_output(SPI_MISO_PIN); 202 203 SPCR = _BV(SPE); 204 205 shake_hands(false); 206 }