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