1/*	$NetBSD: hilms.c,v 1.7 2023/05/11 09:35:57 martin 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	int error;
171
172	error = config_detach_children(self, flags);
173	if (error)
174		return error;
175
176	return 0;
177}
178
179int
180hilms_enable(void *v)
181{
182	struct hilms_softc *sc = v;
183
184	if (sc->sc_enabled)
185		return EBUSY;
186
187	sc->sc_enabled = 1;
188	sc->sc_buttonstate = 0;
189
190	return 0;
191}
192
193void
194hilms_disable(void *v)
195{
196	struct hilms_softc *sc = v;
197
198	sc->sc_enabled = 0;
199}
200
201int
202hilms_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
203{
204#if 0
205	struct hilms_softc *sc = v;
206#endif
207
208	switch (cmd) {
209	case WSMOUSEIO_GTYPE:
210		*(int *)data = WSMOUSE_TYPE_HIL;
211		return 0;
212	}
213
214	return EPASSTHROUGH;
215}
216
217void
218hilms_callback(struct hildev_softc *hdsc, u_int buflen, uint8_t *buf)
219{
220	struct hilms_softc *sc = device_private(hdsc->sc_dev);
221	int type, flags;
222	int dx, dy, dz, button;
223#ifdef DIAGNOSTIC
224	int minlen;
225#endif
226
227	/*
228	 * Ignore packet if we don't need it
229	 */
230	if (sc->sc_enabled == 0)
231		return;
232
233	type = *buf++;
234
235#ifdef DIAGNOSTIC
236	/*
237	 * Check that the packet contains all the expected data,
238	 * ignore it if too short.
239	 */
240	minlen = 1;
241	if (type & HIL_MOUSEMOTION) {
242		minlen += sc->sc_axes <<
243		    (sc->sc_features & HIL_16_BITS) ? 1 : 0;
244	}
245	if (type & HIL_MOUSEBUTTON)
246		minlen++;
247
248	if (minlen > buflen)
249		return;
250#endif
251
252	/*
253	 * The packet can contain both a mouse motion and a button event.
254	 * In this case, the motion data comes first.
255	 */
256
257	if (type & HIL_MOUSEMOTION) {
258		flags = sc->sc_features & HIL_ABSOLUTE ?
259		    WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y |
260		    WSMOUSE_INPUT_ABSOLUTE_Z : WSMOUSE_INPUT_DELTA;
261		if (sc->sc_features & HIL_16_BITS) {
262			dx = *buf++;
263			dx |= (*buf++) << 8;
264			if (!(sc->sc_features & HIL_ABSOLUTE))
265				dx = (int16_t)dx;
266		} else {
267			dx = *buf++;
268			if (!(sc->sc_features & HIL_ABSOLUTE))
269				dx = (int8_t)dx;
270		}
271		if (sc->sc_axes > 1) {
272			if (sc->sc_features & HIL_16_BITS) {
273				dy = *buf++;
274				dy |= (*buf++) << 8;
275				if (!(sc->sc_features & HIL_ABSOLUTE))
276					dy = (int16_t)dy;
277			} else {
278				dy = *buf++;
279				if (!(sc->sc_features & HIL_ABSOLUTE))
280					dy = (int8_t)dy;
281			}
282			if (sc->sc_axes > 2) {
283				if (sc->sc_features & HIL_16_BITS) {
284					dz = *buf++;
285					dz |= (*buf++) << 8;
286					if (!(sc->sc_features & HIL_ABSOLUTE))
287						dz = (int16_t)dz;
288				} else {
289					dz = *buf++;
290					if (!(sc->sc_features & HIL_ABSOLUTE))
291						dz = (int8_t)dz;
292				}
293			} else
294				dz = 0;
295		} else
296			dy = dz = 0;
297
298		/*
299		 * Correct Y direction for button boxes.
300		 */
301		if ((sc->sc_features & HIL_ABSOLUTE) == 0 &&
302		    sc->sc_buttons == 0)
303			dy = -dy;
304	} else
305		dx = dy = dz = flags = 0;
306
307	if (type & HIL_MOUSEBUTTON) {
308		button = *buf;
309		/*
310		 * The pressure sensor is very primitive and only has
311		 * a boolean behaviour, as an extra mouse button, which is
312		 * down if there is pressure or the pen is near the tablet,
313		 * and up if there is no pressure or the pen is far from the
314		 * tablet - at least for Tablet id 0x94, P/N 46088B
315		 *
316		 * The corresponding codes are 0x8f and 0x8e. Convert them
317		 * to a pseudo fourth button - even if the tablet never
318		 * has three buttons.
319		 */
320		button = (button - 0x80) >> 1;
321		if (button > 4)
322			button = 4;
323
324		if (*buf & 1) {
325			/* Button released, or no pressure */
326			sc->sc_buttonstate &= ~(1 << button);
327		} else {
328			/* Button pressed, or pressure */
329			sc->sc_buttonstate |= (1 << button);
330		}
331		/* buf++; */
332	}
333
334	if (sc->sc_wsmousedev != NULL)
335		wsmouse_input(sc->sc_wsmousedev,
336		    sc->sc_buttonstate, dx, dy, dz, 0, flags);
337}
338