From f7fcf0eda704a00139969bc10ba7604a33f846d3 Mon Sep 17 00:00:00 2001 From: diijkstra <16804536+diijkstra@users.noreply.github.com> Date: Sat, 14 Mar 2026 18:55:30 +0100 Subject: [PATCH 1/2] Console: Add Ingress type & VLAN show table Added new `vlan show` command to dump current VLAN settings. Supports printing PVID & ingress filtering per port. Added new `ingress [p]` command to setup ingress filtering. Added wrappers for enabling/disabling vlan filtering. Currently not configurable, but state in console is read via ASIC registers. Full VLAN dump & web support of new commands will be added later. --- cmd_parser.c | 75 ++++++++++++++++++++++++++++++++- doc/vlan.md | 27 ++++++++++-- rtl837x_port.c | 110 +++++++++++++++++++++++++++++++++++++++++++++--- rtl837x_port.h | 20 ++++++++- rtlplayground.c | 3 +- 5 files changed, 221 insertions(+), 14 deletions(-) diff --git a/cmd_parser.c b/cmd_parser.c index 28575de..9c4dd29 100644 --- a/cmd_parser.c +++ b/cmd_parser.c @@ -342,16 +342,86 @@ void parse_vlan(void) w++; } vlan_create(); + } else if (cmd_words_b[1] > 0 && cmd_compare(1, "show")) { + vlan_dump(); + } else { + goto err; } + if (cmd_words_b[2] > 0 && isletter(cmd_buffer[cmd_words_b[2]])) { print_string("vlan_ptr "); print_short(vlan_ptr); write_char(':'); write_char('>'); print_string_x(&vlan_names[0]); write_char('<'); write_char('\n'); } return; err: - print_string("Error: vlan [port][t/u]..."); + print_string("Error: vlan (|show) [port][t/u]..."); } +bool vlan_ingress_mode_parse(char c, vlan_ingress_mode_t *mode) +{ + switch (c) { + case 'u': + *mode = VLAN_UNTAGGED; + return true; + case 't': + *mode = VLAN_TAGGED; + return true; + case 'a': + *mode = VLAN_ALL; + return true; + default: + *mode = VLAN_INVALID; + return false; + } +} + +void parse_ingress(void) +{ + if (cmd_words_b[1] <= 0) { + goto err; + } + __xdata uint8_t log_port = 0; + __xdata vlan_ingress_mode_t mode = VLAN_INVALID; + __xdata uint8_t w = 1; + + if (vlan_ingress_mode_parse(cmd_buffer[cmd_words_b[w]], &mode)) { + // Setting mode for all ports at once + for (log_port = machine.min_port; log_port <= machine.max_port; log_port++) { + if (!port_ingress_filter(log_port, mode)) { + print_string("Error setting ingress filter for port "); print_byte(machine.log_to_phys_port[log_port]); write_char('\n'); + return; + } + print_string("All ports ingress filter set to: "); + print_port_ingress_filter_mode(mode); write_char('\n'); + } + return; + } else { + for(w = 1; cmd_words_b[w] > 0; w++) { + if (!isnumber(cmd_buffer[cmd_words_b[w]])) { + continue; + } + if (cmd_buffer[cmd_words_b[w]] - '1' > 9) { + print_string("Invalid physical port number: "); write_char(cmd_buffer[cmd_words_b[w]]); write_char('\n'); + continue; + } + log_port = machine.phys_to_log_port[cmd_buffer[cmd_words_b[w]] - '1']; + if (!vlan_ingress_mode_parse(cmd_buffer[cmd_words_b[w] + 1], &mode)) { + print_string("Invalid ingress mode for port "); write_char(cmd_buffer[cmd_words_b[w]]); print_string(" in ingress command\n"); + goto err; + } + if (!port_ingress_filter(log_port, mode)) { + print_string("Error setting ingress filter for port "); write_char(cmd_buffer[cmd_words_b[w]]); write_char('\n'); + return; + } + print_string("Port "); write_char(cmd_buffer[cmd_words_b[w]]); + print_string(" ingress filter set to: "); + print_port_ingress_filter_mode(mode); write_char('\n'); + } + return; + } +err: + print_string("Error: ingress [p]... \n"); +} void parse_mirror(void) { @@ -1044,6 +1114,8 @@ void cmd_parser(void) __banked write_char(cmd_history[p]); p = (p + 1) & CMD_HISTORY_MASK; } + } else if (cmd_compare(0, "ingress")) { + parse_ingress(); } else { print_string("Unknown command\n"); @@ -1070,7 +1142,6 @@ void cmd_parser(void) __banked } } - void clear_command_history(void) __banked { for (cmd_history_ptr = 0; cmd_history_ptr < CMD_HISTORY_SIZE; cmd_history_ptr++) diff --git a/doc/vlan.md b/doc/vlan.md index 1d3004b..902f4e6 100644 --- a/doc/vlan.md +++ b/doc/vlan.md @@ -38,10 +38,18 @@ an even port uses bits [11:0]. The base register is RTL837x_PVID_BASE_REG (0x4e1c) and the registers go to 0x4e2c so that also the CPU-Port may have a PVID. -Register RTL837x_REG_INGRESS (0x4e10) allows to define the iingress rules of +Register RTL837x_REG_INGRESS (0x4e10) allows to define the ingress rules of a port. 2 bits define a rule and bits 0-19 are being used. A value of 00 defines no filtering, 01 (0x01) allows only tagged packets, while 10 (0x02) -allows only untagged packets to enter a port. The default PVID is 1. +allows only untagged packets to enter a port. + +Register RTL837X_VLAN_PORT_IGR_FLTR (0x4e18) enables or disables ingres VLAN +filtering, each bit corresponds to given port (port0 -> bit0, port9 -> bit9). +When enabled, incomming package's vlan tag is checked against VLAN membership +on given port. When package contains VLAN not in member list, package is dropped. + +The default PVID on all port is 1, ingress VLAN filtering is enabled and all types of +frames are accepted on input on all ports. By default, the ports transmit Ethernet frames with Realtek's proprietary tag format. By setting bit 6 (0x40) of the respective port configuration @@ -68,9 +76,20 @@ vlan p[t/u]... vlan d deletes the VLAN +vlan show + Dumps the current ingress vlan settings. + pvid assigns PVID to a port. ports are numbered as on the casing -ingress [tagged|untagged|all] - Allows ingress only for the named packages at the given port +ingress [p]... + Allows ingress packages on port `p` only when `t`agged, `u`ntagged or `a`ny. + Multiple ports can be given at once as in vlan. When `p` is missing, all ports + are assigned the same mode. CPU port can not be changed. + + Use `vlan show` to see current configuration. + + Example: + `ingress 1t 2a` -> Set port 1 as tagged input only, set port 2 accepting any frames. + `ingress a` -> Set all ports to accept both tagged and untagged frames (default behaviour). ``` diff --git a/rtl837x_port.c b/rtl837x_port.c index 79b396e..fdfa27b 100644 --- a/rtl837x_port.c +++ b/rtl837x_port.c @@ -48,16 +48,38 @@ void port_mirror_del(void) __banked } -void port_ingress_filter(__xdata uint8_t port, __xdata uint8_t type) __banked +bool port_ingress_filter(__xdata uint8_t port, __xdata vlan_ingress_mode_t type) __banked { - if (type & 0x1) + if (port > 9 || type >= VLAN_INVALID) { + print_string("Invalid port or ingress filter type\n"); + return false; + } + + if (type & 0x1) { reg_bit_set(RTL837x_REG_INGRESS, port << 1); - else + } else { reg_bit_clear(RTL837x_REG_INGRESS, port << 1); - if (type & 0x2) + } + if (type & 0x2) { reg_bit_set(RTL837x_REG_INGRESS, (port << 1) + 1); - else + } else { reg_bit_clear(RTL837x_REG_INGRESS, (port << 1) + 1); + } + return true; +} + +vlan_ingress_mode_t port_ingress_filter_get(__xdata uint8_t port) __banked +{ + reg_read_m(RTL837x_REG_INGRESS); + if (port > 9) { + return VLAN_INVALID; + } + + // Each port is represented by 2 bits in the ingress register, starting from bit 0 for port 0 + const uint8_t sfr_index = 3 - (port / 4); + const uint8_t shift = (port % 4) << 1; + + return (vlan_ingress_mode_t)((sfr_data[sfr_index] >> shift) & 0x03); } @@ -210,7 +232,8 @@ void vlan_setup(void) __banked // EGRESS filtering for port: removal of additional VLAN tag (mode 0x3 for each port) reg_bit_clear(RTL837X_VLAN_PORT_EGR_TAG, i << 1); reg_bit_clear(RTL837X_VLAN_PORT_EGR_TAG, (i << 1) + 1); - reg_bit_set(RTL837X_VLAN_PORT_IGR_FLTR, i); + // Enable INGRESS filtering for port: discard packets not belonging to member VLAN on that port + port_ingress_vlan_filter_set(i, true); #ifdef DEBUG print_string("\n"); @@ -235,6 +258,7 @@ void vlan_setup(void) __banked #ifdef DEBUG print_string("\nvlan_setup, REG 0x6738: "); print_reg(0x6738); + print_string("\nvlan_setup, REG 0x4e10: "); print_reg(0x4e10); print_string("\nvlan_setup, REG 0x4e18: "); print_reg(0x4e18); print_string("\nvlan_setup, REG 0x4e14: "); print_reg(0x4e14); print_string("\nvlan_setup, REG 0x4e30: "); print_reg(0x4e30); @@ -636,3 +660,77 @@ void port_lag_hash_set(__xdata uint8_t lag, __xdata uint8_t hash_bits) __banked print_string("Link aggregation group must be 0-3!"); REG_WRITE(RTL837X_TRK_HASH_CTRL_BASE + (lag << 2), 0, 0, 0, hash_bits); } + +void print_port_ingress_filter_mode(vlan_ingress_mode_t mode) __banked +{ + switch (mode) { + case VLAN_UNTAGGED: + print_string("Untag."); + break; + case VLAN_TAGGED: + print_string("Tagged"); + break; + case VLAN_ALL: + print_string("Any"); + break; + default: + print_string("!!err!!"); + } +} + +static void print_phys_port(uint8_t port) __banked +{ + if (port >= machine.min_port && port <= machine.max_port) + write_char(machine.log_to_phys_port[port] + '0'); + else if (port == 9) + write_char('9'); + else + write_char('?'); +} + +void print_vlan_ingress_port(uint8_t log_port) __banked +{ + print_phys_port(log_port);write_char('\t'); + print_short(port_pvid_get(log_port));write_char('\t'); + print_port_ingress_filter_mode(port_ingress_filter_get(log_port));write_char('\t'); + port_ingress_vlan_filter_get(log_port) ? print_string("Enabled") : print_string("Disabled"); + write_char('\n'); +} +/* + * Dumps the VLAN ingress configuration + */ +void vlan_dump(void) __banked +{ + print_string("Ingress VLAN configuration:\n"); + print_string("Port\tPVID\tType\tFiltering\n"); + for (uint8_t port = machine.min_port; port <= machine.max_port; port++) { + print_vlan_ingress_port(port); + } + print_vlan_ingress_port(9); + + write_char('\n'); + print_string("Type - Which frame types are allowed: untagged, tagged or any\n"); + print_string("Filtering - Whether packets not belonging to member VLANs on that port are dropped\n"); + print_string("PVID - Assumed VLAN for untagged packets\n"); +} + + +/** Set the ingress VLAN filtering */ +bool port_ingress_vlan_filter_set(__xdata uint8_t port, __xdata bool enabled) __banked +{ + if (port < machine.min_port || port > machine.max_port && port != 9) { + return false; + } + reg_bit_set(RTL837X_VLAN_PORT_IGR_FLTR, port); + return true; +} + +/** Get the ingress VLAN filtering status */ +bool port_ingress_vlan_filter_get(__xdata uint8_t port) __banked +{ + if (port < machine.min_port || port > machine.max_port && port != 9) { + return false; + } + + return reg_bit_test(RTL837X_VLAN_PORT_IGR_FLTR, port); +} diff --git a/rtl837x_port.h b/rtl837x_port.h index 60e2282..20132fb 100644 --- a/rtl837x_port.h +++ b/rtl837x_port.h @@ -2,6 +2,7 @@ #define _RTL837X_PORT_H_ #include +#include "rtl837x_regs.h" #define STAT_COUNTER_TX_PKTS 46 #define STAT_COUNTER_RX_PKTS 47 @@ -13,6 +14,19 @@ reg_read_m(RTL837X_STAT_GET); \ } while (sfr_data[3] & 0x1); +// Possible values for ingress filter type +typedef enum { + VLAN_ALL = INGR_ALLOW_ALL, + VLAN_TAGGED = INGR_ALLOW_TAGGED, + VLAN_UNTAGGED = INGR_ALLOW_UNTAGGED, + VLAN_INVALID = 3 +} vlan_ingress_mode_e; + +// Since we lack a way to force enum to be 1 byte, +// This is a typedef for the VLAN ingress filter type +// which holds vlan_ingress_mode_e values +typedef uint8_t vlan_ingress_mode_t; + struct vlan_settings { uint16_t vlan; uint16_t members; @@ -31,9 +45,10 @@ void port_pvid_set(uint8_t port, __xdata uint16_t pvid) __banked; uint16_t port_pvid_get(uint8_t port) __banked; void vlan_create(void) __banked; void vlan_delete(uint16_t vlan) __banked; +void vlan_dump(void) __banked; void port_mirror_set(register uint8_t port, __xdata uint16_t rx_pmask, __xdata uint16_t tx_pmask) __banked; void port_mirror_del(void) __banked; -void port_ingress_filter(__xdata uint8_t port, __xdata uint8_t type) __banked; +bool port_ingress_filter(__xdata uint8_t port, __xdata vlan_ingress_mode_t type) __banked; void port_l2_setup(void) __banked; void port_lag_members_set(__xdata uint8_t lag, __xdata uint16_t members) __banked; void port_lag_hash_set(__xdata uint8_t lag, __xdata uint8_t hash) __banked; @@ -43,4 +58,7 @@ void port_eee_status_all(void) __banked; void port_eee_enable(__xdata uint8_t port, __xdata uint8_t speed) __banked; void port_eee_disable(uint8_t port) __banked; void port_eee_status(uint8_t port) __banked; +void print_port_ingress_filter_mode(vlan_ingress_mode_t mode) __banked; +bool port_ingress_vlan_filter_set(__xdata uint8_t port, __xdata bool enabled) __banked; +bool port_ingress_vlan_filter_get(__xdata uint8_t port) __banked; #endif diff --git a/rtlplayground.c b/rtlplayground.c index d2727b2..a587d00 100644 --- a/rtlplayground.c +++ b/rtlplayground.c @@ -95,6 +95,7 @@ extern __xdata struct dhcp_state dhcp_state; __xdata volatile uint8_t sbuf_ptr; __xdata uint8_t sbuf[SBUF_SIZE]; +// Registry data in sfr is in *big endian* order, so sfr_data[0] is the MSB and sfr_data[3] the LSB __xdata uint8_t sfr_data[4]; extern __xdata uint8_t gpio_last_value[8]; @@ -462,7 +463,7 @@ void reg_bit_clear(uint16_t reg_addr, char bit) /* - * This sets a bit in the 32bit wide switch register reg_addr + * This tests a bit in the 32bit wide switch register reg_addr */ uint8_t reg_bit_test(uint16_t reg_addr, char bit) { From cb8d0e45bbcef35d5dea8411538bc874b00f3e01 Mon Sep 17 00:00:00 2001 From: diijkstra <16804536+diijkstra@users.noreply.github.com> Date: Mon, 23 Mar 2026 19:31:06 +0100 Subject: [PATCH 2/2] Review: Move w initialization --- cmd_parser.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cmd_parser.c b/cmd_parser.c index 9c4dd29..8ed00e8 100644 --- a/cmd_parser.c +++ b/cmd_parser.c @@ -382,9 +382,8 @@ void parse_ingress(void) } __xdata uint8_t log_port = 0; __xdata vlan_ingress_mode_t mode = VLAN_INVALID; - __xdata uint8_t w = 1; - if (vlan_ingress_mode_parse(cmd_buffer[cmd_words_b[w]], &mode)) { + if (vlan_ingress_mode_parse(cmd_buffer[cmd_words_b[1]], &mode)) { // Setting mode for all ports at once for (log_port = machine.min_port; log_port <= machine.max_port; log_port++) { if (!port_ingress_filter(log_port, mode)) { @@ -396,7 +395,7 @@ void parse_ingress(void) } return; } else { - for(w = 1; cmd_words_b[w] > 0; w++) { + for(uint8_t w = 1; cmd_words_b[w] > 0; w++) { if (!isnumber(cmd_buffer[cmd_words_b[w]])) { continue; }