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