FreeBSD/Linux Kernel Cross Reference
sys/dev/mse/mse.c
1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2004 M. Warner Losh
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.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD: releng/12.0/sys/dev/mse/mse.c 334923 2018-06-10 10:23:31Z wulf $
29 */
30
31 /*
32 * Copyright 1992 by the University of Guelph
33 *
34 * Permission to use, copy and modify this
35 * software and its documentation for any purpose and without
36 * fee is hereby granted, provided that the above copyright
37 * notice appear in all copies and that both that copyright
38 * notice and this permission notice appear in supporting
39 * documentation.
40 * University of Guelph makes no representations about the suitability of
41 * this software for any purpose. It is provided "as is"
42 * without express or implied warranty.
43 */
44 /*
45 * Driver for the Logitech and ATI Inport Bus mice for use with 386bsd and
46 * the X386 port, courtesy of
47 * Rick Macklem, rick@snowhite.cis.uoguelph.ca
48 * Caveats: The driver currently uses spltty(), but doesn't use any
49 * generic tty code. It could use splmse() (that only masks off the
50 * bus mouse interrupt, but that would require hacking in i386/isa/icu.s.
51 * (This may be worth the effort, since the Logitech generates 30/60
52 * interrupts/sec continuously while it is open.)
53 * NB: The ATI has NOT been tested yet!
54 */
55
56 /*
57 * Modification history:
58 * Sep 6, 1994 -- Lars Fredriksen(fredriks@mcs.com)
59 * improved probe based on input from Logitech.
60 *
61 * Oct 19, 1992 -- E. Stark (stark@cs.sunysb.edu)
62 * fixes to make it work with Microsoft InPort busmouse
63 *
64 * Jan, 1993 -- E. Stark (stark@cs.sunysb.edu)
65 * added patches for new "select" interface
66 *
67 * May 4, 1993 -- E. Stark (stark@cs.sunysb.edu)
68 * changed position of some spl()'s in mseread
69 *
70 * October 8, 1993 -- E. Stark (stark@cs.sunysb.edu)
71 * limit maximum negative x/y value to -127 to work around XFree problem
72 * that causes spurious button pushes.
73 */
74
75 #include <sys/param.h>
76 #include <sys/systm.h>
77 #include <sys/conf.h>
78 #include <sys/kernel.h>
79 #include <sys/module.h>
80 #include <sys/bus.h>
81 #include <sys/poll.h>
82 #include <sys/selinfo.h>
83 #include <sys/uio.h>
84 #include <sys/mouse.h>
85
86 #include <machine/bus.h>
87 #include <machine/resource.h>
88 #include <sys/rman.h>
89
90 #include <isa/isavar.h>
91
92 #include <dev/mse/msevar.h>
93
94 devclass_t mse_devclass;
95
96 static d_open_t mseopen;
97 static d_close_t mseclose;
98 static d_read_t mseread;
99 static d_ioctl_t mseioctl;
100 static d_poll_t msepoll;
101
102 static struct cdevsw mse_cdevsw = {
103 .d_version = D_VERSION,
104 .d_open = mseopen,
105 .d_close = mseclose,
106 .d_read = mseread,
107 .d_ioctl = mseioctl,
108 .d_poll = msepoll,
109 .d_name = "mse",
110 };
111
112 static void mseintr(void *);
113 static void mseintr_locked(mse_softc_t *sc);
114 static void msetimeout(void *);
115
116 #define MSE_NBLOCKIO(dev) (dev2unit(dev) != 0)
117
118 #define MSEPRI (PZERO + 3)
119
120 int
121 mse_common_attach(device_t dev)
122 {
123 mse_softc_t *sc;
124 int unit, flags, rid;
125
126 sc = device_get_softc(dev);
127 unit = device_get_unit(dev);
128 mtx_init(&sc->sc_lock, "mse", NULL, MTX_DEF);
129 callout_init_mtx(&sc->sc_callout, &sc->sc_lock, 0);
130
131 rid = 0;
132 sc->sc_intr = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
133 RF_ACTIVE);
134 if (sc->sc_intr == NULL) {
135 bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->sc_port);
136 mtx_destroy(&sc->sc_lock);
137 return ENXIO;
138 }
139
140 if (bus_setup_intr(dev, sc->sc_intr, INTR_TYPE_TTY | INTR_MPSAFE,
141 NULL, mseintr, sc, &sc->sc_ih)) {
142 bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->sc_port);
143 bus_release_resource(dev, SYS_RES_IRQ, rid, sc->sc_intr);
144 mtx_destroy(&sc->sc_lock);
145 return ENXIO;
146 }
147 flags = device_get_flags(dev);
148 sc->mode.accelfactor = (flags & MSE_CONFIG_ACCEL) >> 4;
149
150 sc->sc_dev = make_dev(&mse_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
151 "mse%d", unit);
152 sc->sc_dev->si_drv1 = sc;
153 sc->sc_ndev = make_dev(&mse_cdevsw, 1, UID_ROOT, GID_WHEEL, 0600,
154 "nmse%d", unit);
155 sc->sc_ndev->si_drv1 = sc;
156 gone_in_dev(dev, 12, "mse(4) driver");
157
158 return 0;
159 }
160
161 int
162 mse_detach(device_t dev)
163 {
164 mse_softc_t *sc;
165 int rid;
166
167 sc = device_get_softc(dev);
168 MSE_LOCK(sc);
169 if (sc->sc_flags & MSESC_OPEN) {
170 MSE_UNLOCK(sc);
171 return EBUSY;
172 }
173
174 /* Sabotage subsequent opens. */
175 sc->sc_mousetype = MSE_NONE;
176 MSE_UNLOCK(sc);
177
178 destroy_dev(sc->sc_dev);
179 destroy_dev(sc->sc_ndev);
180
181 rid = 0;
182 bus_teardown_intr(dev, sc->sc_intr, sc->sc_ih);
183 bus_release_resource(dev, SYS_RES_IRQ, rid, sc->sc_intr);
184 bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->sc_port);
185
186 callout_drain(&sc->sc_callout);
187 mtx_destroy(&sc->sc_lock);
188
189 return 0;
190 }
191
192 /*
193 * Exclusive open the mouse, initialize it and enable interrupts.
194 */
195 static int
196 mseopen(struct cdev *dev, int flags, int fmt, struct thread *td)
197 {
198 mse_softc_t *sc = dev->si_drv1;
199
200 MSE_LOCK(sc);
201 if (sc->sc_mousetype == MSE_NONE) {
202 MSE_UNLOCK(sc);
203 return (ENXIO);
204 }
205 if (sc->sc_flags & MSESC_OPEN) {
206 MSE_UNLOCK(sc);
207 return (EBUSY);
208 }
209 sc->sc_flags |= MSESC_OPEN;
210 sc->sc_obuttons = sc->sc_buttons = MOUSE_MSC_BUTTONS;
211 sc->sc_deltax = sc->sc_deltay = 0;
212 sc->sc_bytesread = sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
213 sc->sc_watchdog = FALSE;
214 callout_reset(&sc->sc_callout, hz * 2, msetimeout, dev);
215 sc->mode.level = 0;
216 sc->status.flags = 0;
217 sc->status.button = sc->status.obutton = 0;
218 sc->status.dx = sc->status.dy = sc->status.dz = 0;
219
220 /*
221 * Initialize mouse interface and enable interrupts.
222 */
223 (*sc->sc_enablemouse)(sc->sc_port);
224 MSE_UNLOCK(sc);
225 return (0);
226 }
227
228 /*
229 * mseclose: just turn off mouse innterrupts.
230 */
231 static int
232 mseclose(struct cdev *dev, int flags, int fmt, struct thread *td)
233 {
234 mse_softc_t *sc = dev->si_drv1;
235
236 MSE_LOCK(sc);
237 callout_stop(&sc->sc_callout);
238 (*sc->sc_disablemouse)(sc->sc_port);
239 sc->sc_flags &= ~MSESC_OPEN;
240 MSE_UNLOCK(sc);
241 return(0);
242 }
243
244 /*
245 * mseread: return mouse info using the MSC serial protocol, but without
246 * using bytes 4 and 5.
247 * (Yes this is cheesy, but it makes the X386 server happy, so...)
248 */
249 static int
250 mseread(struct cdev *dev, struct uio *uio, int ioflag)
251 {
252 mse_softc_t *sc = dev->si_drv1;
253 int xfer, error;
254
255 /*
256 * If there are no protocol bytes to be read, set up a new protocol
257 * packet.
258 */
259 MSE_LOCK(sc);
260 while (sc->sc_flags & MSESC_READING) {
261 if (MSE_NBLOCKIO(dev)) {
262 MSE_UNLOCK(sc);
263 return (0);
264 }
265 sc->sc_flags |= MSESC_WANT;
266 error = mtx_sleep(sc, &sc->sc_lock, MSEPRI | PCATCH, "mseread",
267 0);
268 if (error) {
269 MSE_UNLOCK(sc);
270 return (error);
271 }
272 }
273 sc->sc_flags |= MSESC_READING;
274 xfer = 0;
275 if (sc->sc_bytesread >= sc->mode.packetsize) {
276 while (sc->sc_deltax == 0 && sc->sc_deltay == 0 &&
277 (sc->sc_obuttons ^ sc->sc_buttons) == 0) {
278 if (MSE_NBLOCKIO(dev))
279 goto out;
280 sc->sc_flags |= MSESC_WANT;
281 error = mtx_sleep(sc, &sc->sc_lock, MSEPRI | PCATCH,
282 "mseread", 0);
283 if (error)
284 goto out;
285 }
286
287 /*
288 * Generate protocol bytes.
289 * For some reason X386 expects 5 bytes but never uses
290 * the fourth or fifth?
291 */
292 sc->sc_bytes[0] = sc->mode.syncmask[1]
293 | (sc->sc_buttons & ~sc->mode.syncmask[0]);
294 if (sc->sc_deltax > 127)
295 sc->sc_deltax = 127;
296 if (sc->sc_deltax < -127)
297 sc->sc_deltax = -127;
298 sc->sc_deltay = -sc->sc_deltay; /* Otherwise mousey goes wrong way */
299 if (sc->sc_deltay > 127)
300 sc->sc_deltay = 127;
301 if (sc->sc_deltay < -127)
302 sc->sc_deltay = -127;
303 sc->sc_bytes[1] = sc->sc_deltax;
304 sc->sc_bytes[2] = sc->sc_deltay;
305 sc->sc_bytes[3] = sc->sc_bytes[4] = 0;
306 sc->sc_bytes[5] = sc->sc_bytes[6] = 0;
307 sc->sc_bytes[7] = MOUSE_SYS_EXTBUTTONS;
308 sc->sc_obuttons = sc->sc_buttons;
309 sc->sc_deltax = sc->sc_deltay = 0;
310 sc->sc_bytesread = 0;
311 }
312 xfer = min(uio->uio_resid, sc->mode.packetsize - sc->sc_bytesread);
313 MSE_UNLOCK(sc);
314 error = uiomove(&sc->sc_bytes[sc->sc_bytesread], xfer, uio);
315 MSE_LOCK(sc);
316 out:
317 sc->sc_flags &= ~MSESC_READING;
318 if (error == 0)
319 sc->sc_bytesread += xfer;
320 if (sc->sc_flags & MSESC_WANT) {
321 sc->sc_flags &= ~MSESC_WANT;
322 MSE_UNLOCK(sc);
323 wakeup(sc);
324 } else
325 MSE_UNLOCK(sc);
326 return (error);
327 }
328
329 /*
330 * mseioctl: process ioctl commands.
331 */
332 static int
333 mseioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
334 {
335 mse_softc_t *sc = dev->si_drv1;
336 mousestatus_t status;
337 int err = 0;
338
339 switch (cmd) {
340
341 case MOUSE_GETHWINFO:
342 MSE_LOCK(sc);
343 *(mousehw_t *)addr = sc->hw;
344 if (sc->mode.level == 0)
345 ((mousehw_t *)addr)->model = MOUSE_MODEL_GENERIC;
346 MSE_UNLOCK(sc);
347 break;
348
349 case MOUSE_GETMODE:
350 MSE_LOCK(sc);
351 *(mousemode_t *)addr = sc->mode;
352 switch (sc->mode.level) {
353 case 0:
354 break;
355 case 1:
356 ((mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE;
357 ((mousemode_t *)addr)->syncmask[0] = MOUSE_SYS_SYNCMASK;
358 ((mousemode_t *)addr)->syncmask[1] = MOUSE_SYS_SYNC;
359 break;
360 }
361 MSE_UNLOCK(sc);
362 break;
363
364 case MOUSE_SETMODE:
365 switch (((mousemode_t *)addr)->level) {
366 case 0:
367 case 1:
368 break;
369 default:
370 return (EINVAL);
371 }
372 MSE_LOCK(sc);
373 if (((mousemode_t *)addr)->accelfactor < -1) {
374 MSE_UNLOCK(sc);
375 return (EINVAL);
376 } else if (((mousemode_t *)addr)->accelfactor >= 0)
377 sc->mode.accelfactor =
378 ((mousemode_t *)addr)->accelfactor;
379 sc->mode.level = ((mousemode_t *)addr)->level;
380 switch (sc->mode.level) {
381 case 0:
382 sc->sc_bytesread = sc->mode.packetsize
383 = MOUSE_MSC_PACKETSIZE;
384 break;
385 case 1:
386 sc->sc_bytesread = sc->mode.packetsize
387 = MOUSE_SYS_PACKETSIZE;
388 break;
389 }
390 MSE_UNLOCK(sc);
391 break;
392
393 case MOUSE_GETLEVEL:
394 MSE_LOCK(sc);
395 *(int *)addr = sc->mode.level;
396 MSE_UNLOCK(sc);
397 break;
398
399 case MOUSE_SETLEVEL:
400 switch (*(int *)addr) {
401 case 0:
402 MSE_LOCK(sc);
403 sc->mode.level = *(int *)addr;
404 sc->sc_bytesread = sc->mode.packetsize
405 = MOUSE_MSC_PACKETSIZE;
406 MSE_UNLOCK(sc);
407 break;
408 case 1:
409 MSE_LOCK(sc);
410 sc->mode.level = *(int *)addr;
411 sc->sc_bytesread = sc->mode.packetsize
412 = MOUSE_SYS_PACKETSIZE;
413 MSE_UNLOCK(sc);
414 break;
415 default:
416 return (EINVAL);
417 }
418 break;
419
420 case MOUSE_GETSTATUS:
421 MSE_LOCK(sc);
422 status = sc->status;
423 sc->status.flags = 0;
424 sc->status.obutton = sc->status.button;
425 sc->status.button = 0;
426 sc->status.dx = 0;
427 sc->status.dy = 0;
428 sc->status.dz = 0;
429 MSE_UNLOCK(sc);
430 *(mousestatus_t *)addr = status;
431 break;
432
433 case MOUSE_READSTATE:
434 case MOUSE_READDATA:
435 return (ENODEV);
436
437 default:
438 return (ENOTTY);
439 }
440 return (err);
441 }
442
443 /*
444 * msepoll: check for mouse input to be processed.
445 */
446 static int
447 msepoll(struct cdev *dev, int events, struct thread *td)
448 {
449 mse_softc_t *sc = dev->si_drv1;
450 int revents = 0;
451
452 MSE_LOCK(sc);
453 if (events & (POLLIN | POLLRDNORM)) {
454 if (sc->sc_bytesread != sc->mode.packetsize ||
455 sc->sc_deltax != 0 || sc->sc_deltay != 0 ||
456 (sc->sc_obuttons ^ sc->sc_buttons) != 0)
457 revents |= events & (POLLIN | POLLRDNORM);
458 else
459 selrecord(td, &sc->sc_selp);
460 }
461 MSE_UNLOCK(sc);
462 return (revents);
463 }
464
465 /*
466 * msetimeout: watchdog timer routine.
467 */
468 static void
469 msetimeout(void *arg)
470 {
471 struct cdev *dev;
472 mse_softc_t *sc;
473
474 dev = (struct cdev *)arg;
475 sc = dev->si_drv1;
476 MSE_ASSERT_LOCKED(sc);
477 if (sc->sc_watchdog) {
478 if (bootverbose)
479 printf("%s: lost interrupt?\n", devtoname(dev));
480 mseintr_locked(sc);
481 }
482 sc->sc_watchdog = TRUE;
483 callout_schedule(&sc->sc_callout, hz);
484 }
485
486 /*
487 * mseintr: update mouse status. sc_deltax and sc_deltay are accumulative.
488 */
489 static void
490 mseintr(void *arg)
491 {
492 mse_softc_t *sc = arg;
493
494 MSE_LOCK(sc);
495 mseintr_locked(sc);
496 MSE_UNLOCK(sc);
497 }
498
499 static void
500 mseintr_locked(mse_softc_t *sc)
501 {
502 /*
503 * the table to turn MouseSystem button bits (MOUSE_MSC_BUTTON?UP)
504 * into `mousestatus' button bits (MOUSE_BUTTON?DOWN).
505 */
506 static int butmap[8] = {
507 0,
508 MOUSE_BUTTON3DOWN,
509 MOUSE_BUTTON2DOWN,
510 MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
511 MOUSE_BUTTON1DOWN,
512 MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
513 MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
514 MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
515 };
516 int dx, dy, but;
517 int sign;
518
519 #ifdef DEBUG
520 static int mse_intrcnt = 0;
521 if((mse_intrcnt++ % 10000) == 0)
522 printf("mseintr\n");
523 #endif /* DEBUG */
524 if ((sc->sc_flags & MSESC_OPEN) == 0)
525 return;
526
527 (*sc->sc_getmouse)(sc->sc_port, &dx, &dy, &but);
528 if (sc->mode.accelfactor > 0) {
529 sign = (dx < 0);
530 dx = dx * dx / sc->mode.accelfactor;
531 if (dx == 0)
532 dx = 1;
533 if (sign)
534 dx = -dx;
535 sign = (dy < 0);
536 dy = dy * dy / sc->mode.accelfactor;
537 if (dy == 0)
538 dy = 1;
539 if (sign)
540 dy = -dy;
541 }
542 sc->sc_deltax += dx;
543 sc->sc_deltay += dy;
544 sc->sc_buttons = but;
545
546 but = butmap[~but & MOUSE_MSC_BUTTONS];
547 sc->status.dx += dx;
548 sc->status.dy += dy;
549 sc->status.flags |= ((dx || dy) ? MOUSE_POSCHANGED : 0)
550 | (sc->status.button ^ but);
551 sc->status.button = but;
552
553 sc->sc_watchdog = FALSE;
554
555 /*
556 * If mouse state has changed, wake up anyone wanting to know.
557 */
558 if (sc->sc_deltax != 0 || sc->sc_deltay != 0 ||
559 (sc->sc_obuttons ^ sc->sc_buttons) != 0) {
560 if (sc->sc_flags & MSESC_WANT) {
561 sc->sc_flags &= ~MSESC_WANT;
562 wakeup(sc);
563 }
564 selwakeuppri(&sc->sc_selp, MSEPRI);
565 }
566 }
Cache object: 3a46aff83c520a7e99c0f4e0719a3c5c
|