aboutsummaryrefslogtreecommitdiff
path: root/drivers/ps2
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ps2')
-rw-r--r--drivers/ps2/ps2.h139
-rw-r--r--drivers/ps2/ps2_busywait.c187
-rw-r--r--drivers/ps2/ps2_interrupt.c340
-rw-r--r--drivers/ps2/ps2_io.h11
-rw-r--r--drivers/ps2/ps2_mouse.c270
-rw-r--r--drivers/ps2/ps2_mouse.h177
6 files changed, 1124 insertions, 0 deletions
diff --git a/drivers/ps2/ps2.h b/drivers/ps2/ps2.h
new file mode 100644
index 000000000..f12319285
--- /dev/null
+++ b/drivers/ps2/ps2.h
@@ -0,0 +1,139 @@
1/*
2Copyright 2010,2011,2012,2013 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#pragma once
39
40#include <stdbool.h>
41#include "wait.h"
42#include "ps2_io.h"
43#include "print.h"
44
45/*
46 * Primitive PS/2 Library for AVR
47 *
48 * PS/2 Resources
49 * --------------
50 * [1] The PS/2 Mouse/Keyboard Protocol
51 * http://www.computer-engineering.org/ps2protocol/
52 * Concise and thorough primer of PS/2 protocol.
53 *
54 * [2] Keyboard and Auxiliary Device Controller
55 * http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
56 * Signal Timing and Format
57 *
58 * [3] Keyboards(101- and 102-key)
59 * http://www.mcamafia.de/pdf/ibm_hitrc11.pdf
60 * Keyboard Layout, Scan Code Set, POR, and Commands.
61 *
62 * [4] PS/2 Reference Manuals
63 * http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
64 * Collection of IBM Personal System/2 documents.
65 *
66 * [5] TrackPoint Engineering Specifications for version 3E
67 * https://web.archive.org/web/20100526161812/http://wwwcssrv.almaden.ibm.com/trackpoint/download.html
68 */
69#define PS2_ACK 0xFA
70#define PS2_RESEND 0xFE
71#define PS2_SET_LED 0xED
72
73// TODO: error numbers
74#define PS2_ERR_NONE 0
75#define PS2_ERR_STARTBIT1 1
76#define PS2_ERR_STARTBIT2 2
77#define PS2_ERR_STARTBIT3 3
78#define PS2_ERR_PARITY 0x10
79#define PS2_ERR_NODATA 0x20
80
81#define PS2_LED_SCROLL_LOCK 0
82#define PS2_LED_NUM_LOCK 1
83#define PS2_LED_CAPS_LOCK 2
84
85extern uint8_t ps2_error;
86
87void ps2_host_init(void);
88uint8_t ps2_host_send(uint8_t data);
89uint8_t ps2_host_recv_response(void);
90uint8_t ps2_host_recv(void);
91void ps2_host_set_led(uint8_t usb_led);
92
93/*--------------------------------------------------------------------
94 * static functions
95 *------------------------------------------------------------------*/
96static inline uint16_t wait_clock_lo(uint16_t us) {
97 while (clock_in() && us) {
98 asm("");
99 wait_us(1);
100 us--;
101 }
102 return us;
103}
104static inline uint16_t wait_clock_hi(uint16_t us) {
105 while (!clock_in() && us) {
106 asm("");
107 wait_us(1);
108 us--;
109 }
110 return us;
111}
112static inline uint16_t wait_data_lo(uint16_t us) {
113 while (data_in() && us) {
114 asm("");
115 wait_us(1);
116 us--;
117 }
118 return us;
119}
120static inline uint16_t wait_data_hi(uint16_t us) {
121 while (!data_in() && us) {
122 asm("");
123 wait_us(1);
124 us--;
125 }
126 return us;
127}
128
129/* idle state that device can send */
130static inline void idle(void) {
131 clock_hi();
132 data_hi();
133}
134
135/* inhibit device to send */
136static inline void inhibit(void) {
137 clock_lo();
138 data_hi();
139}
diff --git a/drivers/ps2/ps2_busywait.c b/drivers/ps2/ps2_busywait.c
new file mode 100644
index 000000000..983194eea
--- /dev/null
+++ b/drivers/ps2/ps2_busywait.c
@@ -0,0 +1,187 @@
1/*
2Copyright 2010,2011,2012,2013 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/*
39 * PS/2 protocol busywait version
40 */
41
42#include <stdbool.h>
43#include "wait.h"
44#include "ps2.h"
45#include "ps2_io.h"
46#include "debug.h"
47
48#define WAIT(stat, us, err) \
49 do { \
50 if (!wait_##stat(us)) { \
51 ps2_error = err; \
52 goto ERROR; \
53 } \
54 } while (0)
55
56uint8_t ps2_error = PS2_ERR_NONE;
57
58void ps2_host_init(void) {
59 clock_init();
60 data_init();
61
62 // POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)
63 wait_ms(2500);
64
65 inhibit();
66}
67
68uint8_t ps2_host_send(uint8_t data) {
69 bool parity = true;
70 ps2_error = PS2_ERR_NONE;
71
72 /* terminate a transmission if we have */
73 inhibit();
74 wait_us(100); // 100us [4]p.13, [5]p.50
75
76 /* 'Request to Send' and Start bit */
77 data_lo();
78 clock_hi();
79 WAIT(clock_lo, 10000, 10); // 10ms [5]p.50
80
81 /* Data bit */
82 for (uint8_t i = 0; i < 8; i++) {
83 wait_us(15);
84 if (data & (1 << i)) {
85 parity = !parity;
86 data_hi();
87 } else {
88 data_lo();
89 }
90 WAIT(clock_hi, 50, 2);
91 WAIT(clock_lo, 50, 3);
92 }
93
94 /* Parity bit */
95 wait_us(15);
96 if (parity) {
97 data_hi();
98 } else {
99 data_lo();
100 }
101 WAIT(clock_hi, 50, 4);
102 WAIT(clock_lo, 50, 5);
103
104 /* Stop bit */
105 wait_us(15);
106 data_hi();
107
108 /* Ack */
109 WAIT(data_lo, 50, 6);
110 WAIT(clock_lo, 50, 7);
111
112 /* wait for idle state */
113 WAIT(clock_hi, 50, 8);
114 WAIT(data_hi, 50, 9);
115
116 inhibit();
117 return ps2_host_recv_response();
118ERROR:
119 inhibit();
120 return 0;
121}
122
123/* receive data when host want else inhibit communication */
124uint8_t ps2_host_recv_response(void) {
125 // Command may take 25ms/20ms at most([5]p.46, [3]p.21)
126 // 250 * 100us(wait for start bit in ps2_host_recv)
127 uint8_t data = 0;
128 uint8_t try
129 = 250;
130 do {
131 data = ps2_host_recv();
132 } while (try --&&ps2_error);
133 return data;
134}
135
136/* called after start bit comes */
137uint8_t ps2_host_recv(void) {
138 uint8_t data = 0;
139 bool parity = true;
140 ps2_error = PS2_ERR_NONE;
141
142 /* release lines(idle state) */
143 idle();
144
145 /* start bit [1] */
146 WAIT(clock_lo, 100, 1); // TODO: this is enough?
147 WAIT(data_lo, 1, 2);
148 WAIT(clock_hi, 50, 3);
149
150 /* data [2-9] */
151 for (uint8_t i = 0; i < 8; i++) {
152 WAIT(clock_lo, 50, 4);
153 if (data_in()) {
154 parity = !parity;
155 data |= (1 << i);
156 }
157 WAIT(clock_hi, 50, 5);
158 }
159
160 /* parity [10] */
161 WAIT(clock_lo, 50, 6);
162 if (data_in() != parity) {
163 ps2_error = PS2_ERR_PARITY;
164 goto ERROR;
165 }
166 WAIT(clock_hi, 50, 7);
167
168 /* stop bit [11] */
169 WAIT(clock_lo, 50, 8);
170 WAIT(data_hi, 1, 9);
171 WAIT(clock_hi, 50, 10);
172
173 inhibit();
174 return data;
175ERROR:
176 if (ps2_error > PS2_ERR_STARTBIT3) {
177 xprintf("x%02X\n", ps2_error);
178 }
179 inhibit();
180 return 0;
181}
182
183/* send LED state to keyboard */
184void ps2_host_set_led(uint8_t led) {
185 ps2_host_send(0xED);
186 ps2_host_send(led);
187}
diff --git a/drivers/ps2/ps2_interrupt.c b/drivers/ps2/ps2_interrupt.c
new file mode 100644
index 000000000..70debd02f
--- /dev/null
+++ b/drivers/ps2/ps2_interrupt.c
@@ -0,0 +1,340 @@
1/*
2Copyright 2010,2011,2012,2013 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/*
39 * PS/2 protocol Pin interrupt version
40 */
41
42#include <stdbool.h>
43
44#if defined(__AVR__)
45# include <avr/interrupt.h>
46#elif defined(PROTOCOL_CHIBIOS) // TODO: or STM32 ?
47// chibiOS headers
48# include "ch.h"
49# include "hal.h"
50#endif
51
52#include "ps2.h"
53#include "ps2_io.h"
54#include "print.h"
55#include "wait.h"
56
57#define WAIT(stat, us, err) \
58 do { \
59 if (!wait_##stat(us)) { \
60 ps2_error = err; \
61 goto ERROR; \
62 } \
63 } while (0)
64
65uint8_t ps2_error = PS2_ERR_NONE;
66
67static inline uint8_t pbuf_dequeue(void);
68static inline void pbuf_enqueue(uint8_t data);
69static inline bool pbuf_has_data(void);
70static inline void pbuf_clear(void);
71
72#if defined(PROTOCOL_CHIBIOS)
73void ps2_interrupt_service_routine(void);
74void palCallback(void *arg) { ps2_interrupt_service_routine(); }
75
76# define PS2_INT_INIT() \
77 { palSetLineMode(PS2_CLOCK_PIN, PAL_MODE_INPUT); } \
78 while (0)
79# define PS2_INT_ON() \
80 { \
81 palEnableLineEvent(PS2_CLOCK_PIN, PAL_EVENT_MODE_FALLING_EDGE); \
82 palSetLineCallback(PS2_CLOCK_PIN, palCallback, NULL); \
83 } \
84 while (0)
85# define PS2_INT_OFF() \
86 { palDisableLineEvent(PS2_CLOCK_PIN); } \
87 while (0)
88#endif // PROTOCOL_CHIBIOS
89
90void ps2_host_init(void) {
91 idle();
92 PS2_INT_INIT();
93 PS2_INT_ON();
94 // POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)
95 // wait_ms(2500);
96}
97
98uint8_t ps2_host_send(uint8_t data) {
99 bool parity = true;
100 ps2_error = PS2_ERR_NONE;
101
102 PS2_INT_OFF();
103
104 /* terminate a transmission if we have */
105 inhibit();
106 wait_us(100); // 100us [4]p.13, [5]p.50
107
108 /* 'Request to Send' and Start bit */
109 data_lo();
110 clock_hi();
111 WAIT(clock_lo, 10000, 10); // 10ms [5]p.50
112
113 /* Data bit[2-9] */
114 for (uint8_t i = 0; i < 8; i++) {
115 if (data & (1 << i)) {
116 parity = !parity;
117 data_hi();
118 } else {
119 data_lo();
120 }
121 WAIT(clock_hi, 50, 2);
122 WAIT(clock_lo, 50, 3);
123 }
124
125 /* Parity bit */
126 wait_us(15);
127 if (parity) {
128 data_hi();
129 } else {
130 data_lo();
131 }
132 WAIT(clock_hi, 50, 4);
133 WAIT(clock_lo, 50, 5);
134
135 /* Stop bit */
136 wait_us(15);
137 data_hi();
138
139 /* Ack */
140 WAIT(data_lo, 50, 6);
141 WAIT(clock_lo, 50, 7);
142
143 /* wait for idle state */
144 WAIT(clock_hi, 50, 8);
145 WAIT(data_hi, 50, 9);
146
147 idle();
148 PS2_INT_ON();
149 return ps2_host_recv_response();
150ERROR:
151 idle();
152 PS2_INT_ON();
153 return 0;
154}
155
156uint8_t ps2_host_recv_response(void) {
157 // Command may take 25ms/20ms at most([5]p.46, [3]p.21)
158 uint8_t retry = 25;
159 while (retry-- && !pbuf_has_data()) {
160 wait_ms(1);
161 }
162 return pbuf_dequeue();
163}
164
165/* get data received by interrupt */
166uint8_t ps2_host_recv(void) {
167 if (pbuf_has_data()) {
168 ps2_error = PS2_ERR_NONE;
169 return pbuf_dequeue();
170 } else {
171 ps2_error = PS2_ERR_NODATA;
172 return 0;
173 }
174}
175
176void ps2_interrupt_service_routine(void) {
177 static enum {
178 INIT,
179 START,
180 BIT0,
181 BIT1,
182 BIT2,
183 BIT3,
184 BIT4,
185 BIT5,
186 BIT6,
187 BIT7,
188 PARITY,
189 STOP,
190 } state = INIT;
191 static uint8_t data = 0;
192 static uint8_t parity = 1;
193
194 // TODO: abort if elapse 100us from previous interrupt
195
196 // return unless falling edge
197 if (clock_in()) {
198 goto RETURN;
199 }
200
201 state++;
202 switch (state) {
203 case START:
204 if (data_in()) goto ERROR;
205 break;
206 case BIT0:
207 case BIT1:
208 case BIT2:
209 case BIT3:
210 case BIT4:
211 case BIT5:
212 case BIT6:
213 case BIT7:
214 data >>= 1;
215 if (data_in()) {
216 data |= 0x80;
217 parity++;
218 }
219 break;
220 case PARITY:
221 if (data_in()) {
222 if (!(parity & 0x01)) goto ERROR;
223 } else {
224 if (parity & 0x01) goto ERROR;
225 }
226 break;
227 case STOP:
228 if (!data_in()) goto ERROR;
229 pbuf_enqueue(data);
230 goto DONE;
231 break;
232 default:
233 goto ERROR;
234 }
235 goto RETURN;
236ERROR:
237 ps2_error = state;
238DONE:
239 state = INIT;
240 data = 0;
241 parity = 1;
242RETURN:
243 return;
244}
245
246#if defined(__AVR__)
247ISR(PS2_INT_VECT) { ps2_interrupt_service_routine(); }
248#endif
249
250/* send LED state to keyboard */
251void ps2_host_set_led(uint8_t led) {
252 ps2_host_send(0xED);
253 ps2_host_send(led);
254}
255
256/*--------------------------------------------------------------------
257 * Ring buffer to store scan codes from keyboard
258 *------------------------------------------------------------------*/
259#define PBUF_SIZE 32
260static uint8_t pbuf[PBUF_SIZE];
261static uint8_t pbuf_head = 0;
262static uint8_t pbuf_tail = 0;
263static inline void pbuf_enqueue(uint8_t data) {
264#if defined(__AVR__)
265 uint8_t sreg = SREG;
266 cli();
267#elif defined(PROTOCOL_CHIBIOS)
268 chSysLockFromISR();
269#endif
270
271 uint8_t next = (pbuf_head + 1) % PBUF_SIZE;
272 if (next != pbuf_tail) {
273 pbuf[pbuf_head] = data;
274 pbuf_head = next;
275 } else {
276 print("pbuf: full\n");
277 }
278
279#if defined(__AVR__)
280 SREG = sreg;
281#elif defined(PROTOCOL_CHIBIOS)
282 chSysUnlockFromISR();
283#endif
284}
285static inline uint8_t pbuf_dequeue(void) {
286 uint8_t val = 0;
287
288#if defined(__AVR__)
289 uint8_t sreg = SREG;
290 cli();
291#elif defined(PROTOCOL_CHIBIOS)
292 chSysLock();
293#endif
294
295 if (pbuf_head != pbuf_tail) {
296 val = pbuf[pbuf_tail];
297 pbuf_tail = (pbuf_tail + 1) % PBUF_SIZE;
298 }
299
300#if defined(__AVR__)
301 SREG = sreg;
302#elif defined(PROTOCOL_CHIBIOS)
303 chSysUnlock();
304#endif
305
306 return val;
307}
308static inline bool pbuf_has_data(void) {
309#if defined(__AVR__)
310 uint8_t sreg = SREG;
311 cli();
312#elif defined(PROTOCOL_CHIBIOS)
313 chSysLock();
314#endif
315
316 bool has_data = (pbuf_head != pbuf_tail);
317
318#if defined(__AVR__)
319 SREG = sreg;
320#elif defined(PROTOCOL_CHIBIOS)
321 chSysUnlock();
322#endif
323 return has_data;
324}
325static inline void pbuf_clear(void) {
326#if defined(__AVR__)
327 uint8_t sreg = SREG;
328 cli();
329#elif defined(PROTOCOL_CHIBIOS)
330 chSysLock();
331#endif
332
333 pbuf_head = pbuf_tail = 0;
334
335#if defined(__AVR__)
336 SREG = sreg;
337#elif defined(PROTOCOL_CHIBIOS)
338 chSysUnlock();
339#endif
340}
diff --git a/drivers/ps2/ps2_io.h b/drivers/ps2/ps2_io.h
new file mode 100644
index 000000000..de93cb7a3
--- /dev/null
+++ b/drivers/ps2/ps2_io.h
@@ -0,0 +1,11 @@
1#pragma once
2
3void clock_init(void);
4void clock_lo(void);
5void clock_hi(void);
6bool clock_in(void);
7
8void data_init(void);
9void data_lo(void);
10void data_hi(void);
11bool data_in(void);
diff --git a/drivers/ps2/ps2_mouse.c b/drivers/ps2/ps2_mouse.c
new file mode 100644
index 000000000..8a6668b41
--- /dev/null
+++ b/drivers/ps2/ps2_mouse.c
@@ -0,0 +1,270 @@
1/*
2Copyright 2011,2013 Jun Wako <wakojun@gmail.com>
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#include <stdbool.h>
19#include "ps2_mouse.h"
20#include "wait.h"
21#include "gpio.h"
22#include "host.h"
23#include "timer.h"
24#include "print.h"
25#include "report.h"
26#include "debug.h"
27#include "ps2.h"
28
29/* ============================= MACROS ============================ */
30
31static report_mouse_t mouse_report = {};
32
33static inline void ps2_mouse_print_report(report_mouse_t *mouse_report);
34static inline void ps2_mouse_convert_report_to_hid(report_mouse_t *mouse_report);
35static inline void ps2_mouse_clear_report(report_mouse_t *mouse_report);
36static inline void ps2_mouse_enable_scrolling(void);
37static inline void ps2_mouse_scroll_button_task(report_mouse_t *mouse_report);
38
39/* ============================= IMPLEMENTATION ============================ */
40
41/* supports only 3 button mouse at this time */
42void ps2_mouse_init(void) {
43 ps2_host_init();
44
45 wait_ms(PS2_MOUSE_INIT_DELAY); // wait for powering up
46
47 PS2_MOUSE_SEND(PS2_MOUSE_RESET, "ps2_mouse_init: sending reset");
48
49 PS2_MOUSE_RECEIVE("ps2_mouse_init: read BAT");
50 PS2_MOUSE_RECEIVE("ps2_mouse_init: read DevID");
51
52#ifdef PS2_MOUSE_USE_REMOTE_MODE
53 ps2_mouse_set_remote_mode();
54#else
55 ps2_mouse_enable_data_reporting();
56#endif
57
58#ifdef PS2_MOUSE_ENABLE_SCROLLING
59 ps2_mouse_enable_scrolling();
60#endif
61
62#ifdef PS2_MOUSE_USE_2_1_SCALING
63 ps2_mouse_set_scaling_2_1();
64#endif
65
66 ps2_mouse_init_user();
67}
68
69__attribute__((weak)) void ps2_mouse_init_user(void) {}
70
71__attribute__((weak)) void ps2_mouse_moved_user(report_mouse_t *mouse_report) {}
72
73void ps2_mouse_task(void) {
74 static uint8_t buttons_prev = 0;
75 extern int tp_buttons;
76
77 /* receives packet from mouse */
78 uint8_t rcv;
79 rcv = ps2_host_send(PS2_MOUSE_READ_DATA);
80 if (rcv == PS2_ACK) {
81 mouse_report.buttons = ps2_host_recv_response() | tp_buttons;
82 mouse_report.x = ps2_host_recv_response() * PS2_MOUSE_X_MULTIPLIER;
83 mouse_report.y = ps2_host_recv_response() * PS2_MOUSE_Y_MULTIPLIER;
84#ifdef PS2_MOUSE_ENABLE_SCROLLING
85 mouse_report.v = -(ps2_host_recv_response() & PS2_MOUSE_SCROLL_MASK) * PS2_MOUSE_V_MULTIPLIER;
86#endif
87 } else {
88 if (debug_mouse) print("ps2_mouse: fail to get mouse packet\n");
89 return;
90 }
91
92 /* if mouse moves or buttons state changes */
93 if (mouse_report.x || mouse_report.y || mouse_report.v || ((mouse_report.buttons ^ buttons_prev) & PS2_MOUSE_BTN_MASK)) {
94#ifdef PS2_MOUSE_DEBUG_RAW
95 // Used to debug raw ps2 bytes from mouse
96 ps2_mouse_print_report(&mouse_report);
97#endif
98 buttons_prev = mouse_report.buttons;
99 ps2_mouse_convert_report_to_hid(&mouse_report);
100#if PS2_MOUSE_SCROLL_BTN_MASK
101 ps2_mouse_scroll_button_task(&mouse_report);
102#endif
103 if (mouse_report.x || mouse_report.y || mouse_report.v) {
104 ps2_mouse_moved_user(&mouse_report);
105 }
106#ifdef PS2_MOUSE_DEBUG_HID
107 // Used to debug the bytes sent to the host
108 ps2_mouse_print_report(&mouse_report);
109#endif
110 host_mouse_send(&mouse_report);
111 }
112
113 ps2_mouse_clear_report(&mouse_report);
114}
115
116void ps2_mouse_disable_data_reporting(void) { PS2_MOUSE_SEND(PS2_MOUSE_DISABLE_DATA_REPORTING, "ps2 mouse disable data reporting"); }
117
118void ps2_mouse_enable_data_reporting(void) { PS2_MOUSE_SEND(PS2_MOUSE_ENABLE_DATA_REPORTING, "ps2 mouse enable data reporting"); }
119
120void ps2_mouse_set_remote_mode(void) {
121 PS2_MOUSE_SEND_SAFE(PS2_MOUSE_SET_REMOTE_MODE, "ps2 mouse set remote mode");
122 ps2_mouse_mode = PS2_MOUSE_REMOTE_MODE;
123}
124
125void ps2_mouse_set_stream_mode(void) {
126 PS2_MOUSE_SEND_SAFE(PS2_MOUSE_SET_STREAM_MODE, "ps2 mouse set stream mode");
127 ps2_mouse_mode = PS2_MOUSE_STREAM_MODE;
128}
129
130void ps2_mouse_set_scaling_2_1(void) { PS2_MOUSE_SEND_SAFE(PS2_MOUSE_SET_SCALING_2_1, "ps2 mouse set scaling 2:1"); }
131
132void ps2_mouse_set_scaling_1_1(void) { PS2_MOUSE_SEND_SAFE(PS2_MOUSE_SET_SCALING_1_1, "ps2 mouse set scaling 1:1"); }
133
134void ps2_mouse_set_resolution(ps2_mouse_resolution_t resolution) { PS2_MOUSE_SET_SAFE(PS2_MOUSE_SET_RESOLUTION, resolution, "ps2 mouse set resolution"); }
135
136void ps2_mouse_set_sample_rate(ps2_mouse_sample_rate_t sample_rate) { PS2_MOUSE_SET_SAFE(PS2_MOUSE_SET_SAMPLE_RATE, sample_rate, "ps2 mouse set sample rate"); }
137
138/* ============================= HELPERS ============================ */
139
140#define X_IS_NEG (mouse_report->buttons & (1 << PS2_MOUSE_X_SIGN))
141#define Y_IS_NEG (mouse_report->buttons & (1 << PS2_MOUSE_Y_SIGN))
142#define X_IS_OVF (mouse_report->buttons & (1 << PS2_MOUSE_X_OVFLW))
143#define Y_IS_OVF (mouse_report->buttons & (1 << PS2_MOUSE_Y_OVFLW))
144static inline void ps2_mouse_convert_report_to_hid(report_mouse_t *mouse_report) {
145 // PS/2 mouse data is '9-bit integer'(-256 to 255) which is comprised of sign-bit and 8-bit value.
146 // bit: 8 7 ... 0
147 // sign \8-bit/
148 //
149 // Meanwhile USB HID mouse indicates 8bit data(-127 to 127), note that -128 is not used.
150 //
151 // This converts PS/2 data into HID value. Use only -127-127 out of PS/2 9-bit.
152 mouse_report->x = X_IS_NEG ? ((!X_IS_OVF && -127 <= mouse_report->x && mouse_report->x <= -1) ? mouse_report->x : -127) : ((!X_IS_OVF && 0 <= mouse_report->x && mouse_report->x <= 127) ? mouse_report->x : 127);
153 mouse_report->y = Y_IS_NEG ? ((!Y_IS_OVF && -127 <= mouse_report->y && mouse_report->y <= -1) ? mouse_report->y : -127) : ((!Y_IS_OVF && 0 <= mouse_report->y && mouse_report->y <= 127) ? mouse_report->y : 127);
154
155#ifdef PS2_MOUSE_INVERT_BUTTONS
156 // swap left & right buttons
157 uint8_t needs_left = mouse_report->buttons & PS2_MOUSE_BTN_RIGHT;
158 uint8_t needs_right = mouse_report->buttons & PS2_MOUSE_BTN_LEFT;
159 mouse_report->buttons = (mouse_report->buttons & ~(PS2_MOUSE_BTN_MASK)) | (needs_left ? PS2_MOUSE_BTN_LEFT : 0) | (needs_right ? PS2_MOUSE_BTN_RIGHT : 0);
160#else
161 // remove sign and overflow flags
162 mouse_report->buttons &= PS2_MOUSE_BTN_MASK;
163#endif
164
165#ifdef PS2_MOUSE_INVERT_X
166 mouse_report->x = -mouse_report->x;
167#endif
168#ifndef PS2_MOUSE_INVERT_Y // NOTE if not!
169 // invert coordinate of y to conform to USB HID mouse
170 mouse_report->y = -mouse_report->y;
171#endif
172
173#ifdef PS2_MOUSE_ROTATE
174 int8_t x = mouse_report->x;
175 int8_t y = mouse_report->y;
176# if PS2_MOUSE_ROTATE == 90
177 mouse_report->x = y;
178 mouse_report->y = -x;
179# elif PS2_MOUSE_ROTATE == 180
180 mouse_report->x = -x;
181 mouse_report->y = -y;
182# elif PS2_MOUSE_ROTATE == 270
183 mouse_report->x = -y;
184 mouse_report->y = x;
185# endif
186#endif
187}
188
189static inline void ps2_mouse_clear_report(report_mouse_t *mouse_report) {
190 mouse_report->x = 0;
191 mouse_report->y = 0;
192 mouse_report->v = 0;
193 mouse_report->h = 0;
194 mouse_report->buttons = 0;
195}
196
197static inline void ps2_mouse_print_report(report_mouse_t *mouse_report) {
198 if (!debug_mouse) return;
199 print("ps2_mouse: [");
200 print_hex8(mouse_report->buttons);
201 print("|");
202 print_hex8((uint8_t)mouse_report->x);
203 print(" ");
204 print_hex8((uint8_t)mouse_report->y);
205 print(" ");
206 print_hex8((uint8_t)mouse_report->v);
207 print(" ");
208 print_hex8((uint8_t)mouse_report->h);
209 print("]\n");
210}
211
212static inline void ps2_mouse_enable_scrolling(void) {
213 PS2_MOUSE_SEND(PS2_MOUSE_SET_SAMPLE_RATE, "Initiaing scroll wheel enable: Set sample rate");
214 PS2_MOUSE_SEND(200, "200");
215 PS2_MOUSE_SEND(PS2_MOUSE_SET_SAMPLE_RATE, "Set sample rate");
216 PS2_MOUSE_SEND(100, "100");
217 PS2_MOUSE_SEND(PS2_MOUSE_SET_SAMPLE_RATE, "Set sample rate");
218 PS2_MOUSE_SEND(80, "80");
219 PS2_MOUSE_SEND(PS2_MOUSE_GET_DEVICE_ID, "Finished enabling scroll wheel");
220 wait_ms(20);
221}
222
223#define PRESS_SCROLL_BUTTONS mouse_report->buttons |= (PS2_MOUSE_SCROLL_BTN_MASK)
224#define RELEASE_SCROLL_BUTTONS mouse_report->buttons &= ~(PS2_MOUSE_SCROLL_BTN_MASK)
225static inline void ps2_mouse_scroll_button_task(report_mouse_t *mouse_report) {
226 static enum {
227 SCROLL_NONE,
228 SCROLL_BTN,
229 SCROLL_SENT,
230 } scroll_state = SCROLL_NONE;
231 static uint16_t scroll_button_time = 0;
232
233 if (PS2_MOUSE_SCROLL_BTN_MASK == (mouse_report->buttons & (PS2_MOUSE_SCROLL_BTN_MASK))) {
234 // All scroll buttons are pressed
235
236 if (scroll_state == SCROLL_NONE) {
237 scroll_button_time = timer_read();
238 scroll_state = SCROLL_BTN;
239 }
240
241 // If the mouse has moved, update the report to scroll instead of move the mouse
242 if (mouse_report->x || mouse_report->y) {
243 scroll_state = SCROLL_SENT;
244 mouse_report->v = -mouse_report->y / (PS2_MOUSE_SCROLL_DIVISOR_V);
245 mouse_report->h = mouse_report->x / (PS2_MOUSE_SCROLL_DIVISOR_H);
246 mouse_report->x = 0;
247 mouse_report->y = 0;
248#ifdef PS2_MOUSE_INVERT_H
249 mouse_report->h = -mouse_report->h;
250#endif
251#ifdef PS2_MOUSE_INVERT_V
252 mouse_report->v = -mouse_report->v;
253#endif
254 }
255 } else if (0 == (PS2_MOUSE_SCROLL_BTN_MASK & mouse_report->buttons)) {
256 // None of the scroll buttons are pressed
257
258#if PS2_MOUSE_SCROLL_BTN_SEND
259 if (scroll_state == SCROLL_BTN && timer_elapsed(scroll_button_time) < PS2_MOUSE_SCROLL_BTN_SEND) {
260 PRESS_SCROLL_BUTTONS;
261 host_mouse_send(mouse_report);
262 wait_ms(100);
263 RELEASE_SCROLL_BUTTONS;
264 }
265#endif
266 scroll_state = SCROLL_NONE;
267 }
268
269 RELEASE_SCROLL_BUTTONS;
270}
diff --git a/drivers/ps2/ps2_mouse.h b/drivers/ps2/ps2_mouse.h
new file mode 100644
index 000000000..c97c6c893
--- /dev/null
+++ b/drivers/ps2/ps2_mouse.h
@@ -0,0 +1,177 @@
1/*
2Copyright 2011 Jun Wako <wakojun@gmail.com>
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#pragma once
19
20#include <stdbool.h>
21#include "debug.h"
22#include "report.h"
23
24#define PS2_MOUSE_SEND(command, message) \
25 do { \
26 __attribute__((unused)) uint8_t rcv = ps2_host_send(command); \
27 if (debug_mouse) { \
28 print((message)); \
29 xprintf(" command: %X, result: %X, error: %X \n", command, rcv, ps2_error); \
30 } \
31 } while (0)
32
33#define PS2_MOUSE_SEND_SAFE(command, message) \
34 do { \
35 if (PS2_MOUSE_STREAM_MODE == ps2_mouse_mode) { \
36 ps2_mouse_disable_data_reporting(); \
37 } \
38 PS2_MOUSE_SEND(command, message); \
39 if (PS2_MOUSE_STREAM_MODE == ps2_mouse_mode) { \
40 ps2_mouse_enable_data_reporting(); \
41 } \
42 } while (0)
43
44#define PS2_MOUSE_SET_SAFE(command, value, message) \
45 do { \
46 if (PS2_MOUSE_STREAM_MODE == ps2_mouse_mode) { \
47 ps2_mouse_disable_data_reporting(); \
48 } \
49 PS2_MOUSE_SEND(command, message); \
50 PS2_MOUSE_SEND(value, "Sending value"); \
51 if (PS2_MOUSE_STREAM_MODE == ps2_mouse_mode) { \
52 ps2_mouse_enable_data_reporting(); \
53 } \
54 } while (0)
55
56#define PS2_MOUSE_RECEIVE(message) \
57 do { \
58 __attribute__((unused)) uint8_t rcv = ps2_host_recv_response(); \
59 if (debug_mouse) { \
60 print((message)); \
61 xprintf(" result: %X, error: %X \n", rcv, ps2_error); \
62 } \
63 } while (0)
64
65__attribute__((unused)) static enum ps2_mouse_mode_e {
66 PS2_MOUSE_STREAM_MODE,
67 PS2_MOUSE_REMOTE_MODE,
68} ps2_mouse_mode = PS2_MOUSE_STREAM_MODE;
69
70/*
71 * Data format:
72 * byte|7 6 5 4 3 2 1 0
73 * ----+----------------------------------------------------------------
74 * 0|[Yovflw][Xovflw][Ysign ][Xsign ][ 1 ][Middle][Right ][Left ]
75 * 1|[ X movement(0-255) ]
76 * 2|[ Y movement(0-255) ]
77 */
78#define PS2_MOUSE_BTN_MASK 0x07
79#define PS2_MOUSE_BTN_LEFT 0
80#define PS2_MOUSE_BTN_RIGHT 1
81#define PS2_MOUSE_BTN_MIDDLE 2
82#define PS2_MOUSE_X_SIGN 4
83#define PS2_MOUSE_Y_SIGN 5
84#define PS2_MOUSE_X_OVFLW 6
85#define PS2_MOUSE_Y_OVFLW 7
86
87/* mouse button to start scrolling; set 0 to disable scroll */
88#ifndef PS2_MOUSE_SCROLL_BTN_MASK
89# define PS2_MOUSE_SCROLL_BTN_MASK (1 << PS2_MOUSE_BTN_MIDDLE)
90#endif
91/* send button event when button is released within this value(ms); set 0 to disable */
92#ifndef PS2_MOUSE_SCROLL_BTN_SEND
93# define PS2_MOUSE_SCROLL_BTN_SEND 300
94#endif
95/* divide virtical and horizontal mouse move by this to convert to scroll move */
96#ifndef PS2_MOUSE_SCROLL_DIVISOR_V
97# define PS2_MOUSE_SCROLL_DIVISOR_V 2
98#endif
99#ifndef PS2_MOUSE_SCROLL_DIVISOR_H
100# define PS2_MOUSE_SCROLL_DIVISOR_H 2
101#endif
102/* multiply reported mouse values by these */
103#ifndef PS2_MOUSE_X_MULTIPLIER
104# define PS2_MOUSE_X_MULTIPLIER 1
105#endif
106#ifndef PS2_MOUSE_Y_MULTIPLIER
107# define PS2_MOUSE_Y_MULTIPLIER 1
108#endif
109#ifndef PS2_MOUSE_V_MULTIPLIER
110# define PS2_MOUSE_V_MULTIPLIER 1
111#endif
112/* For some mice this will need to be 0x0F */
113#ifndef PS2_MOUSE_SCROLL_MASK
114# define PS2_MOUSE_SCROLL_MASK 0xFF
115#endif
116#ifndef PS2_MOUSE_INIT_DELAY
117# define PS2_MOUSE_INIT_DELAY 1000
118#endif
119
120enum ps2_mouse_command_e {
121 PS2_MOUSE_RESET = 0xFF,
122 PS2_MOUSE_RESEND = 0xFE,
123 PS2_MOSUE_SET_DEFAULTS = 0xF6,
124 PS2_MOUSE_DISABLE_DATA_REPORTING = 0xF5,
125 PS2_MOUSE_ENABLE_DATA_REPORTING = 0xF4,
126 PS2_MOUSE_SET_SAMPLE_RATE = 0xF3,
127 PS2_MOUSE_GET_DEVICE_ID = 0xF2,
128 PS2_MOUSE_SET_REMOTE_MODE = 0xF0,
129 PS2_MOUSE_SET_WRAP_MODE = 0xEC,
130 PS2_MOUSE_READ_DATA = 0xEB,
131 PS2_MOUSE_SET_STREAM_MODE = 0xEA,
132 PS2_MOUSE_STATUS_REQUEST = 0xE9,
133 PS2_MOUSE_SET_RESOLUTION = 0xE8,
134 PS2_MOUSE_SET_SCALING_2_1 = 0xE7,
135 PS2_MOUSE_SET_SCALING_1_1 = 0xE6,
136};
137
138typedef enum ps2_mouse_resolution_e {
139 PS2_MOUSE_1_COUNT_MM,
140 PS2_MOUSE_2_COUNT_MM,
141 PS2_MOUSE_4_COUNT_MM,
142 PS2_MOUSE_8_COUNT_MM,
143} ps2_mouse_resolution_t;
144
145typedef enum ps2_mouse_sample_rate_e {
146 PS2_MOUSE_10_SAMPLES_SEC = 10,
147 PS2_MOUSE_20_SAMPLES_SEC = 20,
148 PS2_MOUSE_40_SAMPLES_SEC = 40,
149 PS2_MOUSE_60_SAMPLES_SEC = 60,
150 PS2_MOUSE_80_SAMPLES_SEC = 80,
151 PS2_MOUSE_100_SAMPLES_SEC = 100,
152 PS2_MOUSE_200_SAMPLES_SEC = 200,
153} ps2_mouse_sample_rate_t;
154
155void ps2_mouse_init(void);
156
157void ps2_mouse_init_user(void);
158
159void ps2_mouse_task(void);
160
161void ps2_mouse_disable_data_reporting(void);
162
163void ps2_mouse_enable_data_reporting(void);
164
165void ps2_mouse_set_remote_mode(void);
166
167void ps2_mouse_set_stream_mode(void);
168
169void ps2_mouse_set_scaling_2_1(void);
170
171void ps2_mouse_set_scaling_1_1(void);
172
173void ps2_mouse_set_resolution(ps2_mouse_resolution_t resolution);
174
175void ps2_mouse_set_sample_rate(ps2_mouse_sample_rate_t sample_rate);
176
177void ps2_mouse_moved_user(report_mouse_t *mouse_report);