--- /dev/null
+/* $OpenBSD: if_uaq.c,v 1.1 2021/09/04 12:11:45 jmatthew Exp $ */
+/*-
+ * Copyright (c) 2021 Jonathan Matthew <jonathan@d14n.org>
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/rwlock.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/device.h>
+
+#include <machine/bus.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usbdevs.h>
+
+#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 32768
+
+#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);
+ if (ifp->if_flags & IFF_PROMISC || sc->sc_ac.ac_multirangecnt > 0) {
+ sc->sc_rxctl |= UAQ_SFR_RX_CTL_PRO;
+ } else if (ifp->if_flags & IFF_ALLMULTI ||
+ sc->sc_ac.ac_multirangecnt > 0) {
+ sc->sc_rxctl |= UAQ_SFR_RX_CTL_AMALL;
+ } else if (sc->sc_ac.ac_multicnt > 0) {
+ 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);
+ return;
+ }
+
+ 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);
+}