xref: /freebsd-src/sys/dev/usb/gadget/g_modem.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
146902503SHans Petter Selasky /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
446902503SHans Petter Selasky  * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
546902503SHans Petter Selasky  *
646902503SHans Petter Selasky  * Redistribution and use in source and binary forms, with or without
746902503SHans Petter Selasky  * modification, are permitted provided that the following conditions
846902503SHans Petter Selasky  * are met:
946902503SHans Petter Selasky  * 1. Redistributions of source code must retain the above copyright
1046902503SHans Petter Selasky  *    notice, this list of conditions and the following disclaimer.
1146902503SHans Petter Selasky  * 2. Redistributions in binary form must reproduce the above copyright
1246902503SHans Petter Selasky  *    notice, this list of conditions and the following disclaimer in the
1346902503SHans Petter Selasky  *    documentation and/or other materials provided with the distribution.
1446902503SHans Petter Selasky  *
1546902503SHans Petter Selasky  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1646902503SHans Petter Selasky  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1746902503SHans Petter Selasky  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1846902503SHans Petter Selasky  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1946902503SHans Petter Selasky  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2046902503SHans Petter Selasky  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2146902503SHans Petter Selasky  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2246902503SHans Petter Selasky  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2346902503SHans Petter Selasky  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2446902503SHans Petter Selasky  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2546902503SHans Petter Selasky  * SUCH DAMAGE.
2646902503SHans Petter Selasky  */
2746902503SHans Petter Selasky 
2846902503SHans Petter Selasky /*
2946902503SHans Petter Selasky  * Comm Class spec:  http://www.usb.org/developers/devclass_docs/usbccs10.pdf
3046902503SHans Petter Selasky  *                   http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
3146902503SHans Petter Selasky  *                   http://www.usb.org/developers/devclass_docs/cdc_wmc10.zip
3246902503SHans Petter Selasky  */
3346902503SHans Petter Selasky 
341ae56a1cSDavid E. O'Brien #include <sys/param.h>
3546902503SHans Petter Selasky #include <sys/stdint.h>
3646902503SHans Petter Selasky #include <sys/stddef.h>
3746902503SHans Petter Selasky #include <sys/queue.h>
3846902503SHans Petter Selasky #include <sys/systm.h>
3946902503SHans Petter Selasky #include <sys/kernel.h>
4046902503SHans Petter Selasky #include <sys/bus.h>
4146902503SHans Petter Selasky #include <sys/linker_set.h>
4246902503SHans Petter Selasky #include <sys/module.h>
4346902503SHans Petter Selasky #include <sys/lock.h>
4446902503SHans Petter Selasky #include <sys/mutex.h>
4546902503SHans Petter Selasky #include <sys/condvar.h>
4646902503SHans Petter Selasky #include <sys/sysctl.h>
4746902503SHans Petter Selasky #include <sys/sx.h>
4846902503SHans Petter Selasky #include <sys/unistd.h>
4946902503SHans Petter Selasky #include <sys/callout.h>
5046902503SHans Petter Selasky #include <sys/malloc.h>
5146902503SHans Petter Selasky #include <sys/priv.h>
5246902503SHans Petter Selasky 
5346902503SHans Petter Selasky #include <dev/usb/usb.h>
5446902503SHans Petter Selasky #include <dev/usb/usb_cdc.h>
5546902503SHans Petter Selasky #include <dev/usb/usbdi.h>
5646902503SHans Petter Selasky #include <dev/usb/usbdi_util.h>
5746902503SHans Petter Selasky #include <dev/usb/usbhid.h>
5846902503SHans Petter Selasky #include "usb_if.h"
5946902503SHans Petter Selasky 
6046902503SHans Petter Selasky #define	USB_DEBUG_VAR g_modem_debug
6146902503SHans Petter Selasky #include <dev/usb/usb_debug.h>
6246902503SHans Petter Selasky 
6346902503SHans Petter Selasky #include <dev/usb/gadget/g_modem.h>
6446902503SHans Petter Selasky 
6546902503SHans Petter Selasky enum {
6646902503SHans Petter Selasky 	G_MODEM_INTR_DT,
6746902503SHans Petter Selasky 	G_MODEM_BULK_RD,
6846902503SHans Petter Selasky 	G_MODEM_BULK_WR,
6946902503SHans Petter Selasky 	G_MODEM_N_TRANSFER,
7046902503SHans Petter Selasky };
7146902503SHans Petter Selasky 
7246902503SHans Petter Selasky struct g_modem_softc {
7346902503SHans Petter Selasky 	struct mtx sc_mtx;
7446902503SHans Petter Selasky 	struct usb_callout sc_callout;
7546902503SHans Petter Selasky 	struct usb_callout sc_watchdog;
7646902503SHans Petter Selasky 	struct usb_xfer *sc_xfer[G_MODEM_N_TRANSFER];
7746902503SHans Petter Selasky 
7846902503SHans Petter Selasky 	int	sc_mode;
7946902503SHans Petter Selasky 	int	sc_tx_busy;
8046902503SHans Petter Selasky 	int	sc_pattern_len;
8146902503SHans Petter Selasky 	int	sc_throughput;
8246902503SHans Petter Selasky 	int	sc_tx_interval;
8346902503SHans Petter Selasky 
8446902503SHans Petter Selasky 	char	sc_pattern[G_MODEM_MAX_STRLEN];
8546902503SHans Petter Selasky 
8646902503SHans Petter Selasky 	uint16_t sc_data_len;
8746902503SHans Petter Selasky 
8846902503SHans Petter Selasky 	uint8_t sc_data_buf[G_MODEM_BUFSIZE];
8946902503SHans Petter Selasky 	uint8_t	sc_line_coding[32];
9046902503SHans Petter Selasky 	uint8_t	sc_abstract_state[32];
9146902503SHans Petter Selasky };
9246902503SHans Petter Selasky 
93f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_hw_usb, OID_AUTO, g_modem, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
94f8d2b1f3SPawel Biernacki     "USB modem gadget");
9546902503SHans Petter Selasky 
9646902503SHans Petter Selasky #ifdef USB_DEBUG
9746902503SHans Petter Selasky static int g_modem_debug = 0;
9846902503SHans Petter Selasky 
99ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, debug, CTLFLAG_RWTUN,
10046902503SHans Petter Selasky     &g_modem_debug, 0, "Debug level");
10146902503SHans Petter Selasky #endif
10246902503SHans Petter Selasky 
10346902503SHans Petter Selasky static int g_modem_mode = 0;
10446902503SHans Petter Selasky 
105ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, mode, CTLFLAG_RWTUN,
10646902503SHans Petter Selasky     &g_modem_mode, 0, "Mode selection");
10746902503SHans Petter Selasky 
10846902503SHans Petter Selasky static int g_modem_pattern_interval = 1000;
10946902503SHans Petter Selasky 
110ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, pattern_interval, CTLFLAG_RWTUN,
11146902503SHans Petter Selasky     &g_modem_pattern_interval, 0, "Pattern interval in milliseconds");
11246902503SHans Petter Selasky 
11346902503SHans Petter Selasky static char g_modem_pattern_data[G_MODEM_MAX_STRLEN];
11446902503SHans Petter Selasky 
11546902503SHans Petter Selasky SYSCTL_STRING(_hw_usb_g_modem, OID_AUTO, pattern, CTLFLAG_RW,
11646902503SHans Petter Selasky     &g_modem_pattern_data, sizeof(g_modem_pattern_data), "Data pattern");
11746902503SHans Petter Selasky 
11846902503SHans Petter Selasky static int g_modem_throughput;
11946902503SHans Petter Selasky 
12046902503SHans Petter Selasky SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, throughput, CTLFLAG_RD,
12146902503SHans Petter Selasky     &g_modem_throughput, sizeof(g_modem_throughput), "Throughput in bytes per second");
12246902503SHans Petter Selasky 
12346902503SHans Petter Selasky static device_probe_t g_modem_probe;
12446902503SHans Petter Selasky static device_attach_t g_modem_attach;
12546902503SHans Petter Selasky static device_detach_t g_modem_detach;
12646902503SHans Petter Selasky static usb_handle_request_t g_modem_handle_request;
12746902503SHans Petter Selasky static usb_callback_t g_modem_intr_callback;
12846902503SHans Petter Selasky static usb_callback_t g_modem_bulk_read_callback;
12946902503SHans Petter Selasky static usb_callback_t g_modem_bulk_write_callback;
13046902503SHans Petter Selasky 
13146902503SHans Petter Selasky static void g_modem_timeout(void *arg);
13246902503SHans Petter Selasky 
13346902503SHans Petter Selasky static device_method_t g_modem_methods[] = {
13446902503SHans Petter Selasky 	/* USB interface */
13546902503SHans Petter Selasky 	DEVMETHOD(usb_handle_request, g_modem_handle_request),
13646902503SHans Petter Selasky 
13746902503SHans Petter Selasky 	/* Device interface */
13846902503SHans Petter Selasky 	DEVMETHOD(device_probe, g_modem_probe),
13946902503SHans Petter Selasky 	DEVMETHOD(device_attach, g_modem_attach),
14046902503SHans Petter Selasky 	DEVMETHOD(device_detach, g_modem_detach),
14146902503SHans Petter Selasky 
14246902503SHans Petter Selasky 	DEVMETHOD_END
14346902503SHans Petter Selasky };
14446902503SHans Petter Selasky 
14546902503SHans Petter Selasky static driver_t g_modem_driver = {
14646902503SHans Petter Selasky 	.name = "g_modem",
14746902503SHans Petter Selasky 	.methods = g_modem_methods,
14846902503SHans Petter Selasky 	.size = sizeof(struct g_modem_softc),
14946902503SHans Petter Selasky };
15046902503SHans Petter Selasky 
151bc9372d7SJohn Baldwin DRIVER_MODULE(g_modem, uhub, g_modem_driver, 0, 0);
15246902503SHans Petter Selasky MODULE_DEPEND(g_modem, usb, 1, 1, 1);
15346902503SHans Petter Selasky 
15446902503SHans Petter Selasky static const struct usb_config g_modem_config[G_MODEM_N_TRANSFER] = {
15546902503SHans Petter Selasky 	[G_MODEM_INTR_DT] = {
15646902503SHans Petter Selasky 		.type = UE_INTERRUPT,
15746902503SHans Petter Selasky 		.endpoint = UE_ADDR_ANY,
15846902503SHans Petter Selasky 		.direction = UE_DIR_TX,
15946902503SHans Petter Selasky 		.flags = {.ext_buffer = 1,.pipe_bof = 1,},
16046902503SHans Petter Selasky 		.bufsize = 0,	/* use wMaxPacketSize */
16146902503SHans Petter Selasky 		.callback = &g_modem_intr_callback,
16246902503SHans Petter Selasky 		.frames = 1,
16346902503SHans Petter Selasky 		.usb_mode = USB_MODE_DEVICE,
16446902503SHans Petter Selasky 		.if_index = 0,
16546902503SHans Petter Selasky 	},
16646902503SHans Petter Selasky 
16746902503SHans Petter Selasky 	[G_MODEM_BULK_RD] = {
16846902503SHans Petter Selasky 		.type = UE_BULK,
16946902503SHans Petter Selasky 		.endpoint = UE_ADDR_ANY,
17046902503SHans Petter Selasky 		.direction = UE_DIR_RX,
17146902503SHans Petter Selasky 		.flags = {.ext_buffer = 1,.pipe_bof = 1,.short_xfer_ok = 1,},
17246902503SHans Petter Selasky 		.bufsize = G_MODEM_BUFSIZE,
17346902503SHans Petter Selasky 		.callback = &g_modem_bulk_read_callback,
17446902503SHans Petter Selasky 		.frames = 1,
17546902503SHans Petter Selasky 		.usb_mode = USB_MODE_DEVICE,
17646902503SHans Petter Selasky 		.if_index = 1,
17746902503SHans Petter Selasky 	},
17846902503SHans Petter Selasky 
17946902503SHans Petter Selasky 	[G_MODEM_BULK_WR] = {
18046902503SHans Petter Selasky 		.type = UE_BULK,
18146902503SHans Petter Selasky 		.endpoint = UE_ADDR_ANY,
18246902503SHans Petter Selasky 		.direction = UE_DIR_TX,
18346902503SHans Petter Selasky 		.flags = {.ext_buffer = 1,.pipe_bof = 1,},
18446902503SHans Petter Selasky 		.bufsize = G_MODEM_BUFSIZE,
18546902503SHans Petter Selasky 		.callback = &g_modem_bulk_write_callback,
18646902503SHans Petter Selasky 		.frames = 1,
18746902503SHans Petter Selasky 		.usb_mode = USB_MODE_DEVICE,
18846902503SHans Petter Selasky 		.if_index = 1,
18946902503SHans Petter Selasky 	},
19046902503SHans Petter Selasky };
19146902503SHans Petter Selasky 
19246902503SHans Petter Selasky static void
g_modem_timeout_reset(struct g_modem_softc * sc)19346902503SHans Petter Selasky g_modem_timeout_reset(struct g_modem_softc *sc)
19446902503SHans Petter Selasky {
19546902503SHans Petter Selasky 	int i = g_modem_pattern_interval;
19646902503SHans Petter Selasky 
19746902503SHans Petter Selasky 	sc->sc_tx_interval = i;
19846902503SHans Petter Selasky 
19946902503SHans Petter Selasky 	if (i <= 0)
20046902503SHans Petter Selasky 		i = 1;
20146902503SHans Petter Selasky 	else if (i > 1023)
20246902503SHans Petter Selasky 		i = 1023;
20346902503SHans Petter Selasky 
20446902503SHans Petter Selasky 	i = USB_MS_TO_TICKS(i);
20546902503SHans Petter Selasky 
20646902503SHans Petter Selasky 	usb_callout_reset(&sc->sc_callout, i, &g_modem_timeout, sc);
20746902503SHans Petter Selasky }
20846902503SHans Petter Selasky 
20946902503SHans Petter Selasky static void
g_modem_timeout(void * arg)21046902503SHans Petter Selasky g_modem_timeout(void *arg)
21146902503SHans Petter Selasky {
21246902503SHans Petter Selasky 	struct g_modem_softc *sc = arg;
21346902503SHans Petter Selasky 
21446902503SHans Petter Selasky 	sc->sc_mode = g_modem_mode;
21546902503SHans Petter Selasky 
21646902503SHans Petter Selasky 	memcpy(sc->sc_pattern, g_modem_pattern_data, sizeof(sc->sc_pattern));
21746902503SHans Petter Selasky 
21846902503SHans Petter Selasky 	sc->sc_pattern[G_MODEM_MAX_STRLEN - 1] = 0;
21946902503SHans Petter Selasky 
22046902503SHans Petter Selasky 	sc->sc_pattern_len = strlen(sc->sc_pattern);
22146902503SHans Petter Selasky 
22246902503SHans Petter Selasky 	DPRINTFN(11, "Timeout %p\n", sc->sc_xfer[G_MODEM_INTR_DT]);
22346902503SHans Petter Selasky 
22446902503SHans Petter Selasky 	usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_WR]);
22546902503SHans Petter Selasky 	usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_RD]);
22646902503SHans Petter Selasky 
22746902503SHans Petter Selasky 	g_modem_timeout_reset(sc);
22846902503SHans Petter Selasky }
22946902503SHans Petter Selasky 
23046902503SHans Petter Selasky static void g_modem_watchdog(void *arg);
23146902503SHans Petter Selasky 
23246902503SHans Petter Selasky static void
g_modem_watchdog_reset(struct g_modem_softc * sc)23346902503SHans Petter Selasky g_modem_watchdog_reset(struct g_modem_softc *sc)
23446902503SHans Petter Selasky {
23546902503SHans Petter Selasky 	usb_callout_reset(&sc->sc_watchdog, hz, &g_modem_watchdog, sc);
23646902503SHans Petter Selasky }
23746902503SHans Petter Selasky 
23846902503SHans Petter Selasky static void
g_modem_watchdog(void * arg)23946902503SHans Petter Selasky g_modem_watchdog(void *arg)
24046902503SHans Petter Selasky {
24146902503SHans Petter Selasky 	struct g_modem_softc *sc = arg;
24246902503SHans Petter Selasky 	int i;
24346902503SHans Petter Selasky 
24446902503SHans Petter Selasky 	i = sc->sc_throughput;
24546902503SHans Petter Selasky 
24646902503SHans Petter Selasky 	sc->sc_throughput = 0;
24746902503SHans Petter Selasky 
24846902503SHans Petter Selasky 	g_modem_throughput = i;
24946902503SHans Petter Selasky 
25046902503SHans Petter Selasky 	g_modem_watchdog_reset(sc);
25146902503SHans Petter Selasky }
25246902503SHans Petter Selasky 
25346902503SHans Petter Selasky static int
g_modem_probe(device_t dev)25446902503SHans Petter Selasky g_modem_probe(device_t dev)
25546902503SHans Petter Selasky {
25646902503SHans Petter Selasky 	struct usb_attach_arg *uaa = device_get_ivars(dev);
25746902503SHans Petter Selasky 
25846902503SHans Petter Selasky 	DPRINTFN(11, "\n");
25946902503SHans Petter Selasky 
26046902503SHans Petter Selasky 	if (uaa->usb_mode != USB_MODE_DEVICE)
26146902503SHans Petter Selasky 		return (ENXIO);
26246902503SHans Petter Selasky 
26346902503SHans Petter Selasky 	if ((uaa->info.bInterfaceClass == UICLASS_CDC) &&
26446902503SHans Petter Selasky 	    (uaa->info.bInterfaceSubClass == UISUBCLASS_ABSTRACT_CONTROL_MODEL) &&
26546902503SHans Petter Selasky 	    (uaa->info.bInterfaceProtocol == UIPROTO_CDC_AT))
26646902503SHans Petter Selasky 		return (0);
26746902503SHans Petter Selasky 
26846902503SHans Petter Selasky 	return (ENXIO);
26946902503SHans Petter Selasky }
27046902503SHans Petter Selasky 
27146902503SHans Petter Selasky static int
g_modem_attach(device_t dev)27246902503SHans Petter Selasky g_modem_attach(device_t dev)
27346902503SHans Petter Selasky {
27446902503SHans Petter Selasky 	struct g_modem_softc *sc = device_get_softc(dev);
27546902503SHans Petter Selasky 	struct usb_attach_arg *uaa = device_get_ivars(dev);
27646902503SHans Petter Selasky 	int error;
27746902503SHans Petter Selasky 	uint8_t iface_index[2];
27846902503SHans Petter Selasky 
27946902503SHans Petter Selasky 	DPRINTFN(11, "\n");
28046902503SHans Petter Selasky 
28146902503SHans Petter Selasky 	device_set_usb_desc(dev);
28246902503SHans Petter Selasky 
28346902503SHans Petter Selasky 	mtx_init(&sc->sc_mtx, "g_modem", NULL, MTX_DEF);
28446902503SHans Petter Selasky 
28546902503SHans Petter Selasky 	usb_callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0);
28646902503SHans Petter Selasky 	usb_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0);
28746902503SHans Petter Selasky 
28846902503SHans Petter Selasky 	sc->sc_mode = G_MODEM_MODE_SILENT;
28946902503SHans Petter Selasky 
29046902503SHans Petter Selasky 	iface_index[0] = uaa->info.bIfaceIndex;
29146902503SHans Petter Selasky 	iface_index[1] = uaa->info.bIfaceIndex + 1;
29246902503SHans Petter Selasky 
29346902503SHans Petter Selasky 	error = usbd_transfer_setup(uaa->device,
29446902503SHans Petter Selasky 	    iface_index, sc->sc_xfer, g_modem_config,
29546902503SHans Petter Selasky 	    G_MODEM_N_TRANSFER, sc, &sc->sc_mtx);
29646902503SHans Petter Selasky 
29746902503SHans Petter Selasky 	if (error) {
29846902503SHans Petter Selasky 		DPRINTF("error=%s\n", usbd_errstr(error));
29946902503SHans Petter Selasky 		goto detach;
30046902503SHans Petter Selasky 	}
30146902503SHans Petter Selasky 	usbd_set_parent_iface(uaa->device, iface_index[1], iface_index[0]);
30246902503SHans Petter Selasky 
30346902503SHans Petter Selasky 	mtx_lock(&sc->sc_mtx);
30446902503SHans Petter Selasky 	g_modem_timeout_reset(sc);
30546902503SHans Petter Selasky 	g_modem_watchdog_reset(sc);
30646902503SHans Petter Selasky 	mtx_unlock(&sc->sc_mtx);
30746902503SHans Petter Selasky 
30846902503SHans Petter Selasky 	return (0);			/* success */
30946902503SHans Petter Selasky 
31046902503SHans Petter Selasky detach:
31146902503SHans Petter Selasky 	g_modem_detach(dev);
31246902503SHans Petter Selasky 
31346902503SHans Petter Selasky 	return (ENXIO);			/* error */
31446902503SHans Petter Selasky }
31546902503SHans Petter Selasky 
31646902503SHans Petter Selasky static int
g_modem_detach(device_t dev)31746902503SHans Petter Selasky g_modem_detach(device_t dev)
31846902503SHans Petter Selasky {
31946902503SHans Petter Selasky 	struct g_modem_softc *sc = device_get_softc(dev);
32046902503SHans Petter Selasky 
32146902503SHans Petter Selasky 	DPRINTF("\n");
32246902503SHans Petter Selasky 
32346902503SHans Petter Selasky 	mtx_lock(&sc->sc_mtx);
32446902503SHans Petter Selasky 	usb_callout_stop(&sc->sc_callout);
32546902503SHans Petter Selasky 	usb_callout_stop(&sc->sc_watchdog);
32646902503SHans Petter Selasky 	mtx_unlock(&sc->sc_mtx);
32746902503SHans Petter Selasky 
32846902503SHans Petter Selasky 	usbd_transfer_unsetup(sc->sc_xfer, G_MODEM_N_TRANSFER);
32946902503SHans Petter Selasky 
33046902503SHans Petter Selasky 	usb_callout_drain(&sc->sc_callout);
33146902503SHans Petter Selasky 	usb_callout_drain(&sc->sc_watchdog);
33246902503SHans Petter Selasky 
33346902503SHans Petter Selasky 	mtx_destroy(&sc->sc_mtx);
33446902503SHans Petter Selasky 
33546902503SHans Petter Selasky 	return (0);
33646902503SHans Petter Selasky }
33746902503SHans Petter Selasky 
33846902503SHans Petter Selasky static void
g_modem_intr_callback(struct usb_xfer * xfer,usb_error_t error)33946902503SHans Petter Selasky g_modem_intr_callback(struct usb_xfer *xfer, usb_error_t error)
34046902503SHans Petter Selasky {
34146902503SHans Petter Selasky 	int actlen;
34246902503SHans Petter Selasky 	int aframes;
34346902503SHans Petter Selasky 
34446902503SHans Petter Selasky 	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
34546902503SHans Petter Selasky 
34646902503SHans Petter Selasky 	DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
34746902503SHans Petter Selasky 	    USB_GET_STATE(xfer), aframes, actlen);
34846902503SHans Petter Selasky 
34946902503SHans Petter Selasky 	switch (USB_GET_STATE(xfer)) {
35046902503SHans Petter Selasky 	case USB_ST_TRANSFERRED:
35146902503SHans Petter Selasky 		break;
35246902503SHans Petter Selasky 
35346902503SHans Petter Selasky 	case USB_ST_SETUP:
35446902503SHans Petter Selasky tr_setup:
35546902503SHans Petter Selasky 		break;
35646902503SHans Petter Selasky 
35746902503SHans Petter Selasky 	default:			/* Error */
35846902503SHans Petter Selasky 		DPRINTF("error=%s\n", usbd_errstr(error));
35946902503SHans Petter Selasky 
36046902503SHans Petter Selasky 		if (error != USB_ERR_CANCELLED) {
36146902503SHans Petter Selasky 			/* try to clear stall first */
36246902503SHans Petter Selasky 			usbd_xfer_set_stall(xfer);
36346902503SHans Petter Selasky 			goto tr_setup;
36446902503SHans Petter Selasky 		}
36546902503SHans Petter Selasky 		break;
36646902503SHans Petter Selasky 	}
36746902503SHans Petter Selasky }
36846902503SHans Petter Selasky 
36946902503SHans Petter Selasky static void
g_modem_bulk_write_callback(struct usb_xfer * xfer,usb_error_t error)37046902503SHans Petter Selasky g_modem_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
37146902503SHans Petter Selasky {
37246902503SHans Petter Selasky 	struct g_modem_softc *sc = usbd_xfer_softc(xfer);
37346902503SHans Petter Selasky 	int actlen;
37446902503SHans Petter Selasky 	int aframes;
37546902503SHans Petter Selasky 	int mod;
37646902503SHans Petter Selasky 	int x;
37746902503SHans Petter Selasky 	int max;
37846902503SHans Petter Selasky 
37946902503SHans Petter Selasky 	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
38046902503SHans Petter Selasky 
38146902503SHans Petter Selasky 	DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
38246902503SHans Petter Selasky 	    USB_GET_STATE(xfer), aframes, actlen);
38346902503SHans Petter Selasky 
38446902503SHans Petter Selasky 	switch (USB_GET_STATE(xfer)) {
38546902503SHans Petter Selasky 	case USB_ST_TRANSFERRED:
38646902503SHans Petter Selasky 
38746902503SHans Petter Selasky 		sc->sc_tx_busy = 0;
38846902503SHans Petter Selasky 		sc->sc_throughput += actlen;
38946902503SHans Petter Selasky 
39046902503SHans Petter Selasky 		if (sc->sc_mode == G_MODEM_MODE_LOOP) {
39146902503SHans Petter Selasky 			/* start loop */
39246902503SHans Petter Selasky 			usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_RD]);
39346902503SHans Petter Selasky 			break;
39446902503SHans Petter Selasky 		} else if ((sc->sc_mode == G_MODEM_MODE_PATTERN) && (sc->sc_tx_interval != 0)) {
39546902503SHans Petter Selasky 			/* wait for next timeout */
39646902503SHans Petter Selasky 			break;
39746902503SHans Petter Selasky 		}
39846902503SHans Petter Selasky 	case USB_ST_SETUP:
39946902503SHans Petter Selasky tr_setup:
40046902503SHans Petter Selasky 		if (sc->sc_mode == G_MODEM_MODE_PATTERN) {
40146902503SHans Petter Selasky 			mod = sc->sc_pattern_len;
40246902503SHans Petter Selasky 			max = sc->sc_tx_interval ? mod : G_MODEM_BUFSIZE;
40346902503SHans Petter Selasky 
40446902503SHans Petter Selasky 			if (mod == 0) {
40546902503SHans Petter Selasky 				for (x = 0; x != max; x++)
40646902503SHans Petter Selasky 					sc->sc_data_buf[x] = x % 255;
40746902503SHans Petter Selasky 			} else {
40846902503SHans Petter Selasky 				for (x = 0; x != max; x++)
40946902503SHans Petter Selasky 					sc->sc_data_buf[x] = sc->sc_pattern[x % mod];
41046902503SHans Petter Selasky 			}
41146902503SHans Petter Selasky 
41246902503SHans Petter Selasky 			usbd_xfer_set_frame_data(xfer, 0, sc->sc_data_buf, max);
41346902503SHans Petter Selasky 			usbd_xfer_set_interval(xfer, 0);
41446902503SHans Petter Selasky 			usbd_xfer_set_frames(xfer, 1);
41546902503SHans Petter Selasky 			usbd_transfer_submit(xfer);
41646902503SHans Petter Selasky 
41746902503SHans Petter Selasky 		} else if (sc->sc_mode == G_MODEM_MODE_LOOP) {
41846902503SHans Petter Selasky 			if (sc->sc_tx_busy == 0)
41946902503SHans Petter Selasky 				break;
42046902503SHans Petter Selasky 
42146902503SHans Petter Selasky 			x = sc->sc_tx_interval;
42246902503SHans Petter Selasky 
42346902503SHans Petter Selasky 			if (x < 0)
42446902503SHans Petter Selasky 				x = 0;
42546902503SHans Petter Selasky 			else if (x > 256)
42646902503SHans Petter Selasky 				x = 256;
42746902503SHans Petter Selasky 
42846902503SHans Petter Selasky 			usbd_xfer_set_frame_data(xfer, 0, sc->sc_data_buf, sc->sc_data_len);
42946902503SHans Petter Selasky 			usbd_xfer_set_interval(xfer, x);
43046902503SHans Petter Selasky 			usbd_xfer_set_frames(xfer, 1);
43146902503SHans Petter Selasky 			usbd_transfer_submit(xfer);
43246902503SHans Petter Selasky 		} else {
43346902503SHans Petter Selasky 			sc->sc_tx_busy = 0;
43446902503SHans Petter Selasky 		}
43546902503SHans Petter Selasky 		break;
43646902503SHans Petter Selasky 
43746902503SHans Petter Selasky 	default:			/* Error */
43846902503SHans Petter Selasky 		DPRINTF("error=%s\n", usbd_errstr(error));
43946902503SHans Petter Selasky 
44046902503SHans Petter Selasky 		if (error != USB_ERR_CANCELLED) {
44146902503SHans Petter Selasky 			/* try to clear stall first */
44246902503SHans Petter Selasky 			usbd_xfer_set_stall(xfer);
44346902503SHans Petter Selasky 			goto tr_setup;
44446902503SHans Petter Selasky 		}
44546902503SHans Petter Selasky 		break;
44646902503SHans Petter Selasky 	}
44746902503SHans Petter Selasky }
44846902503SHans Petter Selasky 
44946902503SHans Petter Selasky static void
g_modem_bulk_read_callback(struct usb_xfer * xfer,usb_error_t error)45046902503SHans Petter Selasky g_modem_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
45146902503SHans Petter Selasky {
45246902503SHans Petter Selasky 	struct g_modem_softc *sc = usbd_xfer_softc(xfer);
45346902503SHans Petter Selasky 	int actlen;
45446902503SHans Petter Selasky 	int aframes;
45546902503SHans Petter Selasky 
45646902503SHans Petter Selasky 	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
45746902503SHans Petter Selasky 
45846902503SHans Petter Selasky 	DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
45946902503SHans Petter Selasky 	    USB_GET_STATE(xfer), aframes, actlen);
46046902503SHans Petter Selasky 
46146902503SHans Petter Selasky 	switch (USB_GET_STATE(xfer)) {
46246902503SHans Petter Selasky 	case USB_ST_TRANSFERRED:
46346902503SHans Petter Selasky 
46446902503SHans Petter Selasky 		sc->sc_throughput += actlen;
46546902503SHans Petter Selasky 
46646902503SHans Petter Selasky 		if (sc->sc_mode == G_MODEM_MODE_LOOP) {
46746902503SHans Petter Selasky 			sc->sc_tx_busy = 1;
46846902503SHans Petter Selasky 			sc->sc_data_len = actlen;
46946902503SHans Petter Selasky 			usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_WR]);
47046902503SHans Petter Selasky 			break;
47146902503SHans Petter Selasky 		}
47246902503SHans Petter Selasky 
47346902503SHans Petter Selasky 	case USB_ST_SETUP:
47446902503SHans Petter Selasky tr_setup:
47546902503SHans Petter Selasky 		if ((sc->sc_mode == G_MODEM_MODE_SILENT) ||
47646902503SHans Petter Selasky 		    (sc->sc_tx_busy != 0))
47746902503SHans Petter Selasky 			break;
47846902503SHans Petter Selasky 
47946902503SHans Petter Selasky 		usbd_xfer_set_frame_data(xfer, 0, sc->sc_data_buf, G_MODEM_BUFSIZE);
48046902503SHans Petter Selasky 		usbd_xfer_set_frames(xfer, 1);
48146902503SHans Petter Selasky 		usbd_transfer_submit(xfer);
48246902503SHans Petter Selasky 		break;
48346902503SHans Petter Selasky 
48446902503SHans Petter Selasky 	default:			/* Error */
48546902503SHans Petter Selasky 		DPRINTF("error=%s\n", usbd_errstr(error));
48646902503SHans Petter Selasky 
48746902503SHans Petter Selasky 		if (error != USB_ERR_CANCELLED) {
48846902503SHans Petter Selasky 			/* try to clear stall first */
48946902503SHans Petter Selasky 			usbd_xfer_set_stall(xfer);
49046902503SHans Petter Selasky 			goto tr_setup;
49146902503SHans Petter Selasky 		}
49246902503SHans Petter Selasky 		break;
49346902503SHans Petter Selasky 	}
49446902503SHans Petter Selasky }
49546902503SHans Petter Selasky 
49646902503SHans Petter Selasky static int
g_modem_handle_request(device_t dev,const void * preq,void ** pptr,uint16_t * plen,uint16_t offset,uint8_t * pstate)49746902503SHans Petter Selasky g_modem_handle_request(device_t dev,
49846902503SHans Petter Selasky     const void *preq, void **pptr, uint16_t *plen,
49946902503SHans Petter Selasky     uint16_t offset, uint8_t *pstate)
50046902503SHans Petter Selasky {
50146902503SHans Petter Selasky 	struct g_modem_softc *sc = device_get_softc(dev);
50246902503SHans Petter Selasky 	const struct usb_device_request *req = preq;
50346902503SHans Petter Selasky 	uint8_t is_complete = *pstate;
50446902503SHans Petter Selasky 
50546902503SHans Petter Selasky 	if (!is_complete) {
50646902503SHans Petter Selasky 		if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
50746902503SHans Petter Selasky 		    (req->bRequest == UCDC_SET_LINE_CODING) &&
50846902503SHans Petter Selasky 		    (req->wValue[0] == 0x00) &&
50946902503SHans Petter Selasky 		    (req->wValue[1] == 0x00)) {
51046902503SHans Petter Selasky 			if (offset == 0) {
51146902503SHans Petter Selasky 				*plen = sizeof(sc->sc_line_coding);
51246902503SHans Petter Selasky 				*pptr = &sc->sc_line_coding;
51346902503SHans Petter Selasky 			} else {
51446902503SHans Petter Selasky 				*plen = 0;
51546902503SHans Petter Selasky 			}
51646902503SHans Petter Selasky 			return (0);
51746902503SHans Petter Selasky 		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
51846902503SHans Petter Selasky 		    (req->bRequest == UCDC_SET_COMM_FEATURE)) {
51946902503SHans Petter Selasky 			if (offset == 0) {
52046902503SHans Petter Selasky 				*plen = sizeof(sc->sc_abstract_state);
52146902503SHans Petter Selasky 				*pptr = &sc->sc_abstract_state;
52246902503SHans Petter Selasky 			} else {
52346902503SHans Petter Selasky 				*plen = 0;
52446902503SHans Petter Selasky 			}
52546902503SHans Petter Selasky 			return (0);
52646902503SHans Petter Selasky 		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
52746902503SHans Petter Selasky 		    (req->bRequest == UCDC_SET_CONTROL_LINE_STATE)) {
52846902503SHans Petter Selasky 			*plen = 0;
52946902503SHans Petter Selasky 			return (0);
53046902503SHans Petter Selasky 		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
53146902503SHans Petter Selasky 		    (req->bRequest == UCDC_SEND_BREAK)) {
53246902503SHans Petter Selasky 			*plen = 0;
53346902503SHans Petter Selasky 			return (0);
53446902503SHans Petter Selasky 		}
53546902503SHans Petter Selasky 	}
53646902503SHans Petter Selasky 	return (ENXIO);			/* use builtin handler */
53746902503SHans Petter Selasky }
538