mse.c revision 18084
1/*
2 * Copyright 1992 by the University of Guelph
3 *
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
9 * documentation.
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.
13 *
14 * $Id: mse.c,v 1.27 1996/06/08 09:37:51 bde Exp $
15 */
16/*
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!
26 */
27
28/*
29 * Modification history:
30 * Sep 6, 1994 -- Lars Fredriksen(fredriks@mcs.com)
31 *   improved probe based on input from Logitech.
32 *
33 * Oct 19, 1992 -- E. Stark (stark@cs.sunysb.edu)
34 *   fixes to make it work with Microsoft InPort busmouse
35 *
36 * Jan, 1993 -- E. Stark (stark@cs.sunysb.edu)
37 *   added patches for new "select" interface
38 *
39 * May 4, 1993 -- E. Stark (stark@cs.sunysb.edu)
40 *   changed position of some spl()'s in mseread
41 *
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.
45 */
46
47#include "mse.h"
48#if NMSE > 0
49#include <sys/param.h>
50#include <sys/systm.h>
51#include <sys/conf.h>
52#include <sys/proc.h>
53#include <sys/buf.h>
54#include <sys/kernel.h>
55#include <sys/ioctl.h>
56#include <sys/uio.h>
57#ifdef DEVFS
58#include <sys/devfsext.h>
59#endif /*DEVFS*/
60
61#include <machine/clock.h>
62
63#include <i386/isa/isa_device.h>
64
65static int mseprobe(struct isa_device *);
66static int mseattach(struct isa_device *);
67
68struct	isa_driver msedriver = {
69	mseprobe, mseattach, "mse"
70};
71
72static	d_open_t	mseopen;
73static	d_close_t	mseclose;
74static	d_read_t	mseread;
75static	d_select_t	mseselect;
76
77#define CDEV_MAJOR 27
78static struct cdevsw mse_cdevsw =
79	{ mseopen,	mseclose,	mseread,	nowrite,	/*27*/
80	  noioc,	nostop,		nullreset,	nodevtotty,/* mse */
81	  mseselect,	nommap,		NULL,	"mse",	NULL,	-1 };
82
83
84/*
85 * Software control structure for mouse. The sc_enablemouse(),
86 * sc_disablemouse() and sc_getmouse() routines must be called spl'd().
87 */
88#define	PROTOBYTES	5
89static struct mse_softc {
90	int	sc_flags;
91	int	sc_mousetype;
92	struct	selinfo sc_selp;
93	u_int	sc_port;
94	void	(*sc_enablemouse) __P((u_int port));
95	void	(*sc_disablemouse) __P((u_int port));
96	void	(*sc_getmouse) __P((u_int port, int *dx, int *dy, int *but));
97	int	sc_deltax;
98	int	sc_deltay;
99	int	sc_obuttons;
100	int	sc_buttons;
101	int	sc_bytesread;
102	u_char	sc_bytes[PROTOBYTES];
103#ifdef DEVFS
104	void 	*devfs_token;
105	void	*n_devfs_token;
106#endif
107} mse_sc[NMSE];
108
109/* Flags */
110#define	MSESC_OPEN	0x1
111#define	MSESC_WANT	0x2
112
113/* and Mouse Types */
114#define	MSE_LOGITECH	0x1
115#define	MSE_ATIINPORT	0x2
116#define	MSE_LOGI_SIG	0xA5
117
118#define	MSE_PORTA	0
119#define	MSE_PORTB	1
120#define	MSE_PORTC	2
121#define	MSE_PORTD	3
122
123#define	MSE_UNIT(dev)		(minor(dev) >> 1)
124#define	MSE_NBLOCKIO(dev)	(minor(dev) & 0x1)
125
126/*
127 * Logitech bus mouse definitions
128 */
129#define	MSE_SETUP	0x91	/* What does this mean? */
130				/* The definition for the control port */
131				/* is as follows: */
132
133				/* D7 	 =  Mode set flag (1 = active) 	*/
134				/* D6,D5 =  Mode selection (port A) 	*/
135				/* 	    00 = Mode 0 = Basic I/O 	*/
136				/* 	    01 = Mode 1 = Strobed I/O 	*/
137				/* 	    10 = Mode 2 = Bi-dir bus 	*/
138				/* D4	 =  Port A direction (1 = input)*/
139				/* D3	 =  Port C (upper 4 bits) 	*/
140				/*	    direction. (1 = input)	*/
141				/* D2	 =  Mode selection (port B & C) */
142				/*	    0 = Mode 0 = Basic I/O	*/
143				/*	    1 = Mode 1 = Strobed I/O	*/
144				/* D1	 =  Port B direction (1 = input)*/
145				/* D0	 =  Port C (lower 4 bits)	*/
146				/*	    direction. (1 = input)	*/
147
148				/* So 91 means Basic I/O on all 3 ports,*/
149				/* Port A is an input port, B is an 	*/
150				/* output port, C is split with upper	*/
151				/* 4 bits being an output port and lower*/
152				/* 4 bits an input port, and enable the */
153				/* sucker.				*/
154				/* Courtesy Intel 8255 databook. Lars   */
155#define	MSE_HOLD	0x80
156#define	MSE_RXLOW	0x00
157#define	MSE_RXHIGH	0x20
158#define	MSE_RYLOW	0x40
159#define	MSE_RYHIGH	0x60
160#define	MSE_DISINTR	0x10
161#define MSE_INTREN	0x00
162
163static int mse_probelogi __P((struct isa_device *idp));
164static void mse_disablelogi __P((u_int port));
165static void mse_getlogi __P((u_int port, int *dx, int *dy, int *but));
166static void mse_enablelogi __P((u_int port));
167
168/*
169 * ATI Inport mouse definitions
170 */
171#define	MSE_INPORT_RESET	0x80
172#define	MSE_INPORT_STATUS	0x00
173#define	MSE_INPORT_DX		0x01
174#define	MSE_INPORT_DY		0x02
175#define	MSE_INPORT_MODE		0x07
176#define	MSE_INPORT_HOLD		0x20
177#define	MSE_INPORT_INTREN	0x09
178
179static int mse_probeati __P((struct isa_device *idp));
180static void mse_enableati __P((u_int port));
181static void mse_disableati __P((u_int port));
182static void mse_getati __P((u_int port, int *dx, int *dy, int *but));
183
184#define	MSEPRI	(PZERO + 3)
185
186/*
187 * Table of mouse types.
188 * Keep the Logitech last, since I haven't figured out how to probe it
189 * properly yet. (Someday I'll have the documentation.)
190 */
191static struct mse_types {
192	int	m_type;		/* Type of bus mouse */
193	int	(*m_probe) __P((struct isa_device *idp));
194				/* Probe routine to test for it */
195	void	(*m_enable) __P((u_int port));
196				/* Start routine */
197	void	(*m_disable) __P((u_int port));
198				/* Disable interrupts routine */
199	void	(*m_get) __P((u_int port, int *dx, int *dy, int *but));
200				/* and get mouse status */
201} mse_types[] = {
202	{ MSE_ATIINPORT, mse_probeati, mse_enableati, mse_disableati, mse_getati },
203	{ MSE_LOGITECH, mse_probelogi, mse_enablelogi, mse_disablelogi, mse_getlogi },
204	{ 0, },
205};
206
207int
208mseprobe(idp)
209	register struct isa_device *idp;
210{
211	register struct mse_softc *sc = &mse_sc[idp->id_unit];
212	register int i;
213
214	/*
215	 * Check for each mouse type in the table.
216	 */
217	i = 0;
218	while (mse_types[i].m_type) {
219		if ((*mse_types[i].m_probe)(idp)) {
220			sc->sc_mousetype = mse_types[i].m_type;
221			sc->sc_enablemouse = mse_types[i].m_enable;
222			sc->sc_disablemouse = mse_types[i].m_disable;
223			sc->sc_getmouse = mse_types[i].m_get;
224			return (1);
225		}
226		i++;
227	}
228	return (0);
229}
230
231int
232mseattach(idp)
233	struct isa_device *idp;
234{
235	int unit = idp->id_unit;
236	struct mse_softc *sc = &mse_sc[unit];
237
238	sc->sc_port = idp->id_iobase;
239#ifdef	DEVFS
240	sc->devfs_token =
241		devfs_add_devswf(&mse_cdevsw, unit << 1, DV_CHR, 0, 0,
242				 0600, "mse%d", unit);
243	sc->n_devfs_token =
244		devfs_add_devswf(&mse_cdevsw, (unit<<1)+1, DV_CHR,0, 0,
245				 0600, "nmse%d", unit);
246#endif
247	return (1);
248}
249
250/*
251 * Exclusive open the mouse, initialize it and enable interrupts.
252 */
253static	int
254mseopen(dev, flags, fmt, p)
255	dev_t dev;
256	int flags;
257	int fmt;
258	struct proc *p;
259{
260	register struct mse_softc *sc;
261	int s;
262
263	if (MSE_UNIT(dev) >= NMSE)
264		return (ENXIO);
265	sc = &mse_sc[MSE_UNIT(dev)];
266	if (sc->sc_flags & MSESC_OPEN)
267		return (EBUSY);
268	sc->sc_flags |= MSESC_OPEN;
269	sc->sc_obuttons = sc->sc_buttons = 0x7;
270	sc->sc_deltax = sc->sc_deltay = 0;
271	sc->sc_bytesread = PROTOBYTES;
272
273	/*
274	 * Initialize mouse interface and enable interrupts.
275	 */
276	s = spltty();
277	(*sc->sc_enablemouse)(sc->sc_port);
278	splx(s);
279	return (0);
280}
281
282/*
283 * mseclose: just turn off mouse innterrupts.
284 */
285static	int
286mseclose(dev, flags, fmt, p)
287	dev_t dev;
288	int flags;
289	int fmt;
290	struct proc *p;
291{
292	struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)];
293	int s;
294
295	s = spltty();
296	(*sc->sc_disablemouse)(sc->sc_port);
297	sc->sc_flags &= ~MSESC_OPEN;
298	splx(s);
299	return(0);
300}
301
302/*
303 * mseread: return mouse info using the MSC serial protocol, but without
304 * using bytes 4 and 5.
305 * (Yes this is cheesy, but it makes the X386 server happy, so...)
306 */
307static	int
308mseread(dev, uio, ioflag)
309	dev_t dev;
310	struct uio *uio;
311	int ioflag;
312{
313	register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)];
314	int xfer, s, error;
315
316	/*
317	 * If there are no protocol bytes to be read, set up a new protocol
318	 * packet.
319	 */
320	s = spltty(); /* XXX Should be its own spl, but where is imlXX() */
321	if (sc->sc_bytesread >= PROTOBYTES) {
322		while (sc->sc_deltax == 0 && sc->sc_deltay == 0 &&
323		       (sc->sc_obuttons ^ sc->sc_buttons) == 0) {
324			if (MSE_NBLOCKIO(dev)) {
325				splx(s);
326				return (0);
327			}
328			sc->sc_flags |= MSESC_WANT;
329			if (error = tsleep((caddr_t)sc, MSEPRI | PCATCH,
330				"mseread", 0)) {
331				splx(s);
332				return (error);
333			}
334		}
335
336		/*
337		 * Generate protocol bytes.
338		 * For some reason X386 expects 5 bytes but never uses
339		 * the fourth or fifth?
340		 */
341		sc->sc_bytes[0] = 0x80 | (sc->sc_buttons & ~0xf8);
342		if (sc->sc_deltax > 127)
343			sc->sc_deltax = 127;
344		if (sc->sc_deltax < -127)
345			sc->sc_deltax = -127;
346		sc->sc_deltay = -sc->sc_deltay;	/* Otherwise mousey goes wrong way */
347		if (sc->sc_deltay > 127)
348			sc->sc_deltay = 127;
349		if (sc->sc_deltay < -127)
350			sc->sc_deltay = -127;
351		sc->sc_bytes[1] = sc->sc_deltax;
352		sc->sc_bytes[2] = sc->sc_deltay;
353		sc->sc_bytes[3] = sc->sc_bytes[4] = 0;
354		sc->sc_obuttons = sc->sc_buttons;
355		sc->sc_deltax = sc->sc_deltay = 0;
356		sc->sc_bytesread = 0;
357	}
358	splx(s);
359	xfer = min(uio->uio_resid, PROTOBYTES - sc->sc_bytesread);
360	if (error = uiomove(&sc->sc_bytes[sc->sc_bytesread], xfer, uio))
361		return (error);
362	sc->sc_bytesread += xfer;
363	return(0);
364}
365
366/*
367 * mseselect: check for mouse input to be processed.
368 */
369static	int
370mseselect(dev, rw, p)
371	dev_t dev;
372	int rw;
373	struct proc *p;
374{
375	register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)];
376	int s;
377
378	s = spltty();
379	if (sc->sc_bytesread != PROTOBYTES || sc->sc_deltax != 0 ||
380	    sc->sc_deltay != 0 || (sc->sc_obuttons ^ sc->sc_buttons) != 0) {
381		splx(s);
382		return (1);
383	}
384
385	/*
386	 * Since this is an exclusive open device, any previous proc.
387	 * pointer is trash now, so we can just assign it.
388	 */
389	selrecord(p, &sc->sc_selp);
390	splx(s);
391	return (0);
392}
393
394/*
395 * mseintr: update mouse status. sc_deltax and sc_deltay are accumulative.
396 */
397void
398mseintr(unit)
399	int unit;
400{
401	register struct mse_softc *sc = &mse_sc[unit];
402
403#ifdef DEBUG
404	static int mse_intrcnt = 0;
405	if((mse_intrcnt++ % 10000) == 0)
406		printf("mseintr\n");
407#endif /* DEBUG */
408	if ((sc->sc_flags & MSESC_OPEN) == 0)
409		return;
410
411	(*sc->sc_getmouse)(sc->sc_port, &sc->sc_deltax, &sc->sc_deltay, &sc->sc_buttons);
412
413	/*
414	 * If mouse state has changed, wake up anyone wanting to know.
415	 */
416	if (sc->sc_deltax != 0 || sc->sc_deltay != 0 ||
417	    (sc->sc_obuttons ^ sc->sc_buttons) != 0) {
418		if (sc->sc_flags & MSESC_WANT) {
419			sc->sc_flags &= ~MSESC_WANT;
420			wakeup((caddr_t)sc);
421		}
422		selwakeup(&sc->sc_selp);
423	}
424}
425
426/*
427 * Routines for the Logitech mouse.
428 */
429/*
430 * Test for a Logitech bus mouse and return 1 if it is.
431 * (until I know how to use the signature port properly, just disable
432 *  interrupts and return 1)
433 */
434static int
435mse_probelogi(idp)
436	register struct isa_device *idp;
437{
438
439	int sig;
440
441	outb(idp->id_iobase + MSE_PORTD, MSE_SETUP);
442		/* set the signature port */
443	outb(idp->id_iobase + MSE_PORTB, MSE_LOGI_SIG);
444
445	DELAY(30000); /* 30 ms delay */
446	sig = inb(idp->id_iobase + MSE_PORTB) & 0xFF;
447	if (sig == MSE_LOGI_SIG) {
448		outb(idp->id_iobase + MSE_PORTC, MSE_DISINTR);
449		return(1);
450	} else {
451		if (bootverbose)
452			printf("mse%d: wrong signature %x\n",idp->id_unit,sig);
453		return(0);
454	}
455}
456
457/*
458 * Initialize Logitech mouse and enable interrupts.
459 */
460static void
461mse_enablelogi(port)
462	register u_int port;
463{
464	int dx, dy, but;
465
466	outb(port + MSE_PORTD, MSE_SETUP);
467	mse_getlogi(port, &dx, &dy, &but);
468}
469
470/*
471 * Disable interrupts for Logitech mouse.
472 */
473static void
474mse_disablelogi(port)
475	register u_int port;
476{
477
478	outb(port + MSE_PORTC, MSE_DISINTR);
479}
480
481/*
482 * Get the current dx, dy and button up/down state.
483 */
484static void
485mse_getlogi(port, dx, dy, but)
486	register u_int port;
487	int *dx;
488	int *dy;
489	int *but;
490{
491	register char x, y;
492
493	outb(port + MSE_PORTC, MSE_HOLD | MSE_RXLOW);
494	x = inb(port + MSE_PORTA);
495	*but = (x >> 5) & 0x7;
496	x &= 0xf;
497	outb(port + MSE_PORTC, MSE_HOLD | MSE_RXHIGH);
498	x |= (inb(port + MSE_PORTA) << 4);
499	outb(port + MSE_PORTC, MSE_HOLD | MSE_RYLOW);
500	y = (inb(port + MSE_PORTA) & 0xf);
501	outb(port + MSE_PORTC, MSE_HOLD | MSE_RYHIGH);
502	y |= (inb(port + MSE_PORTA) << 4);
503	*dx += x;
504	*dy += y;
505	outb(port + MSE_PORTC, MSE_INTREN);
506}
507
508/*
509 * Routines for the ATI Inport bus mouse.
510 */
511/*
512 * Test for a ATI Inport bus mouse and return 1 if it is.
513 * (do not enable interrupts)
514 */
515static int
516mse_probeati(idp)
517	register struct isa_device *idp;
518{
519	int i;
520
521	for (i = 0; i < 2; i++)
522		if (inb(idp->id_iobase + MSE_PORTC) == 0xde)
523			return (1);
524	return (0);
525}
526
527/*
528 * Initialize ATI Inport mouse and enable interrupts.
529 */
530static void
531mse_enableati(port)
532	register u_int port;
533{
534
535	outb(port + MSE_PORTA, MSE_INPORT_RESET);
536	outb(port + MSE_PORTA, MSE_INPORT_MODE);
537	outb(port + MSE_PORTB, MSE_INPORT_INTREN);
538}
539
540/*
541 * Disable interrupts for ATI Inport mouse.
542 */
543static void
544mse_disableati(port)
545	register u_int port;
546{
547
548	outb(port + MSE_PORTA, MSE_INPORT_MODE);
549	outb(port + MSE_PORTB, 0);
550}
551
552/*
553 * Get current dx, dy and up/down button state.
554 */
555static void
556mse_getati(port, dx, dy, but)
557	register u_int port;
558	int *dx;
559	int *dy;
560	int *but;
561{
562	register char byte;
563
564	outb(port + MSE_PORTA, MSE_INPORT_MODE);
565	outb(port + MSE_PORTB, MSE_INPORT_HOLD);
566	outb(port + MSE_PORTA, MSE_INPORT_STATUS);
567	*but = ~(inb(port + MSE_PORTB) & 0x7);
568	outb(port + MSE_PORTA, MSE_INPORT_DX);
569	byte = inb(port + MSE_PORTB);
570	*dx += byte;
571	outb(port + MSE_PORTA, MSE_INPORT_DY);
572	byte = inb(port + MSE_PORTB);
573	*dy += byte;
574	outb(port + MSE_PORTA, MSE_INPORT_MODE);
575	outb(port + MSE_PORTB, MSE_INPORT_INTREN);
576}
577
578static mse_devsw_installed = 0;
579
580static void 	mse_drvinit(void *unused)
581{
582	dev_t dev;
583
584	if( ! mse_devsw_installed ) {
585		dev = makedev(CDEV_MAJOR, 0);
586		cdevsw_add(&dev,&mse_cdevsw, NULL);
587		mse_devsw_installed = 1;
588    	}
589}
590
591SYSINIT(msedev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,mse_drvinit,NULL)
592
593
594#endif /* NMSE */
595