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