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: stable/11/usr.sbin/bhyve/ps2mouse.c 336189 2018-07-11 07:16:13Z araujo $"); 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 "console.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 87struct 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 95struct 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 111static void 112fifo_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 120static void 121fifo_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 130static void 131fifo_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 143static int 144fifo_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 159static void 160movement_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 168static void 169movement_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 177static void 178movement_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 227static void 228ps2mouse_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 243int 244ps2mouse_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 255int 256ps2mouse_fifocnt(struct ps2mouse_softc *sc) 257{ 258 return (sc->fifo.num); 259} 260 261void 262ps2mouse_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 276void 277ps2mouse_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 fprintf(stderr, "Unhandled ps2 mouse current " 293 "command byte 0x%02x\n", 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 fprintf(stderr, "Unhandled ps2 mouse command " 362 "0x%02x\n", val); 363 break; 364 } 365 } 366 pthread_mutex_unlock(&sc->mtx); 367} 368 369static void 370ps2mouse_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 399struct ps2mouse_softc * 400ps2mouse_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