xref: /openbsd/sys/dev/hil/hilms.c (revision 7b36286a)
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 }
339