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 89struct 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 97struct 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 113static void 114fifo_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 122static void 123fifo_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 132static void 133fifo_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 145static int 146fifo_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 161static void 162movement_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 170static void 171movement_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 179static void 180movement_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 229static void 230ps2mouse_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 245int 246ps2mouse_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 257int 258ps2mouse_fifocnt(struct ps2mouse_softc *sc) 259{ 260 return (sc->fifo.num); 261} 262 263void 264ps2mouse_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 278void 279ps2mouse_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 371static void 372ps2mouse_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 401struct ps2mouse_softc * 402ps2mouse_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 421int 422ps2mouse_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 436done: 437 return (ret); 438} 439#endif 440