diff --git a/cmd_parser.c b/cmd_parser.c index 754740c5..d9f0f041 100644 --- a/cmd_parser.c +++ b/cmd_parser.c @@ -48,6 +48,7 @@ __xdata uint16_t vlan_ptr; __xdata char port_names[9][PORT_NAME_SIZE]; extern __xdata uint16_t management_vlan; +extern __xdata struct uip_eth_addr uip_ethaddr; extern __xdata uint8_t sfp_speed[2]; extern __xdata uint8_t sfp_pins_last; extern __xdata uint8_t sfp_options[2]; @@ -476,7 +477,9 @@ void parse_vlan(void) vlan_settings.vlan = atoi_results_short; if (cmd_compare(2, "mgmt")) { + port_l2_static_mgmt(uip_ethaddr.addr, management_vlan, true); management_vlan = vlan_settings.vlan; + port_l2_static_mgmt(uip_ethaddr.addr, management_vlan, false); if (!vlan_settings.vlan) print_string("Management VLAN disabled\n"); else diff --git a/rtl837x_port.c b/rtl837x_port.c index 325256f3..1c966728 100644 --- a/rtl837x_port.c +++ b/rtl837x_port.c @@ -911,3 +911,57 @@ bool port_ingress_vlan_filter_get(uint8_t port) __banked return reg_bit_test(RTL837X_VLAN_PORT_IGR_FLTR, port); } + +// C1 bit 0: static (no aging); age at C3[4:2] must be non-zero or the +// entry is invisible to lookup +static void l2_mgmt_entry_fill(__xdata uint8_t *mac, __xdata uint16_t vlan) +{ + REG_WRITE(RTL837x_TBL_DATA_IN_A, mac[2], mac[3], mac[4], mac[5]); + REG_WRITE(RTL837x_TBL_DATA_IN_B, 0x20 | ((CPU_PORT & 0x3) << 6) | (vlan >> 8), + vlan & 0xff, mac[0], mac[1]); + REG_WRITE(RTL837x_TBL_DATA_IN_C, 0x00, 0x01, 0x00, (7 << 2) | (CPU_PORT >> 2)); +} + +static void l2_mgmt_tbl_prepare(void) +{ + reg_read_m(RTL837x_TBL_DATA_0); + sfr_data[2] &= 0x3f; + sfr_data[1] &= 0xf8; + reg_write_m(RTL837x_TBL_DATA_0); +} + +static void wait_table_ready(void) +{ + do { + reg_read(RTL837X_TBL_CTRL); + } while (SFR_DATA_0 & TBL_EXECUTE); +} + +/** Pin the management MAC to the CPU port; remove_entry deletes it */ +void port_l2_static_mgmt(__xdata uint8_t *mac, __xdata uint16_t vlan, __xdata bool remove_entry) __banked +{ + wait_table_ready(); + + // vlan 0 = management VLAN disabled: untagged management resolves in + // VLAN 1; clients in other VLANs keep their learned entry and age as before + if (!vlan) + vlan = 1; + l2_mgmt_entry_fill(mac, vlan); + l2_mgmt_tbl_prepare(); + if (remove_entry) { + // a search miss returns index 0, not a slot + REG_WRITE(RTL837X_TBL_CTRL, 0x00, 0x00, TBL_L2_UNICAST, TBL_EXECUTE); + wait_table_ready(); + reg_read_m(RTL837x_TBL_DATA_0); + if (!(sfr_data[2] & 0x10)) + return; + sfr_data[1] |= 0x04; // CLEAR-entry + reg_write_m(RTL837x_TBL_DATA_0); + __xdata uint16_t idx = (((uint16_t)sfr_data[2] & 0x0f) << 8) | sfr_data[3]; + REG_WRITE(RTL837X_TBL_CTRL, idx >> 8, idx & 0xff, TBL_L2_UNICAST, TBL_WRITE | TBL_EXECUTE); + } else { + // method-0 write: the hardware hashes the key and places the entry + REG_WRITE(RTL837X_TBL_CTRL, 0x00, 0x00, TBL_L2_UNICAST, TBL_WRITE | TBL_EXECUTE); + } + wait_table_ready(); +} diff --git a/rtl837x_port.h b/rtl837x_port.h index 692a14da..0325249c 100644 --- a/rtl837x_port.h +++ b/rtl837x_port.h @@ -81,5 +81,6 @@ bool port_ingress_vlan_filter_get(uint8_t port) __banked; vlan_ingress_mode_t port_ingress_filter_get(__xdata uint8_t port) __banked; void port_isolate(uint8_t port, __xdata uint16_t pmask) __banked; uint16_t port_isolation_get(uint8_t port) __banked; +void port_l2_static_mgmt(__xdata uint8_t *mac, __xdata uint16_t vlan, __xdata bool remove_entry) __banked; #endif diff --git a/rtlplayground.c b/rtlplayground.c index 70b8582b..2b4c909c 100644 --- a/rtlplayground.c +++ b/rtlplayground.c @@ -2306,6 +2306,8 @@ void main(void) early_boot_handle_button(); execute_config(); + // After the config so the entry lands in the final management VLAN + port_l2_static_mgmt(uip_ethaddr.addr, management_vlan, false); /* After the config: a name from it wins, otherwise derive one. */ set_hostname_default(); print_cmd_prompt();