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