1300829Sgrehan/*-
2300829Sgrehan * Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
3300829Sgrehan * Copyright (c) 2015 Nahanni Systems Inc.
4300829Sgrehan * All rights reserved.
5300829Sgrehan *
6300829Sgrehan * Redistribution and use in source and binary forms, with or without
7300829Sgrehan * modification, are permitted provided that the following conditions
8300829Sgrehan * are met:
9300829Sgrehan * 1. Redistributions of source code must retain the above copyright
10300829Sgrehan *    notice, this list of conditions and the following disclaimer.
11300829Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
12300829Sgrehan *    notice, this list of conditions and the following disclaimer in the
13300829Sgrehan *    documentation and/or other materials provided with the distribution.
14300829Sgrehan *
15300829Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
16300829Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17300829Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18300829Sgrehan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19300829Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20300829Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21300829Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22300829Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23300829Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24300829Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25300829Sgrehan * SUCH DAMAGE.
26300829Sgrehan */
27300829Sgrehan
28300829Sgrehan#include <sys/cdefs.h>
29300829Sgrehan__FBSDID("$FreeBSD: releng/11.0/usr.sbin/bhyve/ps2mouse.c 302408 2016-07-08 00:04:57Z gjb $");
30300829Sgrehan
31300829Sgrehan#include <sys/types.h>
32300829Sgrehan
33300829Sgrehan#include <assert.h>
34300829Sgrehan#include <stdbool.h>
35300829Sgrehan#include <stdio.h>
36300829Sgrehan#include <stdlib.h>
37300829Sgrehan#include <strings.h>
38300829Sgrehan#include <pthread.h>
39300829Sgrehan#include <pthread_np.h>
40300829Sgrehan
41300829Sgrehan#include "atkbdc.h"
42300829Sgrehan#include "console.h"
43300829Sgrehan
44300829Sgrehan/* mouse device commands */
45300829Sgrehan#define	PS2MC_RESET_DEV		0xff
46300829Sgrehan#define	PS2MC_SET_DEFAULTS	0xf6
47300829Sgrehan#define	PS2MC_DISABLE		0xf5
48300829Sgrehan#define	PS2MC_ENABLE		0xf4
49300829Sgrehan#define	PS2MC_SET_SAMPLING_RATE	0xf3
50300829Sgrehan#define	PS2MC_SEND_DEV_ID	0xf2
51300829Sgrehan#define	PS2MC_SET_REMOTE_MODE	0xf0
52300829Sgrehan#define	PS2MC_SEND_DEV_DATA	0xeb
53300829Sgrehan#define	PS2MC_SET_STREAM_MODE	0xea
54300829Sgrehan#define	PS2MC_SEND_DEV_STATUS	0xe9
55300829Sgrehan#define	PS2MC_SET_RESOLUTION	0xe8
56300829Sgrehan#define	PS2MC_SET_SCALING1	0xe7
57300829Sgrehan#define	PS2MC_SET_SCALING2	0xe6
58300829Sgrehan
59300829Sgrehan#define	PS2MC_BAT_SUCCESS	0xaa
60300829Sgrehan#define	PS2MC_ACK		0xfa
61300829Sgrehan
62300829Sgrehan/* mouse device id */
63300829Sgrehan#define	PS2MOUSE_DEV_ID		0x0
64300829Sgrehan
65300829Sgrehan/* mouse status bits */
66300829Sgrehan#define	PS2M_STS_REMOTE_MODE	0x40
67300829Sgrehan#define	PS2M_STS_ENABLE_DEV	0x20
68300829Sgrehan#define	PS2M_STS_SCALING_21	0x10
69300829Sgrehan#define	PS2M_STS_MID_BUTTON	0x04
70300829Sgrehan#define	PS2M_STS_RIGHT_BUTTON	0x02
71300829Sgrehan#define	PS2M_STS_LEFT_BUTTON	0x01
72300829Sgrehan
73300829Sgrehan#define	PS2MOUSE_FIFOSZ		16
74300829Sgrehan
75300829Sgrehanstruct fifo {
76300829Sgrehan	uint8_t	buf[PS2MOUSE_FIFOSZ];
77300829Sgrehan	int	rindex;		/* index to read from */
78300829Sgrehan	int	windex;		/* index to write to */
79300829Sgrehan	int	num;		/* number of bytes in the fifo */
80300829Sgrehan	int	size;		/* size of the fifo */
81300829Sgrehan};
82300829Sgrehan
83300829Sgrehanstruct ps2mouse_softc {
84300829Sgrehan	struct atkbdc_softc	*atkbdc_sc;
85300829Sgrehan	pthread_mutex_t		mtx;
86300829Sgrehan
87300829Sgrehan	uint8_t		status;
88300829Sgrehan	uint8_t		resolution;
89300829Sgrehan	uint8_t		sampling_rate;
90300829Sgrehan	int		ctrlenable;
91300829Sgrehan	struct fifo	fifo;
92300829Sgrehan
93300829Sgrehan	uint8_t		curcmd;	/* current command for next byte */
94300829Sgrehan
95300829Sgrehan	int		cur_x, cur_y;
96300829Sgrehan	int		delta_x, delta_y;
97300829Sgrehan};
98300829Sgrehan
99300829Sgrehanstatic void
100300829Sgrehanfifo_init(struct ps2mouse_softc *sc)
101300829Sgrehan{
102300829Sgrehan	struct fifo *fifo;
103300829Sgrehan
104300829Sgrehan	fifo = &sc->fifo;
105300829Sgrehan	fifo->size = sizeof(((struct fifo *)0)->buf);
106300829Sgrehan}
107300829Sgrehan
108300829Sgrehanstatic void
109300829Sgrehanfifo_reset(struct ps2mouse_softc *sc)
110300829Sgrehan{
111300829Sgrehan	struct fifo *fifo;
112300829Sgrehan
113300829Sgrehan	fifo = &sc->fifo;
114300829Sgrehan	bzero(fifo, sizeof(struct fifo));
115300829Sgrehan	fifo->size = sizeof(((struct fifo *)0)->buf);
116300829Sgrehan}
117300829Sgrehan
118300829Sgrehanstatic void
119300829Sgrehanfifo_put(struct ps2mouse_softc *sc, uint8_t val)
120300829Sgrehan{
121300829Sgrehan	struct fifo *fifo;
122300829Sgrehan
123300829Sgrehan	fifo = &sc->fifo;
124300829Sgrehan	if (fifo->num < fifo->size) {
125300829Sgrehan		fifo->buf[fifo->windex] = val;
126300829Sgrehan		fifo->windex = (fifo->windex + 1) % fifo->size;
127300829Sgrehan		fifo->num++;
128300829Sgrehan	}
129300829Sgrehan}
130300829Sgrehan
131300829Sgrehanstatic int
132300829Sgrehanfifo_get(struct ps2mouse_softc *sc, uint8_t *val)
133300829Sgrehan{
134300829Sgrehan	struct fifo *fifo;
135300829Sgrehan
136300829Sgrehan	fifo = &sc->fifo;
137300829Sgrehan	if (fifo->num > 0) {
138300829Sgrehan		*val = fifo->buf[fifo->rindex];
139300829Sgrehan		fifo->rindex = (fifo->rindex + 1) % fifo->size;
140300829Sgrehan		fifo->num--;
141300829Sgrehan		return (0);
142300829Sgrehan	}
143300829Sgrehan
144300829Sgrehan	return (-1);
145300829Sgrehan}
146300829Sgrehan
147300829Sgrehanstatic void
148300829Sgrehanmovement_reset(struct ps2mouse_softc *sc)
149300829Sgrehan{
150300829Sgrehan	assert(pthread_mutex_isowned_np(&sc->mtx));
151300829Sgrehan
152300829Sgrehan	sc->delta_x = 0;
153300829Sgrehan	sc->delta_y = 0;
154300829Sgrehan}
155300829Sgrehan
156300829Sgrehanstatic void
157300829Sgrehanmovement_update(struct ps2mouse_softc *sc, int x, int y)
158300829Sgrehan{
159300829Sgrehan	sc->delta_x += x - sc->cur_x;
160300829Sgrehan	sc->delta_y += sc->cur_y - y;
161300829Sgrehan	sc->cur_x = x;
162300829Sgrehan	sc->cur_y = y;
163300829Sgrehan}
164300829Sgrehan
165300829Sgrehanstatic void
166300829Sgrehanmovement_get(struct ps2mouse_softc *sc)
167300829Sgrehan{
168300829Sgrehan	uint8_t val0, val1, val2;
169300829Sgrehan
170300829Sgrehan	assert(pthread_mutex_isowned_np(&sc->mtx));
171300829Sgrehan
172300829Sgrehan	val0 = 	sc->status & (PS2M_STS_LEFT_BUTTON |
173300829Sgrehan	    PS2M_STS_RIGHT_BUTTON | PS2M_STS_MID_BUTTON);
174300829Sgrehan
175300829Sgrehan	if (sc->delta_x >= 0) {
176300829Sgrehan		if (sc->delta_x > 255) {
177300829Sgrehan			val0 |= (1 << 6);
178300829Sgrehan			val1 = 255;
179300829Sgrehan		} else
180300829Sgrehan			val1 = sc->delta_x;
181300829Sgrehan	} else {
182300829Sgrehan		val0 |= (1 << 4);
183300829Sgrehan		if (sc->delta_x < -255) {
184300829Sgrehan			val0 |= (1 << 6);
185300829Sgrehan			val1 = 255;
186300829Sgrehan		} else
187300829Sgrehan			val1 = sc->delta_x;
188300829Sgrehan	}
189300829Sgrehan	sc->delta_x = 0;
190300829Sgrehan
191300829Sgrehan	if (sc->delta_y >= 0) {
192300829Sgrehan		if (sc->delta_y > 255) {
193300829Sgrehan			val0 |= (1 << 7);
194300829Sgrehan			val2 = 255;
195300829Sgrehan		} else
196300829Sgrehan			val2 = sc->delta_y;
197300829Sgrehan	} else {
198300829Sgrehan		val0 |= (1 << 5);
199300829Sgrehan		if (sc->delta_y < -255) {
200300829Sgrehan			val0 |= (1 << 7);
201300829Sgrehan			val2 = 255;
202300829Sgrehan		} else
203300829Sgrehan			val2 = sc->delta_y;
204300829Sgrehan	}
205300829Sgrehan	sc->delta_y = 0;
206300829Sgrehan
207300829Sgrehan	if (sc->fifo.num < (sc->fifo.size - 3)) {
208300829Sgrehan		fifo_put(sc, val0);
209300829Sgrehan		fifo_put(sc, val1);
210300829Sgrehan		fifo_put(sc, val2);
211300829Sgrehan	}
212300829Sgrehan}
213300829Sgrehan
214300829Sgrehanstatic void
215300829Sgrehanps2mouse_reset(struct ps2mouse_softc *sc)
216300829Sgrehan{
217300829Sgrehan	assert(pthread_mutex_isowned_np(&sc->mtx));
218300829Sgrehan	fifo_reset(sc);
219300829Sgrehan	movement_reset(sc);
220300829Sgrehan	sc->status = PS2M_STS_ENABLE_DEV;
221300829Sgrehan	sc->resolution = 4;
222300829Sgrehan	sc->sampling_rate = 100;
223300829Sgrehan
224300829Sgrehan	sc->cur_x = 0;
225300829Sgrehan	sc->cur_y = 0;
226300829Sgrehan	sc->delta_x = 0;
227300829Sgrehan	sc->delta_y = 0;
228300829Sgrehan}
229300829Sgrehan
230300829Sgrehanint
231300829Sgrehanps2mouse_read(struct ps2mouse_softc *sc, uint8_t *val)
232300829Sgrehan{
233300829Sgrehan	int retval;
234300829Sgrehan
235300829Sgrehan	pthread_mutex_lock(&sc->mtx);
236300829Sgrehan	retval = fifo_get(sc, val);
237300829Sgrehan	pthread_mutex_unlock(&sc->mtx);
238300829Sgrehan
239300829Sgrehan	return (retval);
240300829Sgrehan}
241300829Sgrehan
242300829Sgrehanint
243300829Sgrehanps2mouse_fifocnt(struct ps2mouse_softc *sc)
244300829Sgrehan{
245300829Sgrehan	return (sc->fifo.num);
246300829Sgrehan}
247300829Sgrehan
248300829Sgrehanvoid
249300829Sgrehanps2mouse_toggle(struct ps2mouse_softc *sc, int enable)
250300829Sgrehan{
251300829Sgrehan	pthread_mutex_lock(&sc->mtx);
252300829Sgrehan	if (enable)
253300829Sgrehan		sc->ctrlenable = 1;
254300829Sgrehan	else {
255300829Sgrehan		sc->ctrlenable = 0;
256300829Sgrehan		sc->fifo.rindex = 0;
257300829Sgrehan		sc->fifo.windex = 0;
258300829Sgrehan		sc->fifo.num = 0;
259300829Sgrehan	}
260300829Sgrehan	pthread_mutex_unlock(&sc->mtx);
261300829Sgrehan}
262300829Sgrehan
263300829Sgrehanvoid
264300829Sgrehanps2mouse_write(struct ps2mouse_softc *sc, uint8_t val, int insert)
265300829Sgrehan{
266300829Sgrehan	pthread_mutex_lock(&sc->mtx);
267300829Sgrehan	fifo_reset(sc);
268300829Sgrehan	if (sc->curcmd) {
269300829Sgrehan		switch (sc->curcmd) {
270300829Sgrehan		case PS2MC_SET_SAMPLING_RATE:
271300829Sgrehan			sc->sampling_rate = val;
272300829Sgrehan			fifo_put(sc, PS2MC_ACK);
273300829Sgrehan			break;
274300829Sgrehan		case PS2MC_SET_RESOLUTION:
275300829Sgrehan			sc->resolution = val;
276300829Sgrehan			fifo_put(sc, PS2MC_ACK);
277300829Sgrehan			break;
278300829Sgrehan		default:
279300829Sgrehan			fprintf(stderr, "Unhandled ps2 mouse current "
280300829Sgrehan			    "command byte 0x%02x\n", val);
281300829Sgrehan			break;
282300829Sgrehan		}
283300829Sgrehan		sc->curcmd = 0;
284300829Sgrehan
285300829Sgrehan	} else if (insert) {
286300829Sgrehan		fifo_put(sc, val);
287300829Sgrehan	} else {
288300829Sgrehan		switch (val) {
289300829Sgrehan		case 0x00:
290300829Sgrehan			fifo_put(sc, PS2MC_ACK);
291300829Sgrehan			break;
292300829Sgrehan		case PS2MC_RESET_DEV:
293300829Sgrehan			ps2mouse_reset(sc);
294300829Sgrehan			fifo_put(sc, PS2MC_ACK);
295300829Sgrehan			fifo_put(sc, PS2MC_BAT_SUCCESS);
296300829Sgrehan			fifo_put(sc, PS2MOUSE_DEV_ID);
297300829Sgrehan			break;
298300829Sgrehan		case PS2MC_SET_DEFAULTS:
299300829Sgrehan			ps2mouse_reset(sc);
300300829Sgrehan			fifo_put(sc, PS2MC_ACK);
301300829Sgrehan			break;
302300829Sgrehan		case PS2MC_DISABLE:
303300829Sgrehan			fifo_reset(sc);
304300829Sgrehan			sc->status &= ~PS2M_STS_ENABLE_DEV;
305300829Sgrehan			fifo_put(sc, PS2MC_ACK);
306300829Sgrehan			break;
307300829Sgrehan		case PS2MC_ENABLE:
308300829Sgrehan			fifo_reset(sc);
309300829Sgrehan			sc->status |= PS2M_STS_ENABLE_DEV;
310300829Sgrehan			fifo_put(sc, PS2MC_ACK);
311300829Sgrehan			break;
312300829Sgrehan		case PS2MC_SET_SAMPLING_RATE:
313300829Sgrehan			sc->curcmd = val;
314300829Sgrehan			fifo_put(sc, PS2MC_ACK);
315300829Sgrehan			break;
316300829Sgrehan		case PS2MC_SEND_DEV_ID:
317300829Sgrehan			fifo_put(sc, PS2MC_ACK);
318300829Sgrehan			fifo_put(sc, PS2MOUSE_DEV_ID);
319300829Sgrehan			break;
320300829Sgrehan		case PS2MC_SET_REMOTE_MODE:
321300829Sgrehan			sc->status |= PS2M_STS_REMOTE_MODE;
322300829Sgrehan			fifo_put(sc, PS2MC_ACK);
323300829Sgrehan			break;
324300829Sgrehan		case PS2MC_SEND_DEV_DATA:
325300829Sgrehan			fifo_put(sc, PS2MC_ACK);
326300829Sgrehan			movement_get(sc);
327300829Sgrehan			break;
328300829Sgrehan		case PS2MC_SET_STREAM_MODE:
329300829Sgrehan			sc->status &= ~PS2M_STS_REMOTE_MODE;
330300829Sgrehan			fifo_put(sc, PS2MC_ACK);
331300829Sgrehan			break;
332300829Sgrehan		case PS2MC_SEND_DEV_STATUS:
333300829Sgrehan			fifo_put(sc, PS2MC_ACK);
334300829Sgrehan			fifo_put(sc, sc->status);
335300829Sgrehan			fifo_put(sc, sc->resolution);
336300829Sgrehan			fifo_put(sc, sc->sampling_rate);
337300829Sgrehan			break;
338300829Sgrehan		case PS2MC_SET_RESOLUTION:
339300829Sgrehan			sc->curcmd = val;
340300829Sgrehan			fifo_put(sc, PS2MC_ACK);
341300829Sgrehan			break;
342300829Sgrehan		case PS2MC_SET_SCALING1:
343300829Sgrehan		case PS2MC_SET_SCALING2:
344300829Sgrehan			fifo_put(sc, PS2MC_ACK);
345300829Sgrehan			break;
346300829Sgrehan		default:
347300829Sgrehan			fifo_put(sc, PS2MC_ACK);
348300829Sgrehan			fprintf(stderr, "Unhandled ps2 mouse command "
349300829Sgrehan			    "0x%02x\n", val);
350300829Sgrehan			break;
351300829Sgrehan		}
352300829Sgrehan	}
353300829Sgrehan	pthread_mutex_unlock(&sc->mtx);
354300829Sgrehan}
355300829Sgrehan
356300829Sgrehanstatic void
357300829Sgrehanps2mouse_event(uint8_t button, int x, int y, void *arg)
358300829Sgrehan{
359300829Sgrehan	struct ps2mouse_softc *sc = arg;
360300829Sgrehan
361300829Sgrehan	pthread_mutex_lock(&sc->mtx);
362300829Sgrehan	movement_update(sc, x, y);
363300829Sgrehan
364300829Sgrehan	sc->status &= ~(PS2M_STS_LEFT_BUTTON |
365300829Sgrehan	    PS2M_STS_RIGHT_BUTTON | PS2M_STS_MID_BUTTON);
366300829Sgrehan	if (button & (1 << 0))
367300829Sgrehan		sc->status |= PS2M_STS_LEFT_BUTTON;
368300829Sgrehan	if (button & (1 << 1))
369300829Sgrehan		sc->status |= PS2M_STS_MID_BUTTON;
370300829Sgrehan	if (button & (1 << 2))
371300829Sgrehan		sc->status |= PS2M_STS_RIGHT_BUTTON;
372300829Sgrehan
373300829Sgrehan	if ((sc->status & PS2M_STS_ENABLE_DEV) == 0 || !sc->ctrlenable) {
374300829Sgrehan		/* no data reporting */
375300829Sgrehan		pthread_mutex_unlock(&sc->mtx);
376300829Sgrehan		return;
377300829Sgrehan	}
378300829Sgrehan
379300829Sgrehan	movement_get(sc);
380300829Sgrehan	pthread_mutex_unlock(&sc->mtx);
381300829Sgrehan
382300829Sgrehan	if (sc->fifo.num > 0)
383300829Sgrehan		atkbdc_event(sc->atkbdc_sc, 0);
384300829Sgrehan}
385300829Sgrehan
386300829Sgrehanstruct ps2mouse_softc *
387300829Sgrehanps2mouse_init(struct atkbdc_softc *atkbdc_sc)
388300829Sgrehan{
389300829Sgrehan	struct ps2mouse_softc *sc;
390300829Sgrehan
391300829Sgrehan	sc = calloc(1, sizeof (struct ps2mouse_softc));
392300829Sgrehan	pthread_mutex_init(&sc->mtx, NULL);
393300829Sgrehan	fifo_init(sc);
394300829Sgrehan	sc->atkbdc_sc = atkbdc_sc;
395300829Sgrehan
396300829Sgrehan	pthread_mutex_lock(&sc->mtx);
397300829Sgrehan	ps2mouse_reset(sc);
398300829Sgrehan	pthread_mutex_unlock(&sc->mtx);
399300829Sgrehan
400300829Sgrehan	console_ptr_register(ps2mouse_event, sc, 1);
401300829Sgrehan
402300829Sgrehan	return (sc);
403300829Sgrehan}
404300829Sgrehan
405300829Sgrehan
406