From 63722e52121eb7b86537db4a3f53af8ad323b43c Mon Sep 17 00:00:00 2001 From: Edward Tomasz Napierala Date: Wed, 7 Aug 2019 18:14:45 +0000 Subject: Add cdceem(4) driver, for virtual ethernet devices compliant with Communication Device Class Ethernet Emulation Model (CDC EEM). The driver supports both the device, and host side operation; there is a new USB template (#11) for the former. This enables communication with virtual USB NIC provided by iLO 5, as found in new HPE Proliant servers. Reviewed by: hselasky MFC after: 2 weeks Relnotes: yes Sponsored by: Hewlett Packard Enterprise --- share/man/man4/Makefile | 1 + share/man/man4/cdce.4 | 1 + share/man/man4/cdceem.4 | 119 ++++ share/man/man4/ipheth.4 | 1 + share/man/man4/urndis.4 | 1 + share/man/man4/usb_template.4 | 4 +- sys/conf/files | 6 +- sys/dev/usb/net/if_cdceem.c | 870 +++++++++++++++++++++++++++++ sys/dev/usb/template/usb_template.c | 3 + sys/dev/usb/template/usb_template.h | 2 +- sys/dev/usb/template/usb_template_cdceem.c | 263 +++++++++ sys/dev/usb/usb.h | 1 + sys/dev/usb/usb_ioctl.h | 1 + sys/modules/usb/Makefile | 3 +- sys/modules/usb/cdceem/Makefile | 12 + sys/modules/usb/template/Makefile | 3 +- 16 files changed, 1285 insertions(+), 6 deletions(-) create mode 100644 share/man/man4/cdceem.4 create mode 100644 sys/dev/usb/net/if_cdceem.c create mode 100644 sys/dev/usb/template/usb_template_cdceem.c create mode 100644 sys/modules/usb/cdceem/Makefile diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index 0690006fd90e..dcf54f38f63f 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -103,6 +103,7 @@ MAN= aac.4 \ ccr.4 \ cd.4 \ cdce.4 \ + cdceem.4 \ cfi.4 \ cfumass.4 \ ch.4 \ diff --git a/share/man/man4/cdce.4 b/share/man/man4/cdce.4 index 8b45d76836ec..b27d89bc1639 100644 --- a/share/man/man4/cdce.4 +++ b/share/man/man4/cdce.4 @@ -121,6 +121,7 @@ is running low on mbufs. .El .Sh SEE ALSO .Xr arp 4 , +.Xr cdceem 4 , .Xr intro 4 , .Xr ipheth 4 , .Xr netintro 4 , diff --git a/share/man/man4/cdceem.4 b/share/man/man4/cdceem.4 new file mode 100644 index 000000000000..242163c52d84 --- /dev/null +++ b/share/man/man4/cdceem.4 @@ -0,0 +1,119 @@ +.\" Copyright (c) 2019 Edward Tomasz Napierala +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.Dd August 7, 2019 +.Dt CDCEEM 4 +.Os +.Sh NAME +.Nm cdceem +.Nd "USB Communication Device Class Ethernet Emulation Model (CDC EEM) driver" +.Sh SYNOPSIS +To compile this driver into the kernel, +place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device uhci" +.Cd "device ohci" +.Cd "device usb" +.Cd "device miibus" +.Cd "device uether" +.Cd "device cdceem" +.Ed +.Pp +Alternatively, to load the driver as a +module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +if_cdceem_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +driver provides support for USB devices based on the USB Communication +Device Class Ethernet Emulation Model (CDC EEM) specification. +.Pp +The driver works on both host, and device-side; see +.Xr usb_template 4 +for details. +.Pp +The USB device appears as a regular network interface on both sides, +transporting Ethernet frames. +.Pp +For more information on configuring this device, see +.Xr ifconfig 8 . +.Pp +The +.Nm +driver does not support different media types or options. +.Sh SYSCTL VARIABLES +The following variables are available as both +.Xr sysctl 8 +variables and +.Xr loader 8 +tunables: +.Bl -tag -width indent +.It Va hw.usb.cdceem.debug +Verbosity level for log messages from the +.Nm +driver. +Set to 0 to disable logging or 1 to warn about potential problems. +Larger values enable debugging output. +Defaults to 1. +.It Va hw.usb.cdceem.send_echoes +If set to 1, the driver will send an Echo EEM packet when the +interface is brought up. +While responding to Echo is mandatory, some devices cannot handle it. +Only use for debugging. +Defaults to 0. +.It Va hw.usb.cdceem.send_fake_crc +If set to 1, the driver will use 0xdeadbeef as the CRC value +for outgoing Data EEM packets. +Only use for debugging. +Defaults to 0. +.El +.Sh SEE ALSO +.Xr arp 4 , +.Xr cdce 4 , +.Xr intro 4 , +.Xr ipheth 4 , +.Xr netintro 4 , +.Xr urndis 4 , +.Xr usb 4 , +.Xr usb_template 4 , +.Xr ifconfig 8 +.Rs +.%T "Universal Serial Bus Communications Class Subclass Specification for Ethernet Emulation Model Devices" +.%U https://usb.org/sites/default/files/CDC_EEM10.pdf +.Re +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 13.0 . +.Sh AUTHORS +The +.Nm +driver was written by +.An Edward Tomasz Napierala Aq Mt trasz@FreeBSD.org +under sponsorship from Hewlett Packard Enterprise. diff --git a/share/man/man4/ipheth.4 b/share/man/man4/ipheth.4 index bef17139b06c..893ef5bdb0c9 100644 --- a/share/man/man4/ipheth.4 +++ b/share/man/man4/ipheth.4 @@ -159,6 +159,7 @@ The machine should now be connected to the network via USB tethering. .Sh SEE ALSO .Xr arp 4 , .Xr cdce 4 , +.Xr cdceem 4 , .Xr intro 4 , .Xr netintro 4 , .Xr urndis 4 , diff --git a/share/man/man4/urndis.4 b/share/man/man4/urndis.4 index 81314ae2112e..e03d620c9698 100644 --- a/share/man/man4/urndis.4 +++ b/share/man/man4/urndis.4 @@ -79,6 +79,7 @@ functionality of many Android devices. .Sh SEE ALSO .Xr arp 4 , .Xr cdce 4 , +.Xr cdceem 4 , .Xr ipheth 4 , .Xr netintro 4 , .Xr usb 4 , diff --git a/share/man/man4/usb_template.4 b/share/man/man4/usb_template.4 index 4766b35f73d7..4a73389884d5 100644 --- a/share/man/man4/usb_template.4 +++ b/share/man/man4/usb_template.4 @@ -23,7 +23,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 24, 2018 +.Dd August 7, 2019 .Dt USB_TEMPLATE 4 .Os . @@ -88,6 +88,8 @@ Available templates are: .It Dv 8 Ta CDC Ethernet and serial port .It Dv 9 Ta USB MIDI .It Dv 10 Ta CDC Ethernet, serial port, and storage +.It Dv 11 Ta CDC Ethernet Emulation Model, see +.Xr cdceem 4 .El . .Sh SYSCTL VARIABLES diff --git a/sys/conf/files b/sys/conf/files index ca58e4d8b666..af80c49171a6 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -3231,6 +3231,7 @@ dev/usb/net/if_aue.c optional aue dev/usb/net/if_axe.c optional axe dev/usb/net/if_axge.c optional axge dev/usb/net/if_cdce.c optional cdce +dev/usb/net/if_cdceem.c optional cdceem dev/usb/net/if_cue.c optional cue dev/usb/net/if_ipheth.c optional ipheth dev/usb/net/if_kue.c optional kue @@ -3244,8 +3245,8 @@ dev/usb/net/if_usie.c optional usie dev/usb/net/if_urndis.c optional urndis dev/usb/net/ruephy.c optional rue dev/usb/net/usb_ethernet.c optional uether | aue | axe | axge | cdce | \ - cue | ipheth | kue | mos | rue | \ - smsc | udav | ure | urndis | muge + cdceem | cue | ipheth | kue | mos | \ + rue | smsc | udav | ure | urndis | muge dev/usb/net/uhso.c optional uhso # # USB WLAN drivers @@ -3350,6 +3351,7 @@ dev/usb/template/usb_template_phone.c optional usb_template dev/usb/template/usb_template_serialnet.c optional usb_template dev/usb/template/usb_template_midi.c optional usb_template dev/usb/template/usb_template_multi.c optional usb_template +dev/usb/template/usb_template_cdceem.c optional usb_template # # USB video drivers # diff --git a/sys/dev/usb/net/if_cdceem.c b/sys/dev/usb/net/if_cdceem.c new file mode 100644 index 000000000000..e515d65862cc --- /dev/null +++ b/sys/dev/usb/net/if_cdceem.c @@ -0,0 +1,870 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (C) 2012 Ben Gray . + * Copyright (C) 2018 The FreeBSD Foundation. + * Copyright (c) 2019 Edward Tomasz Napierala + * + * This software was developed by Arshan Khanifar + * under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Universal Serial Bus Communications Class Subclass Specification + * for Ethernet Emulation Model Devices: + * + * https://usb.org/sites/default/files/CDC_EEM10.pdf + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include "usbdevs.h" + +#define USB_DEBUG_VAR cdceem_debug +#include +#include +#include +#include "usb_if.h" + +#include + +#define CDCEEM_FRAMES_MAX 1 +#define CDCEEM_ECHO_MAX 1024 + +#define CDCEEM_ECHO_PAYLOAD \ + "ICH DALEKOPIS FALSZUJE GDY PROBY XQV NIE WYTRZYMUJE 1234567890" + +enum { + CDCEEM_BULK_RX, + CDCEEM_BULK_TX, + CDCEEM_N_TRANSFER, +}; + +struct cdceem_softc { + struct usb_ether sc_ue; + struct mtx sc_mtx; + int sc_flags; + struct usb_xfer *sc_xfer[CDCEEM_N_TRANSFER]; + size_t sc_echo_len; + char sc_echo_buffer[CDCEEM_ECHO_MAX]; +}; + +#define CDCEEM_SC_FLAGS_ECHO_RESPONSE_PENDING 0x1 +#define CDCEEM_SC_FLAGS_ECHO_PENDING 0x2 + +static SYSCTL_NODE(_hw_usb, OID_AUTO, cdceem, CTLFLAG_RW, 0, "USB CDC EEM"); +static int cdceem_debug = 1; +SYSCTL_INT(_hw_usb_cdceem, OID_AUTO, debug, CTLFLAG_RWTUN, + &cdceem_debug, 0, "Debug level"); +static int cdceem_send_echoes = 0; +SYSCTL_INT(_hw_usb_cdceem, OID_AUTO, send_echoes, CTLFLAG_RWTUN, + &cdceem_send_echoes, 0, "Send an Echo command"); +static int cdceem_send_fake_crc = 0; +SYSCTL_INT(_hw_usb_cdceem, OID_AUTO, send_fake_crc, CTLFLAG_RWTUN, + &cdceem_send_fake_crc, 0, "Use 0xdeadbeef instead of CRC"); + +#define CDCEEM_DEBUG(S, X, ...) \ + do { \ + if (cdceem_debug > 1) { \ + device_printf(S->sc_ue.ue_dev, "%s: " X "\n", \ + __func__, ## __VA_ARGS__); \ + } \ + } while (0) + +#define CDCEEM_WARN(S, X, ...) \ + do { \ + if (cdceem_debug > 0) { \ + device_printf(S->sc_ue.ue_dev, \ + "WARNING: %s: " X "\n", \ + __func__, ## __VA_ARGS__); \ + } \ + } while (0) + +#define CDCEEM_LOCK(X) mtx_lock(&(X)->sc_mtx) +#define CDCEEM_UNLOCK(X) mtx_unlock(&(X)->sc_mtx) + +#define CDCEEM_TYPE_CMD (0x1 << 15) + +#define CDCEEM_CMD_MASK (0x7 << 11) + +#define CDCEEM_CMD_ECHO (0x0 << 11) +#define CDCEEM_CMD_ECHO_RESPONSE (0x1 << 11) +#define CDCEEM_CMD_SUSPEND_HINT (0x2 << 11) +#define CDCEEM_CMD_RESPONSE_HINT (0x3 << 11) +#define CDCEEM_CMD_RESPONSE_COMPLETE_HINT (0x4 << 11) +#define CDCEEM_CMD_TICKLE (0x5 << 11) + +#define CDCEEM_CMD_RESERVED (0x1 << 14) + +#define CDCEEM_ECHO_LEN_MASK 0x3ff + +#define CDCEEM_DATA_CRC (0x1 << 14) +#define CDCEEM_DATA_LEN_MASK 0x3fff + +static device_probe_t cdceem_probe; +static device_attach_t cdceem_attach; +static device_detach_t cdceem_detach; +static device_suspend_t cdceem_suspend; +static device_resume_t cdceem_resume; + +static usb_callback_t cdceem_bulk_write_callback; +static usb_callback_t cdceem_bulk_read_callback; + +static uether_fn_t cdceem_attach_post; +static uether_fn_t cdceem_init; +static uether_fn_t cdceem_stop; +static uether_fn_t cdceem_start; +static uether_fn_t cdceem_setmulti; +static uether_fn_t cdceem_setpromisc; + +static uint32_t cdceem_m_crc32(struct mbuf *, uint32_t, uint32_t); + +static const struct usb_config cdceem_config[CDCEEM_N_TRANSFER] = { + [CDCEEM_BULK_TX] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_TX, + .bufsize = 16 * (MCLBYTES + 16), + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = cdceem_bulk_write_callback, + .timeout = 10000, /* 10 seconds */ + .usb_mode = USB_MODE_DUAL, + }, + + [CDCEEM_BULK_RX] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_RX, + .bufsize = 20480, /* bytes */ + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = cdceem_bulk_read_callback, + .timeout = 0, /* no timeout */ + .usb_mode = USB_MODE_DUAL, + }, +}; + +static device_method_t cdceem_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, cdceem_probe), + DEVMETHOD(device_attach, cdceem_attach), + DEVMETHOD(device_detach, cdceem_detach), + DEVMETHOD(device_suspend, cdceem_suspend), + DEVMETHOD(device_resume, cdceem_resume), + + DEVMETHOD_END +}; + +static driver_t cdceem_driver = { + .name = "cdceem", + .methods = cdceem_methods, + .size = sizeof(struct cdceem_softc), +}; + +static devclass_t cdceem_devclass; + +static const STRUCT_USB_DUAL_ID cdceem_dual_devs[] = { + {USB_IFACE_CLASS(UICLASS_CDC), + USB_IFACE_SUBCLASS(UISUBCLASS_ETHERNET_EMULATION_MODEL), + 0}, +}; + +DRIVER_MODULE(cdceem, uhub, cdceem_driver, cdceem_devclass, NULL, NULL); +MODULE_VERSION(cdceem, 1); +MODULE_DEPEND(cdceem, uether, 1, 1, 1); +MODULE_DEPEND(cdceem, usb, 1, 1, 1); +MODULE_DEPEND(cdceem, ether, 1, 1, 1); +USB_PNP_DUAL_INFO(cdceem_dual_devs); + +static const struct usb_ether_methods cdceem_ue_methods = { + .ue_attach_post = cdceem_attach_post, + .ue_start = cdceem_start, + .ue_init = cdceem_init, + .ue_stop = cdceem_stop, + .ue_setmulti = cdceem_setmulti, + .ue_setpromisc = cdceem_setpromisc, +}; + +static int +cdceem_probe(device_t dev) +{ + struct usb_attach_arg *uaa; + int error; + + uaa = device_get_ivars(dev); + error = usbd_lookup_id_by_uaa(cdceem_dual_devs, + sizeof(cdceem_dual_devs), uaa); + + return (error); +} + +static void +cdceem_attach_post(struct usb_ether *ue) +{ + + return; +} + +static int +cdceem_attach(device_t dev) +{ + struct cdceem_softc *sc; + struct usb_ether *ue; + struct usb_attach_arg *uaa; + int error; + uint8_t iface_index; + + sc = device_get_softc(dev); + ue = &sc->sc_ue; + uaa = device_get_ivars(dev); + + device_set_usb_desc(dev); + + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + /* Setup the endpoints. */ + iface_index = 0; + error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, + cdceem_config, CDCEEM_N_TRANSFER, sc, &sc->sc_mtx); + if (error != 0) { + CDCEEM_WARN(sc, + "allocating USB transfers failed, error %d", error); + mtx_destroy(&sc->sc_mtx); + return (error); + } + + /* Random MAC address. */ + arc4rand(ue->ue_eaddr, ETHER_ADDR_LEN, 0); + ue->ue_eaddr[0] &= ~0x01; /* unicast */ + ue->ue_eaddr[0] |= 0x02; /* locally administered */ + + ue->ue_sc = sc; + ue->ue_dev = dev; + ue->ue_udev = uaa->device; + ue->ue_mtx = &sc->sc_mtx; + ue->ue_methods = &cdceem_ue_methods; + + error = uether_ifattach(ue); + if (error != 0) { + CDCEEM_WARN(sc, "could not attach interface, error %d", error); + usbd_transfer_unsetup(sc->sc_xfer, CDCEEM_N_TRANSFER); + mtx_destroy(&sc->sc_mtx); + return (error); + } + + return (0); +} + +static int +cdceem_detach(device_t dev) +{ + struct cdceem_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + + /* Stop all USB transfers first. */ + usbd_transfer_unsetup(sc->sc_xfer, CDCEEM_N_TRANSFER); + uether_ifdetach(ue); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +cdceem_handle_cmd(struct usb_xfer *xfer, uint16_t hdr, int *offp) +{ + struct cdceem_softc *sc; + struct usb_page_cache *pc; + int actlen, off; + uint16_t pktlen; + + off = *offp; + sc = usbd_xfer_softc(xfer); + pc = usbd_xfer_get_frame(xfer, 0); + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + if (hdr & CDCEEM_CMD_RESERVED) { + CDCEEM_WARN(sc, "received command header %#x " + "with Reserved bit set; ignoring", hdr); + return; + } + + switch (hdr & CDCEEM_CMD_MASK) { + case CDCEEM_CMD_ECHO: + pktlen = hdr & CDCEEM_ECHO_LEN_MASK; + CDCEEM_DEBUG(sc, "received Echo, length %d", pktlen); + + if (pktlen > (actlen - off)) { + CDCEEM_WARN(sc, + "bad Echo length %d, should be at most %d", + pktlen, actlen - off); + break; + } + + if (pktlen > sizeof(sc->sc_echo_buffer)) { + CDCEEM_WARN(sc, + "Echo length %u too big, must be less than %zd", + pktlen, sizeof(sc->sc_echo_buffer)); + break; + } + + sc->sc_flags |= CDCEEM_SC_FLAGS_ECHO_RESPONSE_PENDING; + sc->sc_echo_len = pktlen; + usbd_copy_out(pc, off, sc->sc_echo_buffer, pktlen); + off += pktlen; + break; + + case CDCEEM_CMD_ECHO_RESPONSE: + pktlen = hdr & CDCEEM_ECHO_LEN_MASK; + CDCEEM_DEBUG(sc, "received Echo Response, length %d", pktlen); + + if (pktlen > (actlen - off)) { + CDCEEM_WARN(sc, + "bad Echo Response length %d, " + "should be at most %d", + pktlen, actlen - off); + break; + } + + if (pktlen != sizeof(CDCEEM_ECHO_PAYLOAD)) { + CDCEEM_WARN(sc, "received Echo Response with bad " + "length %hu, should be %zd", + pktlen, sizeof(CDCEEM_ECHO_PAYLOAD)); + break; + } + + usbd_copy_out(pc, off, sc->sc_echo_buffer, pktlen); + off += pktlen; + + if (memcmp(sc->sc_echo_buffer, CDCEEM_ECHO_PAYLOAD, + sizeof(CDCEEM_ECHO_PAYLOAD)) != 0) { + CDCEEM_WARN(sc, + "received Echo Response payload does not match"); + } else { + CDCEEM_DEBUG(sc, "received Echo Response is valid"); + } + break; + + case CDCEEM_CMD_SUSPEND_HINT: + CDCEEM_DEBUG(sc, "received SuspendHint; ignoring"); + break; + + case CDCEEM_CMD_RESPONSE_HINT: + CDCEEM_DEBUG(sc, "received ResponseHint; ignoring"); + break; + + case CDCEEM_CMD_RESPONSE_COMPLETE_HINT: + CDCEEM_DEBUG(sc, "received ResponseCompleteHint; ignoring"); + break; + + case CDCEEM_CMD_TICKLE: + CDCEEM_DEBUG(sc, "received Tickle; ignoring"); + break; + + default: + CDCEEM_WARN(sc, + "received unknown command %u, header %#x; ignoring", + (hdr & CDCEEM_CMD_MASK >> 11), hdr); + break; + } + + *offp = off; +} + +static void +cdceem_handle_data(struct usb_xfer *xfer, uint16_t hdr, int *offp) +{ + struct cdceem_softc *sc; + struct usb_page_cache *pc; + struct usb_ether *ue; + struct ifnet *ifp; + struct mbuf *m; + int actlen, off; + uint32_t computed_crc, received_crc; + uint16_t pktlen; + + off = *offp; + sc = usbd_xfer_softc(xfer); + pc = usbd_xfer_get_frame(xfer, 0); + ue = &sc->sc_ue; + ifp = uether_getifp(ue); + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + pktlen = hdr & CDCEEM_DATA_LEN_MASK; + CDCEEM_DEBUG(sc, "received Data, CRC %s, length %d", + (hdr & CDCEEM_DATA_CRC) ? "valid" : "absent", + pktlen); + + if (pktlen < ETHER_HDR_LEN) { + CDCEEM_WARN(sc, + "bad ethernet frame length %d, should be at least %d", + pktlen, ETHER_HDR_LEN); + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + return; + } + + if (pktlen > (actlen - off)) { + CDCEEM_WARN(sc, + "bad ethernet frame length %d, should be at most %d", + pktlen, actlen - off); + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + return; + } + + m = uether_newbuf(); + if (m == NULL) { + CDCEEM_WARN(sc, "uether_newbuf() failed"); + if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); + return; + } + + pktlen -= 4; /* Subtract the CRC. */ + usbd_copy_out(pc, off, mtod(m, uint8_t *), pktlen); + off += pktlen; + + usbd_copy_out(pc, off, &received_crc, sizeof(received_crc)); + off += sizeof(received_crc); + + if (hdr & CDCEEM_DATA_CRC) { + computed_crc = cdceem_m_crc32(m, 0, pktlen); + } else { + computed_crc = be32toh(0xdeadbeef); + } + + if (received_crc != computed_crc) { + CDCEEM_WARN(sc, + "received Data packet with wrong CRC %#x, expected %#x", + received_crc, computed_crc); + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + m_freem(m); + return; + } else { + CDCEEM_DEBUG(sc, "received correct CRC %#x", received_crc); + } + + uether_rxmbuf(ue, m, pktlen); + *offp = off; +} + +static void +cdceem_bulk_read_callback(struct usb_xfer *xfer, usb_error_t usb_error) +{ + struct cdceem_softc *sc; + struct usb_page_cache *pc; + int actlen, aframes, off; + uint16_t hdr; + + sc = usbd_xfer_softc(xfer); + usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + CDCEEM_DEBUG(sc, + "received %u bytes in %u frames", actlen, aframes); + + pc = usbd_xfer_get_frame(xfer, 0); + off = 0; + + while (off < actlen) { + usbd_copy_out(pc, off, &hdr, sizeof(hdr)); + CDCEEM_DEBUG(sc, "hdr = %#x", hdr); + off += sizeof(hdr); + + if (hdr == 0) { + CDCEEM_DEBUG(sc, "received Zero Length EEM"); + continue; + } + + hdr = le16toh(hdr); + + if ((hdr & CDCEEM_TYPE_CMD) != 0) { + cdceem_handle_cmd(xfer, hdr, &off); + } else { + cdceem_handle_data(xfer, hdr, &off); + } + + KASSERT(off <= actlen, + ("%s: went past the buffer, off %d, actlen %d", + __func__, off, actlen)); + } + + /* FALLTHROUGH */ + case USB_ST_SETUP: + CDCEEM_DEBUG(sc, "setup"); +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + uether_rxflush(&sc->sc_ue); + break; + + default: + CDCEEM_WARN(sc, "USB_ST_ERROR: %s", usbd_errstr(usb_error)); + + if (usb_error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + break; + } +} + +static void +cdceem_send_echo(struct usb_xfer *xfer, int *offp) +{ + struct cdceem_softc *sc; + struct usb_page_cache *pc; + int maxlen, off; + uint16_t hdr; + + off = *offp; + sc = usbd_xfer_softc(xfer); + pc = usbd_xfer_get_frame(xfer, 0); + maxlen = usbd_xfer_max_len(xfer); + + CDCEEM_DEBUG(sc, "sending Echo, length %zd", + sizeof(CDCEEM_ECHO_PAYLOAD)); + + KASSERT(off + sizeof(hdr) + sizeof(CDCEEM_ECHO_PAYLOAD) < maxlen, + ("%s: out of space; have %d, need %zd", __func__, maxlen, + off + sizeof(hdr) + sizeof(CDCEEM_ECHO_PAYLOAD))); + + hdr = 0; + hdr |= CDCEEM_TYPE_CMD; + hdr |= CDCEEM_CMD_ECHO; + hdr |= sizeof(CDCEEM_ECHO_PAYLOAD); + CDCEEM_DEBUG(sc, "hdr = %#x", hdr); + hdr = htole16(hdr); + + usbd_copy_in(pc, off, &hdr, sizeof(hdr)); + off += sizeof(hdr); + + usbd_copy_in(pc, off, CDCEEM_ECHO_PAYLOAD, + sizeof(CDCEEM_ECHO_PAYLOAD)); + off += sizeof(CDCEEM_ECHO_PAYLOAD); + + sc->sc_flags &= ~CDCEEM_SC_FLAGS_ECHO_PENDING; + + *offp = off; +} + +static void +cdceem_send_echo_response(struct usb_xfer *xfer, int *offp) +{ + struct cdceem_softc *sc; + struct usb_page_cache *pc; + int maxlen, off; + uint16_t hdr; + + off = *offp; + sc = usbd_xfer_softc(xfer); + pc = usbd_xfer_get_frame(xfer, 0); + maxlen = usbd_xfer_max_len(xfer); + + KASSERT(off + sizeof(hdr) + sc->sc_echo_len < maxlen, + ("%s: out of space; have %d, need %zd", __func__, maxlen, + off + sizeof(hdr) + sc->sc_echo_len)); + + CDCEEM_DEBUG(sc, "sending Echo Response, length %zd", sc->sc_echo_len); + + hdr = 0; + hdr |= CDCEEM_TYPE_CMD; + hdr |= CDCEEM_CMD_ECHO_RESPONSE; + hdr |= sc->sc_echo_len; + CDCEEM_DEBUG(sc, "hdr = %#x", hdr); + hdr = htole16(hdr); + + usbd_copy_in(pc, off, &hdr, sizeof(hdr)); + off += sizeof(hdr); + + usbd_copy_in(pc, off, sc->sc_echo_buffer, sc->sc_echo_len); + off += sc->sc_echo_len; + + sc->sc_flags &= ~CDCEEM_SC_FLAGS_ECHO_RESPONSE_PENDING; + sc->sc_echo_len = 0; + + *offp = off; +} + +static void +cdceem_send_data(struct usb_xfer *xfer, int *offp) +{ + struct cdceem_softc *sc; + struct usb_page_cache *pc; + struct ifnet *ifp; + struct mbuf *m; + int maxlen, off; + uint32_t crc; + uint16_t hdr; + + off = *offp; + sc = usbd_xfer_softc(xfer); + pc = usbd_xfer_get_frame(xfer, 0); + ifp = uether_getifp(&sc->sc_ue); + maxlen = usbd_xfer_max_len(xfer); + + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) { + CDCEEM_DEBUG(sc, "no Data packets to send"); + return; + } + + KASSERT((m->m_pkthdr.len & CDCEEM_DATA_LEN_MASK) == m->m_pkthdr.len, + ("%s: packet too long: %d, should be %d\n", __func__, + m->m_pkthdr.len, m->m_pkthdr.len & CDCEEM_DATA_LEN_MASK)); + KASSERT(off + sizeof(hdr) + m->m_pkthdr.len + 4 < maxlen, + ("%s: out of space; have %d, need %zd", __func__, maxlen, + off + sizeof(hdr) + m->m_pkthdr.len + 4)); + + CDCEEM_DEBUG(sc, "sending Data, length %d + 4", m->m_pkthdr.len); + + hdr = 0; + if (!cdceem_send_fake_crc) + hdr |= CDCEEM_DATA_CRC; + hdr |= (m->m_pkthdr.len + 4); /* +4 for CRC */ + CDCEEM_DEBUG(sc, "hdr = %#x", hdr); + hdr = htole16(hdr); + + usbd_copy_in(pc, off, &hdr, sizeof(hdr)); + off += sizeof(hdr); + + usbd_m_copy_in(pc, off, m, 0, m->m_pkthdr.len); + off += m->m_pkthdr.len; + + if (cdceem_send_fake_crc) { + crc = htobe32(0xdeadbeef); + } else { + crc = cdceem_m_crc32(m, 0, m->m_pkthdr.len); + } + CDCEEM_DEBUG(sc, "CRC = %#x", crc); + + usbd_copy_in(pc, off, &crc, sizeof(crc)); + off += sizeof(crc); + + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + + /* + * If there's a BPF listener, bounce a copy of this frame to it. + */ + BPF_MTAP(ifp, m); + m_freem(m); + + *offp = off; +} + +static void +cdceem_bulk_write_callback(struct usb_xfer *xfer, usb_error_t usb_error) +{ + struct cdceem_softc *sc; + struct ifnet *ifp; + int actlen, aframes, maxlen, off; + + sc = usbd_xfer_softc(xfer); + maxlen = usbd_xfer_max_len(xfer); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); + CDCEEM_DEBUG(sc, "transferred %u bytes in %u frames", + actlen, aframes); + + /* FALLTHROUGH */ + case USB_ST_SETUP: + CDCEEM_DEBUG(sc, "setup"); +tr_setup: + + off = 0; + usbd_xfer_set_frame_offset(xfer, 0, 0); + + if (sc->sc_flags & CDCEEM_SC_FLAGS_ECHO_PENDING) { + cdceem_send_echo(xfer, &off); + } else if (sc->sc_flags & CDCEEM_SC_FLAGS_ECHO_RESPONSE_PENDING) { + cdceem_send_echo_response(xfer, &off); + } else { + cdceem_send_data(xfer, &off); + } + + KASSERT(off <= maxlen, + ("%s: went past the buffer, off %d, maxlen %d", + __func__, off, maxlen)); + + if (off > 0) { + CDCEEM_DEBUG(sc, "starting transfer, length %d", off); + usbd_xfer_set_frame_len(xfer, 0, off); + usbd_transfer_submit(xfer); + } else { + CDCEEM_DEBUG(sc, "nothing to transfer"); + } + + break; + + default: + CDCEEM_WARN(sc, "USB_ST_ERROR: %s", usbd_errstr(usb_error)); + + ifp = uether_getifp(&sc->sc_ue); + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + + if (usb_error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + break; + } +} + +static int32_t +cdceem_m_crc32_cb(void *arg, void *src, uint32_t count) +{ + uint32_t *p_crc = arg; + + *p_crc = crc32_raw(src, count, *p_crc); + return (0); +} + +static uint32_t +cdceem_m_crc32(struct mbuf *m, uint32_t src_offset, uint32_t src_len) +{ + uint32_t crc = 0xFFFFFFFF; + int error; + + error = m_apply(m, src_offset, src_len, cdceem_m_crc32_cb, &crc); + return (crc ^ 0xFFFFFFFF); +} + +static void +cdceem_start(struct usb_ether *ue) +{ + struct cdceem_softc *sc; + + sc = uether_getsc(ue); + + /* + * Start the USB transfers, if not already started. + */ + usbd_transfer_start(sc->sc_xfer[CDCEEM_BULK_RX]); + usbd_transfer_start(sc->sc_xfer[CDCEEM_BULK_TX]); +} + +static void +cdceem_init(struct usb_ether *ue) +{ + struct cdceem_softc *sc; + struct ifnet *ifp; + + sc = uether_getsc(ue); + ifp = uether_getifp(ue); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + if (cdceem_send_echoes) + sc->sc_flags = CDCEEM_SC_FLAGS_ECHO_PENDING; + else + sc->sc_flags = 0; + + /* + * Stall data write direction, which depends on USB mode. + * + * Some USB host stacks (e.g. Mac OS X) don't clears stall + * bit as it should, so set it in our host mode only. + */ + if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) + usbd_xfer_set_stall(sc->sc_xfer[CDCEEM_BULK_TX]); + + cdceem_start(ue); +} + +static void +cdceem_stop(struct usb_ether *ue) +{ + struct cdceem_softc *sc; + struct ifnet *ifp; + + sc = uether_getsc(ue); + ifp = uether_getifp(ue); + + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + + usbd_transfer_stop(sc->sc_xfer[CDCEEM_BULK_RX]); + usbd_transfer_stop(sc->sc_xfer[CDCEEM_BULK_TX]); +} + +static void +cdceem_setmulti(struct usb_ether *ue) +{ + /* no-op */ + return; +} + +static void +cdceem_setpromisc(struct usb_ether *ue) +{ + /* no-op */ + return; +} + +static int +cdceem_suspend(device_t dev) +{ + struct cdceem_softc *sc = device_get_softc(dev); + + CDCEEM_DEBUG(sc, "go"); + return (0); +} + +static int +cdceem_resume(device_t dev) +{ + struct cdceem_softc *sc = device_get_softc(dev); + + CDCEEM_DEBUG(sc, "go"); + return (0); +} + diff --git a/sys/dev/usb/template/usb_template.c b/sys/dev/usb/template/usb_template.c index 7377c20ac9f5..db4b76eb4ac6 100644 --- a/sys/dev/usb/template/usb_template.c +++ b/sys/dev/usb/template/usb_template.c @@ -1464,6 +1464,9 @@ usb_temp_setup_by_index(struct usb_device *udev, uint16_t index) case USB_TEMP_MULTI: err = usb_temp_setup(udev, &usb_template_multi); break; + case USB_TEMP_CDCEEM: + err = usb_temp_setup(udev, &usb_template_cdceem); + break; default: return (USB_ERR_INVAL); } diff --git a/sys/dev/usb/template/usb_template.h b/sys/dev/usb/template/usb_template.h index f500e343f676..1bb2424ceef8 100644 --- a/sys/dev/usb/template/usb_template.h +++ b/sys/dev/usb/template/usb_template.h @@ -116,7 +116,7 @@ extern struct usb_temp_device_desc usb_template_phone; extern struct usb_temp_device_desc usb_template_serialnet; extern struct usb_temp_device_desc usb_template_midi; extern struct usb_temp_device_desc usb_template_multi; - +extern struct usb_temp_device_desc usb_template_cdceem; void usb_decode_str_desc(struct usb_string_descriptor *sd, char *buf, size_t buflen); diff --git a/sys/dev/usb/template/usb_template_cdceem.c b/sys/dev/usb/template/usb_template_cdceem.c new file mode 100644 index 000000000000..f97c26b85903 --- /dev/null +++ b/sys/dev/usb/template/usb_template_cdceem.c @@ -0,0 +1,263 @@ +/* $FreeBSD$ */ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2008 Hans Petter Selasky + * Copyright (c) 2018 The FreeBSD Foundation + * Copyright (c) 2019 Edward Tomasz Napierala + * All rights reserved. + * + * Portions of this software were developed by Edward Tomasz Napierala + * under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * This file contains the USB templates for an USB Mass Storage Device. + */ + +#ifdef USB_GLOBAL_INCLUDE_FILE +#include USB_GLOBAL_INCLUDE_FILE +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#endif /* USB_GLOBAL_INCLUDE_FILE */ + +enum { + CDCEEM_LANG_INDEX, + CDCEEM_INTERFACE_INDEX, + CDCEEM_CONFIGURATION_INDEX, + CDCEEM_MANUFACTURER_INDEX, + CDCEEM_PRODUCT_INDEX, + CDCEEM_SERIAL_NUMBER_INDEX, + CDCEEM_MAX_INDEX, +}; + +#define CDCEEM_DEFAULT_VENDOR_ID USB_TEMPLATE_VENDOR +#define CDCEEM_DEFAULT_PRODUCT_ID 0x27df +#define CDCEEM_DEFAULT_INTERFACE "USB CDC EEM Interface" +#define CDCEEM_DEFAULT_CONFIGURATION "Default Config" +#define CDCEEM_DEFAULT_MANUFACTURER USB_TEMPLATE_MANUFACTURER +#define CDCEEM_DEFAULT_PRODUCT "CDC EEM" +#define CDCEEM_DEFAULT_SERIAL_NUMBER "March 2008" + +static struct usb_string_descriptor cdceem_interface; +static struct usb_string_descriptor cdceem_configuration; +static struct usb_string_descriptor cdceem_manufacturer; +static struct usb_string_descriptor cdceem_product; +static struct usb_string_descriptor cdceem_serial_number; + +static struct sysctl_ctx_list cdceem_ctx_list; + +/* prototypes */ + +static usb_temp_get_string_desc_t cdceem_get_string_desc; + +static const struct usb_temp_packet_size bulk_mps = { + .mps[USB_SPEED_FULL] = 64, + .mps[USB_SPEED_HIGH] = 512, +}; + +static const struct usb_temp_endpoint_desc bulk_in_ep = { + .pPacketSize = &bulk_mps, +#ifdef USB_HIP_IN_EP_0 + .bEndpointAddress = USB_HIP_IN_EP_0, +#else + .bEndpointAddress = UE_DIR_IN, +#endif + .bmAttributes = UE_BULK, +}; + +static const struct usb_temp_endpoint_desc bulk_out_ep = { + .pPacketSize = &bulk_mps, +#ifdef USB_HIP_OUT_EP_0 + .bEndpointAddress = USB_HIP_OUT_EP_0, +#else + .bEndpointAddress = UE_DIR_OUT, +#endif + .bmAttributes = UE_BULK, +}; + +static const struct usb_temp_endpoint_desc *cdceem_data_endpoints[] = { + &bulk_in_ep, + &bulk_out_ep, + NULL, +}; + +static const struct usb_temp_interface_desc cdceem_data_interface = { + .ppEndpoints = cdceem_data_endpoints, + .bInterfaceClass = UICLASS_CDC, + .bInterfaceSubClass = UISUBCLASS_ETHERNET_EMULATION_MODEL, + .bInterfaceProtocol = UIPROTO_CDC_EEM, + .iInterface = CDCEEM_INTERFACE_INDEX, +}; + +static const struct usb_temp_interface_desc *cdceem_interfaces[] = { + &cdceem_data_interface, + NULL, +}; + +static const struct usb_temp_config_desc cdceem_config_desc = { + .ppIfaceDesc = cdceem_interfaces, + .bmAttributes = 0, + .bMaxPower = 0, + .iConfiguration = CDCEEM_CONFIGURATION_INDEX, +}; + +static const struct usb_temp_config_desc *cdceem_configs[] = { + &cdceem_config_desc, + NULL, +}; + +struct usb_temp_device_desc usb_template_cdceem = { + .getStringDesc = &cdceem_get_string_desc, + .ppConfigDesc = cdceem_configs, + .idVendor = CDCEEM_DEFAULT_VENDOR_ID, + .idProduct = CDCEEM_DEFAULT_PRODUCT_ID, + .bcdDevice = 0x0100, + .bDeviceClass = UDCLASS_COMM, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .iManufacturer = CDCEEM_MANUFACTURER_INDEX, + .iProduct = CDCEEM_PRODUCT_INDEX, + .iSerialNumber = CDCEEM_SERIAL_NUMBER_INDEX, +}; + +/*------------------------------------------------------------------------* + * cdceem_get_string_desc + * + * Return values: + * NULL: Failure. No such string. + * Else: Success. Pointer to string descriptor is returned. + *------------------------------------------------------------------------*/ +static const void * +cdceem_get_string_desc(uint16_t lang_id, uint8_t string_index) +{ + static const void *ptr[CDCEEM_MAX_INDEX] = { + [CDCEEM_LANG_INDEX] = &usb_string_lang_en, + [CDCEEM_INTERFACE_INDEX] = &cdceem_interface, + [CDCEEM_CONFIGURATION_INDEX] = &cdceem_configuration, + [CDCEEM_MANUFACTURER_INDEX] = &cdceem_manufacturer, + [CDCEEM_PRODUCT_INDEX] = &cdceem_product, + [CDCEEM_SERIAL_NUMBER_INDEX] = &cdceem_serial_number, + }; + + if (string_index == 0) { + return (&usb_string_lang_en); + } + if (lang_id != 0x0409) { + return (NULL); + } + if (string_index < CDCEEM_MAX_INDEX) { + return (ptr[string_index]); + } + return (NULL); +} + +static void +cdceem_init(void *arg __unused) +{ + struct sysctl_oid *parent; + char parent_name[3]; + + usb_make_str_desc(&cdceem_interface, sizeof(cdceem_interface), + CDCEEM_DEFAULT_INTERFACE); + usb_make_str_desc(&cdceem_configuration, sizeof(cdceem_configuration), + CDCEEM_DEFAULT_CONFIGURATION); + usb_make_str_desc(&cdceem_manufacturer, sizeof(cdceem_manufacturer), + CDCEEM_DEFAULT_MANUFACTURER); + usb_make_str_desc(&cdceem_product, sizeof(cdceem_product), + CDCEEM_DEFAULT_PRODUCT); + usb_make_str_desc(&cdceem_serial_number, sizeof(cdceem_serial_number), + CDCEEM_DEFAULT_SERIAL_NUMBER); + + snprintf(parent_name, sizeof(parent_name), "%d", USB_TEMP_CDCEEM); + sysctl_ctx_init(&cdceem_ctx_list); + + parent = SYSCTL_ADD_NODE(&cdceem_ctx_list, + SYSCTL_STATIC_CHILDREN(_hw_usb_templates), OID_AUTO, + parent_name, CTLFLAG_RW, + 0, "USB CDC EEM device side template"); + SYSCTL_ADD_U16(&cdceem_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, + "vendor_id", CTLFLAG_RWTUN, + &usb_template_cdceem.idVendor, 1, "Vendor identifier"); + SYSCTL_ADD_U16(&cdceem_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, + "product_id", CTLFLAG_RWTUN, + &usb_template_cdceem.idProduct, 1, "Product identifier"); +#if 0 + SYSCTL_ADD_PROC(&cdceem_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, + "interface", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, + &cdceem_interface, sizeof(cdceem_interface), usb_temp_sysctl, + "A", "Interface string"); + SYSCTL_ADD_PROC(&cdceem_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, + "configuration", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, + &cdceem_configuration, sizeof(cdceem_configuration), usb_temp_sysctl, + "A", "Configuration string"); +#endif + SYSCTL_ADD_PROC(&cdceem_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, + "manufacturer", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, + &cdceem_manufacturer, sizeof(cdceem_manufacturer), usb_temp_sysctl, + "A", "Manufacturer string"); + SYSCTL_ADD_PROC(&cdceem_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, + "product", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, + &cdceem_product, sizeof(cdceem_product), usb_temp_sysctl, + "A", "Product string"); + SYSCTL_ADD_PROC(&cdceem_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, + "serial_number", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, + &cdceem_serial_number, sizeof(cdceem_serial_number), usb_temp_sysctl, + "A", "Serial number string"); +} + +static void +cdceem_uninit(void *arg __unused) +{ + + sysctl_ctx_free(&cdceem_ctx_list); +} + +SYSINIT(cdceem_init, SI_SUB_LOCK, SI_ORDER_FIRST, cdceem_init, NULL); +SYSUNINIT(cdceem_uninit, SI_SUB_LOCK, SI_ORDER_FIRST, cdceem_uninit, NULL); diff --git a/sys/dev/usb/usb.h b/sys/dev/usb/usb.h index 0e549ae4b05e..7dd6a2f5165c 100644 --- a/sys/dev/usb/usb.h +++ b/sys/dev/usb/usb.h @@ -444,6 +444,7 @@ typedef struct usb_interface_assoc_descriptor usb_interface_assoc_descriptor_t; #define UIPROTO_CDC_NONE 0 #define UIPROTO_CDC_AT 1 +#define UIPROTO_CDC_EEM 7 #define UICLASS_HID 0x03 #define UISUBCLASS_BOOT 1 diff --git a/sys/dev/usb/usb_ioctl.h b/sys/dev/usb/usb_ioctl.h index fcd31e31989c..e7e63fb9a895 100644 --- a/sys/dev/usb/usb_ioctl.h +++ b/sys/dev/usb/usb_ioctl.h @@ -70,6 +70,7 @@ enum { USB_TEMP_SERIALNET, /* USB CDC Ethernet and Modem */ USB_TEMP_MIDI, /* USB MIDI */ USB_TEMP_MULTI, /* USB Ethernet, serial, and storage */ + USB_TEMP_CDCEEM, /* USB Ethernet Emulation Model */ USB_TEMP_MAX, }; diff --git a/sys/modules/usb/Makefile b/sys/modules/usb/Makefile index a64fb772cf3c..59ee709edd3f 100644 --- a/sys/modules/usb/Makefile +++ b/sys/modules/usb/Makefile @@ -51,7 +51,8 @@ SUBDIR += atp cfumass uhid uhid_snes ukbd ums udbp ufm uep wmt wsp ugold uled SUBDIR += ucom u3g uark ubsa ubser uchcom ucycom ufoma uftdi ugensa uipaq ulpt \ umct umcs umodem umoscom uplcom uslcom uvisor uvscom SUBDIR += udl -SUBDIR += uether aue axe axge cdce cue ${_kue} mos rue smsc udav uhso ipheth +SUBDIR += uether aue axe axge cdce cdceem cue ${_kue} mos rue smsc udav uhso \ + ipheth SUBDIR += muge SUBDIR += ure urndis SUBDIR += usfs umass urio diff --git a/sys/modules/usb/cdceem/Makefile b/sys/modules/usb/cdceem/Makefile new file mode 100644 index 000000000000..957fae39fbb3 --- /dev/null +++ b/sys/modules/usb/cdceem/Makefile @@ -0,0 +1,12 @@ +# +# $FreeBSD$ +# $FreeBSD$ + +.PATH: ${SRCTOP}/sys/dev/usb/net + +KMOD= if_cdceem +SRCS= opt_bus.h opt_usb.h device_if.h bus_if.h usb_if.h usbdevs.h \ + miibus_if.h opt_inet.h \ + if_cdceem.c + +.include diff --git a/sys/modules/usb/template/Makefile b/sys/modules/usb/template/Makefile index 8b09974fe044..ee44fb129f2f 100644 --- a/sys/modules/usb/template/Makefile +++ b/sys/modules/usb/template/Makefile @@ -42,6 +42,7 @@ SRCS= opt_bus.h opt_usb.h device_if.h bus_if.h usb_if.h vnode_if.h usbdevs.h \ usb_template_phone.c \ usb_template_serialnet.c \ usb_template_midi.c \ - usb_template_multi.c + usb_template_multi.c \ + usb_template_cdceem.c .include -- cgit v1.2.3