/* $OpenBSD: if_uaq.c,v 1.5 2022/09/01 17:07:09 mlarkin Exp $ */ /*- * Copyright (c) 2021 Jonathan Matthew * All rights reserved. * * 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. */ #include "bpfilter.h" #include "vlan.h" #include #include #include #include #include #include #include #include #include #include #include #if NBPFILTER > 0 #include #endif #include #include #include #include #include #include #include #ifdef UAQ_DEBUG #define DPRINTF(x) do { if (uaqdebug) printf x; } while (0) #define DPRINTFN(n,x) do { if (uaqdebug >= (n)) printf x; } while (0) int uaqdebug = 0; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif #define UAQ_ENDPT_RX 0 #define UAQ_ENDPT_TX 1 #define UAQ_ENDPT_INTR 2 #define UAQ_ENDPT_MAX 3 #define UAQ_TX_LIST_CNT 1 #define UAQ_RX_LIST_CNT 1 #define UAQ_TX_BUF_ALIGN 8 #define UAQ_RX_BUF_ALIGN 8 #define UAQ_TX_BUFSZ 16384 #define UAQ_RX_BUFSZ (62 * 1024) #define UAQ_CTL_READ 1 #define UAQ_CTL_WRITE 2 #define UAQ_MCAST_FILTER_SIZE 8 /* control commands */ #define UAQ_CMD_ACCESS_MAC 0x01 #define UAQ_CMD_FLASH_PARAM 0x20 #define UAQ_CMD_PHY_POWER 0x31 #define UAQ_CMD_WOL_CFG 0x60 #define UAQ_CMD_PHY_OPS 0x61 /* SFR registers */ #define UAQ_SFR_GENERAL_STATUS 0x03 #define UAQ_SFR_CHIP_STATUS 0x05 #define UAQ_SFR_RX_CTL 0x0B #define UAQ_SFR_RX_CTL_STOP 0x0000 #define UAQ_SFR_RX_CTL_PRO 0x0001 #define UAQ_SFR_RX_CTL_AMALL 0x0002 #define UAQ_SFR_RX_CTL_AB 0x0008 #define UAQ_SFR_RX_CTL_AM 0x0010 #define UAQ_SFR_RX_CTL_START 0x0080 #define UAQ_SFR_RX_CTL_IPE 0x0200 #define UAQ_SFR_IPG_0 0x0D #define UAQ_SFR_NODE_ID 0x10 #define UAQ_SFR_MCAST_FILTER 0x16 #define UAQ_SFR_MEDIUM_STATUS_MODE 0x22 #define UAQ_SFR_MEDIUM_XGMIIMODE 0x0001 #define UAQ_SFR_MEDIUM_FULL_DUPLEX 0x0002 #define UAQ_SFR_MEDIUM_RXFLOW_CTRLEN 0x0010 #define UAQ_SFR_MEDIUM_TXFLOW_CTRLEN 0x0020 #define UAQ_SFR_MEDIUM_JUMBO_EN 0x0040 #define UAQ_SFR_MEDIUM_RECEIVE_EN 0x0100 #define UAQ_SFR_MONITOR_MODE 0x24 #define UAQ_SFR_MONITOR_MODE_EPHYRW 0x01 #define UAQ_SFR_MONITOR_MODE_RWLC 0x02 #define UAQ_SFR_MONITOR_MODE_RWMP 0x04 #define UAQ_SFR_MONITOR_MODE_RWWF 0x08 #define UAQ_SFR_MONITOR_MODE_RW_FLAG 0x10 #define UAQ_SFR_MONITOR_MODE_PMEPOL 0x20 #define UAQ_SFR_MONITOR_MODE_PMETYPE 0x40 #define UAQ_SFR_RX_BULKIN_QCTRL 0x2E #define UAQ_SFR_RXCOE_CTL 0x34 #define UAQ_SFR_RXCOE_IP 0x01 #define UAQ_SFR_RXCOE_TCP 0x02 #define UAQ_SFR_RXCOE_UDP 0x04 #define UAQ_SFR_RXCOE_ICMP 0x08 #define UAQ_SFR_RXCOE_IGMP 0x10 #define UAQ_SFR_RXCOE_TCPV6 0x20 #define UAQ_SFR_RXCOE_UDPV6 0x40 #define UAQ_SFR_RXCOE_ICMV6 0x80 #define UAQ_SFR_TXCOE_CTL 0x35 #define UAQ_SFR_TXCOE_IP 0x01 #define UAQ_SFR_TXCOE_TCP 0x02 #define UAQ_SFR_TXCOE_UDP 0x04 #define UAQ_SFR_TXCOE_ICMP 0x08 #define UAQ_SFR_TXCOE_IGMP 0x10 #define UAQ_SFR_TXCOE_TCPV6 0x20 #define UAQ_SFR_TXCOE_UDPV6 0x40 #define UAQ_SFR_TXCOE_ICMV6 0x80 #define UAQ_SFR_BM_INT_MASK 0x41 #define UAQ_SFR_BMRX_DMA_CTRL 0x43 #define UAQ_SFR_BMRX_DMA_EN 0x80 #define UAQ_SFR_BMTX_DMA_CTRL 0x46 #define UAQ_SFR_PAUSE_WATERLVL_LOW 0x54 #define UAQ_SFR_ARC_CTRL 0x9E #define UAQ_SFR_SWP_CTRL 0xB1 #define UAQ_SFR_TX_PAUSE_RESEND_T 0xB2 #define UAQ_SFR_ETH_MAC_PATH 0xB7 #define UAQ_SFR_RX_PATH_READY 0x01 #define UAQ_SFR_BULK_OUT_CTRL 0xB9 #define UAQ_SFR_BULK_OUT_FLUSH_EN 0x01 #define UAQ_SFR_BULK_OUT_EFF_EN 0x02 #define UAQ_FW_VER_MAJOR 0xDA #define UAQ_FW_VER_MINOR 0xDB #define UAQ_FW_VER_REV 0xDC /* phy ops */ #define UAQ_PHY_ADV_100M (1 << 0) #define UAQ_PHY_ADV_1G (1 << 1) #define UAQ_PHY_ADV_2_5G (1 << 2) #define UAQ_PHY_ADV_5G (1 << 3) #define UAQ_PHY_ADV_MASK 0x0F #define UAQ_PHY_PAUSE (1 << 16) #define UAQ_PHY_ASYM_PAUSE (1 << 17) #define UAQ_PHY_LOW_POWER (1 << 18) #define UAQ_PHY_POWER_EN (1 << 19) #define UAQ_PHY_WOL (1 << 20) #define UAQ_PHY_DOWNSHIFT (1 << 21) #define UAQ_PHY_DSH_RETRY_SHIFT 0x18 #define UAQ_PHY_DSH_RETRY_MASK 0xF000000 /* status */ #define UAQ_STATUS_LINK 0x8000 #define UAQ_STATUS_SPEED_MASK 0x7F00 #define UAQ_STATUS_SPEED_SHIFT 8 #define UAQ_STATUS_SPEED_5G 0x000F #define UAQ_STATUS_SPEED_2_5G 0x0010 #define UAQ_STATUS_SPEED_1G 0x0011 #define UAQ_STATUS_SPEED_100M 0x0013 /* rx descriptor */ #define UAQ_RX_HDR_COUNT_MASK 0x1FFF #define UAQ_RX_HDR_OFFSET_MASK 0xFFFFE000 #define UAQ_RX_HDR_OFFSET_SHIFT 13 /* rx packet descriptor */ #define UAQ_RX_PKT_L4_ERR 0x01 #define UAQ_RX_PKT_L3_ERR 0x02 #define UAQ_RX_PKT_L4_MASK 0x1C #define UAQ_RX_PKT_L4_UDP 0x04 #define UAQ_RX_PKT_L4_TCP 0x10 #define UAQ_RX_PKT_L3_MASK 0x60 #define UAQ_RX_PKT_L3_IP 0x20 #define UAQ_RX_PKT_L3_IP6 0x40 #define UAQ_RX_PKT_VLAN 0x400 #define UAQ_RX_PKT_RX_OK 0x800 #define UAQ_RX_PKT_DROP 0x80000000 #define UAQ_RX_PKT_LEN_MASK 0x7FFF0000 #define UAQ_RX_PKT_LEN_SHIFT 16 #define UAQ_RX_PKT_VLAN_SHIFT 32 /* tx packet descriptor */ #define UAQ_TX_PKT_LEN_MASK 0x1FFFFF #define UAQ_TX_PKT_DROP_PADD (1 << 28) #define UAQ_TX_PKT_VLAN (1 << 29) #define UAQ_TX_PKT_VLAN_MASK 0xFFFF #define UAQ_TX_PKT_VLAN_SHIFT 0x30 struct uaq_chain { struct uaq_softc *uc_sc; struct usbd_xfer *uc_xfer; char *uc_buf; uint32_t uc_cnt; uint32_t uc_buflen; uint32_t uc_bufmax; SLIST_ENTRY(uaq_chain) uc_list; uint8_t uc_idx; }; struct uaq_cdata { struct uaq_chain uaq_rx_chain[UAQ_RX_LIST_CNT]; struct uaq_chain uaq_tx_chain[UAQ_TX_LIST_CNT]; SLIST_HEAD(uaq_list_head, uaq_chain) uaq_tx_free; }; struct uaq_softc { struct device sc_dev; struct usbd_device *sc_udev; struct usbd_interface *sc_iface; struct usb_task sc_link_task; struct timeval sc_rx_notice; int sc_ed[UAQ_ENDPT_MAX]; struct usbd_pipe *sc_ep[UAQ_ENDPT_MAX]; int sc_out_frame_size; struct arpcom sc_ac; struct ifmedia sc_ifmedia; struct uaq_cdata sc_cdata; uint64_t sc_link_status; int sc_link_speed; uint32_t sc_phy_cfg; uint16_t sc_rxctl; }; const struct usb_devno uaq_devs[] = { { USB_VENDOR_AQUANTIA, USB_PRODUCT_AQUANTIA_AQC111 }, { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_ASIX111 }, { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_ASIX112 }, { USB_VENDOR_TRENDNET, USB_PRODUCT_TRENDNET_TUCET5G }, { USB_VENDOR_QNAP, USB_PRODUCT_QNAP_UC5G1T }, }; int uaq_match(struct device *, void *, void *); void uaq_attach(struct device *, struct device *, void *); int uaq_detach(struct device *, int); int uaq_ctl(struct uaq_softc *, uint8_t, uint8_t, uint16_t, uint16_t, void *, int); int uaq_read_mem(struct uaq_softc *, uint8_t, uint16_t, uint16_t, void *, int); int uaq_write_mem(struct uaq_softc *, uint8_t, uint16_t, uint16_t, void *, int); uint8_t uaq_read_1(struct uaq_softc *, uint8_t, uint16_t, uint16_t); uint16_t uaq_read_2(struct uaq_softc *, uint8_t, uint16_t, uint16_t); uint32_t uaq_read_4(struct uaq_softc *, uint8_t, uint16_t, uint16_t); int uaq_write_1(struct uaq_softc *, uint8_t, uint16_t, uint16_t, uint32_t); int uaq_write_2(struct uaq_softc *, uint8_t, uint16_t, uint16_t, uint32_t); int uaq_write_4(struct uaq_softc *, uint8_t, uint16_t, uint16_t, uint32_t); int uaq_ifmedia_upd(struct ifnet *); void uaq_ifmedia_sts(struct ifnet *, struct ifmediareq *); void uaq_add_media_types(struct uaq_softc *); void uaq_iff(struct uaq_softc *); void uaq_init(void *); int uaq_ioctl(struct ifnet *, u_long, caddr_t); int uaq_xfer_list_init(struct uaq_softc *, struct uaq_chain *, uint32_t, int); void uaq_xfer_list_free(struct uaq_softc *, struct uaq_chain *, int); void uaq_stop(struct uaq_softc *); void uaq_link(struct uaq_softc *); void uaq_intr(struct usbd_xfer *, void *, usbd_status); void uaq_start(struct ifnet *); void uaq_rxeof(struct usbd_xfer *, void *, usbd_status); void uaq_txeof(struct usbd_xfer *, void *, usbd_status); void uaq_watchdog(struct ifnet *); void uaq_reset(struct uaq_softc *); int uaq_encap_txpkt(struct uaq_softc *, struct mbuf *, char *, uint32_t); int uaq_encap_xfer(struct uaq_softc *, struct uaq_chain *); struct cfdriver uaq_cd = { NULL, "uaq", DV_IFNET }; const struct cfattach uaq_ca = { sizeof(struct uaq_softc), uaq_match, uaq_attach, uaq_detach }; int uaq_ctl(struct uaq_softc *sc, uint8_t rw, uint8_t cmd, uint16_t val, uint16_t index, void *buf, int len) { usb_device_request_t req; usbd_status err; if (usbd_is_dying(sc->sc_udev)) return 0; if (rw == UAQ_CTL_WRITE) req.bmRequestType = UT_WRITE_VENDOR_DEVICE; else req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = cmd; USETW(req.wValue, val); USETW(req.wIndex, index); USETW(req.wLength, len); DPRINTFN(5, ("uaq_ctl: rw %d, val 0x%04hx, index 0x%04hx, len %d\n", rw, val, index, len)); err = usbd_do_request(sc->sc_udev, &req, buf); if (err) { DPRINTF(("uaq_ctl: error %d\n", err)); return -1; } return 0; } int uaq_read_mem(struct uaq_softc *sc, uint8_t cmd, uint16_t addr, uint16_t index, void *buf, int len) { return (uaq_ctl(sc, UAQ_CTL_READ, cmd, addr, index, buf, len)); } int uaq_write_mem(struct uaq_softc *sc, uint8_t cmd, uint16_t addr, uint16_t index, void *buf, int len) { return (uaq_ctl(sc, UAQ_CTL_WRITE, cmd, addr, index, buf, len)); } uint8_t uaq_read_1(struct uaq_softc *sc, uint8_t cmd, uint16_t reg, uint16_t index) { uint8_t val; uaq_read_mem(sc, cmd, reg, index, &val, 1); DPRINTFN(4, ("uaq_read_1: cmd %x reg %x index %x = %x\n", cmd, reg, index, val)); return (val); } uint16_t uaq_read_2(struct uaq_softc *sc, uint8_t cmd, uint16_t reg, uint16_t index) { uint16_t val; uaq_read_mem(sc, cmd, reg, index, &val, 2); DPRINTFN(4, ("uaq_read_2: cmd %x reg %x index %x = %x\n", cmd, reg, index, UGETW(&val))); return (UGETW(&val)); } uint32_t uaq_read_4(struct uaq_softc *sc, uint8_t cmd, uint16_t reg, uint16_t index) { uint32_t val; uaq_read_mem(sc, cmd, reg, index, &val, 4); DPRINTFN(4, ("uaq_read_4: cmd %x reg %x index %x = %x\n", cmd, reg, index, UGETDW(&val))); return (UGETDW(&val)); } int uaq_write_1(struct uaq_softc *sc, uint8_t cmd, uint16_t reg, uint16_t index, uint32_t val) { uint8_t temp; DPRINTFN(4, ("uaq_write_1: cmd %x reg %x index %x: %x\n", cmd, reg, index, val)); temp = val & 0xff; return (uaq_write_mem(sc, cmd, reg, index, &temp, 1)); } int uaq_write_2(struct uaq_softc *sc, uint8_t cmd, uint16_t reg, uint16_t index, uint32_t val) { uint16_t temp; DPRINTFN(4, ("uaq_write_2: cmd %x reg %x index %x: %x\n", cmd, reg, index, val)); USETW(&temp, val & 0xffff); return (uaq_write_mem(sc, cmd, reg, index, &temp, 2)); } int uaq_write_4(struct uaq_softc *sc, uint8_t cmd, uint16_t reg, uint16_t index, uint32_t val) { uint8_t temp[4]; DPRINTFN(4, ("uaq_write_4: cmd %x reg %x index %x: %x\n", cmd, reg, index, val)); USETDW(temp, val); return (uaq_write_mem(sc, cmd, reg, index, &temp, 4)); } int uaq_match(struct device *parent, void *match, void *aux) { struct usb_attach_arg *uaa = aux; if (uaa->iface == NULL || uaa->configno != 1) return (UMATCH_NONE); return (usb_lookup(uaq_devs, uaa->vendor, uaa->product) != NULL ? UMATCH_VENDOR_PRODUCT_CONF_IFACE : UMATCH_NONE); } void uaq_attach(struct device *parent, struct device *self, void *aux) { struct uaq_softc *sc = (struct uaq_softc *)self; struct usb_attach_arg *uaa = aux; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; struct ifnet *ifp; int i, s; sc->sc_udev = uaa->device; sc->sc_iface = uaa->iface; usb_init_task(&sc->sc_link_task, (void (*)(void *))uaq_link, sc, USB_TASK_TYPE_GENERIC); id = usbd_get_interface_descriptor(sc->sc_iface); for (i = 0; i < id->bNumEndpoints; i++) { ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); if (!ed) { printf("%s: couldn't get ep %d\n", sc->sc_dev.dv_xname, i); return; } if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { sc->sc_ed[UAQ_ENDPT_RX] = ed->bEndpointAddress; } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { sc->sc_ed[UAQ_ENDPT_TX] = ed->bEndpointAddress; sc->sc_out_frame_size = UGETW(ed->wMaxPacketSize); } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { sc->sc_ed[UAQ_ENDPT_INTR] = ed->bEndpointAddress; } } if ((sc->sc_ed[UAQ_ENDPT_RX] == 0) || (sc->sc_ed[UAQ_ENDPT_TX] == 0) || (sc->sc_ed[UAQ_ENDPT_INTR] == 0)) { printf("%s: missing one or more endpoints (%d, %d, %d)\n", sc->sc_dev.dv_xname, sc->sc_ed[UAQ_ENDPT_RX], sc->sc_ed[UAQ_ENDPT_TX], sc->sc_ed[UAQ_ENDPT_INTR]); return; } s = splnet(); printf("%s: ver %u.%u.%u", sc->sc_dev.dv_xname, uaq_read_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_FW_VER_MAJOR, 1) & 0x7f, uaq_read_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_FW_VER_MINOR, 1), uaq_read_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_FW_VER_REV, 1)); uaq_read_mem(sc, UAQ_CMD_FLASH_PARAM, 0, 0, &sc->sc_ac.ac_enaddr, ETHER_ADDR_LEN); printf(", address %s\n", ether_sprintf(sc->sc_ac.ac_enaddr)); ifp = &sc->sc_ac.ac_if; ifp->if_softc = sc; strlcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = uaq_ioctl; ifp->if_start = uaq_start; ifp->if_watchdog = uaq_watchdog; ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_CSUM_IPv4 | IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv4; #if NVLAN > 0 ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING; #endif ifmedia_init(&sc->sc_ifmedia, IFM_IMASK, uaq_ifmedia_upd, uaq_ifmedia_sts); uaq_add_media_types(sc); ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL); ifmedia_set(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO); sc->sc_ifmedia.ifm_media = sc->sc_ifmedia.ifm_cur->ifm_media; if_attach(ifp); ether_ifattach(ifp); splx(s); } int uaq_detach(struct device *self, int flags) { struct uaq_softc *sc = (struct uaq_softc *)self; struct ifnet *ifp = &sc->sc_ac.ac_if; int s; if (sc->sc_ep[UAQ_ENDPT_TX] != NULL) usbd_abort_pipe(sc->sc_ep[UAQ_ENDPT_TX]); if (sc->sc_ep[UAQ_ENDPT_RX] != NULL) usbd_abort_pipe(sc->sc_ep[UAQ_ENDPT_RX]); if (sc->sc_ep[UAQ_ENDPT_INTR] != NULL) usbd_abort_pipe(sc->sc_ep[UAQ_ENDPT_INTR]); s = splusb(); usb_rem_task(sc->sc_udev, &sc->sc_link_task); usb_detach_wait(&sc->sc_dev); if (ifp->if_flags & IFF_RUNNING) uaq_stop(sc); if (ifp->if_softc != NULL) { ether_ifdetach(ifp); if_detach(ifp); } splx(s); return 0; } int uaq_ifmedia_upd(struct ifnet *ifp) { struct uaq_softc *sc = ifp->if_softc; struct ifmedia *ifm = &sc->sc_ifmedia; int auto_adv; if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) return (EINVAL); auto_adv = UAQ_PHY_ADV_100M | UAQ_PHY_ADV_1G; if (sc->sc_udev->speed == USB_SPEED_SUPER) auto_adv |= UAQ_PHY_ADV_2_5G | UAQ_PHY_ADV_5G; sc->sc_phy_cfg &= ~(UAQ_PHY_ADV_MASK); sc->sc_phy_cfg |= UAQ_PHY_PAUSE | UAQ_PHY_ASYM_PAUSE | UAQ_PHY_DOWNSHIFT | (3 << UAQ_PHY_DSH_RETRY_SHIFT); switch (IFM_SUBTYPE(ifm->ifm_media)) { case IFM_AUTO: sc->sc_phy_cfg |= auto_adv; break; case IFM_5000_T: sc->sc_phy_cfg |= UAQ_PHY_ADV_5G; break; case IFM_2500_T: sc->sc_phy_cfg |= UAQ_PHY_ADV_2_5G; break; case IFM_1000_T: sc->sc_phy_cfg |= UAQ_PHY_ADV_1G; break; case IFM_100_TX: sc->sc_phy_cfg |= UAQ_PHY_ADV_100M; break; default: printf("%s: unsupported media type\n", sc->sc_dev.dv_xname); return (EINVAL); } DPRINTFN(1, ("%s: phy cfg %x\n", sc->sc_dev.dv_xname, sc->sc_phy_cfg)); uaq_write_4(sc, UAQ_CMD_PHY_OPS, 0, 0, sc->sc_phy_cfg); return (0); } void uaq_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct uaq_softc *sc = ifp->if_softc; ifmr->ifm_status = IFM_AVALID; if (sc->sc_link_speed > 0) { ifmr->ifm_status |= IFM_ACTIVE; ifmr->ifm_active = IFM_ETHER | IFM_FDX; switch (sc->sc_link_speed) { case UAQ_STATUS_SPEED_5G: ifmr->ifm_active |= IFM_5000_T; break; case UAQ_STATUS_SPEED_2_5G: ifmr->ifm_active |= IFM_2500_T; break; case UAQ_STATUS_SPEED_1G: ifmr->ifm_active |= IFM_1000_T; break; case UAQ_STATUS_SPEED_100M: ifmr->ifm_active |= IFM_100_TX; break; default: break; } } } void uaq_add_media_types(struct uaq_softc *sc) { ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_100_TX, 0, NULL); ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL); ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_1000_T, 0, NULL); ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_1000_T | IFM_FDX, 0, NULL); /* only add 2.5G and 5G if at super speed */ ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_2500_T, 0, NULL); ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_2500_T | IFM_FDX, 0, NULL); ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_5000_T, 0, NULL); ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_5000_T | IFM_FDX, 0, NULL); } void uaq_iff(struct uaq_softc *sc) { struct ifnet *ifp = &sc->sc_ac.ac_if; struct ether_multi *enm; struct ether_multistep step; uint8_t filter[UAQ_MCAST_FILTER_SIZE]; uint32_t hash; if (usbd_is_dying(sc->sc_udev)) return; sc->sc_rxctl &= ~(UAQ_SFR_RX_CTL_PRO | UAQ_SFR_RX_CTL_AMALL | UAQ_SFR_RX_CTL_AM); ifp->if_flags &= ~IFF_ALLMULTI; if (ifp->if_flags & IFF_PROMISC) { ifp->if_flags |= IFF_ALLMULTI; sc->sc_rxctl |= UAQ_SFR_RX_CTL_PRO; } else if (sc->sc_ac.ac_multirangecnt > 0) { ifp->if_flags |= IFF_ALLMULTI; sc->sc_rxctl |= UAQ_SFR_RX_CTL_AMALL; } else { sc->sc_rxctl |= UAQ_SFR_RX_CTL_AM; bzero(filter, sizeof(filter)); ETHER_FIRST_MULTI(step, &sc->sc_ac, enm); while (enm != NULL) { hash = ether_crc32_be(enm->enm_addrlo, ETHER_ADDR_LEN) >> 26; filter[hash >> 3] |= (1 << (hash & 7)); ETHER_NEXT_MULTI(step, enm); } uaq_write_mem(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_MCAST_FILTER, UAQ_MCAST_FILTER_SIZE, filter, UAQ_MCAST_FILTER_SIZE); } DPRINTFN(1, ("%s: rxctl = %x\n", sc->sc_dev.dv_xname, sc->sc_rxctl)); uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_RX_CTL, 2, sc->sc_rxctl); } void uaq_reset(struct uaq_softc *sc) { uint8_t mode; sc->sc_phy_cfg = UAQ_PHY_POWER_EN; uaq_write_4(sc, UAQ_CMD_PHY_OPS, 0, 0, sc->sc_phy_cfg); uaq_write_mem(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_NODE_ID, 0, sc->sc_ac.ac_enaddr, ETHER_ADDR_LEN); uaq_write_mem(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_NODE_ID, ETHER_ADDR_LEN, sc->sc_ac.ac_enaddr, ETHER_ADDR_LEN); uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_BM_INT_MASK, 0, 0xff); uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_SWP_CTRL, 0, 0); mode = uaq_read_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_MONITOR_MODE, 1); mode &= ~(UAQ_SFR_MONITOR_MODE_EPHYRW | UAQ_SFR_MONITOR_MODE_RWLC | UAQ_SFR_MONITOR_MODE_RWMP | UAQ_SFR_MONITOR_MODE_RWWF | UAQ_SFR_MONITOR_MODE_RW_FLAG); uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_MONITOR_MODE, 1, mode); sc->sc_link_status = 0; sc->sc_link_speed = 0; } void uaq_init(void *xsc) { struct uaq_softc *sc = xsc; struct uaq_chain *c; struct ifnet *ifp = &sc->sc_ac.ac_if; usbd_status err; int s, i; s = splnet(); uaq_stop(sc); uaq_reset(sc); if (uaq_xfer_list_init(sc, sc->sc_cdata.uaq_rx_chain, UAQ_RX_BUFSZ, UAQ_RX_LIST_CNT) == ENOBUFS) { printf("%s: rx list init failed\n", sc->sc_dev.dv_xname); splx(s); return; } if (uaq_xfer_list_init(sc, sc->sc_cdata.uaq_tx_chain, UAQ_TX_BUFSZ, UAQ_TX_LIST_CNT) == ENOBUFS) { printf("%s: tx list init failed\n", sc->sc_dev.dv_xname); splx(s); return; } SLIST_INIT(&sc->sc_cdata.uaq_tx_free); for (i = 0; i < UAQ_TX_LIST_CNT; i++) SLIST_INSERT_HEAD(&sc->sc_cdata.uaq_tx_free, &sc->sc_cdata.uaq_tx_chain[i], uc_list); err = usbd_open_pipe(sc->sc_iface, sc->sc_ed[UAQ_ENDPT_RX], USBD_EXCLUSIVE_USE, &sc->sc_ep[UAQ_ENDPT_RX]); if (err) { printf("%s: open rx pipe failed: %s\n", sc->sc_dev.dv_xname, usbd_errstr(err)); splx(s); return; } err = usbd_open_pipe(sc->sc_iface, sc->sc_ed[UAQ_ENDPT_TX], USBD_EXCLUSIVE_USE, &sc->sc_ep[UAQ_ENDPT_TX]); if (err) { printf("%s: open tx pipe failed: %s\n", sc->sc_dev.dv_xname, usbd_errstr(err)); splx(s); return; } for (i = 0; i < UAQ_RX_LIST_CNT; i++) { c = &sc->sc_cdata.uaq_rx_chain[i]; usbd_setup_xfer(c->uc_xfer, sc->sc_ep[UAQ_ENDPT_RX], c, c->uc_buf, c->uc_bufmax, USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT, uaq_rxeof); usbd_transfer(c->uc_xfer); } err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ed[UAQ_ENDPT_INTR], 0, &sc->sc_ep[UAQ_ENDPT_INTR], sc, &sc->sc_link_status, sizeof(sc->sc_link_status), uaq_intr, USBD_DEFAULT_INTERVAL); if (err) { printf("%s: couldn't open interrupt pipe\n", sc->sc_dev.dv_xname); splx(s); return; } uaq_iff(sc); uaq_ifmedia_upd(ifp); ifp->if_flags |= IFF_RUNNING; ifq_clr_oactive(&ifp->if_snd); splx(s); } int uaq_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct uaq_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; int s, error = 0; s = splnet(); switch (cmd) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; if (!(ifp->if_flags & IFF_RUNNING)) uaq_init(sc); break; case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_flags & IFF_RUNNING) error = ENETRESET; else uaq_init(sc); } else { if (ifp->if_flags & IFF_RUNNING) uaq_stop(sc); } break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, cmd); break; default: error = ether_ioctl(ifp, &sc->sc_ac, cmd, data); } if (error == ENETRESET) { if (ifp->if_flags & IFF_RUNNING) uaq_iff(sc); error = 0; } splx(s); return (error); } int uaq_xfer_list_init(struct uaq_softc *sc, struct uaq_chain *ch, uint32_t bufsize, int listlen) { struct uaq_chain *c; int i; for (i = 0; i < listlen; i++) { c = &ch[i]; c->uc_sc = sc; c->uc_idx = i; c->uc_buflen = 0; c->uc_bufmax = bufsize; c->uc_cnt = 0; if (c->uc_xfer == NULL) { c->uc_xfer = usbd_alloc_xfer(sc->sc_udev); if (c->uc_xfer == NULL) return (ENOBUFS); c->uc_buf = usbd_alloc_buffer(c->uc_xfer, c->uc_bufmax); if (c->uc_buf == NULL) { usbd_free_xfer(c->uc_xfer); c->uc_xfer = NULL; return (ENOBUFS); } } } return (0); } void uaq_xfer_list_free(struct uaq_softc *sc, struct uaq_chain *ch, int listlen) { int i; for (i = 0; i < listlen; i++) { if (ch[i].uc_buf != NULL) { ch[i].uc_buf = NULL; } ch[i].uc_cnt = 0; if (ch[i].uc_xfer != NULL) { usbd_free_xfer(ch[i].uc_xfer); ch[i].uc_xfer = NULL; } } } void uaq_stop(struct uaq_softc *sc) { struct uaq_cdata *cd; struct ifnet *ifp; usbd_status err; ifp = &sc->sc_ac.ac_if; ifp->if_timer = 0; ifp->if_flags &= ~IFF_RUNNING; ifq_clr_oactive(&ifp->if_snd); sc->sc_link_status = 0; sc->sc_link_speed = 0; if (sc->sc_ep[UAQ_ENDPT_RX] != NULL) { err = usbd_close_pipe(sc->sc_ep[UAQ_ENDPT_RX]); if (err) { printf("%s: close rx pipe failed: %s\n", sc->sc_dev.dv_xname, usbd_errstr(err)); } sc->sc_ep[UAQ_ENDPT_RX] = NULL; } if (sc->sc_ep[UAQ_ENDPT_TX] != NULL) { err = usbd_close_pipe(sc->sc_ep[UAQ_ENDPT_TX]); if (err) { printf("%s: close tx pipe failed: %s\n", sc->sc_dev.dv_xname, usbd_errstr(err)); } sc->sc_ep[UAQ_ENDPT_TX] = NULL; } if (sc->sc_ep[UAQ_ENDPT_INTR] != NULL) { err = usbd_close_pipe(sc->sc_ep[UAQ_ENDPT_INTR]); if (err) { printf("%s: close intr pipe failed: %s\n", sc->sc_dev.dv_xname, usbd_errstr(err)); } sc->sc_ep[UAQ_ENDPT_INTR] = NULL; } cd = &sc->sc_cdata; uaq_xfer_list_free(sc, cd->uaq_rx_chain, UAQ_RX_LIST_CNT); uaq_xfer_list_free(sc, cd->uaq_tx_chain, UAQ_TX_LIST_CNT); } void uaq_link(struct uaq_softc *sc) { if (sc->sc_link_speed > 0) { uint8_t resend[3] = { 0, 0xf8, 7 }; uint8_t qctrl[5] = { 7, 0x00, 0x01, 0x1e, 0xff }; uint8_t ipg = 0; switch (sc->sc_link_speed) { case UAQ_STATUS_SPEED_100M: resend[1] = 0xfb; resend[2] = 0x4; break; case UAQ_STATUS_SPEED_5G: ipg = 5; break; } uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_IPG_0, 1, ipg); uaq_write_mem(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_TX_PAUSE_RESEND_T, 3, resend, 3); uaq_write_mem(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_RX_BULKIN_QCTRL, 5, qctrl, 5); uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_PAUSE_WATERLVL_LOW, 2, 0x0810); uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_BMRX_DMA_CTRL, 1, 0); uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_BMTX_DMA_CTRL, 1, 0); uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_ARC_CTRL, 1, 0); sc->sc_rxctl = UAQ_SFR_RX_CTL_IPE | UAQ_SFR_RX_CTL_AB; uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_RX_CTL, 2, sc->sc_rxctl); uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_ETH_MAC_PATH, 1, UAQ_SFR_RX_PATH_READY); uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_BULK_OUT_CTRL, 1, UAQ_SFR_BULK_OUT_EFF_EN); uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_MEDIUM_STATUS_MODE, 2, 0); uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_MEDIUM_STATUS_MODE, 2, UAQ_SFR_MEDIUM_XGMIIMODE | UAQ_SFR_MEDIUM_FULL_DUPLEX | UAQ_SFR_MEDIUM_RECEIVE_EN | UAQ_SFR_MEDIUM_RXFLOW_CTRLEN | UAQ_SFR_MEDIUM_TXFLOW_CTRLEN); /* JUMBO_EN */ uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_RXCOE_CTL, 1, UAQ_SFR_RXCOE_IP | UAQ_SFR_RXCOE_TCP | UAQ_SFR_RXCOE_UDP | UAQ_SFR_RXCOE_TCPV6 | UAQ_SFR_RXCOE_UDPV6); uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_TXCOE_CTL, 1, UAQ_SFR_TXCOE_IP | UAQ_SFR_TXCOE_TCP | UAQ_SFR_TXCOE_UDP | UAQ_SFR_TXCOE_TCPV6 | UAQ_SFR_TXCOE_UDPV6); sc->sc_rxctl |= UAQ_SFR_RX_CTL_START; uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_RX_CTL, 2, sc->sc_rxctl); } else { uint16_t mode; mode = uaq_read_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_MEDIUM_STATUS_MODE, 2); mode &= ~UAQ_SFR_MEDIUM_RECEIVE_EN; uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_MEDIUM_STATUS_MODE, 2, mode); sc->sc_rxctl &= ~UAQ_SFR_RX_CTL_START; uaq_write_2(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_RX_CTL, 2, sc->sc_rxctl); uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_BULK_OUT_CTRL, 1, UAQ_SFR_BULK_OUT_FLUSH_EN | UAQ_SFR_BULK_OUT_EFF_EN); uaq_write_1(sc, UAQ_CMD_ACCESS_MAC, UAQ_SFR_BULK_OUT_CTRL, 1, UAQ_SFR_BULK_OUT_EFF_EN); } } void uaq_intr(struct usbd_xfer *xfer, void *priv, usbd_status status) { struct uaq_softc *sc = priv; struct ifnet *ifp = &sc->sc_ac.ac_if; uint64_t linkstatus; uint64_t baudrate; int link_state; if (status == USBD_CANCELLED) return; if (status != USBD_NORMAL_COMPLETION) { DPRINTFN(2, ("uaq_intr: status=%d\n", status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall_async( sc->sc_ep[UAQ_ENDPT_INTR]); return; } linkstatus = letoh64(sc->sc_link_status); DPRINTFN(1, ("uaq_intr: link status %llx\n", linkstatus)); if (linkstatus & UAQ_STATUS_LINK) { link_state = LINK_STATE_FULL_DUPLEX; sc->sc_link_speed = (linkstatus & UAQ_STATUS_SPEED_MASK) >> UAQ_STATUS_SPEED_SHIFT; switch (sc->sc_link_speed) { case UAQ_STATUS_SPEED_5G: baudrate = IF_Gbps(5); break; case UAQ_STATUS_SPEED_2_5G: baudrate = IF_Mbps(2500); break; case UAQ_STATUS_SPEED_1G: baudrate = IF_Gbps(1); break; case UAQ_STATUS_SPEED_100M: baudrate = IF_Mbps(100); break; default: baudrate = 0; break; } ifp->if_baudrate = baudrate; } else { link_state = LINK_STATE_DOWN; sc->sc_link_speed = 0; } if (link_state != ifp->if_link_state) { ifp->if_link_state = link_state; if_link_state_change(ifp); usb_add_task(sc->sc_udev, &sc->sc_link_task); } } void uaq_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status) { struct uaq_chain *c = (struct uaq_chain *)priv; struct uaq_softc *sc = c->uc_sc; struct ifnet *ifp = &sc->sc_ac.ac_if; uint8_t *buf; uint64_t *pdesc; uint64_t desc; uint32_t total_len; struct mbuf_list ml = MBUF_LIST_INITIALIZER(); struct mbuf *m; int pktlen, s; int count, offset; if (usbd_is_dying(sc->sc_udev)) return; if (!(ifp->if_flags & IFF_RUNNING)) return; if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) return; if (usbd_ratecheck(&sc->sc_rx_notice)) { printf("%s: usb errors on rx: %s\n", sc->sc_dev.dv_xname, usbd_errstr(status)); } if (status == USBD_STALLED) usbd_clear_endpoint_stall_async( sc->sc_ep[UAQ_ENDPT_RX]); goto done; } usbd_get_xfer_status(xfer, NULL, (void **)&buf, &total_len, NULL); DPRINTFN(3, ("received %d bytes\n", total_len)); if ((total_len & 7) != 0) { printf("%s: weird rx transfer length %d\n", sc->sc_dev.dv_xname, total_len); goto done; } pdesc = (uint64_t *)(buf + (total_len - sizeof(desc))); desc = lemtoh64(pdesc); count = desc & UAQ_RX_HDR_COUNT_MASK; if (count == 0) goto done; /* get offset of packet headers */ offset = total_len - ((count + 1) * sizeof(desc)); if (offset != ((desc & UAQ_RX_HDR_OFFSET_MASK) >> UAQ_RX_HDR_OFFSET_SHIFT)) { printf("%s: offset mismatch, got %d expected %lld\n", sc->sc_dev.dv_xname, offset, desc >> UAQ_RX_HDR_OFFSET_SHIFT); goto done; } if (offset < 0 || offset > total_len) { printf("%s: offset %d outside buffer (%d)\n", sc->sc_dev.dv_xname, offset, total_len); goto done; } pdesc = (uint64_t *)(buf + offset); total_len = offset; while (count-- > 0) { desc = lemtoh64(pdesc); pdesc++; pktlen = (desc & UAQ_RX_PKT_LEN_MASK) >> UAQ_RX_PKT_LEN_SHIFT; if (pktlen > total_len) { DPRINTFN(2, ("not enough bytes for this packet\n")); ifp->if_ierrors++; goto done; } m = m_devget(buf + 2, pktlen - 2, ETHER_ALIGN); if (m == NULL) { DPRINTFN(2, ("m_devget failed for this packet\n")); ifp->if_ierrors++; goto done; } if ((desc & UAQ_RX_PKT_L3_ERR) == 0) m->m_pkthdr.csum_flags |= M_IPV4_CSUM_IN_OK; if ((desc & UAQ_RX_PKT_L4_ERR) == 0) m->m_pkthdr.csum_flags |= M_TCP_CSUM_IN_OK | M_UDP_CSUM_IN_OK; #if NVLAN > 0 if (desc & UAQ_RX_PKT_VLAN) { m->m_pkthdr.ether_vtag = (desc >> UAQ_RX_PKT_VLAN_SHIFT) & 0xfff; m->m_flags |= M_VLANTAG; } #endif ml_enqueue(&ml, m); total_len -= roundup(pktlen, UAQ_RX_BUF_ALIGN); buf += roundup(pktlen, UAQ_RX_BUF_ALIGN); } done: s = splnet(); if_input(ifp, &ml); splx(s); memset(c->uc_buf, 0, UAQ_RX_BUFSZ); usbd_setup_xfer(xfer, sc->sc_ep[UAQ_ENDPT_RX], c, c->uc_buf, UAQ_RX_BUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT, uaq_rxeof); usbd_transfer(xfer); } void uaq_watchdog(struct ifnet *ifp) { struct uaq_softc *sc = ifp->if_softc; struct uaq_chain *c; usbd_status err; int i, s; ifp->if_timer = 0; if (usbd_is_dying(sc->sc_udev)) return; if ((ifp->if_flags & (IFF_RUNNING|IFF_UP)) != (IFF_RUNNING|IFF_UP)) return; sc = ifp->if_softc; s = splnet(); ifp->if_oerrors++; DPRINTF(("%s: watchdog timeout\n", sc->sc_dev.dv_xname)); for (i = 0; i < UAQ_TX_LIST_CNT; i++) { c = &sc->sc_cdata.uaq_tx_chain[i]; if (c->uc_cnt > 0) { usbd_get_xfer_status(c->uc_xfer, NULL, NULL, NULL, &err); uaq_txeof(c->uc_xfer, c, err); } } if (ifq_is_oactive(&ifp->if_snd)) ifq_restart(&ifp->if_snd); splx(s); } void uaq_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status) { struct uaq_softc *sc; struct uaq_chain *c; struct ifnet *ifp; int s; c = priv; sc = c->uc_sc; ifp = &sc->sc_ac.ac_if; if (usbd_is_dying(sc->sc_udev)) return; if (status != USBD_NORMAL_COMPLETION) DPRINTF(("%s: %s uc_idx=%u : %s\n", sc->sc_dev.dv_xname, __func__, c->uc_idx, usbd_errstr(status))); else DPRINTF(("%s: txeof\n", sc->sc_dev.dv_xname)); s = splnet(); c->uc_cnt = 0; c->uc_buflen = 0; SLIST_INSERT_HEAD(&sc->sc_cdata.uaq_tx_free, c, uc_list); if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { splx(s); return; } ifp->if_oerrors++; printf("%s: usb error on tx: %s\n", sc->sc_dev.dv_xname, usbd_errstr(status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall_async( sc->sc_ep[UAQ_ENDPT_TX]); splx(s); return; } ifp->if_timer = 0; if (ifq_is_oactive(&ifp->if_snd)) ifq_restart(&ifp->if_snd); splx(s); } void uaq_start(struct ifnet *ifp) { struct uaq_softc *sc = ifp->if_softc; struct uaq_cdata *cd = &sc->sc_cdata; struct uaq_chain *c; struct mbuf *m = NULL; int s, mlen; if ((sc->sc_link_speed == 0) || (ifp->if_flags & (IFF_RUNNING|IFF_UP)) != (IFF_RUNNING|IFF_UP)) { return; } s = splnet(); c = SLIST_FIRST(&cd->uaq_tx_free); while (c != NULL) { m = ifq_deq_begin(&ifp->if_snd); if (m == NULL) break; mlen = m->m_pkthdr.len; /* Discard packet larger than buffer. */ if (mlen + sizeof(uint64_t) >= c->uc_bufmax) { ifq_deq_commit(&ifp->if_snd, m); m_freem(m); ifp->if_oerrors++; continue; } /* Append packet to current buffer. */ mlen = uaq_encap_txpkt(sc, m, c->uc_buf + c->uc_buflen, c->uc_bufmax - c->uc_buflen); if (mlen <= 0) { ifq_deq_rollback(&ifp->if_snd, m); break; } ifq_deq_commit(&ifp->if_snd, m); c->uc_cnt += 1; c->uc_buflen += mlen; #if NBPFILTER > 0 if (ifp->if_bpf) bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT); #endif m_freem(m); } if (c != NULL) { /* Send current buffer unless empty */ if (c->uc_buflen > 0 && c->uc_cnt > 0) { SLIST_REMOVE_HEAD(&cd->uaq_tx_free, uc_list); if (uaq_encap_xfer(sc, c)) { SLIST_INSERT_HEAD(&cd->uaq_tx_free, c, uc_list); } c = SLIST_FIRST(&cd->uaq_tx_free); ifp->if_timer = 5; if (c == NULL) ifq_set_oactive(&ifp->if_snd); } } splx(s); } int uaq_encap_txpkt(struct uaq_softc *sc, struct mbuf *m, char *buf, uint32_t maxlen) { uint64_t desc; int padded; desc = m->m_pkthdr.len; padded = roundup(m->m_pkthdr.len, UAQ_TX_BUF_ALIGN); if (((padded + sizeof(desc)) % sc->sc_out_frame_size) == 0) { desc |= UAQ_TX_PKT_DROP_PADD; padded += 8; } if (padded + sizeof(desc) > maxlen) return (-1); #if NVLAN > 0 if (m->m_flags & M_VLANTAG) desc |= (((uint64_t)m->m_pkthdr.ether_vtag) << UAQ_TX_PKT_VLAN_SHIFT) | UAQ_TX_PKT_VLAN; #endif htolem64((uint64_t *)buf, desc); m_copydata(m, 0, m->m_pkthdr.len, buf + sizeof(desc)); return (padded + sizeof(desc)); } int uaq_encap_xfer(struct uaq_softc *sc, struct uaq_chain *c) { usbd_status err; usbd_setup_xfer(c->uc_xfer, sc->sc_ep[UAQ_ENDPT_TX], c, c->uc_buf, c->uc_buflen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, 10000, uaq_txeof); err = usbd_transfer(c->uc_xfer); if (err != USBD_IN_PROGRESS) { c->uc_cnt = 0; c->uc_buflen = 0; uaq_stop(sc); return (EIO); } return (0); }