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/net/if_cdceem.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) 2012 Ben Gray <bgray@freebsd.org>.
    5  * Copyright (C) 2018 The FreeBSD Foundation.
    6  * Copyright (c) 2019 Edward Tomasz Napierala <trasz@FreeBSD.org>
    7  *
    8  * This software was developed by Arshan Khanifar <arshankhanifar@gmail.com>
    9  * under sponsorship from the FreeBSD Foundation.
   10  *
   11  * Redistribution and use in source and binary forms, with or without
   12  * modification, are permitted provided that the following conditions
   13  * are met:
   14  * 1. Redistributions of source code must retain the above copyright
   15  *    notice, this list of conditions and the following disclaimer.
   16  * 2. Redistributions in binary form must reproduce the above copyright
   17  *    notice, this list of conditions and the following disclaimer in the
   18  *    documentation and/or other materials provided with the distribution.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   30  * SUCH DAMAGE.
   31  */
   32 
   33 /*
   34  * Universal Serial Bus Communications Class Subclass Specification
   35  * for Ethernet Emulation Model Devices:
   36  *
   37  * https://usb.org/sites/default/files/CDC_EEM10.pdf
   38  */
   39 
   40 #include <sys/cdefs.h>
   41 __FBSDID("$FreeBSD$");
   42 
   43 #include <sys/gsb_crc32.h>
   44 #include <sys/eventhandler.h>
   45 #include <sys/stdint.h>
   46 #include <sys/stddef.h>
   47 #include <sys/queue.h>
   48 #include <sys/systm.h>
   49 #include <sys/socket.h>
   50 #include <sys/kernel.h>
   51 #include <sys/bus.h>
   52 #include <sys/module.h>
   53 #include <sys/lock.h>
   54 #include <sys/mutex.h>
   55 #include <sys/condvar.h>
   56 #include <sys/sysctl.h>
   57 #include <sys/sx.h>
   58 #include <sys/unistd.h>
   59 #include <sys/callout.h>
   60 #include <sys/malloc.h>
   61 #include <sys/priv.h>
   62 
   63 #include <net/if.h>
   64 #include <net/if_var.h>
   65 
   66 #include <dev/usb/usb.h>
   67 #include <dev/usb/usbdi.h>
   68 #include <dev/usb/usbdi_util.h>
   69 #include <dev/usb/usb_cdc.h>
   70 #include "usbdevs.h"
   71 
   72 #define USB_DEBUG_VAR cdceem_debug
   73 #include <dev/usb/usb_debug.h>
   74 #include <dev/usb/usb_process.h>
   75 #include <dev/usb/usb_msctest.h>
   76 #include "usb_if.h"
   77 
   78 #include <dev/usb/net/usb_ethernet.h>
   79 
   80 #define CDCEEM_FRAMES_MAX       1
   81 #define CDCEEM_ECHO_MAX         1024
   82 
   83 #define CDCEEM_ECHO_PAYLOAD     \
   84     "ICH DALEKOPIS FALSZUJE GDY PROBY XQV NIE WYTRZYMUJE 1234567890"
   85 
   86 enum {
   87         CDCEEM_BULK_RX,
   88         CDCEEM_BULK_TX,
   89         CDCEEM_N_TRANSFER,
   90 };
   91 
   92 struct cdceem_softc {
   93         struct usb_ether        sc_ue;
   94         struct mtx              sc_mtx;
   95         int                     sc_flags;
   96         struct usb_xfer         *sc_xfer[CDCEEM_N_TRANSFER];
   97         size_t                  sc_echo_len;
   98         char                    sc_echo_buffer[CDCEEM_ECHO_MAX];
   99 };
  100 
  101 #define CDCEEM_SC_FLAGS_ECHO_RESPONSE_PENDING   0x1
  102 #define CDCEEM_SC_FLAGS_ECHO_PENDING            0x2
  103 
  104 static SYSCTL_NODE(_hw_usb, OID_AUTO, cdceem, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
  105     "USB CDC EEM");
  106 static int cdceem_debug = 1;
  107 SYSCTL_INT(_hw_usb_cdceem, OID_AUTO, debug, CTLFLAG_RWTUN,
  108     &cdceem_debug, 0, "Debug level");
  109 static int cdceem_send_echoes = 0;
  110 SYSCTL_INT(_hw_usb_cdceem, OID_AUTO, send_echoes, CTLFLAG_RWTUN,
  111     &cdceem_send_echoes, 0, "Send an Echo command");
  112 static int cdceem_send_fake_crc = 0;
  113 SYSCTL_INT(_hw_usb_cdceem, OID_AUTO, send_fake_crc, CTLFLAG_RWTUN,
  114     &cdceem_send_fake_crc, 0, "Use 0xdeadbeef instead of CRC");
  115 
  116 #define CDCEEM_DEBUG(S, X, ...)                                         \
  117         do {                                                            \
  118                 if (cdceem_debug > 1) {                                 \
  119                         device_printf(S->sc_ue.ue_dev, "%s: " X "\n",   \
  120                             __func__, ## __VA_ARGS__);                  \
  121                 }                                                       \
  122         } while (0)
  123 
  124 #define CDCEEM_WARN(S, X, ...)                                          \
  125         do {                                                            \
  126                 if (cdceem_debug > 0) {                                 \
  127                         device_printf(S->sc_ue.ue_dev,                  \
  128                             "WARNING: %s: " X "\n",                     \
  129                             __func__, ## __VA_ARGS__);                  \
  130                 }                                                       \
  131         } while (0)
  132 
  133 #define CDCEEM_LOCK(X)                          mtx_lock(&(X)->sc_mtx)
  134 #define CDCEEM_UNLOCK(X)                        mtx_unlock(&(X)->sc_mtx)
  135 
  136 #define CDCEEM_TYPE_CMD                         (0x1 << 15)
  137 
  138 #define CDCEEM_CMD_MASK                         (0x7 << 11)
  139 
  140 #define CDCEEM_CMD_ECHO                         (0x0 << 11)
  141 #define CDCEEM_CMD_ECHO_RESPONSE                (0x1 << 11)
  142 #define CDCEEM_CMD_SUSPEND_HINT                 (0x2 << 11)
  143 #define CDCEEM_CMD_RESPONSE_HINT                (0x3 << 11)
  144 #define CDCEEM_CMD_RESPONSE_COMPLETE_HINT       (0x4 << 11)
  145 #define CDCEEM_CMD_TICKLE                       (0x5 << 11)
  146 
  147 #define CDCEEM_CMD_RESERVED                     (0x1 << 14)
  148 
  149 #define CDCEEM_ECHO_LEN_MASK                    0x3ff
  150 
  151 #define CDCEEM_DATA_CRC                         (0x1 << 14)
  152 #define CDCEEM_DATA_LEN_MASK                    0x3fff
  153 
  154 static device_probe_t cdceem_probe;
  155 static device_attach_t cdceem_attach;
  156 static device_detach_t cdceem_detach;
  157 static device_suspend_t cdceem_suspend;
  158 static device_resume_t cdceem_resume;
  159 
  160 static usb_callback_t cdceem_bulk_write_callback;
  161 static usb_callback_t cdceem_bulk_read_callback;
  162 
  163 static uether_fn_t cdceem_attach_post;
  164 static uether_fn_t cdceem_init;
  165 static uether_fn_t cdceem_stop;
  166 static uether_fn_t cdceem_start;
  167 static uether_fn_t cdceem_setmulti;
  168 static uether_fn_t cdceem_setpromisc;
  169 
  170 static uint32_t cdceem_m_crc32(struct mbuf *, uint32_t, uint32_t);
  171 
  172 static const struct usb_config cdceem_config[CDCEEM_N_TRANSFER] = {
  173         [CDCEEM_BULK_TX] = {
  174                 .type = UE_BULK,
  175                 .endpoint = UE_ADDR_ANY,
  176                 .direction = UE_DIR_TX,
  177                 .bufsize = 16 * (MCLBYTES + 16),
  178                 .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
  179                 .callback = cdceem_bulk_write_callback,
  180                 .timeout = 10000,       /* 10 seconds */
  181                 .usb_mode = USB_MODE_DUAL,
  182         },
  183 
  184         [CDCEEM_BULK_RX] = {
  185                 .type = UE_BULK,
  186                 .endpoint = UE_ADDR_ANY,
  187                 .direction = UE_DIR_RX,
  188                 .bufsize = 20480,       /* bytes */
  189                 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
  190                 .callback = cdceem_bulk_read_callback,
  191                 .timeout = 0,   /* no timeout */
  192                 .usb_mode = USB_MODE_DUAL,
  193         },
  194 };
  195 
  196 static device_method_t cdceem_methods[] = {
  197         /* Device interface */
  198         DEVMETHOD(device_probe, cdceem_probe),
  199         DEVMETHOD(device_attach, cdceem_attach),
  200         DEVMETHOD(device_detach, cdceem_detach),
  201         DEVMETHOD(device_suspend, cdceem_suspend),
  202         DEVMETHOD(device_resume, cdceem_resume),
  203 
  204         DEVMETHOD_END
  205 };
  206 
  207 static driver_t cdceem_driver = {
  208         .name = "cdceem",
  209         .methods = cdceem_methods,
  210         .size = sizeof(struct cdceem_softc),
  211 };
  212 
  213 static const STRUCT_USB_DUAL_ID cdceem_dual_devs[] = {
  214         {USB_IFACE_CLASS(UICLASS_CDC),
  215                 USB_IFACE_SUBCLASS(UISUBCLASS_ETHERNET_EMULATION_MODEL),
  216                 0},
  217 };
  218 
  219 DRIVER_MODULE(cdceem, uhub, cdceem_driver, NULL, NULL);
  220 MODULE_VERSION(cdceem, 1);
  221 MODULE_DEPEND(cdceem, uether, 1, 1, 1);
  222 MODULE_DEPEND(cdceem, usb, 1, 1, 1);
  223 MODULE_DEPEND(cdceem, ether, 1, 1, 1);
  224 USB_PNP_DUAL_INFO(cdceem_dual_devs);
  225 
  226 static const struct usb_ether_methods cdceem_ue_methods = {
  227         .ue_attach_post = cdceem_attach_post,
  228         .ue_start = cdceem_start,
  229         .ue_init = cdceem_init,
  230         .ue_stop = cdceem_stop,
  231         .ue_setmulti = cdceem_setmulti,
  232         .ue_setpromisc = cdceem_setpromisc,
  233 };
  234 
  235 static int
  236 cdceem_probe(device_t dev)
  237 {
  238         struct usb_attach_arg *uaa;
  239         int error;
  240 
  241         uaa = device_get_ivars(dev);
  242         error = usbd_lookup_id_by_uaa(cdceem_dual_devs,
  243             sizeof(cdceem_dual_devs), uaa);
  244 
  245         return (error);
  246 }
  247 
  248 static void
  249 cdceem_attach_post(struct usb_ether *ue)
  250 {
  251 
  252         return;
  253 }
  254 
  255 static int
  256 cdceem_attach(device_t dev)
  257 {
  258         struct cdceem_softc *sc;
  259         struct usb_ether *ue;
  260         struct usb_attach_arg *uaa;
  261         int error;
  262         uint8_t iface_index;
  263 
  264         sc = device_get_softc(dev);
  265         ue = &sc->sc_ue;
  266         uaa = device_get_ivars(dev);
  267 
  268         device_set_usb_desc(dev);
  269 
  270         mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
  271 
  272         /* Setup the endpoints. */
  273         iface_index = 0;
  274         error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
  275             cdceem_config, CDCEEM_N_TRANSFER, sc, &sc->sc_mtx);
  276         if (error != 0) {
  277                 CDCEEM_WARN(sc,
  278                     "allocating USB transfers failed, error %d", error);
  279                 mtx_destroy(&sc->sc_mtx);
  280                 return (error);
  281         }
  282 
  283         /* Random MAC address. */
  284         arc4rand(ue->ue_eaddr, ETHER_ADDR_LEN, 0);
  285         ue->ue_eaddr[0] &= ~0x01;       /* unicast */
  286         ue->ue_eaddr[0] |= 0x02;        /* locally administered */
  287 
  288         ue->ue_sc = sc;
  289         ue->ue_dev = dev;
  290         ue->ue_udev = uaa->device;
  291         ue->ue_mtx = &sc->sc_mtx;
  292         ue->ue_methods = &cdceem_ue_methods;
  293 
  294         error = uether_ifattach(ue);
  295         if (error != 0) {
  296                 CDCEEM_WARN(sc, "could not attach interface, error %d", error);
  297                 usbd_transfer_unsetup(sc->sc_xfer, CDCEEM_N_TRANSFER);
  298                 mtx_destroy(&sc->sc_mtx);
  299                 return (error);
  300         }
  301 
  302         return (0);
  303 }
  304 
  305 static int
  306 cdceem_detach(device_t dev)
  307 {
  308         struct cdceem_softc *sc = device_get_softc(dev);
  309         struct usb_ether *ue = &sc->sc_ue;
  310 
  311         /* Stop all USB transfers first. */
  312         usbd_transfer_unsetup(sc->sc_xfer, CDCEEM_N_TRANSFER);
  313         uether_ifdetach(ue);
  314         mtx_destroy(&sc->sc_mtx);
  315 
  316         return (0);
  317 }
  318 
  319 static void
  320 cdceem_handle_cmd(struct usb_xfer *xfer, uint16_t hdr, int *offp)
  321 {
  322         struct cdceem_softc *sc;
  323         struct usb_page_cache *pc;
  324         int actlen, off;
  325         uint16_t pktlen;
  326 
  327         off = *offp;
  328         sc = usbd_xfer_softc(xfer);
  329         pc = usbd_xfer_get_frame(xfer, 0);
  330         usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
  331 
  332         if (hdr & CDCEEM_CMD_RESERVED) {
  333                 CDCEEM_WARN(sc, "received command header %#x "
  334                     "with Reserved bit set; ignoring", hdr);
  335                 return;
  336         }
  337 
  338         switch (hdr & CDCEEM_CMD_MASK) {
  339         case CDCEEM_CMD_ECHO:
  340                 pktlen = hdr & CDCEEM_ECHO_LEN_MASK;
  341                 CDCEEM_DEBUG(sc, "received Echo, length %d", pktlen);
  342 
  343                 if (pktlen > (actlen - off)) {
  344                         CDCEEM_WARN(sc,
  345                             "bad Echo length %d, should be at most %d",
  346                             pktlen, actlen - off);
  347                         break;
  348                 }
  349 
  350                 if (pktlen > sizeof(sc->sc_echo_buffer)) {
  351                         CDCEEM_WARN(sc,
  352                             "Echo length %u too big, must be less than %zd",
  353                             pktlen, sizeof(sc->sc_echo_buffer));
  354                         break;
  355                 }
  356 
  357                 sc->sc_flags |= CDCEEM_SC_FLAGS_ECHO_RESPONSE_PENDING;
  358                 sc->sc_echo_len = pktlen;
  359                 usbd_copy_out(pc, off, sc->sc_echo_buffer, pktlen);
  360                 off += pktlen;
  361                 break;
  362 
  363         case CDCEEM_CMD_ECHO_RESPONSE:
  364                 pktlen = hdr & CDCEEM_ECHO_LEN_MASK;
  365                 CDCEEM_DEBUG(sc, "received Echo Response, length %d", pktlen);
  366 
  367                 if (pktlen > (actlen - off)) {
  368                         CDCEEM_WARN(sc,
  369                             "bad Echo Response length %d, "
  370                             "should be at most %d",
  371                             pktlen, actlen - off);
  372                         break;
  373                 }
  374 
  375                 if (pktlen != sizeof(CDCEEM_ECHO_PAYLOAD)) {
  376                         CDCEEM_WARN(sc, "received Echo Response with bad "
  377                             "length %hu, should be %zd",
  378                             pktlen, sizeof(CDCEEM_ECHO_PAYLOAD));
  379                         break;
  380                 }
  381 
  382                 usbd_copy_out(pc, off, sc->sc_echo_buffer, pktlen);
  383                 off += pktlen;
  384 
  385                 if (memcmp(sc->sc_echo_buffer, CDCEEM_ECHO_PAYLOAD,
  386                     sizeof(CDCEEM_ECHO_PAYLOAD)) != 0) {
  387                         CDCEEM_WARN(sc,
  388                             "received Echo Response payload does not match");
  389                 } else {
  390                         CDCEEM_DEBUG(sc, "received Echo Response is valid");
  391                 }
  392                 break;
  393 
  394         case CDCEEM_CMD_SUSPEND_HINT:
  395                 CDCEEM_DEBUG(sc, "received SuspendHint; ignoring");
  396                 break;
  397 
  398         case CDCEEM_CMD_RESPONSE_HINT:
  399                 CDCEEM_DEBUG(sc, "received ResponseHint; ignoring");
  400                 break;
  401 
  402         case CDCEEM_CMD_RESPONSE_COMPLETE_HINT:
  403                 CDCEEM_DEBUG(sc, "received ResponseCompleteHint; ignoring");
  404                 break;
  405 
  406         case CDCEEM_CMD_TICKLE:
  407                 CDCEEM_DEBUG(sc, "received Tickle; ignoring");
  408                 break;
  409 
  410         default:
  411                 CDCEEM_WARN(sc,
  412                     "received unknown command %u, header %#x; ignoring",
  413                     (hdr & CDCEEM_CMD_MASK >> 11), hdr);
  414                 break;
  415         }
  416 
  417         *offp = off;
  418 }
  419 
  420 static void
  421 cdceem_handle_data(struct usb_xfer *xfer, uint16_t hdr, int *offp)
  422 {
  423         struct cdceem_softc *sc;
  424         struct usb_page_cache *pc;
  425         struct usb_ether *ue;
  426         struct ifnet *ifp;
  427         struct mbuf *m;
  428         uint32_t computed_crc, received_crc;
  429         int pktlen;
  430         int actlen;
  431         int off;
  432 
  433         off = *offp;
  434         sc = usbd_xfer_softc(xfer);
  435         pc = usbd_xfer_get_frame(xfer, 0);
  436         ue = &sc->sc_ue;
  437         ifp = uether_getifp(ue);
  438         usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
  439 
  440         pktlen = hdr & CDCEEM_DATA_LEN_MASK;
  441         CDCEEM_DEBUG(sc, "received Data, CRC %s, length %d",
  442             (hdr & CDCEEM_DATA_CRC) ? "valid" : "absent",
  443             pktlen);
  444 
  445         if (pktlen < (ETHER_HDR_LEN + 4)) {
  446                 CDCEEM_WARN(sc,
  447                     "bad ethernet frame length %d, should be at least %d",
  448                     pktlen, ETHER_HDR_LEN);
  449                 if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
  450                 return;
  451         }
  452 
  453         if (pktlen > (actlen - off)) {
  454                 CDCEEM_WARN(sc,
  455                     "bad ethernet frame length %d, should be at most %d",
  456                     pktlen, actlen - off);
  457                 if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
  458                 return;
  459         }
  460 
  461         m = uether_newbuf();
  462         if (m == NULL) {
  463                 CDCEEM_WARN(sc, "uether_newbuf() failed");
  464                 if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
  465                 return;
  466         }
  467 
  468         pktlen -= 4; /* Subtract the CRC. */
  469 
  470         if (pktlen > m->m_len) {
  471                 CDCEEM_WARN(sc, "buffer too small %d vs %d bytes",
  472                     pktlen, m->m_len);
  473                 if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
  474                 m_freem(m);
  475                 return;
  476         }
  477         usbd_copy_out(pc, off, mtod(m, uint8_t *), pktlen);
  478         off += pktlen;
  479 
  480         usbd_copy_out(pc, off, &received_crc, sizeof(received_crc));
  481         off += sizeof(received_crc);
  482 
  483         if (hdr & CDCEEM_DATA_CRC) {
  484                 computed_crc = cdceem_m_crc32(m, 0, pktlen);
  485         } else {
  486                 computed_crc = be32toh(0xdeadbeef);
  487         }
  488 
  489         if (received_crc != computed_crc) {
  490                 CDCEEM_WARN(sc,
  491                     "received Data packet with wrong CRC %#x, expected %#x",
  492                     received_crc, computed_crc);
  493                 if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
  494                 m_freem(m);
  495                 return;
  496         } else {
  497                 CDCEEM_DEBUG(sc, "received correct CRC %#x", received_crc);
  498         }
  499 
  500         uether_rxmbuf(ue, m, pktlen);
  501         *offp = off;
  502 }
  503 
  504 static void
  505 cdceem_bulk_read_callback(struct usb_xfer *xfer, usb_error_t usb_error)
  506 {
  507         struct cdceem_softc *sc;
  508         struct usb_page_cache *pc;
  509         int actlen, aframes, off;
  510         uint16_t hdr;
  511 
  512         sc = usbd_xfer_softc(xfer);
  513         usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
  514 
  515         switch (USB_GET_STATE(xfer)) {
  516         case USB_ST_TRANSFERRED:
  517                 CDCEEM_DEBUG(sc,
  518                     "received %u bytes in %u frames", actlen, aframes);
  519 
  520                 pc = usbd_xfer_get_frame(xfer, 0);
  521                 off = 0;
  522 
  523                 while ((off + sizeof(hdr)) <= actlen) {
  524                         usbd_copy_out(pc, off, &hdr, sizeof(hdr));
  525                         CDCEEM_DEBUG(sc, "hdr = %#x", hdr);
  526                         off += sizeof(hdr);
  527 
  528                         if (hdr == 0) {
  529                                 CDCEEM_DEBUG(sc, "received Zero Length EEM");
  530                                 continue;
  531                         }
  532 
  533                         hdr = le16toh(hdr);
  534 
  535                         if ((hdr & CDCEEM_TYPE_CMD) != 0) {
  536                                 cdceem_handle_cmd(xfer, hdr, &off);
  537                         } else {
  538                                 cdceem_handle_data(xfer, hdr, &off);
  539                         }
  540 
  541                         KASSERT(off <= actlen,
  542                             ("%s: went past the buffer, off %d, actlen %d",
  543                              __func__, off, actlen));
  544                 }
  545 
  546                 /* FALLTHROUGH */
  547         case USB_ST_SETUP:
  548                 CDCEEM_DEBUG(sc, "setup");
  549 tr_setup:
  550                 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
  551                 usbd_transfer_submit(xfer);
  552                 uether_rxflush(&sc->sc_ue);
  553                 break;
  554 
  555         default:
  556                 CDCEEM_WARN(sc, "USB_ST_ERROR: %s", usbd_errstr(usb_error));
  557 
  558                 if (usb_error != USB_ERR_CANCELLED) {
  559                         /* try to clear stall first */
  560                         usbd_xfer_set_stall(xfer);
  561                         goto tr_setup;
  562                 }
  563                 break;
  564         }
  565 }
  566 
  567 static void
  568 cdceem_send_echo(struct usb_xfer *xfer, int *offp)
  569 {
  570         struct cdceem_softc *sc;
  571         struct usb_page_cache *pc;
  572         int maxlen __diagused, off;
  573         uint16_t hdr;
  574 
  575         off = *offp;
  576         sc = usbd_xfer_softc(xfer);
  577         pc = usbd_xfer_get_frame(xfer, 0);
  578         maxlen = usbd_xfer_max_len(xfer);
  579 
  580         CDCEEM_DEBUG(sc, "sending Echo, length %zd",
  581             sizeof(CDCEEM_ECHO_PAYLOAD));
  582 
  583         KASSERT(off + sizeof(hdr) + sizeof(CDCEEM_ECHO_PAYLOAD) < maxlen,
  584             ("%s: out of space; have %d, need %zd", __func__, maxlen,
  585             off + sizeof(hdr) + sizeof(CDCEEM_ECHO_PAYLOAD)));
  586 
  587         hdr = 0;
  588         hdr |= CDCEEM_TYPE_CMD;
  589         hdr |= CDCEEM_CMD_ECHO;
  590         hdr |= sizeof(CDCEEM_ECHO_PAYLOAD);
  591         CDCEEM_DEBUG(sc, "hdr = %#x", hdr);
  592         hdr = htole16(hdr);
  593 
  594         usbd_copy_in(pc, off, &hdr, sizeof(hdr));
  595         off += sizeof(hdr);
  596 
  597         usbd_copy_in(pc, off, CDCEEM_ECHO_PAYLOAD,
  598             sizeof(CDCEEM_ECHO_PAYLOAD));
  599         off += sizeof(CDCEEM_ECHO_PAYLOAD);
  600 
  601         sc->sc_flags &= ~CDCEEM_SC_FLAGS_ECHO_PENDING;
  602 
  603         *offp = off;
  604 }
  605 
  606 static void
  607 cdceem_send_echo_response(struct usb_xfer *xfer, int *offp)
  608 {
  609         struct cdceem_softc *sc;
  610         struct usb_page_cache *pc;
  611         int maxlen __diagused, off;
  612         uint16_t hdr;
  613 
  614         off = *offp;
  615         sc = usbd_xfer_softc(xfer);
  616         pc = usbd_xfer_get_frame(xfer, 0);
  617         maxlen = usbd_xfer_max_len(xfer);
  618 
  619         KASSERT(off + sizeof(hdr) + sc->sc_echo_len < maxlen,
  620             ("%s: out of space; have %d, need %zd", __func__, maxlen,
  621             off + sizeof(hdr) + sc->sc_echo_len));
  622 
  623         CDCEEM_DEBUG(sc, "sending Echo Response, length %zd", sc->sc_echo_len);
  624 
  625         hdr = 0;
  626         hdr |= CDCEEM_TYPE_CMD;
  627         hdr |= CDCEEM_CMD_ECHO_RESPONSE;
  628         hdr |= sc->sc_echo_len;
  629         CDCEEM_DEBUG(sc, "hdr = %#x", hdr);
  630         hdr = htole16(hdr);
  631 
  632         usbd_copy_in(pc, off, &hdr, sizeof(hdr));
  633         off += sizeof(hdr);
  634 
  635         usbd_copy_in(pc, off, sc->sc_echo_buffer, sc->sc_echo_len);
  636         off += sc->sc_echo_len;
  637 
  638         sc->sc_flags &= ~CDCEEM_SC_FLAGS_ECHO_RESPONSE_PENDING;
  639         sc->sc_echo_len = 0;
  640 
  641         *offp = off;
  642 }
  643 
  644 static void
  645 cdceem_send_data(struct usb_xfer *xfer, int *offp)
  646 {
  647         struct cdceem_softc *sc;
  648         struct usb_page_cache *pc;
  649         struct ifnet *ifp;
  650         struct mbuf *m;
  651         int maxlen __diagused, off;
  652         uint32_t crc;
  653         uint16_t hdr;
  654 
  655         off = *offp;
  656         sc = usbd_xfer_softc(xfer);
  657         pc = usbd_xfer_get_frame(xfer, 0);
  658         ifp = uether_getifp(&sc->sc_ue);
  659         maxlen = usbd_xfer_max_len(xfer);
  660 
  661         IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
  662         if (m == NULL) {
  663                 CDCEEM_DEBUG(sc, "no Data packets to send");
  664                 return;
  665         }
  666 
  667         KASSERT((m->m_pkthdr.len & CDCEEM_DATA_LEN_MASK) == m->m_pkthdr.len,
  668             ("%s: packet too long: %d, should be %d\n", __func__,
  669              m->m_pkthdr.len, m->m_pkthdr.len & CDCEEM_DATA_LEN_MASK));
  670         KASSERT(off + sizeof(hdr) + m->m_pkthdr.len + 4 < maxlen,
  671             ("%s: out of space; have %d, need %zd", __func__, maxlen,
  672             off + sizeof(hdr) + m->m_pkthdr.len + 4));
  673 
  674         CDCEEM_DEBUG(sc, "sending Data, length %d + 4", m->m_pkthdr.len);
  675 
  676         hdr = 0;
  677         if (!cdceem_send_fake_crc)
  678                 hdr |= CDCEEM_DATA_CRC;
  679         hdr |= (m->m_pkthdr.len + 4); /* +4 for CRC */
  680         CDCEEM_DEBUG(sc, "hdr = %#x", hdr);
  681         hdr = htole16(hdr);
  682 
  683         usbd_copy_in(pc, off, &hdr, sizeof(hdr));
  684         off += sizeof(hdr);
  685 
  686         usbd_m_copy_in(pc, off, m, 0, m->m_pkthdr.len);
  687         off += m->m_pkthdr.len;
  688 
  689         if (cdceem_send_fake_crc) {
  690                 crc = htobe32(0xdeadbeef);
  691         } else {
  692                 crc = cdceem_m_crc32(m, 0, m->m_pkthdr.len);
  693         }
  694         CDCEEM_DEBUG(sc, "CRC = %#x", crc);
  695 
  696         usbd_copy_in(pc, off, &crc, sizeof(crc));
  697         off += sizeof(crc);
  698 
  699         if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
  700 
  701         /*
  702          * If there's a BPF listener, bounce a copy of this frame to it.
  703          */
  704         BPF_MTAP(ifp, m);
  705         m_freem(m);
  706 
  707         *offp = off;
  708 }
  709 
  710 static void
  711 cdceem_bulk_write_callback(struct usb_xfer *xfer, usb_error_t usb_error)
  712 {
  713         struct cdceem_softc *sc;
  714         struct ifnet *ifp;
  715         int actlen, aframes, maxlen __diagused, off;
  716 
  717         sc = usbd_xfer_softc(xfer);
  718         maxlen = usbd_xfer_max_len(xfer);
  719 
  720         switch (USB_GET_STATE(xfer)) {
  721         case USB_ST_TRANSFERRED:
  722                 usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
  723                 CDCEEM_DEBUG(sc, "transferred %u bytes in %u frames",
  724                     actlen, aframes);
  725 
  726                 /* FALLTHROUGH */
  727         case USB_ST_SETUP:
  728                 CDCEEM_DEBUG(sc, "setup");
  729 tr_setup:
  730 
  731                 off = 0;
  732                 usbd_xfer_set_frame_offset(xfer, 0, 0);
  733 
  734                 if (sc->sc_flags & CDCEEM_SC_FLAGS_ECHO_PENDING) {
  735                         cdceem_send_echo(xfer, &off);
  736                 } else if (sc->sc_flags & CDCEEM_SC_FLAGS_ECHO_RESPONSE_PENDING) {
  737                         cdceem_send_echo_response(xfer, &off);
  738                 } else {
  739                         cdceem_send_data(xfer, &off);
  740                 }
  741 
  742                 KASSERT(off <= maxlen,
  743                     ("%s: went past the buffer, off %d, maxlen %d",
  744                      __func__, off, maxlen));
  745 
  746                 if (off > 0) {
  747                         CDCEEM_DEBUG(sc, "starting transfer, length %d", off);
  748                         usbd_xfer_set_frame_len(xfer, 0, off);
  749                         usbd_transfer_submit(xfer);
  750                 } else {
  751                         CDCEEM_DEBUG(sc, "nothing to transfer");
  752                 }
  753 
  754                 break;
  755 
  756         default:
  757                 CDCEEM_WARN(sc, "USB_ST_ERROR: %s", usbd_errstr(usb_error));
  758 
  759                 ifp = uether_getifp(&sc->sc_ue);
  760                 if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
  761 
  762                 if (usb_error != USB_ERR_CANCELLED) {
  763                         /* try to clear stall first */
  764                         usbd_xfer_set_stall(xfer);
  765                         goto tr_setup;
  766                 }
  767                 break;
  768         }
  769 }
  770 
  771 static int32_t
  772 cdceem_m_crc32_cb(void *arg, void *src, uint32_t count)
  773 {
  774         uint32_t *p_crc = arg;
  775 
  776         *p_crc = crc32_raw(src, count, *p_crc);
  777         return (0);
  778 }
  779 
  780 static uint32_t
  781 cdceem_m_crc32(struct mbuf *m, uint32_t src_offset, uint32_t src_len)
  782 {
  783         uint32_t crc = 0xFFFFFFFF;
  784 
  785         m_apply(m, src_offset, src_len, cdceem_m_crc32_cb, &crc);
  786         return (crc ^ 0xFFFFFFFF);
  787 }
  788 
  789 static void
  790 cdceem_start(struct usb_ether *ue)
  791 {
  792         struct cdceem_softc *sc;
  793 
  794         sc = uether_getsc(ue);
  795 
  796         /*
  797          * Start the USB transfers, if not already started.
  798          */
  799         usbd_transfer_start(sc->sc_xfer[CDCEEM_BULK_RX]);
  800         usbd_transfer_start(sc->sc_xfer[CDCEEM_BULK_TX]);
  801 }
  802 
  803 static void
  804 cdceem_init(struct usb_ether *ue)
  805 {
  806         struct cdceem_softc *sc;
  807         struct ifnet *ifp;
  808 
  809         sc = uether_getsc(ue);
  810         ifp = uether_getifp(ue);
  811 
  812         ifp->if_drv_flags |= IFF_DRV_RUNNING;
  813 
  814         if (cdceem_send_echoes)
  815                 sc->sc_flags = CDCEEM_SC_FLAGS_ECHO_PENDING;
  816         else
  817                 sc->sc_flags = 0;
  818 
  819         /*
  820          * Stall data write direction, which depends on USB mode.
  821          *
  822          * Some USB host stacks (e.g. Mac OS X) don't clears stall
  823          * bit as it should, so set it in our host mode only.
  824          */
  825         if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST)
  826                 usbd_xfer_set_stall(sc->sc_xfer[CDCEEM_BULK_TX]);
  827 
  828         cdceem_start(ue);
  829 }
  830 
  831 static void
  832 cdceem_stop(struct usb_ether *ue)
  833 {
  834         struct cdceem_softc *sc;
  835         struct ifnet *ifp;
  836 
  837         sc = uether_getsc(ue);
  838         ifp = uether_getifp(ue);
  839 
  840         ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
  841 
  842         usbd_transfer_stop(sc->sc_xfer[CDCEEM_BULK_RX]);
  843         usbd_transfer_stop(sc->sc_xfer[CDCEEM_BULK_TX]);
  844 }
  845 
  846 static void
  847 cdceem_setmulti(struct usb_ether *ue)
  848 {
  849         /* no-op */
  850         return;
  851 }
  852 
  853 static void
  854 cdceem_setpromisc(struct usb_ether *ue)
  855 {
  856         /* no-op */
  857         return;
  858 }
  859 
  860 static int
  861 cdceem_suspend(device_t dev)
  862 {
  863         struct cdceem_softc *sc = device_get_softc(dev);
  864 
  865         CDCEEM_DEBUG(sc, "go");
  866         return (0);
  867 }
  868 
  869 static int
  870 cdceem_resume(device_t dev)
  871 {
  872         struct cdceem_softc *sc = device_get_softc(dev);
  873 
  874         CDCEEM_DEBUG(sc, "go");
  875         return (0);
  876 }

Cache object: 8954655fafb53067d64f895a93d24e20


[ 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.