mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-10-29 17:54:14 +08:00
Dynamic link aggregation on top of the static LAGG, for RTL8372/8373: receive, selection and mux state machines (coupled mux, one aggregator per group), with "lacp show" to look inside. Each of the four trunk groups runs its own aggregator on the candidate ports assigned with "lag <n> lacp <ports>"; the engine starts with the first such group and stops with the last. LACPDUs reach the CPU through the reserved-multicast trap, so delivery needs no L2 entry and nothing written per VLAN or per PVID. The trap acts on the address before the VLAN lookup and before the ingress filters, so a LACPDU also arrives on a port that admits tagged frames only and on a port STP holds in blocking, neither of which a forwarded one reaches. A trapped frame carries no VLAN tag, so lacp_in() takes the offset of the PDU body from TRAP_RX_BODY(). The action is set with trap_rma_set(), which leaves the flags sharing that register alone, and "lacp off" puts the slow-protocols drop default back. doc/lacp.md and doc/link_aggregation.md record what was measured, including why the trap looked like a dead end at first. The web UI gets a Static/LACP mode per group on the LAG page with live status, and the startup-config validator knows the per-group syntax. A host-side sandbox for the state machines runs in the test harness together with the LAG hash check. It drives both forms of an incoming LACPDU, so the body offset of a trapped one is covered as well. The L2 table page iterated with the "next unicast" read method, so static multicast entries such as an IGMP group were invisible. It now iterates with "next address", skips L3 entries (their layout carries IP addresses where a MAC is expected), and reports the lowest member of a multicast entry's port mask, so such an entry shows up as a static entry on that port. A port taken out of LACP forgets its partner, so "lacp show" after "lacp off" no longer reports one. Applying the same candidate set to a group again, as the LAG page does on every Apply, leaves the trunk alone instead of tearing it down for a second or two. lacp_in() checks the frame length and ethertype before reading a LACPDU, so a runt or a foreign frame on the slow-protocols address cannot leave stale buffer bytes as a partner. The sandbox covers these three in T20-T22. On the LAG page, LACP mode with no ports checked sends "lag N lacp off", and the startup-config validator wants at least one port after "lacp".
102 lines
4.2 KiB
Markdown
102 lines
4.2 KiB
Markdown
# The CPU Port
|
|
|
|
The RTL827x provide a CPU Port for a NIC on the 8051 side of the SoC.
|
|
|
|
## Receiving packets
|
|
In order to receive packets on the ASIC side, bit 0 of RTL837X_REG_RX_CTRL
|
|
(0x785c) must be set. Further bits in the register enable reception of various
|
|
kinds of Ethernet frames. They should all be set in order for the firmware
|
|
to decide what to do with them. To drop packets with incorrect Ethernet frame CRC
|
|
already by the ASIC, clear bit 2 of this register.
|
|
|
|
Packets are received by either polling the RTL837X_REG_RX_AVAIL register
|
|
(0x7874), which will be > 0 if data is within a ring-buffer on the ASIC side
|
|
of the SoC. Alternatively, an interrupt can be triggered (EX1).
|
|
|
|
Data is transferred to the 8051 side by calling an SFR function. First, the
|
|
frame header of the received frame will be copied over. For this, provide
|
|
the destination memory location in xdata memory in SFRs B3 and B4 (little
|
|
endian), the source location on the ASIC-side in SFRs B5/B6 (also little
|
|
endian, found in RTL837X_REG_RX_RINGPTR, 0x787c) and execute the function
|
|
by setting SFR_NIC_CTRL (B7) to the length to be transferred divided by 8,
|
|
i.e. 1.
|
|
|
|
The frame header has the following format:
|
|
```
|
|
SS xx xx xP LL LH xx xx
|
|
SS: 8-bit sequence number
|
|
P: Port number
|
|
LHLL: Length of Ethernet frame (little endian)
|
|
xx: Unknown
|
|
```
|
|
|
|
Next, transfer the actual packet over by repeating the SFR function with a
|
|
pointer to the frame on the ASIC directly after the frame header and a
|
|
length as given by the length in the frame header + 7, again divided by 8.
|
|
|
|
The received frame will have an RTL proprietary Ethernet frame type of
|
|
0x8899 (RRPC) where normally the frame type 0x0800 for IPv4 would be located.
|
|
Further 6 bytes follow describing the frame, before the normal IPv4 data
|
|
starts. A documentation can be found here:
|
|
[TAG8899_COMMIT](https://github.com/torvalds/linux/commit/1521d5adfc2b557e15f97283c8b7ad688c3ebc40)
|
|
|
|
After copying over header and frame, the frame is marked read in the ring
|
|
buffer on the ASIC side by writing 0x1 to RTL837X_REG_RX_DONE (0x784c).
|
|
|
|
## Transmissing packets
|
|
Packets are transmitted by preparing a frame-header plus frame in xdata memory
|
|
and transferring both to the ASIC side via the SFRs. The ASIC will transmit
|
|
packets if bit 0 of RTL837X_REG_TX_CTRL (0x7860) is set.
|
|
|
|
```
|
|
SS 07 00 00 LL LH 00 00
|
|
SS: 8-bit sequence number
|
|
07: Enables header and TCP checksum offloading to ASIC
|
|
LHLL: Length of the Ethernet frame
|
|
```
|
|
The Ethernet frame data starts immediately after the frame header in xdata
|
|
memory. The frame is transferred to the ASIC side by setting SFRs B3 and B4
|
|
to the xdata source address of the frame header, and the ring pointer to the
|
|
free space indicated by register 0x7890 multiplied by 8 and the MSB set.
|
|
The length is given by the length of the frame plus 15, divided by 8.
|
|
|
|
Writing 0x1 to register 0x7850 will transmit the frame. The Ethernet frame
|
|
checksum and the TCP checksum are automatically calculated (offloaded) by the
|
|
ASIC before transmitting on the wire.
|
|
|
|
|
|
## The RTL tag words
|
|
|
|
The frame header uses the Realtek Remote Control Protocol (RRCP) format or
|
|
the like.
|
|
|
|
The `flags` word:
|
|
|
|
```
|
|
bit15 EFID_EN | 14:12 EFID | 11 PRI_EN | 10:8 PRI |
|
|
bit7 KEEP | 6 VSEL | 5 LEARN_DIS | 4:0 VIDX
|
|
```
|
|
|
|
All fields are in network byte order.
|
|
|
|
This word has to be written through `HTONS` like every other field of the tag.
|
|
Writing a constant raw puts the bits in the wrong byte, so `0x0020` reaches the
|
|
wire as `0x2000`, which is EFID rather than LEARN_DIS. The ASIC then fails to
|
|
parse the tag and forwards the frame with the `0x8899` header still on it.
|
|
|
|
* `EFID_EN`, `EFID`: look the destination up under this filtering ID
|
|
instead of the port's own
|
|
* `PRI_EN`, `PRI`: force the given priority on the frame
|
|
* `KEEP`: keep the 802.1Q tagging of the frame exactly as injected,
|
|
bypassing the egress tagging rules of the port
|
|
* `VSEL`, `VIDX`: classify the frame into the VLAN at this index of the
|
|
VLAN table
|
|
* `LEARN_DIS`: do not learn the source address from this frame
|
|
|
|
The `pmask` word: bit 15 is `ALLOW`, bits 14 to 0 are a port mask.
|
|
|
|
* `ALLOW` clear: the mask is the egress set, the frame goes to exactly
|
|
the ports given
|
|
* `ALLOW` set: the ASIC looks the destination up as usual and the mask
|
|
only limits which ports the result may use
|