The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/usb/gadget/g_modem.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   25  * SUCH DAMAGE.
   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/param.h>
   35 __FBSDID("$FreeBSD$");
   36 
   37 #include <sys/stdint.h>
   38 #include <sys/stddef.h>
   39 #include <sys/queue.h>
   40 #include <sys/systm.h>
   41 #include <sys/kernel.h>
   42 #include <sys/bus.h>
   43 #include <sys/linker_set.h>
   44 #include <sys/module.h>
   45 #include <sys/lock.h>
   46 #include <sys/mutex.h>
   47 #include <sys/condvar.h>
   48 #include <sys/sysctl.h>
   49 #include <sys/sx.h>
   50 #include <sys/unistd.h>
   51 #include <sys/callout.h>
   52 #include <sys/malloc.h>
   53 #include <sys/priv.h>
   54 
   55 #include <dev/usb/usb.h>
   56 #include <dev/usb/usb_cdc.h>
   57 #include <dev/usb/usbdi.h>
   58 #include <dev/usb/usbdi_util.h>
   59 #include <dev/usb/usbhid.h>
   60 #include "usb_if.h"
   61 
   62 #define USB_DEBUG_VAR g_modem_debug
   63 #include <dev/usb/usb_debug.h>
   64 
   65 #include <dev/usb/gadget/g_modem.h>
   66 
   67 enum {
   68         G_MODEM_INTR_DT,
   69         G_MODEM_BULK_RD,
   70         G_MODEM_BULK_WR,
   71         G_MODEM_N_TRANSFER,
   72 };
   73 
   74 struct g_modem_softc {
   75         struct mtx sc_mtx;
   76         struct usb_callout sc_callout;
   77         struct usb_callout sc_watchdog;
   78         struct usb_xfer *sc_xfer[G_MODEM_N_TRANSFER];
   79 
   80         int     sc_mode;
   81         int     sc_tx_busy;
   82         int     sc_pattern_len;
   83         int     sc_throughput;
   84         int     sc_tx_interval;
   85 
   86         char    sc_pattern[G_MODEM_MAX_STRLEN];
   87 
   88         uint16_t sc_data_len;
   89 
   90         uint8_t sc_data_buf[G_MODEM_BUFSIZE];
   91         uint8_t sc_line_coding[32];
   92         uint8_t sc_abstract_state[32];
   93 };
   94 
   95 static SYSCTL_NODE(_hw_usb, OID_AUTO, g_modem, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
   96     "USB modem gadget");
   97 
   98 #ifdef USB_DEBUG
   99 static int g_modem_debug = 0;
  100 
  101 SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, debug, CTLFLAG_RWTUN,
  102     &g_modem_debug, 0, "Debug level");
  103 #endif
  104 
  105 static int g_modem_mode = 0;
  106 
  107 SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, mode, CTLFLAG_RWTUN,
  108     &g_modem_mode, 0, "Mode selection");
  109 
  110 static int g_modem_pattern_interval = 1000;
  111 
  112 SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, pattern_interval, CTLFLAG_RWTUN,
  113     &g_modem_pattern_interval, 0, "Pattern interval in milliseconds");
  114 
  115 static char g_modem_pattern_data[G_MODEM_MAX_STRLEN];
  116 
  117 SYSCTL_STRING(_hw_usb_g_modem, OID_AUTO, pattern, CTLFLAG_RW,
  118     &g_modem_pattern_data, sizeof(g_modem_pattern_data), "Data pattern");
  119 
  120 static int g_modem_throughput;
  121 
  122 SYSCTL_INT(_hw_usb_g_modem, OID_AUTO, throughput, CTLFLAG_RD,
  123     &g_modem_throughput, sizeof(g_modem_throughput), "Throughput in bytes per second");
  124 
  125 static device_probe_t g_modem_probe;
  126 static device_attach_t g_modem_attach;
  127 static device_detach_t g_modem_detach;
  128 static usb_handle_request_t g_modem_handle_request;
  129 static usb_callback_t g_modem_intr_callback;
  130 static usb_callback_t g_modem_bulk_read_callback;
  131 static usb_callback_t g_modem_bulk_write_callback;
  132 
  133 static void g_modem_timeout(void *arg);
  134 
  135 static device_method_t g_modem_methods[] = {
  136         /* USB interface */
  137         DEVMETHOD(usb_handle_request, g_modem_handle_request),
  138 
  139         /* Device interface */
  140         DEVMETHOD(device_probe, g_modem_probe),
  141         DEVMETHOD(device_attach, g_modem_attach),
  142         DEVMETHOD(device_detach, g_modem_detach),
  143 
  144         DEVMETHOD_END
  145 };
  146 
  147 static driver_t g_modem_driver = {
  148         .name = "g_modem",
  149         .methods = g_modem_methods,
  150         .size = sizeof(struct g_modem_softc),
  151 };
  152 
  153 DRIVER_MODULE(g_modem, uhub, g_modem_driver, 0, 0);
  154 MODULE_DEPEND(g_modem, usb, 1, 1, 1);
  155 
  156 static const struct usb_config g_modem_config[G_MODEM_N_TRANSFER] = {
  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 
  194 static void
  195 g_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 
  211 static void
  212 g_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 
  232 static void g_modem_watchdog(void *arg);
  233 
  234 static void
  235 g_modem_watchdog_reset(struct g_modem_softc *sc)
  236 {
  237         usb_callout_reset(&sc->sc_watchdog, hz, &g_modem_watchdog, sc);
  238 }
  239 
  240 static void
  241 g_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 
  255 static int
  256 g_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 
  273 static int
  274 g_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 
  312 detach:
  313         g_modem_detach(dev);
  314 
  315         return (ENXIO);                 /* error */
  316 }
  317 
  318 static int
  319 g_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 
  340 static void
  341 g_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:
  356 tr_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 
  371 static void
  372 g_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:
  401 tr_setup:
  402                 if (sc->sc_mode == G_MODEM_MODE_PATTERN) {
  403                         mod = sc->sc_pattern_len;
  404                         max = sc->sc_tx_interval ? mod : G_MODEM_BUFSIZE;
  405 
  406                         if (mod == 0) {
  407                                 for (x = 0; x != max; x++)
  408                                         sc->sc_data_buf[x] = x % 255;
  409                         } else {
  410                                 for (x = 0; x != max; x++)
  411                                         sc->sc_data_buf[x] = sc->sc_pattern[x % mod];
  412                         }
  413 
  414                         usbd_xfer_set_frame_data(xfer, 0, sc->sc_data_buf, max);
  415                         usbd_xfer_set_interval(xfer, 0);
  416                         usbd_xfer_set_frames(xfer, 1);
  417                         usbd_transfer_submit(xfer);
  418 
  419                 } else if (sc->sc_mode == G_MODEM_MODE_LOOP) {
  420                         if (sc->sc_tx_busy == 0)
  421                                 break;
  422 
  423                         x = sc->sc_tx_interval;
  424 
  425                         if (x < 0)
  426                                 x = 0;
  427                         else if (x > 256)
  428                                 x = 256;
  429 
  430                         usbd_xfer_set_frame_data(xfer, 0, sc->sc_data_buf, sc->sc_data_len);
  431                         usbd_xfer_set_interval(xfer, x);
  432                         usbd_xfer_set_frames(xfer, 1);
  433                         usbd_transfer_submit(xfer);
  434                 } else {
  435                         sc->sc_tx_busy = 0;
  436                 }
  437                 break;
  438 
  439         default:                        /* Error */
  440                 DPRINTF("error=%s\n", usbd_errstr(error));
  441 
  442                 if (error != USB_ERR_CANCELLED) {
  443                         /* try to clear stall first */
  444                         usbd_xfer_set_stall(xfer);
  445                         goto tr_setup;
  446                 }
  447                 break;
  448         }
  449 }
  450 
  451 static void
  452 g_modem_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
  453 {
  454         struct g_modem_softc *sc = usbd_xfer_softc(xfer);
  455         int actlen;
  456         int aframes;
  457 
  458         usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
  459 
  460         DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
  461             USB_GET_STATE(xfer), aframes, actlen);
  462 
  463         switch (USB_GET_STATE(xfer)) {
  464         case USB_ST_TRANSFERRED:
  465 
  466                 sc->sc_throughput += actlen;
  467 
  468                 if (sc->sc_mode == G_MODEM_MODE_LOOP) {
  469                         sc->sc_tx_busy = 1;
  470                         sc->sc_data_len = actlen;
  471                         usbd_transfer_start(sc->sc_xfer[G_MODEM_BULK_WR]);
  472                         break;
  473                 }
  474 
  475         case USB_ST_SETUP:
  476 tr_setup:
  477                 if ((sc->sc_mode == G_MODEM_MODE_SILENT) ||
  478                     (sc->sc_tx_busy != 0))
  479                         break;
  480 
  481                 usbd_xfer_set_frame_data(xfer, 0, sc->sc_data_buf, G_MODEM_BUFSIZE);
  482                 usbd_xfer_set_frames(xfer, 1);
  483                 usbd_transfer_submit(xfer);
  484                 break;
  485 
  486         default:                        /* Error */
  487                 DPRINTF("error=%s\n", usbd_errstr(error));
  488 
  489                 if (error != USB_ERR_CANCELLED) {
  490                         /* try to clear stall first */
  491                         usbd_xfer_set_stall(xfer);
  492                         goto tr_setup;
  493                 }
  494                 break;
  495         }
  496 }
  497 
  498 static int
  499 g_modem_handle_request(device_t dev,
  500     const void *preq, void **pptr, uint16_t *plen,
  501     uint16_t offset, uint8_t *pstate)
  502 {
  503         struct g_modem_softc *sc = device_get_softc(dev);
  504         const struct usb_device_request *req = preq;
  505         uint8_t is_complete = *pstate;
  506 
  507         if (!is_complete) {
  508                 if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
  509                     (req->bRequest == UCDC_SET_LINE_CODING) &&
  510                     (req->wValue[0] == 0x00) &&
  511                     (req->wValue[1] == 0x00)) {
  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                         if (offset == 0) {
  522                                 *plen = sizeof(sc->sc_abstract_state);
  523                                 *pptr = &sc->sc_abstract_state;
  524                         } else {
  525                                 *plen = 0;
  526                         }
  527                         return (0);
  528                 } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
  529                     (req->bRequest == UCDC_SET_CONTROL_LINE_STATE)) {
  530                         *plen = 0;
  531                         return (0);
  532                 } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
  533                     (req->bRequest == UCDC_SEND_BREAK)) {
  534                         *plen = 0;
  535                         return (0);
  536                 }
  537         }
  538         return (ENXIO);                 /* use builtin handler */
  539 }

Cache object: aa96f7f6bdbd92cbde6c8a863ff19607


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.