Hi all. This diff adds hardware 802.1q VLAN tagging support to vr(4) (just tag/untag, it doesn't do anything with the VLAN CAM filters).
As far as I know, the capability is only in the VT6105M Rhine III chip which is used, amongst other places, in the pcengines ALIX machines. If anyone has a vr(4) (any revision), especially if you use VLANs, I'd be interested to hear if this works for you. I'm not a kernel guy, so it's quite possible I made some basic mistake in there somewhere, but it works for me on an ALIX: vr0 at pci0 dev 9 function 0 "VIA VT6105M RhineIII" rev 0x96: irq 10, address 00:0d:b9:7e:21:5c ukphy0 at vr0 phy 1: Generic IEEE 802.3u media interface, rev. 3: OUI 0x004063, model 0x0034 $ ifconfig vr0 hwfeatures | grep hwfeatures hwfeatures=8037<CSUM_IPv4,CSUM_TCPv4,CSUM_UDPv4,VLAN_MTU,VLAN_HWTAGGING,WOL> As an aside, I found what I believe is a latent bug in the existing driver. When it's done with the TX descriptor, it sets the "owner" bit to tell the chip that it's good to go: #define VR_TXSTAT_OWN 0x80000000 #define VR_TXOWN(x) x->vr_ptr->vr_status VR_TXOWN(cur_tx) = htole32(VR_TXSTAT_OWN); which expands to: cur_tx->vr_ptr->vr_status = htole32(0x80000000); which zeroes the 31 least significant bits in the first word of the TX descriptor. Sadly, this includes the VLAN ID, which caused me some head-scratching trying to figure out why my tagged frames were all in VLAN 0. On the plus side, nothing else currently uses those bits, so right now it doesn't matter. My questions is: why hide that behind a macro? or at least, if you're going to, why not go the whole hog and put the entire thing in there? #define VR_TXOWN(x) ((x)->vr_ptr->vr_status |= htole32(VR_TXSTAT_OWN)) Thanks. Index: sys/dev/pci/if_vr.c =================================================================== RCS file: /cvs/src/sys/dev/pci/if_vr.c,v retrieving revision 1.114 diff -u -p -r1.114 if_vr.c --- sys/dev/pci/if_vr.c 30 Jan 2012 09:11:30 -0000 1.114 +++ sys/dev/pci/if_vr.c 26 Sep 2012 23:13:45 -0000 @@ -62,6 +62,7 @@ */ #include "bpfilter.h" +#include "vlan.h" #include <sys/param.h> #include <sys/systm.h> @@ -83,6 +84,11 @@ #include <net/if_dl.h> #include <net/if_media.h> +#if NVLAN > 0 +#include <net/if_types.h> +#include <net/if_vlan_var.h> +#endif + #if NBPFILTER > 0 #include <net/bpf.h> #endif @@ -633,6 +639,12 @@ vr_attach(struct device *parent, struct if (sc->vr_quirks & VR_Q_CSUM) ifp->if_capabilities |= IFCAP_CSUM_IPv4|IFCAP_CSUM_TCPv4| IFCAP_CSUM_UDPv4; + +#if NVLAN > 0 + /* if the hardware can do VLAN tagging, say so. */ + if (sc->vr_quirks & VR_Q_HWTAG) + ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING; +#endif #ifndef SMALL_KERNEL if (sc->vr_revid >= REV_ID_VT3065_A) { ifp->if_capabilities |= IFCAP_WOL; @@ -880,6 +892,23 @@ vr_rxeof(struct vr_softc *sc) cur_rx->vr_map->dm_mapsize, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->sc_dmat, cur_rx->vr_map); +#if NVLAN > 0 + /* + * If there's a tagged packet, the 802.1q header will be at the + * 4-byte boundary following the CRC. There will be 2 bytes + * TPID (0x8100) and 2 bytes TCI (including VLAN ID). + * This isn't in the data sheet. + */ + if (rxctl & VR_RXCTL_TAG) { + u_int16_t vtag; + int offset = ((total_len + 3) & ~3) + 2; + + bcopy(m->m_data + offset, &vtag, sizeof(vtag)); + m->m_pkthdr.ether_vtag = ntohs(vtag); + m->m_flags |= M_VLANTAG; + } +#endif + /* * The VIA Rhine chip includes the CRC with every * received frame, and there's no way to turn this @@ -1002,7 +1031,7 @@ vr_txeof(struct vr_softc *sc) sc->vr_flags |= VR_F_RESTART; break; } - VR_TXOWN(cur_tx) = htole32(VR_TXSTAT_OWN); + VR_TXOWN(cur_tx) |= htole32(VR_TXSTAT_OWN); CSR_WRITE_4(sc, VR_TXADDR, cur_tx->vr_paddr); break; } @@ -1170,6 +1199,16 @@ vr_encap(struct vr_softc *sc, struct vr_ struct mbuf *m_new = NULL; u_int32_t vr_flags = 0, vr_status = 0; +#if NVLAN > 0 + /* Tell chip to insert VLAN tag if needed. */ + if (m_head->m_flags & M_VLANTAG) { + u_int32_t vtag = m_head->m_pkthdr.ether_vtag; + vtag = (vtag << VR_TXSTAT_PQSHIFT) & VR_TXSTAT_PQMASK; + vr_status |= vtag; + vr_flags |= VR_TXCTL_INSERTTAG; + } +#endif + if (sc->vr_quirks & VR_Q_CSUM) { if (m_head->m_pkthdr.csum_flags & M_IPV4_CSUM_OUT) vr_flags |= VR_TXCTL_IPCSUM; @@ -1274,7 +1313,7 @@ vr_start(struct ifnet *ifp) break; } - VR_TXOWN(cur_tx) = htole32(VR_TXSTAT_OWN); + VR_TXOWN(cur_tx) |= htole32(VR_TXSTAT_OWN); #if NBPFILTER > 0 /* @@ -1394,6 +1433,11 @@ vr_init(void *xsc) * Program promiscuous mode and multicast filters. */ vr_iff(sc); + +#if NVLAN > 0 + if (ifp->if_capabilities & IFCAP_VLAN_HWTAGGING) + VR_SETBIT(sc, VR_TXCFG, VR_TXCFG_TXTAGEN); +#endif /* * Load the address of the RX list. Index: sys/dev/pci/if_vrreg.h =================================================================== RCS file: /cvs/src/sys/dev/pci/if_vrreg.h,v retrieving revision 1.30 diff -u -p -r1.30 if_vrreg.h --- sys/dev/pci/if_vrreg.h 5 Jan 2012 19:08:25 -0000 1.30 +++ sys/dev/pci/if_vrreg.h 26 Sep 2012 23:13:45 -0000 @@ -83,6 +83,9 @@ #define VR_MPA_CNT 0x7C #define VR_CRC_CNT 0x7E #define VR_STICKHW 0x83 +#define VR_CAMMASK 0x88 /* 4 bytes */ +#define VR_CAMCTRL 0x92 +#define VR_CAMADDR 0x93 /* Misc Registers */ #define VR_MISC_CR1 0x81 @@ -342,6 +345,14 @@ #define VR_BCR1_VLANFILT_ENB 0x80 /* 6105M */ /* + * CAM Control registers (VT6105M) + */ +#define VR_CAMCTRL_WREN 0x01 +#define VR_CAMCTRL_VCAMSEL 0x02 +#define VR_CAMCTRL_WRITE 0x04 +#define VR_CAMCTRL_READ 0x08 + +/* * Rhine TX/RX list structure. */ @@ -404,6 +415,7 @@ struct vr_desc { #define VR_TXSTAT_ERRSUM 0x00008000 #define VR_TXSTAT_PQMASK 0x7FFF0000 #define VR_TXSTAT_OWN 0x80000000 +#define VR_TXSTAT_PQSHIFT 16 #define VR_TXCTL_BUFLEN 0x000007FF #define VR_TXCTL_BUFLEN_EXT 0x00007800 -- Darren Tucker (dtucker at zip.com.au) GPG key 8FF4FA69 / D9A3 86E9 7EEE AF4B B2D4 37C9 C982 80C7 8FF4 FA69 Good judgement comes with experience. Unfortunately, the experience usually comes from bad judgement.