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$"); 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 75 struct 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 83 struct 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 struct fifo fifo; 91 92 uint8_t curcmd; /* current command for next byte */ 93 94 int cur_x, cur_y; 95 int delta_x, delta_y; 96 }; 97 98 static void 99 fifo_init(struct ps2mouse_softc *sc) 100 { 101 struct fifo *fifo; 102 103 fifo = &sc->fifo; 104 fifo->size = sizeof(((struct fifo *)0)->buf); 105 } 106 107 static void 108 fifo_reset(struct ps2mouse_softc *sc) 109 { 110 struct fifo *fifo; 111 112 fifo = &sc->fifo; 113 bzero(fifo, sizeof(struct fifo)); 114 fifo->size = sizeof(((struct fifo *)0)->buf); 115 } 116 117 static void 118 fifo_put(struct ps2mouse_softc *sc, uint8_t val) 119 { 120 struct fifo *fifo; 121 122 fifo = &sc->fifo; 123 if (fifo->num < fifo->size) { 124 fifo->buf[fifo->windex] = val; 125 fifo->windex = (fifo->windex + 1) % fifo->size; 126 fifo->num++; 127 } 128 } 129 130 static int 131 fifo_get(struct ps2mouse_softc *sc, uint8_t *val) 132 { 133 struct fifo *fifo; 134 135 fifo = &sc->fifo; 136 if (fifo->num > 0) { 137 *val = fifo->buf[fifo->rindex]; 138 fifo->rindex = (fifo->rindex + 1) % fifo->size; 139 fifo->num--; 140 return (0); 141 } 142 143 return (-1); 144 } 145 146 static void 147 movement_reset(struct ps2mouse_softc *sc) 148 { 149 assert(pthread_mutex_isowned_np(&sc->mtx)); 150 151 sc->delta_x = 0; 152 sc->delta_y = 0; 153 } 154 155 static void 156 movement_update(struct ps2mouse_softc *sc, int x, int y) 157 { 158 sc->delta_x += x - sc->cur_x; 159 sc->delta_y += sc->cur_y - y; 160 sc->cur_x = x; 161 sc->cur_y = y; 162 } 163 164 static void 165 movement_get(struct ps2mouse_softc *sc) 166 { 167 uint8_t val0, val1, val2; 168 169 assert(pthread_mutex_isowned_np(&sc->mtx)); 170 171 val0 = sc->status & (PS2M_STS_LEFT_BUTTON | 172 PS2M_STS_RIGHT_BUTTON | PS2M_STS_MID_BUTTON); 173 174 if (sc->delta_x >= 0) { 175 if (sc->delta_x > 255) { 176 val0 |= (1 << 6); 177 val1 = 255; 178 } else 179 val1 = sc->delta_x; 180 } else { 181 val0 |= (1 << 4); 182 if (sc->delta_x < -255) { 183 val0 |= (1 << 6); 184 val1 = 255; 185 } else 186 val1 = sc->delta_x; 187 } 188 sc->delta_x = 0; 189 190 if (sc->delta_y >= 0) { 191 if (sc->delta_y > 255) { 192 val0 |= (1 << 7); 193 val2 = 255; 194 } else 195 val2 = sc->delta_y; 196 } else { 197 val0 |= (1 << 5); 198 if (sc->delta_y < -255) { 199 val0 |= (1 << 7); 200 val2 = 255; 201 } else 202 val2 = sc->delta_y; 203 } 204 sc->delta_y = 0; 205 206 fifo_put(sc, val0); 207 fifo_put(sc, val1); 208 fifo_put(sc, val2); 209 } 210 211 static void 212 ps2mouse_reset(struct ps2mouse_softc *sc) 213 { 214 assert(pthread_mutex_isowned_np(&sc->mtx)); 215 fifo_reset(sc); 216 movement_reset(sc); 217 sc->status = 0x8; 218 sc->resolution = 4; 219 sc->sampling_rate = 100; 220 221 sc->cur_x = 0; 222 sc->cur_y = 0; 223 sc->delta_x = 0; 224 sc->delta_y = 0; 225 } 226 227 int 228 ps2mouse_read(struct ps2mouse_softc *sc, uint8_t *val) 229 { 230 int retval; 231 232 pthread_mutex_lock(&sc->mtx); 233 retval = fifo_get(sc, val); 234 pthread_mutex_unlock(&sc->mtx); 235 236 return (retval); 237 } 238 239 void 240 ps2mouse_write(struct ps2mouse_softc *sc, uint8_t val) 241 { 242 pthread_mutex_lock(&sc->mtx); 243 if (sc->curcmd) { 244 switch (sc->curcmd) { 245 case PS2MC_SET_SAMPLING_RATE: 246 sc->sampling_rate = val; 247 fifo_put(sc, PS2MC_ACK); 248 break; 249 case PS2MC_SET_RESOLUTION: 250 sc->resolution = val; 251 fifo_put(sc, PS2MC_ACK); 252 break; 253 default: 254 fprintf(stderr, "Unhandled ps2 mouse current " 255 "command byte 0x%02x\n", val); 256 break; 257 } 258 sc->curcmd = 0; 259 } else { 260 switch (val) { 261 case PS2MC_RESET_DEV: 262 ps2mouse_reset(sc); 263 fifo_put(sc, PS2MC_ACK); 264 fifo_put(sc, PS2MC_BAT_SUCCESS); 265 fifo_put(sc, PS2MOUSE_DEV_ID); 266 break; 267 case PS2MC_SET_DEFAULTS: 268 ps2mouse_reset(sc); 269 fifo_put(sc, PS2MC_ACK); 270 break; 271 case PS2MC_DISABLE: 272 fifo_reset(sc); 273 sc->status &= ~PS2M_STS_ENABLE_DEV; 274 fifo_put(sc, PS2MC_ACK); 275 break; 276 case PS2MC_ENABLE: 277 fifo_reset(sc); 278 sc->status |= PS2M_STS_ENABLE_DEV; 279 fifo_put(sc, PS2MC_ACK); 280 break; 281 case PS2MC_SET_SAMPLING_RATE: 282 sc->curcmd = val; 283 fifo_put(sc, PS2MC_ACK); 284 break; 285 case PS2MC_SEND_DEV_ID: 286 fifo_put(sc, PS2MC_ACK); 287 fifo_put(sc, PS2MOUSE_DEV_ID); 288 break; 289 case PS2MC_SET_REMOTE_MODE: 290 sc->status |= PS2M_STS_REMOTE_MODE; 291 fifo_put(sc, PS2MC_ACK); 292 break; 293 case PS2MC_SEND_DEV_DATA: 294 fifo_put(sc, PS2MC_ACK); 295 movement_get(sc); 296 break; 297 case PS2MC_SET_STREAM_MODE: 298 sc->status &= ~PS2M_STS_REMOTE_MODE; 299 fifo_put(sc, PS2MC_ACK); 300 break; 301 case PS2MC_SEND_DEV_STATUS: 302 fifo_put(sc, PS2MC_ACK); 303 fifo_put(sc, sc->status); 304 fifo_put(sc, sc->resolution); 305 fifo_put(sc, sc->sampling_rate); 306 break; 307 case PS2MC_SET_RESOLUTION: 308 sc->curcmd = val; 309 fifo_put(sc, PS2MC_ACK); 310 break; 311 case PS2MC_SET_SCALING1: 312 case PS2MC_SET_SCALING2: 313 fifo_put(sc, PS2MC_ACK); 314 break; 315 default: 316 fprintf(stderr, "Unhandled ps2 mouse command " 317 "0x%02x\n", val); 318 break; 319 } 320 } 321 pthread_mutex_unlock(&sc->mtx); 322 } 323 324 static void 325 ps2mouse_event(uint8_t button, int x, int y, void *arg) 326 { 327 struct ps2mouse_softc *sc = arg; 328 329 pthread_mutex_lock(&sc->mtx); 330 movement_update(sc, x, y); 331 332 sc->status &= ~(PS2M_STS_LEFT_BUTTON | 333 PS2M_STS_RIGHT_BUTTON | PS2M_STS_MID_BUTTON); 334 if (button & (1 << 0)) 335 sc->status |= PS2M_STS_LEFT_BUTTON; 336 if (button & (1 << 1)) 337 sc->status |= PS2M_STS_MID_BUTTON; 338 if (button & (1 << 2)) 339 sc->status |= PS2M_STS_RIGHT_BUTTON; 340 341 if ((sc->status & PS2M_STS_ENABLE_DEV) == 0) { 342 /* no data reporting */ 343 pthread_mutex_unlock(&sc->mtx); 344 return; 345 } 346 347 movement_get(sc); 348 pthread_mutex_unlock(&sc->mtx); 349 350 atkbdc_event(sc->atkbdc_sc); 351 } 352 353 struct ps2mouse_softc * 354 ps2mouse_init(struct atkbdc_softc *atkbdc_sc) 355 { 356 struct ps2mouse_softc *sc; 357 358 sc = calloc(1, sizeof (struct ps2mouse_softc)); 359 pthread_mutex_init(&sc->mtx, NULL); 360 fifo_init(sc); 361 sc->atkbdc_sc = atkbdc_sc; 362 363 pthread_mutex_lock(&sc->mtx); 364 ps2mouse_reset(sc); 365 pthread_mutex_unlock(&sc->mtx); 366 367 console_ptr_register(ps2mouse_event, sc); 368 369 return (sc); 370 } 371 372