ps2_interrupt.c (8511B)
1 /* 2 Copyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com> 3 4 This software is licensed with a Modified BSD License. 5 All of this is supposed to be Free Software, Open Source, DFSG-free, 6 GPL-compatible, and OK to use in both free and proprietary applications. 7 Additions and corrections to this file are welcome. 8 9 10 Redistribution and use in source and binary forms, with or without 11 modification, are permitted provided that the following conditions are met: 12 13 * Redistributions of source code must retain the above copyright 14 notice, this list of conditions and the following disclaimer. 15 16 * Redistributions in binary form must reproduce the above copyright 17 notice, this list of conditions and the following disclaimer in 18 the documentation and/or other materials provided with the 19 distribution. 20 21 * Neither the name of the copyright holders nor the names of 22 contributors may be used to endorse or promote products derived 23 from this software without specific prior written permission. 24 25 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 26 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 29 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 /* 39 * PS/2 protocol Pin interrupt version 40 */ 41 42 #include <stdbool.h> 43 44 #if defined(__AVR__) 45 # include <avr/interrupt.h> 46 #elif defined(PROTOCOL_CHIBIOS) // TODO: or STM32 ? 47 // chibiOS headers 48 # include "ch.h" 49 # include "hal.h" 50 # include "gpio.h" 51 #endif 52 53 #include "ps2.h" 54 #include "ps2_io.h" 55 #include "print.h" 56 #include "wait.h" 57 58 #define WAIT(stat, us, err) \ 59 do { \ 60 if (!wait_##stat(us)) { \ 61 ps2_error = err; \ 62 goto ERROR; \ 63 } \ 64 } while (0) 65 66 uint8_t ps2_error = PS2_ERR_NONE; 67 68 static inline uint8_t pbuf_dequeue(void); 69 static inline void pbuf_enqueue(uint8_t data); 70 static inline void pbuf_clear(void); 71 bool pbuf_has_data(void); 72 73 #if defined(PROTOCOL_CHIBIOS) 74 void ps2_interrupt_service_routine(void); 75 void palCallback(void *arg) { 76 ps2_interrupt_service_routine(); 77 } 78 79 # define PS2_INT_INIT() \ 80 do { \ 81 palSetLineMode(PS2_CLOCK_PIN, PAL_MODE_INPUT); \ 82 } while (0) 83 # define PS2_INT_ON() \ 84 do { \ 85 palEnableLineEvent(PS2_CLOCK_PIN, PAL_EVENT_MODE_FALLING_EDGE); \ 86 palSetLineCallback(PS2_CLOCK_PIN, palCallback, NULL); \ 87 } while (0) 88 # define PS2_INT_OFF() \ 89 do { \ 90 palDisableLineEvent(PS2_CLOCK_PIN); \ 91 } while (0) 92 #endif // PROTOCOL_CHIBIOS 93 94 void ps2_host_init(void) { 95 idle(); 96 PS2_INT_INIT(); 97 PS2_INT_ON(); 98 // POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20) 99 // wait_ms(2500); 100 } 101 102 uint8_t ps2_host_send(uint8_t data) { 103 bool parity = true; 104 ps2_error = PS2_ERR_NONE; 105 106 PS2_INT_OFF(); 107 108 /* terminate a transmission if we have */ 109 inhibit(); 110 wait_us(100); // 100us [4]p.13, [5]p.50 111 112 /* 'Request to Send' and Start bit */ 113 data_lo(); 114 clock_hi(); 115 WAIT(clock_lo, 10000, 10); // 10ms [5]p.50 116 117 /* Data bit[2-9] */ 118 for (uint8_t i = 0; i < 8; i++) { 119 if (data & (1 << i)) { 120 parity = !parity; 121 data_hi(); 122 } else { 123 data_lo(); 124 } 125 WAIT(clock_hi, 50, 2); 126 WAIT(clock_lo, 50, 3); 127 } 128 129 /* Parity bit */ 130 wait_us(15); 131 if (parity) { 132 data_hi(); 133 } else { 134 data_lo(); 135 } 136 WAIT(clock_hi, 50, 4); 137 WAIT(clock_lo, 50, 5); 138 139 /* Stop bit */ 140 wait_us(15); 141 data_hi(); 142 143 /* Ack */ 144 WAIT(data_lo, 50, 6); 145 WAIT(clock_lo, 50, 7); 146 147 /* wait for idle state */ 148 WAIT(clock_hi, 50, 8); 149 WAIT(data_hi, 50, 9); 150 151 idle(); 152 PS2_INT_ON(); 153 return ps2_host_recv_response(); 154 ERROR: 155 idle(); 156 PS2_INT_ON(); 157 return 0; 158 } 159 160 uint8_t ps2_host_recv_response(void) { 161 // Command may take 25ms/20ms at most([5]p.46, [3]p.21) 162 uint8_t retry = 25; 163 while (retry-- && !pbuf_has_data()) { 164 wait_ms(1); 165 } 166 return pbuf_dequeue(); 167 } 168 169 /* get data received by interrupt */ 170 uint8_t ps2_host_recv(void) { 171 if (pbuf_has_data()) { 172 ps2_error = PS2_ERR_NONE; 173 return pbuf_dequeue(); 174 } else { 175 ps2_error = PS2_ERR_NODATA; 176 return 0; 177 } 178 } 179 180 void ps2_interrupt_service_routine(void) { 181 static enum { 182 INIT, 183 START, 184 BIT0, 185 BIT1, 186 BIT2, 187 BIT3, 188 BIT4, 189 BIT5, 190 BIT6, 191 BIT7, 192 PARITY, 193 STOP, 194 } state = INIT; 195 static uint8_t data = 0; 196 static uint8_t parity = 1; 197 198 // TODO: abort if elapse 100us from previous interrupt 199 200 // return unless falling edge 201 if (clock_in()) { 202 goto RETURN; 203 } 204 205 state++; 206 switch (state) { 207 case START: 208 if (data_in()) goto ERROR; 209 break; 210 case BIT0: 211 case BIT1: 212 case BIT2: 213 case BIT3: 214 case BIT4: 215 case BIT5: 216 case BIT6: 217 case BIT7: 218 data >>= 1; 219 if (data_in()) { 220 data |= 0x80; 221 parity++; 222 } 223 break; 224 case PARITY: 225 if (data_in()) { 226 if (!(parity & 0x01)) goto ERROR; 227 } else { 228 if (parity & 0x01) goto ERROR; 229 } 230 break; 231 case STOP: 232 if (!data_in()) goto ERROR; 233 pbuf_enqueue(data); 234 goto DONE; 235 break; 236 default: 237 goto ERROR; 238 } 239 goto RETURN; 240 ERROR: 241 ps2_error = state; 242 DONE: 243 state = INIT; 244 data = 0; 245 parity = 1; 246 RETURN: 247 return; 248 } 249 250 #if defined(__AVR__) 251 ISR(PS2_INT_VECT) { 252 ps2_interrupt_service_routine(); 253 } 254 #endif 255 256 /* send LED state to keyboard */ 257 void ps2_host_set_led(uint8_t led) { 258 ps2_host_send(0xED); 259 ps2_host_send(led); 260 } 261 262 /*-------------------------------------------------------------------- 263 * Ring buffer to store scan codes from keyboard 264 *------------------------------------------------------------------*/ 265 #define PBUF_SIZE 32 266 static uint8_t pbuf[PBUF_SIZE]; 267 static uint8_t pbuf_head = 0; 268 static uint8_t pbuf_tail = 0; 269 static inline void pbuf_enqueue(uint8_t data) { 270 #if defined(__AVR__) 271 uint8_t sreg = SREG; 272 cli(); 273 #elif defined(PROTOCOL_CHIBIOS) 274 chSysLockFromISR(); 275 #endif 276 277 uint8_t next = (pbuf_head + 1) % PBUF_SIZE; 278 if (next != pbuf_tail) { 279 pbuf[pbuf_head] = data; 280 pbuf_head = next; 281 } else { 282 print("pbuf: full\n"); 283 } 284 285 #if defined(__AVR__) 286 SREG = sreg; 287 #elif defined(PROTOCOL_CHIBIOS) 288 chSysUnlockFromISR(); 289 #endif 290 } 291 static inline uint8_t pbuf_dequeue(void) { 292 uint8_t val = 0; 293 294 #if defined(__AVR__) 295 uint8_t sreg = SREG; 296 cli(); 297 #elif defined(PROTOCOL_CHIBIOS) 298 chSysLock(); 299 #endif 300 301 if (pbuf_head != pbuf_tail) { 302 val = pbuf[pbuf_tail]; 303 pbuf_tail = (pbuf_tail + 1) % PBUF_SIZE; 304 } 305 306 #if defined(__AVR__) 307 SREG = sreg; 308 #elif defined(PROTOCOL_CHIBIOS) 309 chSysUnlock(); 310 #endif 311 312 return val; 313 } 314 bool pbuf_has_data(void) { 315 #if defined(__AVR__) 316 uint8_t sreg = SREG; 317 cli(); 318 #elif defined(PROTOCOL_CHIBIOS) 319 chSysLock(); 320 #endif 321 322 bool has_data = (pbuf_head != pbuf_tail); 323 324 #if defined(__AVR__) 325 SREG = sreg; 326 #elif defined(PROTOCOL_CHIBIOS) 327 chSysUnlock(); 328 #endif 329 return has_data; 330 } 331 static inline void pbuf_clear(void) { 332 #if defined(__AVR__) 333 uint8_t sreg = SREG; 334 cli(); 335 #elif defined(PROTOCOL_CHIBIOS) 336 chSysLock(); 337 #endif 338 339 pbuf_head = pbuf_tail = 0; 340 341 #if defined(__AVR__) 342 SREG = sreg; 343 #elif defined(PROTOCOL_CHIBIOS) 344 chSysUnlock(); 345 #endif 346 }