1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
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 #include <sys/types.h>
32
33 #include <machine/vmm_snapshot.h>
34
35 #include <assert.h>
36 #include <stdbool.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <strings.h>
41 #include <pthread.h>
42 #include <pthread_np.h>
43
44 #include "atkbdc.h"
45 #include "console.h"
46 #include "debug.h"
47 #include "ps2mouse.h"
48
49 /* mouse device commands */
50 #define PS2MC_RESET_DEV 0xff
51 #define PS2MC_SET_DEFAULTS 0xf6
52 #define PS2MC_DISABLE 0xf5
53 #define PS2MC_ENABLE 0xf4
54 #define PS2MC_SET_SAMPLING_RATE 0xf3
55 #define PS2MC_SEND_DEV_ID 0xf2
56 #define PS2MC_SET_REMOTE_MODE 0xf0
57 #define PS2MC_SEND_DEV_DATA 0xeb
58 #define PS2MC_SET_STREAM_MODE 0xea
59 #define PS2MC_SEND_DEV_STATUS 0xe9
60 #define PS2MC_SET_RESOLUTION 0xe8
61 #define PS2MC_SET_SCALING1 0xe7
62 #define PS2MC_SET_SCALING2 0xe6
63
64 #define PS2MC_BAT_SUCCESS 0xaa
65 #define PS2MC_ACK 0xfa
66
67 /* mouse device id */
68 #define PS2MOUSE_DEV_ID 0x0
69
70 /* mouse data bits */
71 #define PS2M_DATA_Y_OFLOW 0x80
72 #define PS2M_DATA_X_OFLOW 0x40
73 #define PS2M_DATA_Y_SIGN 0x20
74 #define PS2M_DATA_X_SIGN 0x10
75 #define PS2M_DATA_AONE 0x08
76 #define PS2M_DATA_MID_BUTTON 0x04
77 #define PS2M_DATA_RIGHT_BUTTON 0x02
78 #define PS2M_DATA_LEFT_BUTTON 0x01
79
80 /* mouse status bits */
81 #define PS2M_STS_REMOTE_MODE 0x40
82 #define PS2M_STS_ENABLE_DEV 0x20
83 #define PS2M_STS_SCALING_21 0x10
84 #define PS2M_STS_MID_BUTTON 0x04
85 #define PS2M_STS_RIGHT_BUTTON 0x02
86 #define PS2M_STS_LEFT_BUTTON 0x01
87
88 #define PS2MOUSE_FIFOSZ 16
89
90 struct fifo {
91 uint8_t buf[PS2MOUSE_FIFOSZ];
92 int rindex; /* index to read from */
93 int windex; /* index to write to */
94 int num; /* number of bytes in the fifo */
95 int size; /* size of the fifo */
96 };
97
98 struct ps2mouse_softc {
99 struct atkbdc_softc *atkbdc_sc;
100 pthread_mutex_t mtx;
101
102 uint8_t status;
103 uint8_t resolution;
104 uint8_t sampling_rate;
105 int ctrlenable;
106 struct fifo fifo;
107
108 uint8_t curcmd; /* current command for next byte */
109
110 int cur_x, cur_y;
111 int delta_x, delta_y;
112 };
113
114 static void
fifo_init(struct ps2mouse_softc * sc)115 fifo_init(struct ps2mouse_softc *sc)
116 {
117 struct fifo *fifo;
118
119 fifo = &sc->fifo;
120 fifo->size = sizeof(((struct fifo *)0)->buf);
121 }
122
123 static void
fifo_reset(struct ps2mouse_softc * sc)124 fifo_reset(struct ps2mouse_softc *sc)
125 {
126 struct fifo *fifo;
127
128 fifo = &sc->fifo;
129 bzero(fifo, sizeof(struct fifo));
130 fifo->size = sizeof(((struct fifo *)0)->buf);
131 }
132
133 static void
fifo_put(struct ps2mouse_softc * sc,uint8_t val)134 fifo_put(struct ps2mouse_softc *sc, uint8_t val)
135 {
136 struct fifo *fifo;
137
138 fifo = &sc->fifo;
139 if (fifo->num < fifo->size) {
140 fifo->buf[fifo->windex] = val;
141 fifo->windex = (fifo->windex + 1) % fifo->size;
142 fifo->num++;
143 }
144 }
145
146 static int
fifo_get(struct ps2mouse_softc * sc,uint8_t * val)147 fifo_get(struct ps2mouse_softc *sc, uint8_t *val)
148 {
149 struct fifo *fifo;
150
151 fifo = &sc->fifo;
152 if (fifo->num > 0) {
153 *val = fifo->buf[fifo->rindex];
154 fifo->rindex = (fifo->rindex + 1) % fifo->size;
155 fifo->num--;
156 return (0);
157 }
158
159 return (-1);
160 }
161
162 static void
movement_reset(struct ps2mouse_softc * sc)163 movement_reset(struct ps2mouse_softc *sc)
164 {
165 assert(pthread_mutex_isowned_np(&sc->mtx));
166
167 sc->delta_x = 0;
168 sc->delta_y = 0;
169 }
170
171 static void
movement_update(struct ps2mouse_softc * sc,int x,int y)172 movement_update(struct ps2mouse_softc *sc, int x, int y)
173 {
174 sc->delta_x += x - sc->cur_x;
175 sc->delta_y += sc->cur_y - y;
176 sc->cur_x = x;
177 sc->cur_y = y;
178 }
179
180 static void
movement_get(struct ps2mouse_softc * sc)181 movement_get(struct ps2mouse_softc *sc)
182 {
183 uint8_t val0, val1, val2;
184
185 assert(pthread_mutex_isowned_np(&sc->mtx));
186
187 val0 = PS2M_DATA_AONE;
188 val0 |= sc->status & (PS2M_DATA_LEFT_BUTTON |
189 PS2M_DATA_RIGHT_BUTTON | PS2M_DATA_MID_BUTTON);
190
191 if (sc->delta_x >= 0) {
192 if (sc->delta_x > 255) {
193 val0 |= PS2M_DATA_X_OFLOW;
194 val1 = 255;
195 } else
196 val1 = sc->delta_x;
197 } else {
198 val0 |= PS2M_DATA_X_SIGN;
199 if (sc->delta_x < -255) {
200 val0 |= PS2M_DATA_X_OFLOW;
201 val1 = 255;
202 } else
203 val1 = sc->delta_x;
204 }
205 sc->delta_x = 0;
206
207 if (sc->delta_y >= 0) {
208 if (sc->delta_y > 255) {
209 val0 |= PS2M_DATA_Y_OFLOW;
210 val2 = 255;
211 } else
212 val2 = sc->delta_y;
213 } else {
214 val0 |= PS2M_DATA_Y_SIGN;
215 if (sc->delta_y < -255) {
216 val0 |= PS2M_DATA_Y_OFLOW;
217 val2 = 255;
218 } else
219 val2 = sc->delta_y;
220 }
221 sc->delta_y = 0;
222
223 if (sc->fifo.num < (sc->fifo.size - 3)) {
224 fifo_put(sc, val0);
225 fifo_put(sc, val1);
226 fifo_put(sc, val2);
227 }
228 }
229
230 static void
ps2mouse_reset(struct ps2mouse_softc * sc)231 ps2mouse_reset(struct ps2mouse_softc *sc)
232 {
233 assert(pthread_mutex_isowned_np(&sc->mtx));
234 fifo_reset(sc);
235 movement_reset(sc);
236 sc->status = PS2M_STS_ENABLE_DEV;
237 sc->resolution = 4;
238 sc->sampling_rate = 100;
239
240 sc->cur_x = 0;
241 sc->cur_y = 0;
242 sc->delta_x = 0;
243 sc->delta_y = 0;
244 }
245
246 int
ps2mouse_read(struct ps2mouse_softc * sc,uint8_t * val)247 ps2mouse_read(struct ps2mouse_softc *sc, uint8_t *val)
248 {
249 int retval;
250
251 pthread_mutex_lock(&sc->mtx);
252 retval = fifo_get(sc, val);
253 pthread_mutex_unlock(&sc->mtx);
254
255 return (retval);
256 }
257
258 int
ps2mouse_fifocnt(struct ps2mouse_softc * sc)259 ps2mouse_fifocnt(struct ps2mouse_softc *sc)
260 {
261 return (sc->fifo.num);
262 }
263
264 void
ps2mouse_toggle(struct ps2mouse_softc * sc,int enable)265 ps2mouse_toggle(struct ps2mouse_softc *sc, int enable)
266 {
267 pthread_mutex_lock(&sc->mtx);
268 if (enable)
269 sc->ctrlenable = 1;
270 else {
271 sc->ctrlenable = 0;
272 sc->fifo.rindex = 0;
273 sc->fifo.windex = 0;
274 sc->fifo.num = 0;
275 }
276 pthread_mutex_unlock(&sc->mtx);
277 }
278
279 void
ps2mouse_write(struct ps2mouse_softc * sc,uint8_t val,int insert)280 ps2mouse_write(struct ps2mouse_softc *sc, uint8_t val, int insert)
281 {
282 pthread_mutex_lock(&sc->mtx);
283 fifo_reset(sc);
284 if (sc->curcmd) {
285 switch (sc->curcmd) {
286 case PS2MC_SET_SAMPLING_RATE:
287 sc->sampling_rate = val;
288 fifo_put(sc, PS2MC_ACK);
289 break;
290 case PS2MC_SET_RESOLUTION:
291 sc->resolution = val;
292 fifo_put(sc, PS2MC_ACK);
293 break;
294 default:
295 EPRINTLN("Unhandled ps2 mouse current "
296 "command byte 0x%02x", val);
297 break;
298 }
299 sc->curcmd = 0;
300
301 } else if (insert) {
302 fifo_put(sc, val);
303 } else {
304 switch (val) {
305 case 0x00:
306 fifo_put(sc, PS2MC_ACK);
307 break;
308 case PS2MC_RESET_DEV:
309 ps2mouse_reset(sc);
310 fifo_put(sc, PS2MC_ACK);
311 fifo_put(sc, PS2MC_BAT_SUCCESS);
312 fifo_put(sc, PS2MOUSE_DEV_ID);
313 break;
314 case PS2MC_SET_DEFAULTS:
315 ps2mouse_reset(sc);
316 fifo_put(sc, PS2MC_ACK);
317 break;
318 case PS2MC_DISABLE:
319 fifo_reset(sc);
320 sc->status &= ~PS2M_STS_ENABLE_DEV;
321 fifo_put(sc, PS2MC_ACK);
322 break;
323 case PS2MC_ENABLE:
324 fifo_reset(sc);
325 sc->status |= PS2M_STS_ENABLE_DEV;
326 fifo_put(sc, PS2MC_ACK);
327 break;
328 case PS2MC_SET_SAMPLING_RATE:
329 sc->curcmd = val;
330 fifo_put(sc, PS2MC_ACK);
331 break;
332 case PS2MC_SEND_DEV_ID:
333 fifo_put(sc, PS2MC_ACK);
334 fifo_put(sc, PS2MOUSE_DEV_ID);
335 break;
336 case PS2MC_SET_REMOTE_MODE:
337 sc->status |= PS2M_STS_REMOTE_MODE;
338 fifo_put(sc, PS2MC_ACK);
339 break;
340 case PS2MC_SEND_DEV_DATA:
341 fifo_put(sc, PS2MC_ACK);
342 movement_get(sc);
343 break;
344 case PS2MC_SET_STREAM_MODE:
345 sc->status &= ~PS2M_STS_REMOTE_MODE;
346 fifo_put(sc, PS2MC_ACK);
347 break;
348 case PS2MC_SEND_DEV_STATUS:
349 fifo_put(sc, PS2MC_ACK);
350 fifo_put(sc, sc->status);
351 fifo_put(sc, sc->resolution);
352 fifo_put(sc, sc->sampling_rate);
353 break;
354 case PS2MC_SET_RESOLUTION:
355 sc->curcmd = val;
356 fifo_put(sc, PS2MC_ACK);
357 break;
358 case PS2MC_SET_SCALING1:
359 case PS2MC_SET_SCALING2:
360 fifo_put(sc, PS2MC_ACK);
361 break;
362 default:
363 fifo_put(sc, PS2MC_ACK);
364 EPRINTLN("Unhandled ps2 mouse command "
365 "0x%02x", val);
366 break;
367 }
368 }
369 pthread_mutex_unlock(&sc->mtx);
370 }
371
372 static void
ps2mouse_event(uint8_t button,int x,int y,void * arg)373 ps2mouse_event(uint8_t button, int x, int y, void *arg)
374 {
375 struct ps2mouse_softc *sc = arg;
376
377 pthread_mutex_lock(&sc->mtx);
378 movement_update(sc, x, y);
379
380 sc->status &= ~(PS2M_STS_LEFT_BUTTON |
381 PS2M_STS_RIGHT_BUTTON | PS2M_STS_MID_BUTTON);
382 if (button & (1 << 0))
383 sc->status |= PS2M_STS_LEFT_BUTTON;
384 if (button & (1 << 1))
385 sc->status |= PS2M_STS_MID_BUTTON;
386 if (button & (1 << 2))
387 sc->status |= PS2M_STS_RIGHT_BUTTON;
388
389 if ((sc->status & PS2M_STS_ENABLE_DEV) == 0 || !sc->ctrlenable) {
390 /* no data reporting */
391 pthread_mutex_unlock(&sc->mtx);
392 return;
393 }
394
395 movement_get(sc);
396 pthread_mutex_unlock(&sc->mtx);
397
398 if (sc->fifo.num > 0)
399 atkbdc_event(sc->atkbdc_sc, 0);
400 }
401
402 struct ps2mouse_softc *
ps2mouse_init(struct atkbdc_softc * atkbdc_sc)403 ps2mouse_init(struct atkbdc_softc *atkbdc_sc)
404 {
405 struct ps2mouse_softc *sc;
406
407 sc = calloc(1, sizeof (struct ps2mouse_softc));
408 pthread_mutex_init(&sc->mtx, NULL);
409 fifo_init(sc);
410 sc->atkbdc_sc = atkbdc_sc;
411
412 pthread_mutex_lock(&sc->mtx);
413 ps2mouse_reset(sc);
414 pthread_mutex_unlock(&sc->mtx);
415
416 console_ptr_register(ps2mouse_event, sc, 1);
417
418 return (sc);
419 }
420
421 #ifdef BHYVE_SNAPSHOT
422 int
ps2mouse_snapshot(struct ps2mouse_softc * sc,struct vm_snapshot_meta * meta)423 ps2mouse_snapshot(struct ps2mouse_softc *sc, struct vm_snapshot_meta *meta)
424 {
425 int ret;
426
427 SNAPSHOT_VAR_OR_LEAVE(sc->status, meta, ret, done);
428 SNAPSHOT_VAR_OR_LEAVE(sc->resolution, meta, ret, done);
429 SNAPSHOT_VAR_OR_LEAVE(sc->sampling_rate, meta, ret, done);
430 SNAPSHOT_VAR_OR_LEAVE(sc->ctrlenable, meta, ret, done);
431 SNAPSHOT_VAR_OR_LEAVE(sc->curcmd, meta, ret, done);
432 SNAPSHOT_VAR_OR_LEAVE(sc->cur_x, meta, ret, done);
433 SNAPSHOT_VAR_OR_LEAVE(sc->cur_y, meta, ret, done);
434 SNAPSHOT_VAR_OR_LEAVE(sc->delta_x, meta, ret, done);
435 SNAPSHOT_VAR_OR_LEAVE(sc->delta_y, meta, ret, done);
436
437 done:
438 return (ret);
439 }
440 #endif
441