163722e52SEdward Tomasz Napierala /*- 263722e52SEdward Tomasz Napierala * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 363722e52SEdward Tomasz Napierala * 463722e52SEdward Tomasz Napierala * Copyright (C) 2012 Ben Gray <bgray@freebsd.org>. 563722e52SEdward Tomasz Napierala * Copyright (C) 2018 The FreeBSD Foundation. 663722e52SEdward Tomasz Napierala * Copyright (c) 2019 Edward Tomasz Napierala <trasz@FreeBSD.org> 763722e52SEdward Tomasz Napierala * 863722e52SEdward Tomasz Napierala * This software was developed by Arshan Khanifar <arshankhanifar@gmail.com> 963722e52SEdward Tomasz Napierala * under sponsorship from the FreeBSD Foundation. 1063722e52SEdward Tomasz Napierala * 1163722e52SEdward Tomasz Napierala * Redistribution and use in source and binary forms, with or without 1263722e52SEdward Tomasz Napierala * modification, are permitted provided that the following conditions 1363722e52SEdward Tomasz Napierala * are met: 1463722e52SEdward Tomasz Napierala * 1. Redistributions of source code must retain the above copyright 1563722e52SEdward Tomasz Napierala * notice, this list of conditions and the following disclaimer. 1663722e52SEdward Tomasz Napierala * 2. Redistributions in binary form must reproduce the above copyright 1763722e52SEdward Tomasz Napierala * notice, this list of conditions and the following disclaimer in the 1863722e52SEdward Tomasz Napierala * documentation and/or other materials provided with the distribution. 1963722e52SEdward Tomasz Napierala * 2063722e52SEdward Tomasz Napierala * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2163722e52SEdward Tomasz Napierala * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2263722e52SEdward Tomasz Napierala * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2363722e52SEdward Tomasz Napierala * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2463722e52SEdward Tomasz Napierala * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2563722e52SEdward Tomasz Napierala * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2663722e52SEdward Tomasz Napierala * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2763722e52SEdward Tomasz Napierala * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2863722e52SEdward Tomasz Napierala * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2963722e52SEdward Tomasz Napierala * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3063722e52SEdward Tomasz Napierala * SUCH DAMAGE. 3163722e52SEdward Tomasz Napierala */ 3263722e52SEdward Tomasz Napierala 3363722e52SEdward Tomasz Napierala /* 3463722e52SEdward Tomasz Napierala * Universal Serial Bus Communications Class Subclass Specification 3563722e52SEdward Tomasz Napierala * for Ethernet Emulation Model Devices: 3663722e52SEdward Tomasz Napierala * 3763722e52SEdward Tomasz Napierala * https://usb.org/sites/default/files/CDC_EEM10.pdf 3863722e52SEdward Tomasz Napierala */ 3963722e52SEdward Tomasz Napierala 4063722e52SEdward Tomasz Napierala #include <sys/cdefs.h> 4163722e52SEdward Tomasz Napierala __FBSDID("$FreeBSD$"); 4263722e52SEdward Tomasz Napierala 4363722e52SEdward Tomasz Napierala #include <sys/gsb_crc32.h> 4463722e52SEdward Tomasz Napierala #include <sys/eventhandler.h> 4563722e52SEdward Tomasz Napierala #include <sys/stdint.h> 4663722e52SEdward Tomasz Napierala #include <sys/stddef.h> 4763722e52SEdward Tomasz Napierala #include <sys/queue.h> 4863722e52SEdward Tomasz Napierala #include <sys/systm.h> 4963722e52SEdward Tomasz Napierala #include <sys/socket.h> 5063722e52SEdward Tomasz Napierala #include <sys/kernel.h> 5163722e52SEdward Tomasz Napierala #include <sys/bus.h> 5263722e52SEdward Tomasz Napierala #include <sys/module.h> 5363722e52SEdward Tomasz Napierala #include <sys/lock.h> 5463722e52SEdward Tomasz Napierala #include <sys/mutex.h> 5563722e52SEdward Tomasz Napierala #include <sys/condvar.h> 5663722e52SEdward Tomasz Napierala #include <sys/sysctl.h> 5763722e52SEdward Tomasz Napierala #include <sys/sx.h> 5863722e52SEdward Tomasz Napierala #include <sys/unistd.h> 5963722e52SEdward Tomasz Napierala #include <sys/callout.h> 6063722e52SEdward Tomasz Napierala #include <sys/malloc.h> 6163722e52SEdward Tomasz Napierala #include <sys/priv.h> 6263722e52SEdward Tomasz Napierala 6363722e52SEdward Tomasz Napierala #include <net/if.h> 6463722e52SEdward Tomasz Napierala #include <net/if_var.h> 6563722e52SEdward Tomasz Napierala 6663722e52SEdward Tomasz Napierala #include <dev/usb/usb.h> 6763722e52SEdward Tomasz Napierala #include <dev/usb/usbdi.h> 6863722e52SEdward Tomasz Napierala #include <dev/usb/usbdi_util.h> 6963722e52SEdward Tomasz Napierala #include <dev/usb/usb_cdc.h> 7063722e52SEdward Tomasz Napierala #include "usbdevs.h" 7163722e52SEdward Tomasz Napierala 7263722e52SEdward Tomasz Napierala #define USB_DEBUG_VAR cdceem_debug 7363722e52SEdward Tomasz Napierala #include <dev/usb/usb_debug.h> 7463722e52SEdward Tomasz Napierala #include <dev/usb/usb_process.h> 7563722e52SEdward Tomasz Napierala #include <dev/usb/usb_msctest.h> 7663722e52SEdward Tomasz Napierala #include "usb_if.h" 7763722e52SEdward Tomasz Napierala 7863722e52SEdward Tomasz Napierala #include <dev/usb/net/usb_ethernet.h> 7963722e52SEdward Tomasz Napierala 8063722e52SEdward Tomasz Napierala #define CDCEEM_FRAMES_MAX 1 8163722e52SEdward Tomasz Napierala #define CDCEEM_ECHO_MAX 1024 8263722e52SEdward Tomasz Napierala 8363722e52SEdward Tomasz Napierala #define CDCEEM_ECHO_PAYLOAD \ 8463722e52SEdward Tomasz Napierala "ICH DALEKOPIS FALSZUJE GDY PROBY XQV NIE WYTRZYMUJE 1234567890" 8563722e52SEdward Tomasz Napierala 8663722e52SEdward Tomasz Napierala enum { 8763722e52SEdward Tomasz Napierala CDCEEM_BULK_RX, 8863722e52SEdward Tomasz Napierala CDCEEM_BULK_TX, 8963722e52SEdward Tomasz Napierala CDCEEM_N_TRANSFER, 9063722e52SEdward Tomasz Napierala }; 9163722e52SEdward Tomasz Napierala 9263722e52SEdward Tomasz Napierala struct cdceem_softc { 9363722e52SEdward Tomasz Napierala struct usb_ether sc_ue; 9463722e52SEdward Tomasz Napierala struct mtx sc_mtx; 9563722e52SEdward Tomasz Napierala int sc_flags; 9663722e52SEdward Tomasz Napierala struct usb_xfer *sc_xfer[CDCEEM_N_TRANSFER]; 9763722e52SEdward Tomasz Napierala size_t sc_echo_len; 9863722e52SEdward Tomasz Napierala char sc_echo_buffer[CDCEEM_ECHO_MAX]; 9963722e52SEdward Tomasz Napierala }; 10063722e52SEdward Tomasz Napierala 10163722e52SEdward Tomasz Napierala #define CDCEEM_SC_FLAGS_ECHO_RESPONSE_PENDING 0x1 10263722e52SEdward Tomasz Napierala #define CDCEEM_SC_FLAGS_ECHO_PENDING 0x2 10363722e52SEdward Tomasz Napierala 104f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_hw_usb, OID_AUTO, cdceem, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 105f8d2b1f3SPawel Biernacki "USB CDC EEM"); 10663722e52SEdward Tomasz Napierala static int cdceem_debug = 1; 10763722e52SEdward Tomasz Napierala SYSCTL_INT(_hw_usb_cdceem, OID_AUTO, debug, CTLFLAG_RWTUN, 10863722e52SEdward Tomasz Napierala &cdceem_debug, 0, "Debug level"); 10963722e52SEdward Tomasz Napierala static int cdceem_send_echoes = 0; 11063722e52SEdward Tomasz Napierala SYSCTL_INT(_hw_usb_cdceem, OID_AUTO, send_echoes, CTLFLAG_RWTUN, 11163722e52SEdward Tomasz Napierala &cdceem_send_echoes, 0, "Send an Echo command"); 11263722e52SEdward Tomasz Napierala static int cdceem_send_fake_crc = 0; 11363722e52SEdward Tomasz Napierala SYSCTL_INT(_hw_usb_cdceem, OID_AUTO, send_fake_crc, CTLFLAG_RWTUN, 11463722e52SEdward Tomasz Napierala &cdceem_send_fake_crc, 0, "Use 0xdeadbeef instead of CRC"); 11563722e52SEdward Tomasz Napierala 11663722e52SEdward Tomasz Napierala #define CDCEEM_DEBUG(S, X, ...) \ 11763722e52SEdward Tomasz Napierala do { \ 11863722e52SEdward Tomasz Napierala if (cdceem_debug > 1) { \ 11963722e52SEdward Tomasz Napierala device_printf(S->sc_ue.ue_dev, "%s: " X "\n", \ 12063722e52SEdward Tomasz Napierala __func__, ## __VA_ARGS__); \ 12163722e52SEdward Tomasz Napierala } \ 12263722e52SEdward Tomasz Napierala } while (0) 12363722e52SEdward Tomasz Napierala 12463722e52SEdward Tomasz Napierala #define CDCEEM_WARN(S, X, ...) \ 12563722e52SEdward Tomasz Napierala do { \ 12663722e52SEdward Tomasz Napierala if (cdceem_debug > 0) { \ 12763722e52SEdward Tomasz Napierala device_printf(S->sc_ue.ue_dev, \ 12863722e52SEdward Tomasz Napierala "WARNING: %s: " X "\n", \ 12963722e52SEdward Tomasz Napierala __func__, ## __VA_ARGS__); \ 13063722e52SEdward Tomasz Napierala } \ 13163722e52SEdward Tomasz Napierala } while (0) 13263722e52SEdward Tomasz Napierala 13363722e52SEdward Tomasz Napierala #define CDCEEM_LOCK(X) mtx_lock(&(X)->sc_mtx) 13463722e52SEdward Tomasz Napierala #define CDCEEM_UNLOCK(X) mtx_unlock(&(X)->sc_mtx) 13563722e52SEdward Tomasz Napierala 13663722e52SEdward Tomasz Napierala #define CDCEEM_TYPE_CMD (0x1 << 15) 13763722e52SEdward Tomasz Napierala 13863722e52SEdward Tomasz Napierala #define CDCEEM_CMD_MASK (0x7 << 11) 13963722e52SEdward Tomasz Napierala 14063722e52SEdward Tomasz Napierala #define CDCEEM_CMD_ECHO (0x0 << 11) 14163722e52SEdward Tomasz Napierala #define CDCEEM_CMD_ECHO_RESPONSE (0x1 << 11) 14263722e52SEdward Tomasz Napierala #define CDCEEM_CMD_SUSPEND_HINT (0x2 << 11) 14363722e52SEdward Tomasz Napierala #define CDCEEM_CMD_RESPONSE_HINT (0x3 << 11) 14463722e52SEdward Tomasz Napierala #define CDCEEM_CMD_RESPONSE_COMPLETE_HINT (0x4 << 11) 14563722e52SEdward Tomasz Napierala #define CDCEEM_CMD_TICKLE (0x5 << 11) 14663722e52SEdward Tomasz Napierala 14763722e52SEdward Tomasz Napierala #define CDCEEM_CMD_RESERVED (0x1 << 14) 14863722e52SEdward Tomasz Napierala 14963722e52SEdward Tomasz Napierala #define CDCEEM_ECHO_LEN_MASK 0x3ff 15063722e52SEdward Tomasz Napierala 15163722e52SEdward Tomasz Napierala #define CDCEEM_DATA_CRC (0x1 << 14) 15263722e52SEdward Tomasz Napierala #define CDCEEM_DATA_LEN_MASK 0x3fff 15363722e52SEdward Tomasz Napierala 15463722e52SEdward Tomasz Napierala static device_probe_t cdceem_probe; 15563722e52SEdward Tomasz Napierala static device_attach_t cdceem_attach; 15663722e52SEdward Tomasz Napierala static device_detach_t cdceem_detach; 15763722e52SEdward Tomasz Napierala static device_suspend_t cdceem_suspend; 15863722e52SEdward Tomasz Napierala static device_resume_t cdceem_resume; 15963722e52SEdward Tomasz Napierala 16063722e52SEdward Tomasz Napierala static usb_callback_t cdceem_bulk_write_callback; 16163722e52SEdward Tomasz Napierala static usb_callback_t cdceem_bulk_read_callback; 16263722e52SEdward Tomasz Napierala 16363722e52SEdward Tomasz Napierala static uether_fn_t cdceem_attach_post; 16463722e52SEdward Tomasz Napierala static uether_fn_t cdceem_init; 16563722e52SEdward Tomasz Napierala static uether_fn_t cdceem_stop; 16663722e52SEdward Tomasz Napierala static uether_fn_t cdceem_start; 16763722e52SEdward Tomasz Napierala static uether_fn_t cdceem_setmulti; 16863722e52SEdward Tomasz Napierala static uether_fn_t cdceem_setpromisc; 16963722e52SEdward Tomasz Napierala 17063722e52SEdward Tomasz Napierala static uint32_t cdceem_m_crc32(struct mbuf *, uint32_t, uint32_t); 17163722e52SEdward Tomasz Napierala 17263722e52SEdward Tomasz Napierala static const struct usb_config cdceem_config[CDCEEM_N_TRANSFER] = { 17363722e52SEdward Tomasz Napierala [CDCEEM_BULK_TX] = { 17463722e52SEdward Tomasz Napierala .type = UE_BULK, 17563722e52SEdward Tomasz Napierala .endpoint = UE_ADDR_ANY, 17663722e52SEdward Tomasz Napierala .direction = UE_DIR_TX, 17763722e52SEdward Tomasz Napierala .bufsize = 16 * (MCLBYTES + 16), 17863722e52SEdward Tomasz Napierala .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 17963722e52SEdward Tomasz Napierala .callback = cdceem_bulk_write_callback, 18063722e52SEdward Tomasz Napierala .timeout = 10000, /* 10 seconds */ 18163722e52SEdward Tomasz Napierala .usb_mode = USB_MODE_DUAL, 18263722e52SEdward Tomasz Napierala }, 18363722e52SEdward Tomasz Napierala 18463722e52SEdward Tomasz Napierala [CDCEEM_BULK_RX] = { 18563722e52SEdward Tomasz Napierala .type = UE_BULK, 18663722e52SEdward Tomasz Napierala .endpoint = UE_ADDR_ANY, 18763722e52SEdward Tomasz Napierala .direction = UE_DIR_RX, 18863722e52SEdward Tomasz Napierala .bufsize = 20480, /* bytes */ 18963722e52SEdward Tomasz Napierala .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 19063722e52SEdward Tomasz Napierala .callback = cdceem_bulk_read_callback, 19163722e52SEdward Tomasz Napierala .timeout = 0, /* no timeout */ 19263722e52SEdward Tomasz Napierala .usb_mode = USB_MODE_DUAL, 19363722e52SEdward Tomasz Napierala }, 19463722e52SEdward Tomasz Napierala }; 19563722e52SEdward Tomasz Napierala 19663722e52SEdward Tomasz Napierala static device_method_t cdceem_methods[] = { 19763722e52SEdward Tomasz Napierala /* Device interface */ 19863722e52SEdward Tomasz Napierala DEVMETHOD(device_probe, cdceem_probe), 19963722e52SEdward Tomasz Napierala DEVMETHOD(device_attach, cdceem_attach), 20063722e52SEdward Tomasz Napierala DEVMETHOD(device_detach, cdceem_detach), 20163722e52SEdward Tomasz Napierala DEVMETHOD(device_suspend, cdceem_suspend), 20263722e52SEdward Tomasz Napierala DEVMETHOD(device_resume, cdceem_resume), 20363722e52SEdward Tomasz Napierala 20463722e52SEdward Tomasz Napierala DEVMETHOD_END 20563722e52SEdward Tomasz Napierala }; 20663722e52SEdward Tomasz Napierala 20763722e52SEdward Tomasz Napierala static driver_t cdceem_driver = { 20863722e52SEdward Tomasz Napierala .name = "cdceem", 20963722e52SEdward Tomasz Napierala .methods = cdceem_methods, 21063722e52SEdward Tomasz Napierala .size = sizeof(struct cdceem_softc), 21163722e52SEdward Tomasz Napierala }; 21263722e52SEdward Tomasz Napierala 21363722e52SEdward Tomasz Napierala static devclass_t cdceem_devclass; 21463722e52SEdward Tomasz Napierala 21563722e52SEdward Tomasz Napierala static const STRUCT_USB_DUAL_ID cdceem_dual_devs[] = { 21663722e52SEdward Tomasz Napierala {USB_IFACE_CLASS(UICLASS_CDC), 21763722e52SEdward Tomasz Napierala USB_IFACE_SUBCLASS(UISUBCLASS_ETHERNET_EMULATION_MODEL), 21863722e52SEdward Tomasz Napierala 0}, 21963722e52SEdward Tomasz Napierala }; 22063722e52SEdward Tomasz Napierala 22163722e52SEdward Tomasz Napierala DRIVER_MODULE(cdceem, uhub, cdceem_driver, cdceem_devclass, NULL, NULL); 22263722e52SEdward Tomasz Napierala MODULE_VERSION(cdceem, 1); 22363722e52SEdward Tomasz Napierala MODULE_DEPEND(cdceem, uether, 1, 1, 1); 22463722e52SEdward Tomasz Napierala MODULE_DEPEND(cdceem, usb, 1, 1, 1); 22563722e52SEdward Tomasz Napierala MODULE_DEPEND(cdceem, ether, 1, 1, 1); 22663722e52SEdward Tomasz Napierala USB_PNP_DUAL_INFO(cdceem_dual_devs); 22763722e52SEdward Tomasz Napierala 22863722e52SEdward Tomasz Napierala static const struct usb_ether_methods cdceem_ue_methods = { 22963722e52SEdward Tomasz Napierala .ue_attach_post = cdceem_attach_post, 23063722e52SEdward Tomasz Napierala .ue_start = cdceem_start, 23163722e52SEdward Tomasz Napierala .ue_init = cdceem_init, 23263722e52SEdward Tomasz Napierala .ue_stop = cdceem_stop, 23363722e52SEdward Tomasz Napierala .ue_setmulti = cdceem_setmulti, 23463722e52SEdward Tomasz Napierala .ue_setpromisc = cdceem_setpromisc, 23563722e52SEdward Tomasz Napierala }; 23663722e52SEdward Tomasz Napierala 23763722e52SEdward Tomasz Napierala static int 23863722e52SEdward Tomasz Napierala cdceem_probe(device_t dev) 23963722e52SEdward Tomasz Napierala { 24063722e52SEdward Tomasz Napierala struct usb_attach_arg *uaa; 24163722e52SEdward Tomasz Napierala int error; 24263722e52SEdward Tomasz Napierala 24363722e52SEdward Tomasz Napierala uaa = device_get_ivars(dev); 24463722e52SEdward Tomasz Napierala error = usbd_lookup_id_by_uaa(cdceem_dual_devs, 24563722e52SEdward Tomasz Napierala sizeof(cdceem_dual_devs), uaa); 24663722e52SEdward Tomasz Napierala 24763722e52SEdward Tomasz Napierala return (error); 24863722e52SEdward Tomasz Napierala } 24963722e52SEdward Tomasz Napierala 25063722e52SEdward Tomasz Napierala static void 25163722e52SEdward Tomasz Napierala cdceem_attach_post(struct usb_ether *ue) 25263722e52SEdward Tomasz Napierala { 25363722e52SEdward Tomasz Napierala 25463722e52SEdward Tomasz Napierala return; 25563722e52SEdward Tomasz Napierala } 25663722e52SEdward Tomasz Napierala 25763722e52SEdward Tomasz Napierala static int 25863722e52SEdward Tomasz Napierala cdceem_attach(device_t dev) 25963722e52SEdward Tomasz Napierala { 26063722e52SEdward Tomasz Napierala struct cdceem_softc *sc; 26163722e52SEdward Tomasz Napierala struct usb_ether *ue; 26263722e52SEdward Tomasz Napierala struct usb_attach_arg *uaa; 26363722e52SEdward Tomasz Napierala int error; 26463722e52SEdward Tomasz Napierala uint8_t iface_index; 26563722e52SEdward Tomasz Napierala 26663722e52SEdward Tomasz Napierala sc = device_get_softc(dev); 26763722e52SEdward Tomasz Napierala ue = &sc->sc_ue; 26863722e52SEdward Tomasz Napierala uaa = device_get_ivars(dev); 26963722e52SEdward Tomasz Napierala 27063722e52SEdward Tomasz Napierala device_set_usb_desc(dev); 27163722e52SEdward Tomasz Napierala 27263722e52SEdward Tomasz Napierala mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); 27363722e52SEdward Tomasz Napierala 27463722e52SEdward Tomasz Napierala /* Setup the endpoints. */ 27563722e52SEdward Tomasz Napierala iface_index = 0; 27663722e52SEdward Tomasz Napierala error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, 27763722e52SEdward Tomasz Napierala cdceem_config, CDCEEM_N_TRANSFER, sc, &sc->sc_mtx); 27863722e52SEdward Tomasz Napierala if (error != 0) { 27963722e52SEdward Tomasz Napierala CDCEEM_WARN(sc, 28063722e52SEdward Tomasz Napierala "allocating USB transfers failed, error %d", error); 28163722e52SEdward Tomasz Napierala mtx_destroy(&sc->sc_mtx); 28263722e52SEdward Tomasz Napierala return (error); 28363722e52SEdward Tomasz Napierala } 28463722e52SEdward Tomasz Napierala 28563722e52SEdward Tomasz Napierala /* Random MAC address. */ 28663722e52SEdward Tomasz Napierala arc4rand(ue->ue_eaddr, ETHER_ADDR_LEN, 0); 28763722e52SEdward Tomasz Napierala ue->ue_eaddr[0] &= ~0x01; /* unicast */ 28863722e52SEdward Tomasz Napierala ue->ue_eaddr[0] |= 0x02; /* locally administered */ 28963722e52SEdward Tomasz Napierala 29063722e52SEdward Tomasz Napierala ue->ue_sc = sc; 29163722e52SEdward Tomasz Napierala ue->ue_dev = dev; 29263722e52SEdward Tomasz Napierala ue->ue_udev = uaa->device; 29363722e52SEdward Tomasz Napierala ue->ue_mtx = &sc->sc_mtx; 29463722e52SEdward Tomasz Napierala ue->ue_methods = &cdceem_ue_methods; 29563722e52SEdward Tomasz Napierala 29663722e52SEdward Tomasz Napierala error = uether_ifattach(ue); 29763722e52SEdward Tomasz Napierala if (error != 0) { 29863722e52SEdward Tomasz Napierala CDCEEM_WARN(sc, "could not attach interface, error %d", error); 29963722e52SEdward Tomasz Napierala usbd_transfer_unsetup(sc->sc_xfer, CDCEEM_N_TRANSFER); 30063722e52SEdward Tomasz Napierala mtx_destroy(&sc->sc_mtx); 30163722e52SEdward Tomasz Napierala return (error); 30263722e52SEdward Tomasz Napierala } 30363722e52SEdward Tomasz Napierala 30463722e52SEdward Tomasz Napierala return (0); 30563722e52SEdward Tomasz Napierala } 30663722e52SEdward Tomasz Napierala 30763722e52SEdward Tomasz Napierala static int 30863722e52SEdward Tomasz Napierala cdceem_detach(device_t dev) 30963722e52SEdward Tomasz Napierala { 31063722e52SEdward Tomasz Napierala struct cdceem_softc *sc = device_get_softc(dev); 31163722e52SEdward Tomasz Napierala struct usb_ether *ue = &sc->sc_ue; 31263722e52SEdward Tomasz Napierala 31363722e52SEdward Tomasz Napierala /* Stop all USB transfers first. */ 31463722e52SEdward Tomasz Napierala usbd_transfer_unsetup(sc->sc_xfer, CDCEEM_N_TRANSFER); 31563722e52SEdward Tomasz Napierala uether_ifdetach(ue); 31663722e52SEdward Tomasz Napierala mtx_destroy(&sc->sc_mtx); 31763722e52SEdward Tomasz Napierala 31863722e52SEdward Tomasz Napierala return (0); 31963722e52SEdward Tomasz Napierala } 32063722e52SEdward Tomasz Napierala 32163722e52SEdward Tomasz Napierala static void 32263722e52SEdward Tomasz Napierala cdceem_handle_cmd(struct usb_xfer *xfer, uint16_t hdr, int *offp) 32363722e52SEdward Tomasz Napierala { 32463722e52SEdward Tomasz Napierala struct cdceem_softc *sc; 32563722e52SEdward Tomasz Napierala struct usb_page_cache *pc; 32663722e52SEdward Tomasz Napierala int actlen, off; 32763722e52SEdward Tomasz Napierala uint16_t pktlen; 32863722e52SEdward Tomasz Napierala 32963722e52SEdward Tomasz Napierala off = *offp; 33063722e52SEdward Tomasz Napierala sc = usbd_xfer_softc(xfer); 33163722e52SEdward Tomasz Napierala pc = usbd_xfer_get_frame(xfer, 0); 33263722e52SEdward Tomasz Napierala usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 33363722e52SEdward Tomasz Napierala 33463722e52SEdward Tomasz Napierala if (hdr & CDCEEM_CMD_RESERVED) { 33563722e52SEdward Tomasz Napierala CDCEEM_WARN(sc, "received command header %#x " 33663722e52SEdward Tomasz Napierala "with Reserved bit set; ignoring", hdr); 33763722e52SEdward Tomasz Napierala return; 33863722e52SEdward Tomasz Napierala } 33963722e52SEdward Tomasz Napierala 34063722e52SEdward Tomasz Napierala switch (hdr & CDCEEM_CMD_MASK) { 34163722e52SEdward Tomasz Napierala case CDCEEM_CMD_ECHO: 34263722e52SEdward Tomasz Napierala pktlen = hdr & CDCEEM_ECHO_LEN_MASK; 34363722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "received Echo, length %d", pktlen); 34463722e52SEdward Tomasz Napierala 34563722e52SEdward Tomasz Napierala if (pktlen > (actlen - off)) { 34663722e52SEdward Tomasz Napierala CDCEEM_WARN(sc, 34763722e52SEdward Tomasz Napierala "bad Echo length %d, should be at most %d", 34863722e52SEdward Tomasz Napierala pktlen, actlen - off); 34963722e52SEdward Tomasz Napierala break; 35063722e52SEdward Tomasz Napierala } 35163722e52SEdward Tomasz Napierala 35263722e52SEdward Tomasz Napierala if (pktlen > sizeof(sc->sc_echo_buffer)) { 35363722e52SEdward Tomasz Napierala CDCEEM_WARN(sc, 35463722e52SEdward Tomasz Napierala "Echo length %u too big, must be less than %zd", 35563722e52SEdward Tomasz Napierala pktlen, sizeof(sc->sc_echo_buffer)); 35663722e52SEdward Tomasz Napierala break; 35763722e52SEdward Tomasz Napierala } 35863722e52SEdward Tomasz Napierala 35963722e52SEdward Tomasz Napierala sc->sc_flags |= CDCEEM_SC_FLAGS_ECHO_RESPONSE_PENDING; 36063722e52SEdward Tomasz Napierala sc->sc_echo_len = pktlen; 36163722e52SEdward Tomasz Napierala usbd_copy_out(pc, off, sc->sc_echo_buffer, pktlen); 36263722e52SEdward Tomasz Napierala off += pktlen; 36363722e52SEdward Tomasz Napierala break; 36463722e52SEdward Tomasz Napierala 36563722e52SEdward Tomasz Napierala case CDCEEM_CMD_ECHO_RESPONSE: 36663722e52SEdward Tomasz Napierala pktlen = hdr & CDCEEM_ECHO_LEN_MASK; 36763722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "received Echo Response, length %d", pktlen); 36863722e52SEdward Tomasz Napierala 36963722e52SEdward Tomasz Napierala if (pktlen > (actlen - off)) { 37063722e52SEdward Tomasz Napierala CDCEEM_WARN(sc, 37163722e52SEdward Tomasz Napierala "bad Echo Response length %d, " 37263722e52SEdward Tomasz Napierala "should be at most %d", 37363722e52SEdward Tomasz Napierala pktlen, actlen - off); 37463722e52SEdward Tomasz Napierala break; 37563722e52SEdward Tomasz Napierala } 37663722e52SEdward Tomasz Napierala 37763722e52SEdward Tomasz Napierala if (pktlen != sizeof(CDCEEM_ECHO_PAYLOAD)) { 37863722e52SEdward Tomasz Napierala CDCEEM_WARN(sc, "received Echo Response with bad " 37963722e52SEdward Tomasz Napierala "length %hu, should be %zd", 38063722e52SEdward Tomasz Napierala pktlen, sizeof(CDCEEM_ECHO_PAYLOAD)); 38163722e52SEdward Tomasz Napierala break; 38263722e52SEdward Tomasz Napierala } 38363722e52SEdward Tomasz Napierala 38463722e52SEdward Tomasz Napierala usbd_copy_out(pc, off, sc->sc_echo_buffer, pktlen); 38563722e52SEdward Tomasz Napierala off += pktlen; 38663722e52SEdward Tomasz Napierala 38763722e52SEdward Tomasz Napierala if (memcmp(sc->sc_echo_buffer, CDCEEM_ECHO_PAYLOAD, 38863722e52SEdward Tomasz Napierala sizeof(CDCEEM_ECHO_PAYLOAD)) != 0) { 38963722e52SEdward Tomasz Napierala CDCEEM_WARN(sc, 39063722e52SEdward Tomasz Napierala "received Echo Response payload does not match"); 39163722e52SEdward Tomasz Napierala } else { 39263722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "received Echo Response is valid"); 39363722e52SEdward Tomasz Napierala } 39463722e52SEdward Tomasz Napierala break; 39563722e52SEdward Tomasz Napierala 39663722e52SEdward Tomasz Napierala case CDCEEM_CMD_SUSPEND_HINT: 39763722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "received SuspendHint; ignoring"); 39863722e52SEdward Tomasz Napierala break; 39963722e52SEdward Tomasz Napierala 40063722e52SEdward Tomasz Napierala case CDCEEM_CMD_RESPONSE_HINT: 40163722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "received ResponseHint; ignoring"); 40263722e52SEdward Tomasz Napierala break; 40363722e52SEdward Tomasz Napierala 40463722e52SEdward Tomasz Napierala case CDCEEM_CMD_RESPONSE_COMPLETE_HINT: 40563722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "received ResponseCompleteHint; ignoring"); 40663722e52SEdward Tomasz Napierala break; 40763722e52SEdward Tomasz Napierala 40863722e52SEdward Tomasz Napierala case CDCEEM_CMD_TICKLE: 40963722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "received Tickle; ignoring"); 41063722e52SEdward Tomasz Napierala break; 41163722e52SEdward Tomasz Napierala 41263722e52SEdward Tomasz Napierala default: 41363722e52SEdward Tomasz Napierala CDCEEM_WARN(sc, 41463722e52SEdward Tomasz Napierala "received unknown command %u, header %#x; ignoring", 41563722e52SEdward Tomasz Napierala (hdr & CDCEEM_CMD_MASK >> 11), hdr); 41663722e52SEdward Tomasz Napierala break; 41763722e52SEdward Tomasz Napierala } 41863722e52SEdward Tomasz Napierala 41963722e52SEdward Tomasz Napierala *offp = off; 42063722e52SEdward Tomasz Napierala } 42163722e52SEdward Tomasz Napierala 42263722e52SEdward Tomasz Napierala static void 42363722e52SEdward Tomasz Napierala cdceem_handle_data(struct usb_xfer *xfer, uint16_t hdr, int *offp) 42463722e52SEdward Tomasz Napierala { 42563722e52SEdward Tomasz Napierala struct cdceem_softc *sc; 42663722e52SEdward Tomasz Napierala struct usb_page_cache *pc; 42763722e52SEdward Tomasz Napierala struct usb_ether *ue; 42863722e52SEdward Tomasz Napierala struct ifnet *ifp; 42963722e52SEdward Tomasz Napierala struct mbuf *m; 43063722e52SEdward Tomasz Napierala uint32_t computed_crc, received_crc; 4319c847ffdSHans Petter Selasky int pktlen; 4329c847ffdSHans Petter Selasky int actlen; 4339c847ffdSHans Petter Selasky int off; 43463722e52SEdward Tomasz Napierala 43563722e52SEdward Tomasz Napierala off = *offp; 43663722e52SEdward Tomasz Napierala sc = usbd_xfer_softc(xfer); 43763722e52SEdward Tomasz Napierala pc = usbd_xfer_get_frame(xfer, 0); 43863722e52SEdward Tomasz Napierala ue = &sc->sc_ue; 43963722e52SEdward Tomasz Napierala ifp = uether_getifp(ue); 44063722e52SEdward Tomasz Napierala usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 44163722e52SEdward Tomasz Napierala 44263722e52SEdward Tomasz Napierala pktlen = hdr & CDCEEM_DATA_LEN_MASK; 44363722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "received Data, CRC %s, length %d", 44463722e52SEdward Tomasz Napierala (hdr & CDCEEM_DATA_CRC) ? "valid" : "absent", 44563722e52SEdward Tomasz Napierala pktlen); 44663722e52SEdward Tomasz Napierala 4479c847ffdSHans Petter Selasky if (pktlen < (ETHER_HDR_LEN + 4)) { 44863722e52SEdward Tomasz Napierala CDCEEM_WARN(sc, 44963722e52SEdward Tomasz Napierala "bad ethernet frame length %d, should be at least %d", 45063722e52SEdward Tomasz Napierala pktlen, ETHER_HDR_LEN); 45163722e52SEdward Tomasz Napierala if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 45263722e52SEdward Tomasz Napierala return; 45363722e52SEdward Tomasz Napierala } 45463722e52SEdward Tomasz Napierala 45563722e52SEdward Tomasz Napierala if (pktlen > (actlen - off)) { 45663722e52SEdward Tomasz Napierala CDCEEM_WARN(sc, 45763722e52SEdward Tomasz Napierala "bad ethernet frame length %d, should be at most %d", 45863722e52SEdward Tomasz Napierala pktlen, actlen - off); 45963722e52SEdward Tomasz Napierala if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 46063722e52SEdward Tomasz Napierala return; 46163722e52SEdward Tomasz Napierala } 46263722e52SEdward Tomasz Napierala 46363722e52SEdward Tomasz Napierala m = uether_newbuf(); 46463722e52SEdward Tomasz Napierala if (m == NULL) { 46563722e52SEdward Tomasz Napierala CDCEEM_WARN(sc, "uether_newbuf() failed"); 46663722e52SEdward Tomasz Napierala if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); 46763722e52SEdward Tomasz Napierala return; 46863722e52SEdward Tomasz Napierala } 46963722e52SEdward Tomasz Napierala 47063722e52SEdward Tomasz Napierala pktlen -= 4; /* Subtract the CRC. */ 4719c847ffdSHans Petter Selasky 4729c847ffdSHans Petter Selasky if (pktlen > m->m_len) { 4739c847ffdSHans Petter Selasky CDCEEM_WARN(sc, "buffer too small %d vs %d bytes", 4749c847ffdSHans Petter Selasky pktlen, m->m_len); 4759c847ffdSHans Petter Selasky if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); 4769c847ffdSHans Petter Selasky m_freem(m); 4779c847ffdSHans Petter Selasky return; 4789c847ffdSHans Petter Selasky } 47963722e52SEdward Tomasz Napierala usbd_copy_out(pc, off, mtod(m, uint8_t *), pktlen); 48063722e52SEdward Tomasz Napierala off += pktlen; 48163722e52SEdward Tomasz Napierala 48263722e52SEdward Tomasz Napierala usbd_copy_out(pc, off, &received_crc, sizeof(received_crc)); 48363722e52SEdward Tomasz Napierala off += sizeof(received_crc); 48463722e52SEdward Tomasz Napierala 48563722e52SEdward Tomasz Napierala if (hdr & CDCEEM_DATA_CRC) { 48663722e52SEdward Tomasz Napierala computed_crc = cdceem_m_crc32(m, 0, pktlen); 48763722e52SEdward Tomasz Napierala } else { 48863722e52SEdward Tomasz Napierala computed_crc = be32toh(0xdeadbeef); 48963722e52SEdward Tomasz Napierala } 49063722e52SEdward Tomasz Napierala 49163722e52SEdward Tomasz Napierala if (received_crc != computed_crc) { 49263722e52SEdward Tomasz Napierala CDCEEM_WARN(sc, 49363722e52SEdward Tomasz Napierala "received Data packet with wrong CRC %#x, expected %#x", 49463722e52SEdward Tomasz Napierala received_crc, computed_crc); 49563722e52SEdward Tomasz Napierala if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 49663722e52SEdward Tomasz Napierala m_freem(m); 49763722e52SEdward Tomasz Napierala return; 49863722e52SEdward Tomasz Napierala } else { 49963722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "received correct CRC %#x", received_crc); 50063722e52SEdward Tomasz Napierala } 50163722e52SEdward Tomasz Napierala 50263722e52SEdward Tomasz Napierala uether_rxmbuf(ue, m, pktlen); 50363722e52SEdward Tomasz Napierala *offp = off; 50463722e52SEdward Tomasz Napierala } 50563722e52SEdward Tomasz Napierala 50663722e52SEdward Tomasz Napierala static void 50763722e52SEdward Tomasz Napierala cdceem_bulk_read_callback(struct usb_xfer *xfer, usb_error_t usb_error) 50863722e52SEdward Tomasz Napierala { 50963722e52SEdward Tomasz Napierala struct cdceem_softc *sc; 51063722e52SEdward Tomasz Napierala struct usb_page_cache *pc; 51163722e52SEdward Tomasz Napierala int actlen, aframes, off; 51263722e52SEdward Tomasz Napierala uint16_t hdr; 51363722e52SEdward Tomasz Napierala 51463722e52SEdward Tomasz Napierala sc = usbd_xfer_softc(xfer); 51563722e52SEdward Tomasz Napierala usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); 51663722e52SEdward Tomasz Napierala 51763722e52SEdward Tomasz Napierala switch (USB_GET_STATE(xfer)) { 51863722e52SEdward Tomasz Napierala case USB_ST_TRANSFERRED: 51963722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, 52063722e52SEdward Tomasz Napierala "received %u bytes in %u frames", actlen, aframes); 52163722e52SEdward Tomasz Napierala 52263722e52SEdward Tomasz Napierala pc = usbd_xfer_get_frame(xfer, 0); 52363722e52SEdward Tomasz Napierala off = 0; 52463722e52SEdward Tomasz Napierala 5259c847ffdSHans Petter Selasky while ((off + sizeof(hdr)) <= actlen) { 52663722e52SEdward Tomasz Napierala usbd_copy_out(pc, off, &hdr, sizeof(hdr)); 52763722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "hdr = %#x", hdr); 52863722e52SEdward Tomasz Napierala off += sizeof(hdr); 52963722e52SEdward Tomasz Napierala 53063722e52SEdward Tomasz Napierala if (hdr == 0) { 53163722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "received Zero Length EEM"); 53263722e52SEdward Tomasz Napierala continue; 53363722e52SEdward Tomasz Napierala } 53463722e52SEdward Tomasz Napierala 53563722e52SEdward Tomasz Napierala hdr = le16toh(hdr); 53663722e52SEdward Tomasz Napierala 53763722e52SEdward Tomasz Napierala if ((hdr & CDCEEM_TYPE_CMD) != 0) { 53863722e52SEdward Tomasz Napierala cdceem_handle_cmd(xfer, hdr, &off); 53963722e52SEdward Tomasz Napierala } else { 54063722e52SEdward Tomasz Napierala cdceem_handle_data(xfer, hdr, &off); 54163722e52SEdward Tomasz Napierala } 54263722e52SEdward Tomasz Napierala 54363722e52SEdward Tomasz Napierala KASSERT(off <= actlen, 54463722e52SEdward Tomasz Napierala ("%s: went past the buffer, off %d, actlen %d", 54563722e52SEdward Tomasz Napierala __func__, off, actlen)); 54663722e52SEdward Tomasz Napierala } 54763722e52SEdward Tomasz Napierala 54863722e52SEdward Tomasz Napierala /* FALLTHROUGH */ 54963722e52SEdward Tomasz Napierala case USB_ST_SETUP: 55063722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "setup"); 55163722e52SEdward Tomasz Napierala tr_setup: 55263722e52SEdward Tomasz Napierala usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 55363722e52SEdward Tomasz Napierala usbd_transfer_submit(xfer); 55463722e52SEdward Tomasz Napierala uether_rxflush(&sc->sc_ue); 55563722e52SEdward Tomasz Napierala break; 55663722e52SEdward Tomasz Napierala 55763722e52SEdward Tomasz Napierala default: 55863722e52SEdward Tomasz Napierala CDCEEM_WARN(sc, "USB_ST_ERROR: %s", usbd_errstr(usb_error)); 55963722e52SEdward Tomasz Napierala 56063722e52SEdward Tomasz Napierala if (usb_error != USB_ERR_CANCELLED) { 56163722e52SEdward Tomasz Napierala /* try to clear stall first */ 56263722e52SEdward Tomasz Napierala usbd_xfer_set_stall(xfer); 56363722e52SEdward Tomasz Napierala goto tr_setup; 56463722e52SEdward Tomasz Napierala } 56563722e52SEdward Tomasz Napierala break; 56663722e52SEdward Tomasz Napierala } 56763722e52SEdward Tomasz Napierala } 56863722e52SEdward Tomasz Napierala 56963722e52SEdward Tomasz Napierala static void 57063722e52SEdward Tomasz Napierala cdceem_send_echo(struct usb_xfer *xfer, int *offp) 57163722e52SEdward Tomasz Napierala { 57263722e52SEdward Tomasz Napierala struct cdceem_softc *sc; 57363722e52SEdward Tomasz Napierala struct usb_page_cache *pc; 574*1cde2140SEdward Tomasz Napierala int maxlen __diagused, off; 57563722e52SEdward Tomasz Napierala uint16_t hdr; 57663722e52SEdward Tomasz Napierala 57763722e52SEdward Tomasz Napierala off = *offp; 57863722e52SEdward Tomasz Napierala sc = usbd_xfer_softc(xfer); 57963722e52SEdward Tomasz Napierala pc = usbd_xfer_get_frame(xfer, 0); 58063722e52SEdward Tomasz Napierala maxlen = usbd_xfer_max_len(xfer); 58163722e52SEdward Tomasz Napierala 58263722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "sending Echo, length %zd", 58363722e52SEdward Tomasz Napierala sizeof(CDCEEM_ECHO_PAYLOAD)); 58463722e52SEdward Tomasz Napierala 58563722e52SEdward Tomasz Napierala KASSERT(off + sizeof(hdr) + sizeof(CDCEEM_ECHO_PAYLOAD) < maxlen, 58663722e52SEdward Tomasz Napierala ("%s: out of space; have %d, need %zd", __func__, maxlen, 58763722e52SEdward Tomasz Napierala off + sizeof(hdr) + sizeof(CDCEEM_ECHO_PAYLOAD))); 58863722e52SEdward Tomasz Napierala 58963722e52SEdward Tomasz Napierala hdr = 0; 59063722e52SEdward Tomasz Napierala hdr |= CDCEEM_TYPE_CMD; 59163722e52SEdward Tomasz Napierala hdr |= CDCEEM_CMD_ECHO; 59263722e52SEdward Tomasz Napierala hdr |= sizeof(CDCEEM_ECHO_PAYLOAD); 59363722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "hdr = %#x", hdr); 59463722e52SEdward Tomasz Napierala hdr = htole16(hdr); 59563722e52SEdward Tomasz Napierala 59663722e52SEdward Tomasz Napierala usbd_copy_in(pc, off, &hdr, sizeof(hdr)); 59763722e52SEdward Tomasz Napierala off += sizeof(hdr); 59863722e52SEdward Tomasz Napierala 59963722e52SEdward Tomasz Napierala usbd_copy_in(pc, off, CDCEEM_ECHO_PAYLOAD, 60063722e52SEdward Tomasz Napierala sizeof(CDCEEM_ECHO_PAYLOAD)); 60163722e52SEdward Tomasz Napierala off += sizeof(CDCEEM_ECHO_PAYLOAD); 60263722e52SEdward Tomasz Napierala 60363722e52SEdward Tomasz Napierala sc->sc_flags &= ~CDCEEM_SC_FLAGS_ECHO_PENDING; 60463722e52SEdward Tomasz Napierala 60563722e52SEdward Tomasz Napierala *offp = off; 60663722e52SEdward Tomasz Napierala } 60763722e52SEdward Tomasz Napierala 60863722e52SEdward Tomasz Napierala static void 60963722e52SEdward Tomasz Napierala cdceem_send_echo_response(struct usb_xfer *xfer, int *offp) 61063722e52SEdward Tomasz Napierala { 61163722e52SEdward Tomasz Napierala struct cdceem_softc *sc; 61263722e52SEdward Tomasz Napierala struct usb_page_cache *pc; 613*1cde2140SEdward Tomasz Napierala int maxlen __diagused, off; 61463722e52SEdward Tomasz Napierala uint16_t hdr; 61563722e52SEdward Tomasz Napierala 61663722e52SEdward Tomasz Napierala off = *offp; 61763722e52SEdward Tomasz Napierala sc = usbd_xfer_softc(xfer); 61863722e52SEdward Tomasz Napierala pc = usbd_xfer_get_frame(xfer, 0); 61963722e52SEdward Tomasz Napierala maxlen = usbd_xfer_max_len(xfer); 62063722e52SEdward Tomasz Napierala 62163722e52SEdward Tomasz Napierala KASSERT(off + sizeof(hdr) + sc->sc_echo_len < maxlen, 62263722e52SEdward Tomasz Napierala ("%s: out of space; have %d, need %zd", __func__, maxlen, 62363722e52SEdward Tomasz Napierala off + sizeof(hdr) + sc->sc_echo_len)); 62463722e52SEdward Tomasz Napierala 62563722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "sending Echo Response, length %zd", sc->sc_echo_len); 62663722e52SEdward Tomasz Napierala 62763722e52SEdward Tomasz Napierala hdr = 0; 62863722e52SEdward Tomasz Napierala hdr |= CDCEEM_TYPE_CMD; 62963722e52SEdward Tomasz Napierala hdr |= CDCEEM_CMD_ECHO_RESPONSE; 63063722e52SEdward Tomasz Napierala hdr |= sc->sc_echo_len; 63163722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "hdr = %#x", hdr); 63263722e52SEdward Tomasz Napierala hdr = htole16(hdr); 63363722e52SEdward Tomasz Napierala 63463722e52SEdward Tomasz Napierala usbd_copy_in(pc, off, &hdr, sizeof(hdr)); 63563722e52SEdward Tomasz Napierala off += sizeof(hdr); 63663722e52SEdward Tomasz Napierala 63763722e52SEdward Tomasz Napierala usbd_copy_in(pc, off, sc->sc_echo_buffer, sc->sc_echo_len); 63863722e52SEdward Tomasz Napierala off += sc->sc_echo_len; 63963722e52SEdward Tomasz Napierala 64063722e52SEdward Tomasz Napierala sc->sc_flags &= ~CDCEEM_SC_FLAGS_ECHO_RESPONSE_PENDING; 64163722e52SEdward Tomasz Napierala sc->sc_echo_len = 0; 64263722e52SEdward Tomasz Napierala 64363722e52SEdward Tomasz Napierala *offp = off; 64463722e52SEdward Tomasz Napierala } 64563722e52SEdward Tomasz Napierala 64663722e52SEdward Tomasz Napierala static void 64763722e52SEdward Tomasz Napierala cdceem_send_data(struct usb_xfer *xfer, int *offp) 64863722e52SEdward Tomasz Napierala { 64963722e52SEdward Tomasz Napierala struct cdceem_softc *sc; 65063722e52SEdward Tomasz Napierala struct usb_page_cache *pc; 65163722e52SEdward Tomasz Napierala struct ifnet *ifp; 65263722e52SEdward Tomasz Napierala struct mbuf *m; 653*1cde2140SEdward Tomasz Napierala int maxlen __diagused, off; 65463722e52SEdward Tomasz Napierala uint32_t crc; 65563722e52SEdward Tomasz Napierala uint16_t hdr; 65663722e52SEdward Tomasz Napierala 65763722e52SEdward Tomasz Napierala off = *offp; 65863722e52SEdward Tomasz Napierala sc = usbd_xfer_softc(xfer); 65963722e52SEdward Tomasz Napierala pc = usbd_xfer_get_frame(xfer, 0); 66063722e52SEdward Tomasz Napierala ifp = uether_getifp(&sc->sc_ue); 66163722e52SEdward Tomasz Napierala maxlen = usbd_xfer_max_len(xfer); 66263722e52SEdward Tomasz Napierala 66363722e52SEdward Tomasz Napierala IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 66463722e52SEdward Tomasz Napierala if (m == NULL) { 66563722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "no Data packets to send"); 66663722e52SEdward Tomasz Napierala return; 66763722e52SEdward Tomasz Napierala } 66863722e52SEdward Tomasz Napierala 66963722e52SEdward Tomasz Napierala KASSERT((m->m_pkthdr.len & CDCEEM_DATA_LEN_MASK) == m->m_pkthdr.len, 67063722e52SEdward Tomasz Napierala ("%s: packet too long: %d, should be %d\n", __func__, 67163722e52SEdward Tomasz Napierala m->m_pkthdr.len, m->m_pkthdr.len & CDCEEM_DATA_LEN_MASK)); 67263722e52SEdward Tomasz Napierala KASSERT(off + sizeof(hdr) + m->m_pkthdr.len + 4 < maxlen, 67363722e52SEdward Tomasz Napierala ("%s: out of space; have %d, need %zd", __func__, maxlen, 67463722e52SEdward Tomasz Napierala off + sizeof(hdr) + m->m_pkthdr.len + 4)); 67563722e52SEdward Tomasz Napierala 67663722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "sending Data, length %d + 4", m->m_pkthdr.len); 67763722e52SEdward Tomasz Napierala 67863722e52SEdward Tomasz Napierala hdr = 0; 67963722e52SEdward Tomasz Napierala if (!cdceem_send_fake_crc) 68063722e52SEdward Tomasz Napierala hdr |= CDCEEM_DATA_CRC; 68163722e52SEdward Tomasz Napierala hdr |= (m->m_pkthdr.len + 4); /* +4 for CRC */ 68263722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "hdr = %#x", hdr); 68363722e52SEdward Tomasz Napierala hdr = htole16(hdr); 68463722e52SEdward Tomasz Napierala 68563722e52SEdward Tomasz Napierala usbd_copy_in(pc, off, &hdr, sizeof(hdr)); 68663722e52SEdward Tomasz Napierala off += sizeof(hdr); 68763722e52SEdward Tomasz Napierala 68863722e52SEdward Tomasz Napierala usbd_m_copy_in(pc, off, m, 0, m->m_pkthdr.len); 68963722e52SEdward Tomasz Napierala off += m->m_pkthdr.len; 69063722e52SEdward Tomasz Napierala 69163722e52SEdward Tomasz Napierala if (cdceem_send_fake_crc) { 69263722e52SEdward Tomasz Napierala crc = htobe32(0xdeadbeef); 69363722e52SEdward Tomasz Napierala } else { 69463722e52SEdward Tomasz Napierala crc = cdceem_m_crc32(m, 0, m->m_pkthdr.len); 69563722e52SEdward Tomasz Napierala } 69663722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "CRC = %#x", crc); 69763722e52SEdward Tomasz Napierala 69863722e52SEdward Tomasz Napierala usbd_copy_in(pc, off, &crc, sizeof(crc)); 69963722e52SEdward Tomasz Napierala off += sizeof(crc); 70063722e52SEdward Tomasz Napierala 70163722e52SEdward Tomasz Napierala if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 70263722e52SEdward Tomasz Napierala 70363722e52SEdward Tomasz Napierala /* 70463722e52SEdward Tomasz Napierala * If there's a BPF listener, bounce a copy of this frame to it. 70563722e52SEdward Tomasz Napierala */ 70663722e52SEdward Tomasz Napierala BPF_MTAP(ifp, m); 70763722e52SEdward Tomasz Napierala m_freem(m); 70863722e52SEdward Tomasz Napierala 70963722e52SEdward Tomasz Napierala *offp = off; 71063722e52SEdward Tomasz Napierala } 71163722e52SEdward Tomasz Napierala 71263722e52SEdward Tomasz Napierala static void 71363722e52SEdward Tomasz Napierala cdceem_bulk_write_callback(struct usb_xfer *xfer, usb_error_t usb_error) 71463722e52SEdward Tomasz Napierala { 71563722e52SEdward Tomasz Napierala struct cdceem_softc *sc; 71663722e52SEdward Tomasz Napierala struct ifnet *ifp; 717*1cde2140SEdward Tomasz Napierala int actlen, aframes, maxlen __diagused, off; 71863722e52SEdward Tomasz Napierala 71963722e52SEdward Tomasz Napierala sc = usbd_xfer_softc(xfer); 72063722e52SEdward Tomasz Napierala maxlen = usbd_xfer_max_len(xfer); 72163722e52SEdward Tomasz Napierala 72263722e52SEdward Tomasz Napierala switch (USB_GET_STATE(xfer)) { 72363722e52SEdward Tomasz Napierala case USB_ST_TRANSFERRED: 72463722e52SEdward Tomasz Napierala usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); 72563722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "transferred %u bytes in %u frames", 72663722e52SEdward Tomasz Napierala actlen, aframes); 72763722e52SEdward Tomasz Napierala 72863722e52SEdward Tomasz Napierala /* FALLTHROUGH */ 72963722e52SEdward Tomasz Napierala case USB_ST_SETUP: 73063722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "setup"); 73163722e52SEdward Tomasz Napierala tr_setup: 73263722e52SEdward Tomasz Napierala 73363722e52SEdward Tomasz Napierala off = 0; 73463722e52SEdward Tomasz Napierala usbd_xfer_set_frame_offset(xfer, 0, 0); 73563722e52SEdward Tomasz Napierala 73663722e52SEdward Tomasz Napierala if (sc->sc_flags & CDCEEM_SC_FLAGS_ECHO_PENDING) { 73763722e52SEdward Tomasz Napierala cdceem_send_echo(xfer, &off); 73863722e52SEdward Tomasz Napierala } else if (sc->sc_flags & CDCEEM_SC_FLAGS_ECHO_RESPONSE_PENDING) { 73963722e52SEdward Tomasz Napierala cdceem_send_echo_response(xfer, &off); 74063722e52SEdward Tomasz Napierala } else { 74163722e52SEdward Tomasz Napierala cdceem_send_data(xfer, &off); 74263722e52SEdward Tomasz Napierala } 74363722e52SEdward Tomasz Napierala 74463722e52SEdward Tomasz Napierala KASSERT(off <= maxlen, 74563722e52SEdward Tomasz Napierala ("%s: went past the buffer, off %d, maxlen %d", 74663722e52SEdward Tomasz Napierala __func__, off, maxlen)); 74763722e52SEdward Tomasz Napierala 74863722e52SEdward Tomasz Napierala if (off > 0) { 74963722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "starting transfer, length %d", off); 75063722e52SEdward Tomasz Napierala usbd_xfer_set_frame_len(xfer, 0, off); 75163722e52SEdward Tomasz Napierala usbd_transfer_submit(xfer); 75263722e52SEdward Tomasz Napierala } else { 75363722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "nothing to transfer"); 75463722e52SEdward Tomasz Napierala } 75563722e52SEdward Tomasz Napierala 75663722e52SEdward Tomasz Napierala break; 75763722e52SEdward Tomasz Napierala 75863722e52SEdward Tomasz Napierala default: 75963722e52SEdward Tomasz Napierala CDCEEM_WARN(sc, "USB_ST_ERROR: %s", usbd_errstr(usb_error)); 76063722e52SEdward Tomasz Napierala 76163722e52SEdward Tomasz Napierala ifp = uether_getifp(&sc->sc_ue); 76263722e52SEdward Tomasz Napierala if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 76363722e52SEdward Tomasz Napierala 76463722e52SEdward Tomasz Napierala if (usb_error != USB_ERR_CANCELLED) { 76563722e52SEdward Tomasz Napierala /* try to clear stall first */ 76663722e52SEdward Tomasz Napierala usbd_xfer_set_stall(xfer); 76763722e52SEdward Tomasz Napierala goto tr_setup; 76863722e52SEdward Tomasz Napierala } 76963722e52SEdward Tomasz Napierala break; 77063722e52SEdward Tomasz Napierala } 77163722e52SEdward Tomasz Napierala } 77263722e52SEdward Tomasz Napierala 77363722e52SEdward Tomasz Napierala static int32_t 77463722e52SEdward Tomasz Napierala cdceem_m_crc32_cb(void *arg, void *src, uint32_t count) 77563722e52SEdward Tomasz Napierala { 77663722e52SEdward Tomasz Napierala uint32_t *p_crc = arg; 77763722e52SEdward Tomasz Napierala 77863722e52SEdward Tomasz Napierala *p_crc = crc32_raw(src, count, *p_crc); 77963722e52SEdward Tomasz Napierala return (0); 78063722e52SEdward Tomasz Napierala } 78163722e52SEdward Tomasz Napierala 78263722e52SEdward Tomasz Napierala static uint32_t 78363722e52SEdward Tomasz Napierala cdceem_m_crc32(struct mbuf *m, uint32_t src_offset, uint32_t src_len) 78463722e52SEdward Tomasz Napierala { 78563722e52SEdward Tomasz Napierala uint32_t crc = 0xFFFFFFFF; 78663722e52SEdward Tomasz Napierala 787*1cde2140SEdward Tomasz Napierala m_apply(m, src_offset, src_len, cdceem_m_crc32_cb, &crc); 78863722e52SEdward Tomasz Napierala return (crc ^ 0xFFFFFFFF); 78963722e52SEdward Tomasz Napierala } 79063722e52SEdward Tomasz Napierala 79163722e52SEdward Tomasz Napierala static void 79263722e52SEdward Tomasz Napierala cdceem_start(struct usb_ether *ue) 79363722e52SEdward Tomasz Napierala { 79463722e52SEdward Tomasz Napierala struct cdceem_softc *sc; 79563722e52SEdward Tomasz Napierala 79663722e52SEdward Tomasz Napierala sc = uether_getsc(ue); 79763722e52SEdward Tomasz Napierala 79863722e52SEdward Tomasz Napierala /* 79963722e52SEdward Tomasz Napierala * Start the USB transfers, if not already started. 80063722e52SEdward Tomasz Napierala */ 80163722e52SEdward Tomasz Napierala usbd_transfer_start(sc->sc_xfer[CDCEEM_BULK_RX]); 80263722e52SEdward Tomasz Napierala usbd_transfer_start(sc->sc_xfer[CDCEEM_BULK_TX]); 80363722e52SEdward Tomasz Napierala } 80463722e52SEdward Tomasz Napierala 80563722e52SEdward Tomasz Napierala static void 80663722e52SEdward Tomasz Napierala cdceem_init(struct usb_ether *ue) 80763722e52SEdward Tomasz Napierala { 80863722e52SEdward Tomasz Napierala struct cdceem_softc *sc; 80963722e52SEdward Tomasz Napierala struct ifnet *ifp; 81063722e52SEdward Tomasz Napierala 81163722e52SEdward Tomasz Napierala sc = uether_getsc(ue); 81263722e52SEdward Tomasz Napierala ifp = uether_getifp(ue); 81363722e52SEdward Tomasz Napierala 81463722e52SEdward Tomasz Napierala ifp->if_drv_flags |= IFF_DRV_RUNNING; 81563722e52SEdward Tomasz Napierala 81663722e52SEdward Tomasz Napierala if (cdceem_send_echoes) 81763722e52SEdward Tomasz Napierala sc->sc_flags = CDCEEM_SC_FLAGS_ECHO_PENDING; 81863722e52SEdward Tomasz Napierala else 81963722e52SEdward Tomasz Napierala sc->sc_flags = 0; 82063722e52SEdward Tomasz Napierala 82163722e52SEdward Tomasz Napierala /* 82263722e52SEdward Tomasz Napierala * Stall data write direction, which depends on USB mode. 82363722e52SEdward Tomasz Napierala * 82463722e52SEdward Tomasz Napierala * Some USB host stacks (e.g. Mac OS X) don't clears stall 82563722e52SEdward Tomasz Napierala * bit as it should, so set it in our host mode only. 82663722e52SEdward Tomasz Napierala */ 82763722e52SEdward Tomasz Napierala if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) 82863722e52SEdward Tomasz Napierala usbd_xfer_set_stall(sc->sc_xfer[CDCEEM_BULK_TX]); 82963722e52SEdward Tomasz Napierala 83063722e52SEdward Tomasz Napierala cdceem_start(ue); 83163722e52SEdward Tomasz Napierala } 83263722e52SEdward Tomasz Napierala 83363722e52SEdward Tomasz Napierala static void 83463722e52SEdward Tomasz Napierala cdceem_stop(struct usb_ether *ue) 83563722e52SEdward Tomasz Napierala { 83663722e52SEdward Tomasz Napierala struct cdceem_softc *sc; 83763722e52SEdward Tomasz Napierala struct ifnet *ifp; 83863722e52SEdward Tomasz Napierala 83963722e52SEdward Tomasz Napierala sc = uether_getsc(ue); 84063722e52SEdward Tomasz Napierala ifp = uether_getifp(ue); 84163722e52SEdward Tomasz Napierala 84263722e52SEdward Tomasz Napierala ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 84363722e52SEdward Tomasz Napierala 84463722e52SEdward Tomasz Napierala usbd_transfer_stop(sc->sc_xfer[CDCEEM_BULK_RX]); 84563722e52SEdward Tomasz Napierala usbd_transfer_stop(sc->sc_xfer[CDCEEM_BULK_TX]); 84663722e52SEdward Tomasz Napierala } 84763722e52SEdward Tomasz Napierala 84863722e52SEdward Tomasz Napierala static void 84963722e52SEdward Tomasz Napierala cdceem_setmulti(struct usb_ether *ue) 85063722e52SEdward Tomasz Napierala { 85163722e52SEdward Tomasz Napierala /* no-op */ 85263722e52SEdward Tomasz Napierala return; 85363722e52SEdward Tomasz Napierala } 85463722e52SEdward Tomasz Napierala 85563722e52SEdward Tomasz Napierala static void 85663722e52SEdward Tomasz Napierala cdceem_setpromisc(struct usb_ether *ue) 85763722e52SEdward Tomasz Napierala { 85863722e52SEdward Tomasz Napierala /* no-op */ 85963722e52SEdward Tomasz Napierala return; 86063722e52SEdward Tomasz Napierala } 86163722e52SEdward Tomasz Napierala 86263722e52SEdward Tomasz Napierala static int 86363722e52SEdward Tomasz Napierala cdceem_suspend(device_t dev) 86463722e52SEdward Tomasz Napierala { 86563722e52SEdward Tomasz Napierala struct cdceem_softc *sc = device_get_softc(dev); 86663722e52SEdward Tomasz Napierala 86763722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "go"); 86863722e52SEdward Tomasz Napierala return (0); 86963722e52SEdward Tomasz Napierala } 87063722e52SEdward Tomasz Napierala 87163722e52SEdward Tomasz Napierala static int 87263722e52SEdward Tomasz Napierala cdceem_resume(device_t dev) 87363722e52SEdward Tomasz Napierala { 87463722e52SEdward Tomasz Napierala struct cdceem_softc *sc = device_get_softc(dev); 87563722e52SEdward Tomasz Napierala 87663722e52SEdward Tomasz Napierala CDCEEM_DEBUG(sc, "go"); 87763722e52SEdward Tomasz Napierala return (0); 87863722e52SEdward Tomasz Napierala } 879