xref: /openbsd-src/sys/dev/usb/uts.c (revision 14b67a9184086c5a4b326b870dbc5799ab4e524d)
1 /*	$OpenBSD: uts.c,v 1.5 2007/04/26 17:00:28 miod Exp $ */
2 
3 /*
4  * Copyright (c) 2007 Robert Nagy <robert@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/sockio.h>
21 #include <sys/sysctl.h>
22 #include <sys/mbuf.h>
23 #include <sys/kernel.h>
24 #include <sys/socket.h>
25 #include <sys/systm.h>
26 #include <sys/malloc.h>
27 #include <sys/timeout.h>
28 #include <sys/conf.h>
29 #include <sys/device.h>
30 
31 #include <machine/bus.h>
32 #include <machine/endian.h>
33 #include <machine/intr.h>
34 
35 #include <dev/usb/usb.h>
36 #include <dev/usb/usbdi.h>
37 #include <dev/usb/usbdi_util.h>
38 #include <dev/usb/usbdevs.h>
39 
40 #include <dev/wscons/wsconsio.h>
41 #include <dev/wscons/wsmousevar.h>
42 
43 #ifdef USB_DEBUG
44 #define UTS_DEBUG
45 #endif
46 
47 #ifdef UTS_DEBUG
48 #define DPRINTF(x)		do { printf x; } while (0)
49 #else
50 #define DPRINTF(x)
51 #endif
52 
53 #define UTS_CONFIG_INDEX 0
54 
55 struct uts_softc {
56 	USBBASEDEVICE		sc_dev;
57 	usbd_device_handle	sc_udev;
58 	usbd_interface_handle	sc_iface;
59 	int			sc_iface_number;
60 	int			sc_product;
61 
62 	int			sc_intr_number;
63 	usbd_pipe_handle	sc_intr_pipe;
64 	u_char			*sc_ibuf;
65 	int			sc_isize;
66 	u_int8_t		sc_pkts;
67 
68 	device_ptr_t		sc_wsmousedev;
69 
70 	int	sc_enabled;
71 	int	sc_buttons;
72 	int	sc_dying;
73 	int	sc_oldx;
74 	int	sc_oldy;
75 };
76 
77 /* Settable via sysctl */
78 int	uts_rawmode;
79 struct utsscale {
80 	int	ts_minx;
81 	int	ts_maxx;
82 	int	ts_miny;
83 	int	ts_maxy;
84 	int	ts_swapxy;
85 	int	ts_resx;
86 	int	ts_resy;
87 } uts_scale = {
88 	67, 1931, 102, 1937, 0, 1024, 768
89 };
90 
91 struct uts_pos {
92 	int	x;
93 	int	y;
94 	int	z;	/* touch pressure */
95 };
96 
97 Static const struct usb_devno uts_devs[] = {
98 	{ USB_VENDOR_FTDI,		USB_PRODUCT_FTDI_ITM_TOUCH },
99 	{ USB_VENDOR_EGALAX,		USB_PRODUCT_EGALAX_TPANEL },
100 	{ USB_VENDOR_EGALAX,		USB_PRODUCT_EGALAX_TPANEL2 }
101 };
102 
103 Static void uts_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
104 struct uts_pos uts_get_pos(usbd_private_handle addr, struct uts_pos tp);
105 
106 Static int	uts_enable(void *);
107 Static void	uts_disable(void *);
108 Static int	uts_ioctl(void *, u_long, caddr_t, int, struct proc *);
109 
110 const struct wsmouse_accessops uts_accessops = {
111 	uts_enable,
112 	uts_ioctl,
113 	uts_disable,
114 };
115 
116 USB_DECLARE_DRIVER(uts);
117 
118 USB_MATCH(uts)
119 {
120 	USB_MATCH_START(uts, uaa);
121 
122 	if (uaa->iface == NULL)
123 		return UMATCH_NONE;
124 
125 	return (usb_lookup(uts_devs, uaa->vendor, uaa->product) != NULL) ?
126 		UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
127 }
128 
129 USB_ATTACH(uts)
130 {
131 	USB_ATTACH_START(uts, sc, uaa);
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 *devinfop;
137 	int i, found;
138 
139 	sc->sc_udev = uaa->device;
140 	sc->sc_product = uaa->product;
141 	sc->sc_intr_number = -1;
142 	sc->sc_intr_pipe = NULL;
143 	sc->sc_enabled = sc->sc_isize = 0;
144 
145 	/* Display device info string */
146 	USB_ATTACH_SETUP;
147 	if ((devinfop = usbd_devinfo_alloc(uaa->device, 0)) != NULL) {
148 		printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfop);
149 		usbd_devinfo_free(devinfop);
150 	}
151 
152 	/* Move the device into the configured state. */
153 	if (usbd_set_config_index(uaa->device, UTS_CONFIG_INDEX, 1) != 0) {
154 		printf("%s: could not set configuartion no\n",
155 			USBDEVNAME(sc->sc_dev));
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 	if (usbd_device2interface_handle(uaa->device, 0, &sc->sc_iface) != 0) {
171 		printf("%s: failed to get interface\n",
172 			USBDEVNAME(sc->sc_dev));
173 		sc->sc_dying = 1;
174 		USB_ATTACH_ERROR_RETURN;
175 	}
176 
177 	/* Find the interrupt endpoint */
178 	id = usbd_get_interface_descriptor(sc->sc_iface);
179 	sc->sc_iface_number = id->bInterfaceNumber;
180 	found = 0;
181 
182 	for (i = 0; i < id->bNumEndpoints; i++) {
183 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
184 		if (ed == NULL) {
185 			printf("%s: no endpoint descriptor for %d\n",
186 				USBDEVNAME(sc->sc_dev), i);
187 			sc->sc_dying = 1;
188 			USB_ATTACH_ERROR_RETURN;
189 		}
190 
191 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
192 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
193 			sc->sc_intr_number = ed->bEndpointAddress;
194 			sc->sc_isize = UGETW(ed->wMaxPacketSize);
195 		}
196 	}
197 
198 	if (sc->sc_intr_number== -1) {
199 		printf("%s: Could not find interrupt in\n",
200 			USBDEVNAME(sc->sc_dev));
201 		sc->sc_dying = 1;
202 		USB_ATTACH_ERROR_RETURN;
203 	}
204 
205 	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
206 			   USBDEV(sc->sc_dev));
207 
208 	a.accessops = &uts_accessops;
209 	a.accesscookie = sc;
210 
211 	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
212 
213 	USB_ATTACH_SUCCESS_RETURN;
214 }
215 
216 USB_DETACH(uts)
217 {
218 	USB_DETACH_START(uts, sc);
219 	int rv = 0;
220 
221 	if (sc->sc_intr_pipe != NULL) {
222 		usbd_abort_pipe(sc->sc_intr_pipe);
223 		usbd_close_pipe(sc->sc_intr_pipe);
224 		sc->sc_intr_pipe = NULL;
225 	}
226 
227 	sc->sc_dying = 1;
228 
229 	if (sc->sc_wsmousedev != NULL) {
230 		rv = config_detach(sc->sc_wsmousedev, flags);
231 		sc->sc_wsmousedev = NULL;
232 	}
233 
234 	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
235 			   USBDEV(sc->sc_dev));
236 
237 	return (rv);
238 }
239 
240 int
241 uts_activate(device_ptr_t self, enum devact act)
242 {
243 	struct uts_softc *sc = (struct uts_softc *)self;
244 	int rv = 0;
245 
246 	switch (act) {
247 	case DVACT_ACTIVATE:
248 		break;
249 
250 	case DVACT_DEACTIVATE:
251 		if (sc->sc_wsmousedev != NULL)
252 			rv = config_deactivate(sc->sc_wsmousedev);
253 		sc->sc_dying = 1;
254 		break;
255 	}
256 
257 	return (rv);
258 }
259 
260 Static int
261 uts_enable(void *v)
262 {
263 	struct uts_softc *sc = v;
264 	int err;
265 
266 	if (sc->sc_dying)
267 		return (EIO);
268 
269 	if (sc->sc_enabled)
270 		return (EBUSY);
271 
272 	if (sc->sc_isize == 0)
273 		return 0;
274 	sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
275 	err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_intr_number,
276 		USBD_SHORT_XFER_OK, &sc->sc_intr_pipe, sc, sc->sc_ibuf,
277 		sc->sc_isize, uts_intr, USBD_DEFAULT_INTERVAL);
278 	if (err) {
279 		free(sc->sc_ibuf, M_USBDEV);
280 		sc->sc_intr_pipe = NULL;
281 		return EIO;
282 	}
283 
284 	sc->sc_enabled = 1;
285 	sc->sc_buttons = 0;
286 
287 	return (0);
288 }
289 
290 Static void
291 uts_disable(void *v)
292 {
293 	struct uts_softc *sc = v;
294 
295 	if (!sc->sc_enabled) {
296 		printf("uts_disable: already disabled!\n");
297 		return;
298 	}
299 
300 	/* Disable interrupts. */
301 	if (sc->sc_intr_pipe != NULL) {
302 		usbd_abort_pipe(sc->sc_intr_pipe);
303 		usbd_close_pipe(sc->sc_intr_pipe);
304 		sc->sc_intr_pipe = NULL;
305 	}
306 
307 	if (sc->sc_ibuf != NULL) {
308 		free(sc->sc_ibuf, M_USBDEV);
309 		sc->sc_ibuf = NULL;
310 	}
311 
312 	sc->sc_enabled = 0;
313 }
314 
315 Static int
316 uts_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *l)
317 {
318 	switch (cmd) {
319 	case WSMOUSEIO_GTYPE:
320 		*(u_int *)data = WSMOUSE_TYPE_TPANEL;
321 		return (0);
322 	}
323 
324 	return (-1);
325 }
326 
327 struct uts_pos
328 uts_get_pos(usbd_private_handle addr, struct uts_pos tp)
329 {
330 	struct uts_softc *sc = addr;
331 	struct utsscale *tsp = &uts_scale;
332 	u_char *p = sc->sc_ibuf;
333 	int down, x, y;
334 
335 	switch (sc->sc_product) {
336 	case USB_PRODUCT_FTDI_ITM_TOUCH:
337 		down = (~p[7] & 0x20);
338 		x = ((p[0] & 0x1f) << 7) | (p[3] & 0x7f);
339 		y = ((p[1] & 0x1f) << 7) | (p[4] & 0x7f);
340 		sc->sc_pkts = 0x8;
341 		break;
342 	case USB_PRODUCT_EGALAX_TPANEL:
343 	case USB_PRODUCT_EGALAX_TPANEL2:
344 		down = (p[0] & 0x01);
345 		x = ((p[3] & 0x0f) << 7) | (p[4] & 0x7f);
346 		y = ((p[1] & 0x0f) << 7) | (p[2] & 0x7f);
347 		sc->sc_pkts = 0x5;
348 		break;
349 	}
350 
351 	DPRINTF(("%s: down = 0x%x, sc->sc_pkts = 0x%x\n",
352 	    USBDEVNAME(sc->sc_dev), down, sc->sc_pkts));
353 
354 	/* x/y values are not reliable if there is no pressure */
355 	if (down) {
356 		if (tsp->ts_swapxy) {	/* Swap X/Y-Axis */
357 			tp.y = x;
358 			tp.x = y;
359 		} else {
360 			tp.x = x;
361 			tp.y = y;
362 		}
363 
364 		if (!uts_rawmode) {
365 			/* Scale down to the screen resolution. */
366 			tp.x = ((tp.x - tsp->ts_minx) * tsp->ts_resx) /
367 			    (tsp->ts_maxx - tsp->ts_minx);
368 			tp.y = ((tp.y - tsp->ts_miny) * tsp->ts_resy) /
369 			    (tsp->ts_maxy - tsp->ts_miny);
370 		}
371 		tp.z = 1;
372 	} else {
373 		tp.x = sc->sc_oldx;
374 		tp.y = sc->sc_oldy;
375 		tp.z = 0;
376 	}
377 
378 	return (tp);
379 }
380 
381 Static void
382 uts_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
383 {
384 	struct uts_softc *sc = addr;
385 	u_int32_t len;
386 	int s;
387 	struct uts_pos tp;
388 
389 	usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
390 
391 	s = spltty();
392 
393 	if (status == USBD_CANCELLED)
394 		return;
395 
396 	if (status != USBD_NORMAL_COMPLETION) {
397 		printf("%s: status %d\n", USBDEVNAME(sc->sc_dev), status);
398 		usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
399 		return;
400 	}
401 
402 	tp = uts_get_pos(sc, tp);
403 
404 	if (len != sc->sc_pkts) {
405 		DPRINTF(("%s: bad input length %d != %d\n",
406 			USBDEVNAME(sc->sc_dev), len, sc->sc_isize));
407 		return;
408 	}
409 
410 	DPRINTF(("%s: tp.z = %d, tp.x = %d, tp.y = %d\n",
411 	    USBDEVNAME(sc->sc_dev), tp.z, tp.x, tp.y));
412 
413 	wsmouse_input(sc->sc_wsmousedev, tp.z, tp.x, tp.y, 0, 0,
414 		WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y |
415 		WSMOUSE_INPUT_ABSOLUTE_Z);
416 	sc->sc_oldy = tp.y;
417 	sc->sc_oldx = tp.x;
418 
419 	splx(s);
420 }
421