1/* $OpenBSD: mms.c,v 1.21 2022/02/21 10:24:28 mpi Exp $ */
2/*	$NetBSD: mms.c,v 1.35 2000/01/08 02:57:25 takemura Exp $	*/
3
4/*-
5 * Copyright (c) 1993, 1994 Charles M. Hannum.
6 * Copyright (c) 1992, 1993 Erik Forsberg.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
16 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
18 * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
22 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/param.h>
28#include <sys/systm.h>
29#include <sys/ioctl.h>
30#include <sys/device.h>
31
32#include <machine/intr.h>
33#include <machine/bus.h>
34
35#include <dev/isa/isavar.h>
36
37#include <dev/wscons/wsconsio.h>
38#include <dev/wscons/wsmousevar.h>
39
40#define	MMS_ADDR	0	/* offset for register select */
41#define	MMS_DATA	1	/* offset for InPort data */
42#define	MMS_IDENT	2	/* offset for identification register */
43#define	MMS_NPORTS	4
44
45struct mms_softc {		/* driver status information */
46	struct device sc_dev;
47	void *sc_ih;
48
49	bus_space_tag_t sc_iot;
50	bus_space_handle_t sc_ioh;
51
52	int sc_enabled; /* device is open */
53
54	struct device *sc_wsmousedev;
55};
56
57int mmsprobe(struct device *, void *, void *);
58void mmsattach(struct device *, struct device *, void *);
59int mmsintr(void *);
60
61const struct cfattach mms_ca = {
62	sizeof(struct mms_softc), mmsprobe, mmsattach
63};
64
65int	mms_enable(void *);
66int	mms_ioctl(void *, u_long, caddr_t, int, struct proc *);
67void	mms_disable(void *);
68
69const struct wsmouse_accessops mms_accessops = {
70	mms_enable,
71	mms_ioctl,
72	mms_disable,
73};
74
75int
76mmsprobe(struct device *parent, void *match, void *aux)
77{
78	struct isa_attach_args *ia = aux;
79	bus_space_tag_t iot = ia->ia_iot;
80	bus_space_handle_t ioh;
81	int rv;
82
83	/* Disallow wildcarded i/o address. */
84	if (ia->ia_iobase == IOBASEUNK)
85		return 0;
86
87	/* Map the i/o space. */
88	if (bus_space_map(iot, ia->ia_iobase, MMS_NPORTS, 0, &ioh))
89		return 0;
90
91	rv = 0;
92
93	/* Read identification register to see if present */
94	if (bus_space_read_1(iot, ioh, MMS_IDENT) != 0xde)
95		goto out;
96
97	/* Seems it was there; reset. */
98	bus_space_write_1(iot, ioh, MMS_ADDR, 0x87);
99
100	rv = 1;
101	ia->ia_iosize = MMS_NPORTS;
102	ia->ia_msize = 0;
103
104out:
105	bus_space_unmap(iot, ioh, MMS_NPORTS);
106	return rv;
107}
108
109void
110mmsattach(struct device *parent, struct device *self, void *aux)
111{
112	struct mms_softc *sc = (void *)self;
113	struct isa_attach_args *ia = aux;
114	bus_space_tag_t iot = ia->ia_iot;
115	bus_space_handle_t ioh;
116	struct wsmousedev_attach_args a;
117
118	printf("\n");
119
120	if (bus_space_map(iot, ia->ia_iobase, MMS_NPORTS, 0, &ioh)) {
121		printf("%s: can't map i/o space\n", sc->sc_dev.dv_xname);
122		return;
123	}
124
125	/* Other initialization was done by mmsprobe. */
126	sc->sc_iot = iot;
127	sc->sc_ioh = ioh;
128	sc->sc_enabled = 0;
129
130	sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq, IST_PULSE,
131	    IPL_TTY, mmsintr, sc, sc->sc_dev.dv_xname);
132
133	a.accessops = &mms_accessops;
134	a.accesscookie = sc;
135
136	/*
137	 * Attach the wsmouse, saving a handle to it.
138	 * Note that we don't need to check this pointer against NULL
139	 * here or in psmintr, because if this fails lms_enable() will
140	 * never be called, so lmsintr() will never be called.
141	 */
142	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
143}
144
145int
146mms_enable(void *v)
147{
148	struct mms_softc *sc = v;
149
150	if (sc->sc_enabled)
151		return EBUSY;
152
153	sc->sc_enabled = 1;
154
155	/* Enable interrupts. */
156	bus_space_write_1(sc->sc_iot, sc->sc_ioh, MMS_ADDR, 0x07);
157	bus_space_write_1(sc->sc_iot, sc->sc_ioh, MMS_DATA, 0x09);
158
159	return 0;
160}
161
162void
163mms_disable(void *v)
164{
165	struct mms_softc *sc = v;
166
167	/* Disable interrupts. */
168	bus_space_write_1(sc->sc_iot, sc->sc_ioh, MMS_ADDR, 0x87);
169
170	sc->sc_enabled = 0;
171}
172
173int
174mms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
175{
176#if 0
177	struct mms_softc *sc = v;
178#endif
179
180	switch (cmd) {
181	case WSMOUSEIO_GTYPE:
182		*(u_int *)data = WSMOUSE_TYPE_MMS;
183		return (0);
184	}
185	return (-1);
186}
187
188int
189mmsintr(void *arg)
190{
191	struct mms_softc *sc = arg;
192	bus_space_tag_t iot = sc->sc_iot;
193	bus_space_handle_t ioh = sc->sc_ioh;
194	u_char status;
195	signed char dx, dy;
196	u_int buttons;
197	int changed;
198
199	if (!sc->sc_enabled)
200		/* Interrupts are not expected. */
201		return 0;
202
203	/* Freeze InPort registers (disabling interrupts). */
204	bus_space_write_1(iot, ioh, MMS_ADDR, 0x07);
205	bus_space_write_1(iot, ioh, MMS_DATA, 0x29);
206
207	bus_space_write_1(iot, ioh, MMS_ADDR, 0x00);
208	status = bus_space_read_1(iot, ioh, MMS_DATA);
209
210	if (status & 0x40) {
211		bus_space_write_1(iot, ioh, MMS_ADDR, 1);
212		dx = bus_space_read_1(iot, ioh, MMS_DATA);
213		/* Bounding at -127 avoids a bug in XFree86. */
214		dx = (dx == -128) ? -127 : dx;
215
216		bus_space_write_1(iot, ioh, MMS_ADDR, 2);
217		dy = bus_space_read_1(iot, ioh, MMS_DATA);
218		dy = (dy == -128) ? 127 : -dy;
219	} else
220		dx = dy = 0;
221
222	/* Unfreeze InPort registers (reenabling interrupts). */
223	bus_space_write_1(iot, ioh, MMS_ADDR, 0x07);
224	bus_space_write_1(iot, ioh, MMS_DATA, 0x09);
225
226	buttons = ((status & 0x04) ? 0x1 : 0) |
227		((status & 0x02) ? 0x2 : 0) |
228		((status & 0x01) ? 0x4 : 0);
229	changed = status & 0x38;
230
231	if (dx || dy || changed)
232		WSMOUSE_INPUT(sc->sc_wsmousedev, buttons, dx, dy, 0, 0);
233
234	return -1;
235}
236
237struct cfdriver mms_cd = {
238	NULL, "mms", DV_DULL
239};
240