mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-08-30 14:52:51 +08:00
Add 802.1Q VLAN and PVID support. Support VLAN tag at CPU-port
This commit is contained in:
+2
-1
@@ -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
|
||||
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.
|
||||
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).
|
||||
|
||||
+149
-37
@@ -3,7 +3,8 @@
|
||||
* This code is in the Public Domain
|
||||
*/
|
||||
|
||||
// #define REGDBG
|
||||
#define REGDBG
|
||||
#define DEBUG
|
||||
|
||||
#include <stdint.h>
|
||||
#include "rtl837x_common.h"
|
||||
@@ -17,6 +18,7 @@ extern __xdata uint8_t minPort;
|
||||
extern __xdata uint8_t maxPort;
|
||||
extern __xdata uint8_t nSFPPorts;
|
||||
extern __xdata uint8_t sfr_data[4];
|
||||
extern __xdata uint8_t cpuPort;
|
||||
|
||||
__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
|
||||
* TT: 04: L2-table, 03: VLAN-table
|
||||
*/
|
||||
#define TBL_L2_UNICAST 0x04
|
||||
// Table operation bit-smasks
|
||||
#define TBL_WRITE 0x02
|
||||
#define TBL_EXECUTE 0x01
|
||||
// Table types
|
||||
#define TBL_L2_UNICAST 0x04
|
||||
#define TBL_VLAN 0x03
|
||||
|
||||
#define RTL837x_TBL_DATA_0 0x5cb0
|
||||
#define RTL837x_L2_LIST_DATA_A 0x5ccc
|
||||
@@ -39,6 +46,83 @@ __xdata uint32_t l2_head;
|
||||
|
||||
#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
|
||||
{
|
||||
print_string("\r\nvlan_setup called \r\n");
|
||||
@@ -48,43 +132,66 @@ void vlan_setup(void) __banked
|
||||
REG_SET(RTL837X_TBL_CTRL, 0x00010303);
|
||||
do {
|
||||
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!
|
||||
for (uint8_t i = minPort; i <= maxPort + 1; i++) { // Do this also for the CPU port
|
||||
print_byte(i); write_char(':'); write_char(' ');
|
||||
for (uint8_t i = minPort; i <= maxPort + 1; i++) { // Do this also for the CPU port (+1)
|
||||
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_sfr_data();
|
||||
#endif
|
||||
reg_read_m(reg);
|
||||
if (i & 0x1) {
|
||||
REG_WRITE(reg, sfr_data[0], 0, sfr_data[2] & 0x0f | 0x10, sfr_data[3]);
|
||||
} else {
|
||||
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();
|
||||
#endif
|
||||
|
||||
// EGRESS filtering for port: removal of additional VLAN tag
|
||||
reg_bit_clear(0x6738, i << 1);
|
||||
reg_bit_clear(0x6738, i << 1 + 1);
|
||||
reg_bit_set(0x4e18, i);
|
||||
reg_bit_clear(0x6738, (i << 1) + 1);
|
||||
if (i != cpuPort)
|
||||
reg_bit_set(0x4e18, i);
|
||||
else
|
||||
reg_bit_clear(0x4e18, i);
|
||||
#ifdef DEBUG
|
||||
print_string("\r\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
// Enable 4k VLAN
|
||||
REG_SET(0x4e14, 4);
|
||||
REG_SET(0x4e30, 0);
|
||||
REG_SET(0x4e34, 0);
|
||||
|
||||
// Enable VLAN 1
|
||||
REG_SET(RTL837x_TBL_DATA_IN_A, 0x0207ffff);
|
||||
REG_SET(RTL837X_TBL_CTRL, 0x00010303);
|
||||
// Enable VLAN 1: Ports 0-9, i.e. including the CPU port are untagged members
|
||||
REG_SET(RTL837x_TBL_DATA_IN_A, 0x0207ffff); // 02: Entry valid, 7ffff: membership
|
||||
REG_SET(RTL837X_TBL_CTRL, 0x00010303); // Write VLAN 1
|
||||
do {
|
||||
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");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
while (1) {
|
||||
uint8_t port = 0;
|
||||
uint8_t port = 0, other = 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]);
|
||||
|
||||
@@ -130,32 +237,34 @@ void port_l2_learned(void) __banked
|
||||
|
||||
// MAC
|
||||
reg_read_m(RTL837x_L2_LIST_DATA_B);
|
||||
print_byte(sfr_data[2]); write_char(':');
|
||||
print_byte(sfr_data[3]); write_char(':');
|
||||
port = (sfr_data[0] >> 6) & 0x3;
|
||||
reg_read_m(RTL837x_L2_LIST_DATA_A);
|
||||
print_byte(sfr_data[0]); write_char(':');
|
||||
print_byte(sfr_data[1]); write_char(':');
|
||||
print_byte(sfr_data[2]); write_char(':');
|
||||
print_byte(sfr_data[3]); write_char('\t');
|
||||
if ((sfr_data[0] & 0x20)) { // Check entry is valid
|
||||
print_byte(sfr_data[2]); write_char(':');
|
||||
print_byte(sfr_data[3]); write_char(':');
|
||||
port = (sfr_data[0] >> 6) & 0x3;
|
||||
other = sfr_data[0];
|
||||
reg_read_m(RTL837x_L2_LIST_DATA_A);
|
||||
print_byte(sfr_data[0]); write_char(':');
|
||||
print_byte(sfr_data[1]); write_char(':');
|
||||
print_byte(sfr_data[2]); write_char(':');
|
||||
print_byte(sfr_data[3]); write_char('\t');
|
||||
|
||||
// VLAN
|
||||
reg_read_m(RTL837x_L2_LIST_DATA_B);
|
||||
print_short( ((uint16_t) (sfr_data[0] & 0x0f)) | sfr_data[1]); // VLAN
|
||||
// VLAN
|
||||
reg_read_m(RTL837x_L2_LIST_DATA_B);
|
||||
print_short( ((uint16_t) (sfr_data[0] & 0x0f)) | sfr_data[1]); // VLAN
|
||||
|
||||
// type
|
||||
reg_read_m(RTL837x_L2_LIST_DATA_C);
|
||||
if (sfr_data[2] & 0x1)
|
||||
print_string("\tstatic\t");
|
||||
else
|
||||
print_string("\tlearned\t");
|
||||
|
||||
port |= (sfr_data[3] & 0x3) << 2;
|
||||
if (port < 9)
|
||||
write_char('1' + port);
|
||||
else
|
||||
print_string("10");
|
||||
// type
|
||||
reg_read_m(RTL837x_L2_LIST_DATA_C);
|
||||
if (sfr_data[2] & 0x1)
|
||||
print_string("\tstatic\t");
|
||||
else
|
||||
print_string("\tlearned\t");
|
||||
|
||||
port |= (sfr_data[3] & 0x3) << 2;
|
||||
if (port < 9)
|
||||
write_char('1' + port);
|
||||
else
|
||||
print_string("10");
|
||||
}
|
||||
reg_read_m(RTL837x_TBL_DATA_0);
|
||||
entry = (((uint16_t)sfr_data[2] & 0x0f) << 8) | sfr_data[3] + 1;
|
||||
if (first_entry == 0xffff) {
|
||||
@@ -164,9 +273,12 @@ void port_l2_learned(void) __banked
|
||||
if (first_entry == entry)
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
write_char(' '); print_sfr_data();
|
||||
write_char(' '); print_byte(other);
|
||||
#endif
|
||||
print_string("\r\n");
|
||||
}
|
||||
print_string("\r\nport_l2_learned done \r\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
+3
-1
@@ -5,5 +5,7 @@ uint8_t port_l2_forget(void) __banked;
|
||||
void port_l2_learned(void) __banked;
|
||||
void port_stats_print(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
|
||||
|
||||
+68
-7
@@ -47,7 +47,7 @@ volatile __xdata uint32_t ticks;
|
||||
volatile __xdata uint8_t sec_counter;
|
||||
volatile __xdata uint16_t sleep_ticks;
|
||||
|
||||
#define N_WORDS 10
|
||||
#define N_WORDS 16
|
||||
__xdata signed char cmd_words_b[N_WORDS];
|
||||
|
||||
// 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];
|
||||
|
||||
// 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 VLAN_TAG_SIZE 4
|
||||
#define RTL_FRAME_TAG_ID 0x8899
|
||||
|
||||
// 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 maxPort;
|
||||
__xdata uint8_t nSFPPorts;
|
||||
__xdata uint8_t cpuPort;
|
||||
|
||||
|
||||
__xdata uint8_t was_offline;
|
||||
@@ -754,14 +758,14 @@ void prepare_icmp_reply(void)
|
||||
tx_buf[34 + i] = ownIP[i];
|
||||
// RTL Tag after dest-mac and source-mac: 8 Bytes
|
||||
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
|
||||
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] = 84 + ETHER_HEADER_SIZE; // Total Ethernet frame len
|
||||
tx_buf[5] = tx_buf[24] = 0;
|
||||
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
|
||||
} 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]) {
|
||||
if (rx_buf[31] == 0x01) {
|
||||
if (rx_buf[23 + RTL_TAG_SIZE + VLAN_TAG_SIZE] == 0x01) {
|
||||
#ifdef RXTXDBG
|
||||
print_string("ICMP PING REQ\r\n");
|
||||
#endif
|
||||
@@ -1413,6 +1417,7 @@ void rtl8372_init(void)
|
||||
uint16_t reg = 0x1238; // Port base register for the bits we set
|
||||
minPort = 0;
|
||||
maxPort = 8;
|
||||
cpuPort = 9;
|
||||
nSFPPorts = 1; // FIXME: It could also be 2
|
||||
if (!isRTL8373) {
|
||||
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)
|
||||
{
|
||||
ticks = 0;
|
||||
@@ -1628,7 +1648,6 @@ void bootloader(void)
|
||||
print_long(ticks);
|
||||
#endif
|
||||
// Print line and parse command into words
|
||||
print_string("\r\n CMD: ");
|
||||
is_white = 1;
|
||||
uint8_t word = 0;
|
||||
cmd_words_b[0] = -1;
|
||||
@@ -1641,6 +1660,11 @@ void bootloader(void)
|
||||
is_white = 1;
|
||||
write_char(sbuf[line_ptr++]);
|
||||
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++] = -1;
|
||||
@@ -1734,6 +1758,43 @@ void bootloader(void)
|
||||
if (cmd_compare(0, "l2")) {
|
||||
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> ");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user