Index: sys/dev/usb/net/if_axge.c =================================================================== --- sys/dev/usb/net/if_axge.c (revision 302023) +++ sys/dev/usb/net/if_axge.c (working copy) @@ -145,7 +145,7 @@ static const struct usb_config axge_config[AXGE_N_ .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .frames = 16, - .bufsize = 16 * MCLBYTES, + .bufsize = 16 * AXGE_TSO_BUFSIZE, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = axge_bulk_write_callback, .timeout = 10000, /* 10 seconds */ @@ -154,7 +154,7 @@ static const struct usb_config axge_config[AXGE_N_ .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, - .bufsize = 65536, + .bufsize = 32 * 1024, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = axge_bulk_read_callback, .timeout = 0, /* no timeout */ @@ -303,8 +303,6 @@ axge_miibus_writereg(device_t dev, int phy, int re int locked; sc = device_get_softc(dev); - if (sc->sc_phyno != phy) - return (0); locked = mtx_owned(&sc->sc_mtx); if (!locked) AXGE_LOCK(sc); @@ -434,7 +432,6 @@ axge_attach_post(struct usb_ether *ue) struct axge_softc *sc; sc = uether_getsc(ue); - sc->sc_phyno = 3; /* Initialize controller and get station address. */ axge_chip_init(sc); @@ -459,14 +456,18 @@ axge_attach_post_sub(struct usb_ether *ue) ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; IFQ_SET_READY(&ifp->if_snd); - ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_TXCSUM | IFCAP_RXCSUM; - ifp->if_hwassist = AXGE_CSUM_FEATURES; + ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_TXCSUM | IFCAP_RXCSUM | + IFCAP_TSO4; + ifp->if_hwassist = AXGE_CSUM_FEATURES | CSUM_TSO; ifp->if_capenable = ifp->if_capabilities; + ifp->if_hw_tsomax = AXGE_TSO_MAXSIZE; + ifp->if_hw_tsomaxsegcount = AXGE_TSO_TXSEGS; + ifp->if_hw_tsomaxsegsize = AXGE_TSO_SEGSIZE; mtx_lock(&Giant); error = mii_attach(ue->ue_dev, &ue->ue_miibus, ifp, uether_ifmedia_upd, ue->ue_methods->ue_mii_sts, - BMSR_DEFCAPMASK, sc->sc_phyno, MII_OFFSET_ANY, MIIF_DOPAUSE); + BMSR_DEFCAPMASK, AXGE_PHY_ADDR, MII_OFFSET_ANY, MIIF_DOPAUSE); mtx_unlock(&Giant); return (error); @@ -633,7 +634,8 @@ axge_bulk_write_callback(struct usb_xfer *xfer, us struct ifnet *ifp; struct usb_page_cache *pc; struct mbuf *m; - uint32_t txhdr; + struct axge_frame_txhdr txhdr; + usb_frlength_t frame_size; int nframes, pos; sc = usbd_xfer_softc(xfer); @@ -654,38 +656,38 @@ tr_setup: return; } + frame_size = usbd_xfer_max_framelen(xfer); for (nframes = 0; nframes < 16 && !IFQ_DRV_IS_EMPTY(&ifp->if_snd); nframes++) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; - usbd_xfer_set_frame_offset(xfer, nframes * MCLBYTES, - nframes); + usbd_xfer_set_frame_offset(xfer, nframes * + AXGE_TSO_BUFSIZE, nframes); pos = 0; pc = usbd_xfer_get_frame(xfer, nframes); - txhdr = htole32(m->m_pkthdr.len); - usbd_copy_in(pc, 0, &txhdr, sizeof(txhdr)); - txhdr = 0; - txhdr = htole32(txhdr); - usbd_copy_in(pc, 4, &txhdr, sizeof(txhdr)); - pos += 8; + txhdr.len = htole32(AXGE_TXBYTES(m->m_pkthdr.len)); + if ((m->m_pkthdr.csum_flags & CSUM_TSO) != 0) + txhdr.mss = htole32(m->m_pkthdr.tso_segsz & + AXGE_MSS_MASK); + else { + txhdr.mss = 0; + if ((m->m_pkthdr.csum_flags & + AXGE_CSUM_FEATURES) == 0) + txhdr.len |= htole32(AXGE_CSUM_DISABLE); + } + + if ((m->m_pkthdr.len + sizeof(txhdr)) % frame_size == 0) + txhdr.mss |= htole32(AXGE_PADDING); + + usbd_copy_in(pc, 0, &txhdr.len, sizeof(txhdr.len)); + pos += sizeof(txhdr.len); + usbd_copy_in(pc, pos, &txhdr.mss, sizeof(txhdr.mss)); + pos += sizeof(txhdr.mss); usbd_m_copy_in(pc, pos, m, 0, m->m_pkthdr.len); pos += m->m_pkthdr.len; - if ((pos % usbd_xfer_max_framelen(xfer)) == 0) - txhdr |= 0x80008000; /* - * XXX - * Update TX packet counter here. This is not - * correct way but it seems that there is no way - * to know how many packets are sent at the end - * of transfer because controller combines - * multiple writes into single one if there is - * room in TX buffer of controller. - */ - if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); - - /* * if there's a BPF listener, bounce a copy * of this frame to him: */ @@ -697,6 +699,16 @@ tr_setup: usbd_xfer_set_frame_len(xfer, nframes, pos); } if (nframes != 0) { + /* + * XXX + * Update TX packet counter here. This is not + * correct way but it seems that there is no way + * to know how many packets are sent at the end + * of transfer because controller combines + * multiple writes into single one if there is + * room in TX buffer of controller. + */ + if_inc_counter(ifp, IFCOUNTER_OPACKETS, nframes); usbd_xfer_set_frames(xfer, nframes); usbd_transfer_submit(xfer); ifp->if_drv_flags |= IFF_DRV_OACTIVE; @@ -727,11 +739,6 @@ axge_tick(struct usb_ether *ue) AXGE_LOCK_ASSERT(sc, MA_OWNED); mii_tick(mii); - if ((sc->sc_flags & AXGE_FLAG_LINK) == 0) { - axge_miibus_statchg(ue->ue_dev); - if ((sc->sc_flags & AXGE_FLAG_LINK) != 0) - axge_start(ue); - } } static void @@ -835,10 +842,15 @@ axge_init(struct usb_ether *ue) /* Configure TX/RX checksum offloading. */ axge_csum_cfg(ue); - /* Configure RX settings. */ - rxmode = (RCR_AM | RCR_SO | RCR_DROP_CRCE); - if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) - rxmode |= RCR_IPE; + /* + * Configure RX settings. + * Don't set RCR_IPE(IP header alignment on 32bit boundary) to diable + * inserting extra padding bytes. This wastes ethernet to USB host + * bandwidth as well as complicating RX handling logic. Current USB + * framework requires copying RX frames to mbufs so there is no need + * to worry about alignment. + */ + rxmode = RCR_AM | RCR_SO | RCR_DROP_CRCE; /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) @@ -857,6 +869,20 @@ axge_init(struct usb_ether *ue) usbd_xfer_set_stall(sc->sc_xfer[AXGE_BULK_DT_WR]); +#if 1 + /* Disable EEE */ + axge_write_cmd_2(sc, AXGE_ACCESS_PHY, MII_MMDACR, AXGE_PHY_ADDR, 0x0007); + axge_write_cmd_2(sc, AXGE_ACCESS_PHY, MII_MMDAADR, AXGE_PHY_ADDR, 0x003C); + axge_write_cmd_2(sc, AXGE_ACCESS_PHY, MII_MMDACR, AXGE_PHY_ADDR, 0x4007); + axge_write_cmd_2(sc, AXGE_ACCESS_PHY, MII_MMDAADR, AXGE_PHY_ADDR, 0x0000); + + /* Disable GreenEthernet */ + axge_write_cmd_2(sc, AXGE_ACCESS_PHY, 0x1F, AXGE_PHY_ADDR, 0x0003); + axge_write_cmd_2(sc, AXGE_ACCESS_PHY, 0x19, AXGE_PHY_ADDR, 0x3246); + axge_write_cmd_2(sc, AXGE_ACCESS_PHY, 0x1F, AXGE_PHY_ADDR, 0x0000); + +#endif + ifp->if_drv_flags |= IFF_DRV_RUNNING; /* Switch to selected media. */ axge_ifmedia_upd(ifp); @@ -904,8 +930,11 @@ axge_ioctl(struct ifnet *ifp, u_long cmd, caddr_t ifp->if_capenable ^= IFCAP_TXCSUM; if ((ifp->if_capenable & IFCAP_TXCSUM) != 0) ifp->if_hwassist |= AXGE_CSUM_FEATURES; - else + else { ifp->if_hwassist &= ~AXGE_CSUM_FEATURES; + ifp->if_hwassist &= ~CSUM_TSO; + ifp->if_capenable &= ~IFCAP_TSO4; + } reinit++; } if ((mask & IFCAP_RXCSUM) != 0 && @@ -913,6 +942,19 @@ axge_ioctl(struct ifnet *ifp, u_long cmd, caddr_t ifp->if_capenable ^= IFCAP_RXCSUM; reinit++; } + if ((mask & IFCAP_TSO4) != 0 && + (ifp->if_capabilities & IFCAP_TSO4) != 0) { + ifp->if_capenable ^= IFCAP_TSO4; + if ((ifp->if_capenable & IFCAP_TSO4) != 0) + ifp->if_hwassist |= CSUM_TSO; + else + ifp->if_hwassist &= ~CSUM_TSO; + /* TSO requires TX checksum offloading. */ + if ((ifp->if_capenable & IFCAP_TXCSUM) == 0) { + ifp->if_capenable &= ~IFCAP_TSO4; + ifp->if_hwassist &= ~CSUM_TSO; + } + } if (reinit > 0 && ifp->if_drv_flags & IFF_DRV_RUNNING) ifp->if_drv_flags &= ~IFF_DRV_RUNNING; else @@ -929,12 +971,12 @@ axge_ioctl(struct ifnet *ifp, u_long cmd, caddr_t static void axge_rx_frame(struct usb_ether *ue, struct usb_page_cache *pc, int actlen) { + struct axge_frame_rxhdr pkt_hdr; + uint32_t rxhdr; uint32_t pos; - uint32_t pkt_cnt; - uint32_t rxhdr; - uint32_t pkt_hdr; + uint32_t pkt_cnt, pkt_end; uint32_t hdr_off; - uint32_t pktlen; + uint32_t pktlen; /* verify we have enough data */ if (actlen < (int)sizeof(rxhdr)) @@ -945,9 +987,20 @@ axge_rx_frame(struct usb_ether *ue, struct usb_pag usbd_copy_out(pc, actlen - sizeof(rxhdr), &rxhdr, sizeof(rxhdr)); rxhdr = le32toh(rxhdr); - pkt_cnt = (uint16_t)rxhdr; - hdr_off = (uint16_t)(rxhdr >> 16); + pkt_cnt = rxhdr & 0xFFFF; + hdr_off = pkt_end = (rxhdr >> 16) & 0xFFFF; + /* + * <----------------------- actlen ------------------------> + * [frame #0]...[frame #N][pkt_hdr #0]...[pkt_hdr #N][rxhdr] + * Each RX frame would be aligned on 8 bytes boundary. If + * RCR_IPE bit is set in AXGE_RCR register, there would be 2 + * padding bytes and 6 dummy bytes(as the padding also should + * be aligned on 8 bytes boundary) for each RX frame to align + * IP header on 32bits boundary. Driver don't set RCR_IPE bit + * of AXGE_RCR register, so there should be no padding bytes + * which simplifies RX logic a lot. + */ while (pkt_cnt--) { /* verify the header offset */ if ((int)(hdr_off + sizeof(pkt_hdr)) > actlen) { @@ -954,24 +1007,19 @@ axge_rx_frame(struct usb_ether *ue, struct usb_pag DPRINTF("End of packet headers\n"); break; } - if ((int)pos >= actlen) { + usbd_copy_out(pc, hdr_off, &pkt_hdr, sizeof(pkt_hdr)); + pkt_hdr.status = le32toh(pkt_hdr.status); + pktlen = AXGE_RXBYTES(pkt_hdr.status); + if (pos + pktlen > pkt_end) { DPRINTF("Data position reached end\n"); break; } - usbd_copy_out(pc, hdr_off, &pkt_hdr, sizeof(pkt_hdr)); - pkt_hdr = le32toh(pkt_hdr); - pktlen = (pkt_hdr >> 16) & 0x1fff; - if (pkt_hdr & (AXGE_RXHDR_CRC_ERR | AXGE_RXHDR_DROP_ERR)) { + if (AXGE_RX_ERR(pkt_hdr.status) != 0) { DPRINTF("Dropped a packet\n"); if_inc_counter(ue->ue_ifp, IFCOUNTER_IERRORS, 1); - } - if (pktlen >= 6 && (int)(pos + pktlen) <= actlen) { - axge_rxeof(ue, pc, pos + 2, pktlen - 6, pkt_hdr); - } else { - DPRINTF("Invalid packet pos=%d len=%d\n", - (int)pos, (int)pktlen); - } + } else + axge_rxeof(ue, pc, pos, pktlen, pkt_hdr.status); pos += (pktlen + 7) & ~7; hdr_off += sizeof(pkt_hdr); } @@ -978,8 +1026,8 @@ axge_rx_frame(struct usb_ether *ue, struct usb_pag } static void -axge_rxeof(struct usb_ether *ue, struct usb_page_cache *pc, - unsigned int offset, unsigned int len, uint32_t pkt_hdr) +axge_rxeof(struct usb_ether *ue, struct usb_page_cache *pc, unsigned int offset, + unsigned int len, uint32_t status) { struct ifnet *ifp; struct mbuf *m; @@ -990,29 +1038,34 @@ static void return; } - m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (len > MHLEN - ETHER_ALIGN) + m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + else + m = m_gethdr(M_NOWAIT, MT_DATA); if (m == NULL) { if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); return; } m->m_pkthdr.rcvif = ifp; - m->m_len = m->m_pkthdr.len = len + ETHER_ALIGN; - m_adj(m, ETHER_ALIGN); + m->m_len = m->m_pkthdr.len = len; + m->m_data += ETHER_ALIGN; usbd_copy_out(pc, offset, mtod(m, uint8_t *), len); - if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); - - if ((pkt_hdr & (AXGE_RXHDR_L4CSUM_ERR | AXGE_RXHDR_L3CSUM_ERR)) == 0) { - if ((pkt_hdr & AXGE_RXHDR_L4_TYPE_MASK) == - AXGE_RXHDR_L4_TYPE_TCP || - (pkt_hdr & AXGE_RXHDR_L4_TYPE_MASK) == - AXGE_RXHDR_L4_TYPE_UDP) { + if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) { + if ((status & AXGE_RX_L3_CSUM_ERR) == 0 && + (status & AXGE_RX_L3_TYPE_MASK) == AXGE_RX_L3_TYPE_IPV4) + m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED | + CSUM_IP_VALID; + if ((status & AXGE_RX_L4_CSUM_ERR) == 0 && + ((status & AXGE_RX_L4_TYPE_MASK) == AXGE_RX_L4_TYPE_UDP || + (status & AXGE_RX_L4_TYPE_MASK) == AXGE_RX_L4_TYPE_TCP)) { m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | - CSUM_PSEUDO_HDR | CSUM_IP_CHECKED | CSUM_IP_VALID; + CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } } + if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); _IF_ENQUEUE(&ue->ue_rxq, m); } Index: sys/dev/usb/net/if_axgereg.h =================================================================== --- sys/dev/usb/net/if_axgereg.h (revision 302023) +++ sys/dev/usb/net/if_axgereg.h (working copy) @@ -141,14 +141,6 @@ #define AXGE_CONFIG_IDX 0 /* config number 1 */ #define AXGE_IFACE_IDX 0 -#define AXGE_RXHDR_L4_TYPE_MASK 0x1c -#define AXGE_RXHDR_L4CSUM_ERR 1 -#define AXGE_RXHDR_L3CSUM_ERR 2 -#define AXGE_RXHDR_L4_TYPE_UDP 4 -#define AXGE_RXHDR_L4_TYPE_TCP 16 -#define AXGE_RXHDR_CRC_ERR 0x20000000 -#define AXGE_RXHDR_DROP_ERR 0x80000000 - #define GET_MII(sc) uether_getmii(&(sc)->sc_ue) /* The interrupt endpoint is currently unused by the ASIX part. */ @@ -158,11 +150,52 @@ enum { AXGE_N_TRANSFER, }; +struct axge_frame_txhdr { + uint32_t len; +#define AXGE_TXLEN_MASK 0x0001FFFF +#define AXGE_VLAN_INSERT 0x20000000 +#define AXGE_CSUM_DISABLE 0x80000000 + uint32_t mss; +#define AXGE_MSS_MASK 0x00003FFF +#define AXGE_PADDING 0x80008000 +#define AXGE_VLAN_TAG_MASK 0xFFFF0000 +} __packed; + +#define AXGE_TXBYTES(x) ((x) & AXGE_TXLEN_MASK) + +struct axge_frame_rxhdr { + uint32_t status; +#define AXGE_RX_L4_CSUM_ERR 0x00000001 +#define AXGE_RX_L3_CSUM_ERR 0x00000002 +#define AXGE_RX_L4_TYPE_UDP 0x00000004 +#define AXGE_RX_L4_TYPE_ICMP 0x00000008 +#define AXGE_RX_L4_TYPE_IGMP 0x0000000C +#define AXGE_RX_L4_TYPE_TCP 0x00000010 +#define AXGE_RX_L4_TYPE_MASK 0x0000001C +#define AXGE_RX_L3_TYPE_IPV4 0x00000020 +#define AXGE_RX_L3_TYPE_IPV6 0x00000040 +#define AXGE_RX_L3_TYPE_MASK 0x00000060 +#define AXGE_RX_VLAN_IND_MASK 0x00000700 +#define AXGE_RX_GOOD_PKT 0x00000800 +#define AXGE_RX_VLAN_PRI_MASK 0x00007000 +#define AXGE_RX_BCAST 0x00008000 +#define AXGE_RX_LEN_MASK 0x1FFF0000 +#define AXGE_RX_CRC_ERR 0x20000000 +#define AXGE_RX_MII_ERR 0x40000000 +#define AXGE_RX_DROP_PKT 0x80000000 +#define AXGE_RX_LEN_SHIFT 16 +} __packed; + +#define AXGE_RXBYTES(x) (((x) & AXGE_RX_LEN_MASK) >> AXGE_RX_LEN_SHIFT) +#define AXGE_RX_ERR(x) \ + ((x) & (AXGE_RX_CRC_ERR | AXGE_RX_MII_ERR | AXGE_RX_DROP_PKT)) + +#define AXGE_PHY_ADDR 3 + struct axge_softc { struct usb_ether sc_ue; struct mtx sc_mtx; struct usb_xfer *sc_xfer[AXGE_N_TRANSFER]; - int sc_phyno; int sc_flags; #define AXGE_FLAG_LINK 0x0001 /* got a link */ @@ -171,3 +204,17 @@ struct axge_softc { #define AXGE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define AXGE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define AXGE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) + +/* + * Controller seems to support Microsoft LSOv1. USB stack requires + * copying packets to USB maintained buffers before transmit. So + * processing a full sized TSO packet(IP_MAXPACKET + L2 header bytes) + * consumes more memory and CPU cycles compared to non-TSO packet. + * To minimize USB buffer size, limit the maximum TSO packet size to + * 16KB - (L2 header + padding). + */ +#define AXGE_TSO_MAXSIZE (16384 - 16) +#define AXGE_TSO_BUFSIZE 16384 +/* Arbitrary one since driver have to copy entire mbuf chain. */ +#define AXGE_TSO_TXSEGS ((AXGE_TSO_MAXSIZE / MCLBYTES) + 2) +#define AXGE_TSO_SEGSIZE 4096