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