]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/ps2mouse.c
ping(8): Fix a mandoc related issue
[FreeBSD/FreeBSD.git] / usr.sbin / bhyve / ps2mouse.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 __FBSDID("$FreeBSD$");
32
33 #include <sys/types.h>
34
35 #include <machine/vmm_snapshot.h>
36
37 #include <assert.h>
38 #include <stdbool.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <strings.h>
43 #include <pthread.h>
44 #include <pthread_np.h>
45
46 #include "atkbdc.h"
47 #include "debug.h"
48 #include "console.h"
49
50 /* mouse device commands */
51 #define PS2MC_RESET_DEV         0xff
52 #define PS2MC_SET_DEFAULTS      0xf6
53 #define PS2MC_DISABLE           0xf5
54 #define PS2MC_ENABLE            0xf4
55 #define PS2MC_SET_SAMPLING_RATE 0xf3
56 #define PS2MC_SEND_DEV_ID       0xf2
57 #define PS2MC_SET_REMOTE_MODE   0xf0
58 #define PS2MC_SEND_DEV_DATA     0xeb
59 #define PS2MC_SET_STREAM_MODE   0xea
60 #define PS2MC_SEND_DEV_STATUS   0xe9
61 #define PS2MC_SET_RESOLUTION    0xe8
62 #define PS2MC_SET_SCALING1      0xe7
63 #define PS2MC_SET_SCALING2      0xe6
64
65 #define PS2MC_BAT_SUCCESS       0xaa
66 #define PS2MC_ACK               0xfa
67
68 /* mouse device id */
69 #define PS2MOUSE_DEV_ID         0x0
70
71 /* mouse data bits */
72 #define PS2M_DATA_Y_OFLOW       0x80
73 #define PS2M_DATA_X_OFLOW       0x40
74 #define PS2M_DATA_Y_SIGN        0x20
75 #define PS2M_DATA_X_SIGN        0x10
76 #define PS2M_DATA_AONE          0x08
77 #define PS2M_DATA_MID_BUTTON    0x04
78 #define PS2M_DATA_RIGHT_BUTTON  0x02
79 #define PS2M_DATA_LEFT_BUTTON   0x01
80
81 /* mouse status bits */
82 #define PS2M_STS_REMOTE_MODE    0x40
83 #define PS2M_STS_ENABLE_DEV     0x20
84 #define PS2M_STS_SCALING_21     0x10
85 #define PS2M_STS_MID_BUTTON     0x04
86 #define PS2M_STS_RIGHT_BUTTON   0x02
87 #define PS2M_STS_LEFT_BUTTON    0x01
88
89 #define PS2MOUSE_FIFOSZ         16
90
91 struct fifo {
92         uint8_t buf[PS2MOUSE_FIFOSZ];
93         int     rindex;         /* index to read from */
94         int     windex;         /* index to write to */
95         int     num;            /* number of bytes in the fifo */
96         int     size;           /* size of the fifo */
97 };
98
99 struct ps2mouse_softc {
100         struct atkbdc_softc     *atkbdc_sc;
101         pthread_mutex_t         mtx;
102
103         uint8_t         status;
104         uint8_t         resolution;
105         uint8_t         sampling_rate;
106         int             ctrlenable;
107         struct fifo     fifo;
108
109         uint8_t         curcmd; /* current command for next byte */
110
111         int             cur_x, cur_y;
112         int             delta_x, delta_y;
113 };
114
115 static void
116 fifo_init(struct ps2mouse_softc *sc)
117 {
118         struct fifo *fifo;
119
120         fifo = &sc->fifo;
121         fifo->size = sizeof(((struct fifo *)0)->buf);
122 }
123
124 static void
125 fifo_reset(struct ps2mouse_softc *sc)
126 {
127         struct fifo *fifo;
128
129         fifo = &sc->fifo;
130         bzero(fifo, sizeof(struct fifo));
131         fifo->size = sizeof(((struct fifo *)0)->buf);
132 }
133
134 static void
135 fifo_put(struct ps2mouse_softc *sc, uint8_t val)
136 {
137         struct fifo *fifo;
138
139         fifo = &sc->fifo;
140         if (fifo->num < fifo->size) {
141                 fifo->buf[fifo->windex] = val;
142                 fifo->windex = (fifo->windex + 1) % fifo->size;
143                 fifo->num++;
144         }
145 }
146
147 static int
148 fifo_get(struct ps2mouse_softc *sc, uint8_t *val)
149 {
150         struct fifo *fifo;
151
152         fifo = &sc->fifo;
153         if (fifo->num > 0) {
154                 *val = fifo->buf[fifo->rindex];
155                 fifo->rindex = (fifo->rindex + 1) % fifo->size;
156                 fifo->num--;
157                 return (0);
158         }
159
160         return (-1);
161 }
162
163 static void
164 movement_reset(struct ps2mouse_softc *sc)
165 {
166         assert(pthread_mutex_isowned_np(&sc->mtx));
167
168         sc->delta_x = 0;
169         sc->delta_y = 0;
170 }
171
172 static void
173 movement_update(struct ps2mouse_softc *sc, int x, int y)
174 {
175         sc->delta_x += x - sc->cur_x;
176         sc->delta_y += sc->cur_y - y;
177         sc->cur_x = x;
178         sc->cur_y = y;
179 }
180
181 static void
182 movement_get(struct ps2mouse_softc *sc)
183 {
184         uint8_t val0, val1, val2;
185
186         assert(pthread_mutex_isowned_np(&sc->mtx));
187
188         val0 = PS2M_DATA_AONE;
189         val0 |= sc->status & (PS2M_DATA_LEFT_BUTTON |
190             PS2M_DATA_RIGHT_BUTTON | PS2M_DATA_MID_BUTTON);
191
192         if (sc->delta_x >= 0) {
193                 if (sc->delta_x > 255) {
194                         val0 |= PS2M_DATA_X_OFLOW;
195                         val1 = 255;
196                 } else
197                         val1 = sc->delta_x;
198         } else {
199                 val0 |= PS2M_DATA_X_SIGN;
200                 if (sc->delta_x < -255) {
201                         val0 |= PS2M_DATA_X_OFLOW;
202                         val1 = 255;
203                 } else
204                         val1 = sc->delta_x;
205         }
206         sc->delta_x = 0;
207
208         if (sc->delta_y >= 0) {
209                 if (sc->delta_y > 255) {
210                         val0 |= PS2M_DATA_Y_OFLOW;
211                         val2 = 255;
212                 } else
213                         val2 = sc->delta_y;
214         } else {
215                 val0 |= PS2M_DATA_Y_SIGN;
216                 if (sc->delta_y < -255) {
217                         val0 |= PS2M_DATA_Y_OFLOW;
218                         val2 = 255;
219                 } else
220                         val2 = sc->delta_y;
221         }
222         sc->delta_y = 0;
223
224         if (sc->fifo.num < (sc->fifo.size - 3)) {
225                 fifo_put(sc, val0);
226                 fifo_put(sc, val1);
227                 fifo_put(sc, val2);
228         }
229 }
230
231 static void
232 ps2mouse_reset(struct ps2mouse_softc *sc)
233 {
234         assert(pthread_mutex_isowned_np(&sc->mtx));
235         fifo_reset(sc);
236         movement_reset(sc);
237         sc->status = PS2M_STS_ENABLE_DEV;
238         sc->resolution = 4;
239         sc->sampling_rate = 100;
240
241         sc->cur_x = 0;
242         sc->cur_y = 0;
243         sc->delta_x = 0;
244         sc->delta_y = 0;
245 }
246
247 int
248 ps2mouse_read(struct ps2mouse_softc *sc, uint8_t *val)
249 {
250         int retval;
251
252         pthread_mutex_lock(&sc->mtx);
253         retval = fifo_get(sc, val);
254         pthread_mutex_unlock(&sc->mtx);
255
256         return (retval);
257 }
258
259 int
260 ps2mouse_fifocnt(struct ps2mouse_softc *sc)
261 {
262         return (sc->fifo.num);
263 }
264
265 void
266 ps2mouse_toggle(struct ps2mouse_softc *sc, int enable)
267 {
268         pthread_mutex_lock(&sc->mtx);
269         if (enable)
270                 sc->ctrlenable = 1;
271         else {
272                 sc->ctrlenable = 0;
273                 sc->fifo.rindex = 0;
274                 sc->fifo.windex = 0;
275                 sc->fifo.num = 0;
276         }
277         pthread_mutex_unlock(&sc->mtx);
278 }
279
280 void
281 ps2mouse_write(struct ps2mouse_softc *sc, uint8_t val, int insert)
282 {
283         pthread_mutex_lock(&sc->mtx);
284         fifo_reset(sc);
285         if (sc->curcmd) {
286                 switch (sc->curcmd) {
287                 case PS2MC_SET_SAMPLING_RATE:
288                         sc->sampling_rate = val;
289                         fifo_put(sc, PS2MC_ACK);
290                         break;
291                 case PS2MC_SET_RESOLUTION:
292                         sc->resolution = val;
293                         fifo_put(sc, PS2MC_ACK);
294                         break;
295                 default:
296                         EPRINTLN("Unhandled ps2 mouse current "
297                             "command byte 0x%02x", val);
298                         break;
299                 }
300                 sc->curcmd = 0;
301
302         } else if (insert) {
303                 fifo_put(sc, val);
304         } else {
305                 switch (val) {
306                 case 0x00:
307                         fifo_put(sc, PS2MC_ACK);
308                         break;
309                 case PS2MC_RESET_DEV:
310                         ps2mouse_reset(sc);
311                         fifo_put(sc, PS2MC_ACK);
312                         fifo_put(sc, PS2MC_BAT_SUCCESS);
313                         fifo_put(sc, PS2MOUSE_DEV_ID);
314                         break;
315                 case PS2MC_SET_DEFAULTS:
316                         ps2mouse_reset(sc);
317                         fifo_put(sc, PS2MC_ACK);
318                         break;
319                 case PS2MC_DISABLE:
320                         fifo_reset(sc);
321                         sc->status &= ~PS2M_STS_ENABLE_DEV;
322                         fifo_put(sc, PS2MC_ACK);
323                         break;
324                 case PS2MC_ENABLE:
325                         fifo_reset(sc);
326                         sc->status |= PS2M_STS_ENABLE_DEV;
327                         fifo_put(sc, PS2MC_ACK);
328                         break;
329                 case PS2MC_SET_SAMPLING_RATE:
330                         sc->curcmd = val;
331                         fifo_put(sc, PS2MC_ACK);
332                         break;
333                 case PS2MC_SEND_DEV_ID:
334                         fifo_put(sc, PS2MC_ACK);
335                         fifo_put(sc, PS2MOUSE_DEV_ID);
336                         break;
337                 case PS2MC_SET_REMOTE_MODE:
338                         sc->status |= PS2M_STS_REMOTE_MODE;
339                         fifo_put(sc, PS2MC_ACK);
340                         break;
341                 case PS2MC_SEND_DEV_DATA:
342                         fifo_put(sc, PS2MC_ACK);
343                         movement_get(sc);
344                         break;
345                 case PS2MC_SET_STREAM_MODE:
346                         sc->status &= ~PS2M_STS_REMOTE_MODE;
347                         fifo_put(sc, PS2MC_ACK);
348                         break;
349                 case PS2MC_SEND_DEV_STATUS:
350                         fifo_put(sc, PS2MC_ACK);
351                         fifo_put(sc, sc->status);
352                         fifo_put(sc, sc->resolution);
353                         fifo_put(sc, sc->sampling_rate);
354                         break;
355                 case PS2MC_SET_RESOLUTION:
356                         sc->curcmd = val;
357                         fifo_put(sc, PS2MC_ACK);
358                         break;
359                 case PS2MC_SET_SCALING1:
360                 case PS2MC_SET_SCALING2:
361                         fifo_put(sc, PS2MC_ACK);
362                         break;
363                 default:
364                         fifo_put(sc, PS2MC_ACK);
365                         EPRINTLN("Unhandled ps2 mouse command "
366                             "0x%02x", val);
367                         break;
368                 }
369         }
370         pthread_mutex_unlock(&sc->mtx);
371 }
372
373 static void
374 ps2mouse_event(uint8_t button, int x, int y, void *arg)
375 {
376         struct ps2mouse_softc *sc = arg;
377
378         pthread_mutex_lock(&sc->mtx);
379         movement_update(sc, x, y);
380
381         sc->status &= ~(PS2M_STS_LEFT_BUTTON |
382             PS2M_STS_RIGHT_BUTTON | PS2M_STS_MID_BUTTON);
383         if (button & (1 << 0))
384                 sc->status |= PS2M_STS_LEFT_BUTTON;
385         if (button & (1 << 1))
386                 sc->status |= PS2M_STS_MID_BUTTON;
387         if (button & (1 << 2))
388                 sc->status |= PS2M_STS_RIGHT_BUTTON;
389
390         if ((sc->status & PS2M_STS_ENABLE_DEV) == 0 || !sc->ctrlenable) {
391                 /* no data reporting */
392                 pthread_mutex_unlock(&sc->mtx);
393                 return;
394         }
395
396         movement_get(sc);
397         pthread_mutex_unlock(&sc->mtx);
398
399         if (sc->fifo.num > 0)
400                 atkbdc_event(sc->atkbdc_sc, 0);
401 }
402
403 struct ps2mouse_softc *
404 ps2mouse_init(struct atkbdc_softc *atkbdc_sc)
405 {
406         struct ps2mouse_softc *sc;
407
408         sc = calloc(1, sizeof (struct ps2mouse_softc));
409         pthread_mutex_init(&sc->mtx, NULL);
410         fifo_init(sc);
411         sc->atkbdc_sc = atkbdc_sc;
412
413         pthread_mutex_lock(&sc->mtx);
414         ps2mouse_reset(sc);
415         pthread_mutex_unlock(&sc->mtx);
416
417         console_ptr_register(ps2mouse_event, sc, 1);
418
419         return (sc);
420 }
421
422 #ifdef BHYVE_SNAPSHOT
423 int
424 ps2mouse_snapshot(struct ps2mouse_softc *sc, struct vm_snapshot_meta *meta)
425 {
426         int ret;
427
428         SNAPSHOT_VAR_OR_LEAVE(sc->status, meta, ret, done);
429         SNAPSHOT_VAR_OR_LEAVE(sc->resolution, meta, ret, done);
430         SNAPSHOT_VAR_OR_LEAVE(sc->sampling_rate, meta, ret, done);
431         SNAPSHOT_VAR_OR_LEAVE(sc->ctrlenable, meta, ret, done);
432         SNAPSHOT_VAR_OR_LEAVE(sc->curcmd, meta, ret, done);
433         SNAPSHOT_VAR_OR_LEAVE(sc->cur_x, meta, ret, done);
434         SNAPSHOT_VAR_OR_LEAVE(sc->cur_y, meta, ret, done);
435         SNAPSHOT_VAR_OR_LEAVE(sc->delta_x, meta, ret, done);
436         SNAPSHOT_VAR_OR_LEAVE(sc->delta_y, meta, ret, done);
437
438 done:
439         return (ret);
440 }
441 #endif