> Date: Sat, 28 May 2022 15:08:49 +1000 > From: David Gwynne <da...@gwynne.id.au> > > the espressobin is the least worst thing ive settled on. it's not > too expensive, it has a case, it has multiple interfaces, and > kettenis and patrick have already worked on the platform stuff. the > only problem with it was that the interfaces are on a switch chip > supported by mvsw, and mvsw configures the switch as a switch. this > makes it hard to use as a router.
So I have a Turris MOX that is based on the same SoC. It is modular and I have a switch module that integrates the same Marvell switch chip. That is the hardware I used to develop the current mvsw(4) driver. The interesting thing is that the Turris MOX actually has two mvneta(4) interface; mvneta0 is connected to a normal Ethernet PHY, whereas mvneta1 is connected to the switch chip. In this setup it makes sense to use the mvneta0 port as a "WAN" port and the swithc ports as "LAN" ports and have let the hardware do the switching between the "LAN" ports. > this diff makes mvsw present its ports as individual interfaces, > and configures the switch so each external port can only talk to > the host system. it's basically an ethernet mux instead of a switch. > > this is inspired by the dsa framework in linux, which in turn seems > to have been inspired by and written for the link street family of > switch chips thats in the espressobin. > > like dsa, mvsw now tries to understand the different roles of switch > ports and their topology. most ports are externally accessible, and one > is wired up internally to mvneta. the fdt describes these roles and > relationships, and now mvsw does different things depending on these > roles. > > the internal port wired up to mvneta is a "cpu" interface. mvsw now > takes over mvneta, much like how aggr or trunk takes over an interface, > and unconditionally configures the switch port to tag packets so the > kernel can know which mvsw port a packet was received on. > > the externally accessible ports are enumerated and attached as separate > network interfaces in the kernel, and configured on the switch so that > they can only talk to the cpu/mvneta switch port. packets sent out > a kernel mvsport interface are tagged and sent out mvneta instead, > kind of like vlan interfaces. How does configuring IP addresses work with your diff? Do you still configure and address on the mvneta(4) interface? Or do you configure addresses on the individual mvsport(4) interfaces? And how does one configure additional VLANs in this scenario? > there is no longer any config that allows mvsw switch ports to > communicate directly, if you want to bridge them now you have to > do it in software with veb (or bridge or tpmr). So this would "break" my current setup on the Turris MOX. Now actually I currently only use a single port on the switch, but my plan was to connect some of my equipment directly to the MOX instead of my 24-port switch. I just didn't get around to doing that. > presenting the ports as separate interfaces allows them to fit in > nicely with the existing kernel functionality. in particular, it > lets us wire up ifmedia and mii so you can see the phy on each port, > which in turn will let ifconfig operate on them as you'd expect. That *is* nice. > the diff below is very rough, but i think it's far enough along > that it demonstrates where im going. if the ideas are acceptable, > i'd like to commit it and hack on it in the tree. I would like to understand things a bit better. And make sure this doesn't break things to badly for me. That Turris MOX is my firewall and wireless access point, so breaking things would be bad for OpenBSD development ;). Unfortunately, it also means it isn't trivial for me to test things. > there's some future work to be done. the mdio bus probably needs a lock > around it. i have no idea how mii/ifmedia works, so some pointers > there would be good. The mvmdio(4) driver already uses a mutex to control access to the bus. > it would be nice to hack on another switch chip at some > point. recent banana pi routers might be a good candidate for > that. they have mediatek or realtek switches on them from what i can > tell, and they link to the doco for them. it might be possible to > factor the "port interface" code out for all these and just have the > switch drivers provide glue for them. The "Banana Pi BPI-R2 Pro Router" might be the most interesting one. That one is based on a Rockchip RK3568 SoC for which we already have some support in the tree and hardware in the hands of other developers (me and patrick). That one integrates a MediaTek MT7531BE switch chip. The other Banana Pi router boards are based on SoCs that we don't support. > it would be interesting to teach veb and vlan how to offload switching > to a switch chip for port interfaces. there's a bunch of functionality > we offer in our virtual bridges that wouldn't work on a hardware > chip (eg, pf, bridge rules, ipsec, etc), but veb already special > cases vport interfaces so special casing these ports could work > too. > > however, having routed interfaces is more useful to me than bridging > right now. > > here's an espressobin dmesg. > > OpenBSD 7.1-current (comp) #56: Fri May 27 17:49:35 AEST 2022 > d...@ebin.lan.animata.net:/home/dlg/comp > real mem = 1048031232 (999MB) > avail mem = 983277568 (937MB) > random: good seed from bootblocks > mainbus0 at root: Globalscale Marvell ESPRESSOBin Board V7 > psci0 at mainbus0: PSCI 1.1, SMCCC 1.2 > cpu0 at mainbus0 mpidr 0: ARM Cortex-A53 r0p4 > cpu0: 32KB 64b/line 2-way L1 VIPT I-cache, 32KB 64b/line 4-way L1 D-cache > cpu0: 256KB 64b/line 16-way L2 cache > cpu0: CRC32,SHA2,SHA1,AES+PMULL,ASID16 > cpu1 at mainbus0 mpidr 1: ARM Cortex-A53 r0p4 > cpu1: 32KB 64b/line 2-way L1 VIPT I-cache, 32KB 64b/line 4-way L1 D-cache > cpu1: 256KB 64b/line 16-way L2 cache > cpu1: CRC32,SHA2,SHA1,AES+PMULL,ASID16 > efi0 at mainbus0: UEFI 2.9 > efi0: Das U-Boot rev 0x20220400 > apm0 at mainbus0 > agtimer0 at mainbus0: 12500 kHz > "pmu" at mainbus0 not configured > simplebus0 at mainbus0: "soc" > simplebus1 at simplebus0: "internal-regs" > syscon0 at simplebus1: "system-controller" > syscon1 at simplebus1: "avs" > mvclock0 at simplebus1 > mvclock1 at simplebus1 > mvclock2 at simplebus1 > mvpinctrl0 at simplebus1 > syscon2 at simplebus1: "syscon" > mvpinctrl1 at simplebus1 > syscon3 at simplebus1: "system-controller" > syscon4 at simplebus1: "system-controller" > agintc0 at simplebus1 shift 4:3 nirq 224 nredist 2 ipi: 0, 1: > "interrupt-controller" > mvdog0 at simplebus1 > mvspi0 at simplebus1 > mvuart0 at simplebus1 > "phy" at simplebus1 not configured > mvneta0 at simplebus1 > mvneta0: Ethernet address f0:ad:4e:1c:08:42 > mvmdio0 at simplebus1: "mdio" > mvsw0 at mvmdio0 phy 1: 88E6341 rev 0 > xhci0 at simplebus1, xHCI 1.0 > usb0 at xhci0: USB revision 3.0 > uhub0 at usb0 configuration 1 interface 0 "Generic xHCI root hub" rev > 3.00/1.00 addr 1 > "phy" at simplebus1 not configured > ehci0 at simplebus1 > usb1 at ehci0: USB revision 2.0 > uhub1 at usb1 configuration 1 interface 0 "Generic EHCI root hub" rev > 2.00/1.00 addr 1 > "phy" at simplebus1 not configured > "xor" at simplebus1 not configured > "crypto" at simplebus1 not configured > "mailbox" at simplebus1 not configured > sdhc0 at simplebus1 > sdhc0: SDHC 3.0, 400 MHz base clock > sdmmc0 at sdhc0: 4-bit, sd high-speed, mmc high-speed, ddr52, dma > ahci0 at simplebus1: AHCI 1.3 > scsibus0 at ahci0: 32 targets > mvkpcie0 at simplebus0 > mvkpcie0: timeout > "firmware" at mainbus0 not configured > "regulator" at mainbus0 not configured > gpioleds0 at mainbus0: "led2" > mvsw0: mvneta0 at port 0 > mvsport0 at mvsw0 port 1: address 00:51:82:11:22:03 > eephy0 at mvsport0 phy 17: 88E1000 1 Gigabit PHY, rev. 1 > mvsport1 at mvsw0 port 2: address 00:51:82:11:22:02 > eephy1 at mvsport1 phy 18: 88E1000 1 Gigabit PHY, rev. 1 > mvsport2 at mvsw0 port 3: address 00:51:82:11:22:01 > eephy2 at mvsport2 phy 19: 88E1000 1 Gigabit PHY, rev. 1 > scsibus1 at sdmmc0: 2 targets, initiator 0 > sd0 at scsibus1 targ 1 lun 0: <SD/MMC, SH32G, 0080> removable > sd0: 30436MB, 512 bytes/sector, 62333952 sectors > vscsi0 at root > scsibus2 at vscsi0: 256 targets > softraid0 at root > scsibus3 at softraid0: 256 targets > root on sd0a (d1ff8372fcd69512.a) swap on sd0b dump on sd0b > > here's the interesting bits of the fdt: > > Node 0x16a0 > name: 'ethernet' > local-mac-address: f0ad4e1c.0842 > compatible: 'marvell,armada-3700-neta' > reg: 00030000.00004000 > interrupts: 00000000.0000002a.00000004 > clocks: 00000009.00000008 > status: 'okay' > pinctrl-names: 'default' > pinctrl-0: 0000000a.0000000b > phy-mode: 'rgmii-id' > phandle: 0000000c > > Node 0x1794 > name: 'fixed-link' > speed: 000003e8 > full-duplex: > > Node 0x17c8 > name: 'mdio' > #address-cells: 00000001 > #size-cells: 00000000 > compatible: 'marvell,orion-mdio' > reg: 00032004.00000004 > > Node 0x182c > name: 'switch0' > compatible: 'marvell,mv88e6085' > #address-cells: 00000001 > #size-cells: 00000000 > reg: 00000001 > dsa,member: 00000000.00000000 > > Node 0x18a0 > name: 'ports' > #address-cells: 00000001 > #size-cells: 00000000 > > Node 0x18cc > name: 'port' > reg: 00000000 > label: 'cpu' > ethernet: 0000000c > phy-mode: 'rgmii-id' > > Node 0x1920 > name: 'fixed-link' > speed: 000003e8 > full-duplex: > > Node 0x1954 > name: 'port' > local-mac-address: 00518211.2203 > reg: 00000001 > label: 'lan1' > phy-handle: 0000000d > > Node 0x19ac > name: 'port' > local-mac-address: 00518211.2202 > reg: 00000002 > label: 'lan0' > phy-handle: 0000000e > > Node 0x1a04 > name: 'port' > local-mac-address: 00518211.2201 > reg: 00000003 > label: 'wan' > phy-handle: 0000000f > > Node 0x1a5c > name: 'mdio' > #address-cells: 00000001 > #size-cells: 00000000 > > Node 0x1a88 > name: 'switch0phy0' > reg: 00000011 > phandle: 0000000d > > Node 0x1ac0 > name: 'switch0phy1' > reg: 00000012 > phandle: 0000000e > > Node 0x1af8 > name: 'switch0phy2' > reg: 00000013 > phandle: 0000000f > > and here's the diff: > > Index: arch/arm64/conf/GENERIC > =================================================================== > RCS file: /cvs/src/sys/arch/arm64/conf/GENERIC,v > retrieving revision 1.228 > diff -u -p -r1.228 GENERIC > --- arch/arm64/conf/GENERIC 31 Mar 2022 14:44:49 -0000 1.228 > +++ arch/arm64/conf/GENERIC 27 May 2022 23:55:17 -0000 > @@ -259,6 +259,7 @@ mvrtc* at fdt? > mvspi* at fdt? > moxtet* at spi? > mvsw* at fdt? > +mvsport* at mvsw? > mvtemp* at fdt? > mvuart* at fdt? > sfp* at fdt? > Index: dev/fdt/files.fdt > =================================================================== > RCS file: /cvs/src/sys/dev/fdt/files.fdt,v > retrieving revision 1.162 > diff -u -p -r1.162 files.fdt > --- dev/fdt/files.fdt 30 Jan 2022 21:40:50 -0000 1.162 > +++ dev/fdt/files.fdt 27 May 2022 23:55:19 -0000 > @@ -462,9 +462,11 @@ device mvspi: spi > attach mvspi at fdt > file dev/fdt/mvspi.c mvspi > > -device mvsw > +device mvsw {} > attach mvsw at fdt > -file dev/fdt/mvsw.c mvsw > +device mvsport: ether, ifnet, mii, ifmedia > +attach mvsport at mvsw > +file dev/fdt/mvsw.c mvsw | mvsport > > device mvtemp > attach mvtemp at fdt > Index: dev/fdt/if_mvneta.c > =================================================================== > RCS file: /cvs/src/sys/dev/fdt/if_mvneta.c,v > retrieving revision 1.17 > diff -u -p -r1.17 if_mvneta.c > --- dev/fdt/if_mvneta.c 24 Oct 2021 17:52:26 -0000 1.17 > +++ dev/fdt/if_mvneta.c 27 May 2022 23:55:19 -0000 > @@ -170,6 +170,8 @@ struct mvneta_softc { > int sc_link; > int sc_sfp; > int sc_node; > + > + struct if_device sc_ifd; > }; > > > @@ -781,6 +783,10 @@ mvneta_attach_deferred(struct device *se > */ > if_attach(ifp); > ether_ifattach(ifp); > + > + sc->sc_ifd.if_node = sc->sc_node; > + sc->sc_ifd.if_ifp = ifp; > + if_register(&sc->sc_ifd); > } > > void > Index: dev/fdt/mvsw.c > =================================================================== > RCS file: /cvs/src/sys/dev/fdt/mvsw.c,v > retrieving revision 1.5 > diff -u -p -r1.5 mvsw.c > --- dev/fdt/mvsw.c 6 Apr 2022 18:59:28 -0000 1.5 > +++ dev/fdt/mvsw.c 27 May 2022 23:55:19 -0000 > @@ -15,10 +15,19 @@ > * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > */ > > +#include "bpfilter.h" > + > #include <sys/param.h> > #include <sys/systm.h> > #include <sys/device.h> > +#include <sys/kernel.h> > +#include <sys/malloc.h> > +#include <sys/mbuf.h> > +#include <sys/queue.h> > +#include <sys/socket.h> > +#include <sys/sockio.h> > #include <sys/timeout.h> > +#include <sys/percpu.h> > > #include <machine/bus.h> > #include <machine/fdt.h> > @@ -27,7 +36,31 @@ > #include <dev/ofw/ofw_misc.h> > #include <dev/ofw/fdt.h> > > +#include <net/if.h> > +#include <net/if_media.h> > +#include <net/if_types.h> > + > #include <dev/mii/mii.h> > +#include <dev/mii/miivar.h> > + > +#include <netinet/in.h> > +#include <netinet/ip.h> > +#include <netinet/if_ether.h> > + > +#include <netinet6/in6_var.h> > +#include <netinet/ip6.h> > + > +#include <crypto/siphash.h> /* if_trunk.h uses siphash bits */ > +#include <net/if_trunk.h> > + > +#if NBPFILTER > 0 > +#include <net/bpf.h> > +#endif > + > +#define MVSW_MAX_PORTS 11 > + > +#define ETHERTYPE_MVSW_ETAG ETHERTYPE_802_EX1 > +#define ETHERTYPE_MVSW_DEFAULT 0x9000 /* who cares? */ > > /* Registers */ > > @@ -49,34 +82,303 @@ > #define MVSW_SMI_TIMEOUT 1600 > > /* Switch registers */ > -#define MVSW_PORT(x) (0x10 + (x)) > -#define MVSW_G2 0x1c > +#define MVSW_PORT(x) (0x10 + (x)) /* Port */ > +#define MVSW_G1 0x1b /* Global1 */ > +#define MVSW_G2 0x1c /* Global2 */ > > +/* > + * Port registers */ > #define MVSW_PORT_SWITCHID 0x03 > #define MVSW_PORT_SWITCHID_PROD_MASK 0xfff0 > #define MVSW_PORT_SWITCHID_PROD_88E6141 0x3400 > #define MVSW_PORT_SWITCHID_PROD_88E6341 0x3410 > #define MVSW_PORT_SWITCHID_REV_MASK 0x000f > -#define MVSW_PORT_CTRL 0x04 > -#define MVSW_PORT_CTRL_STATE_MASK 0x0003 > -#define MVSW_PORT_CTRL_STATE_FORWARD 0x0003 > +#define MVSW_PORT_CTRL0 0x04 > +#define MVSW_PORT_CTRL0_STATE_MASK (0x3 << 0) > +#define MVSW_PORT_CTRL0_STATE_DISABLED (0x0 << 0) > +#define MVSW_PORT_CTRL0_STATE_BLOCKING (0x1 << 0) > +#define MVSW_PORT_CTRL0_STATE_LEARNING (0x2 << 0) > +#define MVSW_PORT_CTRL0_STATE_FORWARD (0x3 << 0) > +#define MVSW_PORT_CTRL0_EGRESS_FLOOD_MCAST (0x1 << 2) > +#define MVSW_PORT_CTRL0_EGRESS_FLOOD_UCAST (0x1 << 3) > +#define MVSW_PORT_CTRL0_TAG_IF_BOTH (0x1 << 6) > +#define MVSW_PORT_CTRL0_VLAN_TUNNEL (0x1 << 7) > +#define MVSW_PORT_CTRL0_FRAME_MODE_MASK (0x3 << 8) > +#define MVSW_PORT_CTRL0_FRAME_MODE_NORMAL (0x0 << 8) > +#define MVSW_PORT_CTRL0_FRAME_MODE_TAG (0x1 << 8) > +#define MVSW_PORT_CTRL0_FRAME_MODE_PROVIDER (0x2 << 8) > +#define MVSW_PORT_CTRL0_FRAME_MODE_ETAG (0x3 << 8) > +#define MVSW_PORT_CTRL0_IGMP_MLD_SNOOP (0x1 << 10) > +#define MVSW_PORT_CTRL0_HEADER (0x1 << 11) > +#define MVSW_PORT_CTRL0_EGRESS_MODE_MASK (0x3 << 12) > +#define MVSW_PORT_CTRL0_EGRESS_MODE_UNMODIFIED (0x0 << 12) > +#define MVSW_PORT_CTRL0_EGRESS_MODE_UNTAGGED (0x1 << 12) > +#define MVSW_PORT_CTRL0_EGRESS_MODE_TAGGED (0x2 << 12) > +#define MVSW_PORT_CTRL0_EGRESS_MODE_ETAG (0x3 << 12) > +#define MVSW_PORT_CTRL0_SAFILTER_MASK (0x3 << 14) > +#define MVSW_PORT_CTRL0_SAFILTER_DROP_ON_LOCK (0x1 << 14) > +#define MVSW_PORT_CTRL0_SAFILTER_DROP_ON_UNLOCK (0x2 << 14) > +#define MVSW_PORT_CTRL0_SAFILTER_DROP_TO_CPU (0x3 << 14) > + > +#define MVSW_PORT_CTRL1 0x05 > +#define MVSW_PORT_CTRL1_FID_HI_SHIFT 0 > +#define MVSW_PORT_CTRL1_FID_HI_MASK 0xff > +#define MVSW_PORT_CTRL1_TRUNK_ID_SHIFT 8 > +#define MVSW_PORT_CTRL1_TRUNK_ID_MASK 0x0f > +#define MVSW_PORT_CTRL1_TRUNK_PORT (0x1 << 14) > +#define MVSW_PORT_CTRL1_MESSAGE_PORT (0x1 << 15) > + > +#define MVSW_PORT_BASED_VLAN 0x06 > +#define MVSW_PORT_BASED_VLAN_FID_LO_SHIFT 0 > +#define MVSW_PORT_BASED_VLAN_FID_LO_MASK 0 > + > +/* Default Port VLAN */ > +#define MVSW_PORT_DEFAULT_VLAN 0x07 > +#define MVSW_PORT_DEVAULT_VLAN_VID_SHIFT 0 > +#define MVSW_PORT_DEVAULT_VLAN_VID_MASK 0xfff > + > +/* Port Control 2 */ > +#define MVSW_PORT_CTRL2 0x08 > +#define MVSW_PORT_CTRL2_JUMBO_MODE_MASK (0x3 << 12) > +#define MVSW_PORT_CTRL2_JUMBO_MODE_1522 (0x0 << 12) > +#define MVSW_PORT_CTRL2_JUMBO_MODE_2048 (0x1 << 12) > +#define MVSW_PORT_CTRL2_JUMBO_MODE_10240 (0x2 << 12) > +#define MVSW_PORT_CTRL2_8021Q_MODE_MASK (0x3 << 10) > +#define MVSW_PORT_CTRL2_8021Q_MODE_DISABLED (0x0 << 10) > +#define MVSW_PORT_CTRL2_8021Q_MODE_FALLBACK (0x1 << 10) > +#define MVSW_PORT_CTRL2_8021Q_MODE_CHECK (0x2 << 10) > +#define MVSW_PORT_CTRL2_8021Q_MODE_SECURE (0x3 << 10) > +#define MVSW_PORT_CTRL2_DISCARD_TAGGED (0x1 << 9) > +#define MVSW_PORT_CTRL2_DISCARD_UNTAGGED (0x1 << 8) > +#define MVSW_PORT_CTRL2_MAP_DA (0x1 << 7) > + > +/* Port Association Vector */ > +#define MVSW_PORT_ASSOC_VECTOR 0x0b > +#define MVSW_PORT_ASSOC_VECTOR_HOLD_AT_1 (0x1 << 15) > +#define MVSW_PORT_ASSOC_VECTOR_INT_AGE_OUT (0x1 << 14) > +#define MVSW_PORT_ASSOC_VECTOR_LOCKED_PORT (0x1 << 13) > +#define MVSW_PORT_ASSOC_VECTOR_IGNORE_WRONG (0x1 << 12) > +#define MVSW_PORT_ASSOC_VECTOR_REFRESH_LOCKED (0x1 << 11) > +/* i think low bits are a bitmap of relevant ports */ > + > +#define MVSW_PORT_ETH_TYPE 0x0f > + > +/* > + * Global1 registers > + */ > + > +/* ATU FID */ > +#define MVSW_G1_ATU_FID 0x01 > + > +#define MVSW_G1_VTU_OP 0x05 > +#define MVSW_G1_VTU_OP_BUSY (0x1 << 15) > +#define MVSW_G1_VTU_OP_MASK (0x7 << 12) > +#define MVSW_G1_VTU_OP_FLUSH_ALL (0x1 << 12) > +#define MVSW_G1_VTU_OP_NOOP (0x2 << 12) > +#define MVSW_G1_VTU_OP_VTU_LOAD_PURGE (0x3 << 12) > +#define MVSW_G1_VTU_OP_VTU_GET_NEXT (0x4 << 12) > +#define MVSW_G1_VTU_OP_STU_LOAD_PURGE (0x5 << 12) > +#define MVSW_G1_VTU_OP_STU_GET_NEXT (0x6 << 12) > +#define MVSW_G1_VTU_OP_GET_CLR_VIOLATION (0x7 << 12) > +#define MVSW_G1_VTU_OP_MEMBER_VIOLATION (0x1 << 6) > +#define MVSW_G1_VTU_OP_MISS_VIOLATION (0x1 << 5) > +#define MVSW_G1_VTU_OP_SPID_MASK (0xf << 0) > + > +/* ATU Control */ > +#define MVSW_G1_ATU_CTRL 0x0a > +#define MVSW_G1_ATU_CTRL_LEARN2ALL (0x1 << 3) > + > +/* ATU Operation */ > +#define MVSW_G1_ATU_OP 0x0a > +#define MVSW_G1_ATU_OP_BUSY (0x1 << 15) > +#define MVSW_G1_ATU_OP_MASK (0x7 << 12) > +#define MVSW_G1_ATU_OP_NOOP (0x0 << 12) > +#define MVSW_G1_ATU_OP_FLUSH_MOVE_ALL (0x1 << 12) > +#define MVSW_G1_ATU_OP_FLUSH_MOVE_NON_STATIC (0x2 << 12) > +#define MVSW_G1_ATU_OP_LOAD_DB (0x3 << 12) > +#define MVSW_G1_ATU_OP_GET_NEXT_DB (0x4 << 12) > +#define MVSW_G1_ATU_OP_FLUSH_MOVE_ALL_DB (0x5 << 12) > +#define MVSW_G1_ATU_OP_FLUSH_MOVE_NON_STATIC_DB (0x6 << 12) > +#define MVSW_G1_ATU_OP_GET_CLR_VIOLATION (0x7 << 12) > +#define MVSW_G1_ATU_OP_AGE_OUT_VIOLATION (0x1 << 7) > +#define MVSW_G1_ATU_OP_MEMBER_VIOLATION (0x1 << 6) > +#define MVSW_G1_ATU_OP_MISS_VIOLATION (0x1 << 5) > +#define MVSW_G1_ATU_OP_FULL_VIOLATION (0x1 << 4) > + > +/* ATU Data */ > +#define MVSW_G1_ATU_DATA 0x0c > +#define MVSW_G1_ATU_DATA_TRUNK (0x1 << 15) > +#define MVSW_G1_ATU_DATA_TRUNK_ID_MASK (0xf << 4) > +#define MVSW_G1_ATU_DATA_PORT_VECTOR_MASK (0x3ff << 4) > +#define MVSW_G1_ATU_DATA_STATE_MASK (0xf << 0) > +#define MVSW_G1_ATU_DATA_STATE_UC_UNUSED (0x0 << 0) > +#define MVSW_G1_ATU_DATA_STATE_UC_AGE_1_OLDEST (0x1 << 0) > +#define MVSW_G1_ATU_DATA_STATE_UC_AGE_2 (0x2 << 0) > +#define MVSW_G1_ATU_DATA_STATE_UC_AGE_3 (0x3 << 0) > +#define MVSW_G1_ATU_DATA_STATE_UC_AGE_4 (0x4 << 0) > +#define MVSW_G1_ATU_DATA_STATE_UC_AGE_5 (0x5 << 0) > +#define MVSW_G1_ATU_DATA_STATE_UC_AGE_6 (0x6 << 0) > +#define MVSW_G1_ATU_DATA_STATE_UC_AGE_7_NEWEST (0x7 << 0) > +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC_POLICY (0x8 << 0) > +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC_POLICY_PO (0x9 << 0) > +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC_AVB_NRL (0xa << 0) > +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC_AVB_NRL_PO (0xb << 0) > +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC_DA_MGMT (0xc << 0) > +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC_DA_MGMT_PO (0xd << 0) > +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC (0xe << 0) > +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC_PO (0xf << 0) > +#define MVSW_G1_ATU_DATA_STATE_MC_UNUSED (0x0 << 0) > +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC_POLICY (0x4 << 0) > +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC_AVB_NRL (0x5 << 0) > +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC_DA_MGMT (0x6 << 0) > +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC (0x7 << 0) > +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC_POLICY_PO (0xc << 0) > +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC_AVB_NRL_PO (0xd << 0) > +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC_DA_MGMT_PO (0xe << 0) > +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC_PO (0xf << 0) > + > +#define MVSW_G1_ATU_MAC_BASE 0x0d > +#define MVSW_G1_ATU_MAC_01 (MVSW_G1_ATU_MAC_BASE + 0) > +#define MVSW_G1_ATU_MAC_23 (MVSW_G1_ATU_MAC_BASE + 1) > +#define MVSW_G1_ATU_MAC_45 (MVSW_G1_ATU_MAC_BASE + 2) > + > +/* Monitor & MGMT Control */ > +#define MVSW_G1_MONITOR_MGMT_CTL 0x1a > +#define MVSW_G1_MONITOR_MGMT_CTL_UPDATE (0x1 << 15) > +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_MASK (0x3f << 8) > +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_0180C200000XLO (0x00 << 8) > +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_0180C200000XHI (0x01 << 8) > +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_0180C200002XLO (0x02 << 8) > +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_0180C200002XHI (0x03 << 8) > +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_INGRESS_DEST (0x20 << 8) > +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_EGRESS_DEST (0x21 << 8) > +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_CPU_DEST (0x30 << 8) > +#define MVSW_G1_MONITOR_MGMT_CTL_DATA_SHIFT 0 > +#define MVSW_G1_MONITOR_MGMT_CTL_DATA_MASK 0xff > +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_CPU_DEST_MGMTPRI 0xe0 > + > +/* Control2 */ > +#define MVSW_G1_CTRL2 0x1c > +#define MVSW_G1_CTRL2_DEVICE_NUMBER_SHIFT 0 > +#define MVSW_G1_CTRL2_DEVICE_NUMBER_MASK 0x1f > +#define MVSW_G1_CTRL2_RMU_MODE_MASK (0x7 << 8) > +#define MVSW_G1_CTRL2_RMU_MODE_PORT_0 (0x0 << 8) > +#define MVSW_G1_CTRL2_RMU_MODE_PORT_1 (0x1 << 8) > +#define MVSW_G1_CTRL2_RMU_MODE_ALL_DSA (0x6 << 8) > +#define MVSW_G1_CTRL2_RMU_MODE_DISABLED (0x7 << 8) > + > +/* > + * Global2 registers > + */ > + > +/* Trunk Mask Table */ > +#define MVSW_G2_TRUNK_MASK 0x07 > +#define MVSW_G2_TRUNK_MASK_UPDATE (0x1 << 15) > +#define MVSW_G2_TRUNK_MASK_SHIFT 12 > +#define MVSW_G2_TRUNK_MASK_COUNT 8 /* 0x0 to 0x7 */ > +#define MVSW_G2_TRUNK_MASK_HASH (0x1 << 11) > +/* low bits are a bitmap of ports in the trunk i think */ > + > +/* Trunk Mapping Table */ > +#define MVSW_G2_TRUNK_MAPPING 0x08 > +#define MVSW_G2_TRUNK_MAPPING_UPDATE (0x1 << 15) > +#define MVSW_G2_TRUNK_MAPPING_ID_SHIFT 11 > +#define MVSW_G2_TRUNK_MAPPING_ID_COUNT 16 /* 0x0 to > 0xf */ > +/* low bits are a bitmap of ports in the trunk i think */ > + > +/* Ingress Rate Command */ > +#define MVSW_G2_IRL_CMD 0x09 > +#define MVSW_G2_IRL_CMD_BUSY (0x1 << 15) > +#define MVSW_G2_IRL_CMD_OP_MASK (0x7 << 12) > +#define MVSW_G2_IRL_CMD_OP_NOOP (0x0 << 12) > +#define MVSW_G2_IRL_CMD_OP_INIT_ALL (0x1 << 12) > +#define MVSW_G2_IRL_CMD_OP_INIT_RES (0x2 << 12) > +#define MVSW_G2_IRL_CMD_OP_WRITE_REG (0x3 << 12) > +#define MVSW_G2_IRL_CMD_OP_READ_REG (0x4 << 12) > +#define MVSW_G2_IRL_CMD_PORT_SHIFT 8 > +#define MVSW_G2_IRL_CMD_PORT_MASK 0xf > +#define MVSW_G2_IRL_CMD_RES_MASK (0x7 << 5) > +#define MVSW_G2_IRL_CMD_REG_MASK (0xf << 0) > + > +/* Ingress Rate Data */ > +#define MVSW_G2_IRL_DATA 0x0a > + > #define MVSW_G2_SMI_PHY_CMD 0x18 > #define MVSW_G2_SMI_PHY_DATA 0x19 > > +/* Misc */ > +#define MVSW_G2_MISC 0x1d > +#define MVSW_G2_MISC_5BIT_PORT (0x1 << 14) > + > /* SERDES registers */ > #define MVSW_SERDES(x) (0x10 + (x)) > #define MVSW_SERDES_BMCR (0x2000 + MII_BMCR) > > +struct mvsw_tag { > + uint16_t tag0; > +#define MVSW_TAG_MODE_SHIFT 14 > +#define MVSW_TAG_MODE_MASK (0x3 << MVSW_TAG_MODE_SHIFT) > +#define MVSW_TAG_MODE_TO_CPU (0x0 << MVSW_TAG_MODE_SHIFT) > +#define MVSW_TAG_MODE_FROM_CPU (0x1 << MVSW_TAG_MODE_SHIFT) > +#define MVSW_TAG_MODE_TO_SNIFFER (0x2 << MVSW_TAG_MODE_SHIFT) > +#define MVSW_TAG_MODE_TAG (0x3 << MVSW_TAG_MODE_SHIFT) > + > +#define MVSW_TAG_IEEE (1 << 13) > + > +#define MVSW_TAG_SWITCH_SHIFT 8 > +#define MVSW_TAG_SWITCH_MASK 0x1f > + > +#define MVSW_TAG_PORT_SHIFT 3 > +#define MVSW_TAG_PORT_MASK 0x1f > + > + uint16_t tag1; > +}; > + > +struct mvsw_etag { > + uint16_t reserved; > + uint16_t tag0; > + uint16_t tag1; > +}; > + > /* XXX #include <dev/mii/mdio.h> */ > #define MDIO_MMD_PHYXS 4 > > +/* > + * The driver. > + */ > + > +struct mvsw_port { > + int p_port; > + struct mvsw_softc *p_softc; > + struct ifnet *p_ifp0; > + > + int (*p_ioctl)(struct ifnet *, u_long, caddr_t); > + void (*p_input)(struct ifnet *, struct mbuf *); > + int (*p_output)(struct ifnet *, struct mbuf *, struct sockaddr *, > + struct rtentry *); > + > + TAILQ_ENTRY(mvsw_port) p_entry; > +}; > +TAILQ_HEAD(mvsw_ports, mvsw_port); > + > +struct mvsport_softc; > + > struct mvsw_softc { > - struct device sc_dev; > + struct device sc_dev; > + > + int sc_node; > + struct mii_bus *sc_mdio; > + int sc_reg; > > - struct mii_bus *sc_mdio; > - int sc_reg; > + unsigned int sc_nports; > + struct mvsport_softc *sc_ports[MVSW_MAX_PORTS]; > + struct mvsw_ports sc_cpus; > + > + caddr_t sc_bpf; > }; > > +#define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname) > + > int mvsw_match(struct device *, void *, void *); > void mvsw_attach(struct device *, struct device *, void *); > > @@ -88,6 +390,26 @@ struct cfdriver mvsw_cd = { > NULL, "mvsw", DV_DULL > }; > > +struct mvsw_defer { > + struct task d_task; > + struct mvsw_softc *d_sc; > +}; > + > +static void mvsw_attach_deferred(void *); > + > +static void mvsw_attach_cpu(struct mvsw_softc *, int, uint32_t); > +static void mvsw_config_cpu(struct mvsw_softc *, struct mvsw_port *); > + > +static int mvsw_p_ioctl(struct ifnet *, u_long, caddr_t); > +static void mvsw_p_input(struct ifnet *, struct mbuf *); > +static int mvsw_p_output(struct ifnet *, struct mbuf *, > + struct sockaddr *, struct rtentry *); > + > +static int mvsw_print(void *, const char *); > + > +static struct mbuf * > + mvsport_input(struct mvsport_softc *, struct mbuf *); > + > int mvsw_smi_read(struct mvsw_softc *, int, int); > void mvsw_smi_write(struct mvsw_softc *, int, int, int); > int mvsw_phy_read(struct mvsw_softc *, int, int); > @@ -95,7 +417,25 @@ void mvsw_phy_write(struct mvsw_softc *, > int mvsw_serdes_read(struct mvsw_softc *, int, int, int); > void mvsw_serdes_write(struct mvsw_softc *, int, int, int, int); > > -void mvsw_port_enable(struct mvsw_softc *, int); > +static int mvsw_wait(struct mvsw_softc *, int, int, uint16_t, uint16_t, > + const char *); > + > +#define mvsw_vtu_wait(_sc) \ > + mvsw_wait((_sc), MVSW_G1, MVSW_G1_VTU_OP, \ > + MVSW_G1_VTU_OP_BUSY, 0, "mvswvtu") > + > +#define mvsw_atu_wait(_sc) \ > + mvsw_wait((_sc), MVSW_G1, MVSW_G1_ATU_OP, \ > + MVSW_G1_ATU_OP_BUSY, 0, "mvswatu") > + > +#define mvsw_irl_wait(_sc) \ > + mvsw_wait((_sc), MVSW_G2, MVSW_G2_IRL_CMD, \ > + MVSW_G2_IRL_CMD_BUSY, 0, "mvswirl") > + > +static int mvsw_vtu_op(struct mvsw_softc *, uint16_t); > +static int mvsw_atu_op(struct mvsw_softc *, uint16_t, uint16_t, uint16_t); > +static int mvsw_irl_op(struct mvsw_softc *, uint16_t); > + > void mvsw_phy_enable(struct mvsw_softc *, int); > void mvsw_serdes_enable(struct mvsw_softc *, int); > > @@ -112,15 +452,18 @@ mvsw_attach(struct device *parent, struc > { > struct mvsw_softc *sc = (struct mvsw_softc *)self; > struct fdt_attach_args *faa = aux; > - int ports, port, node; > - uint32_t phy; > - uint16_t swid; > + uint16_t r; > + struct mvsw_defer *d; > + > + TAILQ_INIT(&sc->sc_cpus); > + sc->sc_nports = nitems(sc->sc_ports); > > if (faa->fa_nreg < 1) { > printf(": no registers\n"); > return; > } > > + sc->sc_node = faa->fa_node; > sc->sc_reg = faa->fa_reg[0].addr; > printf(" phy %d", sc->sc_reg); > > @@ -130,24 +473,136 @@ mvsw_attach(struct device *parent, struc > return; > } > > - swid = mvsw_smi_read(sc, MVSW_PORT(0), MVSW_PORT_SWITCHID); > - switch (swid & MVSW_PORT_SWITCHID_PROD_MASK) { > + r = mvsw_smi_read(sc, MVSW_PORT(0), MVSW_PORT_SWITCHID); > + switch (r & MVSW_PORT_SWITCHID_PROD_MASK) { > case MVSW_PORT_SWITCHID_PROD_88E6141: > + sc->sc_nports = 6; > printf(": 88E6141"); > break; > case MVSW_PORT_SWITCHID_PROD_88E6341: > + sc->sc_nports = 6; > printf(": 88E6341"); > break; > default: > printf(": unknown product 0x%04x\n", > - swid & MVSW_PORT_SWITCHID_PROD_MASK); > + r & MVSW_PORT_SWITCHID_PROD_MASK); > return; > } > - printf(" rev %d\n", swid & MVSW_PORT_SWITCHID_REV_MASK); > + printf(" rev %d\n", r & MVSW_PORT_SWITCHID_REV_MASK); > > - ports = OF_getnodebyname(faa->fa_node, "ports"); > - if (ports == 0) > + if (sc->sc_dev.dv_unit & ~MVSW_G1_CTRL2_DEVICE_NUMBER_MASK) { > + printf("%s: too many switches\n", DEVNAME(sc)); > return; > + } > + > + /* > + * wait until the cpu port is (probably) attached to wire things up. > + */ > + > + d = malloc(sizeof(*d), M_TEMP, M_WAITOK); > + task_set(&d->d_task, mvsw_attach_deferred, d); > + d->d_sc = sc; > + > + config_pending_incr(); > + task_add(systq, &d->d_task); > +} > + > +static void > +mvsw_attach_deferred(void *arg) > +{ > + struct mvsw_defer *d = arg; > + struct mvsw_softc *sc = d->d_sc; > + int ports, port, node, i; > + uint32_t phy, phandle; > + uint16_t r; > + struct mvsw_port *p; > + > + free(d, M_TEMP, sizeof(*d)); > + > + for (port = 0; port < sc->sc_nports; port++) { > + /* start with all ports disabled */ > + r = mvsw_smi_read(sc, MVSW_PORT(port), MVSW_PORT_CTRL0); > + CLR(r, MVSW_PORT_CTRL0_STATE_MASK); > + SET(r, MVSW_PORT_CTRL0_STATE_DISABLED); > + mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_CTRL0, r); > + > + r = mvsw_smi_read(sc, MVSW_PORT(port), MVSW_PORT_CTRL1); > + CLR(r, MVSW_PORT_CTRL1_MESSAGE_PORT); > + mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_CTRL1, r); > + > + mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_DEFAULT_VLAN, 0); > + > + /* reset ingress rate limiting (IRL) */ > + if (mvsw_irl_op(sc, MVSW_G2_IRL_CMD_OP_INIT_ALL | > + (port << MVSW_G2_IRL_CMD_PORT_SHIFT)) == -1) { > + printf("%s: unable to reset ingress rate limiting " > + "on port %u\n", DEVNAME(sc), port); > + /* we can carry on */ > + } > + } > + > + /* flush the vlan translation unit */ > + if (mvsw_vtu_wait(sc) == -1) { > + printf("%s: VLAN Translation Unit busy\n", DEVNAME(sc)); > + goto done; > + } > + > + if (mvsw_vtu_op(sc, MVSW_G1_VTU_OP_FLUSH_ALL) == -1) { > + printf("%s: VLAN Translation Unit flush timeout\n", > + DEVNAME(sc)); > + goto done; > + } > + > + /* clear 5 bit port use in port vlan table (PVT) */ > + r = mvsw_smi_read(sc, MVSW_G2, MVSW_G2_MISC); > + CLR(r, MVSW_G2_MISC_5BIT_PORT); > + mvsw_smi_write(sc, MVSW_G2, MVSW_G2_MISC, r); > + > + /* XXX PVT clear/reset/setup? */ > + > + /* flush the address translation unit */ > + if (mvsw_atu_wait(sc) == -1) { > + printf("%s: Address Translation Unit busy\n", DEVNAME(sc)); > + goto done; > + } > + > + if (mvsw_atu_op(sc, 0, MVSW_G1_ATU_OP_FLUSH_MOVE_ALL, 0) == -1) { > + printf("%s: Address Translation Unit flush timeout\n", > + DEVNAME(sc)); > + goto done; > + } > + > + /* XXX clear priority overrite table */ > + > + r = mvsw_smi_read(sc, MVSW_G1, MVSW_G1_CTRL2); > + /* set device number */ > + CLR(r, MVSW_G1_CTRL2_DEVICE_NUMBER_MASK << > + MVSW_G1_CTRL2_DEVICE_NUMBER_SHIFT); > + SET(r, sc->sc_dev.dv_unit << > + MVSW_G1_CTRL2_DEVICE_NUMBER_SHIFT); > + > + /* disable remote management */ > + CLR(r, MVSW_G1_CTRL2_RMU_MODE_MASK); > + SET(r, MVSW_G1_CTRL2_RMU_MODE_DISABLED); > + mvsw_smi_write(sc, MVSW_G1, MVSW_G1_CTRL2, r); > + > + /* clear trunk setup */ > + for (i = 0; i < MVSW_G2_TRUNK_MASK_COUNT; i++) { > +// mvsw_smi_write(sc, MVSW_G2, MVSW_G2_TRUNK_MASK, > +// MVSW_G2_TRUNK_MASK_UPDATE | > +// (i << MVSW_G2_TRUNK_MASK_SHIFT) | /* clear bitmap */ 0); > + } > + for (i = 0; i < MVSW_G2_TRUNK_MAPPING_ID_COUNT; i++) { > +// mvsw_smi_write(sc, MVSW_G2, MVSW_G2_TRUNK_MAPPING, > +// MVSW_G2_TRUNK_MAPPING_UPDATE | > +// (i << MVSW_G2_TRUNK_MAPPING_ID_SHIFT) | > +// /* clear bitmap */ 0); > + } > + > + ports = OF_getnodebyname(sc->sc_node, "ports"); > + if (ports == 0) > + goto done; > + > for (port = OF_child(ports); port; port = OF_peer(port)) { > phy = OF_getpropint(port, "phy-handle", 0); > node = OF_getnodebyphandle(phy); > @@ -156,8 +611,274 @@ mvsw_attach(struct device *parent, struc > else > mvsw_serdes_enable(sc, port); > > - mvsw_port_enable(sc, port); > + phandle = OF_getpropint(port, "ethernet", 0); > + if (phandle != 0) > + mvsw_attach_cpu(sc, port, phandle); > + else { > + uint32_t reg = OF_getpropint(port, "reg", > + MVSW_MAX_PORTS); > + struct device *child; > + > + if (reg < sc->sc_nports) { > + child = config_found(&sc->sc_dev, &port, > + mvsw_print); > + sc->sc_ports[reg] = > + (struct mvsport_softc *)child; > + } > + } > + } > + > + p = TAILQ_FIRST(&sc->sc_cpus); > + if (p == NULL) { > + printf("%s: no CPU ports found\n", DEVNAME(sc)); > + goto done; > + } > + > + mvsw_config_cpu(sc, p); > + > + r = 0x1 << p->p_port; > + for (port = 0; port < sc->sc_nports; port++) { > + if (sc->sc_ports[port] == NULL) > + continue; > + > + mvsw_smi_write(sc, MVSW_PORT(port), > + MVSW_PORT_BASED_VLAN, r); > } > + > +done: > + config_pending_decr(); > +} > + > +static void > +mvsw_attach_cpu(struct mvsw_softc *sc, int node, uint32_t phandle) > +{ > + struct ifnet *ifp0; > + struct arpcom *ac0; > + struct mvsw_port *p; > + int port; > + uint16_t r; > + > + port = OF_getpropint(node, "reg", -1); > + if (port == -1) { > + printf("%s: can't find cpu interface port number\n", > + DEVNAME(sc)); > + return; > + } > + > + ifp0 = if_byphandle(phandle); > + if (ifp0 == NULL) { > + printf("%s: unable to find cpu interface on port %u\n", > + DEVNAME(sc), port); > + return; > + } > + > + if (ifp0->if_type != IFT_ETHER) { > + printf("%s: unsupported type of cpu interface on port %u\n", > + DEVNAME(sc), port); > + return; > + } > + > + printf("%s: %s at port %u\n", DEVNAME(sc), ifp0->if_xname, port); > + > + NET_LOCK(); > + ac0 = (struct arpcom *)ifp0; > + if (ac0->ac_trunkport != NULL) { > + printf("%s: cpu interface %s is busy\n", > + DEVNAME(sc), ifp0->if_xname); > + NET_UNLOCK(); > + return; > + } > + > + p = malloc(sizeof(*p), M_DEVBUF, M_WAITOK); > + > + p->p_softc = sc; > + p->p_ifp0 = ifp0; > + p->p_port = port; > + p->p_ioctl = ifp0->if_ioctl; > + p->p_input = ifp0->if_input; > + p->p_output = ifp0->if_output; > + > + TAILQ_INSERT_TAIL(&sc->sc_cpus, p, p_entry); > + > + if (ifpromisc(ifp0, 1) != 0) > + printf("%s: %s promisc error\n", DEVNAME(sc), ifp0->if_xname); > + > + ac0->ac_trunkport = p; > + /* membar_producer()? */ > + ifp0->if_ioctl = mvsw_p_ioctl; > + ifp0->if_input = mvsw_p_input; > + ifp0->if_output = mvsw_p_output; > + NET_UNLOCK(); > + > + /* Enable port. */ > + r = mvsw_smi_read(sc, MVSW_PORT(port), MVSW_PORT_CTRL0); > + CLR(r, MVSW_PORT_CTRL0_STATE_MASK); > + SET(r, MVSW_PORT_CTRL0_STATE_FORWARD); > + CLR(r, MVSW_PORT_CTRL0_FRAME_MODE_MASK); > + SET(r, MVSW_PORT_CTRL0_FRAME_MODE_ETAG); > + CLR(r, MVSW_PORT_CTRL0_EGRESS_MODE_MASK); > + SET(r, MVSW_PORT_CTRL0_EGRESS_MODE_ETAG); > + SET(r, MVSW_PORT_CTRL0_EGRESS_FLOOD_UCAST | > + MVSW_PORT_CTRL0_EGRESS_FLOOD_MCAST); > + mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_CTRL0, r); > + > + r = mvsw_smi_read(sc, MVSW_PORT(port), MVSW_PORT_CTRL2); > + CLR(r, MVSW_PORT_CTRL2_MAP_DA); > + CLR(r, MVSW_PORT_CTRL2_JUMBO_MODE_MASK); > + SET(r, MVSW_PORT_CTRL2_JUMBO_MODE_10240); > + CLR(r, MVSW_PORT_CTRL2_8021Q_MODE_MASK); > + SET(r, MVSW_PORT_CTRL2_8021Q_MODE_DISABLED); > + CLR(r, MVSW_PORT_CTRL2_DISCARD_TAGGED); > + CLR(r, MVSW_PORT_CTRL2_DISCARD_UNTAGGED); > + mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_CTRL2, r); > + > + mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_ASSOC_VECTOR, > + 0x1 << port); > + > + mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_ETH_TYPE, > + ETHERTYPE_MVSW_ETAG); > +} > + > +static void > +mvsw_config_cpu(struct mvsw_softc *sc, struct mvsw_port *p) > +{ > + int port = p->p_port; > + uint16_t r; > + > + /* tell the switch this is the cpu port */ > + r = MVSW_G1_MONITOR_MGMT_CTL_UPDATE | > + MVSW_G1_MONITOR_MGMT_CTL_PTR_CPU_DEST | > + (port | MVSW_G1_MONITOR_MGMT_CTL_PTR_CPU_DEST_MGMTPRI); > + mvsw_smi_write(sc, MVSW_G2, MVSW_G1_MONITOR_MGMT_CTL, r); > + > + r = MVSW_G1_MONITOR_MGMT_CTL_UPDATE | > + MVSW_G1_MONITOR_MGMT_CTL_PTR_INGRESS_DEST | > + port; > + mvsw_smi_write(sc, MVSW_G2, MVSW_G1_MONITOR_MGMT_CTL, r); > + > + r = MVSW_G1_MONITOR_MGMT_CTL_UPDATE | > + MVSW_G1_MONITOR_MGMT_CTL_PTR_EGRESS_DEST | > + port; > + mvsw_smi_write(sc, MVSW_G2, MVSW_G1_MONITOR_MGMT_CTL, r); > +} > + > +static int > +mvsw_p_ioctl(struct ifnet *ifp0, u_long cmd, caddr_t data) > +{ > + struct arpcom *ac0 = (struct arpcom *)ifp0; > + struct mvsw_port *p = ac0->ac_trunkport; > + int error = 0; > + > + switch (cmd) { > + case SIOCGTRUNKPORT: { > + struct trunk_reqport *rp = (struct trunk_reqport *)data; > + struct mvsw_softc *sc = p->p_softc; > + > + if (strncmp(rp->rp_ifname, rp->rp_portname, > + sizeof(rp->rp_ifname)) != 0) > + return (EINVAL); > + > + (void)strlcpy(rp->rp_ifname, DEVNAME(sc), > + sizeof(rp->rp_ifname)); > + break; > + } > + > + case SIOCSIFLLADDR: > + error = EBUSY; > + break; > + > + default: > + error = (*p->p_ioctl)(ifp0, cmd, data); > + break; > + } > + > + return (error); > +} > + > +static int > +mvsw_p_output(struct ifnet *ifp0, struct mbuf *m, struct sockaddr *dst, > + struct rtentry *rt) > +{ > + struct arpcom *ac0 = (struct arpcom *)ifp0; > + struct mvsw_port *p = ac0->ac_trunkport; > + > +#if 0 > + /* restrict transmission to bpf only */ > + if (m_tag_find(m, PACKET_TAG_DLT, NULL) == NULL) { > + m_freem(m); > + return (EBUSY); > + } > +#endif > + > + return ((*p->p_output)(ifp0, m, dst, rt)); > +} > + > +static void > +mvsw_p_input(struct ifnet *ifp0, struct mbuf *m) > +{ > + struct arpcom *ac0 = (struct arpcom *)ifp0; > + struct mvsw_port *p = ac0->ac_trunkport; > + struct mvsw_softc *sc = p->p_softc; > + struct ether_header *eh; > + struct mvsw_etag *etag; > + int hlen = sizeof(*eh) + sizeof(*etag); > + int diff = hlen - offsetof(struct ether_header, ether_type); > + uint16_t tag0; > + struct mvsport_softc *psc; > + > + eh = mtod(m, struct ether_header *); > + if (eh->ether_type != htons(ETHERTYPE_MVSW_ETAG)) > + goto drop; > + > + if (m->m_len < hlen) { > + m = m_pullup(m, hlen); > + if (m == NULL) { > + /* drop++ */ > + return; > + } > + > + eh = mtod(m, struct ether_header *); > + } > + > + etag = (struct mvsw_etag *)(eh + 1); > + tag0 = bemtoh16(&etag->tag0); > + > + int port = (tag0 >> MVSW_TAG_PORT_SHIFT) & MVSW_TAG_PORT_MASK; > + if (port >= sc->sc_nports) > + goto drop; > + > + psc = sc->sc_ports[port]; > + if (psc == NULL) > + goto drop; > + > + memmove(mtod(m, caddr_t) + diff, mtod(m, caddr_t), > + offsetof(struct ether_header, ether_type)); > + m_adj(m, diff); > + > + m = mvsport_input(psc, m); > + if (m == NULL) > + return; > + > + ether_input(ifp0, m); > + return; > + > +drop: > + m_freem(m); > +} > + > +static int > +mvsw_print(void *aux, const char *pnp) > +{ > + int node = *(int *)aux; > + int port; > + > + if (pnp != NULL) > + printf("\"port\" at %s", pnp); > + > + port = OF_getpropint(node, "reg", 0); > + printf(" port %d", port); > + > + return (UNCONF); > } > > static inline int > @@ -219,6 +940,53 @@ mvsw_smi_write(struct mvsw_softc *sc, in > mvsw_smi_wait(sc); > } > > +static int > +mvsw_wait(struct mvsw_softc *sc, int phy, int reg, uint16_t mask, uint16_t v, > + const char *wmesg) > +{ > + unsigned int i; > + uint16_t r; > + > + for (i = 0; i < 16; i++) { > + r = mvsw_smi_read(sc, phy, reg); > + if ((r & mask) == v) > + return (0); > + > + tsleep_nsec(&sc->sc_mdio, PPAUSE, wmesg, 1500000); > + } > + > + return (-1); > +} > + > +static int > +mvsw_vtu_op(struct mvsw_softc *sc, uint16_t op) > +{ > + mvsw_smi_write(sc, MVSW_G1, MVSW_G1_VTU_OP, > + MVSW_G1_VTU_OP_BUSY | op); > + > + return (mvsw_vtu_wait(sc)); > +} > + > +static int > +mvsw_atu_op(struct mvsw_softc *sc, uint16_t fid, uint16_t op, uint16_t data) > +{ > + mvsw_smi_write(sc, MVSW_G1, MVSW_G1_ATU_DATA, data); > + mvsw_smi_write(sc, MVSW_G1, MVSW_G1_ATU_FID, fid); > + mvsw_smi_write(sc, MVSW_G1, MVSW_G1_VTU_OP, > + MVSW_G1_VTU_OP_BUSY | op); > + > + return (mvsw_atu_wait(sc)); > +} > + > +static int > +mvsw_irl_op(struct mvsw_softc *sc, uint16_t op) > +{ > + mvsw_smi_write(sc, MVSW_G2, MVSW_G2_IRL_CMD, > + MVSW_G2_IRL_CMD_BUSY | op); > + > + return (mvsw_irl_wait(sc)); > +} > + > int > mvsw_phy_wait(struct mvsw_softc *sc) > { > @@ -308,23 +1076,6 @@ mvsw_serdes_write(struct mvsw_softc *sc, > } > > void > -mvsw_port_enable(struct mvsw_softc *sc, int node) > -{ > - uint16_t val; > - int port; > - > - port = OF_getpropint(node, "reg", -1); > - if (port == -1) > - return; > - > - /* Enable port. */ > - val = mvsw_smi_read(sc, MVSW_PORT(port), MVSW_PORT_CTRL); > - val &= ~MVSW_PORT_CTRL_STATE_MASK; > - val |= MVSW_PORT_CTRL_STATE_FORWARD; > - mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_CTRL, val); > -} > - > -void > mvsw_phy_enable(struct mvsw_softc *sc, int node) > { > uint16_t val; > @@ -357,4 +1108,352 @@ mvsw_serdes_enable(struct mvsw_softc *sc > val |= BMCR_AUTOEN; > mvsw_serdes_write(sc, MVSW_SERDES(port), > MDIO_MMD_PHYXS, MVSW_SERDES_BMCR, val); > +} > + > +struct mvsport_softc { > + struct device sc_dev; > + int sc_node; > + int sc_port; > + > + struct arpcom sc_ac; > +#define sc_if sc_ac.ac_if > + > + struct mii_bus *sc_mdio; > + struct mii_data sc_mii; > +#define sc_ifmedia sc_mii.mii_media > + struct mvsw_softc *sc_parent; > + > + struct if_device sc_ifd; > +}; > + > +static int mvsport_match(struct device *, void *, void *); > +static void mvsport_attach(struct device *, struct device *, void *); > + > +const struct cfattach mvsport_ca = { > + sizeof (struct mvsport_softc), mvsport_match, mvsport_attach > +}; > + > +struct cfdriver mvsport_cd = { > + NULL, "mvsport", DV_DULL > +}; > + > +static void mvsport_start(struct ifqueue *); > +static int mvsport_ioctl(struct ifnet *, u_long, caddr_t); > + > +static int mvsport_up(struct mvsport_softc *); > +static int mvsport_down(struct mvsport_softc *); > + > +static int mvsport_miibus_readreg(struct device *, int, int); > +static void mvsport_miibus_writereg(struct device *, int, int, int); > +static void mvsport_miibus_statch(struct device *); > + > +static int mvsport_media_upd(struct ifnet *); > +static void mvsport_media_sts(struct ifnet *, struct ifmediareq *); > + > +static uint16_t mvsport_smi_read(struct mvsport_softc *, int); > +static void mvsport_smi_write(struct mvsport_softc *, int, uint16_t); > + > +static int > +mvsport_match(struct device *parent, void *match, void *aux) > +{ > + int node = *(int *)aux; > + char buf[32]; > + > + if (OF_getprop(node, "status", buf, sizeof(buf)) > 0 && > + strcmp(buf, "disabled") == 0) > + return (0); > + > + return (1); > +} > + > +static void > +mvsport_attach(struct device *parent, struct device *self, void *aux) > +{ > + struct mvsport_softc *sc = (struct mvsport_softc *)self; > + int node = *(int *)aux; > + struct ifnet *ifp; > + int phyph, phynode; > + int port; > + uint16_t r; > + > + sc->sc_node = node; > + sc->sc_port = port = OF_getpropint(node, "reg", -1); > + > + ifp = &sc->sc_if; > + (void)strlcpy(ifp->if_xname, DEVNAME(sc), sizeof(ifp->if_xname)); > + ifp->if_softc = sc; > + ifp->if_hardmtu = ETHER_MAX_HARDMTU_LEN; > + ifp->if_ioctl = mvsport_ioctl; > + ifp->if_qstart = mvsport_start; > + ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST; > + ifp->if_xflags = IFXF_CLONED | IFXF_MPSAFE; > + > + OF_getprop(node, "label", > + ifp->if_description, sizeof(ifp->if_description)); > + > + if (OF_getprop(node, "local-mac-address", &sc->sc_ac.ac_enaddr, > + sizeof(sc->sc_ac.ac_enaddr)) != sizeof(sc->sc_ac.ac_enaddr)) > + ether_fakeaddr(ifp); > + > + printf(": address %s\n", ether_sprintf(sc->sc_ac.ac_enaddr)); > + > + phyph = OF_getpropint(node, "phy-handle", 0); > + phynode = OF_getnodebyphandle(phyph); > + if (phynode != 0) { > + int phyloc = OF_getpropint(phynode, "reg", 0); > + struct mii_data *mii = &sc->sc_mii; > + > + mii->mii_ifp = ifp; > + mii->mii_readreg = mvsport_miibus_readreg; > + mii->mii_writereg = mvsport_miibus_writereg; > + mii->mii_statchg = mvsport_miibus_statch; > + > + ifmedia_init(&sc->sc_ifmedia, 0, > + mvsport_media_upd, mvsport_media_sts); > + > + mii_attach(self, mii, 0xffffffff, phyloc, MII_OFFSET_ANY, 0); > + if (LIST_FIRST(&mii->mii_phys) == NULL) { > + printf("%s: no PHY found!\n", DEVNAME(sc)); > + ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_MANUAL, > + 0, NULL); > + ifmedia_set(&sc->sc_ifmedia, IFM_ETHER|IFM_MANUAL); > + } else > + ifmedia_set(&sc->sc_ifmedia, IFM_ETHER|IFM_AUTO); > + } > + > + if_counters_alloc(ifp); > + if_attach(ifp); > + ether_ifattach(ifp); > + > + sc->sc_ifd.if_node = sc->sc_node; > + sc->sc_ifd.if_ifp = ifp; > + if_register(&sc->sc_ifd); > + > + /* Configure port. */ > + r = mvsport_smi_read(sc, MVSW_PORT_CTRL0); > + CLR(r, MVSW_PORT_CTRL0_STATE_MASK); > + SET(r, MVSW_PORT_CTRL0_STATE_DISABLED); > + CLR(r, MVSW_PORT_CTRL0_FRAME_MODE_MASK); > + SET(r, MVSW_PORT_CTRL0_FRAME_MODE_NORMAL); > + CLR(r, MVSW_PORT_CTRL0_EGRESS_MODE_MASK); > + SET(r, MVSW_PORT_CTRL0_EGRESS_MODE_UNMODIFIED); > + SET(r, MVSW_PORT_CTRL0_EGRESS_FLOOD_UCAST | > + MVSW_PORT_CTRL0_EGRESS_FLOOD_MCAST); > + mvsport_smi_write(sc, MVSW_PORT_CTRL0, r); > + > + r = mvsport_smi_read(sc, MVSW_PORT_CTRL2); > + //SET(r, MVSW_PORT_CTRL2_MAP_DA); > + CLR(r, MVSW_PORT_CTRL2_MAP_DA); > + CLR(r, MVSW_PORT_CTRL2_JUMBO_MODE_MASK); > + SET(r, MVSW_PORT_CTRL2_JUMBO_MODE_10240); > + CLR(r, MVSW_PORT_CTRL2_8021Q_MODE_MASK); > + SET(r, MVSW_PORT_CTRL2_8021Q_MODE_DISABLED); > + CLR(r, MVSW_PORT_CTRL2_DISCARD_TAGGED); > + CLR(r, MVSW_PORT_CTRL2_DISCARD_UNTAGGED); > + mvsport_smi_write(sc, MVSW_PORT_CTRL2, r); > + > + mvsport_smi_write(sc, MVSW_PORT_ASSOC_VECTOR, 0); > + > + mvsport_smi_write(sc, MVSW_PORT_ETH_TYPE, ETHERTYPE_MVSW_DEFAULT); > +} > + > +static uint16_t > +mvsport_smi_read(struct mvsport_softc *sc, int reg) > +{ > + return mvsw_smi_read((struct mvsw_softc *)sc->sc_dev.dv_parent, > + MVSW_PORT(sc->sc_port), reg); > +} > + > +static void > +mvsport_smi_write(struct mvsport_softc *sc, int reg, uint16_t r) > +{ > + mvsw_smi_write((struct mvsw_softc *)sc->sc_dev.dv_parent, > + MVSW_PORT(sc->sc_port), reg, r); > +} > + > +static int > +mvsport_miibus_readreg(struct device *dev, int phy, int reg) > +{ > + struct device *parent = dev->dv_parent; > + struct mvsw_softc *sc = (struct mvsw_softc *)parent; > + > + return (mvsw_phy_read(sc, phy, reg)); > +} > + > +static void > +mvsport_miibus_writereg(struct device *dev, int phy, int reg, int val) > +{ > + struct device *parent = dev->dv_parent; > + struct mvsw_softc *sc = (struct mvsw_softc *)parent; > + > + return (mvsw_phy_write(sc, phy, reg, val)); > +} > + > +static void > +mvsport_miibus_statch(struct device *dev) > +{ > + printf("%s: %s[%u]\n", dev->dv_xname, __func__, __LINE__); > +} > + > +static int > +mvsport_media_upd(struct ifnet *ifp) > +{ > + struct mvsport_softc *sc = ifp->if_softc; > + > + if (LIST_FIRST(&sc->sc_mii.mii_phys)) > + mii_mediachg(&sc->sc_mii); > + > + return (0); > +} > + > +static void > +mvsport_media_sts(struct ifnet *ifp, struct ifmediareq *ifmr) > +{ > + struct mvsport_softc *sc = ifp->if_softc; > + > + if (LIST_FIRST(&sc->sc_mii.mii_phys)) { > + mii_pollstat(&sc->sc_mii); > + ifmr->ifm_active = sc->sc_mii.mii_media_active; > + ifmr->ifm_status = sc->sc_mii.mii_media_status; > + } > +} > + > +static int > +mvsport_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) > +{ > + struct mvsport_softc *sc = ifp->if_softc; > + struct ifreq *ifr = (struct ifreq *)data; > + int error = 0; > + > + switch (cmd) { > + case SIOCSIFFLAGS: > + if (ISSET(ifp->if_flags, IFF_UP)) { > + if (!ISSET(ifp->if_flags, IFF_RUNNING)) > + error = mvsport_up(sc); > + } else { > + if (ISSET(ifp->if_flags, IFF_RUNNING)) > + error = mvsport_down(sc); > + } > + break; > + > + case SIOCSIFMEDIA: > + case SIOCGIFMEDIA: > + error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, cmd); > + break; > + > + default: > + error = ether_ioctl(ifp, &sc->sc_ac, cmd, data); > + break; > + } > + > + if (error == ENETRESET) { > + /* hardware doesnt need reprogramming */ > + error = 0; > + } > + > + return (error); > +} > + > +static int > +mvsport_up(struct mvsport_softc *sc) > +{ > + struct ifnet *ifp = &sc->sc_if; > + uint16_t r; > + > + r = mvsport_smi_read(sc, MVSW_PORT_CTRL0); > + CLR(r, MVSW_PORT_CTRL0_STATE_MASK); > + SET(r, MVSW_PORT_CTRL0_STATE_FORWARD); > + mvsport_smi_write(sc, MVSW_PORT_CTRL0, r); > + > + SET(ifp->if_flags, IFF_RUNNING); > + > + return (0); > +} > + > +static int > +mvsport_down(struct mvsport_softc *sc) > +{ > + struct ifnet *ifp = &sc->sc_if; > + uint16_t r; > + > + CLR(ifp->if_flags, IFF_RUNNING); > + > + r = mvsport_smi_read(sc, MVSW_PORT_CTRL0); > + CLR(r, MVSW_PORT_CTRL0_STATE_MASK); > + SET(r, MVSW_PORT_CTRL0_STATE_DISABLED); > + mvsport_smi_write(sc, MVSW_PORT_CTRL0, r); > + > + return (0); > +} > + > +static void > +mvsport_start(struct ifqueue *ifq) > +{ > + struct ifnet *ifp = ifq->ifq_if; > + struct mvsport_softc *sc = ifp->if_softc; > + struct mbuf *m; > + struct ether_header *eh; > + struct mvsw_etag *etag; > + const int hlen = sizeof(*eh) + sizeof(*etag); > + const int offs = offsetof(struct ether_header, ether_type); > + const int diff = hlen - offs; > + int errors = 0; > + > + struct mvsw_softc *ssc = (struct mvsw_softc *)sc->sc_dev.dv_parent; > + struct mvsw_port *p = TAILQ_FIRST(&ssc->sc_cpus); > + > + if (p == NULL) { > + ifq_purge(ifq); > + return; > + } > + > + while ((m = ifq_dequeue(ifq)) != NULL) { > +#if NBPFILTER > 0 > + { > + caddr_t if_bpf = ifp->if_bpf; > + if (if_bpf) > + bpf_mtap_ether(if_bpf, m, BPF_DIRECTION_OUT); > + } > +#endif > + > + m = m_prepend(m, diff, M_NOWAIT); > + if (m == NULL) { > + errors++; > + continue; > + } > + if (m->m_len < hlen) { > + m = m_pullup(m, hlen); > + if (m == NULL) { > + errors++; > + continue; > + } > + } > + > + memmove(mtod(m, caddr_t), mtod(m, caddr_t) + diff, offs); > + eh = mtod(m, struct ether_header *); > + eh->ether_type = htons(ETHERTYPE_MVSW_ETAG); > + > + etag = (struct mvsw_etag *)(eh + 1); > + etag->reserved = htons(0); > + etag->tag0 = htons(MVSW_TAG_MODE_FROM_CPU | > + (ssc->sc_dev.dv_unit << MVSW_TAG_SWITCH_SHIFT) | > + (sc->sc_port << MVSW_TAG_PORT_SHIFT)); > + etag->tag1 = htons(0); > + > + if (if_enqueue(p->p_ifp0, m) != 0) > + errors++; > + } > + > + if (errors) > + counters_add(ifp->if_counters, ifc_oerrors, errors); > +} > + > +static struct mbuf * > +mvsport_input(struct mvsport_softc *sc, struct mbuf *m) > +{ > + struct ifnet *ifp = &sc->sc_if; > + > + if_vinput(ifp, m); > + > + return (NULL); > } > Index: dev/ofw/ofw_misc.c > =================================================================== > RCS file: /cvs/src/sys/dev/ofw/ofw_misc.c,v > retrieving revision 1.36 > diff -u -p -r1.36 ofw_misc.c > --- dev/ofw/ofw_misc.c 25 Mar 2022 15:49:29 -0000 1.36 > +++ dev/ofw/ofw_misc.c 27 May 2022 23:55:19 -0000 > @@ -119,6 +119,46 @@ regmap_read_4(struct regmap *rm, bus_siz > return bus_space_read_4(rm->rm_tag, rm->rm_handle, offset); > } > > +/* > + * Network interface support. > + */ > + > +LIST_HEAD(, if_device) if_devices = > + LIST_HEAD_INITIALIZER(if_devices); > + > +void > +if_register(struct if_device *ifd) > +{ > + ifd->if_phandle = OF_getpropint(ifd->if_node, "phandle", 0); > + > + LIST_INSERT_HEAD(&if_devices, ifd, if_list); > +} > + > +struct ifnet * > +if_bynode(int node) > +{ > + struct if_device *ifd; > + > + LIST_FOREACH(ifd, &if_devices, if_list) { > + if (ifd->if_node == node) > + return (ifd->if_ifp); > + } > + > + return (NULL); > +} > + > +struct ifnet * > +if_byphandle(uint32_t phandle) > +{ > + struct if_device *ifd; > + > + LIST_FOREACH(ifd, &if_devices, if_list) { > + if (ifd->if_phandle == phandle) > + return (ifd->if_ifp); > + } > + > + return (NULL); > +} > > /* > * PHY support. > Index: dev/ofw/ofw_misc.h > =================================================================== > RCS file: /cvs/src/sys/dev/ofw/ofw_misc.h,v > retrieving revision 1.24 > diff -u -p -r1.24 ofw_misc.h > --- dev/ofw/ofw_misc.h 21 Mar 2022 19:22:40 -0000 1.24 > +++ dev/ofw/ofw_misc.h 27 May 2022 23:55:19 -0000 > @@ -30,6 +30,23 @@ struct regmap *regmap_byphandle(uint32_t > uint32_t regmap_read_4(struct regmap *, bus_size_t); > void regmap_write_4(struct regmap *, bus_size_t, uint32_t); > > +/* Interface support */ > + > +struct ifnet; > + > +struct if_device { > + int if_node; > + struct ifnet *if_ifp; > + > + LIST_ENTRY(if_device) if_list; > + uint32_t if_phandle; > +}; > + > +void if_register(struct if_device *); > + > +struct ifnet *if_bynode(int); > +struct ifnet *if_byphandle(uint32_t); > + > /* PHY support */ > > #define PHY_NONE 0 > Index: net/ethertypes.h > =================================================================== > RCS file: /cvs/src/sys/net/ethertypes.h,v > retrieving revision 1.16 > diff -u -p -r1.16 ethertypes.h > --- net/ethertypes.h 5 Jan 2022 05:19:22 -0000 1.16 > +++ net/ethertypes.h 27 May 2022 23:55:22 -0000 > @@ -303,6 +303,8 @@ > #define ETHERTYPE_AOE 0x88A2 /* ATA over Ethernet */ > #define ETHERTYPE_QINQ 0x88A8 /* 802.1ad VLAN stacking */ > #define ETHERTYPE_LLDP 0x88CC /* Link Layer Discovery > Protocol */ > +#define ETHERTYPE_802_EX1 0x88B5 /* IEEE Std 802 - Local > Experimental */ > +#define ETHERTYPE_802_EX2 0x88B6 /* IEEE Std 802 - Local > Experimental */ > #define ETHERTYPE_MACSEC 0x88e5 /* 802.1AE MACsec */ > #define ETHERTYPE_PBB 0x88e7 /* 802.1Q Provider Backbone > Bridging */ > #define ETHERTYPE_NSH 0x984F /* Network Service Header > (RFC8300) */ > >