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