ps2_mouse.c (11591B)
1 /* 2 Copyright 2011,2013 Jun Wako <wakojun@gmail.com> 3 4 This program is free software: you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation, either version 2 of the License, or 7 (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along 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 31 static report_mouse_t mouse_report = {}; 32 33 static inline void ps2_mouse_print_report(report_mouse_t *mouse_report); 34 static inline void ps2_mouse_convert_report_to_hid(report_mouse_t *mouse_report); 35 static inline void ps2_mouse_clear_report(report_mouse_t *mouse_report); 36 static inline void ps2_mouse_enable_scrolling(void); 37 static 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 */ 42 void 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 ps2_mouse_set_stream_mode(); 57 #endif 58 59 #ifdef PS2_MOUSE_ENABLE_SCROLLING 60 ps2_mouse_enable_scrolling(); 61 #endif 62 63 #ifdef PS2_MOUSE_USE_2_1_SCALING 64 ps2_mouse_set_scaling_2_1(); 65 #endif 66 67 ps2_mouse_init_user(); 68 } 69 70 __attribute__((weak)) void ps2_mouse_init_user(void) {} 71 72 __attribute__((weak)) void ps2_mouse_moved_user(report_mouse_t *mouse_report) {} 73 74 void ps2_mouse_task(void) { 75 static uint8_t buttons_prev = 0; 76 extern int tp_buttons; 77 78 /* receives packet from mouse */ 79 #ifdef PS2_MOUSE_USE_REMOTE_MODE 80 uint8_t rcv; 81 rcv = ps2_host_send(PS2_MOUSE_READ_DATA); 82 if (rcv == PS2_ACK) { 83 mouse_report.buttons = ps2_host_recv_response(); 84 mouse_report.x = ps2_host_recv_response(); 85 mouse_report.y = ps2_host_recv_response(); 86 # ifdef PS2_MOUSE_ENABLE_SCROLLING 87 mouse_report.v = -(ps2_host_recv_response() & PS2_MOUSE_SCROLL_MASK); 88 # endif 89 } else { 90 if (debug_mouse) print("ps2_mouse: fail to get mouse packet\n"); 91 /* return here to avoid updating the mouse button state */ 92 return; 93 } 94 #else 95 if (pbuf_has_data()) { 96 mouse_report.buttons = ps2_host_recv_response(); 97 mouse_report.x = ps2_host_recv_response(); 98 mouse_report.y = ps2_host_recv_response(); 99 # ifdef PS2_MOUSE_ENABLE_SCROLLING 100 mouse_report.v = -(ps2_host_recv_response() & PS2_MOUSE_SCROLL_MASK); 101 # endif 102 } else { 103 if (debug_mouse) print("ps2_mouse: fail to get mouse packet\n"); 104 /* return here to avoid updating the mouse button state */ 105 return; 106 } 107 #endif 108 109 mouse_report.buttons |= tp_buttons; 110 /* if mouse moves or buttons state changes */ 111 if (mouse_report.x || mouse_report.y || mouse_report.v || ((mouse_report.buttons ^ buttons_prev) & PS2_MOUSE_BTN_MASK)) { 112 #ifdef PS2_MOUSE_DEBUG_RAW 113 // Used to debug raw ps2 bytes from mouse 114 ps2_mouse_print_report(&mouse_report); 115 #endif 116 buttons_prev = mouse_report.buttons; 117 ps2_mouse_convert_report_to_hid(&mouse_report); 118 #if PS2_MOUSE_SCROLL_BTN_MASK 119 ps2_mouse_scroll_button_task(&mouse_report); 120 #endif 121 if (mouse_report.x || mouse_report.y || mouse_report.v) { 122 ps2_mouse_moved_user(&mouse_report); 123 } 124 #ifdef PS2_MOUSE_DEBUG_HID 125 // Used to debug the bytes sent to the host 126 ps2_mouse_print_report(&mouse_report); 127 #endif 128 host_mouse_send(&mouse_report); 129 } 130 131 ps2_mouse_clear_report(&mouse_report); 132 } 133 134 void ps2_mouse_disable_data_reporting(void) { 135 PS2_MOUSE_SEND(PS2_MOUSE_DISABLE_DATA_REPORTING, "ps2 mouse disable data reporting"); 136 } 137 138 void ps2_mouse_enable_data_reporting(void) { 139 PS2_MOUSE_SEND(PS2_MOUSE_ENABLE_DATA_REPORTING, "ps2 mouse enable data reporting"); 140 } 141 142 void ps2_mouse_set_remote_mode(void) { 143 PS2_MOUSE_SEND_SAFE(PS2_MOUSE_SET_REMOTE_MODE, "ps2 mouse set remote mode"); 144 ps2_mouse_mode = PS2_MOUSE_REMOTE_MODE; 145 } 146 147 void ps2_mouse_set_stream_mode(void) { 148 PS2_MOUSE_SEND_SAFE(PS2_MOUSE_SET_STREAM_MODE, "ps2 mouse set stream mode"); 149 ps2_mouse_mode = PS2_MOUSE_STREAM_MODE; 150 } 151 152 void ps2_mouse_set_scaling_2_1(void) { 153 PS2_MOUSE_SEND_SAFE(PS2_MOUSE_SET_SCALING_2_1, "ps2 mouse set scaling 2:1"); 154 } 155 156 void ps2_mouse_set_scaling_1_1(void) { 157 PS2_MOUSE_SEND_SAFE(PS2_MOUSE_SET_SCALING_1_1, "ps2 mouse set scaling 1:1"); 158 } 159 160 void ps2_mouse_set_resolution(ps2_mouse_resolution_t resolution) { 161 PS2_MOUSE_SET_SAFE(PS2_MOUSE_SET_RESOLUTION, resolution, "ps2 mouse set resolution"); 162 } 163 164 void ps2_mouse_set_sample_rate(ps2_mouse_sample_rate_t sample_rate) { 165 PS2_MOUSE_SET_SAFE(PS2_MOUSE_SET_SAMPLE_RATE, sample_rate, "ps2 mouse set sample rate"); 166 } 167 168 /* ============================= HELPERS ============================ */ 169 170 #define X_IS_NEG (mouse_report->buttons & (1 << PS2_MOUSE_X_SIGN)) 171 #define Y_IS_NEG (mouse_report->buttons & (1 << PS2_MOUSE_Y_SIGN)) 172 #define X_IS_OVF (mouse_report->buttons & (1 << PS2_MOUSE_X_OVFLW)) 173 #define Y_IS_OVF (mouse_report->buttons & (1 << PS2_MOUSE_Y_OVFLW)) 174 static inline void ps2_mouse_convert_report_to_hid(report_mouse_t *mouse_report) { 175 #ifndef MOUSE_EXTENDED_REPORT 176 // PS/2 mouse data is '9-bit integer'(-256 to 255) which is comprised of sign-bit and 8-bit value. 177 // bit: 8 7 ... 0 178 // sign \8-bit/ 179 // 180 // Meanwhile USB HID mouse indicates 8bit data(-127 to 127), note that -128 is not used. 181 // 182 // This converts PS/2 data into HID value. Use only -127-127 out of PS/2 9-bit. 183 mouse_report->x *= PS2_MOUSE_X_MULTIPLIER; 184 mouse_report->y *= PS2_MOUSE_Y_MULTIPLIER; 185 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); 186 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); 187 #else 188 // Sign extend if negative, otherwise leave positive 8-bits as-is 189 mouse_report->x = X_IS_NEG ? (mouse_report->x | ~0xFF) : mouse_report->x; 190 mouse_report->y = Y_IS_NEG ? (mouse_report->y | ~0xFF) : mouse_report->y; 191 mouse_report->x *= PS2_MOUSE_X_MULTIPLIER; 192 mouse_report->y *= PS2_MOUSE_Y_MULTIPLIER; 193 #endif 194 mouse_report->v *= PS2_MOUSE_V_MULTIPLIER; 195 196 #ifdef PS2_MOUSE_INVERT_BUTTONS 197 // swap left & right buttons 198 bool needs_left = mouse_report->buttons & (1 << PS2_MOUSE_BTN_RIGHT); 199 bool needs_right = mouse_report->buttons & (1 << PS2_MOUSE_BTN_LEFT); 200 mouse_report->buttons = (mouse_report->buttons & ~((1 << PS2_MOUSE_BTN_LEFT) | (1 << PS2_MOUSE_BTN_RIGHT))) | (needs_left << PS2_MOUSE_BTN_LEFT) | (needs_right << PS2_MOUSE_BTN_RIGHT); 201 #endif 202 // remove sign and overflow flags 203 mouse_report->buttons &= PS2_MOUSE_BTN_MASK; 204 205 #ifdef PS2_MOUSE_INVERT_X 206 mouse_report->x = -mouse_report->x; 207 #endif 208 #ifndef PS2_MOUSE_INVERT_Y // NOTE if not! 209 // invert coordinate of y to conform to USB HID mouse 210 mouse_report->y = -mouse_report->y; 211 #endif 212 213 #ifdef PS2_MOUSE_ROTATE 214 mouse_xy_report_t x = mouse_report->x; 215 mouse_xy_report_t y = mouse_report->y; 216 # if PS2_MOUSE_ROTATE == 90 217 mouse_report->x = y; 218 mouse_report->y = -x; 219 # elif PS2_MOUSE_ROTATE == 180 220 mouse_report->x = -x; 221 mouse_report->y = -y; 222 # elif PS2_MOUSE_ROTATE == 270 223 mouse_report->x = -y; 224 mouse_report->y = x; 225 # endif 226 #endif 227 } 228 229 static inline void ps2_mouse_clear_report(report_mouse_t *mouse_report) { 230 mouse_report->x = 0; 231 mouse_report->y = 0; 232 mouse_report->v = 0; 233 mouse_report->h = 0; 234 mouse_report->buttons = 0; 235 } 236 237 static inline void ps2_mouse_print_report(report_mouse_t *mouse_report) { 238 if (!debug_mouse) return; 239 print("ps2_mouse: ["); 240 print_hex8(mouse_report->buttons); 241 print("|"); 242 print_hex8((uint8_t)mouse_report->x); 243 print(" "); 244 print_hex8((uint8_t)mouse_report->y); 245 print(" "); 246 print_hex8((uint8_t)mouse_report->v); 247 print(" "); 248 print_hex8((uint8_t)mouse_report->h); 249 print("]\n"); 250 } 251 252 static inline void ps2_mouse_enable_scrolling(void) { 253 PS2_MOUSE_SEND(PS2_MOUSE_SET_SAMPLE_RATE, "Initiaing scroll wheel enable: Set sample rate"); 254 PS2_MOUSE_SEND(200, "200"); 255 PS2_MOUSE_SEND(PS2_MOUSE_SET_SAMPLE_RATE, "Set sample rate"); 256 PS2_MOUSE_SEND(100, "100"); 257 PS2_MOUSE_SEND(PS2_MOUSE_SET_SAMPLE_RATE, "Set sample rate"); 258 PS2_MOUSE_SEND(80, "80"); 259 PS2_MOUSE_SEND(PS2_MOUSE_GET_DEVICE_ID, "Finished enabling scroll wheel"); 260 wait_ms(20); 261 } 262 263 #define PRESS_SCROLL_BUTTONS mouse_report->buttons |= (PS2_MOUSE_SCROLL_BTN_MASK) 264 #define RELEASE_SCROLL_BUTTONS mouse_report->buttons &= ~(PS2_MOUSE_SCROLL_BTN_MASK) 265 static inline void ps2_mouse_scroll_button_task(report_mouse_t *mouse_report) { 266 static enum { 267 SCROLL_NONE, 268 SCROLL_BTN, 269 SCROLL_SENT, 270 } scroll_state = SCROLL_NONE; 271 static uint16_t scroll_button_time = 0; 272 static int16_t scroll_x, scroll_y; 273 274 if (PS2_MOUSE_SCROLL_BTN_MASK == (mouse_report->buttons & (PS2_MOUSE_SCROLL_BTN_MASK))) { 275 // All scroll buttons are pressed 276 277 if (scroll_state == SCROLL_NONE) { 278 scroll_button_time = timer_read(); 279 scroll_state = SCROLL_BTN; 280 scroll_x = 0; 281 scroll_y = 0; 282 } 283 284 // If the mouse has moved, update the report to scroll instead of move the mouse 285 if (mouse_report->x || mouse_report->y) { 286 scroll_state = SCROLL_SENT; 287 scroll_y += mouse_report->y; 288 scroll_x += mouse_report->x; 289 mouse_report->v = -scroll_y / (PS2_MOUSE_SCROLL_DIVISOR_V); 290 mouse_report->h = scroll_x / (PS2_MOUSE_SCROLL_DIVISOR_H); 291 scroll_y += (mouse_report->v * (PS2_MOUSE_SCROLL_DIVISOR_V)); 292 scroll_x -= (mouse_report->h * (PS2_MOUSE_SCROLL_DIVISOR_H)); 293 mouse_report->x = 0; 294 mouse_report->y = 0; 295 #ifdef PS2_MOUSE_INVERT_H 296 mouse_report->h = -mouse_report->h; 297 #endif 298 #ifdef PS2_MOUSE_INVERT_V 299 mouse_report->v = -mouse_report->v; 300 #endif 301 } 302 } else if (0 == (PS2_MOUSE_SCROLL_BTN_MASK & mouse_report->buttons)) { 303 // None of the scroll buttons are pressed 304 305 #if PS2_MOUSE_SCROLL_BTN_SEND 306 if (scroll_state == SCROLL_BTN && timer_elapsed(scroll_button_time) < PS2_MOUSE_SCROLL_BTN_SEND) { 307 PRESS_SCROLL_BUTTONS; 308 host_mouse_send(mouse_report); 309 wait_ms(100); 310 RELEASE_SCROLL_BUTTONS; 311 } 312 #endif 313 scroll_state = SCROLL_NONE; 314 } 315 316 RELEASE_SCROLL_BUTTONS; 317 }