xref: /netbsd-src/sys/dev/usb/uts.c (revision 4e8e66439e246961c4367c78584be80e23f52897)
1 /*	$NetBSD: uts.c,v 1.4 2016/04/23 10:15:32 skrll Exp $	*/
2 
3 /*
4  * Copyright (c) 2012 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Pierre Pronchery (khorben@defora.org).
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  *  USB generic Touch Screen driver.
34  */
35 
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: uts.c,v 1.4 2016/04/23 10:15:32 skrll Exp $");
38 
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/device.h>
43 #include <sys/ioctl.h>
44 #include <sys/vnode.h>
45 
46 #include <dev/usb/usb.h>
47 #include <dev/usb/usbhid.h>
48 
49 #include <dev/usb/usbdi.h>
50 #include <dev/usb/usbdi_util.h>
51 #include <dev/usb/usbdevs.h>
52 #include <dev/usb/usb_quirks.h>
53 #include <dev/usb/uhidev.h>
54 #include <dev/usb/hid.h>
55 
56 #include <dev/wscons/wsconsio.h>
57 #include <dev/wscons/wsmousevar.h>
58 #include <dev/wscons/tpcalibvar.h>
59 
60 #ifdef UTS_DEBUG
61 #define DPRINTF(x)	if (utsdebug) printf x
62 #define DPRINTFN(n,x)	if (utsdebug>(n)) printf x
63 int	utsdebug = 0;
64 #else
65 #define DPRINTF(x)
66 #define DPRINTFN(n,x)
67 #endif
68 
69 
70 struct uts_softc {
71 	struct uhidev sc_hdev;
72 
73 	struct hid_location sc_loc_x, sc_loc_y, sc_loc_z;
74 	struct hid_location sc_loc_btn;
75 
76 	int sc_enabled;
77 
78 	int flags;		/* device configuration */
79 #define UTS_ABS		0x1	/* absolute position */
80 
81 	uint32_t		sc_buttons;	/* touchscreen button status */
82 	device_t		sc_wsmousedev;
83 	struct tpcalib_softc	sc_tpcalib;	/* calibration */
84 	struct wsmouse_calibcoords sc_calibcoords;
85 
86 	char sc_dying;
87 };
88 
89 #define TSCREEN_FLAGS_MASK (HIO_CONST|HIO_RELATIVE)
90 
91 Static void	uts_intr(struct uhidev *, void *, u_int);
92 
93 Static int	uts_enable(void *);
94 Static void	uts_disable(void *);
95 Static int	uts_ioctl(void *, u_long, void *, int, struct lwp *);
96 
97 Static const struct wsmouse_accessops uts_accessops = {
98 	uts_enable,
99 	uts_ioctl,
100 	uts_disable,
101 };
102 
103 Static int	uts_match(device_t, cfdata_t, void *);
104 Static void	uts_attach(device_t, device_t, void *);
105 Static void	uts_childdet(device_t, device_t);
106 Static int	uts_detach(device_t, int);
107 Static int	uts_activate(device_t, enum devact);
108 
109 extern struct cfdriver uts_cd;
110 
111 CFATTACH_DECL2_NEW(uts, sizeof(struct uts_softc), uts_match, uts_attach,
112     uts_detach, uts_activate, NULL, uts_childdet);
113 
114 Static int
115 uts_match(device_t parent, cfdata_t match, void *aux)
116 {
117 	struct uhidev_attach_arg *uha = aux;
118 	int size;
119 	void *desc;
120 
121 	uhidev_get_report_desc(uha->parent, &desc, &size);
122 	if (!hid_is_collection(desc, size, uha->reportid,
123 	    HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCH_SCREEN)) &&
124 	    !hid_is_collection(desc, size, uha->reportid,
125 	    HID_USAGE2(HUP_DIGITIZERS, HUD_FINGER)))
126 		return UMATCH_NONE;
127 
128 	return UMATCH_IFACECLASS;
129 }
130 
131 Static void
132 uts_attach(device_t parent, device_t self, void *aux)
133 {
134 	struct uts_softc *sc = device_private(self);
135 	struct uhidev_attach_arg *uha = aux;
136 	struct wsmousedev_attach_args a;
137 	int size;
138 	void *desc;
139 	uint32_t flags;
140 	struct hid_data * d;
141 	struct hid_item item;
142 
143 	aprint_naive("\n");
144 
145 	sc->sc_hdev.sc_dev = self;
146 	sc->sc_hdev.sc_intr = uts_intr;
147 	sc->sc_hdev.sc_parent = uha->parent;
148 	sc->sc_hdev.sc_report_id = uha->reportid;
149 
150 	uhidev_get_report_desc(uha->parent, &desc, &size);
151 
152 	if (!pmf_device_register(self, NULL, NULL))
153 		aprint_error_dev(self, "couldn't establish power handler\n");
154 
155 	/* requires HID usage Generic_Desktop:X */
156 	if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
157 		uha->reportid, hid_input, &sc->sc_loc_x, &flags)) {
158 		aprint_error_dev(sc->sc_hdev.sc_dev,
159 		    "touchscreen has no X report\n");
160 		return;
161 	}
162 	switch (flags & TSCREEN_FLAGS_MASK) {
163 	case 0:
164 		sc->flags |= UTS_ABS;
165 		break;
166 	case HIO_RELATIVE:
167 		break;
168 	default:
169 		aprint_error_dev(sc->sc_hdev.sc_dev,
170 		    "X report 0x%04x not supported\n", flags);
171 		return;
172 	}
173 
174 	/* requires HID usage Generic_Desktop:Y */
175 	if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
176 		uha->reportid, hid_input, &sc->sc_loc_y, &flags)) {
177 		aprint_error_dev(sc->sc_hdev.sc_dev,
178 		    "touchscreen has no Y report\n");
179 		return;
180 	}
181 	switch (flags & TSCREEN_FLAGS_MASK) {
182 	case 0:
183 		sc->flags |= UTS_ABS;
184 		break;
185 	case HIO_RELATIVE:
186 		break;
187 	default:
188 		aprint_error_dev(sc->sc_hdev.sc_dev,
189 		    "Y report 0x%04x not supported\n", flags);
190 		return;
191 	}
192 
193 	/* requires HID usage Digitizer:Tip_Switch */
194 	if (!hid_locate(desc, size, HID_USAGE2(HUP_DIGITIZERS, HUD_TIP_SWITCH),
195 	    uha->reportid, hid_input, &sc->sc_loc_btn, 0)) {
196 		aprint_error_dev(sc->sc_hdev.sc_dev,
197 		    "touchscreen has no tip switch report\n");
198 		return;
199 	}
200 
201 	/* requires HID usage Digitizer:In_Range */
202 	if (!hid_locate(desc, size, HID_USAGE2(HUP_DIGITIZERS, HUD_IN_RANGE),
203 		uha->reportid, hid_input, &sc->sc_loc_z, &flags)) {
204 		aprint_error_dev(sc->sc_hdev.sc_dev,
205 		    "touchscreen has no range report\n");
206 		return;
207 	}
208 
209 	/* multi-touch support would need HUD_CONTACTID and HUD_CONTACTMAX */
210 
211 #ifdef UTS_DEBUG
212 	DPRINTF(("uts_attach: sc=%p\n", sc));
213 	DPRINTF(("uts_attach: X\t%d/%d\n",
214 		sc->sc_loc_x.pos, sc->sc_loc_x.size));
215 	DPRINTF(("uts_attach: Y\t%d/%d\n",
216 		sc->sc_loc_y.pos, sc->sc_loc_y.size));
217 	DPRINTF(("uts_attach: Z\t%d/%d\n",
218 		sc->sc_loc_z.pos, sc->sc_loc_z.size));
219 #endif
220 
221 	a.accessops = &uts_accessops;
222 	a.accesscookie = sc;
223 
224 	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
225 
226 	/* calibrate the touchscreen */
227 	memset(&sc->sc_calibcoords, 0, sizeof(sc->sc_calibcoords));
228 	sc->sc_calibcoords.maxx = 4095;
229 	sc->sc_calibcoords.maxy = 4095;
230 	sc->sc_calibcoords.samplelen = WSMOUSE_CALIBCOORDS_RESET;
231 	d = hid_start_parse(desc, size, hid_input);
232 	while (hid_get_item(d, &item)) {
233 		if (item.kind != hid_input
234 		    || HID_GET_USAGE_PAGE(item.usage) != HUP_GENERIC_DESKTOP
235 		    || item.report_ID != sc->sc_hdev.sc_report_id)
236 			continue;
237 		if (HID_GET_USAGE(item.usage) == HUG_X) {
238 			sc->sc_calibcoords.minx = item.logical_minimum;
239 			sc->sc_calibcoords.maxx = item.logical_maximum;
240 		}
241 		if (HID_GET_USAGE(item.usage) == HUG_Y) {
242 			sc->sc_calibcoords.miny = item.logical_minimum;
243 			sc->sc_calibcoords.maxy = item.logical_maximum;
244 		}
245 	}
246 	hid_end_parse(d);
247 
248 	tpcalib_init(&sc->sc_tpcalib);
249 	tpcalib_ioctl(&sc->sc_tpcalib, WSMOUSEIO_SCALIBCOORDS,
250 	    (void *)&sc->sc_calibcoords, 0, 0);
251 
252 	return;
253 }
254 
255 Static int
256 uts_detach(device_t self, int flags)
257 {
258 	struct uts_softc *sc = device_private(self);
259 	int rv = 0;
260 
261 	DPRINTF(("uts_detach: sc=%p flags=%d\n", sc, flags));
262 
263 	if (sc->sc_wsmousedev != NULL)
264 		rv = config_detach(sc->sc_wsmousedev, flags);
265 
266 	pmf_device_deregister(self);
267 
268 	return rv;
269 }
270 
271 Static void
272 uts_childdet(device_t self, device_t child)
273 {
274 	struct uts_softc *sc = device_private(self);
275 
276 	KASSERT(sc->sc_wsmousedev == child);
277 	sc->sc_wsmousedev = NULL;
278 }
279 
280 Static int
281 uts_activate(device_t self, enum devact act)
282 {
283 	struct uts_softc *sc = device_private(self);
284 
285 	switch (act) {
286 	case DVACT_DEACTIVATE:
287 		sc->sc_dying = 1;
288 		return 0;
289 	default:
290 		return EOPNOTSUPP;
291 	}
292 }
293 
294 Static int
295 uts_enable(void *v)
296 {
297 	struct uts_softc *sc = v;
298 
299 	DPRINTFN(1,("uts_enable: sc=%p\n", sc));
300 
301 	if (sc->sc_dying)
302 		return EIO;
303 
304 	if (sc->sc_enabled)
305 		return EBUSY;
306 
307 	sc->sc_enabled = 1;
308 	sc->sc_buttons = 0;
309 
310 	return uhidev_open(&sc->sc_hdev);
311 }
312 
313 Static void
314 uts_disable(void *v)
315 {
316 	struct uts_softc *sc = v;
317 
318 	DPRINTFN(1,("uts_disable: sc=%p\n", sc));
319 #ifdef DIAGNOSTIC
320 	if (!sc->sc_enabled) {
321 		printf("uts_disable: not enabled\n");
322 		return;
323 	}
324 #endif
325 
326 	sc->sc_enabled = 0;
327 	uhidev_close(&sc->sc_hdev);
328 }
329 
330 Static int
331 uts_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
332 {
333 	struct uts_softc *sc = v;
334 
335 	switch (cmd) {
336 	case WSMOUSEIO_GTYPE:
337 		if (sc->flags & UTS_ABS)
338 			*(u_int *)data = WSMOUSE_TYPE_TPANEL;
339 		else
340 			*(u_int *)data = WSMOUSE_TYPE_USB;
341 		return 0;
342 	case WSMOUSEIO_SCALIBCOORDS:
343 	case WSMOUSEIO_GCALIBCOORDS:
344 		return tpcalib_ioctl(&sc->sc_tpcalib, cmd, data, flag, l);
345 	}
346 
347 	return EPASSTHROUGH;
348 }
349 
350 Static void
351 uts_intr(struct uhidev *addr, void *ibuf, u_int len)
352 {
353 	struct uts_softc *sc = (struct uts_softc *)addr;
354 	int dx, dy, dz;
355 	uint32_t buttons = 0;
356 	int flags, s;
357 
358 	DPRINTFN(5,("uts_intr: len=%d\n", len));
359 
360 	flags = WSMOUSE_INPUT_DELTA | WSMOUSE_INPUT_ABSOLUTE_Z;
361 
362 	dx = hid_get_data(ibuf, &sc->sc_loc_x);
363 	if (sc->flags & UTS_ABS) {
364 		flags |= (WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y);
365 		dy = hid_get_data(ibuf, &sc->sc_loc_y);
366 		tpcalib_trans(&sc->sc_tpcalib, dx, dy, &dx, &dy);
367 	} else
368 		dy = -hid_get_data(ibuf, &sc->sc_loc_y);
369 
370 	dz = hid_get_data(ibuf, &sc->sc_loc_z);
371 
372 	if (hid_get_data(ibuf, &sc->sc_loc_btn))
373 		buttons |= 1;
374 
375 	if (dx != 0 || dy != 0 || dz != 0 || buttons != sc->sc_buttons) {
376 		DPRINTFN(10,("uts_intr: x:%d y:%d z:%d buttons:0x%x\n",
377 		    dx, dy, dz, buttons));
378 		sc->sc_buttons = buttons;
379 		if (sc->sc_wsmousedev != NULL) {
380 			s = spltty();
381 			wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, dz, 0,
382 			    flags);
383 			splx(s);
384 		}
385 	}
386 }
387