]> git.gir.st - tmk_keyboard.git/blob - protocol/ps2.c
Add LED support to x68k
[tmk_keyboard.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 }
Imprint / Impressum