]> git.gir.st - tmk_keyboard.git/blob - ps2.c
added initial V-USB support for HHKB
[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 }
Imprint / Impressum