/* * This is a driver implementation for the Port features for the RTL827x platform * This code is in the Public Domain */ // #define REGDBG // #define DEBUG #include #include "rtl837x_common.h" #include "rtl837x_sfr.h" #include "rtl837x_regs.h" #include "rtl837x_port.h" #include "rtl837x_phy.h" #include "phy.h" #include "machine.h" #pragma codeseg BANK1 #pragma constseg BANK1 extern __code uint8_t * __code hex; extern __code uint16_t bit_mask[16]; extern __code struct machine machine; extern __xdata uint8_t sfr_data[4]; extern __xdata uint16_t vlan_ptr; extern __xdata uint8_t vlan_names[VLAN_NAMES_SIZE]; extern __xdata struct machine_runtime machine_detected; __xdata uint32_t l2_head; __xdata struct vlan_settings vlan_settings; void port_mirror_set(register uint8_t port, __xdata uint16_t rx_pmask, __xdata uint16_t tx_pmask) __banked { print_string("\nport_mirror_set called \n"); print_string("Mirroring port: "); print_byte(port); print_string(" with rx-mask: "); print_short(rx_pmask); print_string(", tx mask: "); print_short(tx_pmask); write_char('\n'); REG_WRITE(RTL837x_MIRROR_CONF, rx_pmask >> 8, rx_pmask, tx_pmask >> 8, tx_pmask); REG_WRITE(RTL837x_MIRROR_CTRL, 0, 0, 0, (port << 1) | 0x1); } void port_mirror_del(void) __banked { print_string("\nport_mirror_del called \n"); REG_SET(RTL837x_MIRROR_CTRL, 0); } bool port_ingress_filter(__xdata uint8_t port, __xdata vlan_ingress_mode_t type) __banked { 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 { reg_bit_clear(RTL837x_REG_INGRESS, port << 1); } if (type & 0x2) { reg_bit_set(RTL837x_REG_INGRESS, (port << 1) + 1); } 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); } /* * Define a Primary VLAN ID for a port */ void port_pvid_set(uint8_t port, __xdata uint16_t pvid) __banked { // r4e1c:00001001 R4e1c-000017d0 r6738:00000000 R6738-00000000 (no filtering) print_string("\nport_pvid_set called \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); } } uint16_t port_pvid_get(uint8_t port) __banked { uint16_t reg = RTL837x_PVID_BASE_REG + ((port >> 1) << 2); reg_read_m(reg); if (port & 0x1) { return (sfr_data[1] << 4) | (sfr_data[2] >> 4); } else { return ((sfr_data[2] & 0x0f) << 8) | sfr_data[3]; } } void vlan_delete(uint16_t vlan) __banked { print_string("\nvlan_delete called \n"); print_short(vlan); REG_WRITE(RTL837x_TBL_DATA_IN_A, 0, 0, 0, 0); REG_WRITE(RTL837X_TBL_CTRL, vlan >> 8, vlan, TBL_VLAN, TBL_WRITE | TBL_EXECUTE); } /* * Reads VLAN information from VLAN table * Returns data in sfr_data */ int8_t vlan_get(register uint16_t vlan) __banked { if (vlan >= 0xfff) // VLAN 4095 is special return -1; REG_WRITE(RTL837X_TBL_CTRL, vlan >> 8, vlan, TBL_VLAN, TBL_EXECUTE); do { reg_read_m(RTL837X_TBL_CTRL); } while (sfr_data[3] & TBL_EXECUTE); reg_read_m(RTL837x_L2_DATA_OUT_A); return 0; } __xdata uint16_t vlan_name(register uint16_t vlan) __banked { __xdata int16_t i = 0; __xdata uint8_t begin = 1; while (vlan_names[i]) { if (begin && vlan_names[i] == hex[(vlan >> 8) & 0xf] && vlan_names[i + 1] == hex[(vlan >> 4) & 0xf] && vlan_names[i + 2] == hex[vlan & 0xf]) break; begin = vlan_names[i++] == ' ' ? 1 : 0; } if (vlan_names[i]) return i + 3; return 0xffff; } /* * Create a VLAN * The arguments are passed in global structure vlan_settings * A member that is not tagged, is untagged */ void vlan_create(void) __banked { // For now, the CPU-port is always a tagged member: vlan_settings.members |= 0x0200; // Set 10th bit vlan_settings.tagged |= 0x0200; print_string("\nvlan_create called\nvlan: "); print_short(vlan_settings.vlan); print_string(", members: "); print_short(vlan_settings.members); print_string(", tagged: "); print_short(vlan_settings.tagged); write_char('\n'); uint16_t a = (~vlan_settings.members) ^ vlan_settings.tagged ^ vlan_settings.members; // On RTL8372, port-bits 0-2 must be 0, although they are not members if (!machine_detected.isRTL8373) { a &= 0x1f8; vlan_settings.tagged &= 0x3f8; } // Initialize VLAN table with VLAN 1 REG_WRITE(RTL837x_TBL_DATA_IN_A, 0x02, (a >> 6) & 0x0f, (a << 2) | (vlan_settings.members >> 8), vlan_settings.members); REG_WRITE(RTL837X_TBL_CTRL, vlan_settings.vlan >> 8, vlan_settings.vlan, TBL_VLAN, TBL_WRITE | TBL_EXECUTE); do { reg_read_m(RTL837X_TBL_CTRL); } while (sfr_data[3] & TBL_EXECUTE); print_string("vlan_create done \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("\nvlan_setup called \n"); // No VLAN names set up so far vlan_ptr = 0; vlan_names[0] = 0; // Initialize VLAN table for VLAN 1, by disabling that entry REG_SET(RTL837x_TBL_DATA_IN_A, machine_detected.isRTL8373? 0x0007ffff : 0x0007e3f8); REG_SET(RTL837X_TBL_CTRL, 0x00010303); do { reg_read_m(RTL837X_TBL_CTRL); } while (sfr_data[3] & TBL_EXECUTE); // Set PVID 1 for every port. TODO: Skip unused ports! for (uint8_t i = machine.min_port; i <= machine.max_port + 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('='); print_short(reg); write_char(' '); #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 (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); // 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"); #endif } // Ingress filtering. 2 bits per port: allow tagged (01) / untagged (10) and all (00) REG_SET(RTL837x_REG_INGRESS, 0); // No filtering for all ports // Enable 4k VLAN REG_SET(RTL837X_VLAN_CTRL, VLAN_CVLAN_FILTER); REG_SET(RTL837X_VLAN_L2_LRN_DIS_0, 0); REG_SET(RTL837X_VLAN_L2_LRN_DIS_1, 0); // Enable VLAN 1: Ports 0-9, i.e. including the CPU port are untagged members REG_SET(RTL837x_TBL_DATA_IN_A, machine_detected.isRTL8373? 0x0207ffff : 0x0207e3f8); // 02: Entry valid, 7...: membership REG_SET(RTL837X_TBL_CTRL, 0x00010303); // Write VLAN 1 do { reg_read_m(RTL837X_TBL_CTRL); } while (sfr_data[3] & TBL_EXECUTE); #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); print_string("\nvlan_setup, REG 0x4e34: "); print_reg(0x4e34); print_string("\nvlan_setup, REG 0x4f48: "); print_reg(0x4f48); print_string("\nvlan_setup, REG 0x4f4c: "); print_reg(0x4f4c); #endif print_string("vlan_setup done \n"); } /* * Forget all dynamic L2 learned entries */ uint8_t port_l2_forget(void) __banked { print_string("\nport_l2_forget called\n"); // Configure the entries to be flushed: // port-based (bits 0-1 are 0 and dynamic entries, bit 2 specifies dynamic entries REG_SET(RTL837x_L2_TBL_FLUSH_CNF, 0x0); // Flush L2 table for all ports by setting the ports and the flush-exec bit (bit 16) REG_SET(RTL837x_L2_TBL_FLUSH_CTRL, L2_TBL_FLUSH_EXEC | (machine_detected.isRTL8373 ? PMASK_9 : PMASK_6)); // Wait for flush completed do { reg_read_m(RTL837x_L2_TBL_FLUSH_CTRL); } while (sfr_data[1]); print_string("port_l2_forget done\n"); return 0; } void port_l2_learned(void) __banked { // Whait for any table action to be finished do { reg_read_m(RTL837X_TBL_CTRL); } while (sfr_data[3] & 0x01); print_string("\n\tMAC\t\tVLAN\ttype\tport\n"); __xdata uint16_t entry = 0x0000; __xdata uint16_t first_entry = 0xffff; // Table does not have that many entries while (1) { uint8_t port = 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_CTRL, (entry >> 8) & 0xf, entry, TBL_L2_UNICAST, TBL_EXECUTE); do { reg_read_m(RTL837X_TBL_CTRL); } while (sfr_data[3] & TBL_EXECUTE); reg_read_m(RTL837x_TBL_DATA_0); entry = (((uint16_t)sfr_data[2] & 0x0f) << 8) | sfr_data[3]; if (first_entry == 0xffff) { first_entry = entry; } else { if (first_entry == entry) break; } // MAC reg_read_m(RTL837x_L2_DATA_OUT_B); 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; reg_read_m(RTL837x_L2_DATA_OUT_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_DATA_OUT_B); print_short( (((uint16_t) (sfr_data[0] & 0x0f)) << 8) | sfr_data[1]); // VLAN // type reg_read_m(RTL837x_L2_DATA_OUT_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(machine.log_to_phys_port[port] + '0'); else print_string("CPU"); } entry++; print_string("\n"); } } /* * Basic L2 configuration such as time to forget an entry */ void port_l2_setup(void) __banked { print_string("\nport_l2_setup called\n"); port_l2_forget(); for (uint8_t i = machine.min_port; i <= machine.max_port; i++) { // Limit the number of automatically learned MAC-Entries per port to 0x1040 uint16_t reg = RTL837X_L2_LRN_PORT_CONSTRAINT + (i << 2); REG_SET(reg, 0x00001040); // All ports may communicate with each other and CPU-Port reg = RTL837X_PORT_ISOLATION_BASE + (i << 2); REG_SET(reg, PMASK_CPU | (machine_detected.isRTL8373? PMASK_9 : PMASK_6)); } // When maximim entries learned, then simply flood the packet reg_bit_set(RTL837X_L2_LRN_PORT_CONSTRT_ACT, 0); print_string("\nport_l2_setup done\n"); } void port_stats_print(void) __banked { print_string("\n Port\tState\tLink\tTxGood\t\tTxBad\t\tRxGood\t\tRxBad\n"); for (uint8_t i = machine.min_port; i <= machine.max_port; i++) { write_char('0' + machine.log_to_phys_port[i]); write_char('\t'); if (!machine.is_sfp[i]) { phy_read(i, PHY_MMD31, 0xa610); if (SFR_DATA_8 == 0x20) print_string("On\t"); else print_string("Off\t"); } else { // An SFP Module if (!gpio_pin_test(machine.sfp_port[machine.is_sfp[i]-1].pin_detect)) { print_string("SFP IN\t"); } else { print_string("NO SFP\t"); } } uint8_t b = 0; // Determine link state reg_read_m(RTL837X_REG_LINKS_STS); if(!((sfr_data[(i / 8) + 1] >> ( i % 8 ) & 1))) { b = 99; } else { if (i < 8) reg_read_m(RTL837X_REG_LINKS); else reg_read_m(RTL837X_REG_LINKS_89); b = sfr_data[3 - ((i & 7) >> 1)]; b = (i & 1) ? b >> 4 : b & 0xf; } switch (b) { case 0: print_string("10M\t"); break; case 1: print_string("100M\t"); break; case 2: print_string("1000M\t"); break; case 4: print_string("10G\t"); break; case 5: print_string("2.5G\t"); break; case 6: print_string("5G\t"); break; case 99: print_string("Down\t"); break; default: print_string("Up\t"); break; } STAT_GET(STAT_COUNTER_TX_PKTS, i); print_reg(RTL837X_STAT_V_LOW); write_char('\t'); STAT_GET(STAT_COUNTER_ERR_PKTS, i); print_reg(RTL837X_STAT_V_LOW); write_char('\t'); STAT_GET(STAT_COUNTER_RX_PKTS, i); print_reg(RTL837X_STAT_V_LOW); write_char('\t'); STAT_GET(STAT_COUNTER_ERR_PKTS, i); print_reg(RTL837X_STAT_V_HIGH); write_char('\t'); print_string("\n"); } } void port_isolate(register uint8_t port, __xdata uint16_t pmask) __banked { if (port <= machine.max_port) REG_SET(RTL837X_PORT_ISOLATION_BASE + (port << 2), pmask); } uint16_t port_isolation_get(register uint8_t port) __banked { if (port > machine.max_port) return 0; reg_read_m(RTL837X_PORT_ISOLATION_BASE + (port << 2)); return ((uint16_t)sfr_data[2]) << 8 | sfr_data[3]; } void port_eee_enable(__xdata uint8_t port,__xdata uint8_t speed) __banked { if (machine.is_sfp[port]) { print_string("EEE can't be enabled for SFP port "); print_byte(port); print_string("\n"); return; } REG_SET(RTL837X_EEE_CTRL_BASE + (port << 8), EEE_RX_ENABLE | EEE_TX_ENABLE); print_string("EEE on for "); print_byte(port); print_string(" speed "); // Enable all speeds up to the specified speed if ((speed & (EEE_100 | EEE_1000 | EEE_2G5 | EEE_5G | EEE_10G)) == EEE_100) { print_string("100m\n"); // Enable EEE advertisement for 100BASE-T via EEE Advertisement Reg phy_write(port, PHY_MMD_AN, PHY_EEE_ADV, PHY_EEE_BIT_100M); if (!(speed & EEE_NORESET)) phy_reset(port); return; } if ((speed & (EEE_100 | EEE_1000 | EEE_2G5 | EEE_5G | EEE_10G)) == EEE_1000) { print_string("1g\n"); // Disable EEE advertisement for 2.5GBASE-T via EEE Advertisement Reg 2 phy_write(port, PHY_MMD_AN, PHY_EEE_ADV2, 0); // Enable EEE advertisement for 100/1000BASE-T via EEE Advertisement Reg phy_write(port, PHY_MMD_AN, PHY_EEE_ADV, PHY_EEE_BIT_1G | PHY_EEE_BIT_100M); if (!(speed & EEE_NORESET)) phy_reset(port); return; } if ((speed & (EEE_100 | EEE_1000 | EEE_2G5 | EEE_5G | EEE_10G)) == EEE_2G5) { print_string("2g5\n"); // Enable EEE advertisement for 100/1000BASE-T via EEE Advertisement Reg phy_write(port, PHY_MMD_AN, PHY_EEE_ADV, PHY_EEE_BIT_1G | PHY_EEE_BIT_100M); // Enable EEE advertisement for 2.5GBASE-T via EEE Advertisement Reg 2 phy_write(port, PHY_MMD_AN, PHY_EEE_ADV2, PHY_EEE_BIT_2G5); if (!(speed & EEE_NORESET)) phy_reset(port); return; } if ((speed & (EEE_100 | EEE_1000 | EEE_2G5 | EEE_5G | EEE_10G)) == EEE_5G) { print_string("5g\n"); // Enable EEE advertisement for 100/1000BASE-T via EEE Advertisement Reg phy_write(port, PHY_MMD_AN, PHY_EEE_ADV, PHY_EEE_BIT_1G | PHY_EEE_BIT_100M); // Enable EEE advertisement for 2.5GBASE-T via EEE Advertisement Reg 2 phy_write(port, PHY_MMD_AN, PHY_EEE_ADV2, PHY_EEE_BIT_2G5 | PHY_EEE_BIT_5G); if (!(speed & EEE_NORESET)) phy_reset(port); return; } if ((speed & (EEE_100 | EEE_1000 | EEE_2G5 | EEE_5G | EEE_10G)) == EEE_10G) { print_string("10g\n"); // Enable EEE advertisement for 100/1000BASE-T via EEE Advertisement Reg phy_write(port, PHY_MMD_AN, PHY_EEE_ADV, PHY_EEE_BIT_10G | PHY_EEE_BIT_1G | PHY_EEE_BIT_100M); // Enable EEE advertisement for 2.5GBASE-T via EEE Advertisement Reg 2 phy_write(port, PHY_MMD_AN, PHY_EEE_ADV2, PHY_EEE_BIT_2G5 | PHY_EEE_BIT_5G); if (!(speed & EEE_NORESET)) phy_reset(port); return; } } void port_eee_disable(uint8_t port) __banked { if (machine.is_sfp[port]) return; print_string("EEE off for "); print_byte(port); write_char('\n'); REG_SET(RTL837X_EEE_CTRL_BASE + (port << 8), 0); // Disable EEE advertisement for 100/1000BASE-T via EEE Advertisement Reg phy_write(port, PHY_MMD_AN, PHY_EEE_ADV, 0); // Disable EEE advertisement for 2.5GBASE-T via EEE Advertisement Reg 2 phy_write(port, PHY_MMD_AN, PHY_EEE_ADV2, 0); phy_reset(port); } void port_eee_status(uint8_t port) __banked { print_string("Port: "); write_char('0' + machine.log_to_phys_port[port]); print_string(": "); if (machine.is_sfp[port]) { print_string("SFP\n"); return; } uint16_t v; print_string("Advertising: "); if (machine.n_10g) { phy_read(port, PHY_MMD_AN, PHY_EEE_ADV); v = SFR_DATA_U16; if (v & PHY_EEE_BIT_10G) print_string(" 10G"); else print_string(" "); } phy_read(port, PHY_MMD_AN, PHY_EEE_ADV2); v = SFR_DATA_U16; if (machine.n_10g) { if (v & PHY_EEE_BIT_5G) print_string(" 5G"); else print_string(" "); } v = SFR_DATA_U16; if (v & PHY_EEE_BIT_2G5) print_string(" 2.5G"); else print_string(" "); phy_read(port, PHY_MMD_AN, PHY_EEE_ADV); v = SFR_DATA_U16; if (v & PHY_EEE_BIT_1G) print_string(" 1G "); else print_string(" "); if (v & PHY_EEE_BIT_100M) print_string(" 100M"); else print_string(" "); print_string(" Link Partner: "); if (machine.n_10g) { phy_read(port, PHY_MMD_AN, PHY_EEE_LP_ABILITY); v = SFR_DATA_U16; if (v & PHY_EEE_BIT_10G) print_string(" 10G"); else print_string(" "); } phy_read(port, PHY_MMD_AN, PHY_EEE_LP_ABILITY2); v = SFR_DATA_U16; if (machine.n_10g) { if (v & PHY_EEE_BIT_5G) print_string(" 5G"); else print_string(" "); } if (v & PHY_EEE_BIT_2G5) print_string(" 2.5G"); else print_string(" "); phy_read(port, PHY_MMD_AN, PHY_EEE_LP_ABILITY); v = SFR_DATA_U16; if (v & PHY_EEE_BIT_1G) print_string(" 1G "); else print_string(" "); if (v & PHY_EEE_BIT_100M) print_string(" 100M"); else print_string(" "); reg_read_m(RTL8373_PHY_EEE_ABLTY); if (sfr_data[3] & (1 << port)) print_string(" ACTIVE "); else print_string(" INACTIVE "); write_char('\n'); } void port_eee_enable_all(__xdata uint8_t speed) __banked { for (uint8_t i = machine.min_port; i <= machine.max_port; i++) { port_eee_enable(i, speed); } } void port_eee_disable_all(void) __banked { for (uint8_t i = machine.min_port; i <= machine.max_port; i++) { port_eee_disable(i); } } void port_eee_status_all(void) __banked { for (uint8_t i = machine.min_port; i <= machine.max_port; i++) { port_eee_status(i); } } /* * Enable RLDP, Realtek's version of LLDP */ void port_rldp_on(__xdata uint16_t p_ms) { REG_WRITE(RTL8373_RLDP_TIMER, p_ms >> 8, p_ms, p_ms >> 8, p_ms); REG_SET(RTL837X_RMA0_CONF, 0x00000000); // R4ecc REG_SET(RTL837X_RMA_CONF, 0x00000000); // R4ecc } /* * Configure LAGs * Sets the members via port bitmask of a given Link Aggregation Group * The groups have numbers 0-3 * The bitmask represents up to 10 ports * If currently no LAG has algorithm used, a default is applied */ void port_lag_members_set(__xdata uint8_t lag, __xdata uint16_t members) __banked { print_string("port_lag_members_set, lag: "); print_byte(lag); print_string(", members: "); print_short(members); write_char('\n'); if (lag > 3) print_string("Link aggregation group must be 0-3!\n"); reg_read_m(RTL837X_TRK_HASH_CTRL_BASE + (lag << 2)); if (!(sfr_data[0] | sfr_data [1] | sfr_data [2] | sfr_data [3])) REG_SET(RTL837X_TRK_HASH_CTRL_BASE, LAG_HASH_DEFAULT); REG_WRITE(RTL837X_TRK_MBR_CTRL_BASE + (lag << 2), 0, 0, members >> 8, members & 0xff); } /* * Configures the hash algorithm used for a LAG * lag is the Group to configure and hash is a bitmask */ void port_lag_hash_set(__xdata uint8_t lag, __xdata uint8_t hash_bits) __banked { print_string("port_lag_hash_set, lag: "); print_byte(lag); print_string(", hash: "); print_byte(hash_bits); write_char('\n'); if (lag > 3) print_string("Link aggregation group must be 0-3!\n"); 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); }