xref: /freebsd/usr.sbin/bhyve/amd64/ps2mouse.c (revision a2f733abcff64628b7771a47089628b7327a88bd)
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
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
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
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
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
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
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
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
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
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
259 ps2mouse_fifocnt(struct ps2mouse_softc *sc)
260 {
261 	return (sc->fifo.num);
262 }
263 
264 void
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
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
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 *
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
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