xref: /dflybsd-src/sys/bus/u4b/gadget/g_modem.c (revision 2b3f93ea6d1f70880f3e87f3c2cbe0dc0bfc9332)
1129a2703SSascha Wildner /*-
2129a2703SSascha Wildner  * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
3129a2703SSascha Wildner  *
4129a2703SSascha Wildner  * Redistribution and use in source and binary forms, with or without
5129a2703SSascha Wildner  * modification, are permitted provided that the following conditions
6129a2703SSascha Wildner  * are met:
7129a2703SSascha Wildner  * 1. Redistributions of source code must retain the above copyright
8129a2703SSascha Wildner  *    notice, this list of conditions and the following disclaimer.
9129a2703SSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
10129a2703SSascha Wildner  *    notice, this list of conditions and the following disclaimer in the
11129a2703SSascha Wildner  *    documentation and/or other materials provided with the distribution.
12129a2703SSascha Wildner  *
13129a2703SSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14129a2703SSascha Wildner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15129a2703SSascha Wildner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16129a2703SSascha Wildner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17129a2703SSascha Wildner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18129a2703SSascha Wildner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19129a2703SSascha Wildner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20129a2703SSascha Wildner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21129a2703SSascha Wildner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22129a2703SSascha Wildner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23129a2703SSascha Wildner  * SUCH DAMAGE.
24129a2703SSascha Wildner  *
25129a2703SSascha Wildner  * $FreeBSD: head/sys/dev/usb/gadget/g_modem.c 253618 2013-07-24 18:32:15Z obrien $
26129a2703SSascha Wildner  */
27129a2703SSascha Wildner 
28129a2703SSascha Wildner /*
29129a2703SSascha Wildner  * Comm Class spec:  http://www.usb.org/developers/devclass_docs/usbccs10.pdf
30129a2703SSascha Wildner  *                   http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
31129a2703SSascha Wildner  *                   http://www.usb.org/developers/devclass_docs/cdc_wmc10.zip
32129a2703SSascha Wildner  */
33129a2703SSascha Wildner 
34129a2703SSascha Wildner #include <sys/stdint.h>
35129a2703SSascha Wildner #include <sys/queue.h>
36129a2703SSascha Wildner #include <sys/systm.h>
37129a2703SSascha Wildner #include <sys/kernel.h>
38129a2703SSascha Wildner #include <sys/bus.h>
39129a2703SSascha Wildner #include <sys/linker_set.h>
40129a2703SSascha Wildner #include <sys/module.h>
41129a2703SSascha Wildner #include <sys/lock.h>
42129a2703SSascha Wildner #include <sys/condvar.h>
43129a2703SSascha Wildner #include <sys/sysctl.h>
44129a2703SSascha Wildner #include <sys/unistd.h>
45129a2703SSascha Wildner #include <sys/callout.h>
46129a2703SSascha Wildner #include <sys/malloc.h>
47*2b3f93eaSMatthew Dillon #include <sys/caps.h>
48129a2703SSascha Wildner 
49129a2703SSascha Wildner #include <bus/u4b/usb.h>
50129a2703SSascha Wildner #include <bus/u4b/usb_cdc.h>
51129a2703SSascha Wildner #include <bus/u4b/usbdi.h>
52129a2703SSascha Wildner #include <bus/u4b/usbdi_util.h>
53129a2703SSascha Wildner #include <bus/u4b/usbhid.h>
54129a2703SSascha Wildner #include "usb_if.h"
55129a2703SSascha Wildner 
56129a2703SSascha Wildner #define	USB_DEBUG_VAR g_modem_debug
57129a2703SSascha Wildner #include <bus/u4b/usb_debug.h>
58129a2703SSascha Wildner 
59129a2703SSascha Wildner #include <bus/u4b/gadget/g_modem.h>
60129a2703SSascha Wildner 
61129a2703SSascha Wildner enum {
62129a2703SSascha Wildner 	G_MODEM_INTR_DT,
63129a2703SSascha Wildner 	G_MODEM_BULK_RD,
64129a2703SSascha Wildner 	G_MODEM_BULK_WR,
65129a2703SSascha Wildner 	G_MODEM_N_TRANSFER,
66129a2703SSascha Wildner };
67129a2703SSascha Wildner 
68129a2703SSascha Wildner struct g_modem_softc {
69129a2703SSascha Wildner 	struct lock sc_lock;
70129a2703SSascha Wildner 	struct usb_callout sc_callout;
71129a2703SSascha Wildner 	struct usb_callout sc_watchdog;
72129a2703SSascha Wildner 	struct usb_xfer *sc_xfer[G_MODEM_N_TRANSFER];
73129a2703SSascha Wildner 
74129a2703SSascha Wildner 	int	sc_mode;
75129a2703SSascha Wildner 	int	sc_tx_busy;
76129a2703SSascha Wildner 	int	sc_pattern_len;
77129a2703SSascha Wildner 	int	sc_throughput;
78129a2703SSascha Wildner 	int	sc_tx_interval;
79129a2703SSascha Wildner 
80129a2703SSascha Wildner 	char	sc_pattern[G_MODEM_MAX_STRLEN];
81129a2703SSascha Wildner 
82129a2703SSascha Wildner 	uint16_t sc_data_len;
83129a2703SSascha Wildner 
84129a2703SSascha Wildner 	uint8_t sc_data_buf[G_MODEM_BUFSIZE];
85129a2703SSascha Wildner 	uint8_t	sc_line_coding[32];
86129a2703SSascha Wildner 	uint8_t	sc_abstract_state[32];
87129a2703SSascha Wildner };
88129a2703SSascha Wildner 
89129a2703SSascha Wildner static SYSCTL_NODE(_hw_usb, OID_AUTO, g_modem, CTLFLAG_RW, 0, "USB modem gadget");
90129a2703SSascha Wildner 
91129a2703SSascha Wildner #ifdef USB_DEBUG
92129a2703SSascha Wildner static int g_modem_debug = 0;
93129a2703SSascha Wildner 
94129a2703SSascha Wildner SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, debug, CTLFLAG_RW,
95129a2703SSascha Wildner     &g_modem_debug, 0, "Debug level");
96129a2703SSascha Wildner #endif
97129a2703SSascha Wildner 
98129a2703SSascha Wildner static int g_modem_mode = 0;
99129a2703SSascha Wildner 
100129a2703SSascha Wildner SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, mode, CTLFLAG_RW,
101129a2703SSascha Wildner     &g_modem_mode, 0, "Mode selection");
102129a2703SSascha Wildner 
103129a2703SSascha Wildner static int g_modem_pattern_interval = 1000;
104129a2703SSascha Wildner 
105129a2703SSascha Wildner SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, pattern_interval, CTLFLAG_RW,
106129a2703SSascha Wildner     &g_modem_pattern_interval, 0, "Pattern interval in milliseconds");
107129a2703SSascha Wildner 
108129a2703SSascha Wildner static char g_modem_pattern_data[G_MODEM_MAX_STRLEN];
109129a2703SSascha Wildner 
110129a2703SSascha Wildner SYSCTL_STRING(_hw_usb_g_modem, OID_AUTO, pattern, CTLFLAG_RW,
111129a2703SSascha Wildner     &g_modem_pattern_data, sizeof(g_modem_pattern_data), "Data pattern");
112129a2703SSascha Wildner 
113129a2703SSascha Wildner static int g_modem_throughput;
114129a2703SSascha Wildner 
115129a2703SSascha Wildner SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, throughput, CTLFLAG_RD,
116129a2703SSascha Wildner     &g_modem_throughput, sizeof(g_modem_throughput), "Throughput in bytes per second");
117129a2703SSascha Wildner 
118129a2703SSascha Wildner static device_probe_t g_modem_probe;
119129a2703SSascha Wildner static device_attach_t g_modem_attach;
120129a2703SSascha Wildner static device_detach_t g_modem_detach;
121129a2703SSascha Wildner static usb_handle_request_t g_modem_handle_request;
122129a2703SSascha Wildner static usb_callback_t g_modem_intr_callback;
123129a2703SSascha Wildner static usb_callback_t g_modem_bulk_read_callback;
124129a2703SSascha Wildner static usb_callback_t g_modem_bulk_write_callback;
125129a2703SSascha Wildner 
126129a2703SSascha Wildner static void g_modem_timeout(void *arg);
127129a2703SSascha Wildner 
128129a2703SSascha Wildner static devclass_t g_modem_devclass;
129129a2703SSascha Wildner 
130129a2703SSascha Wildner static device_method_t g_modem_methods[] = {
131129a2703SSascha Wildner 	/* USB interface */
132129a2703SSascha Wildner 	DEVMETHOD(usb_handle_request, g_modem_handle_request),
133129a2703SSascha Wildner 
134129a2703SSascha Wildner 	/* Device interface */
135129a2703SSascha Wildner 	DEVMETHOD(device_probe, g_modem_probe),
136129a2703SSascha Wildner 	DEVMETHOD(device_attach, g_modem_attach),
137129a2703SSascha Wildner 	DEVMETHOD(device_detach, g_modem_detach),
138129a2703SSascha Wildner 
139129a2703SSascha Wildner 	DEVMETHOD_END
140129a2703SSascha Wildner };
141129a2703SSascha Wildner 
142129a2703SSascha Wildner static driver_t g_modem_driver = {
143129a2703SSascha Wildner 	.name = "g_modem",
144129a2703SSascha Wildner 	.methods = g_modem_methods,
145129a2703SSascha Wildner 	.size = sizeof(struct g_modem_softc),
146129a2703SSascha Wildner };
147129a2703SSascha Wildner 
148aa6ac96eSSascha Wildner DRIVER_MODULE(g_modem, uhub, g_modem_driver, g_modem_devclass, NULL, NULL);
149129a2703SSascha Wildner MODULE_DEPEND(g_modem, usb, 1, 1, 1);
150129a2703SSascha Wildner 
151129a2703SSascha Wildner static const struct usb_config g_modem_config[G_MODEM_N_TRANSFER] = {
152129a2703SSascha Wildner 
153129a2703SSascha Wildner 	[G_MODEM_INTR_DT] = {
154129a2703SSascha Wildner 		.type = UE_INTERRUPT,
155129a2703SSascha Wildner 		.endpoint = UE_ADDR_ANY,
156129a2703SSascha Wildner 		.direction = UE_DIR_TX,
157129a2703SSascha Wildner 		.flags = {.ext_buffer = 1,.pipe_bof = 1,},
158129a2703SSascha Wildner 		.bufsize = 0,	/* use wMaxPacketSize */
159129a2703SSascha Wildner 		.callback = &g_modem_intr_callback,
160129a2703SSascha Wildner 		.frames = 1,
161129a2703SSascha Wildner 		.usb_mode = USB_MODE_DEVICE,
162129a2703SSascha Wildner 		.if_index = 0,
163129a2703SSascha Wildner 	},
164129a2703SSascha Wildner 
165129a2703SSascha Wildner 	[G_MODEM_BULK_RD] = {
166129a2703SSascha Wildner 		.type = UE_BULK,
167129a2703SSascha Wildner 		.endpoint = UE_ADDR_ANY,
168129a2703SSascha Wildner 		.direction = UE_DIR_RX,
169129a2703SSascha Wildner 		.flags = {.ext_buffer = 1,.pipe_bof = 1,.short_xfer_ok = 1,},
170129a2703SSascha Wildner 		.bufsize = G_MODEM_BUFSIZE,
171129a2703SSascha Wildner 		.callback = &g_modem_bulk_read_callback,
172129a2703SSascha Wildner 		.frames = 1,
173129a2703SSascha Wildner 		.usb_mode = USB_MODE_DEVICE,
174129a2703SSascha Wildner 		.if_index = 1,
175129a2703SSascha Wildner 	},
176129a2703SSascha Wildner 
177129a2703SSascha Wildner 	[G_MODEM_BULK_WR] = {
178129a2703SSascha Wildner 		.type = UE_BULK,
179129a2703SSascha Wildner 		.endpoint = UE_ADDR_ANY,
180129a2703SSascha Wildner 		.direction = UE_DIR_TX,
181129a2703SSascha Wildner 		.flags = {.ext_buffer = 1,.pipe_bof = 1,},
182129a2703SSascha Wildner 		.bufsize = G_MODEM_BUFSIZE,
183129a2703SSascha Wildner 		.callback = &g_modem_bulk_write_callback,
184129a2703SSascha Wildner 		.frames = 1,
185129a2703SSascha Wildner 		.usb_mode = USB_MODE_DEVICE,
186129a2703SSascha Wildner 		.if_index = 1,
187129a2703SSascha Wildner 	},
188129a2703SSascha Wildner };
189129a2703SSascha Wildner 
190129a2703SSascha Wildner static void
g_modem_timeout_reset(struct g_modem_softc * sc)191129a2703SSascha Wildner g_modem_timeout_reset(struct g_modem_softc *sc)
192129a2703SSascha Wildner {
193129a2703SSascha Wildner 	int i = g_modem_pattern_interval;
194129a2703SSascha Wildner 
195129a2703SSascha Wildner 	sc->sc_tx_interval = i;
196129a2703SSascha Wildner 
197129a2703SSascha Wildner 	if (i <= 0)
198129a2703SSascha Wildner 		i = 1;
199129a2703SSascha Wildner 	else if (i > 1023)
200129a2703SSascha Wildner 		i = 1023;
201129a2703SSascha Wildner 
202129a2703SSascha Wildner 	i = USB_MS_TO_TICKS(i);
203129a2703SSascha Wildner 
204129a2703SSascha Wildner 	usb_callout_reset(&sc->sc_callout, i, &g_modem_timeout, sc);
205129a2703SSascha Wildner }
206129a2703SSascha Wildner 
207129a2703SSascha Wildner static void
g_modem_timeout(void * arg)208129a2703SSascha Wildner g_modem_timeout(void *arg)
209129a2703SSascha Wildner {
210129a2703SSascha Wildner 	struct g_modem_softc *sc = arg;
211129a2703SSascha Wildner 
212129a2703SSascha Wildner 	sc->sc_mode = g_modem_mode;
213129a2703SSascha Wildner 
214129a2703SSascha Wildner 	memcpy(sc->sc_pattern, g_modem_pattern_data, sizeof(sc->sc_pattern));
215129a2703SSascha Wildner 
216129a2703SSascha Wildner 	sc->sc_pattern[G_MODEM_MAX_STRLEN - 1] = 0;
217129a2703SSascha Wildner 
218129a2703SSascha Wildner 	sc->sc_pattern_len = strlen(sc->sc_pattern);
219129a2703SSascha Wildner 
220129a2703SSascha Wildner 	DPRINTFN(11, "Timeout %p\n", sc->sc_xfer[G_MODEM_INTR_DT]);
221129a2703SSascha Wildner 
222129a2703SSascha Wildner 	usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_WR]);
223129a2703SSascha Wildner 	usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_RD]);
224129a2703SSascha Wildner 
225129a2703SSascha Wildner 	g_modem_timeout_reset(sc);
226129a2703SSascha Wildner }
227129a2703SSascha Wildner 
228129a2703SSascha Wildner static void g_modem_watchdog(void *arg);
229129a2703SSascha Wildner 
230129a2703SSascha Wildner static void
g_modem_watchdog_reset(struct g_modem_softc * sc)231129a2703SSascha Wildner g_modem_watchdog_reset(struct g_modem_softc *sc)
232129a2703SSascha Wildner {
233129a2703SSascha Wildner 	usb_callout_reset(&sc->sc_watchdog, hz, &g_modem_watchdog, sc);
234129a2703SSascha Wildner }
235129a2703SSascha Wildner 
236129a2703SSascha Wildner static void
g_modem_watchdog(void * arg)237129a2703SSascha Wildner g_modem_watchdog(void *arg)
238129a2703SSascha Wildner {
239129a2703SSascha Wildner 	struct g_modem_softc *sc = arg;
240129a2703SSascha Wildner 	int i;
241129a2703SSascha Wildner 
242129a2703SSascha Wildner 	i = sc->sc_throughput;
243129a2703SSascha Wildner 
244129a2703SSascha Wildner 	sc->sc_throughput = 0;
245129a2703SSascha Wildner 
246129a2703SSascha Wildner 	g_modem_throughput = i;
247129a2703SSascha Wildner 
248129a2703SSascha Wildner 	g_modem_watchdog_reset(sc);
249129a2703SSascha Wildner }
250129a2703SSascha Wildner 
251129a2703SSascha Wildner static int
g_modem_probe(device_t dev)252129a2703SSascha Wildner g_modem_probe(device_t dev)
253129a2703SSascha Wildner {
254129a2703SSascha Wildner 	struct usb_attach_arg *uaa = device_get_ivars(dev);
255129a2703SSascha Wildner 
256129a2703SSascha Wildner 	DPRINTFN(11, "\n");
257129a2703SSascha Wildner 
258129a2703SSascha Wildner 	if (uaa->usb_mode != USB_MODE_DEVICE)
259129a2703SSascha Wildner 		return (ENXIO);
260129a2703SSascha Wildner 
261129a2703SSascha Wildner 	if ((uaa->info.bInterfaceClass == UICLASS_CDC) &&
262129a2703SSascha Wildner 	    (uaa->info.bInterfaceSubClass == UISUBCLASS_ABSTRACT_CONTROL_MODEL) &&
263129a2703SSascha Wildner 	    (uaa->info.bInterfaceProtocol == UIPROTO_CDC_AT))
264129a2703SSascha Wildner 		return (0);
265129a2703SSascha Wildner 
266129a2703SSascha Wildner 	return (ENXIO);
267129a2703SSascha Wildner }
268129a2703SSascha Wildner 
269129a2703SSascha Wildner static int
g_modem_attach(device_t dev)270129a2703SSascha Wildner g_modem_attach(device_t dev)
271129a2703SSascha Wildner {
272129a2703SSascha Wildner 	struct g_modem_softc *sc = device_get_softc(dev);
273129a2703SSascha Wildner 	struct usb_attach_arg *uaa = device_get_ivars(dev);
274129a2703SSascha Wildner 	int error;
275129a2703SSascha Wildner 	uint8_t iface_index[2];
276129a2703SSascha Wildner 
277129a2703SSascha Wildner 	DPRINTFN(11, "\n");
278129a2703SSascha Wildner 
279129a2703SSascha Wildner 	device_set_usb_desc(dev);
280129a2703SSascha Wildner 
281129a2703SSascha Wildner 	lockinit(&sc->sc_lock, "g_modem", 0, 0);
282129a2703SSascha Wildner 
283129a2703SSascha Wildner 	usb_callout_init_mtx(&sc->sc_callout, &sc->sc_lock, 0);
284129a2703SSascha Wildner 	usb_callout_init_mtx(&sc->sc_watchdog, &sc->sc_lock, 0);
285129a2703SSascha Wildner 
286129a2703SSascha Wildner 	sc->sc_mode = G_MODEM_MODE_SILENT;
287129a2703SSascha Wildner 
288129a2703SSascha Wildner 	iface_index[0] = uaa->info.bIfaceIndex;
289129a2703SSascha Wildner 	iface_index[1] = uaa->info.bIfaceIndex + 1;
290129a2703SSascha Wildner 
291129a2703SSascha Wildner 	error = usbd_transfer_setup(uaa->device,
292129a2703SSascha Wildner 	    iface_index, sc->sc_xfer, g_modem_config,
293129a2703SSascha Wildner 	    G_MODEM_N_TRANSFER, sc, &sc->sc_lock);
294129a2703SSascha Wildner 
295129a2703SSascha Wildner 	if (error) {
296129a2703SSascha Wildner 		DPRINTF("error=%s\n", usbd_errstr(error));
297129a2703SSascha Wildner 		goto detach;
298129a2703SSascha Wildner 	}
299129a2703SSascha Wildner 	usbd_set_parent_iface(uaa->device, iface_index[1], iface_index[0]);
300129a2703SSascha Wildner 
301129a2703SSascha Wildner 	lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
302129a2703SSascha Wildner 	g_modem_timeout_reset(sc);
303129a2703SSascha Wildner 	g_modem_watchdog_reset(sc);
304129a2703SSascha Wildner 	lockmgr(&sc->sc_lock, LK_RELEASE);
305129a2703SSascha Wildner 
306129a2703SSascha Wildner 	return (0);			/* success */
307129a2703SSascha Wildner 
308129a2703SSascha Wildner detach:
309129a2703SSascha Wildner 	g_modem_detach(dev);
310129a2703SSascha Wildner 
311129a2703SSascha Wildner 	return (ENXIO);			/* error */
312129a2703SSascha Wildner }
313129a2703SSascha Wildner 
314129a2703SSascha Wildner static int
g_modem_detach(device_t dev)315129a2703SSascha Wildner g_modem_detach(device_t dev)
316129a2703SSascha Wildner {
317129a2703SSascha Wildner 	struct g_modem_softc *sc = device_get_softc(dev);
318129a2703SSascha Wildner 
319129a2703SSascha Wildner 	DPRINTF("\n");
320129a2703SSascha Wildner 
321129a2703SSascha Wildner 	lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
322129a2703SSascha Wildner 	usb_callout_stop(&sc->sc_callout);
323129a2703SSascha Wildner 	usb_callout_stop(&sc->sc_watchdog);
324129a2703SSascha Wildner 	lockmgr(&sc->sc_lock, LK_RELEASE);
325129a2703SSascha Wildner 
326129a2703SSascha Wildner 	usbd_transfer_unsetup(sc->sc_xfer, G_MODEM_N_TRANSFER);
327129a2703SSascha Wildner 
328129a2703SSascha Wildner 	usb_callout_drain(&sc->sc_callout);
329129a2703SSascha Wildner 	usb_callout_drain(&sc->sc_watchdog);
330129a2703SSascha Wildner 
331129a2703SSascha Wildner 	lockuninit(&sc->sc_lock);
332129a2703SSascha Wildner 
333129a2703SSascha Wildner 	return (0);
334129a2703SSascha Wildner }
335129a2703SSascha Wildner 
336129a2703SSascha Wildner static void
g_modem_intr_callback(struct usb_xfer * xfer,usb_error_t error)337129a2703SSascha Wildner g_modem_intr_callback(struct usb_xfer *xfer, usb_error_t error)
338129a2703SSascha Wildner {
339129a2703SSascha Wildner 	int actlen;
340129a2703SSascha Wildner 	int aframes;
341129a2703SSascha Wildner 
342129a2703SSascha Wildner 	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
343129a2703SSascha Wildner 
344129a2703SSascha Wildner 	DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
345129a2703SSascha Wildner 	    USB_GET_STATE(xfer), aframes, actlen);
346129a2703SSascha Wildner 
347129a2703SSascha Wildner 	switch (USB_GET_STATE(xfer)) {
348129a2703SSascha Wildner 	case USB_ST_TRANSFERRED:
349129a2703SSascha Wildner 		break;
350129a2703SSascha Wildner 
351129a2703SSascha Wildner 	case USB_ST_SETUP:
352129a2703SSascha Wildner tr_setup:
353129a2703SSascha Wildner 		break;
354129a2703SSascha Wildner 
355129a2703SSascha Wildner 	default:			/* Error */
356129a2703SSascha Wildner 		DPRINTF("error=%s\n", usbd_errstr(error));
357129a2703SSascha Wildner 
358129a2703SSascha Wildner 		if (error != USB_ERR_CANCELLED) {
359129a2703SSascha Wildner 			/* try to clear stall first */
360129a2703SSascha Wildner 			usbd_xfer_set_stall(xfer);
361129a2703SSascha Wildner 			goto tr_setup;
362129a2703SSascha Wildner 		}
363129a2703SSascha Wildner 		break;
364129a2703SSascha Wildner 	}
365129a2703SSascha Wildner }
366129a2703SSascha Wildner 
367129a2703SSascha Wildner static void
g_modem_bulk_write_callback(struct usb_xfer * xfer,usb_error_t error)368129a2703SSascha Wildner g_modem_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
369129a2703SSascha Wildner {
370129a2703SSascha Wildner 	struct g_modem_softc *sc = usbd_xfer_softc(xfer);
371129a2703SSascha Wildner 	int actlen;
372129a2703SSascha Wildner 	int aframes;
373129a2703SSascha Wildner 	int mod;
374129a2703SSascha Wildner 	int x;
375129a2703SSascha Wildner 	int max;
376129a2703SSascha Wildner 
377129a2703SSascha Wildner 	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
378129a2703SSascha Wildner 
379129a2703SSascha Wildner 	DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
380129a2703SSascha Wildner 	    USB_GET_STATE(xfer), aframes, actlen);
381129a2703SSascha Wildner 
382129a2703SSascha Wildner 	switch (USB_GET_STATE(xfer)) {
383129a2703SSascha Wildner 	case USB_ST_TRANSFERRED:
384129a2703SSascha Wildner 
385129a2703SSascha Wildner 		sc->sc_tx_busy = 0;
386129a2703SSascha Wildner 		sc->sc_throughput += actlen;
387129a2703SSascha Wildner 
388129a2703SSascha Wildner 		if (sc->sc_mode == G_MODEM_MODE_LOOP) {
389129a2703SSascha Wildner 			/* start loop */
390129a2703SSascha Wildner 			usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_RD]);
391129a2703SSascha Wildner 			break;
392129a2703SSascha Wildner 		} else if ((sc->sc_mode == G_MODEM_MODE_PATTERN) && (sc->sc_tx_interval != 0)) {
393129a2703SSascha Wildner 			/* wait for next timeout */
394129a2703SSascha Wildner 			break;
395129a2703SSascha Wildner 		}
396129a2703SSascha Wildner 	case USB_ST_SETUP:
397129a2703SSascha Wildner tr_setup:
398129a2703SSascha Wildner 		if (sc->sc_mode == G_MODEM_MODE_PATTERN) {
399129a2703SSascha Wildner 
400129a2703SSascha Wildner 			mod = sc->sc_pattern_len;
401129a2703SSascha Wildner 			max = sc->sc_tx_interval ? mod : G_MODEM_BUFSIZE;
402129a2703SSascha Wildner 
403129a2703SSascha Wildner 			if (mod == 0) {
404129a2703SSascha Wildner 				for (x = 0; x != max; x++)
405129a2703SSascha Wildner 					sc->sc_data_buf[x] = x % 255;
406129a2703SSascha Wildner 			} else {
407129a2703SSascha Wildner 				for (x = 0; x != max; x++)
408129a2703SSascha Wildner 					sc->sc_data_buf[x] = sc->sc_pattern[x % mod];
409129a2703SSascha Wildner 			}
410129a2703SSascha Wildner 
411129a2703SSascha Wildner 			usbd_xfer_set_frame_data(xfer, 0, sc->sc_data_buf, max);
412129a2703SSascha Wildner 			usbd_xfer_set_interval(xfer, 0);
413129a2703SSascha Wildner 			usbd_xfer_set_frames(xfer, 1);
414129a2703SSascha Wildner 			usbd_transfer_submit(xfer);
415129a2703SSascha Wildner 
416129a2703SSascha Wildner 		} else if (sc->sc_mode == G_MODEM_MODE_LOOP) {
417129a2703SSascha Wildner 
418129a2703SSascha Wildner 			if (sc->sc_tx_busy == 0)
419129a2703SSascha Wildner 				break;
420129a2703SSascha Wildner 
421129a2703SSascha Wildner 			x = sc->sc_tx_interval;
422129a2703SSascha Wildner 
423129a2703SSascha Wildner 			if (x < 0)
424129a2703SSascha Wildner 				x = 0;
425129a2703SSascha Wildner 			else if (x > 256)
426129a2703SSascha Wildner 				x = 256;
427129a2703SSascha Wildner 
428129a2703SSascha Wildner 			usbd_xfer_set_frame_data(xfer, 0, sc->sc_data_buf, sc->sc_data_len);
429129a2703SSascha Wildner 			usbd_xfer_set_interval(xfer, x);
430129a2703SSascha Wildner 			usbd_xfer_set_frames(xfer, 1);
431129a2703SSascha Wildner 			usbd_transfer_submit(xfer);
432129a2703SSascha Wildner 		} else {
433129a2703SSascha Wildner 			sc->sc_tx_busy = 0;
434129a2703SSascha Wildner 		}
435129a2703SSascha Wildner 		break;
436129a2703SSascha Wildner 
437129a2703SSascha Wildner 	default:			/* Error */
438129a2703SSascha Wildner 		DPRINTF("error=%s\n", usbd_errstr(error));
439129a2703SSascha Wildner 
440129a2703SSascha Wildner 		if (error != USB_ERR_CANCELLED) {
441129a2703SSascha Wildner 			/* try to clear stall first */
442129a2703SSascha Wildner 			usbd_xfer_set_stall(xfer);
443129a2703SSascha Wildner 			goto tr_setup;
444129a2703SSascha Wildner 		}
445129a2703SSascha Wildner 		break;
446129a2703SSascha Wildner 	}
447129a2703SSascha Wildner }
448129a2703SSascha Wildner 
449129a2703SSascha Wildner static void
g_modem_bulk_read_callback(struct usb_xfer * xfer,usb_error_t error)450129a2703SSascha Wildner g_modem_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
451129a2703SSascha Wildner {
452129a2703SSascha Wildner 	struct g_modem_softc *sc = usbd_xfer_softc(xfer);
453129a2703SSascha Wildner 	int actlen;
454129a2703SSascha Wildner 	int aframes;
455129a2703SSascha Wildner 
456129a2703SSascha Wildner 	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
457129a2703SSascha Wildner 
458129a2703SSascha Wildner 	DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
459129a2703SSascha Wildner 	    USB_GET_STATE(xfer), aframes, actlen);
460129a2703SSascha Wildner 
461129a2703SSascha Wildner 	switch (USB_GET_STATE(xfer)) {
462129a2703SSascha Wildner 	case USB_ST_TRANSFERRED:
463129a2703SSascha Wildner 
464129a2703SSascha Wildner 		sc->sc_throughput += actlen;
465129a2703SSascha Wildner 
466129a2703SSascha Wildner 		if (sc->sc_mode == G_MODEM_MODE_LOOP) {
467129a2703SSascha Wildner 			sc->sc_tx_busy = 1;
468129a2703SSascha Wildner 			sc->sc_data_len = actlen;
469129a2703SSascha Wildner 			usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_WR]);
470129a2703SSascha Wildner 			break;
471129a2703SSascha Wildner 		}
472129a2703SSascha Wildner 
473129a2703SSascha Wildner 	case USB_ST_SETUP:
474129a2703SSascha Wildner tr_setup:
475129a2703SSascha Wildner 		if ((sc->sc_mode == G_MODEM_MODE_SILENT) ||
476129a2703SSascha Wildner 		    (sc->sc_tx_busy != 0))
477129a2703SSascha Wildner 			break;
478129a2703SSascha Wildner 
479129a2703SSascha Wildner 		usbd_xfer_set_frame_data(xfer, 0, sc->sc_data_buf, G_MODEM_BUFSIZE);
480129a2703SSascha Wildner 		usbd_xfer_set_frames(xfer, 1);
481129a2703SSascha Wildner 		usbd_transfer_submit(xfer);
482129a2703SSascha Wildner 		break;
483129a2703SSascha Wildner 
484129a2703SSascha Wildner 	default:			/* Error */
485129a2703SSascha Wildner 		DPRINTF("error=%s\n", usbd_errstr(error));
486129a2703SSascha Wildner 
487129a2703SSascha Wildner 		if (error != USB_ERR_CANCELLED) {
488129a2703SSascha Wildner 			/* try to clear stall first */
489129a2703SSascha Wildner 			usbd_xfer_set_stall(xfer);
490129a2703SSascha Wildner 			goto tr_setup;
491129a2703SSascha Wildner 		}
492129a2703SSascha Wildner 		break;
493129a2703SSascha Wildner 	}
494129a2703SSascha Wildner }
495129a2703SSascha Wildner 
496129a2703SSascha Wildner 
497129a2703SSascha Wildner static int
g_modem_handle_request(device_t dev,const void * preq,void ** pptr,uint16_t * plen,uint16_t offset,uint8_t * pstate)498129a2703SSascha Wildner g_modem_handle_request(device_t dev,
499129a2703SSascha Wildner     const void *preq, void **pptr, uint16_t *plen,
500129a2703SSascha Wildner     uint16_t offset, uint8_t *pstate)
501129a2703SSascha Wildner {
502129a2703SSascha Wildner 	struct g_modem_softc *sc = device_get_softc(dev);
503129a2703SSascha Wildner 	const struct usb_device_request *req = preq;
504129a2703SSascha Wildner 	uint8_t is_complete = *pstate;
505129a2703SSascha Wildner 
506129a2703SSascha Wildner 	if (!is_complete) {
507129a2703SSascha Wildner 		if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
508129a2703SSascha Wildner 		    (req->bRequest == UCDC_SET_LINE_CODING) &&
509129a2703SSascha Wildner 		    (req->wValue[0] == 0x00) &&
510129a2703SSascha Wildner 		    (req->wValue[1] == 0x00)) {
511129a2703SSascha Wildner 
512129a2703SSascha Wildner 			if (offset == 0) {
513129a2703SSascha Wildner 				*plen = sizeof(sc->sc_line_coding);
514129a2703SSascha Wildner 				*pptr = &sc->sc_line_coding;
515129a2703SSascha Wildner 			} else {
516129a2703SSascha Wildner 				*plen = 0;
517129a2703SSascha Wildner 			}
518129a2703SSascha Wildner 			return (0);
519129a2703SSascha Wildner 		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
520129a2703SSascha Wildner 		    (req->bRequest == UCDC_SET_COMM_FEATURE)) {
521129a2703SSascha Wildner 
522129a2703SSascha Wildner 			if (offset == 0) {
523129a2703SSascha Wildner 				*plen = sizeof(sc->sc_abstract_state);
524129a2703SSascha Wildner 				*pptr = &sc->sc_abstract_state;
525129a2703SSascha Wildner 			} else {
526129a2703SSascha Wildner 				*plen = 0;
527129a2703SSascha Wildner 			}
528129a2703SSascha Wildner 			return (0);
529129a2703SSascha Wildner 		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
530129a2703SSascha Wildner 		    (req->bRequest == UCDC_SET_CONTROL_LINE_STATE)) {
531129a2703SSascha Wildner 			*plen = 0;
532129a2703SSascha Wildner 			return (0);
533129a2703SSascha Wildner 		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
534129a2703SSascha Wildner 		    (req->bRequest == UCDC_SEND_BREAK)) {
535129a2703SSascha Wildner 			*plen = 0;
536129a2703SSascha Wildner 			return (0);
537129a2703SSascha Wildner 		}
538129a2703SSascha Wildner 	}
539129a2703SSascha Wildner 	return (ENXIO);			/* use builtin handler */
540129a2703SSascha Wildner }
541