]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/ps2mouse.c
Import libucl 20160812
[FreeBSD/FreeBSD.git] / usr.sbin / bhyve / ps2mouse.c
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         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
99 static void
100 fifo_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
108 static void
109 fifo_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
118 static void
119 fifo_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
131 static int
132 fifo_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
147 static void
148 movement_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
156 static void
157 movement_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
165 static void
166 movement_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
214 static void
215 ps2mouse_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
230 int
231 ps2mouse_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
242 int
243 ps2mouse_fifocnt(struct ps2mouse_softc *sc)
244 {
245         return (sc->fifo.num);
246 }
247
248 void
249 ps2mouse_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
263 void
264 ps2mouse_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
356 static void
357 ps2mouse_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
386 struct ps2mouse_softc *
387 ps2mouse_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