File: | dev/usb/uts.c |
Warning: | line 403, column 6 Branch condition evaluates to a garbage value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: uts.c,v 1.44 2023/04/26 08:38:51 bentley 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/mbuf.h> | |||
22 | #include <sys/kernel.h> | |||
23 | #include <sys/socket.h> | |||
24 | #include <sys/systm.h> | |||
25 | #include <sys/malloc.h> | |||
26 | #include <sys/timeout.h> | |||
27 | #include <sys/conf.h> | |||
28 | #include <sys/device.h> | |||
29 | #include <sys/endian.h> | |||
30 | ||||
31 | #include <machine/intr.h> | |||
32 | ||||
33 | #include <dev/usb/usb.h> | |||
34 | #include <dev/usb/usbdi.h> | |||
35 | #include <dev/usb/usbdi_util.h> | |||
36 | #include <dev/usb/usbdevs.h> | |||
37 | ||||
38 | #include <dev/wscons/wsconsio.h> | |||
39 | #include <dev/wscons/wsmousevar.h> | |||
40 | ||||
41 | #ifdef UTS_DEBUG | |||
42 | #define DPRINTF(x) do { printf x; } while (0) | |||
43 | #else | |||
44 | #define DPRINTF(x) | |||
45 | #endif | |||
46 | ||||
47 | struct tsscale { | |||
48 | int minx, maxx; | |||
49 | int miny, maxy; | |||
50 | int swapxy; | |||
51 | int resx, resy; | |||
52 | } def_scale = { | |||
53 | 67, 1931, 102, 1937, 0, 1024, 768 | |||
54 | }; | |||
55 | ||||
56 | struct uts_softc { | |||
57 | struct device sc_dev; | |||
58 | struct usbd_device *sc_udev; | |||
59 | struct usbd_interface *sc_iface; | |||
60 | int sc_product; | |||
61 | int sc_vendor; | |||
62 | ||||
63 | int sc_intr_number; | |||
64 | struct usbd_pipe *sc_intr_pipe; | |||
65 | u_char *sc_ibuf; | |||
66 | int sc_isize; | |||
67 | u_int8_t sc_pkts; | |||
68 | ||||
69 | struct device *sc_wsmousedev; | |||
70 | ||||
71 | int sc_enabled; | |||
72 | int sc_buttons; | |||
73 | int sc_oldx; | |||
74 | int sc_oldy; | |||
75 | int sc_rawmode; | |||
76 | ||||
77 | struct tsscale sc_tsscale; | |||
78 | }; | |||
79 | ||||
80 | struct uts_pos { | |||
81 | int down; | |||
82 | int x; | |||
83 | int y; | |||
84 | int z; /* touch pressure */ | |||
85 | }; | |||
86 | ||||
87 | const struct usb_devno uts_devs[] = { | |||
88 | { USB_VENDOR_FTDI0x0403, USB_PRODUCT_FTDI_ITM_TOUCH0xf9e9 }, | |||
89 | { USB_VENDOR_EGALAX0x0eef, USB_PRODUCT_EGALAX_TPANEL0x0001 }, | |||
90 | { USB_VENDOR_EGALAX0x0eef, USB_PRODUCT_EGALAX_TPANEL20x0002 }, | |||
91 | { USB_VENDOR_GUNZE0x0637, USB_PRODUCT_GUNZE_TOUCHPANEL0x0001 } | |||
92 | }; | |||
93 | ||||
94 | void uts_intr(struct usbd_xfer *, void *, usbd_status); | |||
95 | void uts_get_pos(void *addr, struct uts_pos *tp); | |||
96 | ||||
97 | int uts_enable(void *); | |||
98 | void uts_disable(void *); | |||
99 | 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 | int uts_match(struct device *, void *, void *); | |||
108 | void uts_attach(struct device *, struct device *, void *); | |||
109 | int uts_detach(struct device *, int); | |||
110 | int uts_activate(struct device *, int); | |||
111 | ||||
112 | struct cfdriver uts_cd = { | |||
113 | NULL((void *)0), "uts", DV_DULL | |||
114 | }; | |||
115 | ||||
116 | const struct cfattach uts_ca = { | |||
117 | sizeof(struct uts_softc), | |||
118 | uts_match, | |||
119 | uts_attach, | |||
120 | uts_detach, | |||
121 | uts_activate, | |||
122 | }; | |||
123 | ||||
124 | int | |||
125 | uts_match(struct device *parent, void *match, void *aux) | |||
126 | { | |||
127 | struct usb_attach_arg *uaa = aux; | |||
128 | usb_interface_descriptor_t *id; | |||
129 | ||||
130 | if (uaa->iface == NULL((void *)0)) | |||
131 | return (UMATCH_NONE0); | |||
132 | ||||
133 | /* Some eGalax touch screens are HID devices. ignore them */ | |||
134 | id = usbd_get_interface_descriptor(uaa->iface); | |||
135 | if (id != NULL((void *)0) && id->bInterfaceClass == UICLASS_HID0x03) | |||
136 | return (UMATCH_NONE0); | |||
137 | ||||
138 | return (usb_lookup(uts_devs, uaa->vendor, uaa->product)usbd_match_device((const struct usb_devno *)(uts_devs), sizeof (uts_devs) / sizeof ((uts_devs)[0]), sizeof ((uts_devs)[0]), (uaa->vendor), (uaa->product)) != NULL((void *)0)) ? | |||
139 | UMATCH_VENDOR_PRODUCT13 : UMATCH_NONE0; | |||
140 | } | |||
141 | ||||
142 | void | |||
143 | uts_attach(struct device *parent, struct device *self, void *aux) | |||
144 | { | |||
145 | struct uts_softc *sc = (struct uts_softc *)self; | |||
146 | struct usb_attach_arg *uaa = aux; | |||
147 | usb_config_descriptor_t *cdesc; | |||
148 | usb_interface_descriptor_t *id; | |||
149 | usb_endpoint_descriptor_t *ed; | |||
150 | struct wsmousedev_attach_args a; | |||
151 | int i; | |||
152 | ||||
153 | sc->sc_udev = uaa->device; | |||
154 | sc->sc_product = uaa->product; | |||
155 | sc->sc_vendor = uaa->vendor; | |||
156 | sc->sc_intr_number = -1; | |||
157 | sc->sc_intr_pipe = NULL((void *)0); | |||
158 | sc->sc_enabled = sc->sc_isize = 0; | |||
159 | ||||
160 | /* Copy the default scale values to each softc */ | |||
161 | bcopy(&def_scale, &sc->sc_tsscale, sizeof(sc->sc_tsscale)); | |||
162 | ||||
163 | /* get the config descriptor */ | |||
164 | cdesc = usbd_get_config_descriptor(sc->sc_udev); | |||
165 | if (cdesc == NULL((void *)0)) { | |||
166 | printf("%s: failed to get configuration descriptor\n", | |||
167 | sc->sc_dev.dv_xname); | |||
168 | usbd_deactivate(sc->sc_udev); | |||
169 | return; | |||
170 | } | |||
171 | ||||
172 | /* get the interface */ | |||
173 | if (usbd_device2interface_handle(uaa->device, 0, &sc->sc_iface) != 0) { | |||
174 | printf("%s: failed to get interface\n", | |||
175 | sc->sc_dev.dv_xname); | |||
176 | usbd_deactivate(sc->sc_udev); | |||
177 | return; | |||
178 | } | |||
179 | ||||
180 | /* Find the interrupt endpoint */ | |||
181 | id = usbd_get_interface_descriptor(sc->sc_iface); | |||
182 | ||||
183 | for (i = 0; i < id->bNumEndpoints; i++) { | |||
184 | ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); | |||
185 | if (ed == NULL((void *)0)) { | |||
186 | printf("%s: no endpoint descriptor for %d\n", | |||
187 | sc->sc_dev.dv_xname, i); | |||
188 | usbd_deactivate(sc->sc_udev); | |||
189 | return; | |||
190 | } | |||
191 | ||||
192 | if (UE_GET_DIR(ed->bEndpointAddress)((ed->bEndpointAddress) & 0x80) == UE_DIR_IN0x80 && | |||
193 | UE_GET_XFERTYPE(ed->bmAttributes)((ed->bmAttributes) & 0x03) == UE_INTERRUPT0x03) { | |||
194 | sc->sc_intr_number = ed->bEndpointAddress; | |||
195 | sc->sc_isize = UGETW(ed->wMaxPacketSize)(*(u_int16_t *)(ed->wMaxPacketSize)); | |||
196 | } | |||
197 | } | |||
198 | ||||
199 | if (sc->sc_intr_number== -1) { | |||
200 | printf("%s: Could not find interrupt in\n", | |||
201 | sc->sc_dev.dv_xname); | |||
202 | usbd_deactivate(sc->sc_udev); | |||
203 | return; | |||
204 | } | |||
205 | ||||
206 | a.accessops = &uts_accessops; | |||
207 | a.accesscookie = sc; | |||
208 | ||||
209 | sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint)config_found_sm((self), (&a), (wsmousedevprint), ((void * )0)); | |||
210 | } | |||
211 | ||||
212 | int | |||
213 | uts_detach(struct device *self, int flags) | |||
214 | { | |||
215 | struct uts_softc *sc = (struct uts_softc *)self; | |||
216 | int rv = 0; | |||
217 | ||||
218 | if (sc->sc_intr_pipe != NULL((void *)0)) { | |||
219 | usbd_close_pipe(sc->sc_intr_pipe); | |||
220 | sc->sc_intr_pipe = NULL((void *)0); | |||
221 | } | |||
222 | ||||
223 | if (sc->sc_wsmousedev != NULL((void *)0)) { | |||
224 | rv = config_detach(sc->sc_wsmousedev, flags); | |||
225 | sc->sc_wsmousedev = NULL((void *)0); | |||
226 | } | |||
227 | ||||
228 | return (rv); | |||
229 | } | |||
230 | ||||
231 | int | |||
232 | uts_activate(struct device *self, int act) | |||
233 | { | |||
234 | struct uts_softc *sc = (struct uts_softc *)self; | |||
235 | int rv = 0; | |||
236 | ||||
237 | switch (act) { | |||
238 | case DVACT_DEACTIVATE1: | |||
239 | if (sc->sc_wsmousedev != NULL((void *)0)) | |||
240 | rv = config_deactivate(sc->sc_wsmousedev); | |||
241 | usbd_deactivate(sc->sc_udev); | |||
242 | break; | |||
243 | } | |||
244 | ||||
245 | return (rv); | |||
246 | } | |||
247 | ||||
248 | int | |||
249 | uts_enable(void *v) | |||
250 | { | |||
251 | struct uts_softc *sc = v; | |||
252 | int err; | |||
253 | ||||
254 | if (usbd_is_dying(sc->sc_udev)) | |||
255 | return (EIO5); | |||
256 | ||||
257 | if (sc->sc_enabled) | |||
258 | return (EBUSY16); | |||
259 | ||||
260 | if (sc->sc_isize == 0) | |||
261 | return (0); | |||
262 | sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV102, M_WAITOK0x0001); | |||
263 | err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_intr_number, | |||
264 | USBD_SHORT_XFER_OK0x04, &sc->sc_intr_pipe, sc, sc->sc_ibuf, | |||
265 | sc->sc_isize, uts_intr, USBD_DEFAULT_INTERVAL(-1)); | |||
266 | if (err) { | |||
267 | free(sc->sc_ibuf, M_USBDEV102, sc->sc_isize); | |||
268 | sc->sc_intr_pipe = NULL((void *)0); | |||
269 | return (EIO5); | |||
270 | } | |||
271 | ||||
272 | sc->sc_enabled = 1; | |||
273 | sc->sc_buttons = 0; | |||
274 | ||||
275 | return (0); | |||
276 | } | |||
277 | ||||
278 | void | |||
279 | uts_disable(void *v) | |||
280 | { | |||
281 | struct uts_softc *sc = v; | |||
282 | ||||
283 | if (!sc->sc_enabled) { | |||
284 | printf("uts_disable: already disabled!\n"); | |||
285 | return; | |||
286 | } | |||
287 | ||||
288 | /* Disable interrupts. */ | |||
289 | if (sc->sc_intr_pipe != NULL((void *)0)) { | |||
290 | usbd_close_pipe(sc->sc_intr_pipe); | |||
291 | sc->sc_intr_pipe = NULL((void *)0); | |||
292 | } | |||
293 | ||||
294 | if (sc->sc_ibuf != NULL((void *)0)) { | |||
295 | free(sc->sc_ibuf, M_USBDEV102, sc->sc_isize); | |||
296 | sc->sc_ibuf = NULL((void *)0); | |||
297 | } | |||
298 | ||||
299 | sc->sc_enabled = 0; | |||
300 | } | |||
301 | ||||
302 | int | |||
303 | uts_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *l) | |||
304 | { | |||
305 | int error = 0; | |||
306 | struct uts_softc *sc = v; | |||
307 | struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data; | |||
308 | ||||
309 | DPRINTF(("uts_ioctl(%zu, '%c', %zu)\n", | |||
310 | IOCPARM_LEN(cmd), (int) IOCGROUP(cmd), cmd & 0xff)); | |||
311 | ||||
312 | switch (cmd) { | |||
313 | case WSMOUSEIO_SCALIBCOORDS((unsigned long)0x80000000 | ((sizeof(struct wsmouse_calibcoords ) & 0x1fff) << 16) | ((('W')) << 8) | ((36))): | |||
314 | if (!(wsmc->minx >= -32768 && wsmc->maxx >= 0 && | |||
315 | wsmc->miny >= -32768 && wsmc->maxy >= 0 && | |||
316 | wsmc->resx >= 0 && wsmc->resy >= 0 && | |||
317 | wsmc->minx < 32768 && wsmc->maxx < 32768 && | |||
318 | wsmc->miny < 32768 && wsmc->maxy < 32768 && | |||
319 | (wsmc->maxx - wsmc->minx) != 0 && | |||
320 | (wsmc->maxy - wsmc->miny) != 0 && | |||
321 | wsmc->resx < 32768 && wsmc->resy < 32768 && | |||
322 | wsmc->swapxy >= 0 && wsmc->swapxy <= 1 && | |||
323 | wsmc->samplelen >= 0 && wsmc->samplelen <= 1)) | |||
324 | return (EINVAL22); | |||
325 | ||||
326 | sc->sc_tsscale.minx = wsmc->minx; | |||
327 | sc->sc_tsscale.maxx = wsmc->maxx; | |||
328 | sc->sc_tsscale.miny = wsmc->miny; | |||
329 | sc->sc_tsscale.maxy = wsmc->maxy; | |||
330 | sc->sc_tsscale.swapxy = wsmc->swapxy; | |||
331 | sc->sc_tsscale.resx = wsmc->resx; | |||
332 | sc->sc_tsscale.resy = wsmc->resy; | |||
333 | sc->sc_rawmode = wsmc->samplelen; | |||
334 | break; | |||
335 | case WSMOUSEIO_GCALIBCOORDS((unsigned long)0x40000000 | ((sizeof(struct wsmouse_calibcoords ) & 0x1fff) << 16) | ((('W')) << 8) | ((37))): | |||
336 | wsmc->minx = sc->sc_tsscale.minx; | |||
337 | wsmc->maxx = sc->sc_tsscale.maxx; | |||
338 | wsmc->miny = sc->sc_tsscale.miny; | |||
339 | wsmc->maxy = sc->sc_tsscale.maxy; | |||
340 | wsmc->swapxy = sc->sc_tsscale.swapxy; | |||
341 | wsmc->resx = sc->sc_tsscale.resx; | |||
342 | wsmc->resy = sc->sc_tsscale.resy; | |||
343 | wsmc->samplelen = sc->sc_rawmode; | |||
344 | break; | |||
345 | case WSMOUSEIO_GTYPE((unsigned long)0x40000000 | ((sizeof(u_int) & 0x1fff) << 16) | ((('W')) << 8) | ((32))): | |||
346 | *(u_int *)data = WSMOUSE_TYPE_TPANEL6; | |||
347 | break; | |||
348 | default: | |||
349 | error = ENOTTY25; | |||
350 | break; | |||
351 | } | |||
352 | ||||
353 | return (error); | |||
354 | } | |||
355 | ||||
356 | void | |||
357 | uts_get_pos(void *addr, struct uts_pos *tp) | |||
358 | { | |||
359 | struct uts_softc *sc = addr; | |||
360 | u_char *p = sc->sc_ibuf; | |||
361 | int down, x, y, z; | |||
362 | ||||
363 | switch (sc->sc_product) { | |||
364 | case USB_PRODUCT_FTDI_ITM_TOUCH0xf9e9: | |||
365 | down = (~p[7] & 0x20); | |||
366 | x = ((p[0] & 0x1f) << 7) | (p[3] & 0x7f); | |||
367 | /* Invert the Y coordinate */ | |||
368 | y = 0x0fff - abs(((p[1] & 0x1f) << 7) | (p[4] & 0x7f)); | |||
369 | z = ((p[2] & 0x1) << 7) | (p[5] & 0x7f); | |||
370 | sc->sc_pkts = 0x8; | |||
371 | break; | |||
372 | case USB_PRODUCT_EGALAX_TPANEL0x0001: | |||
373 | case USB_PRODUCT_EGALAX_TPANEL20x0002: | |||
374 | /* | |||
375 | * eGalax and Gunze USB touch panels have the same device ID, | |||
376 | * so decide upon the vendor ID. | |||
377 | */ | |||
378 | switch (sc->sc_vendor) { | |||
379 | case USB_VENDOR_EGALAX0x0eef: | |||
380 | down = (p[0] & 0x01); | |||
381 | /* Invert the X coordinate */ | |||
382 | x = 0x07ff - abs(((p[3] & 0x0f) << 7) | (p[4] & 0x7f)); | |||
383 | y = ((p[1] & 0x0f) << 7) | (p[2] & 0x7f); | |||
384 | z = down; | |||
385 | sc->sc_pkts = 0x5; | |||
386 | break; | |||
387 | case USB_VENDOR_GUNZE0x0637: | |||
388 | down = (~p[7] & 0x20); | |||
389 | /* Invert the X coordinate */ | |||
390 | x = 0x0fff - abs(((p[0] & 0x1f) << 7) | (p[2] & 0x7f)); | |||
391 | y = ((p[1] & 0x1f) << 7) | (p[3] & 0x7f); | |||
392 | z = (down != 0); | |||
393 | sc->sc_pkts = 0x4; | |||
394 | break; | |||
395 | } | |||
396 | break; | |||
397 | } | |||
398 | ||||
399 | DPRINTF(("%s: down = 0x%x, sc->sc_pkts = 0x%x\n", | |||
400 | sc->sc_dev.dv_xname, down, sc->sc_pkts)); | |||
401 | ||||
402 | /* x/y values are not reliable if there is no pressure */ | |||
403 | if (down) { | |||
| ||||
404 | if (sc->sc_tsscale.swapxy && !sc->sc_rawmode) { | |||
405 | /* Swap X/Y-Axis */ | |||
406 | tp->y = x; | |||
407 | tp->x = y; | |||
408 | } else { | |||
409 | tp->x = x; | |||
410 | tp->y = y; | |||
411 | } | |||
412 | if (!sc->sc_rawmode && | |||
413 | (sc->sc_tsscale.maxx - sc->sc_tsscale.minx) != 0 && | |||
414 | (sc->sc_tsscale.maxy - sc->sc_tsscale.miny) != 0) { | |||
415 | /* Scale down to the screen resolution. */ | |||
416 | tp->x = ((tp->x - sc->sc_tsscale.minx) * | |||
417 | sc->sc_tsscale.resx) / | |||
418 | (sc->sc_tsscale.maxx - sc->sc_tsscale.minx); | |||
419 | tp->y = ((tp->y - sc->sc_tsscale.miny) * | |||
420 | sc->sc_tsscale.resy) / | |||
421 | (sc->sc_tsscale.maxy - sc->sc_tsscale.miny); | |||
422 | } | |||
423 | } else { | |||
424 | tp->x = sc->sc_oldx; | |||
425 | tp->y = sc->sc_oldy; | |||
426 | } | |||
427 | tp->z = z; | |||
428 | tp->down = down; | |||
429 | } | |||
430 | ||||
431 | void | |||
432 | uts_intr(struct usbd_xfer *xfer, void *addr, usbd_status status) | |||
433 | { | |||
434 | struct uts_softc *sc = addr; | |||
435 | u_int32_t len; | |||
436 | int s; | |||
437 | struct uts_pos tp; | |||
438 | ||||
439 | if (status == USBD_CANCELLED) | |||
| ||||
440 | return; | |||
441 | ||||
442 | if (status != USBD_NORMAL_COMPLETION) { | |||
443 | printf("%s: status %d\n", sc->sc_dev.dv_xname, status); | |||
444 | if (status == USBD_STALLED) | |||
445 | usbd_clear_endpoint_stall_async(sc->sc_intr_pipe); | |||
446 | return; | |||
447 | } | |||
448 | ||||
449 | usbd_get_xfer_status(xfer, NULL((void *)0), NULL((void *)0), &len, NULL((void *)0)); | |||
450 | ||||
451 | s = spltty()splraise(0x9); | |||
452 | ||||
453 | uts_get_pos(sc, &tp); | |||
454 | ||||
455 | if (len != sc->sc_pkts) { | |||
456 | DPRINTF(("%s: bad input length %d != %d\n", | |||
457 | sc->sc_dev.dv_xname, len, sc->sc_isize)); | |||
458 | splx(s)spllower(s); | |||
459 | return; | |||
460 | } | |||
461 | ||||
462 | DPRINTF(("%s: tp.down = %d, tp.z = %d, tp.x = %d, tp.y = %d\n", | |||
463 | sc->sc_dev.dv_xname, tp.down, tp.z, tp.x, tp.y)); | |||
464 | ||||
465 | WSMOUSE_TOUCH(sc->sc_wsmousedev, tp.down, tp.x, tp.y, tp.z, 0)do { wsmouse_buttons((sc->sc_wsmousedev), (tp.down)); wsmouse_position ((sc->sc_wsmousedev), (tp.x), (tp.y)); wsmouse_touch((sc-> sc_wsmousedev), (tp.z), (0)); wsmouse_input_sync(sc->sc_wsmousedev ); } while (0); | |||
466 | sc->sc_oldy = tp.y; | |||
467 | sc->sc_oldx = tp.x; | |||
468 | ||||
469 | splx(s)spllower(s); | |||
470 | } |