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