1 /* $NetBSD: uep.c,v 1.25 2021/08/07 16:19:17 thorpej Exp $ */
2
3 /*
4 * Copyright (c) 2004 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Tyler C. Sarna (tsarna@netbsd.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 * eGalax USB touchpanel controller driver.
34 */
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: uep.c,v 1.25 2021/08/07 16:19:17 thorpej Exp $");
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/kmem.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/usbdi.h>
48 #include <dev/usb/usbdi_util.h>
49 #include <dev/usb/usbdevs.h>
50 #include <dev/usb/usb_quirks.h>
51
52 #include <dev/wscons/wsconsio.h>
53 #include <dev/wscons/wsmousevar.h>
54 #include <dev/wscons/tpcalibvar.h>
55
56 #define UIDSTR "eGalax USB SN000000"
57 /* calibration - integer values, perhaps sysctls? */
58 #define X_RATIO 293
59 #define X_OFFSET -28
60 #define Y_RATIO -348
61 #define Y_OFFSET 537
62 /* an X_RATIO of ``312'' means : reduce by a factor 3.12 x axis amplitude */
63 /* an Y_RATIO of ``-157'' means : reduce by a factor 1.57 y axis amplitude,
64 * and reverse y motion */
65
66 struct uep_softc {
67 device_t sc_dev;
68 struct usbd_device *sc_udev; /* device */
69 struct usbd_interface *sc_iface; /* interface */
70 int sc_iface_number;
71
72 int sc_intr_number; /* interrupt number */
73 struct usbd_pipe * sc_intr_pipe; /* interrupt pipe */
74 u_char *sc_ibuf;
75 int sc_isize;
76
77 device_t sc_wsmousedev; /* wsmouse device */
78 struct tpcalib_softc sc_tpcalib; /* calibration */
79
80 u_char sc_enabled;
81 u_char sc_dying;
82 };
83
84 static struct wsmouse_calibcoords default_calib = {
85 .minx = 0,
86 .miny = 0,
87 .maxx = 2047,
88 .maxy = 2047,
89 .samplelen = WSMOUSE_CALIBCOORDS_RESET,
90 };
91
92 Static void uep_intr(struct usbd_xfer *, void *, usbd_status);
93
94 Static int uep_enable(void *);
95 Static void uep_disable(void *);
96 Static int uep_ioctl(void *, u_long, void *, int, struct lwp *);
97
98 static const struct wsmouse_accessops uep_accessops = {
99 uep_enable,
100 uep_ioctl,
101 uep_disable,
102 };
103
104 static int uep_match(device_t, cfdata_t, void *);
105 static void uep_attach(device_t, device_t, void *);
106 static void uep_childdet(device_t, device_t);
107 static int uep_detach(device_t, int);
108 static int uep_activate(device_t, enum devact);
109
110 CFATTACH_DECL2_NEW(uep, sizeof(struct uep_softc), uep_match, uep_attach,
111 uep_detach, uep_activate, NULL, uep_childdet);
112
113 static int
uep_match(device_t parent,cfdata_t match,void * aux)114 uep_match(device_t parent, cfdata_t match, void *aux)
115 {
116 struct usb_attach_arg *uaa = aux;
117
118 if ((uaa->uaa_vendor == USB_VENDOR_EGALAX) && (
119 (uaa->uaa_product == USB_PRODUCT_EGALAX_TPANEL)
120 || (uaa->uaa_product == USB_PRODUCT_EGALAX_TPANEL2)))
121 return UMATCH_VENDOR_PRODUCT;
122
123 if ((uaa->uaa_vendor == USB_VENDOR_EGALAX2)
124 && (uaa->uaa_product == USB_PRODUCT_EGALAX2_TPANEL))
125 return UMATCH_VENDOR_PRODUCT;
126
127
128 return UMATCH_NONE;
129 }
130
131 static void
uep_attach(device_t parent,device_t self,void * aux)132 uep_attach(device_t parent, device_t self, void *aux)
133 {
134 struct uep_softc *sc = device_private(self);
135 struct usb_attach_arg *uaa = aux;
136 struct usbd_device *dev = uaa->uaa_device;
137 usb_config_descriptor_t *cdesc;
138 usb_interface_descriptor_t *id;
139 usb_endpoint_descriptor_t *ed;
140 usb_device_request_t req;
141 uByte act;
142 struct wsmousedev_attach_args a;
143 char *devinfop;
144 usbd_status err;
145 int i;
146
147 sc->sc_dev = self;
148
149 aprint_naive("\n");
150 aprint_normal("\n");
151
152 devinfop = usbd_devinfo_alloc(dev, 0);
153 aprint_normal_dev(self, "%s\n", devinfop);
154 usbd_devinfo_free(devinfop);
155 sc->sc_udev = dev;
156 sc->sc_intr_number = -1;
157 sc->sc_intr_pipe = NULL;
158 sc->sc_enabled = sc->sc_isize = 0;
159
160 /* Move the device into the configured state. */
161 err = usbd_set_config_index(dev, 0, 1);
162 if (err) {
163 aprint_error("\n%s: failed to set configuration, err=%s\n",
164 device_xname(sc->sc_dev), usbd_errstr(err));
165 sc->sc_dying = 1;
166 return;
167 }
168
169 /* get the config descriptor */
170 cdesc = usbd_get_config_descriptor(sc->sc_udev);
171 if (cdesc == NULL) {
172 aprint_error_dev(self,
173 "failed to get configuration descriptor\n");
174 sc->sc_dying = 1;
175 return;
176 }
177
178 /* get the interface */
179 err = usbd_device2interface_handle(dev, 0, &sc->sc_iface);
180 if (err) {
181 aprint_error("\n%s: failed to get interface, err=%s\n",
182 device_xname(sc->sc_dev), usbd_errstr(err));
183 sc->sc_dying = 1;
184 return;
185 }
186
187 /* Find the interrupt endpoint */
188 id = usbd_get_interface_descriptor(sc->sc_iface);
189 sc->sc_iface_number = id->bInterfaceNumber;
190
191 for (i = 0; i < id->bNumEndpoints; i++) {
192 ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
193 if (ed == NULL) {
194 aprint_error_dev(self,
195 "no endpoint descriptor for %d\n", i);
196 sc->sc_dying = 1;
197 return;
198 }
199
200 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
201 UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
202 sc->sc_intr_number = ed->bEndpointAddress;
203 sc->sc_isize = UGETW(ed->wMaxPacketSize);
204 }
205 }
206
207 if (sc->sc_intr_number== -1) {
208 aprint_error_dev(self, "Could not find interrupt in\n");
209 sc->sc_dying = 1;
210 return;
211 }
212
213 /* Newer controllers need an activation command */
214 req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
215 req.bRequest = 0x0a;
216 USETW(req.wValue, 'A');
217 USETW(req.wIndex, 0);
218 USETW(req.wLength, 1);
219 usbd_do_request(dev, &req, &act);
220
221 usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev);
222
223 a.accessops = &uep_accessops;
224 a.accesscookie = sc;
225
226 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint, CFARGS_NONE);
227
228 tpcalib_init(&sc->sc_tpcalib);
229 tpcalib_ioctl(&sc->sc_tpcalib, WSMOUSEIO_SCALIBCOORDS,
230 (void *)&default_calib, 0, 0);
231
232 return;
233 }
234
235 static int
uep_detach(device_t self,int flags)236 uep_detach(device_t self, int flags)
237 {
238 struct uep_softc *sc = device_private(self);
239 int rv = 0;
240
241 if (sc->sc_intr_pipe != NULL) {
242 usbd_abort_pipe(sc->sc_intr_pipe);
243 usbd_close_pipe(sc->sc_intr_pipe);
244 sc->sc_intr_pipe = NULL;
245 }
246 sc->sc_dying = 1;
247
248 /* save current calib as defaults */
249 default_calib = sc->sc_tpcalib.sc_saved;
250
251 if (sc->sc_wsmousedev != NULL)
252 rv = config_detach(sc->sc_wsmousedev, flags);
253
254 usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev);
255 return rv;
256 }
257
258 static void
uep_childdet(device_t self,device_t child)259 uep_childdet(device_t self, device_t child)
260 {
261 struct uep_softc *sc = device_private(self);
262
263 KASSERT(sc->sc_wsmousedev == child);
264 sc->sc_wsmousedev = NULL;
265 }
266
267 static int
uep_activate(device_t self,enum devact act)268 uep_activate(device_t self, enum devact act)
269 {
270 struct uep_softc *sc = device_private(self);
271
272 switch (act) {
273 case DVACT_DEACTIVATE:
274 sc->sc_dying = 1;
275 return 0;
276 default:
277 return EOPNOTSUPP;
278 }
279 }
280
281 Static int
uep_enable(void * v)282 uep_enable(void *v)
283 {
284 struct uep_softc *sc = v;
285 int err;
286
287 if (sc->sc_dying)
288 return EIO;
289
290 if (sc->sc_enabled)
291 return EBUSY;
292
293 if (sc->sc_isize == 0)
294 return 0;
295
296 sc->sc_ibuf = kmem_alloc(sc->sc_isize, KM_SLEEP);
297 err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_intr_number,
298 USBD_SHORT_XFER_OK, &sc->sc_intr_pipe, sc, sc->sc_ibuf,
299 sc->sc_isize, uep_intr, USBD_DEFAULT_INTERVAL);
300 if (err) {
301 kmem_free(sc->sc_ibuf, sc->sc_isize);
302 sc->sc_intr_pipe = NULL;
303 return EIO;
304 }
305
306 sc->sc_enabled = 1;
307
308 return 0;
309 }
310
311 Static void
uep_disable(void * v)312 uep_disable(void *v)
313 {
314 struct uep_softc *sc = v;
315
316 if (!sc->sc_enabled) {
317 printf("uep_disable: already disabled!\n");
318 return;
319 }
320
321 /* Disable interrupts. */
322 if (sc->sc_intr_pipe != NULL) {
323 usbd_abort_pipe(sc->sc_intr_pipe);
324 usbd_close_pipe(sc->sc_intr_pipe);
325 sc->sc_intr_pipe = NULL;
326 }
327
328 if (sc->sc_ibuf != NULL) {
329 kmem_free(sc->sc_ibuf, sc->sc_isize);
330 sc->sc_ibuf = NULL;
331 }
332
333 sc->sc_enabled = 0;
334 }
335
336 Static int
uep_ioctl(void * v,u_long cmd,void * data,int flag,struct lwp * l)337 uep_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
338 {
339 struct uep_softc *sc = v;
340 struct wsmouse_id *id;
341
342 switch (cmd) {
343 case WSMOUSEIO_GTYPE:
344 *(u_int *)data = WSMOUSE_TYPE_TPANEL;
345 return 0;
346
347 case WSMOUSEIO_GETID:
348 /*
349 * return unique ID string
350 * "<vendor> <model> <serial number>"
351 * unfortunately we have no serial number...
352 */
353 id = (struct wsmouse_id *)data;
354 if (id->type != WSMOUSE_ID_TYPE_UIDSTR)
355 return EINVAL;
356
357 strcpy(id->data, UIDSTR);
358 id->length = strlen(UIDSTR);
359 return 0;
360
361 case WSMOUSEIO_SCALIBCOORDS:
362 case WSMOUSEIO_GCALIBCOORDS:
363 return tpcalib_ioctl(&sc->sc_tpcalib, cmd, data, flag, l);
364 }
365
366 return EPASSTHROUGH;
367 }
368
369 static int
uep_adjust(int v,int off,int rat)370 uep_adjust(int v, int off, int rat)
371 {
372 int num = 100 * v;
373 int quot = num / rat;
374 int rem = num % rat;
375 if (num >= 0 && rem < 0)
376 quot++;
377 return quot + off;
378 }
379
380 void
uep_intr(struct usbd_xfer * xfer,void * addr,usbd_status status)381 uep_intr(struct usbd_xfer *xfer, void *addr, usbd_status status)
382 {
383 struct uep_softc *sc = addr;
384 u_char *p = sc->sc_ibuf;
385 u_char msk;
386 uint32_t len;
387 int x = 0, y = 0, s;
388
389 usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
390
391 if (status == USBD_CANCELLED)
392 return;
393
394 if (status != USBD_NORMAL_COMPLETION) {
395 aprint_error_dev(sc->sc_dev, "status %d\n", status);
396 usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
397 return;
398 }
399
400 /* First bit is always set to 1 */
401 if ((p[0] & 0x80) != 0x80) {
402 aprint_error_dev(sc->sc_dev, "bad input packet format\n");
403 return;
404 }
405
406 if (sc->sc_wsmousedev != NULL) {
407 /*
408 * Each report package may contain 5 or 6 bytes as below:
409 *
410 * Byte 0 1ZM00HLT
411 * Byte 1 0AAAAAAA
412 * Byte 2 0AAAAAAA
413 * Byte 3 0BBBBBBB
414 * Byte 4 0BBBBBBB
415 * Byte 5 0PPPPPPP
416 *
417 * Z: 1=byte 5 is pressure information, 0=no pressure
418 * M: 1=byte 5 is play id, 0=no player id
419 * T: 1=touched, 0=not touched
420 * H,L: Resolution
421 * 0,0: 11 bits
422 * 0,1: 12 bits
423 * 1,0: 13 bits
424 * 1,1: 14 bits
425 * A: bits of axis A position, MSB to LSB
426 * B: bits of axis B position, MSB to LSB
427 *
428 * The packet has six bytes only if Z or M is set.
429 * Byte 5, if sent, is ignored.
430 *
431 * For the unit I have, A = Y and B = X.
432 * I don't know if units exist with A=X and B=Y,
433 * if so we'll cross that bridge when we come to it.
434 *
435 * The controller sends a stream of T=1 events while the
436 * panel is touched, followed by a single T=0 event.
437 */
438 switch (p[0] & 0x06) {
439 case 0x02:
440 msk = 0x1f;
441 break;
442 case 0x04:
443 msk = 0x3f;
444 break;
445 case 0x06:
446 msk = 0x7f;
447 break;
448 default:
449 msk = 0x0f; /* H=0, L=0 */
450 }
451 x = uep_adjust(((p[3] & msk) << 7) | p[4], X_OFFSET, X_RATIO);
452 y = uep_adjust(((p[1] & msk) << 7) | p[2], Y_OFFSET, Y_RATIO);
453
454 tpcalib_trans(&sc->sc_tpcalib, x, y, &x, &y);
455
456 s = spltty();
457 wsmouse_input(sc->sc_wsmousedev, p[0] & 0x01, x, y, 0, 0,
458 WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y);
459 splx(s);
460 }
461 }
462