Add 802.1Q VLAN and PVID support. Support VLAN tag at CPU-port

This commit is contained in:
logicog
2025-07-04 09:17:10 +02:00
parent 997d85ba02
commit 2d8c0efabd
4 changed files with 222 additions and 46 deletions
+2 -1
View File
@@ -31,7 +31,8 @@ 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 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. 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 Further 6 bytes follow describing the frame, before the normal IPv4 data
starts. 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 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). buffer on the ASIC side by writing 0x1 to RTL837X_REG_RX_DONE (0x784c).
+125 -13
View File
@@ -3,7 +3,8 @@
* This code is in the Public Domain * This code is in the Public Domain
*/ */
// #define REGDBG #define REGDBG
#define DEBUG
#include <stdint.h> #include <stdint.h>
#include "rtl837x_common.h" #include "rtl837x_common.h"
@@ -17,6 +18,7 @@ extern __xdata uint8_t minPort;
extern __xdata uint8_t maxPort; extern __xdata uint8_t maxPort;
extern __xdata uint8_t nSFPPorts; extern __xdata uint8_t nSFPPorts;
extern __xdata uint8_t sfr_data[4]; extern __xdata uint8_t sfr_data[4];
extern __xdata uint8_t cpuPort;
__xdata uint32_t l2_head; __xdata uint32_t l2_head;
@@ -29,7 +31,12 @@ __xdata uint32_t l2_head;
* CC: BIT 0: 01: Execute. Bit 1: 1: WRITE, 0: READ * CC: BIT 0: 01: Execute. Bit 1: 1: WRITE, 0: READ
* TT: 04: L2-table, 03: VLAN-table * TT: 04: L2-table, 03: VLAN-table
*/ */
// Table operation bit-smasks
#define TBL_WRITE 0x02
#define TBL_EXECUTE 0x01
// Table types
#define TBL_L2_UNICAST 0x04 #define TBL_L2_UNICAST 0x04
#define TBL_VLAN 0x03
#define RTL837x_TBL_DATA_0 0x5cb0 #define RTL837x_TBL_DATA_0 0x5cb0
#define RTL837x_L2_LIST_DATA_A 0x5ccc #define RTL837x_L2_LIST_DATA_A 0x5ccc
@@ -39,6 +46,83 @@ __xdata uint32_t l2_head;
#define RTL837x_PVID_BASE_REG 0x4e1c #define RTL837x_PVID_BASE_REG 0x4e1c
void port_mirror(uint8_t port, uint16_t source_mask, uint8_t directions) __banked
{
print_string("\r\nport_mirror called \r\n");
}
void port_pvid_set(uint8_t port, uint16_t pvid) __banked
{
// r4e1c:00001001 R4e1c-000017d0 r6738:00000000 R6738-00000000 (no filtering)
print_string("\r\nport_pvid_set called \r\n");
uint16_t reg = RTL837x_PVID_BASE_REG + ((port >> 1) << 2);
reg_read_m(reg);
if (port & 0x1) {
REG_WRITE(reg, sfr_data[0], pvid >> 4, sfr_data[2] & 0x0f | (pvid << 4), sfr_data[3]);
} else {
REG_WRITE(reg, sfr_data[0], sfr_data[1], sfr_data[2] & 0xf0 | (pvid >> 8), pvid);
}
}
void vlan_delete(uint16_t vlan) __banked
{
print_string("\r\nvlan_delete called \r\n");
// R5cac-07d30301 r5cac:07d30300
}
/*
* A member that is not tagged, is untagged
*/
void vlan_create(uint16_t vlan, uint16_t members, uint16_t tagged) __banked
{
/* First line:
7-9: Untagged: 1-1, Not-Member: 1-0
0111111111
1111000000
9 port 0
1-3: Untagged: 1-1
0111111111
1000000111
1-3: Tagged: 0-1
0111111000
1000000111
7-9: Tagged: 0-1
0000111111
1111000000
In 1-0 -> 1-1 FIRST Bit: XOR, Second bit stays
In 1-1 -> 0-1
In 0-1 // Not allowed
In 0-0 -> 1-0
*/
// R5cb8-02 07e207 R5cac-07d20303
print_string("\r\nvlan_create called: "); print_short(vlan); write_char(' '); print_short(members); write_char(':'); print_short(tagged);
uint16_t a = members ^ tagged;
// Initialize VLAN table with VLAN 1
REG_WRITE(RTL837x_TBL_DATA_IN_A, 0x02, (a >> 8) & 0x0f, (a << 2) | (tagged >> 8), tagged);
REG_WRITE(RTL837X_TBL_CTRL, vlan >> 8, vlan, TBL_VLAN, TBL_WRITE | TBL_EXECUTE);
do {
reg_read_m(RTL837X_TBL_CTRL);
} while (sfr_data[3] & TBL_EXECUTE);
print_string("\r\nvlan_create done \r\n");
}
/*
* Configures a default VLAN 1 and enables 4k VLAN tables
* All ports are made members of the VLAN and VLAN filtering
* is enabled on all ports
* PVID is set to 1 for all ports
* Called upon reboot
*/
void vlan_setup(void) __banked void vlan_setup(void) __banked
{ {
print_string("\r\nvlan_setup called \r\n"); print_string("\r\nvlan_setup called \r\n");
@@ -48,43 +132,66 @@ void vlan_setup(void) __banked
REG_SET(RTL837X_TBL_CTRL, 0x00010303); REG_SET(RTL837X_TBL_CTRL, 0x00010303);
do { do {
reg_read_m(RTL837X_TBL_CTRL); reg_read_m(RTL837X_TBL_CTRL);
} while (sfr_data[3] & 0x01); } while (sfr_data[3] & TBL_EXECUTE);
// Set PVID 1 for every port. TODO: Skip unused ports! // Set PVID 1 for every port. TODO: Skip unused ports!
for (uint8_t i = minPort; i <= maxPort + 1; i++) { // Do this also for the CPU port for (uint8_t i = minPort; i <= maxPort + 1; i++) { // Do this also for the CPU port (+1)
print_byte(i); write_char(':'); write_char(' ');
uint16_t reg = RTL837x_PVID_BASE_REG + ((i >> 1) << 2); uint16_t reg = RTL837x_PVID_BASE_REG + ((i >> 1) << 2);
#ifdef DEBUG
print_byte(i); write_char(':'); write_char(' ');
print_short(reg); write_char(' '); write_char('B'); write_char('>'); print_short(reg); write_char(' '); write_char('B'); write_char('>');
print_sfr_data(); print_sfr_data();
#endif
reg_read_m(reg); reg_read_m(reg);
if (i & 0x1) { if (i & 0x1) {
REG_WRITE(reg, sfr_data[0], 0, sfr_data[2] & 0x0f | 0x10, sfr_data[3]); REG_WRITE(reg, sfr_data[0], 0, sfr_data[2] & 0x0f | 0x10, sfr_data[3]);
} else { } else {
REG_WRITE(reg, sfr_data[0], sfr_data[1], sfr_data[2] & 0xf0, 0x01); REG_WRITE(reg, sfr_data[0], sfr_data[1], sfr_data[2] & 0xf0, 0x01);
} }
#ifdef DEBUG
reg_read_m(reg);
write_char(' '); write_char('A'); write_char('>'); print_sfr_data(); write_char(' '); write_char('A'); write_char('>'); print_sfr_data();
#endif
// EGRESS filtering for port: removal of additional VLAN tag
reg_bit_clear(0x6738, i << 1); reg_bit_clear(0x6738, i << 1);
reg_bit_clear(0x6738, i << 1 + 1); reg_bit_clear(0x6738, (i << 1) + 1);
if (i != cpuPort)
reg_bit_set(0x4e18, i); reg_bit_set(0x4e18, i);
else
reg_bit_clear(0x4e18, i);
#ifdef DEBUG
print_string("\r\n"); print_string("\r\n");
#endif
} }
// Enable 4k VLAN // Enable 4k VLAN
REG_SET(0x4e14, 4); REG_SET(0x4e14, 4);
REG_SET(0x4e30, 0); REG_SET(0x4e30, 0);
REG_SET(0x4e34, 0); REG_SET(0x4e34, 0);
// Enable VLAN 1 // Enable VLAN 1: Ports 0-9, i.e. including the CPU port are untagged members
REG_SET(RTL837x_TBL_DATA_IN_A, 0x0207ffff); REG_SET(RTL837x_TBL_DATA_IN_A, 0x0207ffff); // 02: Entry valid, 7ffff: membership
REG_SET(RTL837X_TBL_CTRL, 0x00010303); REG_SET(RTL837X_TBL_CTRL, 0x00010303); // Write VLAN 1
do { do {
reg_read_m(RTL837X_TBL_CTRL); reg_read_m(RTL837X_TBL_CTRL);
} while (sfr_data[3] & 0x01); } while (sfr_data[3] & TBL_EXECUTE);
// Configure trunking
REG_SET(0x4f4c, 0x0000007e);
#ifdef DEBUG
print_string("\r\nvlan_setup, REG 0x6738: "); print_reg(0x6738);
print_string("\r\nvlan_setup, REG 0x4e18: "); print_reg(0x4e18);
print_string("\r\nvlan_setup, REG 0x4e14: "); print_reg(0x4e14);
print_string("\r\nvlan_setup, REG 0x4e30: "); print_reg(0x4e30);
print_string("\r\nvlan_setup, REG 0x4e34: "); print_reg(0x4e34);
print_string("\r\nvlan_setup, REG 0x4f4c: "); print_reg(0x4f4c);
#endif
print_string("\r\nvlan_setup done \r\n"); print_string("\r\nvlan_setup done \r\n");
} }
/* /*
* Forget all dynamic L2 learned entries * Forget all dynamic L2 learned entries
*/ */
@@ -119,7 +226,7 @@ void port_l2_learned(void) __banked
uint16_t first_entry = 0xffff; // Table does not have that many entries uint16_t first_entry = 0xffff; // Table does not have that many entries
while (1) { while (1) {
uint8_t port = 0; uint8_t port = 0, other = 0;
reg_read_m(RTL837x_TBL_DATA_0); reg_read_m(RTL837x_TBL_DATA_0);
REG_WRITE(RTL837x_TBL_DATA_0, sfr_data[0], sfr_data[1],sfr_data[2] | 0xc0, sfr_data[3]); REG_WRITE(RTL837x_TBL_DATA_0, sfr_data[0], sfr_data[1],sfr_data[2] | 0xc0, sfr_data[3]);
@@ -130,9 +237,11 @@ void port_l2_learned(void) __banked
// MAC // MAC
reg_read_m(RTL837x_L2_LIST_DATA_B); reg_read_m(RTL837x_L2_LIST_DATA_B);
if ((sfr_data[0] & 0x20)) { // Check entry is valid
print_byte(sfr_data[2]); write_char(':'); print_byte(sfr_data[2]); write_char(':');
print_byte(sfr_data[3]); write_char(':'); print_byte(sfr_data[3]); write_char(':');
port = (sfr_data[0] >> 6) & 0x3; port = (sfr_data[0] >> 6) & 0x3;
other = sfr_data[0];
reg_read_m(RTL837x_L2_LIST_DATA_A); reg_read_m(RTL837x_L2_LIST_DATA_A);
print_byte(sfr_data[0]); write_char(':'); print_byte(sfr_data[0]); write_char(':');
print_byte(sfr_data[1]); write_char(':'); print_byte(sfr_data[1]); write_char(':');
@@ -155,7 +264,7 @@ void port_l2_learned(void) __banked
write_char('1' + port); write_char('1' + port);
else else
print_string("10"); print_string("10");
}
reg_read_m(RTL837x_TBL_DATA_0); reg_read_m(RTL837x_TBL_DATA_0);
entry = (((uint16_t)sfr_data[2] & 0x0f) << 8) | sfr_data[3] + 1; entry = (((uint16_t)sfr_data[2] & 0x0f) << 8) | sfr_data[3] + 1;
if (first_entry == 0xffff) { if (first_entry == 0xffff) {
@@ -164,9 +273,12 @@ void port_l2_learned(void) __banked
if (first_entry == entry) if (first_entry == entry)
break; break;
} }
#ifdef DEBUG
write_char(' '); print_sfr_data();
write_char(' '); print_byte(other);
#endif
print_string("\r\n"); print_string("\r\n");
} }
print_string("\r\nport_l2_learned done \r\n");
} }
+3 -1
View File
@@ -5,5 +5,7 @@ uint8_t port_l2_forget(void) __banked;
void port_l2_learned(void) __banked; void port_l2_learned(void) __banked;
void port_stats_print(void) __banked; void port_stats_print(void) __banked;
void vlan_setup(void) __banked; void vlan_setup(void) __banked;
void port_pvid_set(uint8_t port, uint16_t pvid) __banked;
void vlan_create(uint16_t vlan, uint16_t members, uint16_t tagged) __banked;
void vlan_delete(uint16_t vlan) __banked;
#endif #endif
+68 -7
View File
@@ -47,7 +47,7 @@ volatile __xdata uint32_t ticks;
volatile __xdata uint8_t sec_counter; volatile __xdata uint8_t sec_counter;
volatile __xdata uint16_t sleep_ticks; volatile __xdata uint16_t sleep_ticks;
#define N_WORDS 10 #define N_WORDS 16
__xdata signed char cmd_words_b[N_WORDS]; __xdata signed char cmd_words_b[N_WORDS];
// Buffer for serial input, SBUF_SIZE must be power of 2 < 256 // Buffer for serial input, SBUF_SIZE must be power of 2 < 256
@@ -62,8 +62,11 @@ __code uint8_t * __code hex = "0123456789abcdef";
__xdata uint8_t flash_buf[256]; __xdata uint8_t flash_buf[256];
// For RX data, a propriatary RTL FRAME is inserted. Instead of 0x0800 for IPv4, // For RX data, a propriatary RTL FRAME is inserted. Instead of 0x0800 for IPv4,
// the RTL_FRAME_TAG_ID is used as part of an 8-byte tag // the RTL_FRAME_TAG_ID is used as part of an 8-byte tag. When VLAN is activated,
// the VLAN tag is inserted after the RTL tag
// See here for the RTL tag: https://github.com/torvalds/linux/commit/1521d5adfc2b557e15f97283c8b7ad688c3ebc40
#define RTL_TAG_SIZE 8 #define RTL_TAG_SIZE 8
#define VLAN_TAG_SIZE 4
#define RTL_FRAME_TAG_ID 0x8899 #define RTL_FRAME_TAG_ID 0x8899
// For RX and TX, an 8 byte header describing the frame to be moved to the Asic // For RX and TX, an 8 byte header describing the frame to be moved to the Asic
@@ -82,6 +85,7 @@ __xdata uint32_t ipv4_checksum; // Note that this is little endian
__xdata uint8_t minPort; __xdata uint8_t minPort;
__xdata uint8_t maxPort; __xdata uint8_t maxPort;
__xdata uint8_t nSFPPorts; __xdata uint8_t nSFPPorts;
__xdata uint8_t cpuPort;
__xdata uint8_t was_offline; __xdata uint8_t was_offline;
@@ -754,14 +758,14 @@ void prepare_icmp_reply(void)
tx_buf[34 + i] = ownIP[i]; tx_buf[34 + i] = ownIP[i];
// RTL Tag after dest-mac and source-mac: 8 Bytes // RTL Tag after dest-mac and source-mac: 8 Bytes
for (uint8_t i = 0; i < 4; i++) for (uint8_t i = 0; i < 4; i++)
tx_buf[26 + i] = rx_buf[26 + i]; tx_buf[26 + i] = rx_buf[18 + RTL_TAG_SIZE + VLAN_TAG_SIZE + i];
for (uint8_t i = 0; i < 4; i++) // DEST-IP for (uint8_t i = 0; i < 4; i++) // DEST-IP
tx_buf[38 + i] = rx_buf[34 + i]; tx_buf[38 + i] = rx_buf[26 + RTL_TAG_SIZE + VLAN_TAG_SIZE + i];
tx_buf[4] = tx_buf[25] = 84; // TCP length tx_buf[4] = tx_buf[25] = 84; // TCP length
tx_buf[4] = 84 + ETHER_HEADER_SIZE; // Total Ethernet frame len tx_buf[4] = 84 + ETHER_HEADER_SIZE; // Total Ethernet frame len
tx_buf[5] = tx_buf[24] = 0; tx_buf[5] = tx_buf[24] = 0;
for (uint8_t i = 0; i < 60; i++) // Copy sequence number, id, timestamp and data over for (uint8_t i = 0; i < 60; i++) // Copy sequence number, id, timestamp and data over
tx_buf[RTL_FRAME_HEADER_SIZE + 38 + i] = rx_buf[RTL_TAG_SIZE + 38 + i]; tx_buf[RTL_FRAME_HEADER_SIZE + 38 + i] = rx_buf[RTL_TAG_SIZE + VLAN_TAG_SIZE + 38 + i];
} }
@@ -822,7 +826,7 @@ void handle_rx(void)
#endif #endif
} else if (rx_buf[0] == ownMAC[0] && rx_buf[1] == ownMAC[1] && rx_buf[2] == ownMAC[2] } else if (rx_buf[0] == ownMAC[0] && rx_buf[1] == ownMAC[1] && rx_buf[2] == ownMAC[2]
&& rx_buf[3] == ownMAC[3] && rx_buf[4] == ownMAC[4] && rx_buf[5] == ownMAC[5]) { && rx_buf[3] == ownMAC[3] && rx_buf[4] == ownMAC[4] && rx_buf[5] == ownMAC[5]) {
if (rx_buf[31] == 0x01) { if (rx_buf[23 + RTL_TAG_SIZE + VLAN_TAG_SIZE] == 0x01) {
#ifdef RXTXDBG #ifdef RXTXDBG
print_string("ICMP PING REQ\r\n"); print_string("ICMP PING REQ\r\n");
#endif #endif
@@ -1413,6 +1417,7 @@ void rtl8372_init(void)
uint16_t reg = 0x1238; // Port base register for the bits we set uint16_t reg = 0x1238; // Port base register for the bits we set
minPort = 0; minPort = 0;
maxPort = 8; maxPort = 8;
cpuPort = 9;
nSFPPorts = 1; // FIXME: It could also be 2 nSFPPorts = 1; // FIXME: It could also be 2
if (!isRTL8373) { if (!isRTL8373) {
minPort = 3; minPort = 3;
@@ -1526,6 +1531,21 @@ void setup_i2c(void)
} }
uint8_t atoi_short(uint16_t *vlan, uint8_t idx)
{
uint8_t err = 1;
*vlan = 0;
while (sbuf[idx] >= '0' && sbuf[idx] <= '9') {
err = 0;
*vlan = (*vlan * 10) + sbuf[idx] - '0';
idx++;
}
return err;
}
void bootloader(void) void bootloader(void)
{ {
ticks = 0; ticks = 0;
@@ -1628,7 +1648,6 @@ void bootloader(void)
print_long(ticks); print_long(ticks);
#endif #endif
// Print line and parse command into words // Print line and parse command into words
print_string("\r\n CMD: ");
is_white = 1; is_white = 1;
uint8_t word = 0; uint8_t word = 0;
cmd_words_b[0] = -1; cmd_words_b[0] = -1;
@@ -1641,6 +1660,11 @@ void bootloader(void)
is_white = 1; is_white = 1;
write_char(sbuf[line_ptr++]); write_char(sbuf[line_ptr++]);
line_ptr &= SBUF_SIZE - 1; line_ptr &= SBUF_SIZE - 1;
if (word >= N_WORDS - 1) {
print_string("\r\ntoo many arguments, truncated");
line_ptr = l; // BUG: We should probably ignore the command
break;
}
} }
cmd_words_b[word++] = line_ptr; cmd_words_b[word++] = line_ptr;
cmd_words_b[word++] = -1; cmd_words_b[word++] = -1;
@@ -1734,6 +1758,43 @@ void bootloader(void)
if (cmd_compare(0, "l2")) { if (cmd_compare(0, "l2")) {
port_l2_learned(); port_l2_learned();
} }
if (cmd_compare(0, "pvid") && cmd_words_b[1] > 0 && cmd_words_b[2] > 0) {
__xdata uint16_t pvid;
uint8_t port;
port = sbuf[cmd_words_b[1]] - '0';
if (!atoi_short(&pvid, cmd_words_b[2]))
port_pvid_set(port, pvid);
}
if (cmd_compare(0, "vlan")) {
__xdata uint16_t vlan;
__xdata uint16_t members = 0;
__xdata uint16_t tagged = 0;
if (!atoi_short(&vlan, cmd_words_b[1])) {
print_short(vlan);
if (cmd_words_b[2] > 0 && sbuf[cmd_words_b[2]] == 'd') {
vlan_delete(vlan);
} else {
uint8_t w = 2;
while (cmd_words_b[w] > 0) {
uint8_t port;
if (sbuf[cmd_words_b[w]] >= '0' && sbuf[cmd_words_b[w]] <= '9') {
port = sbuf[cmd_words_b[w]] - '1';
if (sbuf[cmd_words_b[w] + 1] >= '0' && sbuf[cmd_words_b[w] + 1] <= '9') {
port = (port + 1) * 10 + sbuf[cmd_words_b[w] + 1] - '1';
if (sbuf[cmd_words_b[w] + 2] == 't')
tagged |= ((uint16_t)1) << port;
} else {
if (sbuf[cmd_words_b[w] + 1] == 't')
tagged |= ((uint16_t)1) << port;
}
members |= ((uint16_t)1) << port;
}
w++;
}
vlan_create(vlan, members, tagged);
}
}
}
} }
print_string("\r\n> "); print_string("\r\n> ");
} }