2 * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * Comm Class spec: http://www.usb.org/developers/devclass_docs/usbccs10.pdf
28 * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
29 * http://www.usb.org/developers/devclass_docs/cdc_wmc10.zip
32 #include <sys/param.h>
33 __FBSDID("$FreeBSD$");
35 #include <sys/stdint.h>
36 #include <sys/stddef.h>
37 #include <sys/queue.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
41 #include <sys/linker_set.h>
42 #include <sys/module.h>
44 #include <sys/mutex.h>
45 #include <sys/condvar.h>
46 #include <sys/sysctl.h>
48 #include <sys/unistd.h>
49 #include <sys/callout.h>
50 #include <sys/malloc.h>
53 #include <dev/usb/usb.h>
54 #include <dev/usb/usb_cdc.h>
55 #include <dev/usb/usbdi.h>
56 #include <dev/usb/usbdi_util.h>
57 #include <dev/usb/usbhid.h>
60 #define USB_DEBUG_VAR g_modem_debug
61 #include <dev/usb/usb_debug.h>
63 #include <dev/usb/gadget/g_modem.h>
72 struct g_modem_softc {
74 struct usb_callout sc_callout;
75 struct usb_callout sc_watchdog;
76 struct usb_xfer *sc_xfer[G_MODEM_N_TRANSFER];
84 char sc_pattern[G_MODEM_MAX_STRLEN];
88 uint8_t sc_data_buf[G_MODEM_BUFSIZE];
89 uint8_t sc_line_coding[32];
90 uint8_t sc_abstract_state[32];
93 static SYSCTL_NODE(_hw_usb, OID_AUTO, g_modem, CTLFLAG_RW, 0, "USB modem gadget");
96 static int g_modem_debug = 0;
98 SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, debug, CTLFLAG_RW,
99 &g_modem_debug, 0, "Debug level");
102 static int g_modem_mode = 0;
104 SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, mode, CTLFLAG_RW,
105 &g_modem_mode, 0, "Mode selection");
107 static int g_modem_pattern_interval = 1000;
109 SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, pattern_interval, CTLFLAG_RW,
110 &g_modem_pattern_interval, 0, "Pattern interval in milliseconds");
112 static char g_modem_pattern_data[G_MODEM_MAX_STRLEN];
114 SYSCTL_STRING(_hw_usb_g_modem, OID_AUTO, pattern, CTLFLAG_RW,
115 &g_modem_pattern_data, sizeof(g_modem_pattern_data), "Data pattern");
117 static int g_modem_throughput;
119 SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, throughput, CTLFLAG_RD,
120 &g_modem_throughput, sizeof(g_modem_throughput), "Throughput in bytes per second");
122 static device_probe_t g_modem_probe;
123 static device_attach_t g_modem_attach;
124 static device_detach_t g_modem_detach;
125 static usb_handle_request_t g_modem_handle_request;
126 static usb_callback_t g_modem_intr_callback;
127 static usb_callback_t g_modem_bulk_read_callback;
128 static usb_callback_t g_modem_bulk_write_callback;
130 static void g_modem_timeout(void *arg);
132 static devclass_t g_modem_devclass;
134 static device_method_t g_modem_methods[] = {
136 DEVMETHOD(usb_handle_request, g_modem_handle_request),
138 /* Device interface */
139 DEVMETHOD(device_probe, g_modem_probe),
140 DEVMETHOD(device_attach, g_modem_attach),
141 DEVMETHOD(device_detach, g_modem_detach),
146 static driver_t g_modem_driver = {
148 .methods = g_modem_methods,
149 .size = sizeof(struct g_modem_softc),
152 DRIVER_MODULE(g_modem, uhub, g_modem_driver, g_modem_devclass, 0, 0);
153 MODULE_DEPEND(g_modem, usb, 1, 1, 1);
155 static const struct usb_config g_modem_config[G_MODEM_N_TRANSFER] = {
157 [G_MODEM_INTR_DT] = {
158 .type = UE_INTERRUPT,
159 .endpoint = UE_ADDR_ANY,
160 .direction = UE_DIR_TX,
161 .flags = {.ext_buffer = 1,.pipe_bof = 1,},
162 .bufsize = 0, /* use wMaxPacketSize */
163 .callback = &g_modem_intr_callback,
165 .usb_mode = USB_MODE_DEVICE,
169 [G_MODEM_BULK_RD] = {
171 .endpoint = UE_ADDR_ANY,
172 .direction = UE_DIR_RX,
173 .flags = {.ext_buffer = 1,.pipe_bof = 1,.short_xfer_ok = 1,},
174 .bufsize = G_MODEM_BUFSIZE,
175 .callback = &g_modem_bulk_read_callback,
177 .usb_mode = USB_MODE_DEVICE,
181 [G_MODEM_BULK_WR] = {
183 .endpoint = UE_ADDR_ANY,
184 .direction = UE_DIR_TX,
185 .flags = {.ext_buffer = 1,.pipe_bof = 1,},
186 .bufsize = G_MODEM_BUFSIZE,
187 .callback = &g_modem_bulk_write_callback,
189 .usb_mode = USB_MODE_DEVICE,
195 g_modem_timeout_reset(struct g_modem_softc *sc)
197 int i = g_modem_pattern_interval;
199 sc->sc_tx_interval = i;
206 i = USB_MS_TO_TICKS(i);
208 usb_callout_reset(&sc->sc_callout, i, &g_modem_timeout, sc);
212 g_modem_timeout(void *arg)
214 struct g_modem_softc *sc = arg;
216 sc->sc_mode = g_modem_mode;
218 memcpy(sc->sc_pattern, g_modem_pattern_data, sizeof(sc->sc_pattern));
220 sc->sc_pattern[G_MODEM_MAX_STRLEN - 1] = 0;
222 sc->sc_pattern_len = strlen(sc->sc_pattern);
224 DPRINTFN(11, "Timeout %p\n", sc->sc_xfer[G_MODEM_INTR_DT]);
226 usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_WR]);
227 usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_RD]);
229 g_modem_timeout_reset(sc);
232 static void g_modem_watchdog(void *arg);
235 g_modem_watchdog_reset(struct g_modem_softc *sc)
237 usb_callout_reset(&sc->sc_watchdog, hz, &g_modem_watchdog, sc);
241 g_modem_watchdog(void *arg)
243 struct g_modem_softc *sc = arg;
246 i = sc->sc_throughput;
248 sc->sc_throughput = 0;
250 g_modem_throughput = i;
252 g_modem_watchdog_reset(sc);
256 g_modem_probe(device_t dev)
258 struct usb_attach_arg *uaa = device_get_ivars(dev);
262 if (uaa->usb_mode != USB_MODE_DEVICE)
265 if ((uaa->info.bInterfaceClass == UICLASS_CDC) &&
266 (uaa->info.bInterfaceSubClass == UISUBCLASS_ABSTRACT_CONTROL_MODEL) &&
267 (uaa->info.bInterfaceProtocol == UIPROTO_CDC_AT))
274 g_modem_attach(device_t dev)
276 struct g_modem_softc *sc = device_get_softc(dev);
277 struct usb_attach_arg *uaa = device_get_ivars(dev);
279 uint8_t iface_index[2];
283 device_set_usb_desc(dev);
285 mtx_init(&sc->sc_mtx, "g_modem", NULL, MTX_DEF);
287 usb_callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0);
288 usb_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0);
290 sc->sc_mode = G_MODEM_MODE_SILENT;
292 iface_index[0] = uaa->info.bIfaceIndex;
293 iface_index[1] = uaa->info.bIfaceIndex + 1;
295 error = usbd_transfer_setup(uaa->device,
296 iface_index, sc->sc_xfer, g_modem_config,
297 G_MODEM_N_TRANSFER, sc, &sc->sc_mtx);
300 DPRINTF("error=%s\n", usbd_errstr(error));
303 usbd_set_parent_iface(uaa->device, iface_index[1], iface_index[0]);
305 mtx_lock(&sc->sc_mtx);
306 g_modem_timeout_reset(sc);
307 g_modem_watchdog_reset(sc);
308 mtx_unlock(&sc->sc_mtx);
310 return (0); /* success */
315 return (ENXIO); /* error */
319 g_modem_detach(device_t dev)
321 struct g_modem_softc *sc = device_get_softc(dev);
325 mtx_lock(&sc->sc_mtx);
326 usb_callout_stop(&sc->sc_callout);
327 usb_callout_stop(&sc->sc_watchdog);
328 mtx_unlock(&sc->sc_mtx);
330 usbd_transfer_unsetup(sc->sc_xfer, G_MODEM_N_TRANSFER);
332 usb_callout_drain(&sc->sc_callout);
333 usb_callout_drain(&sc->sc_watchdog);
335 mtx_destroy(&sc->sc_mtx);
341 g_modem_intr_callback(struct usb_xfer *xfer, usb_error_t error)
346 usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
348 DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
349 USB_GET_STATE(xfer), aframes, actlen);
351 switch (USB_GET_STATE(xfer)) {
352 case USB_ST_TRANSFERRED:
360 DPRINTF("error=%s\n", usbd_errstr(error));
362 if (error != USB_ERR_CANCELLED) {
363 /* try to clear stall first */
364 usbd_xfer_set_stall(xfer);
372 g_modem_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
374 struct g_modem_softc *sc = usbd_xfer_softc(xfer);
381 usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
383 DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
384 USB_GET_STATE(xfer), aframes, actlen);
386 switch (USB_GET_STATE(xfer)) {
387 case USB_ST_TRANSFERRED:
390 sc->sc_throughput += actlen;
392 if (sc->sc_mode == G_MODEM_MODE_LOOP) {
394 usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_RD]);
396 } else if ((sc->sc_mode == G_MODEM_MODE_PATTERN) && (sc->sc_tx_interval != 0)) {
397 /* wait for next timeout */
402 if (sc->sc_mode == G_MODEM_MODE_PATTERN) {
404 mod = sc->sc_pattern_len;
405 max = sc->sc_tx_interval ? mod : G_MODEM_BUFSIZE;
408 for (x = 0; x != max; x++)
409 sc->sc_data_buf[x] = x % 255;
411 for (x = 0; x != max; x++)
412 sc->sc_data_buf[x] = sc->sc_pattern[x % mod];
415 usbd_xfer_set_frame_data(xfer, 0, sc->sc_data_buf, max);
416 usbd_xfer_set_interval(xfer, 0);
417 usbd_xfer_set_frames(xfer, 1);
418 usbd_transfer_submit(xfer);
420 } else if (sc->sc_mode == G_MODEM_MODE_LOOP) {
422 if (sc->sc_tx_busy == 0)
425 x = sc->sc_tx_interval;
432 usbd_xfer_set_frame_data(xfer, 0, sc->sc_data_buf, sc->sc_data_len);
433 usbd_xfer_set_interval(xfer, x);
434 usbd_xfer_set_frames(xfer, 1);
435 usbd_transfer_submit(xfer);
442 DPRINTF("error=%s\n", usbd_errstr(error));
444 if (error != USB_ERR_CANCELLED) {
445 /* try to clear stall first */
446 usbd_xfer_set_stall(xfer);
454 g_modem_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
456 struct g_modem_softc *sc = usbd_xfer_softc(xfer);
460 usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
462 DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
463 USB_GET_STATE(xfer), aframes, actlen);
465 switch (USB_GET_STATE(xfer)) {
466 case USB_ST_TRANSFERRED:
468 sc->sc_throughput += actlen;
470 if (sc->sc_mode == G_MODEM_MODE_LOOP) {
472 sc->sc_data_len = actlen;
473 usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_WR]);
479 if ((sc->sc_mode == G_MODEM_MODE_SILENT) ||
480 (sc->sc_tx_busy != 0))
483 usbd_xfer_set_frame_data(xfer, 0, sc->sc_data_buf, G_MODEM_BUFSIZE);
484 usbd_xfer_set_frames(xfer, 1);
485 usbd_transfer_submit(xfer);
489 DPRINTF("error=%s\n", usbd_errstr(error));
491 if (error != USB_ERR_CANCELLED) {
492 /* try to clear stall first */
493 usbd_xfer_set_stall(xfer);
502 g_modem_handle_request(device_t dev,
503 const void *preq, void **pptr, uint16_t *plen,
504 uint16_t offset, uint8_t *pstate)
506 struct g_modem_softc *sc = device_get_softc(dev);
507 const struct usb_device_request *req = preq;
508 uint8_t is_complete = *pstate;
511 if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
512 (req->bRequest == UCDC_SET_LINE_CODING) &&
513 (req->wValue[0] == 0x00) &&
514 (req->wValue[1] == 0x00)) {
517 *plen = sizeof(sc->sc_line_coding);
518 *pptr = &sc->sc_line_coding;
523 } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
524 (req->bRequest == UCDC_SET_COMM_FEATURE)) {
527 *plen = sizeof(sc->sc_abstract_state);
528 *pptr = &sc->sc_abstract_state;
533 } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
534 (req->bRequest == UCDC_SET_CONTROL_LINE_STATE)) {
537 } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
538 (req->bRequest == UCDC_SEND_BREAK)) {
543 return (ENXIO); /* use builtin handler */