]> git.gir.st - tmk_keyboard.git/blob - protocol/ps2.c
Fix interrupt version of ps2.c
[tmk_keyboard.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 }
Imprint / Impressum