1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/types.h> 34 35 #include <assert.h> 36 #include <stdbool.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <strings.h> 40 #include <pthread.h> 41 #include <pthread_np.h> 42 43 #include "atkbdc.h" 44 #include "debug.h" 45 #include "console.h" 46 47 /* mouse device commands */ 48 #define PS2MC_RESET_DEV 0xff 49 #define PS2MC_SET_DEFAULTS 0xf6 50 #define PS2MC_DISABLE 0xf5 51 #define PS2MC_ENABLE 0xf4 52 #define PS2MC_SET_SAMPLING_RATE 0xf3 53 #define PS2MC_SEND_DEV_ID 0xf2 54 #define PS2MC_SET_REMOTE_MODE 0xf0 55 #define PS2MC_SEND_DEV_DATA 0xeb 56 #define PS2MC_SET_STREAM_MODE 0xea 57 #define PS2MC_SEND_DEV_STATUS 0xe9 58 #define PS2MC_SET_RESOLUTION 0xe8 59 #define PS2MC_SET_SCALING1 0xe7 60 #define PS2MC_SET_SCALING2 0xe6 61 62 #define PS2MC_BAT_SUCCESS 0xaa 63 #define PS2MC_ACK 0xfa 64 65 /* mouse device id */ 66 #define PS2MOUSE_DEV_ID 0x0 67 68 /* mouse data bits */ 69 #define PS2M_DATA_Y_OFLOW 0x80 70 #define PS2M_DATA_X_OFLOW 0x40 71 #define PS2M_DATA_Y_SIGN 0x20 72 #define PS2M_DATA_X_SIGN 0x10 73 #define PS2M_DATA_AONE 0x08 74 #define PS2M_DATA_MID_BUTTON 0x04 75 #define PS2M_DATA_RIGHT_BUTTON 0x02 76 #define PS2M_DATA_LEFT_BUTTON 0x01 77 78 /* mouse status bits */ 79 #define PS2M_STS_REMOTE_MODE 0x40 80 #define PS2M_STS_ENABLE_DEV 0x20 81 #define PS2M_STS_SCALING_21 0x10 82 #define PS2M_STS_MID_BUTTON 0x04 83 #define PS2M_STS_RIGHT_BUTTON 0x02 84 #define PS2M_STS_LEFT_BUTTON 0x01 85 86 #define PS2MOUSE_FIFOSZ 16 87 88 struct fifo { 89 uint8_t buf[PS2MOUSE_FIFOSZ]; 90 int rindex; /* index to read from */ 91 int windex; /* index to write to */ 92 int num; /* number of bytes in the fifo */ 93 int size; /* size of the fifo */ 94 }; 95 96 struct ps2mouse_softc { 97 struct atkbdc_softc *atkbdc_sc; 98 pthread_mutex_t mtx; 99 100 uint8_t status; 101 uint8_t resolution; 102 uint8_t sampling_rate; 103 int ctrlenable; 104 struct fifo fifo; 105 106 uint8_t curcmd; /* current command for next byte */ 107 108 int cur_x, cur_y; 109 int delta_x, delta_y; 110 }; 111 112 static void 113 fifo_init(struct ps2mouse_softc *sc) 114 { 115 struct fifo *fifo; 116 117 fifo = &sc->fifo; 118 fifo->size = sizeof(((struct fifo *)0)->buf); 119 } 120 121 static void 122 fifo_reset(struct ps2mouse_softc *sc) 123 { 124 struct fifo *fifo; 125 126 fifo = &sc->fifo; 127 bzero(fifo, sizeof(struct fifo)); 128 fifo->size = sizeof(((struct fifo *)0)->buf); 129 } 130 131 static void 132 fifo_put(struct ps2mouse_softc *sc, uint8_t val) 133 { 134 struct fifo *fifo; 135 136 fifo = &sc->fifo; 137 if (fifo->num < fifo->size) { 138 fifo->buf[fifo->windex] = val; 139 fifo->windex = (fifo->windex + 1) % fifo->size; 140 fifo->num++; 141 } 142 } 143 144 static int 145 fifo_get(struct ps2mouse_softc *sc, uint8_t *val) 146 { 147 struct fifo *fifo; 148 149 fifo = &sc->fifo; 150 if (fifo->num > 0) { 151 *val = fifo->buf[fifo->rindex]; 152 fifo->rindex = (fifo->rindex + 1) % fifo->size; 153 fifo->num--; 154 return (0); 155 } 156 157 return (-1); 158 } 159 160 static void 161 movement_reset(struct ps2mouse_softc *sc) 162 { 163 assert(pthread_mutex_isowned_np(&sc->mtx)); 164 165 sc->delta_x = 0; 166 sc->delta_y = 0; 167 } 168 169 static void 170 movement_update(struct ps2mouse_softc *sc, int x, int y) 171 { 172 sc->delta_x += x - sc->cur_x; 173 sc->delta_y += sc->cur_y - y; 174 sc->cur_x = x; 175 sc->cur_y = y; 176 } 177 178 static void 179 movement_get(struct ps2mouse_softc *sc) 180 { 181 uint8_t val0, val1, val2; 182 183 assert(pthread_mutex_isowned_np(&sc->mtx)); 184 185 val0 = PS2M_DATA_AONE; 186 val0 |= sc->status & (PS2M_DATA_LEFT_BUTTON | 187 PS2M_DATA_RIGHT_BUTTON | PS2M_DATA_MID_BUTTON); 188 189 if (sc->delta_x >= 0) { 190 if (sc->delta_x > 255) { 191 val0 |= PS2M_DATA_X_OFLOW; 192 val1 = 255; 193 } else 194 val1 = sc->delta_x; 195 } else { 196 val0 |= PS2M_DATA_X_SIGN; 197 if (sc->delta_x < -255) { 198 val0 |= PS2M_DATA_X_OFLOW; 199 val1 = 255; 200 } else 201 val1 = sc->delta_x; 202 } 203 sc->delta_x = 0; 204 205 if (sc->delta_y >= 0) { 206 if (sc->delta_y > 255) { 207 val0 |= PS2M_DATA_Y_OFLOW; 208 val2 = 255; 209 } else 210 val2 = sc->delta_y; 211 } else { 212 val0 |= PS2M_DATA_Y_SIGN; 213 if (sc->delta_y < -255) { 214 val0 |= PS2M_DATA_Y_OFLOW; 215 val2 = 255; 216 } else 217 val2 = sc->delta_y; 218 } 219 sc->delta_y = 0; 220 221 if (sc->fifo.num < (sc->fifo.size - 3)) { 222 fifo_put(sc, val0); 223 fifo_put(sc, val1); 224 fifo_put(sc, val2); 225 } 226 } 227 228 static void 229 ps2mouse_reset(struct ps2mouse_softc *sc) 230 { 231 assert(pthread_mutex_isowned_np(&sc->mtx)); 232 fifo_reset(sc); 233 movement_reset(sc); 234 sc->status = PS2M_STS_ENABLE_DEV; 235 sc->resolution = 4; 236 sc->sampling_rate = 100; 237 238 sc->cur_x = 0; 239 sc->cur_y = 0; 240 sc->delta_x = 0; 241 sc->delta_y = 0; 242 } 243 244 int 245 ps2mouse_read(struct ps2mouse_softc *sc, uint8_t *val) 246 { 247 int retval; 248 249 pthread_mutex_lock(&sc->mtx); 250 retval = fifo_get(sc, val); 251 pthread_mutex_unlock(&sc->mtx); 252 253 return (retval); 254 } 255 256 int 257 ps2mouse_fifocnt(struct ps2mouse_softc *sc) 258 { 259 return (sc->fifo.num); 260 } 261 262 void 263 ps2mouse_toggle(struct ps2mouse_softc *sc, int enable) 264 { 265 pthread_mutex_lock(&sc->mtx); 266 if (enable) 267 sc->ctrlenable = 1; 268 else { 269 sc->ctrlenable = 0; 270 sc->fifo.rindex = 0; 271 sc->fifo.windex = 0; 272 sc->fifo.num = 0; 273 } 274 pthread_mutex_unlock(&sc->mtx); 275 } 276 277 void 278 ps2mouse_write(struct ps2mouse_softc *sc, uint8_t val, int insert) 279 { 280 pthread_mutex_lock(&sc->mtx); 281 fifo_reset(sc); 282 if (sc->curcmd) { 283 switch (sc->curcmd) { 284 case PS2MC_SET_SAMPLING_RATE: 285 sc->sampling_rate = val; 286 fifo_put(sc, PS2MC_ACK); 287 break; 288 case PS2MC_SET_RESOLUTION: 289 sc->resolution = val; 290 fifo_put(sc, PS2MC_ACK); 291 break; 292 default: 293 EPRINTLN("Unhandled ps2 mouse current " 294 "command byte 0x%02x", val); 295 break; 296 } 297 sc->curcmd = 0; 298 299 } else if (insert) { 300 fifo_put(sc, val); 301 } else { 302 switch (val) { 303 case 0x00: 304 fifo_put(sc, PS2MC_ACK); 305 break; 306 case PS2MC_RESET_DEV: 307 ps2mouse_reset(sc); 308 fifo_put(sc, PS2MC_ACK); 309 fifo_put(sc, PS2MC_BAT_SUCCESS); 310 fifo_put(sc, PS2MOUSE_DEV_ID); 311 break; 312 case PS2MC_SET_DEFAULTS: 313 ps2mouse_reset(sc); 314 fifo_put(sc, PS2MC_ACK); 315 break; 316 case PS2MC_DISABLE: 317 fifo_reset(sc); 318 sc->status &= ~PS2M_STS_ENABLE_DEV; 319 fifo_put(sc, PS2MC_ACK); 320 break; 321 case PS2MC_ENABLE: 322 fifo_reset(sc); 323 sc->status |= PS2M_STS_ENABLE_DEV; 324 fifo_put(sc, PS2MC_ACK); 325 break; 326 case PS2MC_SET_SAMPLING_RATE: 327 sc->curcmd = val; 328 fifo_put(sc, PS2MC_ACK); 329 break; 330 case PS2MC_SEND_DEV_ID: 331 fifo_put(sc, PS2MC_ACK); 332 fifo_put(sc, PS2MOUSE_DEV_ID); 333 break; 334 case PS2MC_SET_REMOTE_MODE: 335 sc->status |= PS2M_STS_REMOTE_MODE; 336 fifo_put(sc, PS2MC_ACK); 337 break; 338 case PS2MC_SEND_DEV_DATA: 339 fifo_put(sc, PS2MC_ACK); 340 movement_get(sc); 341 break; 342 case PS2MC_SET_STREAM_MODE: 343 sc->status &= ~PS2M_STS_REMOTE_MODE; 344 fifo_put(sc, PS2MC_ACK); 345 break; 346 case PS2MC_SEND_DEV_STATUS: 347 fifo_put(sc, PS2MC_ACK); 348 fifo_put(sc, sc->status); 349 fifo_put(sc, sc->resolution); 350 fifo_put(sc, sc->sampling_rate); 351 break; 352 case PS2MC_SET_RESOLUTION: 353 sc->curcmd = val; 354 fifo_put(sc, PS2MC_ACK); 355 break; 356 case PS2MC_SET_SCALING1: 357 case PS2MC_SET_SCALING2: 358 fifo_put(sc, PS2MC_ACK); 359 break; 360 default: 361 fifo_put(sc, PS2MC_ACK); 362 EPRINTLN("Unhandled ps2 mouse command " 363 "0x%02x", val); 364 break; 365 } 366 } 367 pthread_mutex_unlock(&sc->mtx); 368 } 369 370 static void 371 ps2mouse_event(uint8_t button, int x, int y, void *arg) 372 { 373 struct ps2mouse_softc *sc = arg; 374 375 pthread_mutex_lock(&sc->mtx); 376 movement_update(sc, x, y); 377 378 sc->status &= ~(PS2M_STS_LEFT_BUTTON | 379 PS2M_STS_RIGHT_BUTTON | PS2M_STS_MID_BUTTON); 380 if (button & (1 << 0)) 381 sc->status |= PS2M_STS_LEFT_BUTTON; 382 if (button & (1 << 1)) 383 sc->status |= PS2M_STS_MID_BUTTON; 384 if (button & (1 << 2)) 385 sc->status |= PS2M_STS_RIGHT_BUTTON; 386 387 if ((sc->status & PS2M_STS_ENABLE_DEV) == 0 || !sc->ctrlenable) { 388 /* no data reporting */ 389 pthread_mutex_unlock(&sc->mtx); 390 return; 391 } 392 393 movement_get(sc); 394 pthread_mutex_unlock(&sc->mtx); 395 396 if (sc->fifo.num > 0) 397 atkbdc_event(sc->atkbdc_sc, 0); 398 } 399 400 struct ps2mouse_softc * 401 ps2mouse_init(struct atkbdc_softc *atkbdc_sc) 402 { 403 struct ps2mouse_softc *sc; 404 405 sc = calloc(1, sizeof (struct ps2mouse_softc)); 406 pthread_mutex_init(&sc->mtx, NULL); 407 fifo_init(sc); 408 sc->atkbdc_sc = atkbdc_sc; 409 410 pthread_mutex_lock(&sc->mtx); 411 ps2mouse_reset(sc); 412 pthread_mutex_unlock(&sc->mtx); 413 414 console_ptr_register(ps2mouse_event, sc, 1); 415 416 return (sc); 417 } 418 419 420