1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> 5 * Copyright (c) 2015 Nahanni Systems Inc. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 31 #include <sys/types.h> 32 33 #include <assert.h> 34 #include <stdbool.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <strings.h> 38 #include <pthread.h> 39 #include <pthread_np.h> 40 41 #include "atkbdc.h" 42 #include "console.h" 43 #include "debug.h" 44 #include "ps2mouse.h" 45 46 /* mouse device commands */ 47 #define PS2MC_RESET_DEV 0xff 48 #define PS2MC_SET_DEFAULTS 0xf6 49 #define PS2MC_DISABLE 0xf5 50 #define PS2MC_ENABLE 0xf4 51 #define PS2MC_SET_SAMPLING_RATE 0xf3 52 #define PS2MC_SEND_DEV_ID 0xf2 53 #define PS2MC_SET_REMOTE_MODE 0xf0 54 #define PS2MC_SEND_DEV_DATA 0xeb 55 #define PS2MC_SET_STREAM_MODE 0xea 56 #define PS2MC_SEND_DEV_STATUS 0xe9 57 #define PS2MC_SET_RESOLUTION 0xe8 58 #define PS2MC_SET_SCALING1 0xe7 59 #define PS2MC_SET_SCALING2 0xe6 60 61 #define PS2MC_BAT_SUCCESS 0xaa 62 #define PS2MC_ACK 0xfa 63 64 /* mouse device id */ 65 #define PS2MOUSE_DEV_ID 0x0 66 67 /* mouse data bits */ 68 #define PS2M_DATA_Y_OFLOW 0x80 69 #define PS2M_DATA_X_OFLOW 0x40 70 #define PS2M_DATA_Y_SIGN 0x20 71 #define PS2M_DATA_X_SIGN 0x10 72 #define PS2M_DATA_AONE 0x08 73 #define PS2M_DATA_MID_BUTTON 0x04 74 #define PS2M_DATA_RIGHT_BUTTON 0x02 75 #define PS2M_DATA_LEFT_BUTTON 0x01 76 77 /* mouse status bits */ 78 #define PS2M_STS_REMOTE_MODE 0x40 79 #define PS2M_STS_ENABLE_DEV 0x20 80 #define PS2M_STS_SCALING_21 0x10 81 #define PS2M_STS_MID_BUTTON 0x04 82 #define PS2M_STS_RIGHT_BUTTON 0x02 83 #define PS2M_STS_LEFT_BUTTON 0x01 84 85 #define PS2MOUSE_FIFOSZ 16 86 87 struct fifo { 88 uint8_t buf[PS2MOUSE_FIFOSZ]; 89 int rindex; /* index to read from */ 90 int windex; /* index to write to */ 91 int num; /* number of bytes in the fifo */ 92 int size; /* size of the fifo */ 93 }; 94 95 struct ps2mouse_softc { 96 struct atkbdc_softc *atkbdc_sc; 97 pthread_mutex_t mtx; 98 99 uint8_t status; 100 uint8_t resolution; 101 uint8_t sampling_rate; 102 int ctrlenable; 103 struct fifo fifo; 104 105 uint8_t curcmd; /* current command for next byte */ 106 107 int cur_x, cur_y; 108 int delta_x, delta_y; 109 }; 110 111 static void 112 fifo_init(struct ps2mouse_softc *sc) 113 { 114 struct fifo *fifo; 115 116 fifo = &sc->fifo; 117 fifo->size = sizeof(((struct fifo *)0)->buf); 118 } 119 120 static void 121 fifo_reset(struct ps2mouse_softc *sc) 122 { 123 struct fifo *fifo; 124 125 fifo = &sc->fifo; 126 bzero(fifo, sizeof(struct fifo)); 127 fifo->size = sizeof(((struct fifo *)0)->buf); 128 } 129 130 static void 131 fifo_put(struct ps2mouse_softc *sc, uint8_t val) 132 { 133 struct fifo *fifo; 134 135 fifo = &sc->fifo; 136 if (fifo->num < fifo->size) { 137 fifo->buf[fifo->windex] = val; 138 fifo->windex = (fifo->windex + 1) % fifo->size; 139 fifo->num++; 140 } 141 } 142 143 static int 144 fifo_get(struct ps2mouse_softc *sc, uint8_t *val) 145 { 146 struct fifo *fifo; 147 148 fifo = &sc->fifo; 149 if (fifo->num > 0) { 150 *val = fifo->buf[fifo->rindex]; 151 fifo->rindex = (fifo->rindex + 1) % fifo->size; 152 fifo->num--; 153 return (0); 154 } 155 156 return (-1); 157 } 158 159 static void 160 movement_reset(struct ps2mouse_softc *sc) 161 { 162 assert(pthread_mutex_isowned_np(&sc->mtx)); 163 164 sc->delta_x = 0; 165 sc->delta_y = 0; 166 } 167 168 static void 169 movement_update(struct ps2mouse_softc *sc, int x, int y) 170 { 171 sc->delta_x += x - sc->cur_x; 172 sc->delta_y += sc->cur_y - y; 173 sc->cur_x = x; 174 sc->cur_y = y; 175 } 176 177 static void 178 movement_get(struct ps2mouse_softc *sc) 179 { 180 uint8_t val0, val1, val2; 181 182 assert(pthread_mutex_isowned_np(&sc->mtx)); 183 184 val0 = PS2M_DATA_AONE; 185 val0 |= sc->status & (PS2M_DATA_LEFT_BUTTON | 186 PS2M_DATA_RIGHT_BUTTON | PS2M_DATA_MID_BUTTON); 187 188 if (sc->delta_x >= 0) { 189 if (sc->delta_x > 255) { 190 val0 |= PS2M_DATA_X_OFLOW; 191 val1 = 255; 192 } else 193 val1 = sc->delta_x; 194 } else { 195 val0 |= PS2M_DATA_X_SIGN; 196 if (sc->delta_x < -255) { 197 val0 |= PS2M_DATA_X_OFLOW; 198 val1 = 255; 199 } else 200 val1 = sc->delta_x; 201 } 202 sc->delta_x = 0; 203 204 if (sc->delta_y >= 0) { 205 if (sc->delta_y > 255) { 206 val0 |= PS2M_DATA_Y_OFLOW; 207 val2 = 255; 208 } else 209 val2 = sc->delta_y; 210 } else { 211 val0 |= PS2M_DATA_Y_SIGN; 212 if (sc->delta_y < -255) { 213 val0 |= PS2M_DATA_Y_OFLOW; 214 val2 = 255; 215 } else 216 val2 = sc->delta_y; 217 } 218 sc->delta_y = 0; 219 220 if (sc->fifo.num < (sc->fifo.size - 3)) { 221 fifo_put(sc, val0); 222 fifo_put(sc, val1); 223 fifo_put(sc, val2); 224 } 225 } 226 227 static void 228 ps2mouse_reset(struct ps2mouse_softc *sc) 229 { 230 assert(pthread_mutex_isowned_np(&sc->mtx)); 231 fifo_reset(sc); 232 movement_reset(sc); 233 sc->status = PS2M_STS_ENABLE_DEV; 234 sc->resolution = 4; 235 sc->sampling_rate = 100; 236 237 sc->cur_x = 0; 238 sc->cur_y = 0; 239 sc->delta_x = 0; 240 sc->delta_y = 0; 241 } 242 243 int 244 ps2mouse_read(struct ps2mouse_softc *sc, uint8_t *val) 245 { 246 int retval; 247 248 pthread_mutex_lock(&sc->mtx); 249 retval = fifo_get(sc, val); 250 pthread_mutex_unlock(&sc->mtx); 251 252 return (retval); 253 } 254 255 int 256 ps2mouse_fifocnt(struct ps2mouse_softc *sc) 257 { 258 return (sc->fifo.num); 259 } 260 261 void 262 ps2mouse_toggle(struct ps2mouse_softc *sc, int enable) 263 { 264 pthread_mutex_lock(&sc->mtx); 265 if (enable) 266 sc->ctrlenable = 1; 267 else { 268 sc->ctrlenable = 0; 269 sc->fifo.rindex = 0; 270 sc->fifo.windex = 0; 271 sc->fifo.num = 0; 272 } 273 pthread_mutex_unlock(&sc->mtx); 274 } 275 276 void 277 ps2mouse_write(struct ps2mouse_softc *sc, uint8_t val, int insert) 278 { 279 pthread_mutex_lock(&sc->mtx); 280 fifo_reset(sc); 281 if (sc->curcmd) { 282 switch (sc->curcmd) { 283 case PS2MC_SET_SAMPLING_RATE: 284 sc->sampling_rate = val; 285 fifo_put(sc, PS2MC_ACK); 286 break; 287 case PS2MC_SET_RESOLUTION: 288 sc->resolution = val; 289 fifo_put(sc, PS2MC_ACK); 290 break; 291 default: 292 EPRINTLN("Unhandled ps2 mouse current " 293 "command byte 0x%02x", val); 294 break; 295 } 296 sc->curcmd = 0; 297 298 } else if (insert) { 299 fifo_put(sc, val); 300 } else { 301 switch (val) { 302 case 0x00: 303 fifo_put(sc, PS2MC_ACK); 304 break; 305 case PS2MC_RESET_DEV: 306 ps2mouse_reset(sc); 307 fifo_put(sc, PS2MC_ACK); 308 fifo_put(sc, PS2MC_BAT_SUCCESS); 309 fifo_put(sc, PS2MOUSE_DEV_ID); 310 break; 311 case PS2MC_SET_DEFAULTS: 312 ps2mouse_reset(sc); 313 fifo_put(sc, PS2MC_ACK); 314 break; 315 case PS2MC_DISABLE: 316 fifo_reset(sc); 317 sc->status &= ~PS2M_STS_ENABLE_DEV; 318 fifo_put(sc, PS2MC_ACK); 319 break; 320 case PS2MC_ENABLE: 321 fifo_reset(sc); 322 sc->status |= PS2M_STS_ENABLE_DEV; 323 fifo_put(sc, PS2MC_ACK); 324 break; 325 case PS2MC_SET_SAMPLING_RATE: 326 sc->curcmd = val; 327 fifo_put(sc, PS2MC_ACK); 328 break; 329 case PS2MC_SEND_DEV_ID: 330 fifo_put(sc, PS2MC_ACK); 331 fifo_put(sc, PS2MOUSE_DEV_ID); 332 break; 333 case PS2MC_SET_REMOTE_MODE: 334 sc->status |= PS2M_STS_REMOTE_MODE; 335 fifo_put(sc, PS2MC_ACK); 336 break; 337 case PS2MC_SEND_DEV_DATA: 338 fifo_put(sc, PS2MC_ACK); 339 movement_get(sc); 340 break; 341 case PS2MC_SET_STREAM_MODE: 342 sc->status &= ~PS2M_STS_REMOTE_MODE; 343 fifo_put(sc, PS2MC_ACK); 344 break; 345 case PS2MC_SEND_DEV_STATUS: 346 fifo_put(sc, PS2MC_ACK); 347 fifo_put(sc, sc->status); 348 fifo_put(sc, sc->resolution); 349 fifo_put(sc, sc->sampling_rate); 350 break; 351 case PS2MC_SET_RESOLUTION: 352 sc->curcmd = val; 353 fifo_put(sc, PS2MC_ACK); 354 break; 355 case PS2MC_SET_SCALING1: 356 case PS2MC_SET_SCALING2: 357 fifo_put(sc, PS2MC_ACK); 358 break; 359 default: 360 fifo_put(sc, PS2MC_ACK); 361 EPRINTLN("Unhandled ps2 mouse command " 362 "0x%02x", val); 363 break; 364 } 365 } 366 pthread_mutex_unlock(&sc->mtx); 367 } 368 369 static void 370 ps2mouse_event(uint8_t button, int x, int y, void *arg) 371 { 372 struct ps2mouse_softc *sc = arg; 373 374 pthread_mutex_lock(&sc->mtx); 375 movement_update(sc, x, y); 376 377 sc->status &= ~(PS2M_STS_LEFT_BUTTON | 378 PS2M_STS_RIGHT_BUTTON | PS2M_STS_MID_BUTTON); 379 if (button & (1 << 0)) 380 sc->status |= PS2M_STS_LEFT_BUTTON; 381 if (button & (1 << 1)) 382 sc->status |= PS2M_STS_MID_BUTTON; 383 if (button & (1 << 2)) 384 sc->status |= PS2M_STS_RIGHT_BUTTON; 385 386 if ((sc->status & PS2M_STS_ENABLE_DEV) == 0 || !sc->ctrlenable) { 387 /* no data reporting */ 388 pthread_mutex_unlock(&sc->mtx); 389 return; 390 } 391 392 movement_get(sc); 393 pthread_mutex_unlock(&sc->mtx); 394 395 if (sc->fifo.num > 0) 396 atkbdc_event(sc->atkbdc_sc, 0); 397 } 398 399 struct ps2mouse_softc * 400 ps2mouse_init(struct atkbdc_softc *atkbdc_sc) 401 { 402 struct ps2mouse_softc *sc; 403 404 sc = calloc(1, sizeof (struct ps2mouse_softc)); 405 pthread_mutex_init(&sc->mtx, NULL); 406 fifo_init(sc); 407 sc->atkbdc_sc = atkbdc_sc; 408 409 pthread_mutex_lock(&sc->mtx); 410 ps2mouse_reset(sc); 411 pthread_mutex_unlock(&sc->mtx); 412 413 console_ptr_register(ps2mouse_event, sc, 1); 414 415 return (sc); 416 } 417 418 419