xref: /illumos-gate/usr/src/cmd/bhyve/ps2mouse.c (revision 8124b811)
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