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