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