xref: /openbsd-src/sys/dev/usb/uts.c (revision 457d89f1a2eb7edd3082b8ca34213d8374d62eda)
1 /*	$OpenBSD: uts.c,v 1.2 2007/03/23 14:35:19 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 #define UTS_CONFIG_INDEX 0
44 
45 struct uts_softc {
46 	USBBASEDEVICE		sc_dev;
47 	usbd_device_handle	sc_udev;
48 	usbd_interface_handle	sc_iface;
49 	int			sc_iface_number;
50 	int			sc_product;
51 
52 	int			sc_intr_number;
53 	usbd_pipe_handle	sc_intr_pipe;
54 	u_char			*sc_ibuf;
55 	int			sc_isize;
56 	u_int8_t		sc_pkts;
57 
58 	device_ptr_t		sc_wsmousedev;
59 
60 	int	sc_enabled;
61 	int	sc_buttons;
62 	int	sc_dying;
63 	int	sc_oldx;
64 	int	sc_oldy;
65 };
66 
67 /* Settable via sysctl */
68 int	uts_rawmode;
69 struct utsscale {
70 	int	ts_minx;
71 	int	ts_maxx;
72 	int	ts_miny;
73 	int	ts_maxy;
74 	int	ts_swapxy;
75 	int	ts_resx;
76 	int	ts_resy;
77 } uts_scale = {
78 	3800, 500, 450, 3800, 1, 1024, 768
79 };
80 
81 struct uts_pos {
82 	int	x;
83 	int	y;
84 	int	z;	/* touch pressure */
85 };
86 
87 Static const struct usb_devno uts_devs[] = {
88 	{ USB_VENDOR_FTDI,		USB_PRODUCT_FTDI_ITM_TOUCH },
89 	{ USB_VENDOR_EGALAX,		USB_PRODUCT_EGALAX_TPANEL },
90 	{ USB_VENDOR_EGALAX,		USB_PRODUCT_EGALAX_TPANEL2 },
91 	{ 0, 0 }
92 };
93 
94 Static void uts_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
95 struct uts_pos uts_get_pos(usbd_private_handle addr, struct uts_pos tp);
96 
97 Static int	uts_enable(void *);
98 Static void	uts_disable(void *);
99 Static int	uts_ioctl(void *, u_long, caddr_t, int, struct proc *);
100 
101 const struct wsmouse_accessops uts_accessops = {
102 	uts_enable,
103 	uts_ioctl,
104 	uts_disable,
105 };
106 
107 USB_DECLARE_DRIVER(uts);
108 
109 USB_MATCH(uts)
110 {
111 	USB_MATCH_START(uts, uaa);
112 
113 	if (uaa->iface == NULL)
114 		return UMATCH_NONE;
115 
116 	return (usb_lookup(uts_devs, uaa->vendor, uaa->product) != NULL) ?
117 		UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
118 }
119 
120 USB_ATTACH(uts)
121 {
122 	USB_ATTACH_START(uts, sc, uaa);
123 	usb_config_descriptor_t *cdesc;
124 	usb_interface_descriptor_t *id;
125 	usb_endpoint_descriptor_t *ed;
126 	struct wsmousedev_attach_args a;
127 	char *devinfop;
128 	int i, found;
129 
130 	sc->sc_udev = uaa->device;
131 	sc->sc_product = uaa->product;
132 	sc->sc_intr_number = -1;
133 	sc->sc_intr_pipe = NULL;
134 	sc->sc_enabled = sc->sc_isize = 0;
135 
136 	/* Display device info string */
137 	USB_ATTACH_SETUP;
138 	if ((devinfop = usbd_devinfo_alloc(uaa->device, 0)) != NULL) {
139 		printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfop);
140 		usbd_devinfo_free(devinfop);
141 	}
142 
143 	/* Move the device into the configured state. */
144 	if (usbd_set_config_index(uaa->device, UTS_CONFIG_INDEX, 1) != 0) {
145 		printf("%s: could not set configuartion no\n",
146 			USBDEVNAME(sc->sc_dev));
147 		sc->sc_dying = 1;
148 		USB_ATTACH_ERROR_RETURN;
149 	}
150 
151 	/* get the config descriptor */
152 	cdesc = usbd_get_config_descriptor(sc->sc_udev);
153 	if (cdesc == NULL) {
154 		printf("%s: failed to get configuration descriptor\n",
155 			USBDEVNAME(sc->sc_dev));
156 		sc->sc_dying = 1;
157 		USB_ATTACH_ERROR_RETURN;
158 	}
159 
160 	/* get the interface */
161 	if (usbd_device2interface_handle(uaa->device, 0, &sc->sc_iface) != 0) {
162 		printf("%s: failed to get interface\n",
163 			USBDEVNAME(sc->sc_dev));
164 		sc->sc_dying = 1;
165 		USB_ATTACH_ERROR_RETURN;
166 	}
167 
168 	/* Find the interrupt endpoint */
169 	id = usbd_get_interface_descriptor(sc->sc_iface);
170 	sc->sc_iface_number = id->bInterfaceNumber;
171 	found = 0;
172 
173 	for (i = 0; i < id->bNumEndpoints; i++) {
174 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
175 		if (ed == NULL) {
176 			printf("%s: no endpoint descriptor for %d\n",
177 				USBDEVNAME(sc->sc_dev), i);
178 			sc->sc_dying = 1;
179 			USB_ATTACH_ERROR_RETURN;
180 		}
181 
182 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
183 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
184 			sc->sc_intr_number = ed->bEndpointAddress;
185 			sc->sc_isize = UGETW(ed->wMaxPacketSize);
186 		}
187 	}
188 
189 	if (sc->sc_intr_number== -1) {
190 		printf("%s: Could not find interrupt in\n",
191 			USBDEVNAME(sc->sc_dev));
192 		sc->sc_dying = 1;
193 		USB_ATTACH_ERROR_RETURN;
194 	}
195 
196 	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
197 			   USBDEV(sc->sc_dev));
198 
199 	a.accessops = &uts_accessops;
200 	a.accesscookie = sc;
201 
202 	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
203 
204 	USB_ATTACH_SUCCESS_RETURN;
205 }
206 
207 USB_DETACH(uts)
208 {
209 	USB_DETACH_START(uts, sc);
210 	int rv = 0;
211 
212 	if (sc->sc_intr_pipe != NULL) {
213 		usbd_abort_pipe(sc->sc_intr_pipe);
214 		usbd_close_pipe(sc->sc_intr_pipe);
215 		sc->sc_intr_pipe = NULL;
216 	}
217 
218 	sc->sc_dying = 1;
219 
220 	if (sc->sc_wsmousedev != NULL) {
221 		rv = config_detach(sc->sc_wsmousedev, flags);
222 		sc->sc_wsmousedev = NULL;
223 	}
224 
225 	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
226 			   USBDEV(sc->sc_dev));
227 
228 	return (rv);
229 }
230 
231 int
232 uts_activate(device_ptr_t self, enum devact act)
233 {
234 	struct uts_softc *sc = (struct uts_softc *)self;
235 	int rv = 0;
236 
237 	switch (act) {
238 	case DVACT_ACTIVATE:
239 		break;
240 
241 	case DVACT_DEACTIVATE:
242 		if (sc->sc_wsmousedev != NULL)
243 			rv = config_deactivate(sc->sc_wsmousedev);
244 		sc->sc_dying = 1;
245 		break;
246 	}
247 
248 	return (rv);
249 }
250 
251 Static int
252 uts_enable(void *v)
253 {
254 	struct uts_softc *sc = v;
255 	int err;
256 
257 	if (sc->sc_dying)
258 		return (EIO);
259 
260 	if (sc->sc_enabled)
261 		return (EBUSY);
262 
263 	if (sc->sc_isize == 0)
264 		return 0;
265 	sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
266 	err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_intr_number,
267 		USBD_SHORT_XFER_OK, &sc->sc_intr_pipe, sc, sc->sc_ibuf,
268 		sc->sc_isize, uts_intr, USBD_DEFAULT_INTERVAL);
269 	if (err) {
270 		free(sc->sc_ibuf, M_USBDEV);
271 		sc->sc_intr_pipe = NULL;
272 		return EIO;
273 	}
274 
275 	sc->sc_enabled = 1;
276 	sc->sc_buttons = 0;
277 
278 	return (0);
279 }
280 
281 Static void
282 uts_disable(void *v)
283 {
284 	struct uts_softc *sc = v;
285 
286 	if (!sc->sc_enabled) {
287 		printf("uts_disable: already disabled!\n");
288 		return;
289 	}
290 
291 	/* Disable interrupts. */
292 	if (sc->sc_intr_pipe != NULL) {
293 		usbd_abort_pipe(sc->sc_intr_pipe);
294 		usbd_close_pipe(sc->sc_intr_pipe);
295 		sc->sc_intr_pipe = NULL;
296 	}
297 
298 	if (sc->sc_ibuf != NULL) {
299 		free(sc->sc_ibuf, M_USBDEV);
300 		sc->sc_ibuf = NULL;
301 	}
302 
303 	sc->sc_enabled = 0;
304 }
305 
306 Static int
307 uts_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *l)
308 {
309 	switch (cmd) {
310 	case WSMOUSEIO_GTYPE:
311 		*(u_int *)data = WSMOUSE_TYPE_TPANEL;
312 		return (0);
313 	}
314 
315 	return (-1);
316 }
317 
318 struct uts_pos
319 uts_get_pos(usbd_private_handle addr, struct uts_pos tp)
320 {
321 	struct uts_softc *sc = addr;
322 	struct utsscale *tsp = &uts_scale;
323 	u_char *p = sc->sc_ibuf;
324 	int down, x, y;
325 
326 	switch (sc->sc_product) {
327 	case USB_PRODUCT_FTDI_ITM_TOUCH:
328 		down = (p[7] & 0x20);
329 		x = ((p[0] & 0x1f) << 7) | (p[3] & 0x7f);
330 		y = ((p[1] & 0x1f) << 7) | (p[4] & 0x7f);
331 		sc->sc_pkts = 8;
332 		break;
333 	case USB_PRODUCT_EGALAX_TPANEL:
334 	case USB_PRODUCT_EGALAX_TPANEL2:
335 		down = (p[0] & 0x01);
336 		x = ((p[3] & 0x0f) << 7) | (p[4] & 0x7f);
337 		y = ((p[1] & 0x0f) << 7) | (p[2] & 0x7f);
338 		sc->sc_pkts = 5;
339 		break;
340 	}
341 
342 	if (!down) {
343 		if (tsp->ts_swapxy) {	/* Swap X/Y-Axis */
344 			tp.y = x;
345 			tp.x = y;
346 		} else {
347 			tp.x = x;
348 			tp.y = y;
349 		}
350 
351 		if (!uts_rawmode) {
352 			/* Scale down to the screen resolution. */
353 			tp.x = ((tp.x - tsp->ts_minx) * tsp->ts_resx) /
354 			    (tsp->ts_maxx - tsp->ts_minx);
355 			tp.y = ((tp.y - tsp->ts_miny) * tsp->ts_resy) /
356 			    (tsp->ts_maxy - tsp->ts_miny);
357 		}
358 		tp.z = 1;
359 	} else {
360 		/* x/y values are not reliable if there is no pressure */
361 		tp.x = sc->sc_oldx;
362 		tp.y = sc->sc_oldy;
363 		tp.z = 0;
364 	}
365 
366 	return (tp);
367 }
368 
369 Static void
370 uts_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
371 {
372 	struct uts_softc *sc = addr;
373 	u_int32_t len;
374 	int s;
375 	struct uts_pos tp;
376 
377 	usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
378 
379 	s = spltty();
380 
381 	if (status == USBD_CANCELLED)
382 		return;
383 
384 	if (status != USBD_NORMAL_COMPLETION) {
385 		printf("%s: status %d\n", USBDEVNAME(sc->sc_dev), status);
386 		usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
387 		return;
388 	}
389 
390 	tp = uts_get_pos(sc, tp);
391 
392 	if (len != sc->sc_pkts) {
393 		printf("%s: bad input length %d != %d\n",
394 			USBDEVNAME(sc->sc_dev), len, sc->sc_isize);
395 		return;
396 	}
397 
398 	wsmouse_input(sc->sc_wsmousedev, tp.z, tp.x, tp.y, 0,
399 		WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y |
400 		WSMOUSE_INPUT_ABSOLUTE_Z);
401 	sc->sc_oldy = tp.y;
402 	sc->sc_oldx = tp.x;
403 
404 	splx(s);
405 }
406