]> git.gir.st - tmk_keyboard.git/blob - protocol/ps2.c
Add ps2_usart.c and fix set_led at USB wake
[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 // POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)
95 _delay_ms(2500);
96
97 #ifdef PS2_USE_INT
98 PS2_INT_INIT();
99 PS2_INT_ON();
100 idle();
101 #else
102 inhibit();
103 #endif
104 }
105
106 // TODO: send using interrupt if available
107 uint8_t ps2_host_send(uint8_t data)
108 {
109 uint8_t res = 0;
110 bool parity = true;
111 ps2_error = PS2_ERR_NONE;
112 #ifdef PS2_USE_INT
113 PS2_INT_OFF();
114 #endif
115 /* terminate a transmission if we have */
116 inhibit();
117 _delay_us(200); // at least 100us
118
119 /* start bit [1] */
120 data_lo();
121 clock_hi();
122 WAIT(clock_lo, 20000, 10); // may take 15ms at most until device starts clocking
123 /* data [2-9] */
124 for (uint8_t i = 0; i < 8; i++) {
125 _delay_us(15);
126 if (data&(1<<i)) {
127 parity = !parity;
128 data_hi();
129 } else {
130 data_lo();
131 }
132 WAIT(clock_hi, 50, 2);
133 WAIT(clock_lo, 50, 3);
134 }
135 /* parity [10] */
136 _delay_us(15);
137 if (parity) { data_hi(); } else { data_lo(); }
138 WAIT(clock_hi, 50, 4);
139 WAIT(clock_lo, 50, 5);
140 /* stop bit [11] */
141 _delay_us(15);
142 data_hi();
143 /* ack [12] */
144 WAIT(data_lo, 50, 6);
145 WAIT(clock_lo, 50, 7);
146
147 /* wait for idle state */
148 WAIT(clock_hi, 50, 8);
149 WAIT(data_hi, 50, 9);
150
151 #ifdef PS2_USE_INT
152 PS2_INT_ON();
153 #endif
154 res = ps2_host_recv_response();
155 ERROR:
156 #ifdef PS2_USE_INT
157 PS2_INT_ON();
158 idle();
159 #else
160 inhibit();
161 #endif
162 return res;
163 }
164
165 #ifndef PS2_USE_INT
166 /* receive data when host want else inhibit communication */
167 uint8_t ps2_host_recv_response(void)
168 {
169 // Command might take 20ms to response([3]p.21)
170 // TrackPoint might take 25ms ([5]2.7)
171 uint8_t data = 0;
172 uint8_t try = 200;
173 while (try-- && (data = ps2_host_recv())) ;
174 return data;
175 }
176 #endif
177
178 #ifndef PS2_USE_INT
179 uint8_t ps2_host_recv(void)
180 {
181 uint8_t data = 0;
182
183 /* release lines(idle state) */
184 idle();
185
186 /* wait start bit */
187 wait_clock_lo(100); // TODO: this is enough?
188 data = recv_data();
189
190 inhibit();
191 return data;
192 }
193 #else
194 /* ring buffer to store ps/2 key data */
195 #define PBUF_SIZE 32
196 static uint8_t pbuf[PBUF_SIZE];
197 static uint8_t pbuf_head = 0;
198 static uint8_t pbuf_tail = 0;
199 static inline void pbuf_enqueue(uint8_t data)
200 {
201 uint8_t sreg = SREG;
202 cli();
203 uint8_t next = (pbuf_head + 1) % PBUF_SIZE;
204 if (next != pbuf_tail) {
205 pbuf[pbuf_head] = data;
206 pbuf_head = next;
207 } else {
208 debug("pbuf: full\n");
209 }
210 SREG = sreg;
211 }
212 static inline uint8_t pbuf_dequeue(void)
213 {
214 uint8_t val = 0;
215
216 uint8_t sreg = SREG;
217 cli();
218 if (pbuf_head != pbuf_tail) {
219 val = pbuf[pbuf_tail];
220 pbuf_tail = (pbuf_tail + 1) % PBUF_SIZE;
221 }
222 SREG = sreg;
223
224 return val;
225 }
226 static inline bool pbuf_has_data(void)
227 {
228 uint8_t sreg = SREG;
229 cli();
230 bool has_data = (pbuf_head != pbuf_tail);
231 SREG = sreg;
232 return has_data;
233 }
234 static inline void pbuf_clear(void)
235 {
236 uint8_t sreg = SREG;
237 cli();
238 pbuf_head = pbuf_tail = 0;
239 SREG = sreg;
240 }
241
242 /* get data received by interrupt */
243 uint8_t ps2_host_recv(void)
244 {
245 return pbuf_dequeue();
246 }
247
248 uint8_t ps2_host_recv_response(void)
249 {
250 while (!pbuf_has_data()) ;
251 return pbuf_dequeue();
252 }
253
254 ISR(PS2_INT_VECT)
255 {
256 static enum {
257 INIT,
258 START,
259 BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7,
260 PARITY,
261 STOP,
262 } state = INIT;
263 static uint8_t data = 0;
264 static uint8_t parity = 1;
265
266 // TODO: abort if elapse 100us from previous interrupt
267
268 // return unless falling edge
269 if (clock_in()) {
270 goto RETURN;
271 }
272
273 state++;
274 switch (state) {
275 case START:
276 if (data_in())
277 goto ERROR;
278 break;
279 case BIT0:
280 case BIT1:
281 case BIT2:
282 case BIT3:
283 case BIT4:
284 case BIT5:
285 case BIT6:
286 case BIT7:
287 data >>= 1;
288 if (data_in()) {
289 data |= 0x80;
290 parity++;
291 }
292 break;
293 case PARITY:
294 if (data_in()) {
295 if (!(parity & 0x01))
296 goto ERROR;
297 } else {
298 if (parity & 0x01)
299 goto ERROR;
300 }
301 break;
302 case STOP:
303 if (!data_in())
304 goto ERROR;
305 pbuf_enqueue(data);
306 //phex(data);
307 goto DONE;
308 break;
309 default:
310 goto ERROR;
311 }
312 goto RETURN;
313 ERROR:
314 inhibit();
315 ps2_error = state;
316 DONE:
317 state = INIT;
318 data = 0;
319 parity = 1;
320 RETURN:
321 return;
322 }
323 #endif
324
325
326 /* send LED state to keyboard */
327 void ps2_host_set_led(uint8_t led)
328 {
329 ps2_host_send(0xED);
330 ps2_host_send(led);
331 }
332
333
334 #ifndef PS2_USE_INT
335 /* called after start bit comes */
336 static uint8_t recv_data(void)
337 {
338 uint8_t data = 0;
339 bool parity = true;
340 ps2_error = PS2_ERR_NONE;
341
342 /* start bit [1] */
343 WAIT(clock_lo, 1, 1);
344 WAIT(data_lo, 1, 2);
345 WAIT(clock_hi, 50, 3);
346
347 /* data [2-9] */
348 for (uint8_t i = 0; i < 8; i++) {
349 WAIT(clock_lo, 50, 4);
350 if (data_in()) {
351 parity = !parity;
352 data |= (1<<i);
353 }
354 WAIT(clock_hi, 50, 5);
355 }
356
357 /* parity [10] */
358 WAIT(clock_lo, 50, 6);
359 if (data_in() != parity) {
360 ps2_error = PS2_ERR_PARITY;
361 goto ERROR;
362 }
363 WAIT(clock_hi, 50, 7);
364
365 /* stop bit [11] */
366 WAIT(clock_lo, 50, 8);
367 WAIT(data_hi, 1, 9);
368 WAIT(clock_hi, 50, 10);
369
370 return data;
371 ERROR:
372 return 0;
373 }
374 #endif
375
376 static inline void clock_lo()
377 {
378 PS2_CLOCK_PORT &= ~(1<<PS2_CLOCK_BIT);
379 PS2_CLOCK_DDR |= (1<<PS2_CLOCK_BIT);
380 }
381 static inline void clock_hi()
382 {
383 /* input with pull up */
384 PS2_CLOCK_DDR &= ~(1<<PS2_CLOCK_BIT);
385 PS2_CLOCK_PORT |= (1<<PS2_CLOCK_BIT);
386 }
387 static inline bool clock_in()
388 {
389 PS2_CLOCK_DDR &= ~(1<<PS2_CLOCK_BIT);
390 PS2_CLOCK_PORT |= (1<<PS2_CLOCK_BIT);
391 _delay_us(1);
392 return PS2_CLOCK_PIN&(1<<PS2_CLOCK_BIT);
393 }
394 static inline void data_lo()
395 {
396 PS2_DATA_PORT &= ~(1<<PS2_DATA_BIT);
397 PS2_DATA_DDR |= (1<<PS2_DATA_BIT);
398 }
399 static inline void data_hi()
400 {
401 /* input with pull up */
402 PS2_DATA_DDR &= ~(1<<PS2_DATA_BIT);
403 PS2_DATA_PORT |= (1<<PS2_DATA_BIT);
404 }
405 static inline bool data_in()
406 {
407 PS2_DATA_DDR &= ~(1<<PS2_DATA_BIT);
408 PS2_DATA_PORT |= (1<<PS2_DATA_BIT);
409 _delay_us(1);
410 return PS2_DATA_PIN&(1<<PS2_DATA_BIT);
411 }
412
413 static inline uint16_t wait_clock_lo(uint16_t us)
414 {
415 while (clock_in() && us) { asm(""); _delay_us(1); us--; }
416 return us;
417 }
418 static inline uint16_t wait_clock_hi(uint16_t us)
419 {
420 while (!clock_in() && us) { asm(""); _delay_us(1); us--; }
421 return us;
422 }
423 static inline uint16_t wait_data_lo(uint16_t us)
424 {
425 while (data_in() && us) { asm(""); _delay_us(1); us--; }
426 return us;
427 }
428 static inline uint16_t wait_data_hi(uint16_t us)
429 {
430 while (!data_in() && us) { asm(""); _delay_us(1); us--; }
431 return us;
432 }
433
434 /* idle state that device can send */
435 static inline void idle(void)
436 {
437 clock_hi();
438 data_hi();
439 }
440
441 /* inhibit device to send */
442 static inline void inhibit(void)
443 {
444 clock_lo();
445 data_hi();
446 }
447
448
449 /* PS/2 Resources
450 *
451 * [1] The PS/2 Mouse/Keyboard Protocol
452 * http://www.computer-engineering.org/ps2protocol/
453 * Concise and thorough primer of PS/2 protocol.
454 *
455 * [2] Keyboard and Auxiliary Device Controller
456 * http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
457 * Signal Timing and Format
458 *
459 * [3] Keyboards(101- and 102-key)
460 * http://www.mcamafia.de/pdf/ibm_hitrc11.pdf
461 * Keyboard Layout, Scan Code Set, POR, and Commands.
462 *
463 * [4] PS/2 Reference Manuals
464 * http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
465 * Collection of IBM Personal System/2 documents.
466 *
467 * [5] TrackPoint Engineering Specifications for version 3E
468 * https://web.archive.org/web/20100526161812/http://wwwcssrv.almaden.ibm.com/trackpoint/download.html
469 */
Imprint / Impressum