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
26/*
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
30 */
31
32#include <sys/param.h>
33__FBSDID("$FreeBSD$");
34
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>
40#include <sys/bus.h>
41#include <sys/linker_set.h>
42#include <sys/module.h>
43#include <sys/lock.h>
44#include <sys/mutex.h>
45#include <sys/condvar.h>
46#include <sys/sysctl.h>
47#include <sys/sx.h>
48#include <sys/unistd.h>
49#include <sys/callout.h>
50#include <sys/malloc.h>
51#include <sys/priv.h>
52
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>
58#include "usb_if.h"
59
60#define	USB_DEBUG_VAR g_modem_debug
61#include <dev/usb/usb_debug.h>
62
63#include <dev/usb/gadget/g_modem.h>
64
65enum {
66	G_MODEM_INTR_DT,
67	G_MODEM_BULK_RD,
68	G_MODEM_BULK_WR,
69	G_MODEM_N_TRANSFER,
70};
71
72struct g_modem_softc {
73	struct mtx sc_mtx;
74	struct usb_callout sc_callout;
75	struct usb_callout sc_watchdog;
76	struct usb_xfer *sc_xfer[G_MODEM_N_TRANSFER];
77
78	int	sc_mode;
79	int	sc_tx_busy;
80	int	sc_pattern_len;
81	int	sc_throughput;
82	int	sc_tx_interval;
83
84	char	sc_pattern[G_MODEM_MAX_STRLEN];
85
86	uint16_t sc_data_len;
87
88	uint8_t sc_data_buf[G_MODEM_BUFSIZE];
89	uint8_t	sc_line_coding[32];
90	uint8_t	sc_abstract_state[32];
91};
92
93static SYSCTL_NODE(_hw_usb, OID_AUTO, g_modem, CTLFLAG_RW, 0, "USB modem gadget");
94
95#ifdef USB_DEBUG
96static int g_modem_debug = 0;
97
98SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, debug, CTLFLAG_RWTUN,
99    &g_modem_debug, 0, "Debug level");
100#endif
101
102static int g_modem_mode = 0;
103
104SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, mode, CTLFLAG_RWTUN,
105    &g_modem_mode, 0, "Mode selection");
106
107static int g_modem_pattern_interval = 1000;
108
109SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, pattern_interval, CTLFLAG_RWTUN,
110    &g_modem_pattern_interval, 0, "Pattern interval in milliseconds");
111
112static char g_modem_pattern_data[G_MODEM_MAX_STRLEN];
113
114SYSCTL_STRING(_hw_usb_g_modem, OID_AUTO, pattern, CTLFLAG_RW,
115    &g_modem_pattern_data, sizeof(g_modem_pattern_data), "Data pattern");
116
117static int g_modem_throughput;
118
119SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, throughput, CTLFLAG_RD,
120    &g_modem_throughput, sizeof(g_modem_throughput), "Throughput in bytes per second");
121
122static device_probe_t g_modem_probe;
123static device_attach_t g_modem_attach;
124static device_detach_t g_modem_detach;
125static usb_handle_request_t g_modem_handle_request;
126static usb_callback_t g_modem_intr_callback;
127static usb_callback_t g_modem_bulk_read_callback;
128static usb_callback_t g_modem_bulk_write_callback;
129
130static void g_modem_timeout(void *arg);
131
132static devclass_t g_modem_devclass;
133
134static device_method_t g_modem_methods[] = {
135	/* USB interface */
136	DEVMETHOD(usb_handle_request, g_modem_handle_request),
137
138	/* Device interface */
139	DEVMETHOD(device_probe, g_modem_probe),
140	DEVMETHOD(device_attach, g_modem_attach),
141	DEVMETHOD(device_detach, g_modem_detach),
142
143	DEVMETHOD_END
144};
145
146static driver_t g_modem_driver = {
147	.name = "g_modem",
148	.methods = g_modem_methods,
149	.size = sizeof(struct g_modem_softc),
150};
151
152DRIVER_MODULE(g_modem, uhub, g_modem_driver, g_modem_devclass, 0, 0);
153MODULE_DEPEND(g_modem, usb, 1, 1, 1);
154
155static const struct usb_config g_modem_config[G_MODEM_N_TRANSFER] = {
156
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,
164		.frames = 1,
165		.usb_mode = USB_MODE_DEVICE,
166		.if_index = 0,
167	},
168
169	[G_MODEM_BULK_RD] = {
170		.type = UE_BULK,
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,
176		.frames = 1,
177		.usb_mode = USB_MODE_DEVICE,
178		.if_index = 1,
179	},
180
181	[G_MODEM_BULK_WR] = {
182		.type = UE_BULK,
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,
188		.frames = 1,
189		.usb_mode = USB_MODE_DEVICE,
190		.if_index = 1,
191	},
192};
193
194static void
195g_modem_timeout_reset(struct g_modem_softc *sc)
196{
197	int i = g_modem_pattern_interval;
198
199	sc->sc_tx_interval = i;
200
201	if (i <= 0)
202		i = 1;
203	else if (i > 1023)
204		i = 1023;
205
206	i = USB_MS_TO_TICKS(i);
207
208	usb_callout_reset(&sc->sc_callout, i, &g_modem_timeout, sc);
209}
210
211static void
212g_modem_timeout(void *arg)
213{
214	struct g_modem_softc *sc = arg;
215
216	sc->sc_mode = g_modem_mode;
217
218	memcpy(sc->sc_pattern, g_modem_pattern_data, sizeof(sc->sc_pattern));
219
220	sc->sc_pattern[G_MODEM_MAX_STRLEN - 1] = 0;
221
222	sc->sc_pattern_len = strlen(sc->sc_pattern);
223
224	DPRINTFN(11, "Timeout %p\n", sc->sc_xfer[G_MODEM_INTR_DT]);
225
226	usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_WR]);
227	usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_RD]);
228
229	g_modem_timeout_reset(sc);
230}
231
232static void g_modem_watchdog(void *arg);
233
234static void
235g_modem_watchdog_reset(struct g_modem_softc *sc)
236{
237	usb_callout_reset(&sc->sc_watchdog, hz, &g_modem_watchdog, sc);
238}
239
240static void
241g_modem_watchdog(void *arg)
242{
243	struct g_modem_softc *sc = arg;
244	int i;
245
246	i = sc->sc_throughput;
247
248	sc->sc_throughput = 0;
249
250	g_modem_throughput = i;
251
252	g_modem_watchdog_reset(sc);
253}
254
255static int
256g_modem_probe(device_t dev)
257{
258	struct usb_attach_arg *uaa = device_get_ivars(dev);
259
260	DPRINTFN(11, "\n");
261
262	if (uaa->usb_mode != USB_MODE_DEVICE)
263		return (ENXIO);
264
265	if ((uaa->info.bInterfaceClass == UICLASS_CDC) &&
266	    (uaa->info.bInterfaceSubClass == UISUBCLASS_ABSTRACT_CONTROL_MODEL) &&
267	    (uaa->info.bInterfaceProtocol == UIPROTO_CDC_AT))
268		return (0);
269
270	return (ENXIO);
271}
272
273static int
274g_modem_attach(device_t dev)
275{
276	struct g_modem_softc *sc = device_get_softc(dev);
277	struct usb_attach_arg *uaa = device_get_ivars(dev);
278	int error;
279	uint8_t iface_index[2];
280
281	DPRINTFN(11, "\n");
282
283	device_set_usb_desc(dev);
284
285	mtx_init(&sc->sc_mtx, "g_modem", NULL, MTX_DEF);
286
287	usb_callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0);
288	usb_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0);
289
290	sc->sc_mode = G_MODEM_MODE_SILENT;
291
292	iface_index[0] = uaa->info.bIfaceIndex;
293	iface_index[1] = uaa->info.bIfaceIndex + 1;
294
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);
298
299	if (error) {
300		DPRINTF("error=%s\n", usbd_errstr(error));
301		goto detach;
302	}
303	usbd_set_parent_iface(uaa->device, iface_index[1], iface_index[0]);
304
305	mtx_lock(&sc->sc_mtx);
306	g_modem_timeout_reset(sc);
307	g_modem_watchdog_reset(sc);
308	mtx_unlock(&sc->sc_mtx);
309
310	return (0);			/* success */
311
312detach:
313	g_modem_detach(dev);
314
315	return (ENXIO);			/* error */
316}
317
318static int
319g_modem_detach(device_t dev)
320{
321	struct g_modem_softc *sc = device_get_softc(dev);
322
323	DPRINTF("\n");
324
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);
329
330	usbd_transfer_unsetup(sc->sc_xfer, G_MODEM_N_TRANSFER);
331
332	usb_callout_drain(&sc->sc_callout);
333	usb_callout_drain(&sc->sc_watchdog);
334
335	mtx_destroy(&sc->sc_mtx);
336
337	return (0);
338}
339
340static void
341g_modem_intr_callback(struct usb_xfer *xfer, usb_error_t error)
342{
343	int actlen;
344	int aframes;
345
346	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
347
348	DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
349	    USB_GET_STATE(xfer), aframes, actlen);
350
351	switch (USB_GET_STATE(xfer)) {
352	case USB_ST_TRANSFERRED:
353		break;
354
355	case USB_ST_SETUP:
356tr_setup:
357		break;
358
359	default:			/* Error */
360		DPRINTF("error=%s\n", usbd_errstr(error));
361
362		if (error != USB_ERR_CANCELLED) {
363			/* try to clear stall first */
364			usbd_xfer_set_stall(xfer);
365			goto tr_setup;
366		}
367		break;
368	}
369}
370
371static void
372g_modem_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
373{
374	struct g_modem_softc *sc = usbd_xfer_softc(xfer);
375	int actlen;
376	int aframes;
377	int mod;
378	int x;
379	int max;
380
381	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
382
383	DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
384	    USB_GET_STATE(xfer), aframes, actlen);
385
386	switch (USB_GET_STATE(xfer)) {
387	case USB_ST_TRANSFERRED:
388
389		sc->sc_tx_busy = 0;
390		sc->sc_throughput += actlen;
391
392		if (sc->sc_mode == G_MODEM_MODE_LOOP) {
393			/* start loop */
394			usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_RD]);
395			break;
396		} else if ((sc->sc_mode == G_MODEM_MODE_PATTERN) && (sc->sc_tx_interval != 0)) {
397			/* wait for next timeout */
398			break;
399		}
400	case USB_ST_SETUP:
401tr_setup:
402		if (sc->sc_mode == G_MODEM_MODE_PATTERN) {
403
404			mod = sc->sc_pattern_len;
405			max = sc->sc_tx_interval ? mod : G_MODEM_BUFSIZE;
406
407			if (mod == 0) {
408				for (x = 0; x != max; x++)
409					sc->sc_data_buf[x] = x % 255;
410			} else {
411				for (x = 0; x != max; x++)
412					sc->sc_data_buf[x] = sc->sc_pattern[x % mod];
413			}
414
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);
419
420		} else if (sc->sc_mode == G_MODEM_MODE_LOOP) {
421
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
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);
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 */
446			usbd_xfer_set_stall(xfer);
447			goto tr_setup;
448		}
449		break;
450	}
451}
452
453static void
454g_modem_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
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)) {
466	case USB_ST_TRANSFERRED:
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;
473			usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_WR]);
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
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);
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 */
493			usbd_xfer_set_stall(xfer);
494			goto tr_setup;
495		}
496		break;
497	}
498}
499
500
501static int
502g_modem_handle_request(device_t dev,
503    const void *preq, void **pptr, uint16_t *plen,
504    uint16_t offset, uint8_t *pstate)
505{
506	struct g_modem_softc *sc = device_get_softc(dev);
507	const struct usb_device_request *req = preq;
508	uint8_t is_complete = *pstate;
509
510	if (!is_complete) {
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)) {
515
516			if (offset == 0) {
517				*plen = sizeof(sc->sc_line_coding);
518				*pptr = &sc->sc_line_coding;
519			} else {
520				*plen = 0;
521			}
522			return (0);
523		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
524		    (req->bRequest == UCDC_SET_COMM_FEATURE)) {
525
526			if (offset == 0) {
527				*plen = sizeof(sc->sc_abstract_state);
528				*pptr = &sc->sc_abstract_state;
529			} else {
530				*plen = 0;
531			}
532			return (0);
533		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
534		    (req->bRequest == UCDC_SET_CONTROL_LINE_STATE)) {
535			*plen = 0;
536			return (0);
537		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
538		    (req->bRequest == UCDC_SEND_BREAK)) {
539			*plen = 0;
540			return (0);
541		}
542	}
543	return (ENXIO);			/* use builtin handler */
544}
545