1208554Sglebius/*-
2208554Sglebius * Copyright 2010, Gleb Smirnoff <glebius@FreeBSD.org>
3208554Sglebius * All rights reserved.
4208554Sglebius *
5208554Sglebius * Redistribution and use in source and binary forms, with or without
6208554Sglebius * modification, are permitted provided that the following conditions
7208554Sglebius * are met:
8208554Sglebius * 1. Redistributions of source code must retain the above copyright
9208554Sglebius *    notice, this list of conditions and the following disclaimer.
10208554Sglebius * 2. Redistributions in binary form must reproduce the above copyright
11208554Sglebius *    notice, this list of conditions and the following disclaimer in the
12208554Sglebius *    documentation and/or other materials provided with the distribution.
13208554Sglebius *
14208554Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15208554Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16208554Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17208554Sglebius * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18208554Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19208554Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20208554Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21208554Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22208554Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23208554Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24208554Sglebius * SUCH DAMAGE.
25208554Sglebius *
26208554Sglebius * $FreeBSD$
27208554Sglebius */
28208554Sglebius
29208554Sglebius/*
30208554Sglebius *  http://home.eeti.com.tw/web20/drivers/Software%20Programming%20Guide_v2.0.pdf
31208554Sglebius */
32208554Sglebius
33208554Sglebius#include <sys/param.h>
34208554Sglebius#include <sys/bus.h>
35208554Sglebius#include <sys/callout.h>
36208554Sglebius#include <sys/conf.h>
37208554Sglebius#include <sys/kernel.h>
38208554Sglebius#include <sys/lock.h>
39208554Sglebius#include <sys/module.h>
40208554Sglebius#include <sys/mutex.h>
41208554Sglebius#include <sys/sysctl.h>
42208554Sglebius#include <sys/systm.h>
43208554Sglebius
44208554Sglebius#include <dev/usb/usb.h>
45208554Sglebius#include <dev/usb/usbdi.h>
46208554Sglebius#include <dev/usb/usbdi_util.h>
47208554Sglebius#include <dev/usb/usbhid.h>
48208554Sglebius#include "usbdevs.h"
49208554Sglebius
50208554Sglebius#include <sys/ioccom.h>
51208554Sglebius#include <sys/fcntl.h>
52208554Sglebius#include <sys/tty.h>
53208554Sglebius
54208554Sglebius#define USB_DEBUG_VAR uep_debug
55208554Sglebius#include <dev/usb/usb_debug.h>
56208554Sglebius
57208554Sglebius#ifdef USB_DEBUG
58208554Sglebiusstatic int uep_debug = 0;
59208554Sglebius
60248085Smariusstatic SYSCTL_NODE(_hw_usb, OID_AUTO, uep, CTLFLAG_RW, 0, "USB uep");
61208554SglebiusSYSCTL_INT(_hw_usb_uep, OID_AUTO, debug, CTLFLAG_RW,
62208554Sglebius    &uep_debug, 0, "Debug level");
63208554Sglebius#endif
64208554Sglebius
65208554Sglebius#define UEP_MAX_X		2047
66208554Sglebius#define UEP_MAX_Y		2047
67208554Sglebius
68208554Sglebius#define UEP_DOWN		0x01
69208554Sglebius#define UEP_PACKET_LEN_MAX	16
70208554Sglebius#define UEP_PACKET_LEN_REPORT	5
71208554Sglebius#define UEP_PACKET_LEN_REPORT2	6
72208554Sglebius#define UEP_PACKET_DIAG		0x0a
73208554Sglebius#define UEP_PACKET_REPORT_MASK		0xe0
74208554Sglebius#define UEP_PACKET_REPORT		0x80
75208554Sglebius#define UEP_PACKET_REPORT_PRESSURE	0xc0
76208554Sglebius#define UEP_PACKET_REPORT_PLAYER	0xa0
77208554Sglebius#define	UEP_PACKET_LEN_MASK
78208554Sglebius
79208554Sglebius#define UEP_FIFO_BUF_SIZE	8	/* bytes */
80208554Sglebius#define UEP_FIFO_QUEUE_MAXLEN	50	/* units */
81208554Sglebius
82208554Sglebiusenum {
83208554Sglebius	UEP_INTR_DT,
84208554Sglebius	UEP_N_TRANSFER,
85208554Sglebius};
86208554Sglebius
87208554Sglebiusstruct uep_softc {
88208554Sglebius	struct mtx mtx;
89208554Sglebius
90208554Sglebius	struct usb_xfer *xfer[UEP_N_TRANSFER];
91208554Sglebius	struct usb_fifo_sc fifo;
92208554Sglebius
93208554Sglebius	u_int		pollrate;
94208554Sglebius	u_int		state;
95208554Sglebius#define UEP_ENABLED	0x01
96208554Sglebius
97208554Sglebius	/* Reassembling buffer. */
98208554Sglebius	u_char		buf[UEP_PACKET_LEN_MAX];
99208554Sglebius	uint8_t		buf_len;
100208554Sglebius};
101208554Sglebius
102208554Sglebiusstatic usb_callback_t uep_intr_callback;
103208554Sglebius
104208554Sglebiusstatic device_probe_t	uep_probe;
105208554Sglebiusstatic device_attach_t	uep_attach;
106208554Sglebiusstatic device_detach_t	uep_detach;
107208554Sglebius
108208554Sglebiusstatic usb_fifo_cmd_t	uep_start_read;
109208554Sglebiusstatic usb_fifo_cmd_t	uep_stop_read;
110208554Sglebiusstatic usb_fifo_open_t	uep_open;
111208554Sglebiusstatic usb_fifo_close_t	uep_close;
112208554Sglebius
113208554Sglebiusstatic void uep_put_queue(struct uep_softc *, u_char *);
114208554Sglebius
115208554Sglebiusstatic struct usb_fifo_methods uep_fifo_methods = {
116208554Sglebius	.f_open = &uep_open,
117208554Sglebius	.f_close = &uep_close,
118208554Sglebius	.f_start_read = &uep_start_read,
119208554Sglebius	.f_stop_read = &uep_stop_read,
120208554Sglebius	.basename[0] = "uep",
121208554Sglebius};
122208554Sglebius
123208554Sglebiusstatic int
124208554Sglebiusget_pkt_len(u_char *buf)
125208554Sglebius{
126208554Sglebius	if (buf[0] == UEP_PACKET_DIAG) {
127208554Sglebius		int len;
128208554Sglebius
129208554Sglebius		len = buf[1] + 2;
130208554Sglebius		if (len > UEP_PACKET_LEN_MAX) {
131208554Sglebius			DPRINTF("bad packet len %u\n", len);
132208554Sglebius			return (UEP_PACKET_LEN_MAX);
133208554Sglebius		}
134208554Sglebius
135208554Sglebius		return (len);
136208554Sglebius	}
137208554Sglebius
138208554Sglebius	switch (buf[0] & UEP_PACKET_REPORT_MASK) {
139208554Sglebius	case UEP_PACKET_REPORT:
140208554Sglebius		return (UEP_PACKET_LEN_REPORT);
141208554Sglebius	case UEP_PACKET_REPORT_PRESSURE:
142208554Sglebius	case UEP_PACKET_REPORT_PLAYER:
143208554Sglebius	case UEP_PACKET_REPORT_PRESSURE | UEP_PACKET_REPORT_PLAYER:
144208554Sglebius		return (UEP_PACKET_LEN_REPORT2);
145208554Sglebius	default:
146208554Sglebius		DPRINTF("bad packet len 0\n");
147208554Sglebius		return (0);
148208554Sglebius	}
149208554Sglebius}
150208554Sglebius
151208554Sglebiusstatic void
152208554Sglebiusuep_process_pkt(struct uep_softc *sc, u_char *buf)
153208554Sglebius{
154208554Sglebius	int32_t x, y;
155208554Sglebius
156208554Sglebius	if ((buf[0] & 0xFE) != 0x80) {
157208554Sglebius		DPRINTF("bad input packet format 0x%.2x\n", buf[0]);
158208554Sglebius		return;
159208554Sglebius	}
160208554Sglebius
161208554Sglebius	/*
162208554Sglebius	 * Packet format is 5 bytes:
163208554Sglebius	 *
164208554Sglebius	 * 1000000T
165208554Sglebius	 * 0000AAAA
166208554Sglebius	 * 0AAAAAAA
167208554Sglebius	 * 0000BBBB
168208554Sglebius	 * 0BBBBBBB
169208554Sglebius	 *
170208554Sglebius	 * T: 1=touched 0=not touched
171208554Sglebius	 * A: bits of axis A position, MSB to LSB
172208554Sglebius	 * B: bits of axis B position, MSB to LSB
173208554Sglebius	 *
174208554Sglebius	 * For the unit I have, which is CTF1020-S from CarTFT.com,
175208554Sglebius	 * A = X and B = Y. But in NetBSD uep(4) it is other way round :)
176208554Sglebius	 *
177208554Sglebius	 * The controller sends a stream of T=1 events while the
178208554Sglebius	 * panel is touched, followed by a single T=0 event.
179208554Sglebius	 *
180208554Sglebius	 */
181208554Sglebius
182208554Sglebius	x = (buf[1] << 7) | buf[2];
183208554Sglebius	y = (buf[3] << 7) | buf[4];
184208554Sglebius
185208554Sglebius	DPRINTFN(2, "x %u y %u\n", x, y);
186208554Sglebius
187208554Sglebius	uep_put_queue(sc, buf);
188208554Sglebius}
189208554Sglebius
190208554Sglebiusstatic void
191208554Sglebiusuep_intr_callback(struct usb_xfer *xfer, usb_error_t error)
192208554Sglebius{
193208554Sglebius	struct uep_softc *sc = usbd_xfer_softc(xfer);
194208554Sglebius	int len;
195208554Sglebius
196208554Sglebius	usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
197208554Sglebius
198208554Sglebius	switch (USB_GET_STATE(xfer)) {
199208554Sglebius	case USB_ST_TRANSFERRED:
200208554Sglebius	    {
201208554Sglebius		struct usb_page_cache *pc;
202208554Sglebius		u_char buf[17], *p;
203208554Sglebius		int pkt_len;
204208554Sglebius
205235000Shselasky		if (len > (int)sizeof(buf)) {
206208554Sglebius			DPRINTF("bad input length %d\n", len);
207208554Sglebius			goto tr_setup;
208208554Sglebius		}
209208554Sglebius
210208554Sglebius		pc = usbd_xfer_get_frame(xfer, 0);
211208554Sglebius		usbd_copy_out(pc, 0, buf, len);
212208554Sglebius
213208554Sglebius		/*
214208554Sglebius		 * The below code mimics Linux a lot. I don't know
215208554Sglebius		 * why NetBSD reads complete packets, but we need
216208554Sglebius		 * to reassamble 'em like Linux does (tries?).
217208554Sglebius		 */
218208554Sglebius		if (sc->buf_len > 0) {
219208554Sglebius			int res;
220208554Sglebius
221208554Sglebius			if (sc->buf_len == 1)
222208554Sglebius				sc->buf[1] = buf[0];
223208554Sglebius
224208554Sglebius			if ((pkt_len = get_pkt_len(sc->buf)) == 0)
225208554Sglebius				goto tr_setup;
226208554Sglebius
227208554Sglebius			res = pkt_len - sc->buf_len;
228208554Sglebius			memcpy(sc->buf + sc->buf_len, buf, res);
229208554Sglebius			uep_process_pkt(sc, sc->buf);
230208554Sglebius			sc->buf_len = 0;
231208554Sglebius
232208554Sglebius			p = buf + res;
233208554Sglebius			len -= res;
234208554Sglebius		} else
235208554Sglebius			p = buf;
236208554Sglebius
237208554Sglebius		if (len == 1) {
238208554Sglebius			sc->buf[0] = buf[0];
239208554Sglebius			sc->buf_len = 1;
240208554Sglebius
241208554Sglebius			goto tr_setup;
242208554Sglebius		}
243208554Sglebius
244208554Sglebius		while (len > 0) {
245208554Sglebius			if ((pkt_len = get_pkt_len(p)) == 0)
246208554Sglebius				goto tr_setup;
247208554Sglebius
248208554Sglebius			/* full packet: process */
249208554Sglebius			if (pkt_len <= len) {
250208554Sglebius				uep_process_pkt(sc, p);
251208554Sglebius			} else {
252208554Sglebius				/* incomplete packet: save in buffer */
253208554Sglebius				memcpy(sc->buf, p, len);
254208554Sglebius				sc->buf_len = len;
255208554Sglebius			}
256208554Sglebius			p += pkt_len;
257208554Sglebius			len -= pkt_len;
258208554Sglebius		}
259208554Sglebius	    }
260208554Sglebius	case USB_ST_SETUP:
261208554Sglebius	tr_setup:
262208554Sglebius		/* check if we can put more data into the FIFO */
263208554Sglebius		if (usb_fifo_put_bytes_max(sc->fifo.fp[USB_FIFO_RX]) != 0) {
264208554Sglebius			usbd_xfer_set_frame_len(xfer, 0,
265208554Sglebius			    usbd_xfer_max_len(xfer));
266208554Sglebius			usbd_transfer_submit(xfer);
267208554Sglebius                }
268208554Sglebius		break;
269208554Sglebius
270208554Sglebius	default:
271208554Sglebius		if (error != USB_ERR_CANCELLED) {
272208554Sglebius			/* try clear stall first */
273208554Sglebius			usbd_xfer_set_stall(xfer);
274208554Sglebius			goto tr_setup;
275208554Sglebius		}
276208554Sglebius		break;
277208554Sglebius	}
278208554Sglebius}
279208554Sglebius
280208554Sglebiusstatic const struct usb_config uep_config[UEP_N_TRANSFER] = {
281208554Sglebius	[UEP_INTR_DT] = {
282208554Sglebius		.type = UE_INTERRUPT,
283208554Sglebius		.endpoint = UE_ADDR_ANY,
284208554Sglebius		.direction = UE_DIR_IN,
285208554Sglebius		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
286208554Sglebius		.bufsize = 0,   /* use wMaxPacketSize */
287208554Sglebius		.callback = &uep_intr_callback,
288208554Sglebius	},
289208554Sglebius};
290208554Sglebius
291223521Shselaskystatic const STRUCT_USB_HOST_ID uep_devs[] = {
292223521Shselasky	{USB_VPI(USB_VENDOR_EGALAX, USB_PRODUCT_EGALAX_TPANEL, 0)},
293223521Shselasky	{USB_VPI(USB_VENDOR_EGALAX, USB_PRODUCT_EGALAX_TPANEL2, 0)},
294223521Shselasky	{USB_VPI(USB_VENDOR_EGALAX2, USB_PRODUCT_EGALAX2_TPANEL, 0)},
295223521Shselasky};
296223521Shselasky
297208554Sglebiusstatic int
298208554Sglebiusuep_probe(device_t dev)
299208554Sglebius{
300208554Sglebius	struct usb_attach_arg *uaa = device_get_ivars(dev);
301208554Sglebius
302208554Sglebius	if (uaa->usb_mode != USB_MODE_HOST)
303208554Sglebius		return (ENXIO);
304223521Shselasky	if (uaa->info.bConfigIndex != 0)
305223521Shselasky		return (ENXIO);
306223521Shselasky	if (uaa->info.bIfaceIndex != 0)
307223521Shselasky		return (ENXIO);
308208554Sglebius
309223521Shselasky	return (usbd_lookup_id_by_uaa(uep_devs, sizeof(uep_devs), uaa));
310208554Sglebius}
311208554Sglebius
312208554Sglebiusstatic int
313208554Sglebiusuep_attach(device_t dev)
314208554Sglebius{
315208554Sglebius	struct usb_attach_arg *uaa = device_get_ivars(dev);
316208554Sglebius	struct uep_softc *sc = device_get_softc(dev);
317208554Sglebius	int error;
318208554Sglebius
319208554Sglebius	device_set_usb_desc(dev);
320208554Sglebius
321208554Sglebius	mtx_init(&sc->mtx, "uep lock", NULL, MTX_DEF);
322208554Sglebius
323208554Sglebius	error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex,
324208554Sglebius	    sc->xfer, uep_config, UEP_N_TRANSFER, sc, &sc->mtx);
325208554Sglebius
326208554Sglebius	if (error) {
327208554Sglebius		DPRINTF("usbd_transfer_setup error=%s\n", usbd_errstr(error));
328208554Sglebius		goto detach;
329208554Sglebius	}
330208554Sglebius
331208554Sglebius	error = usb_fifo_attach(uaa->device, sc, &sc->mtx, &uep_fifo_methods,
332235000Shselasky	    &sc->fifo, device_get_unit(dev), -1, uaa->info.bIfaceIndex,
333208554Sglebius	    UID_ROOT, GID_OPERATOR, 0644);
334208554Sglebius
335208554Sglebius        if (error) {
336208554Sglebius		DPRINTF("usb_fifo_attach error=%s\n", usbd_errstr(error));
337208554Sglebius                goto detach;
338208554Sglebius        }
339208554Sglebius
340208554Sglebius	sc->buf_len = 0;
341208554Sglebius
342208554Sglebius	return (0);
343208554Sglebius
344208554Sglebiusdetach:
345208554Sglebius	uep_detach(dev);
346208554Sglebius
347208554Sglebius	return (ENOMEM); /* XXX */
348208554Sglebius}
349208554Sglebius
350208554Sglebiusstatic int
351208554Sglebiusuep_detach(device_t dev)
352208554Sglebius{
353208554Sglebius	struct uep_softc *sc = device_get_softc(dev);
354208554Sglebius
355208554Sglebius	usb_fifo_detach(&sc->fifo);
356208554Sglebius
357208554Sglebius	usbd_transfer_unsetup(sc->xfer, UEP_N_TRANSFER);
358208554Sglebius
359208554Sglebius	mtx_destroy(&sc->mtx);
360208554Sglebius
361208554Sglebius	return (0);
362208554Sglebius}
363208554Sglebius
364208554Sglebiusstatic void
365208554Sglebiusuep_start_read(struct usb_fifo *fifo)
366208554Sglebius{
367208554Sglebius	struct uep_softc *sc = usb_fifo_softc(fifo);
368208554Sglebius	u_int rate;
369208554Sglebius
370208554Sglebius	if ((rate = sc->pollrate) > 1000)
371208554Sglebius		rate = 1000;
372208554Sglebius
373208554Sglebius	if (rate > 0 && sc->xfer[UEP_INTR_DT] != NULL) {
374208554Sglebius		usbd_transfer_stop(sc->xfer[UEP_INTR_DT]);
375208554Sglebius		usbd_xfer_set_interval(sc->xfer[UEP_INTR_DT], 1000 / rate);
376208554Sglebius		sc->pollrate = 0;
377208554Sglebius	}
378208554Sglebius
379208554Sglebius	usbd_transfer_start(sc->xfer[UEP_INTR_DT]);
380208554Sglebius}
381208554Sglebius
382208554Sglebiusstatic void
383208554Sglebiusuep_stop_read(struct usb_fifo *fifo)
384208554Sglebius{
385208554Sglebius	struct uep_softc *sc = usb_fifo_softc(fifo);
386208554Sglebius
387208554Sglebius	usbd_transfer_stop(sc->xfer[UEP_INTR_DT]);
388208554Sglebius}
389208554Sglebius
390208554Sglebiusstatic void
391208554Sglebiusuep_put_queue(struct uep_softc *sc, u_char *buf)
392208554Sglebius{
393208554Sglebius	usb_fifo_put_data_linear(sc->fifo.fp[USB_FIFO_RX], buf,
394208554Sglebius	    UEP_PACKET_LEN_REPORT, 1);
395208554Sglebius}
396208554Sglebius
397208554Sglebiusstatic int
398208554Sglebiusuep_open(struct usb_fifo *fifo, int fflags)
399208554Sglebius{
400208554Sglebius	if (fflags & FREAD) {
401208554Sglebius		struct uep_softc *sc = usb_fifo_softc(fifo);
402208554Sglebius
403208554Sglebius		if (sc->state & UEP_ENABLED)
404208554Sglebius			return (EBUSY);
405208554Sglebius		if (usb_fifo_alloc_buffer(fifo, UEP_FIFO_BUF_SIZE,
406208554Sglebius		    UEP_FIFO_QUEUE_MAXLEN))
407208554Sglebius			return (ENOMEM);
408208554Sglebius
409208554Sglebius		sc->state |= UEP_ENABLED;
410208554Sglebius	}
411208554Sglebius
412208554Sglebius	return (0);
413208554Sglebius}
414208554Sglebius
415208554Sglebiusstatic void
416208554Sglebiusuep_close(struct usb_fifo *fifo, int fflags)
417208554Sglebius{
418208554Sglebius	if (fflags & FREAD) {
419208554Sglebius		struct uep_softc *sc = usb_fifo_softc(fifo);
420208554Sglebius
421208554Sglebius		sc->state &= ~(UEP_ENABLED);
422208554Sglebius		usb_fifo_free_buffer(fifo);
423208554Sglebius	}
424208554Sglebius}
425208554Sglebius
426208554Sglebiusstatic devclass_t uep_devclass;
427208554Sglebius
428208554Sglebiusstatic device_method_t uep_methods[] = {
429208554Sglebius	DEVMETHOD(device_probe, uep_probe),
430208554Sglebius       	DEVMETHOD(device_attach, uep_attach),
431208554Sglebius	DEVMETHOD(device_detach, uep_detach),
432208554Sglebius	{ 0, 0 },
433208554Sglebius};
434208554Sglebius
435208554Sglebiusstatic driver_t uep_driver = {
436208554Sglebius	.name = "uep",
437208554Sglebius	.methods = uep_methods,
438208554Sglebius	.size = sizeof(struct uep_softc),
439208554Sglebius};
440208554Sglebius
441208554SglebiusDRIVER_MODULE(uep, uhub, uep_driver, uep_devclass, NULL, NULL);
442208554SglebiusMODULE_DEPEND(uep, usb, 1, 1, 1);
443212122SthompsaMODULE_VERSION(uep, 1);
444