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