1253544Shselasky/*- 2253544Shselasky * Copyright (c) 2010 Hans Petter Selasky. All rights reserved. 3253544Shselasky * 4253544Shselasky * Redistribution and use in source and binary forms, with or without 5253544Shselasky * modification, are permitted provided that the following conditions 6253544Shselasky * are met: 7253544Shselasky * 1. Redistributions of source code must retain the above copyright 8253544Shselasky * notice, this list of conditions and the following disclaimer. 9253544Shselasky * 2. Redistributions in binary form must reproduce the above copyright 10253544Shselasky * notice, this list of conditions and the following disclaimer in the 11253544Shselasky * documentation and/or other materials provided with the distribution. 12253544Shselasky * 13253544Shselasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14253544Shselasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15253544Shselasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16253544Shselasky * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17253544Shselasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18253544Shselasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19253544Shselasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20253544Shselasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21253544Shselasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22253544Shselasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23253544Shselasky * SUCH DAMAGE. 24253544Shselasky */ 25253544Shselasky 26253544Shselasky/* 27253544Shselasky * Comm Class spec: http://www.usb.org/developers/devclass_docs/usbccs10.pdf 28253544Shselasky * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf 29253544Shselasky * http://www.usb.org/developers/devclass_docs/cdc_wmc10.zip 30253544Shselasky */ 31253544Shselasky 32253618Sobrien#include <sys/param.h> 33253618Sobrien__FBSDID("$FreeBSD: releng/11.0/sys/dev/usb/gadget/g_modem.c 276701 2015-01-05 15:04:17Z hselasky $"); 34253618Sobrien 35253544Shselasky#include <sys/stdint.h> 36253544Shselasky#include <sys/stddef.h> 37253544Shselasky#include <sys/queue.h> 38253544Shselasky#include <sys/systm.h> 39253544Shselasky#include <sys/kernel.h> 40253544Shselasky#include <sys/bus.h> 41253544Shselasky#include <sys/linker_set.h> 42253544Shselasky#include <sys/module.h> 43253544Shselasky#include <sys/lock.h> 44253544Shselasky#include <sys/mutex.h> 45253544Shselasky#include <sys/condvar.h> 46253544Shselasky#include <sys/sysctl.h> 47253544Shselasky#include <sys/sx.h> 48253544Shselasky#include <sys/unistd.h> 49253544Shselasky#include <sys/callout.h> 50253544Shselasky#include <sys/malloc.h> 51253544Shselasky#include <sys/priv.h> 52253544Shselasky 53253544Shselasky#include <dev/usb/usb.h> 54253544Shselasky#include <dev/usb/usb_cdc.h> 55253544Shselasky#include <dev/usb/usbdi.h> 56253544Shselasky#include <dev/usb/usbdi_util.h> 57253544Shselasky#include <dev/usb/usbhid.h> 58253544Shselasky#include "usb_if.h" 59253544Shselasky 60253544Shselasky#define USB_DEBUG_VAR g_modem_debug 61253544Shselasky#include <dev/usb/usb_debug.h> 62253544Shselasky 63253544Shselasky#include <dev/usb/gadget/g_modem.h> 64253544Shselasky 65253544Shselaskyenum { 66253544Shselasky G_MODEM_INTR_DT, 67253544Shselasky G_MODEM_BULK_RD, 68253544Shselasky G_MODEM_BULK_WR, 69253544Shselasky G_MODEM_N_TRANSFER, 70253544Shselasky}; 71253544Shselasky 72253544Shselaskystruct g_modem_softc { 73253544Shselasky struct mtx sc_mtx; 74253544Shselasky struct usb_callout sc_callout; 75253544Shselasky struct usb_callout sc_watchdog; 76253544Shselasky struct usb_xfer *sc_xfer[G_MODEM_N_TRANSFER]; 77253544Shselasky 78253544Shselasky int sc_mode; 79253544Shselasky int sc_tx_busy; 80253544Shselasky int sc_pattern_len; 81253544Shselasky int sc_throughput; 82253544Shselasky int sc_tx_interval; 83253544Shselasky 84253544Shselasky char sc_pattern[G_MODEM_MAX_STRLEN]; 85253544Shselasky 86253544Shselasky uint16_t sc_data_len; 87253544Shselasky 88253544Shselasky uint8_t sc_data_buf[G_MODEM_BUFSIZE]; 89253544Shselasky uint8_t sc_line_coding[32]; 90253544Shselasky uint8_t sc_abstract_state[32]; 91253544Shselasky}; 92253544Shselasky 93253544Shselaskystatic SYSCTL_NODE(_hw_usb, OID_AUTO, g_modem, CTLFLAG_RW, 0, "USB modem gadget"); 94253544Shselasky 95253544Shselasky#ifdef USB_DEBUG 96253544Shselaskystatic int g_modem_debug = 0; 97253544Shselasky 98276701ShselaskySYSCTL_INT(_hw_usb_g_modem, OID_AUTO, debug, CTLFLAG_RWTUN, 99253544Shselasky &g_modem_debug, 0, "Debug level"); 100253544Shselasky#endif 101253544Shselasky 102253544Shselaskystatic int g_modem_mode = 0; 103253544Shselasky 104276701ShselaskySYSCTL_INT(_hw_usb_g_modem, OID_AUTO, mode, CTLFLAG_RWTUN, 105253544Shselasky &g_modem_mode, 0, "Mode selection"); 106253544Shselasky 107253544Shselaskystatic int g_modem_pattern_interval = 1000; 108253544Shselasky 109276701ShselaskySYSCTL_INT(_hw_usb_g_modem, OID_AUTO, pattern_interval, CTLFLAG_RWTUN, 110253544Shselasky &g_modem_pattern_interval, 0, "Pattern interval in milliseconds"); 111253544Shselasky 112253544Shselaskystatic char g_modem_pattern_data[G_MODEM_MAX_STRLEN]; 113253544Shselasky 114253544ShselaskySYSCTL_STRING(_hw_usb_g_modem, OID_AUTO, pattern, CTLFLAG_RW, 115253544Shselasky &g_modem_pattern_data, sizeof(g_modem_pattern_data), "Data pattern"); 116253544Shselasky 117253544Shselaskystatic int g_modem_throughput; 118253544Shselasky 119253544ShselaskySYSCTL_INT(_hw_usb_g_modem, OID_AUTO, throughput, CTLFLAG_RD, 120253544Shselasky &g_modem_throughput, sizeof(g_modem_throughput), "Throughput in bytes per second"); 121253544Shselasky 122253544Shselaskystatic device_probe_t g_modem_probe; 123253544Shselaskystatic device_attach_t g_modem_attach; 124253544Shselaskystatic device_detach_t g_modem_detach; 125253544Shselaskystatic usb_handle_request_t g_modem_handle_request; 126253544Shselaskystatic usb_callback_t g_modem_intr_callback; 127253544Shselaskystatic usb_callback_t g_modem_bulk_read_callback; 128253544Shselaskystatic usb_callback_t g_modem_bulk_write_callback; 129253544Shselasky 130253544Shselaskystatic void g_modem_timeout(void *arg); 131253544Shselasky 132253544Shselaskystatic devclass_t g_modem_devclass; 133253544Shselasky 134253544Shselaskystatic device_method_t g_modem_methods[] = { 135253544Shselasky /* USB interface */ 136253544Shselasky DEVMETHOD(usb_handle_request, g_modem_handle_request), 137253544Shselasky 138253544Shselasky /* Device interface */ 139253544Shselasky DEVMETHOD(device_probe, g_modem_probe), 140253544Shselasky DEVMETHOD(device_attach, g_modem_attach), 141253544Shselasky DEVMETHOD(device_detach, g_modem_detach), 142253544Shselasky 143253544Shselasky DEVMETHOD_END 144253544Shselasky}; 145253544Shselasky 146253544Shselaskystatic driver_t g_modem_driver = { 147253544Shselasky .name = "g_modem", 148253544Shselasky .methods = g_modem_methods, 149253544Shselasky .size = sizeof(struct g_modem_softc), 150253544Shselasky}; 151253544Shselasky 152253544ShselaskyDRIVER_MODULE(g_modem, uhub, g_modem_driver, g_modem_devclass, 0, 0); 153253544ShselaskyMODULE_DEPEND(g_modem, usb, 1, 1, 1); 154253544Shselasky 155253544Shselaskystatic const struct usb_config g_modem_config[G_MODEM_N_TRANSFER] = { 156253544Shselasky 157253544Shselasky [G_MODEM_INTR_DT] = { 158253544Shselasky .type = UE_INTERRUPT, 159253544Shselasky .endpoint = UE_ADDR_ANY, 160253544Shselasky .direction = UE_DIR_TX, 161253544Shselasky .flags = {.ext_buffer = 1,.pipe_bof = 1,}, 162253544Shselasky .bufsize = 0, /* use wMaxPacketSize */ 163253544Shselasky .callback = &g_modem_intr_callback, 164253544Shselasky .frames = 1, 165253544Shselasky .usb_mode = USB_MODE_DEVICE, 166253544Shselasky .if_index = 0, 167253544Shselasky }, 168253544Shselasky 169253544Shselasky [G_MODEM_BULK_RD] = { 170253544Shselasky .type = UE_BULK, 171253544Shselasky .endpoint = UE_ADDR_ANY, 172253544Shselasky .direction = UE_DIR_RX, 173253544Shselasky .flags = {.ext_buffer = 1,.pipe_bof = 1,.short_xfer_ok = 1,}, 174253544Shselasky .bufsize = G_MODEM_BUFSIZE, 175253544Shselasky .callback = &g_modem_bulk_read_callback, 176253544Shselasky .frames = 1, 177253544Shselasky .usb_mode = USB_MODE_DEVICE, 178253544Shselasky .if_index = 1, 179253544Shselasky }, 180253544Shselasky 181253544Shselasky [G_MODEM_BULK_WR] = { 182253544Shselasky .type = UE_BULK, 183253544Shselasky .endpoint = UE_ADDR_ANY, 184253544Shselasky .direction = UE_DIR_TX, 185253544Shselasky .flags = {.ext_buffer = 1,.pipe_bof = 1,}, 186253544Shselasky .bufsize = G_MODEM_BUFSIZE, 187253544Shselasky .callback = &g_modem_bulk_write_callback, 188253544Shselasky .frames = 1, 189253544Shselasky .usb_mode = USB_MODE_DEVICE, 190253544Shselasky .if_index = 1, 191253544Shselasky }, 192253544Shselasky}; 193253544Shselasky 194253544Shselaskystatic void 195253544Shselaskyg_modem_timeout_reset(struct g_modem_softc *sc) 196253544Shselasky{ 197253544Shselasky int i = g_modem_pattern_interval; 198253544Shselasky 199253544Shselasky sc->sc_tx_interval = i; 200253544Shselasky 201253544Shselasky if (i <= 0) 202253544Shselasky i = 1; 203253544Shselasky else if (i > 1023) 204253544Shselasky i = 1023; 205253544Shselasky 206253544Shselasky i = USB_MS_TO_TICKS(i); 207253544Shselasky 208253544Shselasky usb_callout_reset(&sc->sc_callout, i, &g_modem_timeout, sc); 209253544Shselasky} 210253544Shselasky 211253544Shselaskystatic void 212253544Shselaskyg_modem_timeout(void *arg) 213253544Shselasky{ 214253544Shselasky struct g_modem_softc *sc = arg; 215253544Shselasky 216253544Shselasky sc->sc_mode = g_modem_mode; 217253544Shselasky 218253544Shselasky memcpy(sc->sc_pattern, g_modem_pattern_data, sizeof(sc->sc_pattern)); 219253544Shselasky 220253544Shselasky sc->sc_pattern[G_MODEM_MAX_STRLEN - 1] = 0; 221253544Shselasky 222253544Shselasky sc->sc_pattern_len = strlen(sc->sc_pattern); 223253544Shselasky 224253544Shselasky DPRINTFN(11, "Timeout %p\n", sc->sc_xfer[G_MODEM_INTR_DT]); 225253544Shselasky 226253544Shselasky usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_WR]); 227253544Shselasky usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_RD]); 228253544Shselasky 229253544Shselasky g_modem_timeout_reset(sc); 230253544Shselasky} 231253544Shselasky 232253544Shselaskystatic void g_modem_watchdog(void *arg); 233253544Shselasky 234253544Shselaskystatic void 235253544Shselaskyg_modem_watchdog_reset(struct g_modem_softc *sc) 236253544Shselasky{ 237253544Shselasky usb_callout_reset(&sc->sc_watchdog, hz, &g_modem_watchdog, sc); 238253544Shselasky} 239253544Shselasky 240253544Shselaskystatic void 241253544Shselaskyg_modem_watchdog(void *arg) 242253544Shselasky{ 243253544Shselasky struct g_modem_softc *sc = arg; 244253544Shselasky int i; 245253544Shselasky 246253544Shselasky i = sc->sc_throughput; 247253544Shselasky 248253544Shselasky sc->sc_throughput = 0; 249253544Shselasky 250253544Shselasky g_modem_throughput = i; 251253544Shselasky 252253544Shselasky g_modem_watchdog_reset(sc); 253253544Shselasky} 254253544Shselasky 255253544Shselaskystatic int 256253544Shselaskyg_modem_probe(device_t dev) 257253544Shselasky{ 258253544Shselasky struct usb_attach_arg *uaa = device_get_ivars(dev); 259253544Shselasky 260253544Shselasky DPRINTFN(11, "\n"); 261253544Shselasky 262253544Shselasky if (uaa->usb_mode != USB_MODE_DEVICE) 263253544Shselasky return (ENXIO); 264253544Shselasky 265253544Shselasky if ((uaa->info.bInterfaceClass == UICLASS_CDC) && 266253544Shselasky (uaa->info.bInterfaceSubClass == UISUBCLASS_ABSTRACT_CONTROL_MODEL) && 267253544Shselasky (uaa->info.bInterfaceProtocol == UIPROTO_CDC_AT)) 268253544Shselasky return (0); 269253544Shselasky 270253544Shselasky return (ENXIO); 271253544Shselasky} 272253544Shselasky 273253544Shselaskystatic int 274253544Shselaskyg_modem_attach(device_t dev) 275253544Shselasky{ 276253544Shselasky struct g_modem_softc *sc = device_get_softc(dev); 277253544Shselasky struct usb_attach_arg *uaa = device_get_ivars(dev); 278253544Shselasky int error; 279253544Shselasky uint8_t iface_index[2]; 280253544Shselasky 281253544Shselasky DPRINTFN(11, "\n"); 282253544Shselasky 283253544Shselasky device_set_usb_desc(dev); 284253544Shselasky 285253544Shselasky mtx_init(&sc->sc_mtx, "g_modem", NULL, MTX_DEF); 286253544Shselasky 287253544Shselasky usb_callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0); 288253544Shselasky usb_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0); 289253544Shselasky 290253544Shselasky sc->sc_mode = G_MODEM_MODE_SILENT; 291253544Shselasky 292253544Shselasky iface_index[0] = uaa->info.bIfaceIndex; 293253544Shselasky iface_index[1] = uaa->info.bIfaceIndex + 1; 294253544Shselasky 295253544Shselasky error = usbd_transfer_setup(uaa->device, 296253544Shselasky iface_index, sc->sc_xfer, g_modem_config, 297253544Shselasky G_MODEM_N_TRANSFER, sc, &sc->sc_mtx); 298253544Shselasky 299253544Shselasky if (error) { 300253544Shselasky DPRINTF("error=%s\n", usbd_errstr(error)); 301253544Shselasky goto detach; 302253544Shselasky } 303253544Shselasky usbd_set_parent_iface(uaa->device, iface_index[1], iface_index[0]); 304253544Shselasky 305253544Shselasky mtx_lock(&sc->sc_mtx); 306253544Shselasky g_modem_timeout_reset(sc); 307253544Shselasky g_modem_watchdog_reset(sc); 308253544Shselasky mtx_unlock(&sc->sc_mtx); 309253544Shselasky 310253544Shselasky return (0); /* success */ 311253544Shselasky 312253544Shselaskydetach: 313253544Shselasky g_modem_detach(dev); 314253544Shselasky 315253544Shselasky return (ENXIO); /* error */ 316253544Shselasky} 317253544Shselasky 318253544Shselaskystatic int 319253544Shselaskyg_modem_detach(device_t dev) 320253544Shselasky{ 321253544Shselasky struct g_modem_softc *sc = device_get_softc(dev); 322253544Shselasky 323253544Shselasky DPRINTF("\n"); 324253544Shselasky 325253544Shselasky mtx_lock(&sc->sc_mtx); 326253544Shselasky usb_callout_stop(&sc->sc_callout); 327253544Shselasky usb_callout_stop(&sc->sc_watchdog); 328253544Shselasky mtx_unlock(&sc->sc_mtx); 329253544Shselasky 330253544Shselasky usbd_transfer_unsetup(sc->sc_xfer, G_MODEM_N_TRANSFER); 331253544Shselasky 332253544Shselasky usb_callout_drain(&sc->sc_callout); 333253544Shselasky usb_callout_drain(&sc->sc_watchdog); 334253544Shselasky 335253544Shselasky mtx_destroy(&sc->sc_mtx); 336253544Shselasky 337253544Shselasky return (0); 338253544Shselasky} 339253544Shselasky 340253544Shselaskystatic void 341253544Shselaskyg_modem_intr_callback(struct usb_xfer *xfer, usb_error_t error) 342253544Shselasky{ 343253544Shselasky int actlen; 344253544Shselasky int aframes; 345253544Shselasky 346253544Shselasky usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); 347253544Shselasky 348253544Shselasky DPRINTF("st=%d aframes=%d actlen=%d bytes\n", 349253544Shselasky USB_GET_STATE(xfer), aframes, actlen); 350253544Shselasky 351253544Shselasky switch (USB_GET_STATE(xfer)) { 352253544Shselasky case USB_ST_TRANSFERRED: 353253544Shselasky break; 354253544Shselasky 355253544Shselasky case USB_ST_SETUP: 356253544Shselaskytr_setup: 357253544Shselasky break; 358253544Shselasky 359253544Shselasky default: /* Error */ 360253544Shselasky DPRINTF("error=%s\n", usbd_errstr(error)); 361253544Shselasky 362253544Shselasky if (error != USB_ERR_CANCELLED) { 363253544Shselasky /* try to clear stall first */ 364253544Shselasky usbd_xfer_set_stall(xfer); 365253544Shselasky goto tr_setup; 366253544Shselasky } 367253544Shselasky break; 368253544Shselasky } 369253544Shselasky} 370253544Shselasky 371253544Shselaskystatic void 372253544Shselaskyg_modem_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 373253544Shselasky{ 374253544Shselasky struct g_modem_softc *sc = usbd_xfer_softc(xfer); 375253544Shselasky int actlen; 376253544Shselasky int aframes; 377253544Shselasky int mod; 378253544Shselasky int x; 379253544Shselasky int max; 380253544Shselasky 381253544Shselasky usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); 382253544Shselasky 383253544Shselasky DPRINTF("st=%d aframes=%d actlen=%d bytes\n", 384253544Shselasky USB_GET_STATE(xfer), aframes, actlen); 385253544Shselasky 386253544Shselasky switch (USB_GET_STATE(xfer)) { 387253544Shselasky case USB_ST_TRANSFERRED: 388253544Shselasky 389253544Shselasky sc->sc_tx_busy = 0; 390253544Shselasky sc->sc_throughput += actlen; 391253544Shselasky 392253544Shselasky if (sc->sc_mode == G_MODEM_MODE_LOOP) { 393253544Shselasky /* start loop */ 394253544Shselasky usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_RD]); 395253544Shselasky break; 396253544Shselasky } else if ((sc->sc_mode == G_MODEM_MODE_PATTERN) && (sc->sc_tx_interval != 0)) { 397253544Shselasky /* wait for next timeout */ 398253544Shselasky break; 399253544Shselasky } 400253544Shselasky case USB_ST_SETUP: 401253544Shselaskytr_setup: 402253544Shselasky if (sc->sc_mode == G_MODEM_MODE_PATTERN) { 403253544Shselasky 404253544Shselasky mod = sc->sc_pattern_len; 405253544Shselasky max = sc->sc_tx_interval ? mod : G_MODEM_BUFSIZE; 406253544Shselasky 407253544Shselasky if (mod == 0) { 408253544Shselasky for (x = 0; x != max; x++) 409253544Shselasky sc->sc_data_buf[x] = x % 255; 410253544Shselasky } else { 411253544Shselasky for (x = 0; x != max; x++) 412253544Shselasky sc->sc_data_buf[x] = sc->sc_pattern[x % mod]; 413253544Shselasky } 414253544Shselasky 415253544Shselasky usbd_xfer_set_frame_data(xfer, 0, sc->sc_data_buf, max); 416253544Shselasky usbd_xfer_set_interval(xfer, 0); 417253544Shselasky usbd_xfer_set_frames(xfer, 1); 418253544Shselasky usbd_transfer_submit(xfer); 419253544Shselasky 420253544Shselasky } else if (sc->sc_mode == G_MODEM_MODE_LOOP) { 421253544Shselasky 422253544Shselasky if (sc->sc_tx_busy == 0) 423253544Shselasky break; 424253544Shselasky 425253544Shselasky x = sc->sc_tx_interval; 426253544Shselasky 427253544Shselasky if (x < 0) 428253544Shselasky x = 0; 429253544Shselasky else if (x > 256) 430253544Shselasky x = 256; 431253544Shselasky 432253544Shselasky usbd_xfer_set_frame_data(xfer, 0, sc->sc_data_buf, sc->sc_data_len); 433253544Shselasky usbd_xfer_set_interval(xfer, x); 434253544Shselasky usbd_xfer_set_frames(xfer, 1); 435253544Shselasky usbd_transfer_submit(xfer); 436253544Shselasky } else { 437253544Shselasky sc->sc_tx_busy = 0; 438253544Shselasky } 439253544Shselasky break; 440253544Shselasky 441253544Shselasky default: /* Error */ 442253544Shselasky DPRINTF("error=%s\n", usbd_errstr(error)); 443253544Shselasky 444253544Shselasky if (error != USB_ERR_CANCELLED) { 445253544Shselasky /* try to clear stall first */ 446253544Shselasky usbd_xfer_set_stall(xfer); 447253544Shselasky goto tr_setup; 448253544Shselasky } 449253544Shselasky break; 450253544Shselasky } 451253544Shselasky} 452253544Shselasky 453253544Shselaskystatic void 454253544Shselaskyg_modem_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 455253544Shselasky{ 456253544Shselasky struct g_modem_softc *sc = usbd_xfer_softc(xfer); 457253544Shselasky int actlen; 458253544Shselasky int aframes; 459253544Shselasky 460253544Shselasky usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); 461253544Shselasky 462253544Shselasky DPRINTF("st=%d aframes=%d actlen=%d bytes\n", 463253544Shselasky USB_GET_STATE(xfer), aframes, actlen); 464253544Shselasky 465253544Shselasky switch (USB_GET_STATE(xfer)) { 466253544Shselasky case USB_ST_TRANSFERRED: 467253544Shselasky 468253544Shselasky sc->sc_throughput += actlen; 469253544Shselasky 470253544Shselasky if (sc->sc_mode == G_MODEM_MODE_LOOP) { 471253544Shselasky sc->sc_tx_busy = 1; 472253544Shselasky sc->sc_data_len = actlen; 473253544Shselasky usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_WR]); 474253544Shselasky break; 475253544Shselasky } 476253544Shselasky 477253544Shselasky case USB_ST_SETUP: 478253544Shselaskytr_setup: 479253544Shselasky if ((sc->sc_mode == G_MODEM_MODE_SILENT) || 480253544Shselasky (sc->sc_tx_busy != 0)) 481253544Shselasky break; 482253544Shselasky 483253544Shselasky usbd_xfer_set_frame_data(xfer, 0, sc->sc_data_buf, G_MODEM_BUFSIZE); 484253544Shselasky usbd_xfer_set_frames(xfer, 1); 485253544Shselasky usbd_transfer_submit(xfer); 486253544Shselasky break; 487253544Shselasky 488253544Shselasky default: /* Error */ 489253544Shselasky DPRINTF("error=%s\n", usbd_errstr(error)); 490253544Shselasky 491253544Shselasky if (error != USB_ERR_CANCELLED) { 492253544Shselasky /* try to clear stall first */ 493253544Shselasky usbd_xfer_set_stall(xfer); 494253544Shselasky goto tr_setup; 495253544Shselasky } 496253544Shselasky break; 497253544Shselasky } 498253544Shselasky} 499253544Shselasky 500253544Shselasky 501253544Shselaskystatic int 502253544Shselaskyg_modem_handle_request(device_t dev, 503253544Shselasky const void *preq, void **pptr, uint16_t *plen, 504253544Shselasky uint16_t offset, uint8_t *pstate) 505253544Shselasky{ 506253544Shselasky struct g_modem_softc *sc = device_get_softc(dev); 507253544Shselasky const struct usb_device_request *req = preq; 508253544Shselasky uint8_t is_complete = *pstate; 509253544Shselasky 510253544Shselasky if (!is_complete) { 511253544Shselasky if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) && 512253544Shselasky (req->bRequest == UCDC_SET_LINE_CODING) && 513253544Shselasky (req->wValue[0] == 0x00) && 514253544Shselasky (req->wValue[1] == 0x00)) { 515253544Shselasky 516253544Shselasky if (offset == 0) { 517253544Shselasky *plen = sizeof(sc->sc_line_coding); 518253544Shselasky *pptr = &sc->sc_line_coding; 519253544Shselasky } else { 520253544Shselasky *plen = 0; 521253544Shselasky } 522253544Shselasky return (0); 523253544Shselasky } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) && 524253544Shselasky (req->bRequest == UCDC_SET_COMM_FEATURE)) { 525253544Shselasky 526253544Shselasky if (offset == 0) { 527253544Shselasky *plen = sizeof(sc->sc_abstract_state); 528253544Shselasky *pptr = &sc->sc_abstract_state; 529253544Shselasky } else { 530253544Shselasky *plen = 0; 531253544Shselasky } 532253544Shselasky return (0); 533253544Shselasky } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) && 534253544Shselasky (req->bRequest == UCDC_SET_CONTROL_LINE_STATE)) { 535253544Shselasky *plen = 0; 536253544Shselasky return (0); 537253544Shselasky } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) && 538253544Shselasky (req->bRequest == UCDC_SEND_BREAK)) { 539253544Shselasky *plen = 0; 540253544Shselasky return (0); 541253544Shselasky } 542253544Shselasky } 543253544Shselasky return (ENXIO); /* use builtin handler */ 544253544Shselasky} 545