vt_sysmouse.c revision 219888
1185029Spjd/*-
2185029Spjd * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
3185029Spjd * All rights reserved.
4185029Spjd *
5185029Spjd * Copyright (c) 2009 The FreeBSD Foundation
6185029Spjd * All rights reserved.
7185029Spjd *
8185029Spjd * This software was developed by Ed Schouten under sponsorship from the
9185029Spjd * FreeBSD Foundation.
10185029Spjd *
11185029Spjd * Redistribution and use in source and binary forms, with or without
12185029Spjd * modification, are permitted provided that the following conditions
13185029Spjd * are met:
14185029Spjd * 1. Redistributions of source code must retain the above copyright
15185029Spjd *    notice, this list of conditions and the following disclaimer.
16185029Spjd * 2. Redistributions in binary form must reproduce the above copyright
17185029Spjd *    notice, this list of conditions and the following disclaimer in the
18185029Spjd *    documentation and/or other materials provided with the distribution.
19185029Spjd *
20185029Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21185029Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22185029Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23185029Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24185029Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25185029Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26185029Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27185029Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28185029Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29185029Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30185029Spjd * SUCH DAMAGE.
31185029Spjd */
32185029Spjd
33185029Spjd#include <sys/cdefs.h>
34185029Spjd__FBSDID("$FreeBSD: user/ed/newcons/sys/dev/vt/vt_sysmouse.c 219888 2011-03-22 21:31:31Z ed $");
35185029Spjd
36185029Spjd#include <sys/param.h>
37185029Spjd#include <sys/condvar.h>
38185029Spjd#include <sys/conf.h>
39185029Spjd#include <sys/consio.h>
40185029Spjd#include <sys/fcntl.h>
41185029Spjd#include <sys/filio.h>
42185029Spjd#include <sys/kernel.h>
43185029Spjd#include <sys/malloc.h>
44185029Spjd#include <sys/mouse.h>
45185029Spjd#include <sys/poll.h>
46185029Spjd#include <sys/random.h>
47185029Spjd#include <sys/selinfo.h>
48185029Spjd#include <sys/sigio.h>
49185029Spjd#include <sys/signalvar.h>
50185029Spjd#include <sys/systm.h>
51185029Spjd#include <sys/uio.h>
52185029Spjd
53185029Spjd#include <dev/vt/vt.h>
54185029Spjd
55185029Spjdstatic d_open_t		sysmouse_open;
56185029Spjdstatic d_close_t	sysmouse_close;
57185029Spjdstatic d_read_t		sysmouse_read;
58185029Spjdstatic d_ioctl_t	sysmouse_ioctl;
59185029Spjdstatic d_poll_t		sysmouse_poll;
60185029Spjd
61185029Spjdstatic struct cdevsw sysmouse_cdevsw = {
62185029Spjd	.d_version	= D_VERSION,
63185029Spjd	.d_open		= sysmouse_open,
64185029Spjd	.d_close	= sysmouse_close,
65185029Spjd	.d_read		= sysmouse_read,
66185029Spjd	.d_ioctl	= sysmouse_ioctl,
67185029Spjd	.d_poll		= sysmouse_poll,
68185029Spjd	.d_name		= "sysmouse",
69185029Spjd};
70185029Spjd
71185029Spjdstatic struct mtx	 sysmouse_lock;
72185029Spjdstatic struct cv	 sysmouse_sleep;
73185029Spjdstatic struct selinfo	 sysmouse_bufpoll;
74185029Spjd
75185029Spjdstatic int		 sysmouse_level;
76185029Spjdstatic mousestatus_t	 sysmouse_status;
77185029Spjdstatic int		 sysmouse_flags;
78185029Spjd#define	SM_ASYNC	0x1
79185029Spjdstatic struct sigio	*sysmouse_sigio;
80185029Spjd
81185029Spjd#define	SYSMOUSE_MAXFRAMES	250	/* 2 KB */
82185029Spjdstatic MALLOC_DEFINE(M_SYSMOUSE, "sysmouse", "sysmouse device");
83185029Spjdstatic unsigned char	*sysmouse_buffer;
84185029Spjdstatic unsigned int	 sysmouse_start, sysmouse_length;
85185029Spjd
86185029Spjdstatic int
87185029Spjdsysmouse_buf_read(struct uio *uio, unsigned int length)
88185029Spjd{
89185029Spjd	unsigned char buf[MOUSE_SYS_PACKETSIZE];
90185029Spjd	int error;
91185029Spjd
92185029Spjd	if (sysmouse_buffer == NULL)
93185029Spjd		return (ENXIO);
94185029Spjd	else if (sysmouse_length == 0)
95185029Spjd		return (EWOULDBLOCK);
96185029Spjd
97185029Spjd	memcpy(buf, sysmouse_buffer +
98185029Spjd	    sysmouse_start * MOUSE_SYS_PACKETSIZE, MOUSE_SYS_PACKETSIZE);
99185029Spjd	sysmouse_start = (sysmouse_start + 1) % SYSMOUSE_MAXFRAMES;
100185029Spjd	sysmouse_length--;
101185029Spjd
102185029Spjd	mtx_unlock(&sysmouse_lock);
103185029Spjd	error = uiomove(buf, length, uio);
104185029Spjd	mtx_lock(&sysmouse_lock);
105185029Spjd
106185029Spjd	return (error);
107185029Spjd}
108185029Spjd
109185029Spjdstatic void
110185029Spjdsysmouse_buf_store(const unsigned char buf[MOUSE_SYS_PACKETSIZE])
111185029Spjd{
112185029Spjd	unsigned int idx;
113185029Spjd
114185029Spjd	if (sysmouse_buffer == NULL || sysmouse_length == SYSMOUSE_MAXFRAMES)
115185029Spjd		return;
116185029Spjd
117185029Spjd	idx = (sysmouse_start + sysmouse_length) % SYSMOUSE_MAXFRAMES;
118185029Spjd	memcpy(sysmouse_buffer + idx * MOUSE_SYS_PACKETSIZE, buf,
119185029Spjd	    MOUSE_SYS_PACKETSIZE);
120185029Spjd	sysmouse_length++;
121185029Spjd	cv_broadcast(&sysmouse_sleep);
122185029Spjd	selwakeup(&sysmouse_bufpoll);
123185029Spjd	if (sysmouse_flags & SM_ASYNC && sysmouse_sigio != NULL)
124185029Spjd		pgsigio(&sysmouse_sigio, SIGIO, 0);
125185029Spjd}
126185029Spjd
127185029Spjdvoid
128sysmouse_process_event(mouse_info_t *mi)
129{
130	/* MOUSE_BUTTON?DOWN -> MOUSE_MSC_BUTTON?UP */
131	static const int buttonmap[8] = {
132	    MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP,
133	    MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP,
134	    MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON3UP,
135	    MOUSE_MSC_BUTTON3UP,
136	    MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP,
137	    MOUSE_MSC_BUTTON2UP,
138	    MOUSE_MSC_BUTTON1UP,
139	    0,
140	};
141	unsigned char buf[MOUSE_SYS_PACKETSIZE];
142	int x, y, z;
143
144	random_harvest(mi, sizeof *mi, 2, 0, RANDOM_MOUSE);
145
146	mtx_lock(&sysmouse_lock);
147	switch (mi->operation) {
148	case MOUSE_ACTION:
149		sysmouse_status.button = mi->u.data.buttons;
150		/* FALLTHROUGH */
151	case MOUSE_MOTION_EVENT:
152		x = mi->u.data.x;
153		y = mi->u.data.y;
154		z = mi->u.data.z;
155		break;
156	case MOUSE_BUTTON_EVENT:
157		x = y = z = 0;
158		if (mi->u.event.value > 0)
159			sysmouse_status.button |= mi->u.event.id;
160		else
161			sysmouse_status.button &= ~mi->u.event.id;
162		break;
163	default:
164		goto done;
165	}
166
167	sysmouse_status.dx += x;
168	sysmouse_status.dy += y;
169	sysmouse_status.dz += z;
170	sysmouse_status.flags |= ((x || y || z) ? MOUSE_POSCHANGED : 0) |
171	    (sysmouse_status.obutton ^ sysmouse_status.button);
172	if (sysmouse_status.flags == 0)
173		goto done;
174
175	/* The first five bytes are compatible with MouseSystems. */
176	buf[0] = MOUSE_MSC_SYNC |
177	    buttonmap[sysmouse_status.button & MOUSE_STDBUTTONS];
178	x = imax(imin(x, 255), -256);
179	buf[1] = x >> 1;
180	buf[3] = x - buf[1];
181	y = -imax(imin(y, 255), -256);
182	buf[2] = y >> 1;
183	buf[4] = y - buf[2];
184	/* Extended part. */
185        z = imax(imin(z, 127), -128);
186        buf[5] = (z >> 1) & 0x7f;
187        buf[6] = (z - (z >> 1)) & 0x7f;
188        /* Buttons 4-10. */
189        buf[7] = (~sysmouse_status.button >> 3) & 0x7f;
190
191	sysmouse_buf_store(buf);
192done:	mtx_unlock(&sysmouse_lock);
193}
194
195static int
196sysmouse_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
197{
198	void *buf;
199
200	buf = malloc(MOUSE_SYS_PACKETSIZE * SYSMOUSE_MAXFRAMES,
201	    M_SYSMOUSE, M_WAITOK);
202	mtx_lock(&sysmouse_lock);
203	if (sysmouse_buffer == NULL) {
204		sysmouse_buffer = buf;
205		sysmouse_start = sysmouse_length = 0;
206		sysmouse_level = 0;
207	} else {
208		free(buf, M_SYSMOUSE);
209	}
210	mtx_unlock(&sysmouse_lock);
211
212	return (0);
213}
214
215static int
216sysmouse_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
217{
218
219	mtx_lock(&sysmouse_lock);
220	free(sysmouse_buffer, M_SYSMOUSE);
221	sysmouse_buffer = NULL;
222	mtx_unlock(&sysmouse_lock);
223
224	return (0);
225}
226
227static int
228sysmouse_read(struct cdev *dev, struct uio *uio, int ioflag)
229{
230	unsigned int length;
231	ssize_t oresid;
232	int error = 0;
233
234	oresid = uio->uio_resid;
235
236	mtx_lock(&sysmouse_lock);
237	length = sysmouse_level >= 1 ? MOUSE_SYS_PACKETSIZE :
238	    MOUSE_MSC_PACKETSIZE;
239
240	while (uio->uio_resid >= length) {
241		error = sysmouse_buf_read(uio, length);
242		if (error == 0) {
243			/* Process the next frame. */
244			continue;
245		} else if (error != EWOULDBLOCK) {
246			/* Error (e.g. EFAULT). */
247			break;
248		} else {
249			/* Block. */
250			if (oresid != uio->uio_resid || ioflag & O_NONBLOCK)
251				break;
252			error = cv_wait_sig(&sysmouse_sleep, &sysmouse_lock);
253			if (error != 0)
254				break;
255		}
256	}
257	mtx_unlock(&sysmouse_lock);
258
259	return (error);
260}
261
262static int
263sysmouse_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag,
264    struct thread *td)
265{
266
267	switch (cmd) {
268	case FIOASYNC:
269		mtx_lock(&sysmouse_lock);
270		if (*(int *)data)
271			sysmouse_flags |= SM_ASYNC;
272		else
273			sysmouse_flags &= ~SM_ASYNC;
274		mtx_unlock(&sysmouse_lock);
275		return (0);
276	case FIONBIO:
277		return (0);
278	case FIOGETOWN:
279		*(int *)data = fgetown(&sysmouse_sigio);
280		return (0);
281	case FIOSETOWN:
282		return (fsetown(*(int *)data, &sysmouse_sigio));
283	case MOUSE_GETHWINFO: {
284		mousehw_t *hw = (mousehw_t *)data;
285
286		hw->buttons = 10;
287		hw->iftype = MOUSE_IF_SYSMOUSE;
288		hw->type = MOUSE_MOUSE;
289		hw->model = MOUSE_MODEL_GENERIC;
290		hw->hwid = 0;
291
292		return (0);
293	}
294	case MOUSE_GETLEVEL:
295		*(int *)data = sysmouse_level;
296		return (0);
297	case MOUSE_GETMODE: {
298		mousemode_t *mode = (mousemode_t *)data;
299
300		mode->rate = -1;
301		mode->resolution = -1;
302		mode->accelfactor = 0;
303		mode->level = sysmouse_level;
304
305		switch (mode->level) {
306		case 0:
307			mode->protocol = MOUSE_PROTO_MSC;
308			mode->packetsize = MOUSE_MSC_PACKETSIZE;
309			mode->syncmask[0] = MOUSE_MSC_SYNCMASK;
310			mode->syncmask[1] = MOUSE_MSC_SYNC;
311			break;
312		case 1:
313			mode->protocol = MOUSE_PROTO_SYSMOUSE;
314			mode->packetsize = MOUSE_SYS_PACKETSIZE;
315			mode->syncmask[0] = MOUSE_SYS_SYNCMASK;
316			mode->syncmask[1] = MOUSE_SYS_SYNC;
317			break;
318		}
319
320		return (0);
321	}
322	case MOUSE_GETSTATUS:
323		mtx_lock(&sysmouse_lock);
324		*(mousestatus_t *)data = sysmouse_status;
325
326		sysmouse_status.flags = 0;
327		sysmouse_status.obutton = sysmouse_status.button;
328		sysmouse_status.dx = 0;
329		sysmouse_status.dy = 0;
330		sysmouse_status.dz = 0;
331		mtx_unlock(&sysmouse_lock);
332
333		return (0);
334	case MOUSE_SETLEVEL: {
335		int level;
336
337		level = *(int *)data;
338		if (level != 0 && level != 1)
339			return (EINVAL);
340
341		sysmouse_level = level;
342		return (0);
343	}
344	case MOUSE_SETMODE: {
345		mousemode_t *mode = (mousemode_t *)data;
346
347		switch (mode->level) {
348		case -1:
349			/* Do nothing. */
350			break;
351		case 0:
352		case 1:
353			sysmouse_level = mode->level;
354			break;
355		default:
356			return (EINVAL);
357		}
358
359		return (0);
360	}
361	default:
362		printf("sysmouse: unknown ioctl: %c:%lx\n",
363		    (char)IOCGROUP(cmd), IOCBASECMD(cmd));
364		return (ENOIOCTL);
365	}
366}
367
368static int
369sysmouse_poll(struct cdev *dev, int events, struct thread *td)
370{
371	int revents = 0;
372
373	mtx_lock(&sysmouse_lock);
374	if (events & (POLLIN|POLLRDNORM)) {
375		if (sysmouse_length > 0)
376			revents = events & (POLLIN|POLLRDNORM);
377		else
378			selrecord(td, &sysmouse_bufpoll);
379	}
380	mtx_unlock(&sysmouse_lock);
381
382	return (revents);
383}
384
385static void
386sysmouse_drvinit(void *unused)
387{
388
389	mtx_init(&sysmouse_lock, "sysmouse", NULL, MTX_DEF);
390	cv_init(&sysmouse_sleep, "sysmrd");
391	make_dev(&sysmouse_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
392	    "sysmouse");
393}
394
395SYSINIT(sysmouse, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sysmouse_drvinit, NULL);
396