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