mse.c revision 1549
1637Snate/*
2637Snate * Copyright 1992 by the University of Guelph
3637Snate *
4637Snate * Permission to use, copy and modify this
5637Snate * software and its documentation for any purpose and without
6637Snate * fee is hereby granted, provided that the above copyright
7637Snate * notice appear in all copies and that both that copyright
8637Snate * notice and this permission notice appear in supporting
9637Snate * documentation.
10637Snate * University of Guelph makes no representations about the suitability of
11637Snate * this software for any purpose.  It is provided "as is"
12637Snate * without express or implied warranty.
13637Snate */
14637Snate/*
15637Snate * Driver for the Logitech and ATI Inport Bus mice for use with 386bsd and
16637Snate * the X386 port, courtesy of
17637Snate * Rick Macklem, rick@snowhite.cis.uoguelph.ca
18637Snate * Caveats: The driver currently uses spltty(), but doesn't use any
19637Snate * generic tty code. It could use splmse() (that only masks off the
20637Snate * bus mouse interrupt, but that would require hacking in i386/isa/icu.s.
21637Snate * (This may be worth the effort, since the Logitech generates 30/60
22637Snate * interrupts/sec continuously while it is open.)
23637Snate * NB: The ATI has NOT been tested yet!
24637Snate */
25637Snate
26637Snate/*
27637Snate * Modification history:
28637Snate *
29637Snate * Oct 19, 1992 -- E. Stark (stark@cs.sunysb.edu)
30637Snate *   fixes to make it work with Microsoft InPort busmouse
31637Snate *
32637Snate * Jan, 1993 -- E. Stark (stark@cs.sunysb.edu)
33637Snate *   added patches for new "select" interface
34637Snate *
35637Snate * May 4, 1993 -- E. Stark (stark@cs.sunysb.edu)
36637Snate *   changed position of some spl()'s in mseread
37637Snate *
38637Snate * October 8, 1993 -- E. Stark (stark@cs.sunysb.edu)
39637Snate *   limit maximum negative x/y value to -127 to work around XFree problem
40637Snate *   that causes spurious button pushes.
41637Snate */
42637Snate
43637Snate#include "mse.h"
44637Snate#if NMSE > 0
45637Snate#include "param.h"
46637Snate#include "proc.h"
47637Snate#include "user.h"
48637Snate#include "buf.h"
49637Snate#include "systm.h"
50637Snate#include "kernel.h"
51637Snate#include "ioctl.h"
52637Snate#include "tty.h"
53637Snate#include "uio.h"
54637Snate
55637Snate#include "i386/isa/isa_device.h"
56637Snate#include "i386/isa/icu.h"
57637Snate
58798Swollmanstatic int mseprobe(struct isa_device *);
59798Swollmanstatic int mseattach(struct isa_device *);
60798Swollmanvoid mseintr(int);
61637Snate
62637Snatestruct	isa_driver msedriver = {
63637Snate	mseprobe, mseattach, "mse"
64637Snate};
65637Snate
66637Snate/*
67637Snate * Software control structure for mouse. The sc_enablemouse(),
68637Snate * sc_disablemouse() and sc_getmouse() routines must be called spl'd().
69637Snate */
70637Snate#define	PROTOBYTES	5
71637Snatestruct mse_softc {
72637Snate	int	sc_flags;
73637Snate	int	sc_mousetype;
741549Srgrimes	struct	selinfo sc_selp;
75637Snate	u_int	sc_port;
76637Snate	void	(*sc_enablemouse)();
77637Snate	void	(*sc_disablemouse)();
78637Snate	void	(*sc_getmouse)();
79637Snate	int	sc_deltax;
80637Snate	int	sc_deltay;
81637Snate	int	sc_obuttons;
82637Snate	int	sc_buttons;
83637Snate	int	sc_bytesread;
84637Snate	u_char	sc_bytes[PROTOBYTES];
85637Snate} mse_sc[NMSE];
86637Snate
87637Snate/* Flags */
88637Snate#define	MSESC_OPEN	0x1
89637Snate#define	MSESC_WANT	0x2
90637Snate
91637Snate/* and Mouse Types */
92637Snate#define	MSE_LOGITECH	0x1
93637Snate#define	MSE_ATIINPORT	0x2
94637Snate
95637Snate#define	MSE_PORTA	0
96637Snate#define	MSE_PORTB	1
97637Snate#define	MSE_PORTC	2
98637Snate#define	MSE_PORTD	3
99637Snate
100637Snate#define	MSE_UNIT(dev)		(minor(dev) >> 1)
101637Snate#define	MSE_NBLOCKIO(dev)	(minor(dev) & 0x1)
102637Snate
103637Snate/*
104637Snate * Logitech bus mouse definitions
105637Snate */
106637Snate#define	MSE_SETUP	0x91	/* What does this mean? */
107637Snate#define	MSE_HOLD	0x80
108637Snate#define	MSE_RXLOW	0x00
109637Snate#define	MSE_RXHIGH	0x20
110637Snate#define	MSE_RYLOW	0x40
111637Snate#define	MSE_RYHIGH	0x60
112637Snate#define	MSE_DISINTR	0x10
113637Snate#define MSE_INTREN	0x00
114637Snate
115637Snatestatic int mse_probelogi();
116637Snatestatic void mse_enablelogi(), mse_disablelogi(), mse_getlogi();
117637Snate
118637Snate/*
119637Snate * ATI Inport mouse definitions
120637Snate */
121637Snate#define	MSE_INPORT_RESET	0x80
122637Snate#define	MSE_INPORT_STATUS	0x00
123637Snate#define	MSE_INPORT_DX		0x01
124637Snate#define	MSE_INPORT_DY		0x02
125637Snate#define	MSE_INPORT_MODE		0x07
126637Snate#define	MSE_INPORT_HOLD		0x20
127637Snate#define	MSE_INPORT_INTREN	0x09
128637Snate
129637Snatestatic int mse_probeati();
130637Snatestatic void mse_enableati(), mse_disableati(), mse_getati();
131637Snate
132637Snate#define	MSEPRI	(PZERO + 3)
133637Snate
134637Snate/*
135637Snate * Table of mouse types.
136637Snate * Keep the Logitech last, since I haven't figured out how to probe it
137637Snate * properly yet. (Someday I'll have the documentation.)
138637Snate */
139637Snatestruct mse_types {
140637Snate	int	m_type;		/* Type of bus mouse */
141637Snate	int	(*m_probe)();	/* Probe routine to test for it */
142637Snate	void	(*m_enable)();	/* Start routine */
143637Snate	void	(*m_disable)();	/* Disable interrupts routine */
144637Snate	void	(*m_get)();	/* and get mouse status */
145637Snate} mse_types[] = {
146637Snate	{ MSE_ATIINPORT, mse_probeati, mse_enableati, mse_disableati, mse_getati },
147637Snate	{ MSE_LOGITECH, mse_probelogi, mse_enablelogi, mse_disablelogi, mse_getlogi },
148637Snate	{ 0, },
149637Snate};
150637Snate
151798Swollmanint
152637Snatemseprobe(idp)
153637Snate	register struct isa_device *idp;
154637Snate{
155637Snate	register struct mse_softc *sc = &mse_sc[idp->id_unit];
156637Snate	register int i;
157637Snate
158637Snate	/*
159637Snate	 * Check for each mouse type in the table.
160637Snate	 */
161637Snate	i = 0;
162637Snate	while (mse_types[i].m_type) {
163637Snate		if ((*mse_types[i].m_probe)(idp)) {
164637Snate			sc->sc_mousetype = mse_types[i].m_type;
165637Snate			sc->sc_enablemouse = mse_types[i].m_enable;
166637Snate			sc->sc_disablemouse = mse_types[i].m_disable;
167637Snate			sc->sc_getmouse = mse_types[i].m_get;
168637Snate			return (1);
169637Snate		}
170637Snate		i++;
171637Snate	}
172637Snate	return (0);
173637Snate}
174637Snate
175798Swollmanint
176637Snatemseattach(idp)
177637Snate	struct isa_device *idp;
178637Snate{
179637Snate	struct mse_softc *sc = &mse_sc[idp->id_unit];
180637Snate
181637Snate	sc->sc_port = idp->id_iobase;
182637Snate	return (1);
183637Snate}
184637Snate
185637Snate/*
186637Snate * Exclusive open the mouse, initialize it and enable interrupts.
187637Snate */
188798Swollmanint
189637Snatemseopen(dev, flag)
190637Snate	dev_t dev;
191637Snate	int flag;
192637Snate{
193637Snate	register struct mse_softc *sc;
194637Snate	int s;
195637Snate
196637Snate	if (MSE_UNIT(dev) >= NMSE)
197637Snate		return (ENXIO);
198637Snate	sc = &mse_sc[MSE_UNIT(dev)];
199637Snate	if (sc->sc_flags & MSESC_OPEN)
200637Snate		return (EBUSY);
201637Snate	sc->sc_flags |= MSESC_OPEN;
202637Snate	sc->sc_obuttons = sc->sc_buttons = 0x7;
203637Snate	sc->sc_deltax = sc->sc_deltay = 0;
204637Snate	sc->sc_bytesread = PROTOBYTES;
205637Snate
206637Snate	/*
207637Snate	 * Initialize mouse interface and enable interrupts.
208637Snate	 */
209637Snate	s = spltty();
210637Snate	(*sc->sc_enablemouse)(sc->sc_port);
211637Snate	splx(s);
212637Snate	return (0);
213637Snate}
214637Snate
215637Snate/*
216637Snate * mseclose: just turn off mouse innterrupts.
217637Snate */
218798Swollmanint
219637Snatemseclose(dev, flag)
220798Swollman	dev_t dev;
221637Snate	int flag;
222637Snate{
223637Snate	struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)];
224637Snate	int s;
225637Snate
226637Snate	s = spltty();
227637Snate	(*sc->sc_disablemouse)(sc->sc_port);
228637Snate	sc->sc_flags &= ~MSESC_OPEN;
229637Snate	splx(s);
230637Snate	return(0);
231637Snate}
232637Snate
233637Snate/*
234637Snate * mseread: return mouse info using the MSC serial protocol, but without
235637Snate * using bytes 4 and 5.
236637Snate * (Yes this is cheesy, but it makes the X386 server happy, so...)
237637Snate */
238798Swollmanint
239637Snatemseread(dev, uio)
240637Snate	dev_t dev;
241637Snate	struct uio *uio;
242637Snate{
243637Snate	register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)];
244637Snate	int xfer, s, error;
245637Snate
246637Snate	/*
247637Snate	 * If there are no protocol bytes to be read, set up a new protocol
248637Snate	 * packet.
249637Snate	 */
250637Snate	s = spltty(); /* XXX Should be its own spl, but where is imlXX() */
251637Snate	if (sc->sc_bytesread >= PROTOBYTES) {
252637Snate		while (sc->sc_deltax == 0 && sc->sc_deltay == 0 &&
253637Snate		       (sc->sc_obuttons ^ sc->sc_buttons) == 0) {
254637Snate			if (MSE_NBLOCKIO(dev)) {
255637Snate				splx(s);
256637Snate				return (0);
257637Snate			}
258637Snate			sc->sc_flags |= MSESC_WANT;
259637Snate			if (error = tsleep((caddr_t)sc, MSEPRI | PCATCH,
260637Snate				"mseread", 0)) {
261637Snate				splx(s);
262637Snate				return (error);
263637Snate			}
264637Snate		}
265637Snate
266637Snate		/*
267637Snate		 * Generate protocol bytes.
268637Snate		 * For some reason X386 expects 5 bytes but never uses
269637Snate		 * the fourth or fifth?
270637Snate		 */
271637Snate		sc->sc_bytes[0] = 0x80 | (sc->sc_buttons & ~0xf8);
272637Snate		if (sc->sc_deltax > 127)
273637Snate			sc->sc_deltax = 127;
274637Snate		if (sc->sc_deltax < -127)
275637Snate			sc->sc_deltax = -127;
276637Snate		sc->sc_deltay = -sc->sc_deltay;	/* Otherwise mousey goes wrong way */
277637Snate		if (sc->sc_deltay > 127)
278637Snate			sc->sc_deltay = 127;
279637Snate		if (sc->sc_deltay < -127)
280637Snate			sc->sc_deltay = -127;
281637Snate		sc->sc_bytes[1] = sc->sc_deltax;
282637Snate		sc->sc_bytes[2] = sc->sc_deltay;
283637Snate		sc->sc_bytes[3] = sc->sc_bytes[4] = 0;
284637Snate		sc->sc_obuttons = sc->sc_buttons;
285637Snate		sc->sc_deltax = sc->sc_deltay = 0;
286637Snate		sc->sc_bytesread = 0;
287637Snate	}
288637Snate	splx(s);
289637Snate	xfer = MIN(uio->uio_resid, PROTOBYTES - sc->sc_bytesread);
290637Snate	if (error = uiomove(&sc->sc_bytes[sc->sc_bytesread], xfer, uio))
291637Snate		return (error);
292637Snate	sc->sc_bytesread += xfer;
293637Snate	return(0);
294637Snate}
295637Snate
296637Snate/*
297637Snate * mseselect: check for mouse input to be processed.
298637Snate */
299798Swollmanint
300637Snatemseselect(dev, rw, p)
301637Snate	dev_t dev;
302637Snate	int rw;
303637Snate	struct proc *p;
304637Snate{
305637Snate	register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)];
306637Snate	int s;
307637Snate
308637Snate	s = spltty();
309637Snate	if (sc->sc_bytesread != PROTOBYTES || sc->sc_deltax != 0 ||
310637Snate	    sc->sc_deltay != 0 || (sc->sc_obuttons ^ sc->sc_buttons) != 0) {
311637Snate		splx(s);
312637Snate		return (1);
313637Snate	}
314637Snate
315637Snate	/*
316637Snate	 * Since this is an exclusive open device, any previous proc.
317637Snate	 * pointer is trash now, so we can just assign it.
318637Snate	 */
3191549Srgrimes	selrecord(p, &sc->sc_selp);
320637Snate	splx(s);
321637Snate	return (0);
322637Snate}
323637Snate
324637Snate/*
325637Snate * mseintr: update mouse status. sc_deltax and sc_deltay are accumulative.
326637Snate */
327798Swollmanvoid
328637Snatemseintr(unit)
329637Snate	int unit;
330637Snate{
331637Snate	register struct mse_softc *sc = &mse_sc[unit];
332637Snate	pid_t p;
333637Snate
334637Snate#ifdef DEBUG
335637Snate	static int mse_intrcnt = 0;
336637Snate	if((mse_intrcnt++ % 10000) == 0)
337637Snate		printf("mseintr\n");
338637Snate#endif /* DEBUG */
339637Snate	if ((sc->sc_flags & MSESC_OPEN) == 0)
340637Snate		return;
341637Snate
342637Snate	(*sc->sc_getmouse)(sc->sc_port, &sc->sc_deltax, &sc->sc_deltay, &sc->sc_buttons);
343637Snate
344637Snate	/*
345637Snate	 * If mouse state has changed, wake up anyone wanting to know.
346637Snate	 */
347637Snate	if (sc->sc_deltax != 0 || sc->sc_deltay != 0 ||
348637Snate	    (sc->sc_obuttons ^ sc->sc_buttons) != 0) {
349637Snate		if (sc->sc_flags & MSESC_WANT) {
350637Snate			sc->sc_flags &= ~MSESC_WANT;
351637Snate			wakeup((caddr_t)sc);
352637Snate		}
3531549Srgrimes		selwakeup(&sc->sc_selp);
354637Snate	}
355637Snate}
356637Snate
357637Snate/*
358637Snate * Routines for the Logitech mouse.
359637Snate */
360637Snate/*
361637Snate * Test for a Logitech bus mouse and return 1 if it is.
362637Snate * (until I know how to use the signature port properly, just disable
363637Snate *  interrupts and return 1)
364637Snate */
365637Snatestatic int
366637Snatemse_probelogi(idp)
367637Snate	register struct isa_device *idp;
368637Snate{
369637Snate
370637Snate	outb(idp->id_iobase + MSE_PORTB, 0x55);
371637Snate	if (inb(idp->id_iobase + MSE_PORTB) == 0x55) {
372637Snate		outb(idp->id_iobase + MSE_PORTB, 0xaa);
373637Snate		if (inb(idp->id_iobase + MSE_PORTB) == 0xaa)
374637Snate			return (1);
375637Snate	}
376637Snate	return (0);
377637Snate}
378637Snate
379637Snate/*
380637Snate * Initialize Logitech mouse and enable interrupts.
381637Snate */
382637Snatestatic void
383637Snatemse_enablelogi(port)
384637Snate	register u_int port;
385637Snate{
386637Snate	int dx, dy, but;
387637Snate
388637Snate	outb(port + MSE_PORTD, MSE_SETUP);
389637Snate	mse_getlogi(port, &dx, &dy, &but);
390637Snate}
391637Snate
392637Snate/*
393637Snate * Disable interrupts for Logitech mouse.
394637Snate */
395637Snatestatic void
396637Snatemse_disablelogi(port)
397637Snate	register u_int port;
398637Snate{
399637Snate
400637Snate	outb(port + MSE_PORTC, MSE_DISINTR);
401637Snate}
402637Snate
403637Snate/*
404637Snate * Get the current dx, dy and button up/down state.
405637Snate */
406637Snatestatic void
407637Snatemse_getlogi(port, dx, dy, but)
408637Snate	register u_int port;
409637Snate	int *dx;
410637Snate	int *dy;
411637Snate	int *but;
412637Snate{
413637Snate	register char x, y;
414637Snate
415637Snate	outb(port + MSE_PORTC, MSE_HOLD | MSE_RXLOW);
416637Snate	x = inb(port + MSE_PORTA);
417637Snate	*but = (x >> 5) & 0x7;
418637Snate	x &= 0xf;
419637Snate	outb(port + MSE_PORTC, MSE_HOLD | MSE_RXHIGH);
420637Snate	x |= (inb(port + MSE_PORTA) << 4);
421637Snate	outb(port + MSE_PORTC, MSE_HOLD | MSE_RYLOW);
422637Snate	y = (inb(port + MSE_PORTA) & 0xf);
423637Snate	outb(port + MSE_PORTC, MSE_HOLD | MSE_RYHIGH);
424637Snate	y |= (inb(port + MSE_PORTA) << 4);
425637Snate	*dx += x;
426637Snate	*dy += y;
427637Snate	outb(port + MSE_PORTC, MSE_INTREN);
428637Snate}
429637Snate
430637Snate/*
431637Snate * Routines for the ATI Inport bus mouse.
432637Snate */
433637Snate/*
434637Snate * Test for a ATI Inport bus mouse and return 1 if it is.
435637Snate * (do not enable interrupts)
436637Snate */
437637Snatestatic int
438637Snatemse_probeati(idp)
439637Snate	register struct isa_device *idp;
440637Snate{
441637Snate	int i;
442637Snate
443637Snate	for (i = 0; i < 2; i++)
444637Snate		if (inb(idp->id_iobase + MSE_PORTC) == 0xde)
445637Snate			return (1);
446637Snate	return (0);
447637Snate}
448637Snate
449637Snate/*
450637Snate * Initialize ATI Inport mouse and enable interrupts.
451637Snate */
452637Snatestatic void
453637Snatemse_enableati(port)
454637Snate	register u_int port;
455637Snate{
456637Snate
457637Snate	outb(port + MSE_PORTA, MSE_INPORT_RESET);
458637Snate	outb(port + MSE_PORTA, MSE_INPORT_MODE);
459637Snate	outb(port + MSE_PORTB, MSE_INPORT_INTREN);
460637Snate}
461637Snate
462637Snate/*
463637Snate * Disable interrupts for ATI Inport mouse.
464637Snate */
465637Snatestatic void
466637Snatemse_disableati(port)
467637Snate	register u_int port;
468637Snate{
469637Snate
470637Snate	outb(port + MSE_PORTA, MSE_INPORT_MODE);
471637Snate	outb(port + MSE_PORTB, 0);
472637Snate}
473637Snate
474637Snate/*
475637Snate * Get current dx, dy and up/down button state.
476637Snate */
477637Snatestatic void
478637Snatemse_getati(port, dx, dy, but)
479637Snate	register u_int port;
480637Snate	int *dx;
481637Snate	int *dy;
482637Snate	int *but;
483637Snate{
484637Snate	register char byte;
485637Snate
486637Snate	outb(port + MSE_PORTA, MSE_INPORT_MODE);
487637Snate	outb(port + MSE_PORTB, MSE_INPORT_HOLD);
488637Snate	outb(port + MSE_PORTA, MSE_INPORT_STATUS);
489637Snate	*but = ~(inb(port + MSE_PORTB) & 0x7);
490637Snate	outb(port + MSE_PORTA, MSE_INPORT_DX);
491637Snate	byte = inb(port + MSE_PORTB);
492637Snate	*dx += byte;
493637Snate	outb(port + MSE_PORTA, MSE_INPORT_DY);
494637Snate	byte = inb(port + MSE_PORTB);
495637Snate	*dy += byte;
496637Snate	outb(port + MSE_PORTA, MSE_INPORT_MODE);
497637Snate	outb(port + MSE_PORTB, MSE_INPORT_INTREN);
498637Snate}
499637Snate#endif /* NMSE */
500