FreeBSD kernel usb device Code
g_modem.c
Go to the documentation of this file.
1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28/*
29 * Comm Class spec: http://www.usb.org/developers/devclass_docs/usbccs10.pdf
30 * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
31 * http://www.usb.org/developers/devclass_docs/cdc_wmc10.zip
32 */
33
34#include <sys/param.h>
35__FBSDID("$FreeBSD$");
36
37#include <sys/stdint.h>
38#include <sys/stddef.h>
39#include <sys/queue.h>
40#include <sys/systm.h>
41#include <sys/kernel.h>
42#include <sys/bus.h>
43#include <sys/linker_set.h>
44#include <sys/module.h>
45#include <sys/lock.h>
46#include <sys/mutex.h>
47#include <sys/condvar.h>
48#include <sys/sysctl.h>
49#include <sys/sx.h>
50#include <sys/unistd.h>
51#include <sys/callout.h>
52#include <sys/malloc.h>
53#include <sys/priv.h>
54
55#include <dev/usb/usb.h>
56#include <dev/usb/usb_cdc.h>
57#include <dev/usb/usbdi.h>
58#include <dev/usb/usbdi_util.h>
59#include <dev/usb/usbhid.h>
60#include "usb_if.h"
61
62#define USB_DEBUG_VAR g_modem_debug
63#include <dev/usb/usb_debug.h>
64
66
67enum {
72};
73
75 struct mtx sc_mtx;
79
85
87
88 uint16_t sc_data_len;
89
91 uint8_t sc_line_coding[32];
92 uint8_t sc_abstract_state[32];
93};
94
95static SYSCTL_NODE(_hw_usb, OID_AUTO, g_modem, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
96 "USB modem gadget");
97
98#ifdef USB_DEBUG
99static int g_modem_debug = 0;
100
101SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, debug, CTLFLAG_RWTUN,
102 &g_modem_debug, 0, "Debug level");
103#endif
104
105static int g_modem_mode = 0;
106
107SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, mode, CTLFLAG_RWTUN,
108 &g_modem_mode, 0, "Mode selection");
109
110static int g_modem_pattern_interval = 1000;
111
112SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, pattern_interval, CTLFLAG_RWTUN,
113 &g_modem_pattern_interval, 0, "Pattern interval in milliseconds");
114
116
117SYSCTL_STRING(_hw_usb_g_modem, OID_AUTO, pattern, CTLFLAG_RW,
118 &g_modem_pattern_data, sizeof(g_modem_pattern_data), "Data pattern");
119
121
122SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, throughput, CTLFLAG_RD,
123 &g_modem_throughput, sizeof(g_modem_throughput), "Throughput in bytes per second");
124
125static device_probe_t g_modem_probe;
126static device_attach_t g_modem_attach;
127static device_detach_t g_modem_detach;
128static usb_handle_request_t g_modem_handle_request;
132
133static void g_modem_timeout(void *arg);
134
135static devclass_t g_modem_devclass;
136
137static device_method_t g_modem_methods[] = {
138 /* USB interface */
140
141 /* Device interface */
142 DEVMETHOD(device_probe, g_modem_probe),
143 DEVMETHOD(device_attach, g_modem_attach),
144 DEVMETHOD(device_detach, g_modem_detach),
145
146 DEVMETHOD_END
147};
148
149static driver_t g_modem_driver = {
150 .name = "g_modem",
151 .methods = g_modem_methods,
152 .size = sizeof(struct g_modem_softc),
153};
154
156MODULE_DEPEND(g_modem, usb, 1, 1, 1);
157
159 [G_MODEM_INTR_DT] = {
161 .endpoint = UE_ADDR_ANY,
162 .direction = UE_DIR_TX,
163 .flags = {.ext_buffer = 1,.pipe_bof = 1,},
164 .bufsize = 0, /* use wMaxPacketSize */
165 .callback = &g_modem_intr_callback,
166 .frames = 1,
167 .usb_mode = USB_MODE_DEVICE,
168 .if_index = 0,
169 },
170
171 [G_MODEM_BULK_RD] = {
172 .type = UE_BULK,
173 .endpoint = UE_ADDR_ANY,
174 .direction = UE_DIR_RX,
175 .flags = {.ext_buffer = 1,.pipe_bof = 1,.short_xfer_ok = 1,},
176 .bufsize = G_MODEM_BUFSIZE,
177 .callback = &g_modem_bulk_read_callback,
178 .frames = 1,
179 .usb_mode = USB_MODE_DEVICE,
180 .if_index = 1,
181 },
182
183 [G_MODEM_BULK_WR] = {
184 .type = UE_BULK,
185 .endpoint = UE_ADDR_ANY,
186 .direction = UE_DIR_TX,
187 .flags = {.ext_buffer = 1,.pipe_bof = 1,},
188 .bufsize = G_MODEM_BUFSIZE,
189 .callback = &g_modem_bulk_write_callback,
190 .frames = 1,
191 .usb_mode = USB_MODE_DEVICE,
192 .if_index = 1,
193 },
194};
195
196static void
198{
200
201 sc->sc_tx_interval = i;
202
203 if (i <= 0)
204 i = 1;
205 else if (i > 1023)
206 i = 1023;
207
208 i = USB_MS_TO_TICKS(i);
209
211}
212
213static void
215{
216 struct g_modem_softc *sc = arg;
217
218 sc->sc_mode = g_modem_mode;
219
220 memcpy(sc->sc_pattern, g_modem_pattern_data, sizeof(sc->sc_pattern));
221
222 sc->sc_pattern[G_MODEM_MAX_STRLEN - 1] = 0;
223
224 sc->sc_pattern_len = strlen(sc->sc_pattern);
225
226 DPRINTFN(11, "Timeout %p\n", sc->sc_xfer[G_MODEM_INTR_DT]);
227
230
232}
233
234static void g_modem_watchdog(void *arg);
235
236static void
238{
240}
241
242static void
244{
245 struct g_modem_softc *sc = arg;
246 int i;
247
248 i = sc->sc_throughput;
249
250 sc->sc_throughput = 0;
251
253
255}
256
257static int
258g_modem_probe(device_t dev)
259{
260 struct usb_attach_arg *uaa = device_get_ivars(dev);
261
262 DPRINTFN(11, "\n");
263
264 if (uaa->usb_mode != USB_MODE_DEVICE)
265 return (ENXIO);
266
267 if ((uaa->info.bInterfaceClass == UICLASS_CDC) &&
270 return (0);
271
272 return (ENXIO);
273}
274
275static int
276g_modem_attach(device_t dev)
277{
278 struct g_modem_softc *sc = device_get_softc(dev);
279 struct usb_attach_arg *uaa = device_get_ivars(dev);
280 int error;
281 uint8_t iface_index[2];
282
283 DPRINTFN(11, "\n");
284
286
287 mtx_init(&sc->sc_mtx, "g_modem", NULL, MTX_DEF);
288
291
293
294 iface_index[0] = uaa->info.bIfaceIndex;
295 iface_index[1] = uaa->info.bIfaceIndex + 1;
296
298 iface_index, sc->sc_xfer, g_modem_config,
299 G_MODEM_N_TRANSFER, sc, &sc->sc_mtx);
300
301 if (error) {
302 DPRINTF("error=%s\n", usbd_errstr(error));
303 goto detach;
304 }
305 usbd_set_parent_iface(uaa->device, iface_index[1], iface_index[0]);
306
307 mtx_lock(&sc->sc_mtx);
310 mtx_unlock(&sc->sc_mtx);
311
312 return (0); /* success */
313
314detach:
316
317 return (ENXIO); /* error */
318}
319
320static int
321g_modem_detach(device_t dev)
322{
323 struct g_modem_softc *sc = device_get_softc(dev);
324
325 DPRINTF("\n");
326
327 mtx_lock(&sc->sc_mtx);
330 mtx_unlock(&sc->sc_mtx);
331
333
336
337 mtx_destroy(&sc->sc_mtx);
338
339 return (0);
340}
341
342static void
344{
345 int actlen;
346 int aframes;
347
348 usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
349
350 DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
351 USB_GET_STATE(xfer), aframes, actlen);
352
353 switch (USB_GET_STATE(xfer)) {
355 break;
356
357 case USB_ST_SETUP:
358tr_setup:
359 break;
360
361 default: /* Error */
362 DPRINTF("error=%s\n", usbd_errstr(error));
363
364 if (error != USB_ERR_CANCELLED) {
365 /* try to clear stall first */
367 goto tr_setup;
368 }
369 break;
370 }
371}
372
373static void
375{
376 struct g_modem_softc *sc = usbd_xfer_softc(xfer);
377 int actlen;
378 int aframes;
379 int mod;
380 int x;
381 int max;
382
383 usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
384
385 DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
386 USB_GET_STATE(xfer), aframes, actlen);
387
388 switch (USB_GET_STATE(xfer)) {
390
391 sc->sc_tx_busy = 0;
392 sc->sc_throughput += actlen;
393
394 if (sc->sc_mode == G_MODEM_MODE_LOOP) {
395 /* start loop */
397 break;
398 } else if ((sc->sc_mode == G_MODEM_MODE_PATTERN) && (sc->sc_tx_interval != 0)) {
399 /* wait for next timeout */
400 break;
401 }
402 case USB_ST_SETUP:
403tr_setup:
404 if (sc->sc_mode == G_MODEM_MODE_PATTERN) {
405 mod = sc->sc_pattern_len;
406 max = sc->sc_tx_interval ? mod : G_MODEM_BUFSIZE;
407
408 if (mod == 0) {
409 for (x = 0; x != max; x++)
410 sc->sc_data_buf[x] = x % 255;
411 } else {
412 for (x = 0; x != max; x++)
413 sc->sc_data_buf[x] = sc->sc_pattern[x % mod];
414 }
415
416 usbd_xfer_set_frame_data(xfer, 0, sc->sc_data_buf, max);
417 usbd_xfer_set_interval(xfer, 0);
418 usbd_xfer_set_frames(xfer, 1);
420
421 } else if (sc->sc_mode == G_MODEM_MODE_LOOP) {
422 if (sc->sc_tx_busy == 0)
423 break;
424
425 x = sc->sc_tx_interval;
426
427 if (x < 0)
428 x = 0;
429 else if (x > 256)
430 x = 256;
431
433 usbd_xfer_set_interval(xfer, x);
434 usbd_xfer_set_frames(xfer, 1);
436 } else {
437 sc->sc_tx_busy = 0;
438 }
439 break;
440
441 default: /* Error */
442 DPRINTF("error=%s\n", usbd_errstr(error));
443
444 if (error != USB_ERR_CANCELLED) {
445 /* try to clear stall first */
447 goto tr_setup;
448 }
449 break;
450 }
451}
452
453static void
455{
456 struct g_modem_softc *sc = usbd_xfer_softc(xfer);
457 int actlen;
458 int aframes;
459
460 usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
461
462 DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
463 USB_GET_STATE(xfer), aframes, actlen);
464
465 switch (USB_GET_STATE(xfer)) {
467
468 sc->sc_throughput += actlen;
469
470 if (sc->sc_mode == G_MODEM_MODE_LOOP) {
471 sc->sc_tx_busy = 1;
472 sc->sc_data_len = actlen;
474 break;
475 }
476
477 case USB_ST_SETUP:
478tr_setup:
479 if ((sc->sc_mode == G_MODEM_MODE_SILENT) ||
480 (sc->sc_tx_busy != 0))
481 break;
482
484 usbd_xfer_set_frames(xfer, 1);
486 break;
487
488 default: /* Error */
489 DPRINTF("error=%s\n", usbd_errstr(error));
490
491 if (error != USB_ERR_CANCELLED) {
492 /* try to clear stall first */
494 goto tr_setup;
495 }
496 break;
497 }
498}
499
500static int
502 const void *preq, void **pptr, uint16_t *plen,
503 uint16_t offset, uint8_t *pstate)
504{
505 struct g_modem_softc *sc = device_get_softc(dev);
506 const struct usb_device_request *req = preq;
507 uint8_t is_complete = *pstate;
508
509 if (!is_complete) {
510 if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
511 (req->bRequest == UCDC_SET_LINE_CODING) &&
512 (req->wValue[0] == 0x00) &&
513 (req->wValue[1] == 0x00)) {
514 if (offset == 0) {
515 *plen = sizeof(sc->sc_line_coding);
516 *pptr = &sc->sc_line_coding;
517 } else {
518 *plen = 0;
519 }
520 return (0);
521 } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
522 (req->bRequest == UCDC_SET_COMM_FEATURE)) {
523 if (offset == 0) {
524 *plen = sizeof(sc->sc_abstract_state);
525 *pptr = &sc->sc_abstract_state;
526 } else {
527 *plen = 0;
528 }
529 return (0);
530 } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
531 (req->bRequest == UCDC_SET_CONTROL_LINE_STATE)) {
532 *plen = 0;
533 return (0);
534 } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
535 (req->bRequest == UCDC_SEND_BREAK)) {
536 *plen = 0;
537 return (0);
538 }
539 }
540 return (ENXIO); /* use builtin handler */
541}
static int debug
Definition: cfumass.c:73
static usb_callback_t g_modem_bulk_read_callback
Definition: g_modem.c:130
static usb_callback_t g_modem_bulk_write_callback
Definition: g_modem.c:131
static usb_callback_t g_modem_intr_callback
Definition: g_modem.c:129
static device_detach_t g_modem_detach
Definition: g_modem.c:127
SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, mode, CTLFLAG_RWTUN, &g_modem_mode, 0, "Mode selection")
static void g_modem_timeout(void *arg)
Definition: g_modem.c:214
static device_method_t g_modem_methods[]
Definition: g_modem.c:137
static int g_modem_pattern_interval
Definition: g_modem.c:110
static void g_modem_watchdog_reset(struct g_modem_softc *sc)
Definition: g_modem.c:237
MODULE_DEPEND(g_modem, usb, 1, 1, 1)
static void g_modem_watchdog(void *arg)
Definition: g_modem.c:243
@ G_MODEM_BULK_RD
Definition: g_modem.c:69
@ G_MODEM_BULK_WR
Definition: g_modem.c:70
@ G_MODEM_INTR_DT
Definition: g_modem.c:68
@ G_MODEM_N_TRANSFER
Definition: g_modem.c:71
static char g_modem_pattern_data[G_MODEM_MAX_STRLEN]
Definition: g_modem.c:115
static SYSCTL_NODE(_hw_usb, OID_AUTO, g_modem, CTLFLAG_RW|CTLFLAG_MPSAFE, 0, "USB modem gadget")
static usb_handle_request_t g_modem_handle_request
Definition: g_modem.c:128
__FBSDID("$FreeBSD$")
static driver_t g_modem_driver
Definition: g_modem.c:149
static int g_modem_mode
Definition: g_modem.c:105
SYSCTL_STRING(_hw_usb_g_modem, OID_AUTO, pattern, CTLFLAG_RW, &g_modem_pattern_data, sizeof(g_modem_pattern_data), "Data pattern")
static device_attach_t g_modem_attach
Definition: g_modem.c:126
static int g_modem_throughput
Definition: g_modem.c:120
static void g_modem_timeout_reset(struct g_modem_softc *sc)
Definition: g_modem.c:197
static const struct usb_config g_modem_config[G_MODEM_N_TRANSFER]
Definition: g_modem.c:158
DRIVER_MODULE(g_modem, uhub, g_modem_driver, g_modem_devclass, 0, 0)
static devclass_t g_modem_devclass
Definition: g_modem.c:135
static device_probe_t g_modem_probe
Definition: g_modem.c:125
#define G_MODEM_BUFSIZE
Definition: g_modem.h:33
#define G_MODEM_MODE_PATTERN
Definition: g_modem.h:38
#define G_MODEM_MAX_STRLEN
Definition: g_modem.h:32
#define G_MODEM_MODE_LOOP
Definition: g_modem.h:37
#define G_MODEM_MODE_SILENT
Definition: g_modem.h:35
struct @109 error
device_t dev
char sc_pattern[G_MODEM_MAX_STRLEN]
Definition: g_modem.c:86
int sc_tx_interval
Definition: g_modem.c:84
struct mtx sc_mtx
Definition: g_modem.c:75
int sc_pattern_len
Definition: g_modem.c:82
int sc_throughput
Definition: g_modem.c:83
uint8_t sc_abstract_state[32]
Definition: g_modem.c:92
uint8_t sc_data_buf[G_MODEM_BUFSIZE]
Definition: g_modem.c:90
uint16_t sc_data_len
Definition: g_modem.c:88
int sc_mode
Definition: g_modem.c:80
struct usb_callout sc_watchdog
Definition: g_modem.c:77
struct usb_xfer * sc_xfer[G_MODEM_N_TRANSFER]
Definition: g_modem.c:78
struct usb_callout sc_callout
Definition: g_modem.c:76
int sc_tx_busy
Definition: g_modem.c:81
uint8_t sc_line_coding[32]
Definition: g_modem.c:91
enum usb_hc_mode usb_mode
Definition: usbdi.h:432
struct usbd_lookup_info info
Definition: usbdi.h:426
struct usb_device * device
Definition: usbdi.h:430
uint8_t type
Definition: usbdi.h:238
uint8_t bIfaceIndex
Definition: usbdi.h:417
uint8_t bInterfaceSubClass
Definition: usbdi.h:415
uint8_t bInterfaceClass
Definition: usbdi.h:414
uint8_t bInterfaceProtocol
Definition: usbdi.h:416
#define DPRINTF(...)
Definition: umass.c:179
#define UE_INTERRUPT
Definition: usb.h:544
#define UE_ADDR_ANY
Definition: usb.h:537
#define UE_BULK
Definition: usb.h:543
#define UE_DIR_RX
Definition: usb.h:533
#define UICLASS_CDC
Definition: usb.h:434
@ USB_MODE_DEVICE
Definition: usb.h:779
#define UIPROTO_CDC_AT
Definition: usb.h:450
#define UE_DIR_TX
Definition: usb.h:534
#define UT_WRITE_CLASS_INTERFACE
Definition: usb.h:177
#define UISUBCLASS_ABSTRACT_CONTROL_MODEL
Definition: usb.h:436
#define UCDC_SET_LINE_CODING
Definition: usb_cdc.h:111
#define UCDC_SET_CONTROL_LINE_STATE
Definition: usb_cdc.h:113
#define UCDC_SET_COMM_FEATURE
Definition: usb_cdc.h:106
#define UCDC_SEND_BREAK
Definition: usb_cdc.h:116
void usbd_set_parent_iface(struct usb_device *udev, uint8_t iface_index, uint8_t parent_index)
Definition: usb_device.c:1395
const char * usbd_errstr(usb_error_t err)
Definition: usb_error.c:93
static usb_error_t usb_handle_request(struct usb_xfer *)
void ** pptr
Definition: usb_if.m:52
uint8_t * pstate
Definition: usb_if.m:55
uint16_t * plen
Definition: usb_if.m:53
uint16_t offset
Definition: usb_if.m:54
const void * req
Definition: usb_if.m:51
INTERFACE usb
Definition: usb_if.m:35
void usbd_transfer_submit(struct usb_xfer *xfer)
void usbd_xfer_set_frames(struct usb_xfer *xfer, usb_frcount_t n)
void usbd_transfer_unsetup(struct usb_xfer **pxfer, uint16_t n_setup)
void usbd_xfer_set_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void *ptr, usb_frlength_t len)
usb_error_t usbd_transfer_setup(struct usb_device *udev, const uint8_t *ifaces, struct usb_xfer **ppxfer, const struct usb_config *setup_start, uint16_t n_setup, void *priv_sc, struct mtx *xfer_mtx)
Definition: usb_transfer.c:987
void usbd_transfer_start(struct usb_xfer *xfer)
void * usbd_xfer_softc(struct usb_xfer *xfer)
void usbd_xfer_set_stall(struct usb_xfer *xfer)
void usbd_xfer_set_interval(struct usb_xfer *xfer, int i)
void usbd_xfer_status(struct usb_xfer *xfer, int *actlen, int *sumlen, int *aframes, int *nframes)
void device_set_usb_desc(device_t dev)
Definition: usb_util.c:73
#define usb_callout_init_mtx(c, m, f)
Definition: usbdi.h:480
#define USB_ST_SETUP
Definition: usbdi.h:502
#define usb_callout_reset(c,...)
Definition: usbdi.h:481
usb_error_t
Definition: usbdi.h:45
@ USB_ERR_CANCELLED
Definition: usbdi.h:51
#define usb_callout_drain(c)
Definition: usbdi.h:497
#define USB_ST_TRANSFERRED
Definition: usbdi.h:503
#define USB_MS_TO_TICKS(ms)
Definition: usbdi.h:120
void() usb_callback_t(struct usb_xfer *, usb_error_t)
Definition: usbdi.h:94
#define usb_callout_stop(c)
Definition: usbdi.h:489
#define USB_GET_STATE(xfer)
Definition: usbdi.h:515