ps2mouse.c revision 302408
1/*- 2 * Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> 3 * Copyright (c) 2015 Nahanni Systems Inc. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: stable/11/usr.sbin/bhyve/ps2mouse.c 302408 2016-07-08 00:04:57Z gjb $"); 30 31#include <sys/types.h> 32 33#include <assert.h> 34#include <stdbool.h> 35#include <stdio.h> 36#include <stdlib.h> 37#include <strings.h> 38#include <pthread.h> 39#include <pthread_np.h> 40 41#include "atkbdc.h" 42#include "console.h" 43 44/* mouse device commands */ 45#define PS2MC_RESET_DEV 0xff 46#define PS2MC_SET_DEFAULTS 0xf6 47#define PS2MC_DISABLE 0xf5 48#define PS2MC_ENABLE 0xf4 49#define PS2MC_SET_SAMPLING_RATE 0xf3 50#define PS2MC_SEND_DEV_ID 0xf2 51#define PS2MC_SET_REMOTE_MODE 0xf0 52#define PS2MC_SEND_DEV_DATA 0xeb 53#define PS2MC_SET_STREAM_MODE 0xea 54#define PS2MC_SEND_DEV_STATUS 0xe9 55#define PS2MC_SET_RESOLUTION 0xe8 56#define PS2MC_SET_SCALING1 0xe7 57#define PS2MC_SET_SCALING2 0xe6 58 59#define PS2MC_BAT_SUCCESS 0xaa 60#define PS2MC_ACK 0xfa 61 62/* mouse device id */ 63#define PS2MOUSE_DEV_ID 0x0 64 65/* mouse status bits */ 66#define PS2M_STS_REMOTE_MODE 0x40 67#define PS2M_STS_ENABLE_DEV 0x20 68#define PS2M_STS_SCALING_21 0x10 69#define PS2M_STS_MID_BUTTON 0x04 70#define PS2M_STS_RIGHT_BUTTON 0x02 71#define PS2M_STS_LEFT_BUTTON 0x01 72 73#define PS2MOUSE_FIFOSZ 16 74 75struct fifo { 76 uint8_t buf[PS2MOUSE_FIFOSZ]; 77 int rindex; /* index to read from */ 78 int windex; /* index to write to */ 79 int num; /* number of bytes in the fifo */ 80 int size; /* size of the fifo */ 81}; 82 83struct ps2mouse_softc { 84 struct atkbdc_softc *atkbdc_sc; 85 pthread_mutex_t mtx; 86 87 uint8_t status; 88 uint8_t resolution; 89 uint8_t sampling_rate; 90 int ctrlenable; 91 struct fifo fifo; 92 93 uint8_t curcmd; /* current command for next byte */ 94 95 int cur_x, cur_y; 96 int delta_x, delta_y; 97}; 98 99static void 100fifo_init(struct ps2mouse_softc *sc) 101{ 102 struct fifo *fifo; 103 104 fifo = &sc->fifo; 105 fifo->size = sizeof(((struct fifo *)0)->buf); 106} 107 108static void 109fifo_reset(struct ps2mouse_softc *sc) 110{ 111 struct fifo *fifo; 112 113 fifo = &sc->fifo; 114 bzero(fifo, sizeof(struct fifo)); 115 fifo->size = sizeof(((struct fifo *)0)->buf); 116} 117 118static void 119fifo_put(struct ps2mouse_softc *sc, uint8_t val) 120{ 121 struct fifo *fifo; 122 123 fifo = &sc->fifo; 124 if (fifo->num < fifo->size) { 125 fifo->buf[fifo->windex] = val; 126 fifo->windex = (fifo->windex + 1) % fifo->size; 127 fifo->num++; 128 } 129} 130 131static int 132fifo_get(struct ps2mouse_softc *sc, uint8_t *val) 133{ 134 struct fifo *fifo; 135 136 fifo = &sc->fifo; 137 if (fifo->num > 0) { 138 *val = fifo->buf[fifo->rindex]; 139 fifo->rindex = (fifo->rindex + 1) % fifo->size; 140 fifo->num--; 141 return (0); 142 } 143 144 return (-1); 145} 146 147static void 148movement_reset(struct ps2mouse_softc *sc) 149{ 150 assert(pthread_mutex_isowned_np(&sc->mtx)); 151 152 sc->delta_x = 0; 153 sc->delta_y = 0; 154} 155 156static void 157movement_update(struct ps2mouse_softc *sc, int x, int y) 158{ 159 sc->delta_x += x - sc->cur_x; 160 sc->delta_y += sc->cur_y - y; 161 sc->cur_x = x; 162 sc->cur_y = y; 163} 164 165static void 166movement_get(struct ps2mouse_softc *sc) 167{ 168 uint8_t val0, val1, val2; 169 170 assert(pthread_mutex_isowned_np(&sc->mtx)); 171 172 val0 = sc->status & (PS2M_STS_LEFT_BUTTON | 173 PS2M_STS_RIGHT_BUTTON | PS2M_STS_MID_BUTTON); 174 175 if (sc->delta_x >= 0) { 176 if (sc->delta_x > 255) { 177 val0 |= (1 << 6); 178 val1 = 255; 179 } else 180 val1 = sc->delta_x; 181 } else { 182 val0 |= (1 << 4); 183 if (sc->delta_x < -255) { 184 val0 |= (1 << 6); 185 val1 = 255; 186 } else 187 val1 = sc->delta_x; 188 } 189 sc->delta_x = 0; 190 191 if (sc->delta_y >= 0) { 192 if (sc->delta_y > 255) { 193 val0 |= (1 << 7); 194 val2 = 255; 195 } else 196 val2 = sc->delta_y; 197 } else { 198 val0 |= (1 << 5); 199 if (sc->delta_y < -255) { 200 val0 |= (1 << 7); 201 val2 = 255; 202 } else 203 val2 = sc->delta_y; 204 } 205 sc->delta_y = 0; 206 207 if (sc->fifo.num < (sc->fifo.size - 3)) { 208 fifo_put(sc, val0); 209 fifo_put(sc, val1); 210 fifo_put(sc, val2); 211 } 212} 213 214static void 215ps2mouse_reset(struct ps2mouse_softc *sc) 216{ 217 assert(pthread_mutex_isowned_np(&sc->mtx)); 218 fifo_reset(sc); 219 movement_reset(sc); 220 sc->status = PS2M_STS_ENABLE_DEV; 221 sc->resolution = 4; 222 sc->sampling_rate = 100; 223 224 sc->cur_x = 0; 225 sc->cur_y = 0; 226 sc->delta_x = 0; 227 sc->delta_y = 0; 228} 229 230int 231ps2mouse_read(struct ps2mouse_softc *sc, uint8_t *val) 232{ 233 int retval; 234 235 pthread_mutex_lock(&sc->mtx); 236 retval = fifo_get(sc, val); 237 pthread_mutex_unlock(&sc->mtx); 238 239 return (retval); 240} 241 242int 243ps2mouse_fifocnt(struct ps2mouse_softc *sc) 244{ 245 return (sc->fifo.num); 246} 247 248void 249ps2mouse_toggle(struct ps2mouse_softc *sc, int enable) 250{ 251 pthread_mutex_lock(&sc->mtx); 252 if (enable) 253 sc->ctrlenable = 1; 254 else { 255 sc->ctrlenable = 0; 256 sc->fifo.rindex = 0; 257 sc->fifo.windex = 0; 258 sc->fifo.num = 0; 259 } 260 pthread_mutex_unlock(&sc->mtx); 261} 262 263void 264ps2mouse_write(struct ps2mouse_softc *sc, uint8_t val, int insert) 265{ 266 pthread_mutex_lock(&sc->mtx); 267 fifo_reset(sc); 268 if (sc->curcmd) { 269 switch (sc->curcmd) { 270 case PS2MC_SET_SAMPLING_RATE: 271 sc->sampling_rate = val; 272 fifo_put(sc, PS2MC_ACK); 273 break; 274 case PS2MC_SET_RESOLUTION: 275 sc->resolution = val; 276 fifo_put(sc, PS2MC_ACK); 277 break; 278 default: 279 fprintf(stderr, "Unhandled ps2 mouse current " 280 "command byte 0x%02x\n", val); 281 break; 282 } 283 sc->curcmd = 0; 284 285 } else if (insert) { 286 fifo_put(sc, val); 287 } else { 288 switch (val) { 289 case 0x00: 290 fifo_put(sc, PS2MC_ACK); 291 break; 292 case PS2MC_RESET_DEV: 293 ps2mouse_reset(sc); 294 fifo_put(sc, PS2MC_ACK); 295 fifo_put(sc, PS2MC_BAT_SUCCESS); 296 fifo_put(sc, PS2MOUSE_DEV_ID); 297 break; 298 case PS2MC_SET_DEFAULTS: 299 ps2mouse_reset(sc); 300 fifo_put(sc, PS2MC_ACK); 301 break; 302 case PS2MC_DISABLE: 303 fifo_reset(sc); 304 sc->status &= ~PS2M_STS_ENABLE_DEV; 305 fifo_put(sc, PS2MC_ACK); 306 break; 307 case PS2MC_ENABLE: 308 fifo_reset(sc); 309 sc->status |= PS2M_STS_ENABLE_DEV; 310 fifo_put(sc, PS2MC_ACK); 311 break; 312 case PS2MC_SET_SAMPLING_RATE: 313 sc->curcmd = val; 314 fifo_put(sc, PS2MC_ACK); 315 break; 316 case PS2MC_SEND_DEV_ID: 317 fifo_put(sc, PS2MC_ACK); 318 fifo_put(sc, PS2MOUSE_DEV_ID); 319 break; 320 case PS2MC_SET_REMOTE_MODE: 321 sc->status |= PS2M_STS_REMOTE_MODE; 322 fifo_put(sc, PS2MC_ACK); 323 break; 324 case PS2MC_SEND_DEV_DATA: 325 fifo_put(sc, PS2MC_ACK); 326 movement_get(sc); 327 break; 328 case PS2MC_SET_STREAM_MODE: 329 sc->status &= ~PS2M_STS_REMOTE_MODE; 330 fifo_put(sc, PS2MC_ACK); 331 break; 332 case PS2MC_SEND_DEV_STATUS: 333 fifo_put(sc, PS2MC_ACK); 334 fifo_put(sc, sc->status); 335 fifo_put(sc, sc->resolution); 336 fifo_put(sc, sc->sampling_rate); 337 break; 338 case PS2MC_SET_RESOLUTION: 339 sc->curcmd = val; 340 fifo_put(sc, PS2MC_ACK); 341 break; 342 case PS2MC_SET_SCALING1: 343 case PS2MC_SET_SCALING2: 344 fifo_put(sc, PS2MC_ACK); 345 break; 346 default: 347 fifo_put(sc, PS2MC_ACK); 348 fprintf(stderr, "Unhandled ps2 mouse command " 349 "0x%02x\n", val); 350 break; 351 } 352 } 353 pthread_mutex_unlock(&sc->mtx); 354} 355 356static void 357ps2mouse_event(uint8_t button, int x, int y, void *arg) 358{ 359 struct ps2mouse_softc *sc = arg; 360 361 pthread_mutex_lock(&sc->mtx); 362 movement_update(sc, x, y); 363 364 sc->status &= ~(PS2M_STS_LEFT_BUTTON | 365 PS2M_STS_RIGHT_BUTTON | PS2M_STS_MID_BUTTON); 366 if (button & (1 << 0)) 367 sc->status |= PS2M_STS_LEFT_BUTTON; 368 if (button & (1 << 1)) 369 sc->status |= PS2M_STS_MID_BUTTON; 370 if (button & (1 << 2)) 371 sc->status |= PS2M_STS_RIGHT_BUTTON; 372 373 if ((sc->status & PS2M_STS_ENABLE_DEV) == 0 || !sc->ctrlenable) { 374 /* no data reporting */ 375 pthread_mutex_unlock(&sc->mtx); 376 return; 377 } 378 379 movement_get(sc); 380 pthread_mutex_unlock(&sc->mtx); 381 382 if (sc->fifo.num > 0) 383 atkbdc_event(sc->atkbdc_sc, 0); 384} 385 386struct ps2mouse_softc * 387ps2mouse_init(struct atkbdc_softc *atkbdc_sc) 388{ 389 struct ps2mouse_softc *sc; 390 391 sc = calloc(1, sizeof (struct ps2mouse_softc)); 392 pthread_mutex_init(&sc->mtx, NULL); 393 fifo_init(sc); 394 sc->atkbdc_sc = atkbdc_sc; 395 396 pthread_mutex_lock(&sc->mtx); 397 ps2mouse_reset(sc); 398 pthread_mutex_unlock(&sc->mtx); 399 400 console_ptr_register(ps2mouse_event, sc, 1); 401 402 return (sc); 403} 404 405 406