
The new command: ethsw [port <port_no>] [vlan <vid>] fdb { [help] | show | flush | { add | del } <mac> }
Can be used to add and delete FDB entries. Also, the command can be used to show entries from the FDB tables. When used with [port <port_no>] and [vlan <vid>], only the matching the FDB entries can be seen or flushed.
Signed-off-by: Johnson Leung johnson.leung@freescale.com Signed-off-by: Codrin Ciubotariu codrin.ciubotariu@freescale.com Change-Id: I63f2df7d2b5c885c96be4fec3874eaf994e3c26f --- drivers/net/vsc9953.c | 635 +++++++++++++++++++++++++++++++++++++++++++++++++- include/vsc9953.h | 28 +++ 2 files changed, 662 insertions(+), 1 deletion(-)
diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c index 1936c4a..ef7b50c 100644 --- a/drivers/net/vsc9953.c +++ b/drivers/net/vsc9953.c @@ -12,6 +12,7 @@ #include <fsl_memac.h> #include <errno.h> #include <vsc9953.h> +#include <linux/ctype.h>
static struct vsc9953_info vsc9953_l2sw = { .port[0] = VSC9953_PORT_INFO_INITIALIZER(0), @@ -579,6 +580,7 @@ void vsc9953_init(bd_t *bis)
#define VSC9953_MAX_CMD_PARAMS 20 #define VSC9953_CMD_PORT_ALL -1 +#define VSC9953_CMD_VLAN_ALL -1
/* Enable/disable status of a VSC9953 port */ static void vsc9953_port_status_set(int port_no, u8 enabled) @@ -952,6 +954,365 @@ static void vsc9953_port_statistics_clear(int port_no) CONFIG_VSC9953_STAT_CLEAR_DR); }
+/* wait for FDB to become available */ +static int vsc9953_mac_table_poll_idle(void) +{ + struct vsc9953_analyzer *l2ana_reg; + u32 timeout; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + timeout = 50000; + while (((in_le32(&l2ana_reg->ana_tables.mac_access) & + CONFIG_VSC9953_MAC_CMD_MASK) != + CONFIG_VSC9953_MAC_CMD_IDLE) && --timeout) + udelay(1); + + return !!timeout; +} + +/* enum describing available commands for the MAC table */ +enum mac_table_cmd { + MAC_TABLE_READ_DIRECT, + MAC_TABLE_READ_INDIRECT, + MAC_TABLE_WRITE, + MAC_TABLE_LEARN, + MAC_TABLE_FORGET, + MAC_TABLE_GET_NEXT, + MAC_TABLE_AGE, +}; + +/* Issues a command to the FDB table */ +static int vsc9953_mac_table_cmd(enum mac_table_cmd cmd) +{ + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + switch (cmd) { + case MAC_TABLE_READ_DIRECT: + clrsetbits_le32(&l2ana_reg->ana_tables.mac_access, + CONFIG_VSC9953_MAC_CMD_MASK | + CONFIG_VSC9953_MAC_CMD_VALID, + CONFIG_VSC9953_MAC_CMD_READ); + break; + case MAC_TABLE_READ_INDIRECT: + clrsetbits_le32(&l2ana_reg->ana_tables.mac_access, + CONFIG_VSC9953_MAC_CMD_MASK, + CONFIG_VSC9953_MAC_CMD_READ | + CONFIG_VSC9953_MAC_CMD_VALID); + break; + case MAC_TABLE_WRITE: + clrsetbits_le32(&l2ana_reg->ana_tables.mac_access, + CONFIG_VSC9953_MAC_CMD_MASK | + CONFIG_VSC9953_MAC_ENTRYTYPE_MASK, + CONFIG_VSC9953_MAC_CMD_WRITE | + CONFIG_VSC9953_MAC_ENTRYTYPE_LOCKED); + break; + case MAC_TABLE_LEARN: + clrsetbits_le32(&l2ana_reg->ana_tables.mac_access, + CONFIG_VSC9953_MAC_CMD_MASK | + CONFIG_VSC9953_MAC_ENTRYTYPE_MASK, + CONFIG_VSC9953_MAC_CMD_LEARN | + CONFIG_VSC9953_MAC_ENTRYTYPE_LOCKED | + CONFIG_VSC9953_MAC_CMD_VALID); + break; + case MAC_TABLE_FORGET: + clrsetbits_le32(&l2ana_reg->ana_tables.mac_access, + CONFIG_VSC9953_MAC_CMD_MASK | + CONFIG_VSC9953_MAC_ENTRYTYPE_MASK, + CONFIG_VSC9953_MAC_CMD_FORGET); + break; + case MAC_TABLE_GET_NEXT: + clrsetbits_le32(&l2ana_reg->ana_tables.mac_access, + CONFIG_VSC9953_MAC_CMD_MASK | + CONFIG_VSC9953_MAC_ENTRYTYPE_MASK, + CONFIG_VSC9953_MAC_CMD_NEXT); + break; + case MAC_TABLE_AGE: + clrsetbits_le32(&l2ana_reg->ana_tables.mac_access, + CONFIG_VSC9953_MAC_CMD_MASK | + CONFIG_VSC9953_MAC_ENTRYTYPE_MASK, + CONFIG_VSC9953_MAC_CMD_AGE); + break; + default: + printf("Unknown MAC table command\n"); + } + + if (!vsc9953_mac_table_poll_idle()) { + debug("MAC table timeout\n"); + return -1; + } + + /* For some commands we might have a way to + * detect if the command succeeded + */ + if ((cmd == MAC_TABLE_GET_NEXT || cmd == MAC_TABLE_READ_DIRECT || + MAC_TABLE_READ_INDIRECT) && + !(in_le32(&l2ana_reg->ana_tables.mac_access) & + CONFIG_VSC9953_MAC_CMD_VALID)) + return -1; + + return 0; +} + +/* show the FDB entries that correspond to a port and a VLAN */ +static void vsc9953_mac_table_show(int port_no, int vid) +{ + int i, rc[VSC9953_MAX_PORTS]; + u32 val, vlan, mach, macl, dest_indx; + enum port_learn_mode mode[VSC9953_MAX_PORTS]; + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + /* disable auto learning */ + if (port_no == VSC9953_CMD_PORT_ALL) { + for (i = 0; i < VSC9953_MAX_PORTS; i++) { + rc[i] = vsc9953_port_learn_mode_get(i, &mode[i]); + if (!rc[i] && mode[i] != PORT_LEARN_NONE) + vsc9953_port_learn_mode_set(i, + PORT_LEARN_NONE); + } + } else { + rc[port_no] = vsc9953_port_learn_mode_get(port_no, + &mode[port_no]); + if (!rc[port_no] && mode[port_no] != PORT_LEARN_NONE) + vsc9953_port_learn_mode_set(port_no, PORT_LEARN_NONE); + } + + /* write port and vid to get selected FDB entries */ + val = in_le32(&l2ana_reg->ana.anag_efil); + if (port_no != VSC9953_CMD_PORT_ALL) { + val = (val & ~CONFIG_VSC9953_AGE_PORT_MASK) | + CONFIG_VSC9953_AGE_PORT_EN | + field_set(port_no, + CONFIG_VSC9953_AGE_PORT_MASK); + } + if (vid != VSC9953_CMD_VLAN_ALL) { + val = (val & ~CONFIG_VSC9953_AGE_VID_MASK) | + CONFIG_VSC9953_AGE_VID_EN | + field_set(vid, CONFIG_VSC9953_AGE_VID_MASK); + } + out_le32(&l2ana_reg->ana.anag_efil, val); + + /* set MAC and VLAN to 0 to look from beginning */ + clrbits_le32(&l2ana_reg->ana_tables.mach_data, + CONFIG_VSC9953_MAC_VID_MASK | + CONFIG_VSC9953_MAC_MACH_MASK); + out_le32(&l2ana_reg->ana_tables.macl_data, 0); + + /* get entries */ + printf("%10s %17s %5s %4s\n", "EntryType", "MAC", "PORT", "VID"); + do { + /* get out when an invalid entry is found */ + if (vsc9953_mac_table_cmd(MAC_TABLE_GET_NEXT)) + break; + + val = in_le32(&l2ana_reg->ana_tables.mac_access); + + switch (val & CONFIG_VSC9953_MAC_ENTRYTYPE_MASK) { + case CONFIG_VSC9953_MAC_ENTRYTYPE_NORMAL: + printf("%10s ", "Dynamic"); + break; + case CONFIG_VSC9953_MAC_ENTRYTYPE_LOCKED: + printf("%10s ", "Static"); + break; + case CONFIG_VSC9953_MAC_ENTRYTYPE_IPV4MCAST: + printf("%10s ", "IPv4 Mcast"); + break; + case CONFIG_VSC9953_MAC_ENTRYTYPE_IPV6MCAST: + printf("%10s ", "IPv6 Mcast"); + break; + default: + printf("%10s ", "Unknown"); + } + + dest_indx = field_get(val & CONFIG_VSC9953_MAC_DESTIDX_MASK, + CONFIG_VSC9953_MAC_DESTIDX_MASK); + + val = in_le32(&l2ana_reg->ana_tables.mach_data); + vlan = field_get(val & CONFIG_VSC9953_MAC_VID_MASK, + CONFIG_VSC9953_MAC_VID_MASK); + mach = field_get(val & CONFIG_VSC9953_MAC_MACH_MASK, + CONFIG_VSC9953_MAC_MACH_MASK); + macl = in_le32(&l2ana_reg->ana_tables.macl_data); + + printf("%02x:%02x:%02x:%02x:%02x:%02x ", (mach >> 8) & 0xff, + mach & 0xff, (macl >> 24) & 0xff, (macl >> 16) & 0xff, + (macl >> 8) & 0xff, macl & 0xff); + printf("%5d ", dest_indx); + printf("%4d\n", vlan); + } while (1); + + /* set learning mode to previous value */ + if (port_no == VSC9953_CMD_PORT_ALL) { + for (i = 0; i < VSC9953_MAX_PORTS; i++) { + if (!rc[i] && mode[i] != PORT_LEARN_NONE) + vsc9953_port_learn_mode_set(i, mode[i]); + } + } else { + /* If administrative down, skip */ + if (!rc[port_no] && mode[port_no] != PORT_LEARN_NONE) + vsc9953_port_learn_mode_set(port_no, mode[port_no]); + } + + /* reset FDB port and VLAN FDB selection */ + clrbits_le32(&l2ana_reg->ana.anag_efil, CONFIG_VSC9953_AGE_PORT_EN | + CONFIG_VSC9953_AGE_PORT_MASK | CONFIG_VSC9953_AGE_VID_EN | + CONFIG_VSC9953_AGE_VID_MASK); +} + +/* Add a static FDB entry */ +static int vsc9953_mac_table_add(u8 port_no, uchar mac[6], int vid) +{ + u32 val; + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + out_le32(&l2ana_reg->ana_tables.mach_data, + (mac[0] << 8) | (mac[1] << 0) | + (field_set(vid, CONFIG_VSC9953_MACHDATA_VID_MASK) & + CONFIG_VSC9953_MACHDATA_VID_MASK)); + out_le32(&l2ana_reg->ana_tables.macl_data, + (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | + (mac[5] << 0)); + + /* set on which port is the MAC address added */ + clrsetbits_le32(&l2ana_reg->ana_tables.mac_access, + CONFIG_VSC9953_MAC_DESTIDX_MASK, + field_set(port_no, CONFIG_VSC9953_MAC_DESTIDX_MASK)); + if (vsc9953_mac_table_cmd(MAC_TABLE_LEARN)) + return -1; + + /* check if the MAC address was indeed added */ + out_le32(&l2ana_reg->ana_tables.mach_data, + (mac[0] << 8) | (mac[1] << 0) | + (field_set(vid, CONFIG_VSC9953_MACHDATA_VID_MASK) & + CONFIG_VSC9953_MACHDATA_VID_MASK)); + out_le32(&l2ana_reg->ana_tables.macl_data, + (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | + (mac[5] << 0)); + + if (vsc9953_mac_table_cmd(MAC_TABLE_READ_DIRECT)) + return -1; + + val = in_le32(&l2ana_reg->ana_tables.mac_access); + + if ((port_no != field_get(val & CONFIG_VSC9953_MAC_DESTIDX_MASK, + CONFIG_VSC9953_MAC_DESTIDX_MASK))) { + printf("Failed to add MAC address\n"); + return -1; + } + return 0; +} + +/* Delete a FDB entry */ +static int vsc9953_mac_table_del(uchar mac[6], u16 vid) +{ + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + /* check first if MAC entry is present */ + out_le32(&l2ana_reg->ana_tables.mach_data, + (mac[0] << 8) | (mac[1] << 0) | + (field_set(vid, CONFIG_VSC9953_MACHDATA_VID_MASK) & + CONFIG_VSC9953_MACHDATA_VID_MASK)); + out_le32(&l2ana_reg->ana_tables.macl_data, + (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | + (mac[5] << 0)); + + if (vsc9953_mac_table_cmd(MAC_TABLE_READ_INDIRECT)) { + printf("The MAC address: %02x:%02x:%02x:%02x:%02x:%02x ", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + printf("VLAN: %d does not exist.\n", vid); + return -1; + } + + /* FDB entry found, proceed to delete */ + out_le32(&l2ana_reg->ana_tables.mach_data, (mac[0] << 8) | + (mac[1] << 0) | + (field_set(vid, CONFIG_VSC9953_MACHDATA_VID_MASK) & + CONFIG_VSC9953_MACHDATA_VID_MASK)); + out_le32(&l2ana_reg->ana_tables.macl_data, (mac[2] << 24) | + (mac[3] << 16) | (mac[4] << 8) | (mac[5] << 0)); + + if (vsc9953_mac_table_cmd(MAC_TABLE_FORGET)) + return -1; + + /* check if the MAC entry is still in FDB */ + out_le32(&l2ana_reg->ana_tables.mach_data, (mac[0] << 8) | + (mac[1] << 0) | + (field_set(vid, CONFIG_VSC9953_MACHDATA_VID_MASK) & + CONFIG_VSC9953_MACHDATA_VID_MASK)); + out_le32(&l2ana_reg->ana_tables.macl_data, (mac[2] << 24) | + (mac[3] << 16) | (mac[4] << 8) | (mac[5] << 0)); + + if (vsc9953_mac_table_cmd(MAC_TABLE_READ_INDIRECT)) { + printf("Failed to delete MAC address\n"); + return -1; + } + + return 0; +} + +/* age the unlocked entries in FDB */ +static void vsc9953_mac_table_age(int port_no, int vid) +{ + int rc; + u32 val; + struct vsc9953_analyzer *l2ana_reg; + enum port_learn_mode mode; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + /* disable auto learning */ + rc = vsc9953_port_learn_mode_get(port_no, &mode); + if (!rc && mode != PORT_LEARN_NONE) + vsc9953_port_learn_mode_set(port_no, PORT_LEARN_NONE); + + /* set port and VID for selective aging */ + val = in_le32(&l2ana_reg->ana.anag_efil); + if (port_no != VSC9953_CMD_PORT_ALL) { + val = (val & ~CONFIG_VSC9953_AGE_PORT_MASK) | + CONFIG_VSC9953_AGE_PORT_EN | + field_set(port_no, CONFIG_VSC9953_AGE_PORT_MASK); + } + + if (vid != VSC9953_CMD_VLAN_ALL) { + val = (val & ~CONFIG_VSC9953_AGE_VID_MASK) | + CONFIG_VSC9953_AGE_VID_EN | + field_set(vid, CONFIG_VSC9953_AGE_VID_MASK); + } + out_le32(&l2ana_reg->ana.anag_efil, val); + + /* age the dynamic FDB entries */ + vsc9953_mac_table_cmd(MAC_TABLE_AGE); + + /* clear previously set port and VID */ + clrbits_le32(&l2ana_reg->ana.anag_efil, CONFIG_VSC9953_AGE_PORT_EN | + CONFIG_VSC9953_AGE_PORT_MASK | CONFIG_VSC9953_AGE_VID_EN | + CONFIG_VSC9953_AGE_VID_MASK); + + if (!rc && mode != PORT_LEARN_NONE) + vsc9953_port_learn_mode_set(port_no, mode); +} + +/* Delete all the dynamic FDB entries */ +static void vsc9953_mac_table_flush(int port, int vid) +{ + vsc9953_mac_table_age(port, vid); + vsc9953_mac_table_age(port, vid); +} + /* IDs used to track keywords in a command */ enum keyword_id { id_key_end = -1, @@ -964,11 +1325,19 @@ enum keyword_id { id_clear, id_learning, id_auto, + id_vlan, + id_fdb, + id_add, + id_del, + id_flush, id_count, /* keep last */ };
enum keyword_opt_id { id_port_no = id_count + 1, + id_vlan_no, + id_add_del_no, + id_add_del_mac, id_count_all, /* keep last */ };
@@ -977,6 +1346,8 @@ struct command_def { int cmd_keywords_nr; int port; int err; + int vid; + uchar *mac_addr; int (*cmd_function)(struct command_def *parsed_cmd); };
@@ -1149,6 +1520,77 @@ static int vsc9953_learn_set_key_func(struct command_def *parsed_cmd) return 0; }
+#define VSC9953_FDB_HELP "ethsw [port <port_no>] [vlan <vid>] fdb " \ +"{ [help] | show | flush | { add | del } <mac> } " \ +"- Add/delete a mac entry in FDB; use show to see FDB entries; " \ +"if vlan <vid> is missing, will be used VID 1" + +static int vsc9953_fdb_help_key_func(struct command_def *parsed_cmd) +{ + printf(VSC9953_FDB_HELP"\n"); + + return 0; +} + +static int vsc9953_fdb_show_key_func(struct command_def *parsed_cmd) +{ + vsc9953_mac_table_show(parsed_cmd->port, parsed_cmd->vid); + + return 0; +} + +static int vsc9953_fdb_flush_key_func(struct command_def *parsed_cmd) +{ + vsc9953_mac_table_flush(parsed_cmd->port, parsed_cmd->vid); + + return 0; +} + +static int vsc9953_fdb_entry_add_key_func(struct command_def *parsed_cmd) +{ + int vid; + + /* check if MAC address is present */ + if (!parsed_cmd->mac_addr) { + printf("Please use a valid MAC address\n"); + return -EINVAL; + } + + /* a port number must be present */ + if (parsed_cmd->port == VSC9953_CMD_PORT_ALL) { + printf("Please specify a port\n"); + return -EINVAL; + } + + /* Use VLAN 1 if VID is not set */ + vid = (parsed_cmd->vid == VSC9953_CMD_VLAN_ALL ? 1 : parsed_cmd->vid); + + if (vsc9953_mac_table_add(parsed_cmd->port, parsed_cmd->mac_addr, vid)) + return -1; + + return 0; +} + +static int vsc9953_fdb_entry_del_key_func(struct command_def *parsed_cmd) +{ + int vid; + + /* check if MAC address is present */ + if (!parsed_cmd->mac_addr) { + printf("Please use a valid MAC address\n"); + return -EINVAL; + } + + /* Use VLAN 1 if VID is not set */ + vid = (parsed_cmd->vid == VSC9953_CMD_VLAN_ALL ? + 1 : parsed_cmd->vid); + + if (vsc9953_mac_table_del(parsed_cmd->mac_addr, vid)) + return -1; + + return 0; +} + struct keywords_to_function { enum keyword_id cmd_keyword[VSC9953_MAX_CMD_PARAMS]; int (*keyword_function)(struct command_def *parsed_cmd); @@ -1225,6 +1667,49 @@ struct keywords_to_function { id_key_end, }, .keyword_function = &vsc9953_learn_set_key_func, + }, { + .cmd_keyword = { + id_fdb, + id_key_end, + }, + .keyword_function = &vsc9953_fdb_help_key_func, + }, { + .cmd_keyword = { + id_fdb, + id_help, + id_key_end, + }, + .keyword_function = &vsc9953_fdb_help_key_func, + }, { + .cmd_keyword = { + id_fdb, + id_show, + id_key_end, + }, + .keyword_function = &vsc9953_fdb_show_key_func, + }, { + .cmd_keyword = { + id_fdb, + id_flush, + id_key_end, + }, + .keyword_function = &vsc9953_fdb_flush_key_func, + }, { + .cmd_keyword = { + id_fdb, + id_add, + id_add_del_mac, + id_key_end, + }, + .keyword_function = &vsc9953_fdb_entry_add_key_func, + }, { + .cmd_keyword = { + id_fdb, + id_del, + id_add_del_mac, + id_key_end, + }, + .keyword_function = &vsc9953_fdb_entry_del_key_func, }, };
@@ -1237,6 +1722,20 @@ struct keywords_optional { id_port_no, id_key_end, }, + }, { + .cmd_keyword = { + id_vlan, + id_vlan_no, + id_key_end, + }, + }, { + .cmd_keyword = { + id_port, + id_port_no, + id_vlan, + id_vlan_no, + id_key_end, + }, }, };
@@ -1246,6 +1745,12 @@ static int keyword_match_gen(enum keyword_id key_id, int argc, static int keyword_match_port(enum keyword_id key_id, int argc, char *const argv[], int *argc_nr, struct command_def *parsed_cmd); +static int keyword_match_vlan(enum keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct command_def *parsed_cmd); +static int keyword_match_mac_addr(enum keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct command_def *parsed_cmd);
/* Define properties for each keyword; * keep the order synced with enum keyword_id @@ -1282,6 +1787,21 @@ struct keyword_def { }, { .keyword_name = "auto", .match = &keyword_match_gen, + }, { + .keyword_name = "vlan", + .match = &keyword_match_vlan, + }, { + .keyword_name = "fdb", + .match = &keyword_match_gen, + }, { + .keyword_name = "add", + .match = &keyword_match_mac_addr, + }, { + .keyword_name = "del", + .match = &keyword_match_mac_addr, + }, { + .keyword_name = "flush", + .match = &keyword_match_gen, }, };
@@ -1325,6 +1845,112 @@ static int keyword_match_port(enum keyword_id key_id, int argc, return 0; }
+/* Function used to match the command's vlan */ +static int keyword_match_vlan(enum keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct command_def *parsed_cmd) +{ + unsigned long val; + int aux; + + if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd)) + return 0; + + if (*argc_nr + 1 >= argc) + return 0; + + if (strict_strtoul(argv[*argc_nr + 1], 10, &val) != -EINVAL) { + if (!VSC9953_VLAN_CHECK(val)) { + printf("Invalid vlan number: %lu\n", val); + return 0; + } + parsed_cmd->vid = val; + (*argc_nr)++; + parsed_cmd->cmd_to_keywords[*argc_nr] = id_vlan_no; + return 1; + } + + aux = *argc_nr + 1; + + if (keyword_match_gen(id_add, argc, argv, &aux, parsed_cmd)) + parsed_cmd->cmd_to_keywords[*argc_nr + 1] = id_add; + else if (keyword_match_gen(id_del, argc, argv, &aux, parsed_cmd)) + parsed_cmd->cmd_to_keywords[*argc_nr + 1] = id_del; + else + return 0; + + if (*argc_nr + 2 >= argc) + return 0; + + if (strict_strtoul(argv[*argc_nr + 2], 10, &val) != -EINVAL) { + if (!VSC9953_VLAN_CHECK(val)) { + printf("Invalid vlan number: %lu\n", val); + return 0; + } + parsed_cmd->vid = val; + (*argc_nr) += 2; + parsed_cmd->cmd_to_keywords[*argc_nr] = id_add_del_no; + return 1; + } + + return 0; +} + +/* check if the string has the format for a MAC address */ +static int string_is_mac_addr(const char *mac) +{ + int i; + + if (!mac) + return 0; + + for (i = 0; i < 6; i++) { + if (!isxdigit(*mac) || !isxdigit(*(mac + 1))) + return 0; + mac += 2; + if (i != 5) { + if (*mac != ':' && *mac != '-') + return 0; + mac++; + } + } + + if (*mac != '\0') + return 0; + + return 1; +} + +/* Function used to match the command's MAC address */ +static int keyword_match_mac_addr(enum keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct command_def *parsed_cmd) +{ + if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd)) + return 0; + + if ((*argc_nr + 1 >= argc) || parsed_cmd->mac_addr) + return 1; + + if (!string_is_mac_addr(argv[*argc_nr + 1])) { + printf("Invalid mac address: %s\n", argv[*argc_nr + 1]); + return 0; + } + + parsed_cmd->mac_addr = malloc(6); + eth_parse_enetaddr(argv[*argc_nr + 1], parsed_cmd->mac_addr); + + if (is_broadcast_ethaddr(parsed_cmd->mac_addr)) { + free(parsed_cmd->mac_addr); + parsed_cmd->mac_addr = NULL; + return 0; + } + + parsed_cmd->cmd_to_keywords[*argc_nr + 1] = id_add_del_mac; + + return 1; +} + /* match optional keywords */ static void cmd_keywords_opt_check(struct command_def *parsed_cmd, int *argc_val) @@ -1414,13 +2040,19 @@ static void command_def_init(struct command_def *parsed_cmd) parsed_cmd->cmd_to_keywords[i] = -1;
parsed_cmd->port = VSC9953_CMD_PORT_ALL; + parsed_cmd->vid = VSC9953_CMD_VLAN_ALL; parsed_cmd->err = 0; + parsed_cmd->mac_addr = NULL; parsed_cmd->cmd_function = NULL; }
static void command_def_cleanup(struct command_def *parsed_cmd) { - /* Nothing to do for now */ + /* free MAC address */ + if (parsed_cmd->mac_addr) { + free(parsed_cmd->mac_addr); + parsed_cmd->mac_addr = NULL; + } }
/* function to interpret commands starting with "ethsw " */ @@ -1461,6 +2093,7 @@ U_BOOT_CMD(ethsw, VSC9953_MAX_CMD_PARAMS, 0, do_ethsw, VSC9953_PORT_CONF_HELP"\n" VSC9953_PORT_STATS_HELP"\n" VSC9953_LEARN_HELP"\n" + VSC9953_FDB_HELP"\n" );
#endif /* CONFIG_VSC9953_CMD */ diff --git a/include/vsc9953.h b/include/vsc9953.h index 59c85c3..051c24e 100644 --- a/include/vsc9953.h +++ b/include/vsc9953.h @@ -93,6 +93,25 @@ #define CONFIG_VSC9953_VCAP_MV_CFG 0x0000ffff #define CONFIG_VSC9953_VCAP_UPDATE_CTRL 0x01000004
+/* Macros for register vsc9953_ana_ana_tables.mac_access register */ +#define CONFIG_VSC9953_MAC_CMD_IDLE 0x00000000 +#define CONFIG_VSC9953_MAC_CMD_LEARN 0x00000001 +#define CONFIG_VSC9953_MAC_CMD_FORGET 0x00000002 +#define CONFIG_VSC9953_MAC_CMD_AGE 0x00000003 +#define CONFIG_VSC9953_MAC_CMD_NEXT 0x00000004 +#define CONFIG_VSC9953_MAC_CMD_READ 0x00000006 +#define CONFIG_VSC9953_MAC_CMD_WRITE 0x00000007 +#define CONFIG_VSC9953_MAC_CMD_MASK 0x00000007 +#define CONFIG_VSC9953_MAC_CMD_VALID 0x00000800 +#define CONFIG_VSC9953_MAC_ENTRYTYPE_NORMAL 0x00000000 +#define CONFIG_VSC9953_MAC_ENTRYTYPE_LOCKED 0x00000200 +#define CONFIG_VSC9953_MAC_ENTRYTYPE_IPV4MCAST 0x00000400 +#define CONFIG_VSC9953_MAC_ENTRYTYPE_IPV6MCAST 0x00000600 +#define CONFIG_VSC9953_MAC_ENTRYTYPE_MASK 0x00000600 +#define CONFIG_VSC9953_MAC_DESTIDX_MASK 0x000001f8 +#define CONFIG_VSC9953_MAC_VID_MASK 0x1fff0000 +#define CONFIG_VSC9953_MAC_MACH_MASK 0x0000ffff + /* Macros for vsc9953_ana_port.vlan_cfg register */ #define CONFIG_VSC9953_VLAN_CFG_AWARE_ENA 0x00100000 #define CONFIG_VSC9953_VLAN_CFG_POP_CNT_MASK 0x000c0000 @@ -131,6 +150,15 @@ #define CONFIG_VSC9953_TAG_CFG_ALL_ZERO 0x00000100 #define CONFIG_VSC9953_TAG_CFG_ALL 0x00000180
+/* Macros for vsc9953_ana_ana.anag_efil register */ +#define CONFIG_VSC9953_AGE_PORT_EN 0x00080000 +#define CONFIG_VSC9953_AGE_PORT_MASK 0x0007c000 +#define CONFIG_VSC9953_AGE_VID_EN 0x00002000 +#define CONFIG_VSC9953_AGE_VID_MASK 0x00001fff + +/* Macros for vsc9953_ana_ana_tables.mach_data register */ +#define CONFIG_VSC9953_MACHDATA_VID_MASK 0x1fff0000 + #define VSC9953_MAX_PORTS 10 #define VSC9953_PORT_CHECK(port) \ (((port) < 0 || (port) >= VSC9953_MAX_PORTS) ? 0 : 1)