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