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