Hi Codrin, On Tue, Jun 23, 2015 at 11:48 AM, Codrin Ciubotariu <codrin.ciubota...@freescale.com> wrote: > 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.le...@freescale.com> > Signed-off-by: Codrin Ciubotariu <codrin.ciubota...@freescale.com> > --- > Changes for v2: > - removed Change-id field; > > 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;
Use a single space. > + > + 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; Maybe return -EBUSY like suggested in previous patch. > +} > + > +/* 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; Use a single space. > + > + 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; Use a separate line for each variable. > + 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); Seems like a good place to use bitfield_replace() from include/bitfield.h (or a new one that you add that uses the mask for the shift). > + } > + 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); Same here. > + } > + 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); It seems like masking off the val before shifting it would be better implemented inside the field_get function (renamed and moved to include/bitfield.h) instead of on each use. > + 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)); Why do you need to & with the mask again after field_set()? > + 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)); Why do you need to & with the mask again after field_set()? > + 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; Use a single space. > + > + 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)); Why do you need to & with the mask again after field_set()? > + 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)); Why do you need to & with the mask again after field_set()? > + 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)); Why do you need to & with the mask again after field_set()? > + 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); Seems like a good place to use bitfield_replace() from include/bitfield.h (or a new one that you add that uses the mask for the shift). > + } > + > + 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); Same here. > + } > + 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; Use this: + uchar ethaddr[6]; I recently made a pass through U-Boot trying to standardize on that naming. Also, don't make it a pointer that has to be allocated. It is small and of known size. > 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" Please use this: +"if vlan <vid> is missing, VID 1 will be used" > + > +static int vsc9953_fdb_help_key_func(struct command_def *parsed_cmd) > +{ > + printf(VSC9953_FDB_HELP"\n"); > + > + return 0; Please use: + return CMD_RET_SUCCESS; > +} > + > +static int vsc9953_fdb_show_key_func(struct command_def *parsed_cmd) > +{ > + vsc9953_mac_table_show(parsed_cmd->port, parsed_cmd->vid); > + > + return 0; Please use: + return CMD_RET_SUCCESS; > +} > + > +static int vsc9953_fdb_flush_key_func(struct command_def *parsed_cmd) > +{ > + vsc9953_mac_table_flush(parsed_cmd->port, parsed_cmd->vid); > + > + return 0; Please use: + return CMD_RET_SUCCESS; > +} > + > +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) { Use this: + if (is_valid_ethaddr(parsed_cmd->mac_addr)) { > + printf("Please use a valid MAC address\n"); > + return -EINVAL; Please use: + return CMD_RET_USAGE; > + } > + > + /* a port number must be present */ > + if (parsed_cmd->port == VSC9953_CMD_PORT_ALL) { > + printf("Please specify a port\n"); > + return -EINVAL; Please use: + return CMD_RET_USAGE; > + } > + > + /* 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; Please use: + return CMD_RET_FAILURE; > + > + return 0; Please use: + return CMD_RET_SUCCESS; > +} > + > +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) { Use this: + if (is_valid_ethaddr(parsed_cmd->mac_addr)) { > + printf("Please use a valid MAC address\n"); > + return -EINVAL; Please use: + return CMD_RET_USAGE; > + } > + > + /* 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; Please use: + return CMD_RET_FAILURE; > + > + return 0; Please use: + return CMD_RET_SUCCESS; > +} > + > 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; Use a single space. > + > + 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; This functionality is already implemented in common/env_flags.c in the _env_flags_validate_type() function. Maybe that implementation should be extracted. Another option is to make the eth_parse_enetaddr() in net/eth.c return a status and then it can be used. Either way I don't think it should be re-implemented here. > +} > + > +/* 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); Why malloc this? It is small and known size. > + 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; Drop these two lines. > + 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; Drop this. > 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; > + } Don't malloc, and you don't need free. > } > > /* 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 Drop "CONFIG_" from all these. > + > #define VSC9953_MAX_PORTS 10 > #define VSC9953_PORT_CHECK(port) \ > (((port) < 0 || (port) >= VSC9953_MAX_PORTS) ? 0 : 1) > -- > 1.9.3 > > _______________________________________________ > U-Boot mailing list > U-Boot@lists.denx.de > http://lists.denx.de/mailman/listinfo/u-boot _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot