nic: send a frame with the layout it actually has

tcpip_output() decides whether to splice in the 802.1Q tag, and that
decision also moves the frame: the header is shifted forward over its
padding, so a tagged frame starts at uip_buf with the q_frame layout,
while an untagged one keeps the padding and the nonq_frame layout.

nic_tx_packet() took that decision a second time, from management_vlan
alone. That agreed while the sender suppressed the management VLAN around
the transmission, but not since the tag is skipped per frame for a
CPU-tagged one: the descriptor then still sits behind the padding while
the transfer is set up for the shifted layout. The frame goes out four
bytes early and its length is read from the offset where tx_seq and
chksum_flags live, so a sixty byte BPDU is sent as around 1800 bytes of
whatever follows it. Looped back it is no longer a BPDU, the port hears
nothing, auto edge promotes it and the loop stays open.

Record the decision where it is taken and let the transfer follow it.

A variable rather than an argument because internal RAM is full once the
aggregation module shares the image: the overlay area ends at 0x7f, and
an argument or any temporary for the condition no longer fits.
This commit is contained in:
d00f
2026-08-31 02:52:00 +02:00
parent 7f0e7fdc82
commit 8e9bb56a29
+7 -6
View File
@@ -118,6 +118,7 @@ __xdata uint8_t uip_buf[UIP_CONF_BUFFER_SIZE+2];
__xdata uint16_t rx_packet_vlan; __xdata uint16_t rx_packet_vlan;
__xdata uint16_t management_vlan; __xdata uint16_t management_vlan;
__xdata bool frame_tagged;
__xdata uint8_t tx_seq; __xdata uint8_t tx_seq;
__xdata uint8_t stp_enabled; __xdata uint8_t stp_enabled;
@@ -711,13 +712,11 @@ void nic_tx_packet(uint16_t ring_ptr)
uint16_t len; uint16_t len;
uint16_t guard = 0; uint16_t guard = 0;
/* If we have a management VLAN, we have inserted a dot1Q-tag into the frame and /* A frame that got a dot1Q tag was shifted forward over its padding, so it
* the frame starts at the beginning of uip_buf with the RTL TX descriptor, * starts at uip_buf and carries the q_frame layout. One that did not keeps
* otherwise the frame is a normal Ethernet frame which starts with * the padding in front and the nonq_frame layout, so the padding is skipped.
* an RTL TX descriptor being padded at the beginning, in the second case
* we need to skip the padding for the sending of the frame.
*/ */
if (management_vlan) { if (frame_tagged) {
SFR_NIC_DATA_U16LE = (uint16_t) uip_buf; SFR_NIC_DATA_U16LE = (uint16_t) uip_buf;
len = FRAME_Q->len; len = FRAME_Q->len;
/* /*
@@ -1122,7 +1121,9 @@ void tcpip_output(void)
// For the management VLAN we insert an 802.1Q VLAN tag, but never into a // For the management VLAN we insert an 802.1Q VLAN tag, but never into a
// CPU-tagged frame, where the ASIC expects its tag right behind the addresses // CPU-tagged frame, where the ASIC expects its tag right behind the addresses
frame_tagged = false;
if (management_vlan && FRAME_ETHERTYPE != HTONS(RTL_FRAME_TAG_ID)) { if (management_vlan && FRAME_ETHERTYPE != HTONS(RTL_FRAME_TAG_ID)) {
frame_tagged = true;
// Shift the ethernet header before the HW type including the rtl_frame_desc to the beginning of uip_buf // Shift the ethernet header before the HW type including the rtl_frame_desc to the beginning of uip_buf
// to allow space to insert the dot 1Q tag // to allow space to insert the dot 1Q tag
for (uint8_t i = 0; i < sizeof(struct q_frame) - DOT_1Q_TAG_SIZE; i++) for (uint8_t i = 0; i < sizeof(struct q_frame) - DOT_1Q_TAG_SIZE; i++)