1 /* $OpenBSD: hilms.c,v 1.5 2007/04/10 22:37:17 miod Exp $ */
2 /*
3 * Copyright (c) 2003, Miodrag Vallat.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 *
27 */
28
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/device.h>
32 #include <sys/ioctl.h>
33
34 #include <machine/autoconf.h>
35 #include <machine/bus.h>
36 #include <machine/cpu.h>
37
38 #include <dev/hil/hilreg.h>
39 #include <dev/hil/hilvar.h>
40 #include <dev/hil/hildevs.h>
41
42 #include <dev/wscons/wsconsio.h>
43 #include <dev/wscons/wsmousevar.h>
44
45 struct hilms_softc {
46 struct hildev_softc sc_hildev;
47
48 int sc_features;
49 u_int sc_buttons;
50 u_int sc_axes;
51 int sc_enabled;
52 int sc_buttonstate;
53
54 struct device *sc_wsmousedev;
55 };
56
57 int hilmsprobe(struct device *, void *, void *);
58 void hilmsattach(struct device *, struct device *, void *);
59 int hilmsdetach(struct device *, int);
60
61 struct cfdriver hilms_cd = {
62 NULL, "hilms", DV_DULL
63 };
64
65 struct cfattach hilms_ca = {
66 sizeof(struct hilms_softc), hilmsprobe, hilmsattach, hilmsdetach,
67 };
68
69 int hilms_enable(void *);
70 int hilms_ioctl(void *, u_long, caddr_t, int, struct proc *);
71 void hilms_disable(void *);
72
73 const struct wsmouse_accessops hilms_accessops = {
74 hilms_enable,
75 hilms_ioctl,
76 hilms_disable,
77 };
78
79 void hilms_callback(struct hildev_softc *, u_int, u_int8_t *);
80
81 int
82 hilmsprobe(struct device *parent, void *match, void *aux)
83 {
84 struct hil_attach_args *ha = aux;
85
86 if (ha->ha_type != HIL_DEVICE_MOUSE)
87 return (0);
88
89 /*
90 * Reject anything that has only buttons - they are handled as
91 * keyboards, really.
92 */
93 if (ha->ha_infolen > 1 && (ha->ha_info[1] & HIL_AXMASK) == 0)
94 return (0);
95
96 return (1);
97 }
98
99 void
100 hilmsattach(struct device *parent, struct device *self, void *aux)
101 {
102 struct hilms_softc *sc = (void *)self;
103 struct hil_attach_args *ha = aux;
104 struct wsmousedev_attach_args a;
105 int iob, rx, ry;
106
107 sc->hd_code = ha->ha_code;
108 sc->hd_type = ha->ha_type;
109 sc->hd_infolen = ha->ha_infolen;
110 bcopy(ha->ha_info, sc->hd_info, ha->ha_infolen);
111 sc->hd_fn = hilms_callback;
112
113 /*
114 * Interpret the identification bytes, if any
115 */
116 rx = ry = 0;
117 if (ha->ha_infolen > 1) {
118 sc->sc_features = ha->ha_info[1];
119 sc->sc_axes = sc->sc_features & HIL_AXMASK;
120
121 if (sc->sc_features & HIL_IOB) {
122 /* skip resolution bytes */
123 iob = 4;
124 if (sc->sc_features & HIL_ABSOLUTE) {
125 /* skip ranges */
126 rx = ha->ha_info[4] | (ha->ha_info[5] << 8);
127 if (sc->sc_axes > 1)
128 ry = ha->ha_info[6] |
129 (ha->ha_info[7] << 8);
130 iob += 2 * sc->sc_axes;
131 }
132
133 if (iob >= ha->ha_infolen) {
134 sc->sc_features &= ~(HIL_IOB | HILIOB_PIO);
135 } else {
136 iob = ha->ha_info[iob];
137 sc->sc_buttons = iob & HILIOB_BMASK;
138 sc->sc_features |= (iob & HILIOB_PIO);
139 }
140 }
141 }
142
143 printf(", %d axes", sc->sc_axes);
144 if (sc->sc_buttons == 1)
145 printf(", 1 button");
146 else if (sc->sc_buttons > 1)
147 printf(", %d buttons", sc->sc_buttons);
148 if (sc->sc_features & HILIOB_PIO)
149 printf(", pressure sensor");
150 if (sc->sc_features & HIL_ABSOLUTE) {
151 printf ("\n%s: %d", self->dv_xname, rx);
152 if (ry != 0)
153 printf("x%d", ry);
154 else
155 printf(" linear");
156 printf(" fixed area");
157 }
158
159 printf("\n");
160
161 sc->sc_enabled = 0;
162
163 a.accessops = &hilms_accessops;
164 a.accesscookie = sc;
165
166 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
167 }
168
169 int
170 hilmsdetach(struct device *self, int flags)
171 {
172 struct hilms_softc *sc = (void *)self;
173
174 if (sc->sc_wsmousedev != NULL)
175 return config_detach(sc->sc_wsmousedev, flags);
176
177 return (0);
178 }
179
180 int
181 hilms_enable(void *v)
182 {
183 struct hilms_softc *sc = v;
184
185 if (sc->sc_enabled)
186 return EBUSY;
187
188 sc->sc_enabled = 1;
189 sc->sc_buttonstate = 0;
190
191 return (0);
192 }
193
194 void
195 hilms_disable(void *v)
196 {
197 struct hilms_softc *sc = v;
198
199 sc->sc_enabled = 0;
200 }
201
202 int
203 hilms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
204 {
205 #if 0
206 struct hilms_softc *sc = v;
207 #endif
208
209 switch (cmd) {
210 case WSMOUSEIO_GTYPE:
211 *(int *)data = WSMOUSE_TYPE_HIL;
212 return 0;
213 }
214
215 return -1;
216 }
217
218 void
219 hilms_callback(struct hildev_softc *dev, u_int buflen, u_int8_t *buf)
220 {
221 struct hilms_softc *sc = (struct hilms_softc *)dev;
222 int type, flags;
223 int dx, dy, dz, button;
224 #ifdef DIAGNOSTIC
225 int minlen;
226 #endif
227
228 /*
229 * Ignore packet if we don't need it
230 */
231 if (sc->sc_enabled == 0)
232 return;
233
234 type = *buf++;
235
236 #ifdef DIAGNOSTIC
237 /*
238 * Check that the packet contains all the expected data,
239 * ignore it if too short.
240 */
241 minlen = 1;
242 if (type & HIL_MOUSEMOTION) {
243 minlen += sc->sc_axes <<
244 (sc->sc_features & HIL_16_BITS) ? 1 : 0;
245 }
246 if (type & HIL_MOUSEBUTTON)
247 minlen++;
248
249 if (minlen > buflen)
250 return;
251 #endif
252
253 /*
254 * The packet can contain both a mouse motion and a button event.
255 * In this case, the motion data comes first.
256 */
257
258 if (type & HIL_MOUSEMOTION) {
259 flags = sc->sc_features & HIL_ABSOLUTE ?
260 WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y |
261 WSMOUSE_INPUT_ABSOLUTE_Z : WSMOUSE_INPUT_DELTA;
262 if (sc->sc_features & HIL_16_BITS) {
263 dx = *buf++;
264 dx |= (*buf++) << 8;
265 if (!(sc->sc_features & HIL_ABSOLUTE))
266 dx = (int16_t)dx;
267 } else {
268 dx = *buf++;
269 if (!(sc->sc_features & HIL_ABSOLUTE))
270 dx = (int8_t)dx;
271 }
272 if (sc->sc_axes > 1) {
273 if (sc->sc_features & HIL_16_BITS) {
274 dy = *buf++;
275 dy |= (*buf++) << 8;
276 if (!(sc->sc_features & HIL_ABSOLUTE))
277 dy = (int16_t)dy;
278 } else {
279 dy = *buf++;
280 if (!(sc->sc_features & HIL_ABSOLUTE))
281 dy = (int8_t)dy;
282 }
283 if (sc->sc_axes > 2) {
284 if (sc->sc_features & HIL_16_BITS) {
285 dz = *buf++;
286 dz |= (*buf++) << 8;
287 if (!(sc->sc_features & HIL_ABSOLUTE))
288 dz = (int16_t)dz;
289 } else {
290 dz = *buf++;
291 if (!(sc->sc_features & HIL_ABSOLUTE))
292 dz = (int8_t)dz;
293 }
294 } else
295 dz = 0;
296 } else
297 dy = dz = 0;
298
299 /*
300 * Correct Y direction for button boxes.
301 */
302 if ((sc->sc_features & HIL_ABSOLUTE) == 0 &&
303 sc->sc_buttons == 0)
304 dy = -dy;
305 } else
306 dx = dy = dz = flags = 0;
307
308 if (type & HIL_MOUSEBUTTON) {
309 button = *buf;
310 /*
311 * The pressure sensor is very primitive and only has
312 * a boolean behaviour, as an extra mouse button, which is
313 * down if there is pressure or the pen is near the tablet,
314 * and up if there is no pressure or the pen is far from the
315 * tablet - at least for Tablet id 0x94, P/N 46088B
316 *
317 * The corresponding codes are 0x8f and 0x8e. Convert them
318 * to a pseudo fourth button - even if the tablet never
319 * has three buttons.
320 */
321 button = (button - 0x80) >> 1;
322 if (button > 4)
323 button = 4;
324
325 if (*buf & 1) {
326 /* Button released, or no pressure */
327 sc->sc_buttonstate &= ~(1 << button);
328 } else {
329 /* Button pressed, or pressure */
330 sc->sc_buttonstate |= (1 << button);
331 }
332 /* buf++; */
333 }
334
335 if (sc->sc_wsmousedev != NULL)
336 wsmouse_input(sc->sc_wsmousedev,
337 sc->sc_buttonstate, dx, dy, dz, 0, flags);
338 }