]> git.donarmstrong.com Git - tmk_firmware.git/blob - protocol/ps2.c
Fix PS/2 USART version
[tmk_firmware.git] / protocol / ps2.c
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 #include <stdbool.h>
39 #include <avr/io.h>
40 #include <avr/interrupt.h>
41 #include <util/delay.h>
42 #include "ps2.h"
43 #include "debug.h"
44
45
46 #ifndef PS2_USE_INT
47 static uint8_t recv_data(void);
48 #endif
49 static inline void clock_lo(void);
50 static inline void clock_hi(void);
51 static inline bool clock_in(void);
52 static inline void data_lo(void);
53 static inline void data_hi(void);
54 static inline bool data_in(void);
55 static inline uint16_t wait_clock_lo(uint16_t us);
56 static inline uint16_t wait_clock_hi(uint16_t us);
57 static inline uint16_t wait_data_lo(uint16_t us);
58 static inline uint16_t wait_data_hi(uint16_t us);
59 static inline void idle(void);
60 static inline void inhibit(void);
61
62
63 /*
64 Primitive PS/2 Library for AVR
65 ==============================
66 Host side is only supported now.
67
68
69 I/O control
70 -----------
71 High state is asserted by input with pull up.
72
73
74 PS/2 References
75 ---------------
76 http://www.computer-engineering.org/ps2protocol/
77 http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
78 */
79
80
81 #define WAIT(stat, us, err) do { \
82     if (!wait_##stat(us)) { \
83         ps2_error = err; \
84         goto ERROR; \
85     } \
86 } while (0)
87
88
89 uint8_t ps2_error = PS2_ERR_NONE;
90
91
92 void ps2_host_init(void)
93 {
94 #ifdef PS2_USE_INT
95     PS2_INT_INIT();
96     PS2_INT_ON();
97     idle();
98 #else
99     inhibit();
100 #endif
101 }
102
103 // TODO: send using interrupt if available
104 uint8_t ps2_host_send(uint8_t data)
105 {
106     uint8_t res = 0;
107     bool parity = true;
108     ps2_error = PS2_ERR_NONE;
109 #ifdef PS2_USE_INT
110     PS2_INT_OFF();
111 #endif
112     /* terminate a transmission if we have */
113     inhibit();
114     _delay_us(200); // at least 100us
115
116     /* start bit [1] */
117     data_lo();
118     clock_hi();
119     WAIT(clock_lo, 20000, 10);   // may take 15ms at most until device starts clocking
120     /* data [2-9] */
121     for (uint8_t i = 0; i < 8; i++) {
122         _delay_us(15);
123         if (data&(1<<i)) {
124             parity = !parity;
125             data_hi();
126         } else {
127             data_lo();
128         }
129         WAIT(clock_hi, 50, 2);
130         WAIT(clock_lo, 50, 3);
131     }
132     /* parity [10] */
133     _delay_us(15);
134     if (parity) { data_hi(); } else { data_lo(); }
135     WAIT(clock_hi, 50, 4);
136     WAIT(clock_lo, 50, 5);
137     /* stop bit [11] */
138     _delay_us(15);
139     data_hi();
140     /* ack [12] */
141     WAIT(data_lo, 50, 6);
142     WAIT(clock_lo, 50, 7);
143
144     /* wait for idle state */
145     WAIT(clock_hi, 50, 8);
146     WAIT(data_hi, 50, 9);
147
148 #ifdef PS2_USE_INT
149     PS2_INT_ON();
150 #endif
151     res = ps2_host_recv_response();
152 ERROR:
153 #ifdef PS2_USE_INT
154     PS2_INT_ON();
155     idle();
156 #else
157     inhibit();
158 #endif
159     return res;
160 }
161
162 #ifndef PS2_USE_INT
163 /* receive data when host want else inhibit communication */
164 uint8_t ps2_host_recv_response(void)
165 {
166     uint8_t data = 0;
167
168 #ifdef PS2_USE_INT
169     PS2_INT_OFF();
170 #endif
171     /* terminate a transmission if we have */
172     inhibit();
173     _delay_us(100);
174
175     /* release lines(idle state) */
176     idle();
177
178     /* wait start bit */
179     wait_clock_lo(25000);    // command response may take 20 ms at most
180     data = recv_data();
181
182     inhibit();
183     return data;
184 }
185 #endif
186
187 #ifndef PS2_USE_INT
188 uint8_t ps2_host_recv(void)
189 {
190     return ps2_host_recv_response();
191 }
192 #else
193 /* ring buffer to store ps/2 key data */
194 #define PBUF_SIZE 32
195 static uint8_t pbuf[PBUF_SIZE];
196 static uint8_t pbuf_head = 0;
197 static uint8_t pbuf_tail = 0;
198 static inline void pbuf_enqueue(uint8_t data)
199 {
200     uint8_t sreg = SREG;
201     cli();
202     uint8_t next = (pbuf_head + 1) % PBUF_SIZE;
203     if (next != pbuf_tail) {
204         pbuf[pbuf_head] = data;
205         pbuf_head = next;
206     } else {
207         debug("pbuf: full\n");
208     }
209     SREG = sreg;
210 }
211 static inline uint8_t pbuf_dequeue(void)
212 {
213     uint8_t val = 0;
214
215     uint8_t sreg = SREG;
216     cli();
217     if (pbuf_head != pbuf_tail) {
218         val = pbuf[pbuf_tail];
219         pbuf_tail = (pbuf_tail + 1) % PBUF_SIZE;
220     }
221     SREG = sreg;
222
223     return val;
224 }
225 static inline bool pbuf_has_data(void)
226 {
227     uint8_t sreg = SREG;
228     cli();
229     bool has_data = (pbuf_head != pbuf_tail);
230     SREG = sreg;
231     return has_data;
232 }
233 static inline void pbuf_clear(void)
234 {
235     uint8_t sreg = SREG;
236     cli();
237     pbuf_head = pbuf_tail = 0;
238     SREG = sreg;
239 }
240
241 /* get data received by interrupt */
242 uint8_t ps2_host_recv(void)
243 {
244     if (ps2_error) {
245         print("x");
246         phex(ps2_error);
247         ps2_host_send(0xFE);    // request to resend
248         ps2_error = PS2_ERR_NONE;
249     }
250     idle();
251     return pbuf_dequeue();
252 }
253
254 uint8_t ps2_host_recv_response(void)
255 {
256     while (!pbuf_has_data()) ;
257     return pbuf_dequeue();
258 }
259
260 ISR(PS2_INT_VECT)
261 {
262     static enum {
263         INIT,
264         START,
265         BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7,
266         PARITY,
267         STOP,
268     } state = INIT;
269     static uint8_t data = 0;
270     static uint8_t parity = 1;
271
272     // TODO: abort if elapse 100us from previous interrupt
273
274     // return unless falling edge
275     if (clock_in()) {
276         goto RETURN;
277     }
278
279     state++;
280     switch (state) {
281         case START:
282             if (data_in())
283                 goto ERROR;
284             break;
285         case BIT0:
286         case BIT1:
287         case BIT2:
288         case BIT3:
289         case BIT4:
290         case BIT5:
291         case BIT6:
292         case BIT7:
293             data >>= 1;
294             if (data_in()) {
295                 data |= 0x80;
296                 parity++;
297             }
298             break;
299         case PARITY:
300             if (data_in()) {
301                 if (!(parity & 0x01))
302                     goto ERROR;
303             } else {
304                 if (parity & 0x01)
305                     goto ERROR;
306             }
307             break;
308         case STOP:
309             if (!data_in())
310                 goto ERROR;
311             pbuf_enqueue(data);
312 //phex(data);
313             goto DONE;
314             break;
315         default:
316             goto ERROR;
317     }
318     goto RETURN;
319 ERROR:
320     inhibit();
321     ps2_error = state;
322 DONE:
323     state = INIT;
324     data = 0;
325     parity = 1;
326 RETURN:
327     return;
328 }
329 #endif
330
331
332 /* send LED state to keyboard */
333 void ps2_host_set_led(uint8_t led)
334 {
335     ps2_host_send(0xED);
336     ps2_host_send(led);
337 }
338
339
340 #ifndef PS2_USE_INT
341 /* called after start bit comes */
342 static uint8_t recv_data(void)
343 {
344     uint8_t data = 0;
345     bool parity = true;
346     ps2_error = PS2_ERR_NONE;
347
348     /* start bit [1] */
349     WAIT(clock_lo, 1, 1);
350     WAIT(data_lo, 1, 2);
351     WAIT(clock_hi, 50, 3);
352
353     /* data [2-9] */
354     for (uint8_t i = 0; i < 8; i++) {
355         WAIT(clock_lo, 50, 4);
356         if (data_in()) {
357             parity = !parity;
358             data |= (1<<i);
359         }
360         WAIT(clock_hi, 50, 5);
361     }
362
363     /* parity [10] */
364     WAIT(clock_lo, 50, 6);
365     if (data_in() != parity) {
366         ps2_error = PS2_ERR_PARITY;
367         goto ERROR;
368     }
369     WAIT(clock_hi, 50, 7);
370
371     /* stop bit [11] */
372     WAIT(clock_lo, 50, 8);
373     WAIT(data_hi, 1, 9);
374     WAIT(clock_hi, 50, 10);
375
376     return data;
377 ERROR:
378     return 0;
379 }
380 #endif
381
382 static inline void clock_lo()
383 {
384     PS2_CLOCK_PORT &= ~(1<<PS2_CLOCK_BIT);
385     PS2_CLOCK_DDR  |=  (1<<PS2_CLOCK_BIT);
386 }
387 static inline void clock_hi()
388 {
389     /* input with pull up */
390     PS2_CLOCK_DDR  &= ~(1<<PS2_CLOCK_BIT);
391     PS2_CLOCK_PORT |=  (1<<PS2_CLOCK_BIT);
392 }
393 static inline bool clock_in()
394 {
395     PS2_CLOCK_DDR  &= ~(1<<PS2_CLOCK_BIT);
396     PS2_CLOCK_PORT |=  (1<<PS2_CLOCK_BIT);
397     _delay_us(1);
398     return PS2_CLOCK_PIN&(1<<PS2_CLOCK_BIT);
399 }
400 static inline void data_lo()
401 {
402     PS2_DATA_PORT &= ~(1<<PS2_DATA_BIT);
403     PS2_DATA_DDR  |=  (1<<PS2_DATA_BIT);
404 }
405 static inline void data_hi()
406 {
407     /* input with pull up */
408     PS2_DATA_DDR  &= ~(1<<PS2_DATA_BIT);
409     PS2_DATA_PORT |=  (1<<PS2_DATA_BIT);
410 }
411 static inline bool data_in()
412 {
413     PS2_DATA_DDR  &= ~(1<<PS2_DATA_BIT);
414     PS2_DATA_PORT |=  (1<<PS2_DATA_BIT);
415     _delay_us(1);
416     return PS2_DATA_PIN&(1<<PS2_DATA_BIT);
417 }
418
419 static inline uint16_t wait_clock_lo(uint16_t us)
420 {
421     while (clock_in()  && us) { asm(""); _delay_us(1); us--; }
422     return us;
423 }
424 static inline uint16_t wait_clock_hi(uint16_t us)
425 {
426     while (!clock_in() && us) { asm(""); _delay_us(1); us--; }
427     return us;
428 }
429 static inline uint16_t wait_data_lo(uint16_t us)
430 {
431     while (data_in() && us)  { asm(""); _delay_us(1); us--; }
432     return us;
433 }
434 static inline uint16_t wait_data_hi(uint16_t us)
435 {
436     while (!data_in() && us)  { asm(""); _delay_us(1); us--; }
437     return us;
438 }
439
440 /* idle state that device can send */
441 static inline void idle(void)
442 {
443     clock_hi();
444     data_hi();
445 }
446
447 /* inhibit device to send */
448 static inline void inhibit(void)
449 {
450     clock_lo();
451     data_hi();
452 }