]>
git.gir.st - tmk_keyboard.git/blob - protocol/ps2.c
2 Copyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com>
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.
10 Redistribution and use in source and binary forms, with or without
11 modification, are permitted provided that the following conditions are met:
13 * Redistributions of source code must retain the above copyright
14 notice, this list of conditions and the following disclaimer.
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
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.
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.
40 #include <avr/interrupt.h>
41 #include <util/delay.h>
47 static uint8_t recv_data(void);
49 static inline void clock_lo(void);
50 static inline void clock_hi(void);
51 static inline bool clock_in(void);
52 static inline void data_lo(void);
53 static inline void data_hi(void);
54 static inline bool data_in(void);
55 static inline uint16_t wait_clock_lo(uint16_t us
);
56 static inline uint16_t wait_clock_hi(uint16_t us
);
57 static inline uint16_t wait_data_lo(uint16_t us
);
58 static inline uint16_t wait_data_hi(uint16_t us
);
59 static inline void idle(void);
60 static inline void inhibit(void);
64 Primitive PS/2 Library for AVR
65 ==============================
66 Host side is only supported now.
71 High state is asserted by input with pull up.
76 http://www.computer-engineering.org/ps2protocol/
77 http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
81 #define WAIT(stat, us, err) do { \
82 if (!wait_##stat(us)) { \
89 uint8_t ps2_error
= PS2_ERR_NONE
;
92 void ps2_host_init(void)
94 // POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)
106 // TODO: send using interrupt if available
107 uint8_t ps2_host_send(uint8_t data
)
111 ps2_error
= PS2_ERR_NONE
;
115 /* terminate a transmission if we have */
117 _delay_us(200); // at least 100us
122 WAIT(clock_lo
, 20000, 10); // may take 15ms at most until device starts clocking
124 for (uint8_t i
= 0; i
< 8; i
++) {
132 WAIT(clock_hi
, 50, 2);
133 WAIT(clock_lo
, 50, 3);
137 if (parity
) { data_hi(); } else { data_lo(); }
138 WAIT(clock_hi
, 50, 4);
139 WAIT(clock_lo
, 50, 5);
144 WAIT(data_lo
, 50, 6);
145 WAIT(clock_lo
, 50, 7);
147 /* wait for idle state */
148 WAIT(clock_hi
, 50, 8);
149 WAIT(data_hi
, 50, 9);
154 res
= ps2_host_recv_response();
166 /* receive data when host want else inhibit communication */
167 uint8_t ps2_host_recv_response(void)
169 // Command might take 20ms to response([3]p.21)
170 // TrackPoint might take 25ms ([5]2.7)
173 while (try-- && (data
= ps2_host_recv())) ;
179 uint8_t ps2_host_recv(void)
183 /* release lines(idle state) */
187 wait_clock_lo(100); // TODO: this is enough?
194 /* ring buffer to store ps/2 key data */
196 static uint8_t pbuf
[PBUF_SIZE
];
197 static uint8_t pbuf_head
= 0;
198 static uint8_t pbuf_tail
= 0;
199 static inline void pbuf_enqueue(uint8_t data
)
203 uint8_t next
= (pbuf_head
+ 1) % PBUF_SIZE
;
204 if (next
!= pbuf_tail
) {
205 pbuf
[pbuf_head
] = data
;
208 debug("pbuf: full\n");
212 static inline uint8_t pbuf_dequeue(void)
218 if (pbuf_head
!= pbuf_tail
) {
219 val
= pbuf
[pbuf_tail
];
220 pbuf_tail
= (pbuf_tail
+ 1) % PBUF_SIZE
;
226 static inline bool pbuf_has_data(void)
230 bool has_data
= (pbuf_head
!= pbuf_tail
);
234 static inline void pbuf_clear(void)
238 pbuf_head
= pbuf_tail
= 0;
242 /* get data received by interrupt */
243 uint8_t ps2_host_recv(void)
245 return pbuf_dequeue();
248 uint8_t ps2_host_recv_response(void)
250 while (!pbuf_has_data()) ;
251 return pbuf_dequeue();
259 BIT0
, BIT1
, BIT2
, BIT3
, BIT4
, BIT5
, BIT6
, BIT7
,
263 static uint8_t data
= 0;
264 static uint8_t parity
= 1;
266 // TODO: abort if elapse 100us from previous interrupt
268 // return unless falling edge
295 if (!(parity
& 0x01))
326 /* send LED state to keyboard */
327 void ps2_host_set_led(uint8_t led
)
335 /* called after start bit comes */
336 static uint8_t recv_data(void)
340 ps2_error
= PS2_ERR_NONE
;
343 WAIT(clock_lo
, 1, 1);
345 WAIT(clock_hi
, 50, 3);
348 for (uint8_t i
= 0; i
< 8; i
++) {
349 WAIT(clock_lo
, 50, 4);
354 WAIT(clock_hi
, 50, 5);
358 WAIT(clock_lo
, 50, 6);
359 if (data_in() != parity
) {
360 ps2_error
= PS2_ERR_PARITY
;
363 WAIT(clock_hi
, 50, 7);
366 WAIT(clock_lo
, 50, 8);
368 WAIT(clock_hi
, 50, 10);
376 static inline void clock_lo()
378 PS2_CLOCK_PORT
&= ~(1<<PS2_CLOCK_BIT
);
379 PS2_CLOCK_DDR
|= (1<<PS2_CLOCK_BIT
);
381 static inline void clock_hi()
383 /* input with pull up */
384 PS2_CLOCK_DDR
&= ~(1<<PS2_CLOCK_BIT
);
385 PS2_CLOCK_PORT
|= (1<<PS2_CLOCK_BIT
);
387 static inline bool clock_in()
389 PS2_CLOCK_DDR
&= ~(1<<PS2_CLOCK_BIT
);
390 PS2_CLOCK_PORT
|= (1<<PS2_CLOCK_BIT
);
392 return PS2_CLOCK_PIN
&(1<<PS2_CLOCK_BIT
);
394 static inline void data_lo()
396 PS2_DATA_PORT
&= ~(1<<PS2_DATA_BIT
);
397 PS2_DATA_DDR
|= (1<<PS2_DATA_BIT
);
399 static inline void data_hi()
401 /* input with pull up */
402 PS2_DATA_DDR
&= ~(1<<PS2_DATA_BIT
);
403 PS2_DATA_PORT
|= (1<<PS2_DATA_BIT
);
405 static inline bool data_in()
407 PS2_DATA_DDR
&= ~(1<<PS2_DATA_BIT
);
408 PS2_DATA_PORT
|= (1<<PS2_DATA_BIT
);
410 return PS2_DATA_PIN
&(1<<PS2_DATA_BIT
);
413 static inline uint16_t wait_clock_lo(uint16_t us
)
415 while (clock_in() && us
) { asm(""); _delay_us(1); us
--; }
418 static inline uint16_t wait_clock_hi(uint16_t us
)
420 while (!clock_in() && us
) { asm(""); _delay_us(1); us
--; }
423 static inline uint16_t wait_data_lo(uint16_t us
)
425 while (data_in() && us
) { asm(""); _delay_us(1); us
--; }
428 static inline uint16_t wait_data_hi(uint16_t us
)
430 while (!data_in() && us
) { asm(""); _delay_us(1); us
--; }
434 /* idle state that device can send */
435 static inline void idle(void)
441 /* inhibit device to send */
442 static inline void inhibit(void)
451 * [1] The PS/2 Mouse/Keyboard Protocol
452 * http://www.computer-engineering.org/ps2protocol/
453 * Concise and thorough primer of PS/2 protocol.
455 * [2] Keyboard and Auxiliary Device Controller
456 * http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
457 * Signal Timing and Format
459 * [3] Keyboards(101- and 102-key)
460 * http://www.mcamafia.de/pdf/ibm_hitrc11.pdf
461 * Keyboard Layout, Scan Code Set, POR, and Commands.
463 * [4] PS/2 Reference Manuals
464 * http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
465 * Collection of IBM Personal System/2 documents.
467 * [5] TrackPoint Engineering Specifications for version 3E
468 * https://web.archive.org/web/20100526161812/http://wwwcssrv.almaden.ibm.com/trackpoint/download.html