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