aboutsummaryrefslogtreecommitdiff
path: root/drivers/ps2/ps2_mouse.c
diff options
context:
space:
mode:
authorJoel Challis <git@zvecr.com>2021-10-20 21:18:49 +0100
committerGitHub <noreply@github.com>2021-10-20 21:18:49 +0100
commitd4be4b67a251ecc046d857c5cd00cfb37c394ab7 (patch)
tree15f6dd05ec293081782a9b42a30e1a81b33b6aa0 /drivers/ps2/ps2_mouse.c
parent5500c428dd41348243e8a1695986b0da070e2ffa (diff)
downloadqmk_firmware-d4be4b67a251ecc046d857c5cd00cfb37c394ab7.tar.gz
qmk_firmware-d4be4b67a251ecc046d857c5cd00cfb37c394ab7.zip
Relocate PS2 code (#14895)
* Relocate ps2 protocol code * clang * Move makefile logic
Diffstat (limited to 'drivers/ps2/ps2_mouse.c')
-rw-r--r--drivers/ps2/ps2_mouse.c270
1 files changed, 270 insertions, 0 deletions
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}