1 /*-
2 * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
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.
12 *
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
23 * SUCH DAMAGE.
24 *
25 * $FreeBSD: head/sys/dev/usb/gadget/g_modem.c 253618 2013-07-24 18:32:15Z obrien $
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/stdint.h>
35 #include <sys/queue.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/bus.h>
39 #include <sys/linker_set.h>
40 #include <sys/module.h>
41 #include <sys/lock.h>
42 #include <sys/condvar.h>
43 #include <sys/sysctl.h>
44 #include <sys/unistd.h>
45 #include <sys/callout.h>
46 #include <sys/malloc.h>
47 #include <sys/caps.h>
48
49 #include <bus/u4b/usb.h>
50 #include <bus/u4b/usb_cdc.h>
51 #include <bus/u4b/usbdi.h>
52 #include <bus/u4b/usbdi_util.h>
53 #include <bus/u4b/usbhid.h>
54 #include "usb_if.h"
55
56 #define USB_DEBUG_VAR g_modem_debug
57 #include <bus/u4b/usb_debug.h>
58
59 #include <bus/u4b/gadget/g_modem.h>
60
61 enum {
62 G_MODEM_INTR_DT,
63 G_MODEM_BULK_RD,
64 G_MODEM_BULK_WR,
65 G_MODEM_N_TRANSFER,
66 };
67
68 struct g_modem_softc {
69 struct lock sc_lock;
70 struct usb_callout sc_callout;
71 struct usb_callout sc_watchdog;
72 struct usb_xfer *sc_xfer[G_MODEM_N_TRANSFER];
73
74 int sc_mode;
75 int sc_tx_busy;
76 int sc_pattern_len;
77 int sc_throughput;
78 int sc_tx_interval;
79
80 char sc_pattern[G_MODEM_MAX_STRLEN];
81
82 uint16_t sc_data_len;
83
84 uint8_t sc_data_buf[G_MODEM_BUFSIZE];
85 uint8_t sc_line_coding[32];
86 uint8_t sc_abstract_state[32];
87 };
88
89 static SYSCTL_NODE(_hw_usb, OID_AUTO, g_modem, CTLFLAG_RW, 0, "USB modem gadget");
90
91 #ifdef USB_DEBUG
92 static int g_modem_debug = 0;
93
94 SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, debug, CTLFLAG_RW,
95 &g_modem_debug, 0, "Debug level");
96 #endif
97
98 static int g_modem_mode = 0;
99
100 SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, mode, CTLFLAG_RW,
101 &g_modem_mode, 0, "Mode selection");
102
103 static int g_modem_pattern_interval = 1000;
104
105 SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, pattern_interval, CTLFLAG_RW,
106 &g_modem_pattern_interval, 0, "Pattern interval in milliseconds");
107
108 static char g_modem_pattern_data[G_MODEM_MAX_STRLEN];
109
110 SYSCTL_STRING(_hw_usb_g_modem, OID_AUTO, pattern, CTLFLAG_RW,
111 &g_modem_pattern_data, sizeof(g_modem_pattern_data), "Data pattern");
112
113 static int g_modem_throughput;
114
115 SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, throughput, CTLFLAG_RD,
116 &g_modem_throughput, sizeof(g_modem_throughput), "Throughput in bytes per second");
117
118 static device_probe_t g_modem_probe;
119 static device_attach_t g_modem_attach;
120 static device_detach_t g_modem_detach;
121 static usb_handle_request_t g_modem_handle_request;
122 static usb_callback_t g_modem_intr_callback;
123 static usb_callback_t g_modem_bulk_read_callback;
124 static usb_callback_t g_modem_bulk_write_callback;
125
126 static void g_modem_timeout(void *arg);
127
128 static devclass_t g_modem_devclass;
129
130 static device_method_t g_modem_methods[] = {
131 /* USB interface */
132 DEVMETHOD(usb_handle_request, g_modem_handle_request),
133
134 /* Device interface */
135 DEVMETHOD(device_probe, g_modem_probe),
136 DEVMETHOD(device_attach, g_modem_attach),
137 DEVMETHOD(device_detach, g_modem_detach),
138
139 DEVMETHOD_END
140 };
141
142 static driver_t g_modem_driver = {
143 .name = "g_modem",
144 .methods = g_modem_methods,
145 .size = sizeof(struct g_modem_softc),
146 };
147
148 DRIVER_MODULE(g_modem, uhub, g_modem_driver, g_modem_devclass, NULL, NULL);
149 MODULE_DEPEND(g_modem, usb, 1, 1, 1);
150
151 static const struct usb_config g_modem_config[G_MODEM_N_TRANSFER] = {
152
153 [G_MODEM_INTR_DT] = {
154 .type = UE_INTERRUPT,
155 .endpoint = UE_ADDR_ANY,
156 .direction = UE_DIR_TX,
157 .flags = {.ext_buffer = 1,.pipe_bof = 1,},
158 .bufsize = 0, /* use wMaxPacketSize */
159 .callback = &g_modem_intr_callback,
160 .frames = 1,
161 .usb_mode = USB_MODE_DEVICE,
162 .if_index = 0,
163 },
164
165 [G_MODEM_BULK_RD] = {
166 .type = UE_BULK,
167 .endpoint = UE_ADDR_ANY,
168 .direction = UE_DIR_RX,
169 .flags = {.ext_buffer = 1,.pipe_bof = 1,.short_xfer_ok = 1,},
170 .bufsize = G_MODEM_BUFSIZE,
171 .callback = &g_modem_bulk_read_callback,
172 .frames = 1,
173 .usb_mode = USB_MODE_DEVICE,
174 .if_index = 1,
175 },
176
177 [G_MODEM_BULK_WR] = {
178 .type = UE_BULK,
179 .endpoint = UE_ADDR_ANY,
180 .direction = UE_DIR_TX,
181 .flags = {.ext_buffer = 1,.pipe_bof = 1,},
182 .bufsize = G_MODEM_BUFSIZE,
183 .callback = &g_modem_bulk_write_callback,
184 .frames = 1,
185 .usb_mode = USB_MODE_DEVICE,
186 .if_index = 1,
187 },
188 };
189
190 static void
g_modem_timeout_reset(struct g_modem_softc * sc)191 g_modem_timeout_reset(struct g_modem_softc *sc)
192 {
193 int i = g_modem_pattern_interval;
194
195 sc->sc_tx_interval = i;
196
197 if (i <= 0)
198 i = 1;
199 else if (i > 1023)
200 i = 1023;
201
202 i = USB_MS_TO_TICKS(i);
203
204 usb_callout_reset(&sc->sc_callout, i, &g_modem_timeout, sc);
205 }
206
207 static void
g_modem_timeout(void * arg)208 g_modem_timeout(void *arg)
209 {
210 struct g_modem_softc *sc = arg;
211
212 sc->sc_mode = g_modem_mode;
213
214 memcpy(sc->sc_pattern, g_modem_pattern_data, sizeof(sc->sc_pattern));
215
216 sc->sc_pattern[G_MODEM_MAX_STRLEN - 1] = 0;
217
218 sc->sc_pattern_len = strlen(sc->sc_pattern);
219
220 DPRINTFN(11, "Timeout %p\n", sc->sc_xfer[G_MODEM_INTR_DT]);
221
222 usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_WR]);
223 usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_RD]);
224
225 g_modem_timeout_reset(sc);
226 }
227
228 static void g_modem_watchdog(void *arg);
229
230 static void
g_modem_watchdog_reset(struct g_modem_softc * sc)231 g_modem_watchdog_reset(struct g_modem_softc *sc)
232 {
233 usb_callout_reset(&sc->sc_watchdog, hz, &g_modem_watchdog, sc);
234 }
235
236 static void
g_modem_watchdog(void * arg)237 g_modem_watchdog(void *arg)
238 {
239 struct g_modem_softc *sc = arg;
240 int i;
241
242 i = sc->sc_throughput;
243
244 sc->sc_throughput = 0;
245
246 g_modem_throughput = i;
247
248 g_modem_watchdog_reset(sc);
249 }
250
251 static int
g_modem_probe(device_t dev)252 g_modem_probe(device_t dev)
253 {
254 struct usb_attach_arg *uaa = device_get_ivars(dev);
255
256 DPRINTFN(11, "\n");
257
258 if (uaa->usb_mode != USB_MODE_DEVICE)
259 return (ENXIO);
260
261 if ((uaa->info.bInterfaceClass == UICLASS_CDC) &&
262 (uaa->info.bInterfaceSubClass == UISUBCLASS_ABSTRACT_CONTROL_MODEL) &&
263 (uaa->info.bInterfaceProtocol == UIPROTO_CDC_AT))
264 return (0);
265
266 return (ENXIO);
267 }
268
269 static int
g_modem_attach(device_t dev)270 g_modem_attach(device_t dev)
271 {
272 struct g_modem_softc *sc = device_get_softc(dev);
273 struct usb_attach_arg *uaa = device_get_ivars(dev);
274 int error;
275 uint8_t iface_index[2];
276
277 DPRINTFN(11, "\n");
278
279 device_set_usb_desc(dev);
280
281 lockinit(&sc->sc_lock, "g_modem", 0, 0);
282
283 usb_callout_init_mtx(&sc->sc_callout, &sc->sc_lock, 0);
284 usb_callout_init_mtx(&sc->sc_watchdog, &sc->sc_lock, 0);
285
286 sc->sc_mode = G_MODEM_MODE_SILENT;
287
288 iface_index[0] = uaa->info.bIfaceIndex;
289 iface_index[1] = uaa->info.bIfaceIndex + 1;
290
291 error = usbd_transfer_setup(uaa->device,
292 iface_index, sc->sc_xfer, g_modem_config,
293 G_MODEM_N_TRANSFER, sc, &sc->sc_lock);
294
295 if (error) {
296 DPRINTF("error=%s\n", usbd_errstr(error));
297 goto detach;
298 }
299 usbd_set_parent_iface(uaa->device, iface_index[1], iface_index[0]);
300
301 lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
302 g_modem_timeout_reset(sc);
303 g_modem_watchdog_reset(sc);
304 lockmgr(&sc->sc_lock, LK_RELEASE);
305
306 return (0); /* success */
307
308 detach:
309 g_modem_detach(dev);
310
311 return (ENXIO); /* error */
312 }
313
314 static int
g_modem_detach(device_t dev)315 g_modem_detach(device_t dev)
316 {
317 struct g_modem_softc *sc = device_get_softc(dev);
318
319 DPRINTF("\n");
320
321 lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
322 usb_callout_stop(&sc->sc_callout);
323 usb_callout_stop(&sc->sc_watchdog);
324 lockmgr(&sc->sc_lock, LK_RELEASE);
325
326 usbd_transfer_unsetup(sc->sc_xfer, G_MODEM_N_TRANSFER);
327
328 usb_callout_drain(&sc->sc_callout);
329 usb_callout_drain(&sc->sc_watchdog);
330
331 lockuninit(&sc->sc_lock);
332
333 return (0);
334 }
335
336 static void
g_modem_intr_callback(struct usb_xfer * xfer,usb_error_t error)337 g_modem_intr_callback(struct usb_xfer *xfer, usb_error_t error)
338 {
339 int actlen;
340 int aframes;
341
342 usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
343
344 DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
345 USB_GET_STATE(xfer), aframes, actlen);
346
347 switch (USB_GET_STATE(xfer)) {
348 case USB_ST_TRANSFERRED:
349 break;
350
351 case USB_ST_SETUP:
352 tr_setup:
353 break;
354
355 default: /* Error */
356 DPRINTF("error=%s\n", usbd_errstr(error));
357
358 if (error != USB_ERR_CANCELLED) {
359 /* try to clear stall first */
360 usbd_xfer_set_stall(xfer);
361 goto tr_setup;
362 }
363 break;
364 }
365 }
366
367 static void
g_modem_bulk_write_callback(struct usb_xfer * xfer,usb_error_t error)368 g_modem_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
369 {
370 struct g_modem_softc *sc = usbd_xfer_softc(xfer);
371 int actlen;
372 int aframes;
373 int mod;
374 int x;
375 int max;
376
377 usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
378
379 DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
380 USB_GET_STATE(xfer), aframes, actlen);
381
382 switch (USB_GET_STATE(xfer)) {
383 case USB_ST_TRANSFERRED:
384
385 sc->sc_tx_busy = 0;
386 sc->sc_throughput += actlen;
387
388 if (sc->sc_mode == G_MODEM_MODE_LOOP) {
389 /* start loop */
390 usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_RD]);
391 break;
392 } else if ((sc->sc_mode == G_MODEM_MODE_PATTERN) && (sc->sc_tx_interval != 0)) {
393 /* wait for next timeout */
394 break;
395 }
396 case USB_ST_SETUP:
397 tr_setup:
398 if (sc->sc_mode == G_MODEM_MODE_PATTERN) {
399
400 mod = sc->sc_pattern_len;
401 max = sc->sc_tx_interval ? mod : G_MODEM_BUFSIZE;
402
403 if (mod == 0) {
404 for (x = 0; x != max; x++)
405 sc->sc_data_buf[x] = x % 255;
406 } else {
407 for (x = 0; x != max; x++)
408 sc->sc_data_buf[x] = sc->sc_pattern[x % mod];
409 }
410
411 usbd_xfer_set_frame_data(xfer, 0, sc->sc_data_buf, max);
412 usbd_xfer_set_interval(xfer, 0);
413 usbd_xfer_set_frames(xfer, 1);
414 usbd_transfer_submit(xfer);
415
416 } else if (sc->sc_mode == G_MODEM_MODE_LOOP) {
417
418 if (sc->sc_tx_busy == 0)
419 break;
420
421 x = sc->sc_tx_interval;
422
423 if (x < 0)
424 x = 0;
425 else if (x > 256)
426 x = 256;
427
428 usbd_xfer_set_frame_data(xfer, 0, sc->sc_data_buf, sc->sc_data_len);
429 usbd_xfer_set_interval(xfer, x);
430 usbd_xfer_set_frames(xfer, 1);
431 usbd_transfer_submit(xfer);
432 } else {
433 sc->sc_tx_busy = 0;
434 }
435 break;
436
437 default: /* Error */
438 DPRINTF("error=%s\n", usbd_errstr(error));
439
440 if (error != USB_ERR_CANCELLED) {
441 /* try to clear stall first */
442 usbd_xfer_set_stall(xfer);
443 goto tr_setup;
444 }
445 break;
446 }
447 }
448
449 static void
g_modem_bulk_read_callback(struct usb_xfer * xfer,usb_error_t error)450 g_modem_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
451 {
452 struct g_modem_softc *sc = usbd_xfer_softc(xfer);
453 int actlen;
454 int aframes;
455
456 usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
457
458 DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
459 USB_GET_STATE(xfer), aframes, actlen);
460
461 switch (USB_GET_STATE(xfer)) {
462 case USB_ST_TRANSFERRED:
463
464 sc->sc_throughput += actlen;
465
466 if (sc->sc_mode == G_MODEM_MODE_LOOP) {
467 sc->sc_tx_busy = 1;
468 sc->sc_data_len = actlen;
469 usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_WR]);
470 break;
471 }
472
473 case USB_ST_SETUP:
474 tr_setup:
475 if ((sc->sc_mode == G_MODEM_MODE_SILENT) ||
476 (sc->sc_tx_busy != 0))
477 break;
478
479 usbd_xfer_set_frame_data(xfer, 0, sc->sc_data_buf, G_MODEM_BUFSIZE);
480 usbd_xfer_set_frames(xfer, 1);
481 usbd_transfer_submit(xfer);
482 break;
483
484 default: /* Error */
485 DPRINTF("error=%s\n", usbd_errstr(error));
486
487 if (error != USB_ERR_CANCELLED) {
488 /* try to clear stall first */
489 usbd_xfer_set_stall(xfer);
490 goto tr_setup;
491 }
492 break;
493 }
494 }
495
496
497 static int
g_modem_handle_request(device_t dev,const void * preq,void ** pptr,uint16_t * plen,uint16_t offset,uint8_t * pstate)498 g_modem_handle_request(device_t dev,
499 const void *preq, void **pptr, uint16_t *plen,
500 uint16_t offset, uint8_t *pstate)
501 {
502 struct g_modem_softc *sc = device_get_softc(dev);
503 const struct usb_device_request *req = preq;
504 uint8_t is_complete = *pstate;
505
506 if (!is_complete) {
507 if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
508 (req->bRequest == UCDC_SET_LINE_CODING) &&
509 (req->wValue[0] == 0x00) &&
510 (req->wValue[1] == 0x00)) {
511
512 if (offset == 0) {
513 *plen = sizeof(sc->sc_line_coding);
514 *pptr = &sc->sc_line_coding;
515 } else {
516 *plen = 0;
517 }
518 return (0);
519 } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
520 (req->bRequest == UCDC_SET_COMM_FEATURE)) {
521
522 if (offset == 0) {
523 *plen = sizeof(sc->sc_abstract_state);
524 *pptr = &sc->sc_abstract_state;
525 } else {
526 *plen = 0;
527 }
528 return (0);
529 } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
530 (req->bRequest == UCDC_SET_CONTROL_LINE_STATE)) {
531 *plen = 0;
532 return (0);
533 } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
534 (req->bRequest == UCDC_SEND_BREAK)) {
535 *plen = 0;
536 return (0);
537 }
538 }
539 return (ENXIO); /* use builtin handler */
540 }
541