xref: /freebsd/sys/dev/usb/input/uep.c (revision 780fb4a2)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright 2010, Gleb Smirnoff <glebius@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30 
31 /*
32  *  http://www.eeti.com.tw/pdf/Software%20Programming%20Guide_v2.0.pdf
33  */
34 
35 #include <sys/param.h>
36 #include <sys/bus.h>
37 #include <sys/callout.h>
38 #include <sys/conf.h>
39 #include <sys/kernel.h>
40 #include <sys/lock.h>
41 #include <sys/module.h>
42 #include <sys/mutex.h>
43 #include <sys/sysctl.h>
44 #include <sys/systm.h>
45 
46 #include <dev/usb/usb.h>
47 #include <dev/usb/usbdi.h>
48 #include <dev/usb/usbdi_util.h>
49 #include <dev/usb/usbhid.h>
50 #include "usbdevs.h"
51 
52 #include <sys/ioccom.h>
53 #include <sys/fcntl.h>
54 #include <sys/tty.h>
55 
56 #define USB_DEBUG_VAR uep_debug
57 #include <dev/usb/usb_debug.h>
58 
59 #ifdef USB_DEBUG
60 static int uep_debug = 0;
61 
62 static SYSCTL_NODE(_hw_usb, OID_AUTO, uep, CTLFLAG_RW, 0, "USB uep");
63 SYSCTL_INT(_hw_usb_uep, OID_AUTO, debug, CTLFLAG_RWTUN,
64     &uep_debug, 0, "Debug level");
65 #endif
66 
67 #define UEP_MAX_X		2047
68 #define UEP_MAX_Y		2047
69 
70 #define UEP_DOWN		0x01
71 #define UEP_PACKET_LEN_MAX	16
72 #define UEP_PACKET_LEN_REPORT	5
73 #define UEP_PACKET_LEN_REPORT2	6
74 #define UEP_PACKET_DIAG		0x0a
75 #define UEP_PACKET_REPORT_MASK		0xe0
76 #define UEP_PACKET_REPORT		0x80
77 #define UEP_PACKET_REPORT_PRESSURE	0xc0
78 #define UEP_PACKET_REPORT_PLAYER	0xa0
79 #define	UEP_PACKET_LEN_MASK
80 
81 #define UEP_FIFO_BUF_SIZE	8	/* bytes */
82 #define UEP_FIFO_QUEUE_MAXLEN	50	/* units */
83 
84 enum {
85 	UEP_INTR_DT,
86 	UEP_N_TRANSFER,
87 };
88 
89 struct uep_softc {
90 	struct mtx mtx;
91 
92 	struct usb_xfer *xfer[UEP_N_TRANSFER];
93 	struct usb_fifo_sc fifo;
94 
95 	u_int		pollrate;
96 	u_int		state;
97 #define UEP_ENABLED	0x01
98 
99 	/* Reassembling buffer. */
100 	u_char		buf[UEP_PACKET_LEN_MAX];
101 	uint8_t		buf_len;
102 };
103 
104 static usb_callback_t uep_intr_callback;
105 
106 static device_probe_t	uep_probe;
107 static device_attach_t	uep_attach;
108 static device_detach_t	uep_detach;
109 
110 static usb_fifo_cmd_t	uep_start_read;
111 static usb_fifo_cmd_t	uep_stop_read;
112 static usb_fifo_open_t	uep_open;
113 static usb_fifo_close_t	uep_close;
114 
115 static void uep_put_queue(struct uep_softc *, u_char *);
116 
117 static struct usb_fifo_methods uep_fifo_methods = {
118 	.f_open = &uep_open,
119 	.f_close = &uep_close,
120 	.f_start_read = &uep_start_read,
121 	.f_stop_read = &uep_stop_read,
122 	.basename[0] = "uep",
123 };
124 
125 static int
126 get_pkt_len(u_char *buf)
127 {
128 	if (buf[0] == UEP_PACKET_DIAG) {
129 		int len;
130 
131 		len = buf[1] + 2;
132 		if (len > UEP_PACKET_LEN_MAX) {
133 			DPRINTF("bad packet len %u\n", len);
134 			return (UEP_PACKET_LEN_MAX);
135 		}
136 
137 		return (len);
138 	}
139 
140 	switch (buf[0] & UEP_PACKET_REPORT_MASK) {
141 	case UEP_PACKET_REPORT:
142 		return (UEP_PACKET_LEN_REPORT);
143 	case UEP_PACKET_REPORT_PRESSURE:
144 	case UEP_PACKET_REPORT_PLAYER:
145 	case UEP_PACKET_REPORT_PRESSURE | UEP_PACKET_REPORT_PLAYER:
146 		return (UEP_PACKET_LEN_REPORT2);
147 	default:
148 		DPRINTF("bad packet len 0\n");
149 		return (0);
150 	}
151 }
152 
153 static void
154 uep_process_pkt(struct uep_softc *sc, u_char *buf)
155 {
156 	int32_t x, y;
157 
158 	if ((buf[0] & 0xFE) != 0x80) {
159 		DPRINTF("bad input packet format 0x%.2x\n", buf[0]);
160 		return;
161 	}
162 
163 	/*
164 	 * Packet format is 5 bytes:
165 	 *
166 	 * 1000000T
167 	 * 0000AAAA
168 	 * 0AAAAAAA
169 	 * 0000BBBB
170 	 * 0BBBBBBB
171 	 *
172 	 * T: 1=touched 0=not touched
173 	 * A: bits of axis A position, MSB to LSB
174 	 * B: bits of axis B position, MSB to LSB
175 	 *
176 	 * For the unit I have, which is CTF1020-S from CarTFT.com,
177 	 * A = X and B = Y. But in NetBSD uep(4) it is other way round :)
178 	 *
179 	 * The controller sends a stream of T=1 events while the
180 	 * panel is touched, followed by a single T=0 event.
181 	 *
182 	 */
183 
184 	x = (buf[1] << 7) | buf[2];
185 	y = (buf[3] << 7) | buf[4];
186 
187 	DPRINTFN(2, "x %u y %u\n", x, y);
188 
189 	uep_put_queue(sc, buf);
190 }
191 
192 static void
193 uep_intr_callback(struct usb_xfer *xfer, usb_error_t error)
194 {
195 	struct uep_softc *sc = usbd_xfer_softc(xfer);
196 	int len;
197 
198 	usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
199 
200 	switch (USB_GET_STATE(xfer)) {
201 	case USB_ST_TRANSFERRED:
202 	    {
203 		struct usb_page_cache *pc;
204 		u_char buf[17], *p;
205 		int pkt_len;
206 
207 		if (len > (int)sizeof(buf)) {
208 			DPRINTF("bad input length %d\n", len);
209 			goto tr_setup;
210 		}
211 
212 		pc = usbd_xfer_get_frame(xfer, 0);
213 		usbd_copy_out(pc, 0, buf, len);
214 
215 		/*
216 		 * The below code mimics Linux a lot. I don't know
217 		 * why NetBSD reads complete packets, but we need
218 		 * to reassamble 'em like Linux does (tries?).
219 		 */
220 		if (sc->buf_len > 0) {
221 			int res;
222 
223 			if (sc->buf_len == 1)
224 				sc->buf[1] = buf[0];
225 
226 			if ((pkt_len = get_pkt_len(sc->buf)) == 0)
227 				goto tr_setup;
228 
229 			res = pkt_len - sc->buf_len;
230 			memcpy(sc->buf + sc->buf_len, buf, res);
231 			uep_process_pkt(sc, sc->buf);
232 			sc->buf_len = 0;
233 
234 			p = buf + res;
235 			len -= res;
236 		} else
237 			p = buf;
238 
239 		if (len == 1) {
240 			sc->buf[0] = buf[0];
241 			sc->buf_len = 1;
242 
243 			goto tr_setup;
244 		}
245 
246 		while (len > 0) {
247 			if ((pkt_len = get_pkt_len(p)) == 0)
248 				goto tr_setup;
249 
250 			/* full packet: process */
251 			if (pkt_len <= len) {
252 				uep_process_pkt(sc, p);
253 			} else {
254 				/* incomplete packet: save in buffer */
255 				memcpy(sc->buf, p, len);
256 				sc->buf_len = len;
257 			}
258 			p += pkt_len;
259 			len -= pkt_len;
260 		}
261 	    }
262 	case USB_ST_SETUP:
263 	tr_setup:
264 		/* check if we can put more data into the FIFO */
265 		if (usb_fifo_put_bytes_max(sc->fifo.fp[USB_FIFO_RX]) != 0) {
266 			usbd_xfer_set_frame_len(xfer, 0,
267 			    usbd_xfer_max_len(xfer));
268 			usbd_transfer_submit(xfer);
269                 }
270 		break;
271 
272 	default:
273 		if (error != USB_ERR_CANCELLED) {
274 			/* try clear stall first */
275 			usbd_xfer_set_stall(xfer);
276 			goto tr_setup;
277 		}
278 		break;
279 	}
280 }
281 
282 static const struct usb_config uep_config[UEP_N_TRANSFER] = {
283 	[UEP_INTR_DT] = {
284 		.type = UE_INTERRUPT,
285 		.endpoint = UE_ADDR_ANY,
286 		.direction = UE_DIR_IN,
287 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
288 		.bufsize = 0,   /* use wMaxPacketSize */
289 		.callback = &uep_intr_callback,
290 	},
291 };
292 
293 static const STRUCT_USB_HOST_ID uep_devs[] = {
294 	{USB_VPI(USB_VENDOR_EGALAX, USB_PRODUCT_EGALAX_TPANEL, 0)},
295 	{USB_VPI(USB_VENDOR_EGALAX, USB_PRODUCT_EGALAX_TPANEL2, 0)},
296 	{USB_VPI(USB_VENDOR_EGALAX2, USB_PRODUCT_EGALAX2_TPANEL, 0)},
297 };
298 
299 static int
300 uep_probe(device_t dev)
301 {
302 	struct usb_attach_arg *uaa = device_get_ivars(dev);
303 
304 	if (uaa->usb_mode != USB_MODE_HOST)
305 		return (ENXIO);
306 	if (uaa->info.bConfigIndex != 0)
307 		return (ENXIO);
308 	if (uaa->info.bIfaceIndex != 0)
309 		return (ENXIO);
310 
311 	return (usbd_lookup_id_by_uaa(uep_devs, sizeof(uep_devs), uaa));
312 }
313 
314 static int
315 uep_attach(device_t dev)
316 {
317 	struct usb_attach_arg *uaa = device_get_ivars(dev);
318 	struct uep_softc *sc = device_get_softc(dev);
319 	int error;
320 
321 	device_set_usb_desc(dev);
322 
323 	mtx_init(&sc->mtx, "uep lock", NULL, MTX_DEF);
324 
325 	error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex,
326 	    sc->xfer, uep_config, UEP_N_TRANSFER, sc, &sc->mtx);
327 
328 	if (error) {
329 		DPRINTF("usbd_transfer_setup error=%s\n", usbd_errstr(error));
330 		goto detach;
331 	}
332 
333 	error = usb_fifo_attach(uaa->device, sc, &sc->mtx, &uep_fifo_methods,
334 	    &sc->fifo, device_get_unit(dev), -1, uaa->info.bIfaceIndex,
335 	    UID_ROOT, GID_OPERATOR, 0644);
336 
337         if (error) {
338 		DPRINTF("usb_fifo_attach error=%s\n", usbd_errstr(error));
339                 goto detach;
340         }
341 
342 	sc->buf_len = 0;
343 
344 	return (0);
345 
346 detach:
347 	uep_detach(dev);
348 
349 	return (ENOMEM); /* XXX */
350 }
351 
352 static int
353 uep_detach(device_t dev)
354 {
355 	struct uep_softc *sc = device_get_softc(dev);
356 
357 	usb_fifo_detach(&sc->fifo);
358 
359 	usbd_transfer_unsetup(sc->xfer, UEP_N_TRANSFER);
360 
361 	mtx_destroy(&sc->mtx);
362 
363 	return (0);
364 }
365 
366 static void
367 uep_start_read(struct usb_fifo *fifo)
368 {
369 	struct uep_softc *sc = usb_fifo_softc(fifo);
370 	u_int rate;
371 
372 	if ((rate = sc->pollrate) > 1000)
373 		rate = 1000;
374 
375 	if (rate > 0 && sc->xfer[UEP_INTR_DT] != NULL) {
376 		usbd_transfer_stop(sc->xfer[UEP_INTR_DT]);
377 		usbd_xfer_set_interval(sc->xfer[UEP_INTR_DT], 1000 / rate);
378 		sc->pollrate = 0;
379 	}
380 
381 	usbd_transfer_start(sc->xfer[UEP_INTR_DT]);
382 }
383 
384 static void
385 uep_stop_read(struct usb_fifo *fifo)
386 {
387 	struct uep_softc *sc = usb_fifo_softc(fifo);
388 
389 	usbd_transfer_stop(sc->xfer[UEP_INTR_DT]);
390 }
391 
392 static void
393 uep_put_queue(struct uep_softc *sc, u_char *buf)
394 {
395 	usb_fifo_put_data_linear(sc->fifo.fp[USB_FIFO_RX], buf,
396 	    UEP_PACKET_LEN_REPORT, 1);
397 }
398 
399 static int
400 uep_open(struct usb_fifo *fifo, int fflags)
401 {
402 	if (fflags & FREAD) {
403 		struct uep_softc *sc = usb_fifo_softc(fifo);
404 
405 		if (sc->state & UEP_ENABLED)
406 			return (EBUSY);
407 		if (usb_fifo_alloc_buffer(fifo, UEP_FIFO_BUF_SIZE,
408 		    UEP_FIFO_QUEUE_MAXLEN))
409 			return (ENOMEM);
410 
411 		sc->state |= UEP_ENABLED;
412 	}
413 
414 	return (0);
415 }
416 
417 static void
418 uep_close(struct usb_fifo *fifo, int fflags)
419 {
420 	if (fflags & FREAD) {
421 		struct uep_softc *sc = usb_fifo_softc(fifo);
422 
423 		sc->state &= ~(UEP_ENABLED);
424 		usb_fifo_free_buffer(fifo);
425 	}
426 }
427 
428 static devclass_t uep_devclass;
429 
430 static device_method_t uep_methods[] = {
431 	DEVMETHOD(device_probe, uep_probe),
432        	DEVMETHOD(device_attach, uep_attach),
433 	DEVMETHOD(device_detach, uep_detach),
434 	{ 0, 0 },
435 };
436 
437 static driver_t uep_driver = {
438 	.name = "uep",
439 	.methods = uep_methods,
440 	.size = sizeof(struct uep_softc),
441 };
442 
443 DRIVER_MODULE(uep, uhub, uep_driver, uep_devclass, NULL, NULL);
444 MODULE_DEPEND(uep, usb, 1, 1, 1);
445 MODULE_VERSION(uep, 1);
446 USB_PNP_HOST_INFO(uep_devs);
447