aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn-Mark Gurney <jmg@FreeBSD.org>2021-06-26 00:47:02 +0000
committerWojciech Macek <wma@FreeBSD.org>2021-09-28 07:03:48 +0000
commit973fb85188eaf6ee9f8a2d0265c0d666ce7f19e3 (patch)
tree7709a87e135bb9cf4e5e4e09523c24165c3cbaae
parentfea962297f1f26d49885845ad36590ca6bcfbe3e (diff)
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 (cherry picked from commit b43d600c839a9a4d66139c93506e26128370ed7c)
-rw-r--r--sys/dev/usb/net/if_cdce.c152
-rw-r--r--sys/dev/usb/net/if_cdcereg.h3
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,
@@ -561,6 +568,36 @@ cdce_attach_post(struct usb_ether *ue)
}
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)
{
struct cdce_softc *sc = device_get_softc(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];