]> git.friedersdorff.com Git - max/tmk_keyboard.git/blob - ps2.c
added initial V-USB support for HHKB
[max/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
191     uint8_t sreg = SREG;
192     cli();
193     uint8_t next = (pbuf_head + 1) % PBUF_SIZE;
194     if (next != pbuf_tail) {
195         pbuf[pbuf_head] = data;
196         pbuf_head = next;
197     } else {
198         debug("pbuf: full\n");
199     }
200     SREG = sreg;
201 }
202 static inline uint8_t pbuf_dequeue(void)
203 {
204     uint8_t val = 0;
205
206     uint8_t sreg = SREG;
207     cli();
208     if (pbuf_head != pbuf_tail) {
209         val = pbuf[pbuf_tail];
210         pbuf_tail = (pbuf_tail + 1) % PBUF_SIZE;
211     }
212     SREG = sreg;
213
214     return val;
215 }
216
217 /* get data received by interrupt */
218 uint8_t ps2_host_recv(void)
219 {
220     if (ps2_error) {
221         print("x");
222         phex(ps2_error);
223         ps2_host_send(0xFE);    // request to resend
224         ps2_error = PS2_ERR_NONE;
225     }
226     idle();
227     return pbuf_dequeue();
228 }
229
230 #if 0
231 #define DEBUGP_INIT() do { DDRC = 0xFF; } while (0)
232 #define DEBUGP(x) do { PORTC = x; } while (0)
233 #else
234 #define DEBUGP_INIT()
235 #define DEBUGP(x)
236 #endif
237 ISR(PS2_INT_VECT)
238 {
239     static enum {
240         INIT,
241         START,
242         BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7,
243         PARITY,
244         STOP,
245     } state = INIT;
246     static uint8_t data = 0;
247     static uint8_t parity = 1;
248
249     // TODO: abort if elapse 100us from previous interrupt
250
251     // return unless falling edge
252     if (clock_in()) {
253         goto RETURN;
254     }
255
256     state++;
257     DEBUGP(state);
258     switch (state) {
259         case START:
260             if (data_in())
261                 goto ERROR;
262             break;
263         case BIT0:
264         case BIT1:
265         case BIT2:
266         case BIT3:
267         case BIT4:
268         case BIT5:
269         case BIT6:
270         case BIT7:
271             data >>= 1;
272             if (data_in()) {
273                 data |= 0x80;
274                 parity++;
275             }
276             break;
277         case PARITY:
278             if (data_in()) {
279                 if (!(parity & 0x01))
280                     goto ERROR;
281             } else {
282                 if (parity & 0x01)
283                     goto ERROR;
284             }
285             break;
286         case STOP:
287             if (!data_in())
288                 goto ERROR;
289             pbuf_enqueue(data);
290             goto DONE;
291             break;
292         default:
293             goto ERROR;
294     }
295     goto RETURN;
296 ERROR:
297     DEBUGP(0x0F);
298     inhibit();
299     ps2_error = state;
300 DONE:
301     state = INIT;
302     data = 0;
303     parity = 1;
304 RETURN:
305     return;
306 }
307 #endif
308
309
310 static void ps2_reset(void)
311 {
312     ps2_host_send(0xFF);
313 }
314
315 /* send LED state to keyboard */
316 void ps2_host_set_led(uint8_t led)
317 {
318     ps2_host_send(0xED);
319     ps2_host_send(led);
320 }
321
322
323 /* called after start bit comes */
324 static uint8_t recv_data(void)
325 {
326     uint8_t data = 0;
327     bool parity = true;
328     ps2_error = PS2_ERR_NONE;
329
330     /* start bit [1] */
331     WAIT(clock_lo, 1, 1);
332     WAIT(data_lo, 1, 2);
333     WAIT(clock_hi, 50, 3);
334
335     /* data [2-9] */
336     for (uint8_t i = 0; i < 8; i++) {
337         WAIT(clock_lo, 50, 4);
338         if (data_in()) {
339             parity = !parity;
340             data |= (1<<i);
341         }
342         WAIT(clock_hi, 50, 5);
343     }
344
345     /* parity [10] */
346     WAIT(clock_lo, 50, 6);
347     if (data_in() != parity) {
348         ps2_error = PS2_ERR_PARITY;
349         goto ERROR;
350     }
351     WAIT(clock_hi, 50, 7);
352
353     /* stop bit [11] */
354     WAIT(clock_lo, 50, 8);
355     WAIT(data_hi, 1, 9);
356     WAIT(clock_hi, 50, 10);
357
358     return data;
359 ERROR:
360     return 0;
361 }
362
363 static inline void clock_lo()
364 {
365     PS2_CLOCK_PORT &= ~(1<<PS2_CLOCK_BIT);
366     PS2_CLOCK_DDR  |=  (1<<PS2_CLOCK_BIT);
367 }
368 static inline void clock_hi()
369 {
370     /* input with pull up */
371     PS2_CLOCK_DDR  &= ~(1<<PS2_CLOCK_BIT);
372     PS2_CLOCK_PORT |=  (1<<PS2_CLOCK_BIT);
373 }
374 static inline bool clock_in()
375 {
376     PS2_CLOCK_DDR  &= ~(1<<PS2_CLOCK_BIT);
377     PS2_CLOCK_PORT |=  (1<<PS2_CLOCK_BIT);
378     _delay_us(1);
379     return PS2_CLOCK_PIN&(1<<PS2_CLOCK_BIT);
380 }
381 static inline void data_lo()
382 {
383     PS2_DATA_PORT &= ~(1<<PS2_DATA_BIT);
384     PS2_DATA_DDR  |=  (1<<PS2_DATA_BIT);
385 }
386 static inline void data_hi()
387 {
388     /* input with pull up */
389     PS2_DATA_DDR  &= ~(1<<PS2_DATA_BIT);
390     PS2_DATA_PORT |=  (1<<PS2_DATA_BIT);
391 }
392 static inline bool data_in()
393 {
394     PS2_DATA_DDR  &= ~(1<<PS2_DATA_BIT);
395     PS2_DATA_PORT |=  (1<<PS2_DATA_BIT);
396     _delay_us(1);
397     return PS2_DATA_PIN&(1<<PS2_DATA_BIT);
398 }
399
400 static inline uint16_t wait_clock_lo(uint16_t us)
401 {
402     while (clock_in()  && us) { asm(""); _delay_us(1); us--; }
403     return us;
404 }
405 static inline uint16_t wait_clock_hi(uint16_t us)
406 {
407     while (!clock_in() && us) { asm(""); _delay_us(1); us--; }
408     return us;
409 }
410 static inline uint16_t wait_data_lo(uint16_t us)
411 {
412     while (data_in() && us)  { asm(""); _delay_us(1); us--; }
413     return us;
414 }
415 static inline uint16_t wait_data_hi(uint16_t us)
416 {
417     while (!data_in() && us)  { asm(""); _delay_us(1); us--; }
418     return us;
419 }
420
421 /* idle state that device can send */
422 static inline void idle(void)
423 {
424     clock_hi();
425     data_hi();
426 }
427
428 /* inhibit device to send */
429 static inline void inhibit(void)
430 {
431     clock_lo();
432     data_hi();
433 }