xref: /dragonfly/sys/dev/misc/syscons/sysmouse.c (revision 6e5c5008)
1 /*-
2  * (MPSAFE)
3  *
4  * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer as
12  *    the first lines of this file unmodified.
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 AUTHORS ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * $FreeBSD: src/sys/dev/syscons/sysmouse.c,v 1.2.2.2 2001/07/16 05:21:24 yokota Exp $
29  */
30 
31 /* MPSAFE NOTE: Take care with locking in sysmouse_event which is called
32  *              from syscons.
33  */
34 #include "opt_syscons.h"
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/conf.h>
39 #include <sys/device.h>
40 #include <sys/event.h>
41 #include <sys/uio.h>
42 #include <sys/priv.h>
43 #include <sys/vnode.h>
44 #include <sys/kernel.h>
45 #include <sys/signalvar.h>
46 #include <sys/filio.h>
47 
48 #include <machine/console.h>
49 #include <sys/mouse.h>
50 
51 #include "syscons.h"
52 
53 #ifndef SC_NO_SYSMOUSE
54 
55 #define FIFO_SIZE	256
56 
57 struct event_fifo {
58 	mouse_info_t buf[FIFO_SIZE];
59 	int start;
60 	int fill;
61 	unsigned int dropped;
62 };
63 
64 struct sysmouse_state {
65 	struct event_fifo *fifo;
66 	int level;	/* sysmouse protocol level */
67 	mousestatus_t syncstatus;
68 	mousestatus_t readstatus;	/* Only needed for button status */
69 	int opened;
70 	int asyncio;
71 	struct lock sm_lock;
72 	struct sigio *sm_sigio;
73 	struct kqinfo rkq;
74 };
75 
76 static d_open_t		smopen;
77 static d_close_t	smclose;
78 static d_read_t		smread;
79 static d_ioctl_t	smioctl;
80 static d_kqfilter_t	smkqfilter;
81 
82 static struct dev_ops sm_ops = {
83 	{ "sysmouse", 0, D_MPSAFE },
84 	.d_open =	smopen,
85 	.d_close =	smclose,
86 	.d_read =	smread,
87 	.d_ioctl =	smioctl,
88 	.d_kqfilter =	smkqfilter,
89 };
90 
91 /* local variables */
92 static struct sysmouse_state mouse_state;
93 
94 static int	sysmouse_evtopkt(struct sysmouse_state *sc, mouse_info_t *info,
95 		    u_char *buf);
96 static void	smqueue(struct sysmouse_state *sc, mouse_info_t *info);
97 static int	pktlen(struct sysmouse_state *sc);
98 static void	smfilter_detach(struct knote *);
99 static int	smfilter(struct knote *, long);
100 static void	smget(struct sysmouse_state *sc, mouse_info_t *info);
101 static void	smpop(struct sysmouse_state *sc);
102 
103 static int
104 pktlen(struct sysmouse_state *sc)
105 {
106 	if (sc->level == 0)
107 		return 5;
108 	else
109 		return 8;
110 }
111 
112 static int
113 smopen(struct dev_open_args *ap)
114 {
115 	cdev_t dev = ap->a_head.a_dev;
116 	struct sysmouse_state *sc = &mouse_state;
117 	int ret;
118 
119 	DPRINTF(5, ("smopen: dev:%d,%d\n",
120 		major(dev), minor(dev)));
121 
122 	lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
123 	if (!sc->opened) {
124 		sc->fifo = kmalloc(sizeof(struct event_fifo),
125 		    M_SYSCONS, M_WAITOK | M_ZERO);
126 		sc->opened = 1;
127 		sc->asyncio = 0;
128 		sc->sm_sigio = NULL;
129 		bzero(&sc->readstatus, sizeof(sc->readstatus));
130 		bzero(&sc->syncstatus, sizeof(sc->syncstatus));
131 		ret = 0;
132 	} else {
133 		ret = EBUSY;
134 	}
135 	lockmgr(&sc->sm_lock, LK_RELEASE);
136 
137 	return ret;
138 }
139 
140 static int
141 smclose(struct dev_close_args *ap)
142 {
143 	struct sysmouse_state *sc = &mouse_state;
144 
145 	lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
146 	funsetown(&sc->sm_sigio);
147 	sc->opened = 0;
148 	sc->asyncio = 0;
149 	sc->level = 0;
150 	kfree(sc->fifo, M_SYSCONS);
151 	sc->fifo = NULL;
152 	lockmgr(&sc->sm_lock, LK_RELEASE);
153 
154 	return 0;
155 }
156 
157 static int
158 smread(struct dev_read_args *ap)
159 {
160 	struct sysmouse_state *sc = &mouse_state;
161 	mousestatus_t backupstatus;
162 	mouse_info_t info;
163 	u_char buf[8];
164 	struct uio *uio = ap->a_uio;
165 	int error = 0, val, cnt = 0;
166 
167 	lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
168 	while (sc->fifo->fill <= 0) {
169 		/* Buffer too small to fit a complete mouse packet */
170 		if (uio->uio_resid < pktlen(sc)) {
171 			error = EIO;
172 			goto done;
173 		}
174 		if (ap->a_ioflag & IO_NDELAY) {
175 			error = EAGAIN;
176 			goto done;
177 		}
178 		error = lksleep(sc, &sc->sm_lock, PCATCH, "smread", 0);
179 		if (error == EINTR || error == ERESTART) {
180 			goto done;
181 		}
182 	}
183 
184 	do {
185 		/* Buffer too small to fit a complete mouse packet */
186 		if (uio->uio_resid < pktlen(sc)) {
187 			error = EIO;
188 			goto done;
189 		}
190 		smget(sc, &info);
191 		backupstatus = sc->readstatus;
192 		val = sysmouse_evtopkt(sc, &info, buf);
193 		if (val > 0) {
194 			error = uiomove(buf, val, uio);
195 			if (error != 0) {
196 				sc->readstatus = backupstatus;
197 				goto done;
198 			}
199 			cnt++;
200 		}
201 		smpop(sc);
202 	} while (sc->fifo->fill > 0);
203 
204 done:
205 	lockmgr(&sc->sm_lock, LK_RELEASE);
206 	if (cnt > 0 && error != EFAULT)
207 		return 0;
208 	return error;
209 }
210 
211 static int
212 smioctl(struct dev_ioctl_args *ap)
213 {
214 	struct sysmouse_state *sc = &mouse_state;
215 	mousehw_t *hw;
216 	mousemode_t *mode;
217 
218 	switch (ap->a_cmd) {
219 	case FIOSETOWN:
220 		lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
221 		fsetown(*(int *)ap->a_data, &sc->sm_sigio);
222 		lockmgr(&sc->sm_lock, LK_RELEASE);
223 		return 0;
224 	case FIOGETOWN:
225 		lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
226 		*(int *)ap->a_data = fgetown(&sc->sm_sigio);
227 		lockmgr(&sc->sm_lock, LK_RELEASE);
228 		return 0;
229 	case FIOASYNC:
230 		lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
231 		if (*(int *)ap->a_data) {
232 			sc->asyncio = 1;
233 		} else {
234 			sc->asyncio = 0;
235 		}
236 		lockmgr(&sc->sm_lock, LK_RELEASE);
237 		return 0;
238 	case MOUSE_GETHWINFO:	/* get device information */
239 		hw = (mousehw_t *)ap->a_data;
240 		hw->buttons = 10;		/* XXX unknown */
241 		hw->iftype = MOUSE_IF_SYSMOUSE;
242 		hw->type = MOUSE_MOUSE;
243 		hw->model = MOUSE_MODEL_GENERIC;
244 		hw->hwid = 0;
245 		return 0;
246 
247 	case MOUSE_GETMODE:	/* get protocol/mode */
248 		mode = (mousemode_t *)ap->a_data;
249 		lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
250 		mode->level = sc->level;
251 		lockmgr(&sc->sm_lock, LK_RELEASE);
252 		switch (mode->level) {
253 		case 0: /* emulate MouseSystems protocol */
254 			mode->protocol = MOUSE_PROTO_MSC;
255 			mode->rate = -1;		/* unknown */
256 			mode->resolution = -1;	/* unknown */
257 			mode->accelfactor = 0;	/* disabled */
258 			mode->packetsize = MOUSE_MSC_PACKETSIZE;
259 			mode->syncmask[0] = MOUSE_MSC_SYNCMASK;
260 			mode->syncmask[1] = MOUSE_MSC_SYNC;
261 			break;
262 
263 		case 1: /* sysmouse protocol */
264 			mode->protocol = MOUSE_PROTO_SYSMOUSE;
265 			mode->rate = -1;
266 			mode->resolution = -1;
267 			mode->accelfactor = 0;
268 			mode->packetsize = MOUSE_SYS_PACKETSIZE;
269 			mode->syncmask[0] = MOUSE_SYS_SYNCMASK;
270 			mode->syncmask[1] = MOUSE_SYS_SYNC;
271 			break;
272 		}
273 		return 0;
274 
275 	case MOUSE_SETMODE:	/* set protocol/mode */
276 		mode = (mousemode_t *)ap->a_data;
277 		if (mode->level == -1)
278 			; 	/* don't change the current setting */
279 		else if ((mode->level < 0) || (mode->level > 1)) {
280 			return EINVAL;
281 		} else {
282 			lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
283 			sc->level = mode->level;
284 			lockmgr(&sc->sm_lock, LK_RELEASE);
285 		}
286 		return 0;
287 
288 	case MOUSE_GETLEVEL:	/* get operation level */
289 		lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
290 		*(int *)ap->a_data = sc->level;
291 		lockmgr(&sc->sm_lock, LK_RELEASE);
292 		return 0;
293 
294 	case MOUSE_SETLEVEL:	/* set operation level */
295 		if ((*(int *)ap->a_data  < 0) || (*(int *)ap->a_data > 1)) {
296 			return EINVAL;
297 		}
298 		lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
299 		sc->level = *(int *)ap->a_data;
300 		lockmgr(&sc->sm_lock, LK_RELEASE);
301 		return 0;
302 
303 	case MOUSE_GETSTATUS:	/* get accumulated mouse events */
304 		lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
305 		*(mousestatus_t *)ap->a_data = sc->syncstatus;
306 		sc->syncstatus.flags = 0;
307 		sc->syncstatus.obutton = sc->syncstatus.button;
308 		sc->syncstatus.dx = 0;
309 		sc->syncstatus.dy = 0;
310 		sc->syncstatus.dz = 0;
311 		lockmgr(&sc->sm_lock, LK_RELEASE);
312 		return 0;
313 
314 #if 0 /* notyet */
315 	case MOUSE_GETVARS:	/* get internal mouse variables */
316 	case MOUSE_SETVARS:	/* set internal mouse variables */
317 		return ENODEV;
318 #endif
319 
320 	case MOUSE_READSTATE:	/* read status from the device */
321 	case MOUSE_READDATA:	/* read data from the device */
322 		return ENODEV;
323 	}
324 
325 	return ENOTTY;
326 }
327 
328 static struct filterops smfiltops =
329         { FILTEROP_MPSAFE | FILTEROP_ISFD, NULL, smfilter_detach, smfilter };
330 
331 static int
332 smkqfilter(struct dev_kqfilter_args *ap)
333 {
334 	struct sysmouse_state *sc = &mouse_state;
335 	struct knote *kn = ap->a_kn;
336 	struct klist *klist;
337 
338 	ap->a_result = 0;
339 
340 	switch (kn->kn_filter) {
341 	case EVFILT_READ:
342 		kn->kn_fop = &smfiltops;
343 		kn->kn_hook = (caddr_t)sc;
344 		break;
345 	default:
346 		ap->a_result = EOPNOTSUPP;
347 		return (0);
348 	}
349 
350 	klist = &sc->rkq.ki_note;
351 	knote_insert(klist, kn);
352 
353 	return (0);
354 }
355 
356 static void
357 smfilter_detach(struct knote *kn)
358 {
359 	struct sysmouse_state *sc = &mouse_state;
360 	struct klist *klist;
361 
362 	klist = &sc->rkq.ki_note;
363 	knote_remove(klist, kn);
364 }
365 
366 static int
367 smfilter(struct knote *kn, long hint)
368 {
369 	struct sysmouse_state *sc = &mouse_state;
370 	int ready = 0;
371 
372 	lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
373 	if (sc->fifo->fill > 0) {
374 		ready = 1;
375 		kn->kn_data = 0;
376 	}
377 	lockmgr(&sc->sm_lock, LK_RELEASE);
378 
379 	return ready;
380 }
381 
382 static void
383 smqueue(struct sysmouse_state *sc, mouse_info_t *info)
384 {
385 	struct event_fifo *f = sc->fifo;
386 
387 	if (f->fill >= FIFO_SIZE) {
388 		f->fill = FIFO_SIZE;
389 		f->buf[f->start] = *info;
390 		f->start = (f->start + 1) % FIFO_SIZE;
391 		f->dropped++;
392 	} else {
393 		f->buf[(f->start + f->fill) % FIFO_SIZE] = *info;
394 		f->fill++;
395 	}
396 
397 }
398 
399 static void
400 smget(struct sysmouse_state *sc, mouse_info_t *info)
401 {
402 	struct event_fifo *f = sc->fifo;
403 
404 	if (f->fill > 0)
405 		*info = f->buf[f->start];
406 }
407 
408 static void
409 smpop(struct sysmouse_state *sc)
410 {
411 	struct event_fifo *f = sc->fifo;
412 
413 	if (f->fill > 0) {
414 		f->fill--;
415 		f->start = (f->start + 1) % FIFO_SIZE;
416 	}
417 }
418 
419 static void
420 sm_attach_mouse(void *unused)
421 {
422 	struct sysmouse_state *sc = &mouse_state;
423 	cdev_t dev;
424 
425 	lockinit(&mouse_state.sm_lock, "sysmouse", 0, LK_CANRECURSE);
426 	sc->fifo = NULL;
427 
428 	dev = make_dev(&sm_ops, 0, UID_ROOT, GID_WHEEL, 0600, "sysmouse");
429 }
430 
431 SYSINIT(sysmouse, SI_SUB_DRIVERS, SI_ORDER_ANY, sm_attach_mouse, NULL);
432 
433 static int
434 sysmouse_updatestatus(mousestatus_t *status, mouse_info_t *info)
435 {
436 	int x, y, z;
437 
438 	status->obutton = status->button;
439 
440 	switch (info->operation) {
441 	case MOUSE_ACTION:
442 		status->button = info->u.data.buttons;
443 		/* FALL THROUGH */
444 	case MOUSE_MOTION_EVENT:
445 		x = info->u.data.x;
446 		y = info->u.data.y;
447 		z = info->u.data.z;
448 		break;
449 	case MOUSE_BUTTON_EVENT:
450 		x = y = z = 0;
451 		if (info->u.event.value > 0)
452 			status->button |= info->u.event.id;
453 		else
454 			status->button &= ~info->u.event.id;
455 		break;
456 	default:
457 		return 0;
458 	}
459 
460 	status->dx += x;
461 	status->dy += y;
462 	status->dz += z;
463 	status->flags |= ((x || y || z) ? MOUSE_POSCHANGED : 0)
464 			 | (status->obutton ^ status->button);
465 
466 	return 1;
467 }
468 
469 /* Requires buf to hold at least 8 bytes, returns number of bytes written */
470 static int
471 sysmouse_evtopkt(struct sysmouse_state *sc, mouse_info_t *info, u_char *buf)
472 {
473 	/* MOUSE_BUTTON?DOWN -> MOUSE_MSC_BUTTON?UP */
474 	static int butmap[8] = {
475 	    MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP,
476 	    MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP,
477 	    MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON3UP,
478 	    MOUSE_MSC_BUTTON3UP,
479 	    MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP,
480 	    MOUSE_MSC_BUTTON2UP,
481 	    MOUSE_MSC_BUTTON1UP,
482 	    0,
483 	};
484 	int x, y, z;
485 
486 	sc->readstatus.dx = 0;
487 	sc->readstatus.dy = 0;
488 	sc->readstatus.dz = 0;
489 	sc->readstatus.flags = 0;
490 	if (sysmouse_updatestatus(&sc->readstatus, info) == 0)
491 		return 0;
492 
493 	/* We aren't using the sc->readstatus.dx/dy/dz values */
494 
495 	if (sc->readstatus.flags == 0)
496 		return 0;
497 
498 	x = (info->operation == MOUSE_BUTTON_EVENT ? 0 : info->u.data.x);
499 	y = (info->operation == MOUSE_BUTTON_EVENT ? 0 : info->u.data.y);
500 	z = (info->operation == MOUSE_BUTTON_EVENT ? 0 : info->u.data.z);
501 
502 	/* the first five bytes are compatible with MouseSystems' */
503 	buf[0] = MOUSE_MSC_SYNC
504 		 | butmap[sc->readstatus.button & MOUSE_STDBUTTONS];
505 	x = imax(imin(x, 255), -256);
506 	buf[1] = x >> 1;
507 	buf[3] = x - buf[1];
508 	y = -imax(imin(y, 255), -256);
509 	buf[2] = y >> 1;
510 	buf[4] = y - buf[2];
511 	if (sc->level >= 1) {
512 		/* extended part */
513 		z = imax(imin(z, 127), -128);
514         	buf[5] = (z >> 1) & 0x7f;
515         	buf[6] = (z - (z >> 1)) & 0x7f;
516 		/* buttons 4-10 */
517 		buf[7] = (~sc->readstatus.button >> 3) & 0x7f;
518 	}
519 
520 	if (sc->level >= 1)
521 		return 8;
522 
523 	return 5;
524 }
525 
526 int
527 sysmouse_event(mouse_info_t *info)
528 {
529 	struct sysmouse_state *sc = &mouse_state;
530 	int ret;
531 
532 	lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
533 	ret = sysmouse_updatestatus(&sc->syncstatus, info);
534 	if (ret != 0)
535 		ret = sc->syncstatus.flags;
536 	if (!sc->opened) {
537 		lockmgr(&sc->sm_lock, LK_RELEASE);
538 		return ret;
539 	}
540 
541 	switch (info->operation) {
542 	case MOUSE_ACTION:
543 	case MOUSE_MOTION_EVENT:
544 	case MOUSE_BUTTON_EVENT:
545 		smqueue(sc, info);
546 		if (sc->asyncio)
547 	                pgsigio(sc->sm_sigio, SIGIO, 0);
548 		lockmgr(&sc->sm_lock, LK_RELEASE);
549 		wakeup(sc);
550 		KNOTE(&sc->rkq.ki_note, 0);
551 		break;
552 	default:
553 		lockmgr(&sc->sm_lock, LK_RELEASE);
554 		break;
555 	}
556 
557 	return ret;
558 }
559 
560 #endif /* !SC_NO_SYSMOUSE */
561