hilms.c revision 1.6
1/*	$NetBSD: hilms.c,v 1.6 2023/05/10 00:09:47 riastradh Exp $	*/
2/*	$OpenBSD: hilms.c,v 1.5 2007/04/10 22:37:17 miod Exp $	*/
3/*
4 * Copyright (c) 2003, Miodrag Vallat.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 *
28 */
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/device.h>
33#include <sys/ioctl.h>
34#include <sys/bus.h>
35#include <sys/cpu.h>
36
37#include <machine/autoconf.h>
38
39#include <dev/hil/hilreg.h>
40#include <dev/hil/hilvar.h>
41#include <dev/hil/hildevs.h>
42
43#include <dev/wscons/wsconsio.h>
44#include <dev/wscons/wsmousevar.h>
45
46struct hilms_softc {
47	struct hildev_softc sc_hildev;
48
49	int		sc_features;
50	u_int		sc_buttons;
51	u_int		sc_axes;
52	int		sc_enabled;
53	int		sc_buttonstate;
54
55	device_t	sc_wsmousedev;
56};
57
58static int	hilmsprobe(device_t, cfdata_t, void *);
59static void	hilmsattach(device_t, device_t, void *);
60static int	hilmsdetach(device_t, int);
61
62CFATTACH_DECL_NEW(hilms, sizeof(struct hilms_softc),
63    hilmsprobe, hilmsattach, hilmsdetach, NULL);
64
65static int	hilms_enable(void *);
66static int	hilms_ioctl(void *, u_long, void *, int, struct lwp *);
67static void	hilms_disable(void *);
68
69static const struct wsmouse_accessops hilms_accessops = {
70	.enable  = hilms_enable,
71	.ioctl   = hilms_ioctl,
72	.disable = hilms_disable,
73};
74
75static void	hilms_callback(struct hildev_softc *, u_int, uint8_t *);
76
77int
78hilmsprobe(device_t parent, cfdata_t cf, void *aux)
79{
80	struct hil_attach_args *ha = aux;
81
82	if (ha->ha_type != HIL_DEVICE_MOUSE)
83		return 0;
84
85	/*
86	 * Reject anything that has only buttons - they are handled as
87	 * keyboards, really.
88	 */
89	if (ha->ha_infolen > 1 && (ha->ha_info[1] & HIL_AXMASK) == 0)
90		return 0;
91
92	return 1;
93}
94
95void
96hilmsattach(device_t parent, device_t self, void *aux)
97{
98	struct hilms_softc *sc = device_private(self);
99	struct hil_attach_args *ha = aux;
100	struct wsmousedev_attach_args a;
101	int iob, rx, ry;
102
103	sc->sc_hildev.sc_dev = self;
104	sc->hd_code = ha->ha_code;
105	sc->hd_type = ha->ha_type;
106	sc->hd_infolen = ha->ha_infolen;
107	memcpy(sc->hd_info, ha->ha_info, ha->ha_infolen);
108	sc->hd_fn = hilms_callback;
109
110	/*
111	 * Interpret the identification bytes, if any
112	 */
113	rx = ry = 0;
114	if (ha->ha_infolen > 1) {
115		sc->sc_features = ha->ha_info[1];
116		sc->sc_axes = sc->sc_features & HIL_AXMASK;
117
118		if (sc->sc_features & HIL_IOB) {
119			/* skip resolution bytes */
120			iob = 4;
121			if (sc->sc_features & HIL_ABSOLUTE) {
122				/* skip ranges */
123				rx = ha->ha_info[4] | (ha->ha_info[5] << 8);
124				if (sc->sc_axes > 1)
125					ry = ha->ha_info[6] |
126					    (ha->ha_info[7] << 8);
127				iob += 2 * sc->sc_axes;
128			}
129
130			if (iob >= ha->ha_infolen) {
131				sc->sc_features &= ~(HIL_IOB | HILIOB_PIO);
132			} else {
133				iob = ha->ha_info[iob];
134				sc->sc_buttons = iob & HILIOB_BMASK;
135				sc->sc_features |= (iob & HILIOB_PIO);
136			}
137		}
138	}
139
140	aprint_normal(", %d axes", sc->sc_axes);
141	if (sc->sc_buttons == 1)
142		aprint_normal(", 1 button");
143	else if (sc->sc_buttons > 1)
144		aprint_normal(", %d buttons", sc->sc_buttons);
145	if (sc->sc_features & HILIOB_PIO)
146		aprint_normal(", pressure sensor");
147	if (sc->sc_features & HIL_ABSOLUTE) {
148		aprint_normal("\n");
149		aprint_normal_dev(self, "%d", rx);
150		if (ry != 0)
151			aprint_normal("x%d", ry);
152		else
153			aprint_normal(" linear");
154		aprint_normal(" fixed area");
155	}
156
157	aprint_normal("\n");
158
159	sc->sc_enabled = 0;
160
161	a.accessops = &hilms_accessops;
162	a.accesscookie = sc;
163
164	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint, CFARGS_NONE);
165}
166
167int
168hilmsdetach(device_t self, int flags)
169{
170	struct hilms_softc *sc = device_private(self);
171	int error;
172
173	error = config_detach_children(self, flags);
174	if (error)
175		return error;
176
177	return 0;
178}
179
180int
181hilms_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
194void
195hilms_disable(void *v)
196{
197	struct hilms_softc *sc = v;
198
199	sc->sc_enabled = 0;
200}
201
202int
203hilms_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
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 EPASSTHROUGH;
216}
217
218void
219hilms_callback(struct hildev_softc *hdsc, u_int buflen, uint8_t *buf)
220{
221	struct hilms_softc *sc = device_private(hdsc->sc_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