xref: /dragonfly/sys/bus/u4b/gadget/g_modem.c (revision 2b3f93ea)
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