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