]> git.gir.st - tmk_keyboard.git/blob - ps2.c
Synchronous USART support for PS/2 on V-USB stack
[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 }
Imprint / Impressum