]> git.donarmstrong.com Git - tmk_firmware.git/blob - ps2.c
fixed macway to comply new API.
[tmk_firmware.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
191     uint8_t sreg = SREG;
192     cli();
193     uint8_t next = (pbuf_head + 1) % PBUF_SIZE;
194     if (next != pbuf_tail) {
195         pbuf[pbuf_head] = data;
196         pbuf_head = next;
197     } else {
198         debug("pbuf: full\n");
199     }
200     SREG = sreg;
201 }
202 static inline uint8_t pbuf_dequeue(void)
203 {
204     uint8_t val = 0;
205
206     uint8_t sreg = SREG;
207     cli();
208     if (pbuf_head != pbuf_tail) {
209         val = pbuf[pbuf_tail];
210         pbuf_tail = (pbuf_tail + 1) % PBUF_SIZE;
211     }
212     SREG = sreg;
213
214     return val;
215 }
216
217 /* get data received by interrupt */
218 uint8_t ps2_host_recv(void)
219 {
220     if (ps2_error) {
221         print("x");
222         phex(ps2_error);
223         ps2_host_send(0xFE);    // request to resend
224         ps2_error = PS2_ERR_NONE;
225     }
226     idle();
227     return pbuf_dequeue();
228 }
229
230 #define DEBUGP_INIT() do { DDRC = 0xFF; } while (0)
231 #define DEBUGP(x) do { PORTC = x; } while (0)
232 ISR(PS2_INT_VECT)
233 {
234     static enum {
235         INIT,
236         START,
237         BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7,
238         PARITY,
239         STOP,
240     } state = INIT;
241     static uint8_t data = 0;
242     static uint8_t parity = 1;
243
244     // TODO: abort if elapse 100us from previous interrupt
245
246     // return unless falling edge
247     if (clock_in()) {
248         goto RETURN;
249     }
250
251     state++;
252     DEBUGP(state);
253     switch (state) {
254         case START:
255             if (data_in())
256                 goto ERROR;
257             break;
258         case BIT0:
259         case BIT1:
260         case BIT2:
261         case BIT3:
262         case BIT4:
263         case BIT5:
264         case BIT6:
265         case BIT7:
266             data >>= 1;
267             if (data_in()) {
268                 data |= 0x80;
269                 parity++;
270             }
271             break;
272         case PARITY:
273             if (data_in()) {
274                 if (!(parity & 0x01))
275                     goto ERROR;
276             } else {
277                 if (parity & 0x01)
278                     goto ERROR;
279             }
280             break;
281         case STOP:
282             if (!data_in())
283                 goto ERROR;
284             pbuf_enqueue(data);
285             goto DONE;
286             break;
287         default:
288             goto ERROR;
289     }
290     goto RETURN;
291 ERROR:
292     DEBUGP(0x0F);
293     inhibit();
294     ps2_error = state;
295 DONE:
296     state = INIT;
297     data = 0;
298     parity = 1;
299 RETURN:
300     return;
301 }
302 #endif
303
304
305 static void ps2_reset(void)
306 {
307     ps2_host_send(0xFF);
308 }
309
310 /* send LED state to keyboard */
311 void ps2_host_set_led(uint8_t led)
312 {
313     ps2_host_send(0xED);
314     ps2_host_send(led);
315 }
316
317
318 /* called after start bit comes */
319 static uint8_t recv_data(void)
320 {
321     uint8_t data = 0;
322     bool parity = true;
323     ps2_error = PS2_ERR_NONE;
324
325     /* start bit [1] */
326     WAIT(clock_lo, 1, 1);
327     WAIT(data_lo, 1, 2);
328     WAIT(clock_hi, 50, 3);
329
330     /* data [2-9] */
331     for (uint8_t i = 0; i < 8; i++) {
332         WAIT(clock_lo, 50, 4);
333         if (data_in()) {
334             parity = !parity;
335             data |= (1<<i);
336         }
337         WAIT(clock_hi, 50, 5);
338     }
339
340     /* parity [10] */
341     WAIT(clock_lo, 50, 6);
342     if (data_in() != parity) {
343         ps2_error = PS2_ERR_PARITY;
344         goto ERROR;
345     }
346     WAIT(clock_hi, 50, 7);
347
348     /* stop bit [11] */
349     WAIT(clock_lo, 50, 8);
350     WAIT(data_hi, 1, 9);
351     WAIT(clock_hi, 50, 10);
352
353     return data;
354 ERROR:
355     return 0;
356 }
357
358 static inline void clock_lo()
359 {
360     PS2_CLOCK_PORT &= ~(1<<PS2_CLOCK_BIT);
361     PS2_CLOCK_DDR  |=  (1<<PS2_CLOCK_BIT);
362 }
363 static inline void clock_hi()
364 {
365     /* input with pull up */
366     PS2_CLOCK_DDR  &= ~(1<<PS2_CLOCK_BIT);
367     PS2_CLOCK_PORT |=  (1<<PS2_CLOCK_BIT);
368 }
369 static inline bool clock_in()
370 {
371     PS2_CLOCK_DDR  &= ~(1<<PS2_CLOCK_BIT);
372     PS2_CLOCK_PORT |=  (1<<PS2_CLOCK_BIT);
373     _delay_us(1);
374     return PS2_CLOCK_PIN&(1<<PS2_CLOCK_BIT);
375 }
376 static inline void data_lo()
377 {
378     PS2_DATA_PORT &= ~(1<<PS2_DATA_BIT);
379     PS2_DATA_DDR  |=  (1<<PS2_DATA_BIT);
380 }
381 static inline void data_hi()
382 {
383     /* input with pull up */
384     PS2_DATA_DDR  &= ~(1<<PS2_DATA_BIT);
385     PS2_DATA_PORT |=  (1<<PS2_DATA_BIT);
386 }
387 static inline bool data_in()
388 {
389     PS2_DATA_DDR  &= ~(1<<PS2_DATA_BIT);
390     PS2_DATA_PORT |=  (1<<PS2_DATA_BIT);
391     _delay_us(1);
392     return PS2_DATA_PIN&(1<<PS2_DATA_BIT);
393 }
394
395 static inline uint16_t wait_clock_lo(uint16_t us)
396 {
397     while (clock_in()  && us) { asm(""); _delay_us(1); us--; }
398     return us;
399 }
400 static inline uint16_t wait_clock_hi(uint16_t us)
401 {
402     while (!clock_in() && us) { asm(""); _delay_us(1); us--; }
403     return us;
404 }
405 static inline uint16_t wait_data_lo(uint16_t us)
406 {
407     while (data_in() && us)  { asm(""); _delay_us(1); us--; }
408     return us;
409 }
410 static inline uint16_t wait_data_hi(uint16_t us)
411 {
412     while (!data_in() && us)  { asm(""); _delay_us(1); us--; }
413     return us;
414 }
415
416 /* idle state that device can send */
417 static inline void idle(void)
418 {
419     clock_hi();
420     data_hi();
421 }
422
423 /* inhibit device to send */
424 static inline void inhibit(void)
425 {
426     clock_lo();
427     data_hi();
428 }