git: b43d600c839a - main - Add support for link status, media and VLAN MTU (if supported) to if_cdce...

John-Mark Gurney jmg at FreeBSD.org
Sat Jun 26 00:57:33 UTC 2021


The branch main has been updated by jmg:

URL: https://cgit.FreeBSD.org/src/commit/?id=b43d600c839a9a4d66139c93506e26128370ed7c

commit b43d600c839a9a4d66139c93506e26128370ed7c
Author:     John-Mark Gurney <jmg at FreeBSD.org>
AuthorDate: 2021-06-26 00:47:02 +0000
Commit:     John-Mark Gurney <jmg at FreeBSD.org>
CommitDate: 2021-06-26 00:57:04 +0000

    Add support for link status, media and VLAN MTU (if supported) to if_cdce...
    
    This makes it more usable in that dhclient will autolaunch from devd
    now when cdce devices are plugged in..  It also sets the baudrate, but
    this isn't exported via tools, and CDCE doesn't have a good way to
    specify the media type, so there isn't a good way to tell userland
    what the speed is currently...
    
    Reviewed by:            hps
    Relnotes:               yes
    Differential Revision:  https://reviews.freebsd.org/D30625
---
 sys/dev/usb/net/if_cdce.c    | 152 +++++++++++++++++++++++++++++++++++++++++--
 sys/dev/usb/net/if_cdcereg.h |   3 +
 2 files changed, 150 insertions(+), 5 deletions(-)

diff --git a/sys/dev/usb/net/if_cdce.c b/sys/dev/usb/net/if_cdce.c
index 96df8b73f8ce..64c31896d417 100644
--- a/sys/dev/usb/net/if_cdce.c
+++ b/sys/dev/usb/net/if_cdce.c
@@ -111,6 +111,10 @@ static uether_fn_t cdce_stop;
 static uether_fn_t cdce_start;
 static uether_fn_t cdce_setmulti;
 static uether_fn_t cdce_setpromisc;
+static int cdce_attach_post_sub(struct usb_ether *);
+static int cdce_ioctl(struct ifnet *, u_long, caddr_t);
+static int cdce_media_change_cb(struct ifnet *);
+static void cdce_media_status_cb(struct ifnet *, struct ifmediareq *);
 
 static uint32_t	cdce_m_crc32(struct mbuf *, uint32_t, uint32_t);
 
@@ -124,6 +128,8 @@ SYSCTL_INT(_hw_usb_cdce, OID_AUTO, debug, CTLFLAG_RWTUN, &cdce_debug, 0,
     "Debug level");
 SYSCTL_INT(_hw_usb_cdce, OID_AUTO, interval, CTLFLAG_RWTUN, &cdce_tx_interval, 0,
     "NCM transmit interval in ms");
+#else
+#define cdce_debug 0
 #endif
 
 static const struct usb_config cdce_config[CDCE_N_TRANSFER] = {
@@ -307,6 +313,7 @@ USB_PNP_DUAL_INFO(cdce_dual_devs);
 
 static const struct usb_ether_methods cdce_ue_methods = {
 	.ue_attach_post = cdce_attach_post,
+	.ue_attach_post_sub = cdce_attach_post_sub,
 	.ue_start = cdce_start,
 	.ue_init = cdce_init,
 	.ue_stop = cdce_stop,
@@ -560,6 +567,36 @@ cdce_attach_post(struct usb_ether *ue)
 	return;
 }
 
+static int
+cdce_attach_post_sub(struct usb_ether *ue)
+{
+	struct cdce_softc *sc = uether_getsc(ue);
+	struct ifnet *ifp = uether_getifp(ue);
+
+	/* mostly copied from usb_ethernet.c */
+	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+	ifp->if_start = uether_start;
+	ifp->if_ioctl = cdce_ioctl;
+	ifp->if_init = uether_init;
+	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
+	ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
+	IFQ_SET_READY(&ifp->if_snd);
+
+	if ((sc->sc_flags & CDCE_FLAG_VLAN) == CDCE_FLAG_VLAN)
+		if_setcapabilitiesbit(ifp, IFCAP_VLAN_MTU, 0);
+
+	if_setcapabilitiesbit(ifp, IFCAP_LINKSTATE, 0);
+	if_setcapenable(ifp, if_getcapabilities(ifp));
+
+	ifmedia_init(&sc->sc_media, IFM_IMASK, cdce_media_change_cb,
+	    cdce_media_status_cb);
+	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
+	ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
+	sc->sc_media.ifm_media = sc->sc_media.ifm_cur->ifm_media;
+
+	return 0;
+}
+
 static int
 cdce_attach(device_t dev)
 {
@@ -676,6 +713,14 @@ alloc_transfers:
 		    eaddr_str, sizeof(eaddr_str), ued->iMacAddress);
 	}
 
+	/*
+	 * ECM 1.2 doesn't say it excludes the CRC, but states that it's
+	 * normally 1514, which excludes the CRC.
+	 */
+	DPRINTF("max segsize: %d\n", UGETW(ued->wMaxSegmentSize));
+	if (UGETW(ued->wMaxSegmentSize) >= (ETHER_MAX_LEN - ETHER_CRC_LEN + ETHER_VLAN_ENCAP_LEN))
+		sc->sc_flags |= CDCE_FLAG_VLAN;
+
 	if (error) {
 		/* fake MAC address */
 
@@ -742,6 +787,8 @@ cdce_detach(device_t dev)
 	uether_ifdetach(ue);
 	mtx_destroy(&sc->sc_mtx);
 
+	ifmedia_removeall(&sc->sc_media);
+
 	return (0);
 }
 
@@ -757,6 +804,29 @@ cdce_start(struct usb_ether *ue)
 	usbd_transfer_start(sc->sc_xfer[CDCE_BULK_RX]);
 }
 
+static int
+cdce_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+	struct usb_ether *ue = ifp->if_softc;
+	struct cdce_softc *sc = uether_getsc(ue);
+	struct ifreq *ifr = (struct ifreq *)data;
+	int error;
+
+	error = 0;
+
+	switch(command) {
+	case SIOCGIFMEDIA:
+	case SIOCSIFMEDIA:
+		error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, command);
+		break;
+	default:
+		error = uether_ioctl(ifp, command, data);
+		break;
+	}
+
+	return (error);
+}
+
 static void
 cdce_free_queue(struct mbuf **ppm, uint8_t n)
 {
@@ -769,6 +839,26 @@ cdce_free_queue(struct mbuf **ppm, uint8_t n)
 	}
 }
 
+static int
+cdce_media_change_cb(struct ifnet *ifp)
+{
+
+	return (EOPNOTSUPP);
+}
+
+static void
+cdce_media_status_cb(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+
+	if ((if_getflags(ifp) & IFF_UP) == 0)
+		return;
+
+	ifmr->ifm_active = IFM_ETHER;
+	ifmr->ifm_status = IFM_AVALID;
+	ifmr->ifm_status |=
+	    ifp->if_link_state == LINK_STATE_UP ? IFM_ACTIVE : 0;
+}
+
 static void
 cdce_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
 {
@@ -1043,17 +1133,69 @@ tr_stall:
 static void
 cdce_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
 {
-	struct cdce_softc *sc = usbd_xfer_softc(xfer);
-	int actlen;
+	u_char buf[CDCE_IND_SIZE_MAX];
+	struct usb_cdc_notification ucn;
+	struct cdce_softc *sc;
+	struct ifnet *ifp;
+	struct usb_page_cache *pc;
+	int off, actlen;
+	uint32_t downrate, uprate;
+
+	sc = usbd_xfer_softc(xfer);
+	ifp = uether_getifp(&sc->sc_ue);
+	pc = usbd_xfer_get_frame(xfer, 0);
 
 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
 
 	switch (USB_GET_STATE(xfer)) {
 	case USB_ST_TRANSFERRED:
+		if (USB_DEBUG_VAR)
+			usbd_copy_out(pc, 0, buf, MIN(actlen, sizeof buf));
+		DPRINTF("Received %d bytes: %*D\n", actlen,
+		    (int)MIN(actlen, sizeof buf), buf, "");
+
+		off = 0;
+		while (actlen - off >= UCDC_NOTIFICATION_LENGTH) {
+			usbd_copy_out(pc, off, &ucn, UCDC_NOTIFICATION_LENGTH);
+
+			do {
+				if (ucn.bmRequestType != 0xa1)
+					break;
+
+				switch (ucn.bNotification) {
+				case UCDC_N_NETWORK_CONNECTION:
+					DPRINTF("changing link state: %d\n",
+					    UGETW(ucn.wValue));
+					if_link_state_change(ifp,
+					    UGETW(ucn.wValue) ? LINK_STATE_UP :
+					    LINK_STATE_DOWN);
+					break;
+
+				case UCDC_N_CONNECTION_SPEED_CHANGE:
+					if (UGETW(ucn.wLength) != 8)
+						break;
+
+					usbd_copy_out(pc, off +
+					    UCDC_NOTIFICATION_LENGTH,
+					    &ucn.data, UGETW(ucn.wLength));
+					downrate = UGETDW(ucn.data);
+					uprate = UGETDW(ucn.data);
+					if (downrate != uprate)
+						break;
+
+					/* set rate */
+					DPRINTF("changing baudrate: %u\n",
+					    downrate);
+					if_setbaudrate(ifp, downrate);
+					break;
+
+				default:
+					break;
+				}
+			} while (0);
 
-		DPRINTF("Received %d bytes\n", actlen);
-
-		/* TODO: decode some indications */
+			off += UCDC_NOTIFICATION_LENGTH + UGETW(ucn.wLength);
+		}
 
 		/* FALLTHROUGH */
 	case USB_ST_SETUP:
diff --git a/sys/dev/usb/net/if_cdcereg.h b/sys/dev/usb/net/if_cdcereg.h
index 26d037c593b8..724aa0b9b017 100644
--- a/sys/dev/usb/net/if_cdcereg.h
+++ b/sys/dev/usb/net/if_cdcereg.h
@@ -88,10 +88,13 @@ struct cdce_softc {
 	struct mbuf		*sc_rx_buf[CDCE_FRAMES_MAX];
 	struct mbuf		*sc_tx_buf[CDCE_FRAMES_MAX];
 
+	struct ifmedia		sc_media;
+
 	int 			sc_flags;
 #define	CDCE_FLAG_ZAURUS	0x0001
 #define	CDCE_FLAG_NO_UNION	0x0002
 #define	CDCE_FLAG_RX_DATA	0x0010
+#define	CDCE_FLAG_VLAN		0x0020
 
 	uint8_t sc_eaddr_str_index;
 	uint8_t	sc_ifaces_index[2];


More information about the dev-commits-src-all mailing list