]> git.gir.st - tmk_keyboard.git/blob - ps2.c
added copyright and license notice.
[tmk_keyboard.git] / ps2.c
1 /*
2 Copyright 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
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 static uint8_t recv_data(void);
47 static inline void clock_lo(void);
48 static inline void clock_hi(void);
49 static inline bool clock_in(void);
50 static inline void data_lo(void);
51 static inline void data_hi(void);
52 static inline bool data_in(void);
53 static inline uint16_t wait_clock_lo(uint16_t us);
54 static inline uint16_t wait_clock_hi(uint16_t us);
55 static inline uint16_t wait_data_lo(uint16_t us);
56 static inline uint16_t wait_data_hi(uint16_t us);
57 static inline void idle(void);
58 static inline void inhibit(void);
59
60
61 /*
62 Primitive PS/2 Library for AVR
63 ==============================
64 Host side is only supported now.
65
66
67 I/O control
68 -----------
69 High state is asserted by input with pull up.
70
71
72 PS/2 References
73 ---------------
74 http://www.computer-engineering.org/ps2protocol/
75 http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
76 */
77
78
79 #define WAIT(stat, us, err) do { \
80 if (!wait_##stat(us)) { \
81 ps2_error = err; \
82 goto ERROR; \
83 } \
84 } while (0)
85
86
87 uint8_t ps2_error = PS2_ERR_NONE;
88
89
90 void ps2_host_init(void)
91 {
92 #ifdef PS2_INT_ENABLE
93 PS2_INT_ENABLE();
94 idle();
95 #else
96 inhibit();
97 #endif
98 }
99
100 // TODO: send using interrupt if available
101 uint8_t ps2_host_send(uint8_t data)
102 {
103 uint8_t res = 0;
104 bool parity = true;
105 ps2_error = PS2_ERR_NONE;
106 #ifdef PS2_INT_DISABLE
107 PS2_INT_DISABLE();
108 #endif
109 /* terminate a transmission if we have */
110 inhibit();
111 _delay_us(100);
112
113 /* start bit [1] */
114 data_lo();
115 clock_hi();
116 WAIT(clock_lo, 15000, 1);
117 /* data [2-9] */
118 for (uint8_t i = 0; i < 8; i++) {
119 _delay_us(15);
120 if (data&(1<<i)) {
121 parity = !parity;
122 data_hi();
123 } else {
124 data_lo();
125 }
126 WAIT(clock_hi, 50, 2);
127 WAIT(clock_lo, 50, 3);
128 }
129 /* parity [10] */
130 _delay_us(15);
131 if (parity) { data_hi(); } else { data_lo(); }
132 WAIT(clock_hi, 50, 4);
133 WAIT(clock_lo, 50, 5);
134 /* stop bit [11] */
135 _delay_us(15);
136 data_hi();
137 /* ack [12] */
138 WAIT(data_lo, 50, 6);
139 WAIT(clock_lo, 50, 7);
140
141 /* wait for idle state */
142 WAIT(clock_hi, 50, 8);
143 WAIT(data_hi, 50, 9);
144
145 res = ps2_host_recv_response();
146 ERROR:
147 #ifdef PS2_INT_ENABLE
148 PS2_INT_ENABLE();
149 idle();
150 #else
151 inhibit();
152 #endif
153 return res;
154 }
155
156 /* receive data when host want else inhibit communication */
157 uint8_t ps2_host_recv_response(void)
158 {
159 uint8_t data = 0;
160
161 /* terminate a transmission if we have */
162 inhibit();
163 _delay_us(100);
164
165 /* release lines(idle state) */
166 idle();
167
168 /* wait start bit */
169 wait_clock_lo(2000);
170 data = recv_data();
171
172 inhibit();
173 return data;
174 }
175
176 #ifndef PS2_INT_VECT
177 uint8_t ps2_host_recv(void)
178 {
179 return ps2_host_recv_response();
180 }
181 #else
182 /* ring buffer to store ps/2 key data */
183 #define PBUF_SIZE 8
184 static uint8_t pbuf[PBUF_SIZE];
185 static uint8_t pbuf_head = 0;
186 static uint8_t pbuf_tail = 0;
187 static inline void pbuf_enqueue(uint8_t data)
188 {
189 if (!data)
190 return;
191
192 uint8_t sreg = SREG;
193 cli();
194 uint8_t next = (pbuf_head + 1) % PBUF_SIZE;
195 if (next != pbuf_tail) {
196 pbuf[pbuf_head] = data;
197 pbuf_head = next;
198 } else {
199 debug("pbuf: full\n");
200 }
201 SREG = sreg;
202 }
203 static inline uint8_t pbuf_dequeue(void)
204 {
205 uint8_t val = 0;
206
207 uint8_t sreg = SREG;
208 cli();
209 if (pbuf_head != pbuf_tail) {
210 val = pbuf[pbuf_tail];
211 pbuf_tail = (pbuf_tail + 1) % PBUF_SIZE;
212 }
213 SREG = sreg;
214
215 return val;
216 }
217
218 /* get data received by interrupt */
219 uint8_t ps2_host_recv(void)
220 {
221 if (ps2_error) {
222 print("x");
223 phex(ps2_error);
224 ps2_host_send(0xFE); // request to resend
225 ps2_error = PS2_ERR_NONE;
226 }
227 idle();
228 return pbuf_dequeue();
229 }
230
231 #if 0
232 #define DEBUGP_INIT() do { DDRC = 0xFF; } while (0)
233 #define DEBUGP(x) do { PORTC = x; } while (0)
234 #else
235 #define DEBUGP_INIT()
236 #define DEBUGP(x)
237 #endif
238 ISR(PS2_INT_VECT)
239 {
240 static enum {
241 INIT,
242 START,
243 BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7,
244 PARITY,
245 STOP,
246 } state = INIT;
247 static uint8_t data = 0;
248 static uint8_t parity = 1;
249
250 // TODO: abort if elapse 100us from previous interrupt
251
252 // return unless falling edge
253 if (clock_in()) {
254 goto RETURN;
255 }
256
257 state++;
258 DEBUGP(state);
259 switch (state) {
260 case START:
261 if (data_in())
262 goto ERROR;
263 break;
264 case BIT0:
265 case BIT1:
266 case BIT2:
267 case BIT3:
268 case BIT4:
269 case BIT5:
270 case BIT6:
271 case BIT7:
272 data >>= 1;
273 if (data_in()) {
274 data |= 0x80;
275 parity++;
276 }
277 break;
278 case PARITY:
279 if (data_in()) {
280 if (!(parity & 0x01))
281 goto ERROR;
282 } else {
283 if (parity & 0x01)
284 goto ERROR;
285 }
286 break;
287 case STOP:
288 if (!data_in())
289 goto ERROR;
290 pbuf_enqueue(data);
291 goto DONE;
292 break;
293 default:
294 goto ERROR;
295 }
296 goto RETURN;
297 ERROR:
298 DEBUGP(0x0F);
299 inhibit();
300 ps2_error = state;
301 DONE:
302 state = INIT;
303 data = 0;
304 parity = 1;
305 RETURN:
306 return;
307 }
308 #endif
309
310
311 static void ps2_reset(void)
312 {
313 ps2_host_send(0xFF);
314 }
315
316 /* send LED state to keyboard */
317 void ps2_host_set_led(uint8_t led)
318 {
319 ps2_host_send(0xED);
320 ps2_host_send(led);
321 }
322
323
324 /* called after start bit comes */
325 static uint8_t recv_data(void)
326 {
327 uint8_t data = 0;
328 bool parity = true;
329 ps2_error = PS2_ERR_NONE;
330
331 /* start bit [1] */
332 WAIT(clock_lo, 1, 1);
333 WAIT(data_lo, 1, 2);
334 WAIT(clock_hi, 50, 3);
335
336 /* data [2-9] */
337 for (uint8_t i = 0; i < 8; i++) {
338 WAIT(clock_lo, 50, 4);
339 if (data_in()) {
340 parity = !parity;
341 data |= (1<<i);
342 }
343 WAIT(clock_hi, 50, 5);
344 }
345
346 /* parity [10] */
347 WAIT(clock_lo, 50, 6);
348 if (data_in() != parity) {
349 ps2_error = PS2_ERR_PARITY;
350 goto ERROR;
351 }
352 WAIT(clock_hi, 50, 7);
353
354 /* stop bit [11] */
355 WAIT(clock_lo, 50, 8);
356 WAIT(data_hi, 1, 9);
357 WAIT(clock_hi, 50, 10);
358
359 return data;
360 ERROR:
361 return 0;
362 }
363
364 static inline void clock_lo()
365 {
366 PS2_CLOCK_PORT &= ~(1<<PS2_CLOCK_BIT);
367 PS2_CLOCK_DDR |= (1<<PS2_CLOCK_BIT);
368 }
369 static inline void clock_hi()
370 {
371 /* input with pull up */
372 PS2_CLOCK_DDR &= ~(1<<PS2_CLOCK_BIT);
373 PS2_CLOCK_PORT |= (1<<PS2_CLOCK_BIT);
374 }
375 static inline bool clock_in()
376 {
377 PS2_CLOCK_DDR &= ~(1<<PS2_CLOCK_BIT);
378 PS2_CLOCK_PORT |= (1<<PS2_CLOCK_BIT);
379 _delay_us(1);
380 return PS2_CLOCK_PIN&(1<<PS2_CLOCK_BIT);
381 }
382 static inline void data_lo()
383 {
384 PS2_DATA_PORT &= ~(1<<PS2_DATA_BIT);
385 PS2_DATA_DDR |= (1<<PS2_DATA_BIT);
386 }
387 static inline void data_hi()
388 {
389 /* input with pull up */
390 PS2_DATA_DDR &= ~(1<<PS2_DATA_BIT);
391 PS2_DATA_PORT |= (1<<PS2_DATA_BIT);
392 }
393 static inline bool data_in()
394 {
395 PS2_DATA_DDR &= ~(1<<PS2_DATA_BIT);
396 PS2_DATA_PORT |= (1<<PS2_DATA_BIT);
397 _delay_us(1);
398 return PS2_DATA_PIN&(1<<PS2_DATA_BIT);
399 }
400
401 static inline uint16_t wait_clock_lo(uint16_t us)
402 {
403 while (clock_in() && us) { asm(""); _delay_us(1); us--; }
404 return us;
405 }
406 static inline uint16_t wait_clock_hi(uint16_t us)
407 {
408 while (!clock_in() && us) { asm(""); _delay_us(1); us--; }
409 return us;
410 }
411 static inline uint16_t wait_data_lo(uint16_t us)
412 {
413 while (data_in() && us) { asm(""); _delay_us(1); us--; }
414 return us;
415 }
416 static inline uint16_t wait_data_hi(uint16_t us)
417 {
418 while (!data_in() && us) { asm(""); _delay_us(1); us--; }
419 return us;
420 }
421
422 /* idle state that device can send */
423 static inline void idle(void)
424 {
425 clock_hi();
426 data_hi();
427 }
428
429 /* inhibit device to send */
430 static inline void inhibit(void)
431 {
432 clock_lo();
433 data_hi();
434 }
Imprint / Impressum