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