1300829Sgrehan/*- 2336189Saraujo * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3336189Saraujo * 4300829Sgrehan * Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> 5300829Sgrehan * Copyright (c) 2015 Nahanni Systems Inc. 6300829Sgrehan * All rights reserved. 7300829Sgrehan * 8300829Sgrehan * Redistribution and use in source and binary forms, with or without 9300829Sgrehan * modification, are permitted provided that the following conditions 10300829Sgrehan * are met: 11300829Sgrehan * 1. Redistributions of source code must retain the above copyright 12300829Sgrehan * notice, this list of conditions and the following disclaimer. 13300829Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 14300829Sgrehan * notice, this list of conditions and the following disclaimer in the 15300829Sgrehan * documentation and/or other materials provided with the distribution. 16300829Sgrehan * 17300829Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND 18300829Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19300829Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20300829Sgrehan * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21300829Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22300829Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23300829Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24300829Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25300829Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26300829Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27300829Sgrehan * SUCH DAMAGE. 28300829Sgrehan */ 29300829Sgrehan 30300829Sgrehan#include <sys/cdefs.h> 31300829Sgrehan__FBSDID("$FreeBSD: stable/11/usr.sbin/bhyve/ps2mouse.c 336189 2018-07-11 07:16:13Z araujo $"); 32300829Sgrehan 33300829Sgrehan#include <sys/types.h> 34300829Sgrehan 35300829Sgrehan#include <assert.h> 36300829Sgrehan#include <stdbool.h> 37300829Sgrehan#include <stdio.h> 38300829Sgrehan#include <stdlib.h> 39300829Sgrehan#include <strings.h> 40300829Sgrehan#include <pthread.h> 41300829Sgrehan#include <pthread_np.h> 42300829Sgrehan 43300829Sgrehan#include "atkbdc.h" 44300829Sgrehan#include "console.h" 45300829Sgrehan 46300829Sgrehan/* mouse device commands */ 47300829Sgrehan#define PS2MC_RESET_DEV 0xff 48300829Sgrehan#define PS2MC_SET_DEFAULTS 0xf6 49300829Sgrehan#define PS2MC_DISABLE 0xf5 50300829Sgrehan#define PS2MC_ENABLE 0xf4 51300829Sgrehan#define PS2MC_SET_SAMPLING_RATE 0xf3 52300829Sgrehan#define PS2MC_SEND_DEV_ID 0xf2 53300829Sgrehan#define PS2MC_SET_REMOTE_MODE 0xf0 54300829Sgrehan#define PS2MC_SEND_DEV_DATA 0xeb 55300829Sgrehan#define PS2MC_SET_STREAM_MODE 0xea 56300829Sgrehan#define PS2MC_SEND_DEV_STATUS 0xe9 57300829Sgrehan#define PS2MC_SET_RESOLUTION 0xe8 58300829Sgrehan#define PS2MC_SET_SCALING1 0xe7 59300829Sgrehan#define PS2MC_SET_SCALING2 0xe6 60300829Sgrehan 61300829Sgrehan#define PS2MC_BAT_SUCCESS 0xaa 62300829Sgrehan#define PS2MC_ACK 0xfa 63300829Sgrehan 64300829Sgrehan/* mouse device id */ 65300829Sgrehan#define PS2MOUSE_DEV_ID 0x0 66300829Sgrehan 67320855Sgrehan/* mouse data bits */ 68320855Sgrehan#define PS2M_DATA_Y_OFLOW 0x80 69320855Sgrehan#define PS2M_DATA_X_OFLOW 0x40 70320855Sgrehan#define PS2M_DATA_Y_SIGN 0x20 71320855Sgrehan#define PS2M_DATA_X_SIGN 0x10 72320855Sgrehan#define PS2M_DATA_AONE 0x08 73320855Sgrehan#define PS2M_DATA_MID_BUTTON 0x04 74320855Sgrehan#define PS2M_DATA_RIGHT_BUTTON 0x02 75320855Sgrehan#define PS2M_DATA_LEFT_BUTTON 0x01 76320855Sgrehan 77300829Sgrehan/* mouse status bits */ 78300829Sgrehan#define PS2M_STS_REMOTE_MODE 0x40 79300829Sgrehan#define PS2M_STS_ENABLE_DEV 0x20 80300829Sgrehan#define PS2M_STS_SCALING_21 0x10 81300829Sgrehan#define PS2M_STS_MID_BUTTON 0x04 82300829Sgrehan#define PS2M_STS_RIGHT_BUTTON 0x02 83300829Sgrehan#define PS2M_STS_LEFT_BUTTON 0x01 84300829Sgrehan 85300829Sgrehan#define PS2MOUSE_FIFOSZ 16 86300829Sgrehan 87300829Sgrehanstruct fifo { 88300829Sgrehan uint8_t buf[PS2MOUSE_FIFOSZ]; 89300829Sgrehan int rindex; /* index to read from */ 90300829Sgrehan int windex; /* index to write to */ 91300829Sgrehan int num; /* number of bytes in the fifo */ 92300829Sgrehan int size; /* size of the fifo */ 93300829Sgrehan}; 94300829Sgrehan 95300829Sgrehanstruct ps2mouse_softc { 96300829Sgrehan struct atkbdc_softc *atkbdc_sc; 97300829Sgrehan pthread_mutex_t mtx; 98300829Sgrehan 99300829Sgrehan uint8_t status; 100300829Sgrehan uint8_t resolution; 101300829Sgrehan uint8_t sampling_rate; 102300829Sgrehan int ctrlenable; 103300829Sgrehan struct fifo fifo; 104300829Sgrehan 105300829Sgrehan uint8_t curcmd; /* current command for next byte */ 106300829Sgrehan 107300829Sgrehan int cur_x, cur_y; 108300829Sgrehan int delta_x, delta_y; 109300829Sgrehan}; 110300829Sgrehan 111300829Sgrehanstatic void 112300829Sgrehanfifo_init(struct ps2mouse_softc *sc) 113300829Sgrehan{ 114300829Sgrehan struct fifo *fifo; 115300829Sgrehan 116300829Sgrehan fifo = &sc->fifo; 117300829Sgrehan fifo->size = sizeof(((struct fifo *)0)->buf); 118300829Sgrehan} 119300829Sgrehan 120300829Sgrehanstatic void 121300829Sgrehanfifo_reset(struct ps2mouse_softc *sc) 122300829Sgrehan{ 123300829Sgrehan struct fifo *fifo; 124300829Sgrehan 125300829Sgrehan fifo = &sc->fifo; 126300829Sgrehan bzero(fifo, sizeof(struct fifo)); 127300829Sgrehan fifo->size = sizeof(((struct fifo *)0)->buf); 128300829Sgrehan} 129300829Sgrehan 130300829Sgrehanstatic void 131300829Sgrehanfifo_put(struct ps2mouse_softc *sc, uint8_t val) 132300829Sgrehan{ 133300829Sgrehan struct fifo *fifo; 134300829Sgrehan 135300829Sgrehan fifo = &sc->fifo; 136300829Sgrehan if (fifo->num < fifo->size) { 137300829Sgrehan fifo->buf[fifo->windex] = val; 138300829Sgrehan fifo->windex = (fifo->windex + 1) % fifo->size; 139300829Sgrehan fifo->num++; 140300829Sgrehan } 141300829Sgrehan} 142300829Sgrehan 143300829Sgrehanstatic int 144300829Sgrehanfifo_get(struct ps2mouse_softc *sc, uint8_t *val) 145300829Sgrehan{ 146300829Sgrehan struct fifo *fifo; 147300829Sgrehan 148300829Sgrehan fifo = &sc->fifo; 149300829Sgrehan if (fifo->num > 0) { 150300829Sgrehan *val = fifo->buf[fifo->rindex]; 151300829Sgrehan fifo->rindex = (fifo->rindex + 1) % fifo->size; 152300829Sgrehan fifo->num--; 153300829Sgrehan return (0); 154300829Sgrehan } 155300829Sgrehan 156300829Sgrehan return (-1); 157300829Sgrehan} 158300829Sgrehan 159300829Sgrehanstatic void 160300829Sgrehanmovement_reset(struct ps2mouse_softc *sc) 161300829Sgrehan{ 162300829Sgrehan assert(pthread_mutex_isowned_np(&sc->mtx)); 163300829Sgrehan 164300829Sgrehan sc->delta_x = 0; 165300829Sgrehan sc->delta_y = 0; 166300829Sgrehan} 167300829Sgrehan 168300829Sgrehanstatic void 169300829Sgrehanmovement_update(struct ps2mouse_softc *sc, int x, int y) 170300829Sgrehan{ 171300829Sgrehan sc->delta_x += x - sc->cur_x; 172300829Sgrehan sc->delta_y += sc->cur_y - y; 173300829Sgrehan sc->cur_x = x; 174300829Sgrehan sc->cur_y = y; 175300829Sgrehan} 176300829Sgrehan 177300829Sgrehanstatic void 178300829Sgrehanmovement_get(struct ps2mouse_softc *sc) 179300829Sgrehan{ 180300829Sgrehan uint8_t val0, val1, val2; 181300829Sgrehan 182300829Sgrehan assert(pthread_mutex_isowned_np(&sc->mtx)); 183300829Sgrehan 184320855Sgrehan val0 = PS2M_DATA_AONE; 185320855Sgrehan val0 |= sc->status & (PS2M_DATA_LEFT_BUTTON | 186320855Sgrehan PS2M_DATA_RIGHT_BUTTON | PS2M_DATA_MID_BUTTON); 187300829Sgrehan 188300829Sgrehan if (sc->delta_x >= 0) { 189300829Sgrehan if (sc->delta_x > 255) { 190320855Sgrehan val0 |= PS2M_DATA_X_OFLOW; 191300829Sgrehan val1 = 255; 192300829Sgrehan } else 193300829Sgrehan val1 = sc->delta_x; 194300829Sgrehan } else { 195320855Sgrehan val0 |= PS2M_DATA_X_SIGN; 196300829Sgrehan if (sc->delta_x < -255) { 197320855Sgrehan val0 |= PS2M_DATA_X_OFLOW; 198300829Sgrehan val1 = 255; 199300829Sgrehan } else 200300829Sgrehan val1 = sc->delta_x; 201300829Sgrehan } 202300829Sgrehan sc->delta_x = 0; 203300829Sgrehan 204300829Sgrehan if (sc->delta_y >= 0) { 205300829Sgrehan if (sc->delta_y > 255) { 206320855Sgrehan val0 |= PS2M_DATA_Y_OFLOW; 207300829Sgrehan val2 = 255; 208300829Sgrehan } else 209300829Sgrehan val2 = sc->delta_y; 210300829Sgrehan } else { 211320855Sgrehan val0 |= PS2M_DATA_Y_SIGN; 212300829Sgrehan if (sc->delta_y < -255) { 213320855Sgrehan val0 |= PS2M_DATA_Y_OFLOW; 214300829Sgrehan val2 = 255; 215300829Sgrehan } else 216300829Sgrehan val2 = sc->delta_y; 217300829Sgrehan } 218300829Sgrehan sc->delta_y = 0; 219300829Sgrehan 220300829Sgrehan if (sc->fifo.num < (sc->fifo.size - 3)) { 221300829Sgrehan fifo_put(sc, val0); 222300829Sgrehan fifo_put(sc, val1); 223300829Sgrehan fifo_put(sc, val2); 224300829Sgrehan } 225300829Sgrehan} 226300829Sgrehan 227300829Sgrehanstatic void 228300829Sgrehanps2mouse_reset(struct ps2mouse_softc *sc) 229300829Sgrehan{ 230300829Sgrehan assert(pthread_mutex_isowned_np(&sc->mtx)); 231300829Sgrehan fifo_reset(sc); 232300829Sgrehan movement_reset(sc); 233300829Sgrehan sc->status = PS2M_STS_ENABLE_DEV; 234300829Sgrehan sc->resolution = 4; 235300829Sgrehan sc->sampling_rate = 100; 236300829Sgrehan 237300829Sgrehan sc->cur_x = 0; 238300829Sgrehan sc->cur_y = 0; 239300829Sgrehan sc->delta_x = 0; 240300829Sgrehan sc->delta_y = 0; 241300829Sgrehan} 242300829Sgrehan 243300829Sgrehanint 244300829Sgrehanps2mouse_read(struct ps2mouse_softc *sc, uint8_t *val) 245300829Sgrehan{ 246300829Sgrehan int retval; 247300829Sgrehan 248300829Sgrehan pthread_mutex_lock(&sc->mtx); 249300829Sgrehan retval = fifo_get(sc, val); 250300829Sgrehan pthread_mutex_unlock(&sc->mtx); 251300829Sgrehan 252300829Sgrehan return (retval); 253300829Sgrehan} 254300829Sgrehan 255300829Sgrehanint 256300829Sgrehanps2mouse_fifocnt(struct ps2mouse_softc *sc) 257300829Sgrehan{ 258300829Sgrehan return (sc->fifo.num); 259300829Sgrehan} 260300829Sgrehan 261300829Sgrehanvoid 262300829Sgrehanps2mouse_toggle(struct ps2mouse_softc *sc, int enable) 263300829Sgrehan{ 264300829Sgrehan pthread_mutex_lock(&sc->mtx); 265300829Sgrehan if (enable) 266300829Sgrehan sc->ctrlenable = 1; 267300829Sgrehan else { 268300829Sgrehan sc->ctrlenable = 0; 269300829Sgrehan sc->fifo.rindex = 0; 270300829Sgrehan sc->fifo.windex = 0; 271300829Sgrehan sc->fifo.num = 0; 272300829Sgrehan } 273300829Sgrehan pthread_mutex_unlock(&sc->mtx); 274300829Sgrehan} 275300829Sgrehan 276300829Sgrehanvoid 277300829Sgrehanps2mouse_write(struct ps2mouse_softc *sc, uint8_t val, int insert) 278300829Sgrehan{ 279300829Sgrehan pthread_mutex_lock(&sc->mtx); 280300829Sgrehan fifo_reset(sc); 281300829Sgrehan if (sc->curcmd) { 282300829Sgrehan switch (sc->curcmd) { 283300829Sgrehan case PS2MC_SET_SAMPLING_RATE: 284300829Sgrehan sc->sampling_rate = val; 285300829Sgrehan fifo_put(sc, PS2MC_ACK); 286300829Sgrehan break; 287300829Sgrehan case PS2MC_SET_RESOLUTION: 288300829Sgrehan sc->resolution = val; 289300829Sgrehan fifo_put(sc, PS2MC_ACK); 290300829Sgrehan break; 291300829Sgrehan default: 292300829Sgrehan fprintf(stderr, "Unhandled ps2 mouse current " 293300829Sgrehan "command byte 0x%02x\n", val); 294300829Sgrehan break; 295300829Sgrehan } 296300829Sgrehan sc->curcmd = 0; 297300829Sgrehan 298300829Sgrehan } else if (insert) { 299300829Sgrehan fifo_put(sc, val); 300300829Sgrehan } else { 301300829Sgrehan switch (val) { 302300829Sgrehan case 0x00: 303300829Sgrehan fifo_put(sc, PS2MC_ACK); 304300829Sgrehan break; 305300829Sgrehan case PS2MC_RESET_DEV: 306300829Sgrehan ps2mouse_reset(sc); 307300829Sgrehan fifo_put(sc, PS2MC_ACK); 308300829Sgrehan fifo_put(sc, PS2MC_BAT_SUCCESS); 309300829Sgrehan fifo_put(sc, PS2MOUSE_DEV_ID); 310300829Sgrehan break; 311300829Sgrehan case PS2MC_SET_DEFAULTS: 312300829Sgrehan ps2mouse_reset(sc); 313300829Sgrehan fifo_put(sc, PS2MC_ACK); 314300829Sgrehan break; 315300829Sgrehan case PS2MC_DISABLE: 316300829Sgrehan fifo_reset(sc); 317300829Sgrehan sc->status &= ~PS2M_STS_ENABLE_DEV; 318300829Sgrehan fifo_put(sc, PS2MC_ACK); 319300829Sgrehan break; 320300829Sgrehan case PS2MC_ENABLE: 321300829Sgrehan fifo_reset(sc); 322300829Sgrehan sc->status |= PS2M_STS_ENABLE_DEV; 323300829Sgrehan fifo_put(sc, PS2MC_ACK); 324300829Sgrehan break; 325300829Sgrehan case PS2MC_SET_SAMPLING_RATE: 326300829Sgrehan sc->curcmd = val; 327300829Sgrehan fifo_put(sc, PS2MC_ACK); 328300829Sgrehan break; 329300829Sgrehan case PS2MC_SEND_DEV_ID: 330300829Sgrehan fifo_put(sc, PS2MC_ACK); 331300829Sgrehan fifo_put(sc, PS2MOUSE_DEV_ID); 332300829Sgrehan break; 333300829Sgrehan case PS2MC_SET_REMOTE_MODE: 334300829Sgrehan sc->status |= PS2M_STS_REMOTE_MODE; 335300829Sgrehan fifo_put(sc, PS2MC_ACK); 336300829Sgrehan break; 337300829Sgrehan case PS2MC_SEND_DEV_DATA: 338300829Sgrehan fifo_put(sc, PS2MC_ACK); 339300829Sgrehan movement_get(sc); 340300829Sgrehan break; 341300829Sgrehan case PS2MC_SET_STREAM_MODE: 342300829Sgrehan sc->status &= ~PS2M_STS_REMOTE_MODE; 343300829Sgrehan fifo_put(sc, PS2MC_ACK); 344300829Sgrehan break; 345300829Sgrehan case PS2MC_SEND_DEV_STATUS: 346300829Sgrehan fifo_put(sc, PS2MC_ACK); 347300829Sgrehan fifo_put(sc, sc->status); 348300829Sgrehan fifo_put(sc, sc->resolution); 349300829Sgrehan fifo_put(sc, sc->sampling_rate); 350300829Sgrehan break; 351300829Sgrehan case PS2MC_SET_RESOLUTION: 352300829Sgrehan sc->curcmd = val; 353300829Sgrehan fifo_put(sc, PS2MC_ACK); 354300829Sgrehan break; 355300829Sgrehan case PS2MC_SET_SCALING1: 356300829Sgrehan case PS2MC_SET_SCALING2: 357300829Sgrehan fifo_put(sc, PS2MC_ACK); 358300829Sgrehan break; 359300829Sgrehan default: 360300829Sgrehan fifo_put(sc, PS2MC_ACK); 361300829Sgrehan fprintf(stderr, "Unhandled ps2 mouse command " 362300829Sgrehan "0x%02x\n", val); 363300829Sgrehan break; 364300829Sgrehan } 365300829Sgrehan } 366300829Sgrehan pthread_mutex_unlock(&sc->mtx); 367300829Sgrehan} 368300829Sgrehan 369300829Sgrehanstatic void 370300829Sgrehanps2mouse_event(uint8_t button, int x, int y, void *arg) 371300829Sgrehan{ 372300829Sgrehan struct ps2mouse_softc *sc = arg; 373300829Sgrehan 374300829Sgrehan pthread_mutex_lock(&sc->mtx); 375300829Sgrehan movement_update(sc, x, y); 376300829Sgrehan 377300829Sgrehan sc->status &= ~(PS2M_STS_LEFT_BUTTON | 378300829Sgrehan PS2M_STS_RIGHT_BUTTON | PS2M_STS_MID_BUTTON); 379300829Sgrehan if (button & (1 << 0)) 380300829Sgrehan sc->status |= PS2M_STS_LEFT_BUTTON; 381300829Sgrehan if (button & (1 << 1)) 382300829Sgrehan sc->status |= PS2M_STS_MID_BUTTON; 383300829Sgrehan if (button & (1 << 2)) 384300829Sgrehan sc->status |= PS2M_STS_RIGHT_BUTTON; 385300829Sgrehan 386300829Sgrehan if ((sc->status & PS2M_STS_ENABLE_DEV) == 0 || !sc->ctrlenable) { 387300829Sgrehan /* no data reporting */ 388300829Sgrehan pthread_mutex_unlock(&sc->mtx); 389300829Sgrehan return; 390300829Sgrehan } 391300829Sgrehan 392300829Sgrehan movement_get(sc); 393300829Sgrehan pthread_mutex_unlock(&sc->mtx); 394300829Sgrehan 395300829Sgrehan if (sc->fifo.num > 0) 396300829Sgrehan atkbdc_event(sc->atkbdc_sc, 0); 397300829Sgrehan} 398300829Sgrehan 399300829Sgrehanstruct ps2mouse_softc * 400300829Sgrehanps2mouse_init(struct atkbdc_softc *atkbdc_sc) 401300829Sgrehan{ 402300829Sgrehan struct ps2mouse_softc *sc; 403300829Sgrehan 404300829Sgrehan sc = calloc(1, sizeof (struct ps2mouse_softc)); 405300829Sgrehan pthread_mutex_init(&sc->mtx, NULL); 406300829Sgrehan fifo_init(sc); 407300829Sgrehan sc->atkbdc_sc = atkbdc_sc; 408300829Sgrehan 409300829Sgrehan pthread_mutex_lock(&sc->mtx); 410300829Sgrehan ps2mouse_reset(sc); 411300829Sgrehan pthread_mutex_unlock(&sc->mtx); 412300829Sgrehan 413300829Sgrehan console_ptr_register(ps2mouse_event, sc, 1); 414300829Sgrehan 415300829Sgrehan return (sc); 416300829Sgrehan} 417300829Sgrehan 418300829Sgrehan 419