xref: /openbsd-src/sys/dev/usb/uts.c (revision 565e952ad0e2e3a5f651cc3098f6b838f628f1fc)
1 /*	$OpenBSD: uts.c,v 1.4 2007/04/25 14:17:42 robert 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 	{ 0, 0 }
102 };
103 
104 Static void uts_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
105 struct uts_pos uts_get_pos(usbd_private_handle addr, struct uts_pos tp);
106 
107 Static int	uts_enable(void *);
108 Static void	uts_disable(void *);
109 Static int	uts_ioctl(void *, u_long, caddr_t, int, struct proc *);
110 
111 const struct wsmouse_accessops uts_accessops = {
112 	uts_enable,
113 	uts_ioctl,
114 	uts_disable,
115 };
116 
117 USB_DECLARE_DRIVER(uts);
118 
119 USB_MATCH(uts)
120 {
121 	USB_MATCH_START(uts, uaa);
122 
123 	if (uaa->iface == NULL)
124 		return UMATCH_NONE;
125 
126 	return (usb_lookup(uts_devs, uaa->vendor, uaa->product) != NULL) ?
127 		UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
128 }
129 
130 USB_ATTACH(uts)
131 {
132 	USB_ATTACH_START(uts, sc, uaa);
133 	usb_config_descriptor_t *cdesc;
134 	usb_interface_descriptor_t *id;
135 	usb_endpoint_descriptor_t *ed;
136 	struct wsmousedev_attach_args a;
137 	char *devinfop;
138 	int i, found;
139 
140 	sc->sc_udev = uaa->device;
141 	sc->sc_product = uaa->product;
142 	sc->sc_intr_number = -1;
143 	sc->sc_intr_pipe = NULL;
144 	sc->sc_enabled = sc->sc_isize = 0;
145 
146 	/* Display device info string */
147 	USB_ATTACH_SETUP;
148 	if ((devinfop = usbd_devinfo_alloc(uaa->device, 0)) != NULL) {
149 		printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfop);
150 		usbd_devinfo_free(devinfop);
151 	}
152 
153 	/* Move the device into the configured state. */
154 	if (usbd_set_config_index(uaa->device, UTS_CONFIG_INDEX, 1) != 0) {
155 		printf("%s: could not set configuartion no\n",
156 			USBDEVNAME(sc->sc_dev));
157 		sc->sc_dying = 1;
158 		USB_ATTACH_ERROR_RETURN;
159 	}
160 
161 	/* get the config descriptor */
162 	cdesc = usbd_get_config_descriptor(sc->sc_udev);
163 	if (cdesc == NULL) {
164 		printf("%s: failed to get configuration descriptor\n",
165 			USBDEVNAME(sc->sc_dev));
166 		sc->sc_dying = 1;
167 		USB_ATTACH_ERROR_RETURN;
168 	}
169 
170 	/* get the interface */
171 	if (usbd_device2interface_handle(uaa->device, 0, &sc->sc_iface) != 0) {
172 		printf("%s: failed to get interface\n",
173 			USBDEVNAME(sc->sc_dev));
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 = &uts_accessops;
210 	a.accesscookie = sc;
211 
212 	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
213 
214 	USB_ATTACH_SUCCESS_RETURN;
215 }
216 
217 USB_DETACH(uts)
218 {
219 	USB_DETACH_START(uts, sc);
220 	int rv = 0;
221 
222 	if (sc->sc_intr_pipe != NULL) {
223 		usbd_abort_pipe(sc->sc_intr_pipe);
224 		usbd_close_pipe(sc->sc_intr_pipe);
225 		sc->sc_intr_pipe = NULL;
226 	}
227 
228 	sc->sc_dying = 1;
229 
230 	if (sc->sc_wsmousedev != NULL) {
231 		rv = config_detach(sc->sc_wsmousedev, flags);
232 		sc->sc_wsmousedev = NULL;
233 	}
234 
235 	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
236 			   USBDEV(sc->sc_dev));
237 
238 	return (rv);
239 }
240 
241 int
242 uts_activate(device_ptr_t self, enum devact act)
243 {
244 	struct uts_softc *sc = (struct uts_softc *)self;
245 	int rv = 0;
246 
247 	switch (act) {
248 	case DVACT_ACTIVATE:
249 		break;
250 
251 	case DVACT_DEACTIVATE:
252 		if (sc->sc_wsmousedev != NULL)
253 			rv = config_deactivate(sc->sc_wsmousedev);
254 		sc->sc_dying = 1;
255 		break;
256 	}
257 
258 	return (rv);
259 }
260 
261 Static int
262 uts_enable(void *v)
263 {
264 	struct uts_softc *sc = v;
265 	int err;
266 
267 	if (sc->sc_dying)
268 		return (EIO);
269 
270 	if (sc->sc_enabled)
271 		return (EBUSY);
272 
273 	if (sc->sc_isize == 0)
274 		return 0;
275 	sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
276 	err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_intr_number,
277 		USBD_SHORT_XFER_OK, &sc->sc_intr_pipe, sc, sc->sc_ibuf,
278 		sc->sc_isize, uts_intr, USBD_DEFAULT_INTERVAL);
279 	if (err) {
280 		free(sc->sc_ibuf, M_USBDEV);
281 		sc->sc_intr_pipe = NULL;
282 		return EIO;
283 	}
284 
285 	sc->sc_enabled = 1;
286 	sc->sc_buttons = 0;
287 
288 	return (0);
289 }
290 
291 Static void
292 uts_disable(void *v)
293 {
294 	struct uts_softc *sc = v;
295 
296 	if (!sc->sc_enabled) {
297 		printf("uts_disable: already disabled!\n");
298 		return;
299 	}
300 
301 	/* Disable interrupts. */
302 	if (sc->sc_intr_pipe != NULL) {
303 		usbd_abort_pipe(sc->sc_intr_pipe);
304 		usbd_close_pipe(sc->sc_intr_pipe);
305 		sc->sc_intr_pipe = NULL;
306 	}
307 
308 	if (sc->sc_ibuf != NULL) {
309 		free(sc->sc_ibuf, M_USBDEV);
310 		sc->sc_ibuf = NULL;
311 	}
312 
313 	sc->sc_enabled = 0;
314 }
315 
316 Static int
317 uts_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *l)
318 {
319 	switch (cmd) {
320 	case WSMOUSEIO_GTYPE:
321 		*(u_int *)data = WSMOUSE_TYPE_TPANEL;
322 		return (0);
323 	}
324 
325 	return (-1);
326 }
327 
328 struct uts_pos
329 uts_get_pos(usbd_private_handle addr, struct uts_pos tp)
330 {
331 	struct uts_softc *sc = addr;
332 	struct utsscale *tsp = &uts_scale;
333 	u_char *p = sc->sc_ibuf;
334 	int down, x, y;
335 
336 	switch (sc->sc_product) {
337 	case USB_PRODUCT_FTDI_ITM_TOUCH:
338 		down = (~p[7] & 0x20);
339 		x = ((p[0] & 0x1f) << 7) | (p[3] & 0x7f);
340 		y = ((p[1] & 0x1f) << 7) | (p[4] & 0x7f);
341 		sc->sc_pkts = 0x8;
342 		break;
343 	case USB_PRODUCT_EGALAX_TPANEL:
344 	case USB_PRODUCT_EGALAX_TPANEL2:
345 		down = (p[0] & 0x01);
346 		x = ((p[3] & 0x0f) << 7) | (p[4] & 0x7f);
347 		y = ((p[1] & 0x0f) << 7) | (p[2] & 0x7f);
348 		sc->sc_pkts = 0x5;
349 		break;
350 	}
351 
352 	DPRINTF(("%s: down = 0x%x, sc->sc_pkts = 0x%x\n",
353 	    USBDEVNAME(sc->sc_dev), down, sc->sc_pkts));
354 
355 	/* x/y values are not reliable if there is no pressure */
356 	if (down) {
357 		if (tsp->ts_swapxy) {	/* Swap X/Y-Axis */
358 			tp.y = x;
359 			tp.x = y;
360 		} else {
361 			tp.x = x;
362 			tp.y = y;
363 		}
364 
365 		if (!uts_rawmode) {
366 			/* Scale down to the screen resolution. */
367 			tp.x = ((tp.x - tsp->ts_minx) * tsp->ts_resx) /
368 			    (tsp->ts_maxx - tsp->ts_minx);
369 			tp.y = ((tp.y - tsp->ts_miny) * tsp->ts_resy) /
370 			    (tsp->ts_maxy - tsp->ts_miny);
371 		}
372 		tp.z = 1;
373 	} else {
374 		tp.x = sc->sc_oldx;
375 		tp.y = sc->sc_oldy;
376 		tp.z = 0;
377 	}
378 
379 	return (tp);
380 }
381 
382 Static void
383 uts_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
384 {
385 	struct uts_softc *sc = addr;
386 	u_int32_t len;
387 	int s;
388 	struct uts_pos tp;
389 
390 	usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
391 
392 	s = spltty();
393 
394 	if (status == USBD_CANCELLED)
395 		return;
396 
397 	if (status != USBD_NORMAL_COMPLETION) {
398 		printf("%s: status %d\n", USBDEVNAME(sc->sc_dev), status);
399 		usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
400 		return;
401 	}
402 
403 	tp = uts_get_pos(sc, tp);
404 
405 	if (len != sc->sc_pkts) {
406 		DPRINTF(("%s: bad input length %d != %d\n",
407 			USBDEVNAME(sc->sc_dev), len, sc->sc_isize));
408 		return;
409 	}
410 
411 	DPRINTF(("%s: tp.z = %d, tp.x = %d, tp.y = %d\n",
412 	    USBDEVNAME(sc->sc_dev), tp.z, tp.x, tp.y));
413 
414 	wsmouse_input(sc->sc_wsmousedev, tp.z, tp.x, tp.y, 0, 0,
415 		WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y |
416 		WSMOUSE_INPUT_ABSOLUTE_Z);
417 	sc->sc_oldy = tp.y;
418 	sc->sc_oldx = tp.x;
419 
420 	splx(s);
421 }
422