aboutsummaryrefslogtreecommitdiff
path: root/protocol/ps2.c
diff options
context:
space:
mode:
Diffstat (limited to 'protocol/ps2.c')
-rw-r--r--protocol/ps2.c434
1 files changed, 434 insertions, 0 deletions
diff --git a/protocol/ps2.c b/protocol/ps2.c
new file mode 100644
index 000000000..8a0591621
--- /dev/null
+++ b/protocol/ps2.c
@@ -0,0 +1,434 @@
1/*
2Copyright 2010,2011 Jun WAKO <wakojun@gmail.com>
3
4This software is licensed with a Modified BSD License.
5All of this is supposed to be Free Software, Open Source, DFSG-free,
6GPL-compatible, and OK to use in both free and proprietary applications.
7Additions and corrections to this file are welcome.
8
9
10Redistribution and use in source and binary forms, with or without
11modification, 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
25THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
29LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35POSSIBILITY 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
46static uint8_t recv_data(void);
47static inline void clock_lo(void);
48static inline void clock_hi(void);
49static inline bool clock_in(void);
50static inline void data_lo(void);
51static inline void data_hi(void);
52static inline bool data_in(void);
53static inline uint16_t wait_clock_lo(uint16_t us);
54static inline uint16_t wait_clock_hi(uint16_t us);
55static inline uint16_t wait_data_lo(uint16_t us);
56static inline uint16_t wait_data_hi(uint16_t us);
57static inline void idle(void);
58static inline void inhibit(void);
59
60
61/*
62Primitive PS/2 Library for AVR
63==============================
64Host side is only supported now.
65
66
67I/O control
68-----------
69High state is asserted by input with pull up.
70
71
72PS/2 References
73---------------
74http://www.computer-engineering.org/ps2protocol/
75http://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
87uint8_t ps2_error = PS2_ERR_NONE;
88
89
90void 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
101uint8_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();
146ERROR:
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 */
157uint8_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
177uint8_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
184static uint8_t pbuf[PBUF_SIZE];
185static uint8_t pbuf_head = 0;
186static uint8_t pbuf_tail = 0;
187static 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}
203static 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 */
219uint8_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
238ISR(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;
297ERROR:
298 DEBUGP(0x0F);
299 inhibit();
300 ps2_error = state;
301DONE:
302 state = INIT;
303 data = 0;
304 parity = 1;
305RETURN:
306 return;
307}
308#endif
309
310
311static void ps2_reset(void)
312{
313 ps2_host_send(0xFF);
314}
315
316/* send LED state to keyboard */
317void 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 */
325static 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;
360ERROR:
361 return 0;
362}
363
364static inline void clock_lo()
365{
366 PS2_CLOCK_PORT &= ~(1<<PS2_CLOCK_BIT);
367 PS2_CLOCK_DDR |= (1<<PS2_CLOCK_BIT);
368}
369static 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}
375static 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}
382static inline void data_lo()
383{
384 PS2_DATA_PORT &= ~(1<<PS2_DATA_BIT);
385 PS2_DATA_DDR |= (1<<PS2_DATA_BIT);
386}
387static 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}
393static 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
401static inline uint16_t wait_clock_lo(uint16_t us)
402{
403 while (clock_in() && us) { asm(""); _delay_us(1); us--; }
404 return us;
405}
406static inline uint16_t wait_clock_hi(uint16_t us)
407{
408 while (!clock_in() && us) { asm(""); _delay_us(1); us--; }
409 return us;
410}
411static inline uint16_t wait_data_lo(uint16_t us)
412{
413 while (data_in() && us) { asm(""); _delay_us(1); us--; }
414 return us;
415}
416static 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 */
423static inline void idle(void)
424{
425 clock_hi();
426 data_hi();
427}
428
429/* inhibit device to send */
430static inline void inhibit(void)
431{
432 clock_lo();
433 data_hi();
434}