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
45 struct 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
57 int mmsprobe(struct device *, void *, void *);
58 void mmsattach(struct device *, struct device *, void *);
59 int mmsintr(void *);
60
61 const struct cfattach mms_ca = {
62 sizeof(struct mms_softc), mmsprobe, mmsattach
63 };
64
65 int mms_enable(void *);
66 int mms_ioctl(void *, u_long, caddr_t, int, struct proc *);
67 void mms_disable(void *);
68
69 const struct wsmouse_accessops mms_accessops = {
70 mms_enable,
71 mms_ioctl,
72 mms_disable,
73 };
74
75 int
mmsprobe(struct device * parent,void * match,void * aux)76 mmsprobe(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
104 out:
105 bus_space_unmap(iot, ioh, MMS_NPORTS);
106 return rv;
107 }
108
109 void
mmsattach(struct device * parent,struct device * self,void * aux)110 mmsattach(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
145 int
mms_enable(void * v)146 mms_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
162 void
mms_disable(void * v)163 mms_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
173 int
mms_ioctl(void * v,u_long cmd,caddr_t data,int flag,struct proc * p)174 mms_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
188 int
mmsintr(void * arg)189 mmsintr(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
237 struct cfdriver mms_cd = {
238 NULL, "mms", DV_DULL
239 };
240