uep.c revision 1.3 1 /* $NetBSD: uep.c,v 1.3 2005/02/27 00:27:51 perry 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 (at) 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 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * eGalax USB touchpanel controller driver.
41 *
42 * For Programming Documentation, see:
43 *
44 * http://www.egalax.com/SoftwareProgrammingGuide_1.1.pdf
45 */
46
47 #include <sys/cdefs.h>
48 __KERNEL_RCSID(0, "$NetBSD: uep.c,v 1.3 2005/02/27 00:27:51 perry Exp $");
49
50 #include <sys/param.h>
51 #include <sys/systm.h>
52 #include <sys/kernel.h>
53 #include <sys/malloc.h>
54 #include <sys/device.h>
55 #include <sys/ioctl.h>
56 #include <sys/vnode.h>
57
58 #include <dev/usb/usb.h>
59 #include <dev/usb/usbdi.h>
60 #include <dev/usb/usbdi_util.h>
61 #include <dev/usb/usbdevs.h>
62 #include <dev/usb/usb_quirks.h>
63
64 #include <dev/wscons/wsconsio.h>
65 #include <dev/wscons/wsmousevar.h>
66 #include <dev/wscons/tpcalibvar.h>
67
68 #define UIDSTR "eGalax USB SN000000"
69
70 struct uep_softc {
71 USBBASEDEVICE sc_dev;
72 usbd_device_handle sc_udev; /* device */
73 usbd_interface_handle sc_iface; /* interface */
74 int sc_iface_number;
75
76 int sc_intr_number; /* interrupt number */
77 usbd_pipe_handle sc_intr_pipe; /* interrupt pipe */
78 u_char *sc_ibuf;
79 int sc_isize;
80
81 device_ptr_t sc_wsmousedev; /* wsmouse device */
82 struct tpcalib_softc sc_tpcalib; /* calibration */
83
84 u_char sc_enabled;
85 u_char sc_dying;
86 };
87
88 static struct wsmouse_calibcoords default_calib = {
89 0, 0, 2047, 2047,
90 WSMOUSE_CALIBCOORDS_RESET,
91 };
92
93 Static void uep_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
94
95 Static int uep_enable(void *);
96 Static void uep_disable(void *);
97 Static int uep_ioctl(void *, u_long, caddr_t, int, usb_proc_ptr);
98
99 const struct wsmouse_accessops uep_accessops = {
100 uep_enable,
101 uep_ioctl,
102 uep_disable,
103 };
104
105 USB_DECLARE_DRIVER(uep);
106
107 USB_MATCH(uep)
108 {
109 USB_MATCH_START(uep, uaa);
110
111 if (uaa->iface == NULL)
112 return UMATCH_NONE;
113
114 if ((uaa->vendor == USB_VENDOR_EGALAX) && (
115 (uaa->product == USB_PRODUCT_EGALAX_TPANEL)
116 || (uaa->product == USB_PRODUCT_EGALAX_TPANEL2)
117 ))
118 return UMATCH_VENDOR_PRODUCT;
119
120 if ((uaa->vendor == USB_VENDOR_EGALAX2)
121 && (uaa->product == USB_PRODUCT_EGALAX2_TPANEL))
122 return UMATCH_VENDOR_PRODUCT;
123
124
125 return UMATCH_NONE;
126 }
127
128 USB_ATTACH(uep)
129 {
130 USB_ATTACH_START(uep, sc, uaa);
131 usbd_device_handle dev = uaa->device;
132 usb_config_descriptor_t *cdesc;
133 usb_interface_descriptor_t *id;
134 usb_endpoint_descriptor_t *ed;
135 struct wsmousedev_attach_args a;
136 char devinfo[1024];
137 usbd_status err;
138 int i, found;
139
140 usbd_devinfo(dev, 0, devinfo, sizeof(devinfo));
141
142 USB_ATTACH_SETUP;
143
144 printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);
145
146 sc->sc_udev = dev;
147 sc->sc_intr_number = -1;
148 sc->sc_intr_pipe = NULL;
149 sc->sc_enabled = sc->sc_isize = 0;
150
151 /* Move the device into the configured state. */
152 err = usbd_set_config_index(dev, 0, 1);
153 if (err) {
154 printf("\n%s: failed to set configuration, err=%s\n",
155 USBDEVNAME(sc->sc_dev), usbd_errstr(err));
156 sc->sc_dying = 1;
157 USB_ATTACH_ERROR_RETURN;
158 }
159
160 /* get the config descriptor */
161 cdesc = usbd_get_config_descriptor(sc->sc_udev);
162 if (cdesc == NULL) {
163 printf("%s: failed to get configuration descriptor\n",
164 USBDEVNAME(sc->sc_dev));
165 sc->sc_dying = 1;
166 USB_ATTACH_ERROR_RETURN;
167 }
168
169 /* get the interface */
170 err = usbd_device2interface_handle(dev, 0, &sc->sc_iface);
171 if (err) {
172 printf("\n%s: failed to get interface, err=%s\n",
173 USBDEVNAME(sc->sc_dev), usbd_errstr(err));
174 sc->sc_dying = 1;
175 USB_ATTACH_ERROR_RETURN;
176 }
177
178 /* Find the interrupt endpoint */
179 id = usbd_get_interface_descriptor(sc->sc_iface);
180 sc->sc_iface_number = id->bInterfaceNumber;
181 found = 0;
182
183 for (i = 0; i < id->bNumEndpoints; i++) {
184 ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
185 if (ed == NULL) {
186 printf("%s: no endpoint descriptor for %d\n",
187 USBDEVNAME(sc->sc_dev), i);
188 sc->sc_dying = 1;
189 USB_ATTACH_ERROR_RETURN;
190 }
191
192 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
193 UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
194 sc->sc_intr_number = ed->bEndpointAddress;
195 sc->sc_isize = UGETW(ed->wMaxPacketSize);
196 }
197 }
198
199 if (sc->sc_intr_number== -1) {
200 printf("%s: Could not find interrupt in\n",
201 USBDEVNAME(sc->sc_dev));
202 sc->sc_dying = 1;
203 USB_ATTACH_ERROR_RETURN;
204 }
205
206 usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
207 USBDEV(sc->sc_dev));
208
209 a.accessops = &uep_accessops;
210 a.accesscookie = sc;
211
212 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
213
214 tpcalib_init(&sc->sc_tpcalib);
215 tpcalib_ioctl(&sc->sc_tpcalib, WSMOUSEIO_SCALIBCOORDS,
216 (caddr_t)&default_calib, 0, 0);
217
218 USB_ATTACH_SUCCESS_RETURN;
219 }
220
221 USB_DETACH(uep)
222 {
223 USB_DETACH_START(uep, sc);
224 int rv = 0;
225
226 if (sc->sc_intr_pipe != NULL) {
227 usbd_abort_pipe(sc->sc_intr_pipe);
228 usbd_close_pipe(sc->sc_intr_pipe);
229 sc->sc_intr_pipe = NULL;
230 }
231
232 sc->sc_dying = 1;
233
234 /* save current calib as defaults */
235 default_calib = sc->sc_tpcalib.sc_saved;
236
237 if (sc->sc_wsmousedev != NULL) {
238 rv = config_detach(sc->sc_wsmousedev, flags);
239 sc->sc_wsmousedev = NULL;
240 }
241
242 usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
243 USBDEV(sc->sc_dev));
244
245 return rv;
246 }
247
248 int
249 uep_activate(device_ptr_t self, enum devact act)
250 {
251 struct uep_softc *sc = (struct uep_softc *)self;
252 int rv = 0;
253
254 switch (act) {
255 case DVACT_ACTIVATE:
256 return EOPNOTSUPP;
257
258 case DVACT_DEACTIVATE:
259 if (sc->sc_wsmousedev != NULL)
260 rv = config_deactivate(sc->sc_wsmousedev);
261 sc->sc_dying = 1;
262 break;
263 }
264
265 return rv;
266 }
267
268 Static int
269 uep_enable(void *v)
270 {
271 struct uep_softc *sc = v;
272 int err;
273
274 if (sc->sc_dying)
275 return EIO;
276
277 if (sc->sc_enabled)
278 return EBUSY;
279
280 if (sc->sc_isize == 0)
281 return 0;
282 sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
283 err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_intr_number,
284 USBD_SHORT_XFER_OK, &sc->sc_intr_pipe, sc, sc->sc_ibuf,
285 sc->sc_isize, uep_intr, USBD_DEFAULT_INTERVAL);
286 if (err) {
287 free(sc->sc_ibuf, M_USBDEV);
288 sc->sc_intr_pipe = NULL;
289 return EIO;
290 }
291
292 sc->sc_enabled = 1;
293
294 return 0;
295 }
296
297 Static void
298 uep_disable(void *v)
299 {
300 struct uep_softc *sc = v;
301
302 if (!sc->sc_enabled) {
303 printf("uep_disable: already disabled!\n");
304 return;
305 }
306
307 /* Disable interrupts. */
308 if (sc->sc_intr_pipe != NULL) {
309 usbd_abort_pipe(sc->sc_intr_pipe);
310 usbd_close_pipe(sc->sc_intr_pipe);
311 sc->sc_intr_pipe = NULL;
312 }
313
314 if (sc->sc_ibuf != NULL) {
315 free(sc->sc_ibuf, M_USBDEV);
316 sc->sc_ibuf = NULL;
317 }
318
319 sc->sc_enabled = 0;
320 }
321
322 Static int
323 uep_ioctl(void *v, u_long cmd, caddr_t data, int flag, usb_proc_ptr p)
324 {
325 struct uep_softc *sc = v;
326 struct wsmouse_id *id;
327
328 switch (cmd) {
329 case WSMOUSEIO_GTYPE:
330 *(u_int *)data = WSMOUSE_TYPE_TPANEL;
331 return 0;
332
333 case WSMOUSEIO_GETID:
334 /*
335 * return unique ID string
336 * "<vendor> <model> <serial number>"
337 * unfortunately we have no serial number...
338 */
339 id = (struct wsmouse_id *)data;
340 if (id->type != WSMOUSE_ID_TYPE_UIDSTR)
341 return EINVAL;
342
343 strcpy(id->data, UIDSTR);
344 id->length = strlen(UIDSTR);
345 return 0;
346
347 case WSMOUSEIO_SCALIBCOORDS:
348 case WSMOUSEIO_GCALIBCOORDS:
349 return tpcalib_ioctl(&sc->sc_tpcalib, cmd, data, flag, p);
350 }
351
352 return EPASSTHROUGH;
353 }
354
355 void
356 uep_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
357 {
358 struct uep_softc *sc = addr;
359 u_char *p = sc->sc_ibuf;
360 u_int32_t len;
361 int x = 0, y = 0, s;
362
363 usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
364
365 if (status == USBD_CANCELLED)
366 return;
367
368 if (status != USBD_NORMAL_COMPLETION) {
369 printf("%s: status %d\n", USBDEVNAME(sc->sc_dev), status);
370 usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
371 return;
372 }
373
374 if (len != 5) {
375 #if 0
376 printf("%s: bad input length %d != %d\n",
377 USBDEVNAME(sc->sc_dev), len, sc->sc_isize);
378 #endif
379 return;
380 }
381
382 if ((p[0] & 0xFE) != 0x80) {
383 printf("%s: bad input packet format\n", USBDEVNAME(sc->sc_dev));
384 return;
385 }
386
387 if (sc->sc_wsmousedev != NULL) {
388 /*
389 * Packet format is 5 bytes:
390 *
391 * 1000000T
392 * 0000AAAA
393 * 0AAAAAAA
394 * 0000BBBB
395 * 0BBBBBBB
396 *
397 * T: 1=touched 0=not touched
398 * A: bits of axis A position, MSB to LSB
399 * B: bits of axis B position, MSB to LSB
400 *
401 * For the unit I have, A = Y and B = X.
402 * I don't know if units exist with A=X and B=Y,
403 * if so we'll cross that bridge when we come to it.
404 *
405 * The controller sends a stream of T=1 events while the
406 * panel is touched, followed by a single T=0 event.
407 *
408 */
409
410 x = (p[3] << 7) | p[4];
411 y = (p[1] << 7) | p[2];
412
413 tpcalib_trans(&sc->sc_tpcalib, x, y, &x, &y);
414
415 s = spltty();
416 wsmouse_input(sc->sc_wsmousedev, p[0] & 0x01, x, y, 0,
417 WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y);
418 splx(s);
419 }
420 }
421