]> git.donarmstrong.com Git - tmk_firmware.git/blob - protocol/ps2.c
Add ps2_usart.c and fix set_led at USB wake
[tmk_firmware.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  */