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
This commit is contained in:
Edward Tomasz Napierala 2019-08-07 18:14:45 +00:00
parent c5fb9d20cd
commit 63722e5212
16 changed files with 1285 additions and 6 deletions

View File

@ -103,6 +103,7 @@ MAN= aac.4 \
ccr.4 \
cd.4 \
cdce.4 \
cdceem.4 \
cfi.4 \
cfumass.4 \
ch.4 \

View File

@ -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 ,

119
share/man/man4/cdceem.4 Normal file
View File

@ -0,0 +1,119 @@
.\" Copyright (c) 2019 Edward Tomasz Napierala <trasz@FreeBSD.org>
.\"
.\" 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.

View File

@ -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 ,

View File

@ -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 ,

View File

@ -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

View File

@ -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
#

870
sys/dev/usb/net/if_cdceem.c Normal file
View File

@ -0,0 +1,870 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (C) 2012 Ben Gray <bgray@freebsd.org>.
* Copyright (C) 2018 The FreeBSD Foundation.
* Copyright (c) 2019 Edward Tomasz Napierala <trasz@FreeBSD.org>
*
* This software was developed by Arshan Khanifar <arshankhanifar@gmail.com>
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/gsb_crc32.h>
#include <sys/eventhandler.h>
#include <sys/stdint.h>
#include <sys/stddef.h>
#include <sys/queue.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#include <sys/sysctl.h>
#include <sys/sx.h>
#include <sys/unistd.h>
#include <sys/callout.h>
#include <sys/malloc.h>
#include <sys/priv.h>
#include <net/if.h>
#include <net/if_var.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include <dev/usb/usb_cdc.h>
#include "usbdevs.h"
#define USB_DEBUG_VAR cdceem_debug
#include <dev/usb/usb_debug.h>
#include <dev/usb/usb_process.h>
#include <dev/usb/usb_msctest.h>
#include "usb_if.h"
#include <dev/usb/net/usb_ethernet.h>
#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);
}

View File

@ -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);
}

View File

@ -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);

View File

@ -0,0 +1,263 @@
/* $FreeBSD$ */
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2008 Hans Petter Selasky <hselasky@FreeBSD.org>
* Copyright (c) 2018 The FreeBSD Foundation
* Copyright (c) 2019 Edward Tomasz Napierala <trasz@FreeBSD.org>
* 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 <sys/stdint.h>
#include <sys/stddef.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#include <sys/sysctl.h>
#include <sys/sx.h>
#include <sys/unistd.h>
#include <sys/callout.h>
#include <sys/malloc.h>
#include <sys/priv.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usb_core.h>
#include <dev/usb/usb_ioctl.h>
#include <dev/usb/usb_util.h>
#include <dev/usb/template/usb_template.h>
#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);

View File

@ -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

View File

@ -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,
};

View File

@ -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

View File

@ -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 <bsd.kmod.mk>

View File

@ -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 <bsd.kmod.mk>