]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/dev/mse/mse.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.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_UNIT(dev)           (minor(dev) >> 1)
115 #define MSE_NBLOCKIO(dev)       (minor(dev) & 0x1)
116
117 #define MSEPRI  (PZERO + 3)
118
119 int
120 mse_common_attach(device_t dev)
121 {
122         mse_softc_t *sc;
123         int unit, flags, rid;
124
125         sc = device_get_softc(dev);
126         unit = device_get_unit(dev);
127
128         rid = 0;
129         sc->sc_intr = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
130                                              RF_ACTIVE);
131         if (sc->sc_intr == NULL) {
132                 bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->sc_port);
133                 return ENXIO;
134         }
135
136         if (bus_setup_intr(dev, sc->sc_intr,
137             INTR_TYPE_TTY, NULL, mseintr, sc, &sc->sc_ih)) {
138                 bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->sc_port);
139                 bus_release_resource(dev, SYS_RES_IRQ, rid, sc->sc_intr);
140                 return ENXIO;
141         }
142         flags = device_get_flags(dev);
143         sc->mode.accelfactor = (flags & MSE_CONFIG_ACCEL) >> 4;
144         callout_handle_init(&sc->sc_callout);
145
146         sc->sc_dev = make_dev(&mse_cdevsw, unit << 1, 0, 0, 0600,
147           "mse%d", unit);
148         sc->sc_ndev = make_dev(&mse_cdevsw, (unit<<1)+1, 0, 0, 0600,
149           "nmse%d", unit);
150         return 0;
151 }
152
153 /*
154  * Exclusive open the mouse, initialize it and enable interrupts.
155  */
156 static  int
157 mseopen(struct cdev *dev, int flags, int fmt, struct thread *td)
158 {
159         mse_softc_t *sc;
160         int s;
161
162         sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev));
163         if (sc == NULL)
164                 return (ENXIO);
165         if (sc->sc_mousetype == MSE_NONE)
166                 return (ENXIO);
167         if (sc->sc_flags & MSESC_OPEN)
168                 return (EBUSY);
169         sc->sc_flags |= MSESC_OPEN;
170         sc->sc_obuttons = sc->sc_buttons = MOUSE_MSC_BUTTONS;
171         sc->sc_deltax = sc->sc_deltay = 0;
172         sc->sc_bytesread = sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
173         sc->sc_watchdog = FALSE;
174         sc->sc_callout = timeout(msetimeout, dev, hz*2);
175         sc->mode.level = 0;
176         sc->status.flags = 0;
177         sc->status.button = sc->status.obutton = 0;
178         sc->status.dx = sc->status.dy = sc->status.dz = 0;
179
180         /*
181          * Initialize mouse interface and enable interrupts.
182          */
183         s = spltty();
184         (*sc->sc_enablemouse)(sc->sc_iot, sc->sc_ioh);
185         splx(s);
186         return (0);
187 }
188
189 /*
190  * mseclose: just turn off mouse innterrupts.
191  */
192 static  int
193 mseclose(struct cdev *dev, int flags, int fmt, struct thread *td)
194 {
195         mse_softc_t *sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev));
196         int s;
197
198         untimeout(msetimeout, dev, sc->sc_callout);
199         callout_handle_init(&sc->sc_callout);
200         s = spltty();
201         (*sc->sc_disablemouse)(sc->sc_iot, sc->sc_ioh);
202         sc->sc_flags &= ~MSESC_OPEN;
203         splx(s);
204         return(0);
205 }
206
207 /*
208  * mseread: return mouse info using the MSC serial protocol, but without
209  * using bytes 4 and 5.
210  * (Yes this is cheesy, but it makes the X386 server happy, so...)
211  */
212 static  int
213 mseread(struct cdev *dev, struct uio *uio, int ioflag)
214 {
215         mse_softc_t *sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev));
216         int xfer, s, error;
217
218         /*
219          * If there are no protocol bytes to be read, set up a new protocol
220          * packet.
221          */
222         s = spltty(); /* XXX Should be its own spl, but where is imlXX() */
223         if (sc->sc_bytesread >= sc->mode.packetsize) {
224                 while (sc->sc_deltax == 0 && sc->sc_deltay == 0 &&
225                        (sc->sc_obuttons ^ sc->sc_buttons) == 0) {
226                         if (MSE_NBLOCKIO(dev)) {
227                                 splx(s);
228                                 return (0);
229                         }
230                         sc->sc_flags |= MSESC_WANT;
231                         error = tsleep(sc, MSEPRI | PCATCH,
232                                 "mseread", 0);
233                         if (error) {
234                                 splx(s);
235                                 return (error);
236                         }
237                 }
238
239                 /*
240                  * Generate protocol bytes.
241                  * For some reason X386 expects 5 bytes but never uses
242                  * the fourth or fifth?
243                  */
244                 sc->sc_bytes[0] = sc->mode.syncmask[1] 
245                     | (sc->sc_buttons & ~sc->mode.syncmask[0]);
246                 if (sc->sc_deltax > 127)
247                         sc->sc_deltax = 127;
248                 if (sc->sc_deltax < -127)
249                         sc->sc_deltax = -127;
250                 sc->sc_deltay = -sc->sc_deltay; /* Otherwise mousey goes wrong way */
251                 if (sc->sc_deltay > 127)
252                         sc->sc_deltay = 127;
253                 if (sc->sc_deltay < -127)
254                         sc->sc_deltay = -127;
255                 sc->sc_bytes[1] = sc->sc_deltax;
256                 sc->sc_bytes[2] = sc->sc_deltay;
257                 sc->sc_bytes[3] = sc->sc_bytes[4] = 0;
258                 sc->sc_bytes[5] = sc->sc_bytes[6] = 0;
259                 sc->sc_bytes[7] = MOUSE_SYS_EXTBUTTONS;
260                 sc->sc_obuttons = sc->sc_buttons;
261                 sc->sc_deltax = sc->sc_deltay = 0;
262                 sc->sc_bytesread = 0;
263         }
264         splx(s);
265         xfer = min(uio->uio_resid, sc->mode.packetsize - sc->sc_bytesread);
266         error = uiomove(&sc->sc_bytes[sc->sc_bytesread], xfer, uio);
267         if (error)
268                 return (error);
269         sc->sc_bytesread += xfer;
270         return(0);
271 }
272
273 /*
274  * mseioctl: process ioctl commands.
275  */
276 static int
277 mseioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
278 {
279         mse_softc_t *sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev));
280         mousestatus_t status;
281         int err = 0;
282         int s;
283
284         switch (cmd) {
285
286         case MOUSE_GETHWINFO:
287                 s = spltty();
288                 *(mousehw_t *)addr = sc->hw;
289                 if (sc->mode.level == 0)
290                         ((mousehw_t *)addr)->model = MOUSE_MODEL_GENERIC;
291                 splx(s);
292                 break;
293
294         case MOUSE_GETMODE:
295                 s = spltty();
296                 *(mousemode_t *)addr = sc->mode;
297                 switch (sc->mode.level) {
298                 case 0:
299                         break;
300                 case 1:
301                         ((mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE;
302                         ((mousemode_t *)addr)->syncmask[0] = MOUSE_SYS_SYNCMASK;
303                         ((mousemode_t *)addr)->syncmask[1] = MOUSE_SYS_SYNC;
304                         break;
305                 }
306                 splx(s);
307                 break;
308
309         case MOUSE_SETMODE:
310                 switch (((mousemode_t *)addr)->level) {
311                 case 0:
312                 case 1:
313                         break;
314                 default:
315                         return (EINVAL);
316                 }
317                 if (((mousemode_t *)addr)->accelfactor < -1)
318                         return (EINVAL);
319                 else if (((mousemode_t *)addr)->accelfactor >= 0)
320                         sc->mode.accelfactor = 
321                             ((mousemode_t *)addr)->accelfactor;
322                 sc->mode.level = ((mousemode_t *)addr)->level;
323                 switch (sc->mode.level) {
324                 case 0:
325                         sc->sc_bytesread = sc->mode.packetsize 
326                             = MOUSE_MSC_PACKETSIZE;
327                         break;
328                 case 1:
329                         sc->sc_bytesread = sc->mode.packetsize 
330                             = MOUSE_SYS_PACKETSIZE;
331                         break;
332                 }
333                 break;
334
335         case MOUSE_GETLEVEL:
336                 *(int *)addr = sc->mode.level;
337                 break;
338
339         case MOUSE_SETLEVEL:
340                 switch (*(int *)addr) {
341                 case 0:
342                         sc->mode.level = *(int *)addr;
343                         sc->sc_bytesread = sc->mode.packetsize 
344                             = MOUSE_MSC_PACKETSIZE;
345                         break;
346                 case 1:
347                         sc->mode.level = *(int *)addr;
348                         sc->sc_bytesread = sc->mode.packetsize 
349                             = MOUSE_SYS_PACKETSIZE;
350                         break;
351                 default:
352                         return (EINVAL);
353                 }
354                 break;
355
356         case MOUSE_GETSTATUS:
357                 s = spltty();
358                 status = sc->status;
359                 sc->status.flags = 0;
360                 sc->status.obutton = sc->status.button;
361                 sc->status.button = 0;
362                 sc->status.dx = 0;
363                 sc->status.dy = 0;
364                 sc->status.dz = 0;
365                 splx(s);
366                 *(mousestatus_t *)addr = status;
367                 break;
368
369         case MOUSE_READSTATE:
370         case MOUSE_READDATA:
371                 return (ENODEV);
372
373 #if (defined(MOUSE_GETVARS))
374         case MOUSE_GETVARS:
375         case MOUSE_SETVARS:
376                 return (ENODEV);
377 #endif
378
379         default:
380                 return (ENOTTY);
381         }
382         return (err);
383 }
384
385 /*
386  * msepoll: check for mouse input to be processed.
387  */
388 static  int
389 msepoll(struct cdev *dev, int events, struct thread *td)
390 {
391         mse_softc_t *sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev));
392         int s;
393         int revents = 0;
394
395         s = spltty();
396         if (events & (POLLIN | POLLRDNORM)) {
397                 if (sc->sc_bytesread != sc->mode.packetsize ||
398                     sc->sc_deltax != 0 || sc->sc_deltay != 0 ||
399                     (sc->sc_obuttons ^ sc->sc_buttons) != 0)
400                         revents |= events & (POLLIN | POLLRDNORM);
401                 else {
402                         /*
403                          * Since this is an exclusive open device, any previous
404                          * proc pointer is trash now, so we can just assign it.
405                          */
406                         selrecord(td, &sc->sc_selp);
407                 }
408         }
409         splx(s);
410         return (revents);
411 }
412
413 /*
414  * msetimeout: watchdog timer routine.
415  */
416 static void
417 msetimeout(void *arg)
418 {
419         struct cdev *dev;
420         mse_softc_t *sc;
421
422         dev = (struct cdev *)arg;
423         sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev));
424         if (sc->sc_watchdog) {
425                 if (bootverbose)
426                         printf("mse%d: lost interrupt?\n", MSE_UNIT(dev));
427                 mseintr(sc);
428         }
429         sc->sc_watchdog = TRUE;
430         sc->sc_callout = timeout(msetimeout, dev, hz);
431 }
432
433 /*
434  * mseintr: update mouse status. sc_deltax and sc_deltay are accumulative.
435  */
436 static void
437 mseintr(void *arg)
438 {
439         /*
440          * the table to turn MouseSystem button bits (MOUSE_MSC_BUTTON?UP)
441          * into `mousestatus' button bits (MOUSE_BUTTON?DOWN).
442          */
443         static int butmap[8] = {
444                 0, 
445                 MOUSE_BUTTON3DOWN, 
446                 MOUSE_BUTTON2DOWN, 
447                 MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN, 
448                 MOUSE_BUTTON1DOWN, 
449                 MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN, 
450                 MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
451                 MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
452         };
453         mse_softc_t *sc = arg;
454         int dx, dy, but;
455         int sign;
456
457 #ifdef DEBUG
458         static int mse_intrcnt = 0;
459         if((mse_intrcnt++ % 10000) == 0)
460                 printf("mseintr\n");
461 #endif /* DEBUG */
462         if ((sc->sc_flags & MSESC_OPEN) == 0)
463                 return;
464
465         (*sc->sc_getmouse)(sc->sc_iot, sc->sc_ioh, &dx, &dy, &but);
466         if (sc->mode.accelfactor > 0) {
467                 sign = (dx < 0);
468                 dx = dx * dx / sc->mode.accelfactor;
469                 if (dx == 0)
470                         dx = 1;
471                 if (sign)
472                         dx = -dx;
473                 sign = (dy < 0);
474                 dy = dy * dy / sc->mode.accelfactor;
475                 if (dy == 0)
476                         dy = 1;
477                 if (sign)
478                         dy = -dy;
479         }
480         sc->sc_deltax += dx;
481         sc->sc_deltay += dy;
482         sc->sc_buttons = but;
483
484         but = butmap[~but & MOUSE_MSC_BUTTONS];
485         sc->status.dx += dx;
486         sc->status.dy += dy;
487         sc->status.flags |= ((dx || dy) ? MOUSE_POSCHANGED : 0)
488             | (sc->status.button ^ but);
489         sc->status.button = but;
490
491         sc->sc_watchdog = FALSE;
492
493         /*
494          * If mouse state has changed, wake up anyone wanting to know.
495          */
496         if (sc->sc_deltax != 0 || sc->sc_deltay != 0 ||
497             (sc->sc_obuttons ^ sc->sc_buttons) != 0) {
498                 if (sc->sc_flags & MSESC_WANT) {
499                         sc->sc_flags &= ~MSESC_WANT;
500                         wakeup(sc);
501                 }
502                 selwakeuppri(&sc->sc_selp, MSEPRI);
503         }
504 }