2 * Copyright 1992 by the University of Guelph
4 * Permission to use, copy and modify this
5 * software and its documentation for any purpose and without
6 * fee is hereby granted, provided that the above copyright
7 * notice appear in all copies and that both that copyright
8 * notice and this permission notice appear in supporting
10 * University of Guelph makes no representations about the suitability of
11 * this software for any purpose. It is provided "as is"
12 * without express or implied warranty.
17 * Driver for the Logitech and ATI Inport Bus mice for use with 386bsd and
18 * the X386 port, courtesy of
19 * Rick Macklem, rick@snowhite.cis.uoguelph.ca
20 * Caveats: The driver currently uses spltty(), but doesn't use any
21 * generic tty code. It could use splmse() (that only masks off the
22 * bus mouse interrupt, but that would require hacking in i386/isa/icu.s.
23 * (This may be worth the effort, since the Logitech generates 30/60
24 * interrupts/sec continuously while it is open.)
25 * NB: The ATI has NOT been tested yet!
29 * Modification history:
30 * Sep 6, 1994 -- Lars Fredriksen(fredriks@mcs.com)
31 * improved probe based on input from Logitech.
33 * Oct 19, 1992 -- E. Stark (stark@cs.sunysb.edu)
34 * fixes to make it work with Microsoft InPort busmouse
36 * Jan, 1993 -- E. Stark (stark@cs.sunysb.edu)
37 * added patches for new "select" interface
39 * May 4, 1993 -- E. Stark (stark@cs.sunysb.edu)
40 * changed position of some spl()'s in mseread
42 * October 8, 1993 -- E. Stark (stark@cs.sunysb.edu)
43 * limit maximum negative x/y value to -127 to work around XFree problem
44 * that causes spurious button pushes.
50 #include <sys/param.h>
51 #include <sys/systm.h>
53 #include <sys/kernel.h>
55 #include <sys/select.h>
58 #include <machine/clock.h>
59 #include <machine/mouse.h>
61 #include <i386/isa/isa_device.h>
62 #include <i386/isa/icu.h>
64 /* driver configuration flags (config) */
65 #define MSE_CONFIG_ACCEL 0x00f0 /* acceleration factor */
66 #define MSE_CONFIG_FLAGS (MSE_CONFIG_ACCEL)
68 static int mseprobe(struct isa_device *);
69 static int mseattach(struct isa_device *);
71 struct isa_driver msedriver = {
72 mseprobe, mseattach, "mse"
75 static d_open_t mseopen;
76 static d_close_t mseclose;
77 static d_read_t mseread;
78 static d_ioctl_t mseioctl;
79 static d_poll_t msepoll;
82 static struct cdevsw mse_cdevsw = {
90 /* strategy */ nostrategy,
99 static ointhand2_t mseintr;
102 * Software control structure for mouse. The sc_enablemouse(),
103 * sc_disablemouse() and sc_getmouse() routines must be called spl'd().
105 static struct mse_softc {
108 struct selinfo sc_selp;
110 void (*sc_enablemouse) __P((u_int port));
111 void (*sc_disablemouse) __P((u_int port));
112 void (*sc_getmouse) __P((u_int port, int *dx, int *dy, int *but));
118 u_char sc_bytes[MOUSE_SYS_PACKETSIZE];
121 mousestatus_t status;
125 #define MSESC_OPEN 0x1
126 #define MSESC_WANT 0x2
128 /* and Mouse Types */
129 #define MSE_NONE 0 /* don't move this! */
131 #define MSE_98BUSMOUSE 0x1
133 #define MSE_LOGITECH 0x1
134 #define MSE_ATIINPORT 0x2
135 #define MSE_LOGI_SIG 0xA5
139 #define NORMAL_MSPORT 0x7fd9
152 #define MSE_UNIT(dev) (minor(dev) >> 1)
153 #define MSE_NBLOCKIO(dev) (minor(dev) & 0x1)
157 * PC-9801 Bus mouse definitions
169 #define INT_ENABLE 0x8
170 #define INT_DISABLE 0x9
171 #define HC_NO_CLEAR 0xe
174 #define NORMAL_MSIRQ IRQ13 /* INT6 */
179 static int mse_probe98m __P((struct isa_device *idp));
180 static void mse_disable98m __P((u_int port));
181 static void mse_get98m __P((u_int port, int *dx, int *dy, int *but));
182 static void mse_enable98m __P((u_int port));
185 * Logitech bus mouse definitions
187 #define MSE_SETUP 0x91 /* What does this mean? */
188 /* The definition for the control port */
191 /* D7 = Mode set flag (1 = active) */
192 /* D6,D5 = Mode selection (port A) */
193 /* 00 = Mode 0 = Basic I/O */
194 /* 01 = Mode 1 = Strobed I/O */
195 /* 10 = Mode 2 = Bi-dir bus */
196 /* D4 = Port A direction (1 = input)*/
197 /* D3 = Port C (upper 4 bits) */
198 /* direction. (1 = input) */
199 /* D2 = Mode selection (port B & C) */
200 /* 0 = Mode 0 = Basic I/O */
201 /* 1 = Mode 1 = Strobed I/O */
202 /* D1 = Port B direction (1 = input)*/
203 /* D0 = Port C (lower 4 bits) */
204 /* direction. (1 = input) */
206 /* So 91 means Basic I/O on all 3 ports,*/
207 /* Port A is an input port, B is an */
208 /* output port, C is split with upper */
209 /* 4 bits being an output port and lower*/
210 /* 4 bits an input port, and enable the */
212 /* Courtesy Intel 8255 databook. Lars */
213 #define MSE_HOLD 0x80
214 #define MSE_RXLOW 0x00
215 #define MSE_RXHIGH 0x20
216 #define MSE_RYLOW 0x40
217 #define MSE_RYHIGH 0x60
218 #define MSE_DISINTR 0x10
219 #define MSE_INTREN 0x00
221 static int mse_probelogi __P((struct isa_device *idp));
222 static void mse_disablelogi __P((u_int port));
223 static void mse_getlogi __P((u_int port, int *dx, int *dy, int *but));
224 static void mse_enablelogi __P((u_int port));
227 * ATI Inport mouse definitions
229 #define MSE_INPORT_RESET 0x80
230 #define MSE_INPORT_STATUS 0x00
231 #define MSE_INPORT_DX 0x01
232 #define MSE_INPORT_DY 0x02
233 #define MSE_INPORT_MODE 0x07
234 #define MSE_INPORT_HOLD 0x20
235 #define MSE_INPORT_INTREN 0x09
237 static int mse_probeati __P((struct isa_device *idp));
238 static void mse_enableati __P((u_int port));
239 static void mse_disableati __P((u_int port));
240 static void mse_getati __P((u_int port, int *dx, int *dy, int *but));
243 #define MSEPRI (PZERO + 3)
246 * Table of mouse types.
247 * Keep the Logitech last, since I haven't figured out how to probe it
248 * properly yet. (Someday I'll have the documentation.)
250 static struct mse_types {
251 int m_type; /* Type of bus mouse */
252 int (*m_probe) __P((struct isa_device *idp));
253 /* Probe routine to test for it */
254 void (*m_enable) __P((u_int port));
256 void (*m_disable) __P((u_int port));
257 /* Disable interrupts routine */
258 void (*m_get) __P((u_int port, int *dx, int *dy, int *but));
259 /* and get mouse status */
260 mousehw_t m_hw; /* buttons iftype type model hwid */
261 mousemode_t m_mode; /* proto rate res accel level size mask */
265 mse_probe98m, mse_enable98m, mse_disable98m, mse_get98m,
266 { 2, MOUSE_IF_BUS, MOUSE_MOUSE, MOUSE_MODEL_GENERIC, 0, },
267 { MOUSE_PROTO_BUS, -1, -1, 0, 0, MOUSE_MSC_PACKETSIZE,
268 { MOUSE_MSC_SYNCMASK, MOUSE_MSC_SYNC, }, }, },
271 mse_probeati, mse_enableati, mse_disableati, mse_getati,
272 { 2, MOUSE_IF_INPORT, MOUSE_MOUSE, MOUSE_MODEL_GENERIC, 0, },
273 { MOUSE_PROTO_INPORT, -1, -1, 0, 0, MOUSE_MSC_PACKETSIZE,
274 { MOUSE_MSC_SYNCMASK, MOUSE_MSC_SYNC, }, }, },
276 mse_probelogi, mse_enablelogi, mse_disablelogi, mse_getlogi,
277 { 2, MOUSE_IF_BUS, MOUSE_MOUSE, MOUSE_MODEL_GENERIC, 0, },
278 { MOUSE_PROTO_BUS, -1, -1, 0, 0, MOUSE_MSC_PACKETSIZE,
279 { MOUSE_MSC_SYNCMASK, MOUSE_MSC_SYNC, }, }, },
286 register struct isa_device *idp;
288 register struct mse_softc *sc = &mse_sc[idp->id_unit];
292 * Check for each mouse type in the table.
295 while (mse_types[i].m_type) {
296 if ((*mse_types[i].m_probe)(idp)) {
297 sc->sc_mousetype = mse_types[i].m_type;
298 sc->sc_enablemouse = mse_types[i].m_enable;
299 sc->sc_disablemouse = mse_types[i].m_disable;
300 sc->sc_getmouse = mse_types[i].m_get;
301 sc->hw = mse_types[i].m_hw;
302 sc->mode = mse_types[i].m_mode;
312 struct isa_device *idp;
314 int unit = idp->id_unit;
315 struct mse_softc *sc = &mse_sc[unit];
318 if (msport != idp->id_iobase) {
319 idp->id_iobase = msport;
320 printf(" [ioport is changed to #0x%x]", msport);
323 if (msirq != idp->id_irq) {
325 printf(" [irq is changed to IR%d]", ffs(msirq)-1);
329 idp->id_ointr = mseintr;
330 sc->sc_port = idp->id_iobase;
331 sc->mode.accelfactor = (idp->id_flags & MSE_CONFIG_ACCEL) >> 4;
332 make_dev(&mse_cdevsw, unit << 1, 0, 0, 0600, "mse%d", unit);
333 make_dev(&mse_cdevsw, (unit<<1)+1, 0, 0, 0600, "nmse%d", unit);
338 * Exclusive open the mouse, initialize it and enable interrupts.
341 mseopen(dev, flags, fmt, p)
347 register struct mse_softc *sc;
350 if (MSE_UNIT(dev) >= NMSE)
352 sc = &mse_sc[MSE_UNIT(dev)];
353 if (sc->sc_mousetype == MSE_NONE)
355 if (sc->sc_flags & MSESC_OPEN)
357 sc->sc_flags |= MSESC_OPEN;
358 sc->sc_obuttons = sc->sc_buttons = MOUSE_MSC_BUTTONS;
359 sc->sc_deltax = sc->sc_deltay = 0;
360 sc->sc_bytesread = sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
362 sc->status.flags = 0;
363 sc->status.button = sc->status.obutton = 0;
364 sc->status.dx = sc->status.dy = sc->status.dz = 0;
367 * Initialize mouse interface and enable interrupts.
370 (*sc->sc_enablemouse)(sc->sc_port);
376 * mseclose: just turn off mouse innterrupts.
379 mseclose(dev, flags, fmt, p)
385 struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)];
389 (*sc->sc_disablemouse)(sc->sc_port);
390 sc->sc_flags &= ~MSESC_OPEN;
396 * mseread: return mouse info using the MSC serial protocol, but without
397 * using bytes 4 and 5.
398 * (Yes this is cheesy, but it makes the X386 server happy, so...)
401 mseread(dev, uio, ioflag)
406 register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)];
410 * If there are no protocol bytes to be read, set up a new protocol
413 s = spltty(); /* XXX Should be its own spl, but where is imlXX() */
414 if (sc->sc_bytesread >= sc->mode.packetsize) {
415 while (sc->sc_deltax == 0 && sc->sc_deltay == 0 &&
416 (sc->sc_obuttons ^ sc->sc_buttons) == 0) {
417 if (MSE_NBLOCKIO(dev)) {
421 sc->sc_flags |= MSESC_WANT;
422 error = tsleep((caddr_t)sc, MSEPRI | PCATCH,
431 * Generate protocol bytes.
432 * For some reason X386 expects 5 bytes but never uses
433 * the fourth or fifth?
435 sc->sc_bytes[0] = sc->mode.syncmask[1]
436 | (sc->sc_buttons & ~sc->mode.syncmask[0]);
437 if (sc->sc_deltax > 127)
439 if (sc->sc_deltax < -127)
440 sc->sc_deltax = -127;
441 sc->sc_deltay = -sc->sc_deltay; /* Otherwise mousey goes wrong way */
442 if (sc->sc_deltay > 127)
444 if (sc->sc_deltay < -127)
445 sc->sc_deltay = -127;
446 sc->sc_bytes[1] = sc->sc_deltax;
447 sc->sc_bytes[2] = sc->sc_deltay;
448 sc->sc_bytes[3] = sc->sc_bytes[4] = 0;
449 sc->sc_bytes[5] = sc->sc_bytes[6] = 0;
450 sc->sc_bytes[7] = MOUSE_SYS_EXTBUTTONS;
451 sc->sc_obuttons = sc->sc_buttons;
452 sc->sc_deltax = sc->sc_deltay = 0;
453 sc->sc_bytesread = 0;
456 xfer = min(uio->uio_resid, sc->mode.packetsize - sc->sc_bytesread);
457 error = uiomove(&sc->sc_bytes[sc->sc_bytesread], xfer, uio);
460 sc->sc_bytesread += xfer;
465 * mseioctl: process ioctl commands.
468 mseioctl(dev, cmd, addr, flag, p)
475 register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)];
476 mousestatus_t status;
482 case MOUSE_GETHWINFO:
484 *(mousehw_t *)addr = sc->hw;
485 if (sc->mode.level == 0)
486 ((mousehw_t *)addr)->model = MOUSE_MODEL_GENERIC;
492 *(mousemode_t *)addr = sc->mode;
493 switch (sc->mode.level) {
497 ((mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE;
498 ((mousemode_t *)addr)->syncmask[0] = MOUSE_SYS_SYNCMASK;
499 ((mousemode_t *)addr)->syncmask[1] = MOUSE_SYS_SYNC;
506 switch (((mousemode_t *)addr)->level) {
513 if (((mousemode_t *)addr)->accelfactor < -1)
515 else if (((mousemode_t *)addr)->accelfactor >= 0)
516 sc->mode.accelfactor =
517 ((mousemode_t *)addr)->accelfactor;
518 sc->mode.level = ((mousemode_t *)addr)->level;
519 switch (sc->mode.level) {
521 sc->sc_bytesread = sc->mode.packetsize
522 = MOUSE_MSC_PACKETSIZE;
525 sc->sc_bytesread = sc->mode.packetsize
526 = MOUSE_SYS_PACKETSIZE;
532 *(int *)addr = sc->mode.level;
536 switch (*(int *)addr) {
538 sc->mode.level = *(int *)addr;
539 sc->sc_bytesread = sc->mode.packetsize
540 = MOUSE_MSC_PACKETSIZE;
543 sc->mode.level = *(int *)addr;
544 sc->sc_bytesread = sc->mode.packetsize
545 = MOUSE_SYS_PACKETSIZE;
552 case MOUSE_GETSTATUS:
555 sc->status.flags = 0;
556 sc->status.obutton = sc->status.button;
557 sc->status.button = 0;
562 *(mousestatus_t *)addr = status;
565 case MOUSE_READSTATE:
569 #if (defined(MOUSE_GETVARS))
582 * msepoll: check for mouse input to be processed.
585 msepoll(dev, events, p)
590 register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)];
595 if (events & (POLLIN | POLLRDNORM)) {
596 if (sc->sc_bytesread != sc->mode.packetsize ||
597 sc->sc_deltax != 0 || sc->sc_deltay != 0 ||
598 (sc->sc_obuttons ^ sc->sc_buttons) != 0)
599 revents |= events & (POLLIN | POLLRDNORM);
602 * Since this is an exclusive open device, any previous
603 * proc pointer is trash now, so we can just assign it.
605 selrecord(p, &sc->sc_selp);
613 * mseintr: update mouse status. sc_deltax and sc_deltay are accumulative.
620 * the table to turn MouseSystem button bits (MOUSE_MSC_BUTTON?UP)
621 * into `mousestatus' button bits (MOUSE_BUTTON?DOWN).
623 static int butmap[8] = {
627 MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
629 MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
630 MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
631 MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
633 register struct mse_softc *sc = &mse_sc[unit];
638 static int mse_intrcnt = 0;
639 if((mse_intrcnt++ % 10000) == 0)
642 if ((sc->sc_flags & MSESC_OPEN) == 0)
645 (*sc->sc_getmouse)(sc->sc_port, &dx, &dy, &but);
646 if (sc->mode.accelfactor > 0) {
648 dx = dx * dx / sc->mode.accelfactor;
654 dy = dy * dy / sc->mode.accelfactor;
662 sc->sc_buttons = but;
664 but = butmap[~but & MOUSE_MSC_BUTTONS];
667 sc->status.flags |= ((dx || dy) ? MOUSE_POSCHANGED : 0)
668 | (sc->status.button ^ but);
669 sc->status.button = but;
672 * If mouse state has changed, wake up anyone wanting to know.
674 if (sc->sc_deltax != 0 || sc->sc_deltay != 0 ||
675 (sc->sc_obuttons ^ sc->sc_buttons) != 0) {
676 if (sc->sc_flags & MSESC_WANT) {
677 sc->sc_flags &= ~MSESC_WANT;
680 selwakeup(&sc->sc_selp);
686 * Routines for the Logitech mouse.
689 * Test for a Logitech bus mouse and return 1 if it is.
690 * (until I know how to use the signature port properly, just disable
691 * interrupts and return 1)
695 register struct isa_device *idp;
700 outb(idp->id_iobase + MSE_PORTD, MSE_SETUP);
701 /* set the signature port */
702 outb(idp->id_iobase + MSE_PORTB, MSE_LOGI_SIG);
704 DELAY(30000); /* 30 ms delay */
705 sig = inb(idp->id_iobase + MSE_PORTB) & 0xFF;
706 if (sig == MSE_LOGI_SIG) {
707 outb(idp->id_iobase + MSE_PORTC, MSE_DISINTR);
711 printf("mse%d: wrong signature %x\n",idp->id_unit,sig);
717 * Initialize Logitech mouse and enable interrupts.
725 outb(port + MSE_PORTD, MSE_SETUP);
726 mse_getlogi(port, &dx, &dy, &but);
730 * Disable interrupts for Logitech mouse.
733 mse_disablelogi(port)
737 outb(port + MSE_PORTC, MSE_DISINTR);
741 * Get the current dx, dy and button up/down state.
744 mse_getlogi(port, dx, dy, but)
752 outb(port + MSE_PORTC, MSE_HOLD | MSE_RXLOW);
753 x = inb(port + MSE_PORTA);
754 *but = (x >> 5) & MOUSE_MSC_BUTTONS;
756 outb(port + MSE_PORTC, MSE_HOLD | MSE_RXHIGH);
757 x |= (inb(port + MSE_PORTA) << 4);
758 outb(port + MSE_PORTC, MSE_HOLD | MSE_RYLOW);
759 y = (inb(port + MSE_PORTA) & 0xf);
760 outb(port + MSE_PORTC, MSE_HOLD | MSE_RYHIGH);
761 y |= (inb(port + MSE_PORTA) << 4);
764 outb(port + MSE_PORTC, MSE_INTREN);
768 * Routines for the ATI Inport bus mouse.
771 * Test for a ATI Inport bus mouse and return 1 if it is.
772 * (do not enable interrupts)
776 register struct isa_device *idp;
780 for (i = 0; i < 2; i++)
781 if (inb(idp->id_iobase + MSE_PORTC) == 0xde)
787 * Initialize ATI Inport mouse and enable interrupts.
794 outb(port + MSE_PORTA, MSE_INPORT_RESET);
795 outb(port + MSE_PORTA, MSE_INPORT_MODE);
796 outb(port + MSE_PORTB, MSE_INPORT_INTREN);
800 * Disable interrupts for ATI Inport mouse.
807 outb(port + MSE_PORTA, MSE_INPORT_MODE);
808 outb(port + MSE_PORTB, 0);
812 * Get current dx, dy and up/down button state.
815 mse_getati(port, dx, dy, but)
823 outb(port + MSE_PORTA, MSE_INPORT_MODE);
824 outb(port + MSE_PORTB, MSE_INPORT_HOLD);
825 outb(port + MSE_PORTA, MSE_INPORT_STATUS);
826 *but = ~inb(port + MSE_PORTB) & MOUSE_MSC_BUTTONS;
827 outb(port + MSE_PORTA, MSE_INPORT_DX);
828 byte = inb(port + MSE_PORTB);
830 outb(port + MSE_PORTA, MSE_INPORT_DY);
831 byte = inb(port + MSE_PORTB);
833 outb(port + MSE_PORTA, MSE_INPORT_MODE);
834 outb(port + MSE_PORTB, MSE_INPORT_INTREN);
841 * Routines for the PC98 bus mouse.
845 * Test for a PC98 bus mouse and return 1 if it is.
846 * (do not enable interrupts)
850 register struct isa_device *idp;
853 msport = NORMAL_MSPORT;
854 msirq = NORMAL_MSIRQ;
857 outb(msport + MODE, 0x93);
859 outb(msport + INT, INT_DISABLE); /* INT disable */
860 outb(msport + HC, HC_NO_CLEAR); /* HC = 0 */
861 outb(msport + HC, HC_CLEAR); /* HC = 1 */
866 * Initialize PC98 bus mouse and enable interrupts.
872 outb(port + INT, INT_ENABLE);/* INT enable */
873 outb(port + HC, HC_NO_CLEAR); /* HC = 0 */
874 outb(port + HC, HC_CLEAR); /* HC = 1 */
878 * Disable interrupts for PC98 Bus mouse.
884 outb(port + INT, INT_DISABLE);/* INT disable */
885 outb(port + HC, HC_NO_CLEAR); /* HC = 0 */
886 outb(port + HC, HC_CLEAR); /* HC = 1 */
890 * Get current dx, dy and up/down button state.
893 mse_get98m(port, dx, dy, but)
901 outb(port + INT, INT_DISABLE); /* INT disable */
903 outb(port + HC, HC_CLEAR); /* HC = 1 */
905 outb(port + PORT_C, 0x90 | XL);
906 x = inb(port + PORT_A) & 0x0f; /* X low */
907 outb(port + PORT_C, 0x90 | XH);
908 x |= ((inb(port + PORT_A) & 0x0f) << 4); /* X high */
910 outb(port + PORT_C, 0x90 | YL);
911 y = (inb(port + PORT_A) & 0x0f); /* Y low */
912 outb(port + PORT_C, 0x90 | YH);
913 y |= ((inb(port + PORT_A) & 0x0f) << 4);
915 *but = (inb(port + PORT_A) >> 5) & 7;
920 outb(port + HC, HC_NO_CLEAR); /* HC = 0 */
922 outb(port + INT, INT_ENABLE); /* INT enable */