add support for ethtool dump
authorMugunthan V N <mugunthanvnm@ti.com>
Mon, 20 Oct 2014 11:05:55 +0000 (16:35 +0530)
committerMugunthan V N <mugunthanvnm@ti.com>
Thu, 6 Nov 2014 10:00:19 +0000 (15:30 +0530)
Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
switch-config.c

index ce7e3881ad75619bdb491987d09203f12eb34e2e..d49cac7b294127a80235d9d0868a444a49b17653 100644 (file)
@@ -32,6 +32,198 @@ typedef __uint16_t u16;
 typedef __uint8_t u8;
 typedef __int32_t s32;
 
+enum {
+       EXTENDED_CONFIG_SWITCH_INVALID,
+       EXTENDED_CONFIG_SWITCH_DUMP,
+};
+
+#ifndef DIV_ROUND_UP
+#define        DIV_ROUND_UP(x,y)       (((x) + ((y) - 1)) / (y))
+#endif
+
+#define CPSW_MAJOR_VERSION(reg)                (reg >> 8 & 0x7)
+#define CPSW_MINOR_VERSION(reg)                (reg & 0xff)
+#define CPSW_RTL_VERSION(reg)          ((reg >> 11) & 0x1f)
+
+#define ADDR_FMT_ARGS(addr)    (addr)[0], (addr)[1], (addr)[2], \
+                               (addr)[3], (addr)[4], (addr)[5]
+
+#define ALE_ENTRY_BITS          68
+#define ALE_ENTRY_WORDS         DIV_ROUND_UP(ALE_ENTRY_BITS, 32)
+
+#define BIT(nr)                        (1 << (nr))
+#define BITMASK(bits)          (BIT(bits) - 1)
+
+#define ALE_TYPE_FREE                  0
+#define ALE_TYPE_ADDR                  1
+#define ALE_TYPE_VLAN                  2
+#define ALE_TYPE_VLAN_ADDR             3
+
+static inline int cpsw_ale_get_field(u32 *ale_entry, u32 start, u32 bits)
+{
+       int idx;
+
+       idx    = start / 32;
+       start -= idx * 32;
+       idx    = 2 - idx; /* flip */
+       return (ale_entry[idx] >> start) & BITMASK(bits);
+}
+
+static inline void cpsw_ale_set_field(u32 *ale_entry, u32 start, u32 bits,
+                                     u32 value)
+{
+       int idx;
+
+       value &= BITMASK(bits);
+       idx    = start / 32;
+       start -= idx * 32;
+       idx    = 2 - idx; /* flip */
+       ale_entry[idx] &= ~(BITMASK(bits) << start);
+       ale_entry[idx] |=  (value << start);
+}
+
+#define DEFINE_ALE_FIELD(name, start, bits)                            \
+static inline int cpsw_ale_get_##name(u32 *ale_entry)                  \
+{                                                                      \
+       return cpsw_ale_get_field(ale_entry, start, bits);              \
+}                                                                      \
+static inline void cpsw_ale_set_##name(u32 *ale_entry, u32 value)      \
+{                                                                      \
+       cpsw_ale_set_field(ale_entry, start, bits, value);              \
+}
+
+DEFINE_ALE_FIELD(entry_type,           60,     2)
+DEFINE_ALE_FIELD(vlan_id,              48,     12)
+DEFINE_ALE_FIELD(mcast_state,          62,     2)
+DEFINE_ALE_FIELD(port_mask,            66,     3)
+DEFINE_ALE_FIELD(super,                        65,     1)
+DEFINE_ALE_FIELD(ucast_type,           62,     2)
+DEFINE_ALE_FIELD(port_num,             66,     2)
+DEFINE_ALE_FIELD(blocked,              65,     1)
+DEFINE_ALE_FIELD(secure,               64,     1)
+DEFINE_ALE_FIELD(vlan_untag_force,     24,     3)
+DEFINE_ALE_FIELD(vlan_reg_mcast,       16,     3)
+DEFINE_ALE_FIELD(vlan_unreg_mcast,     8,      3)
+DEFINE_ALE_FIELD(vlan_member_list,     0,      3)
+DEFINE_ALE_FIELD(mcast,                        40,     1)
+
+static inline void cpsw_ale_get_addr(u32 *ale_entry, u8 *addr)
+{
+       int i;
+
+       for (i = 0; i < 6; i++)
+               addr[i] = cpsw_ale_get_field(ale_entry, 40 - 8*i, 8);
+}
+
+static void cpsw_ale_dump_vlan(int index, u32 *ale_entry)
+{
+       int vlan = cpsw_ale_get_vlan_id(ale_entry);
+       int untag_force = cpsw_ale_get_vlan_untag_force(ale_entry);
+       int reg_mcast   = cpsw_ale_get_vlan_reg_mcast(ale_entry);
+       int unreg_mcast = cpsw_ale_get_vlan_unreg_mcast(ale_entry);
+       int member_list = cpsw_ale_get_vlan_member_list(ale_entry);
+
+       fprintf(stdout, "%-4d: type: vlan , vid = %d, untag_force = 0x%x, reg_mcast = 0x%x, unreg_mcast = 0x%x, member_list = 0x%x\n",
+               index, vlan, untag_force, reg_mcast, unreg_mcast, member_list);
+}
+
+static void cpsw_ale_dump_addr(int index, u32 *ale_entry)
+{
+       u8 addr[6];
+
+       cpsw_ale_get_addr(ale_entry, addr);
+       if (cpsw_ale_get_mcast(ale_entry)) {
+               static const char *str_mcast_state[] = {"f", "blf", "lf", "f"};
+               int state     = cpsw_ale_get_mcast_state(ale_entry);
+               int port_mask = cpsw_ale_get_port_mask(ale_entry);
+               int super     = cpsw_ale_get_super(ale_entry);
+
+               fprintf(stdout, "%-4d: type: mcast, addr = %02x:%02x:%02x:%02x:%02x:%02x, mcast_state = %s, %ssuper, port_mask = 0x%x\n",
+                       index, ADDR_FMT_ARGS(addr), str_mcast_state[state],
+                       super ? "" : "no ", port_mask);
+       } else {
+               static const char *s_ucast_type[] = {"persistant", "untouched ",
+                                                    "oui       ", "touched   "};
+               int ucast_type = cpsw_ale_get_ucast_type(ale_entry);
+               int port_num   = cpsw_ale_get_port_num(ale_entry);
+               int secure     = cpsw_ale_get_secure(ale_entry);
+               int blocked    = cpsw_ale_get_blocked(ale_entry);
+
+               fprintf(stdout, "%-4d: type: ucast, addr = %02x:%02x:%02x:%02x:%02x:%02x, ucast_type = %s, port_num = 0x%x%s%s\n",
+                       index, ADDR_FMT_ARGS(addr), s_ucast_type[ucast_type],
+                       port_num, secure ? ", Secure" : "",
+                       blocked ? ", Blocked" : "");
+       }
+}
+
+static void cpsw_ale_dump_vlan_addr(int index, u32 *ale_entry)
+{
+       u8 addr[6];
+       int vlan = cpsw_ale_get_vlan_id(ale_entry);
+
+       cpsw_ale_get_addr(ale_entry, addr);
+       if (cpsw_ale_get_mcast(ale_entry)) {
+               static const char *str_mcast_state[] = {"f", "blf", "lf", "f"};
+               int state     = cpsw_ale_get_mcast_state(ale_entry);
+               int port_mask = cpsw_ale_get_port_mask(ale_entry);
+               int super     = cpsw_ale_get_super(ale_entry);
+
+               fprintf(stdout, "%-4d: type: mcast, vid = %d, addr = %02x:%02x:%02x:%02x:%02x:%02x, mcast_state = %s, %ssuper, port_mask = 0x%x\n",
+                       index, vlan, ADDR_FMT_ARGS(addr),
+                       str_mcast_state[state], super ? "" : "no ", port_mask);
+       } else {
+               static const char *s_ucast_type[] = {"persistant", "untouched ",
+                                                    "oui       ", "touched   "};
+               int ucast_type = cpsw_ale_get_ucast_type(ale_entry);
+               int port_num   = cpsw_ale_get_port_num(ale_entry);
+               int secure     = cpsw_ale_get_secure(ale_entry);
+               int blocked    = cpsw_ale_get_blocked(ale_entry);
+
+               fprintf(stdout, "%-4d: type: ucast, vid = %d, addr = %02x:%02x:%02x:%02x:%02x:%02x, ucast_type = %s, port_num = 0x%x%s%s\n",
+                       index, vlan, ADDR_FMT_ARGS(addr),
+                       s_ucast_type[ucast_type], port_num,
+                       secure ? ", Secure" : "", blocked ? ", Blocked" : "");
+       }
+}
+
+int cpsw_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs)
+{
+       u32 *reg = (u32 *)regs->data;
+       int i;
+
+       fprintf(stdout, "cpsw hw version %d.%d (%d)\n",
+               CPSW_MAJOR_VERSION(regs->version),
+               CPSW_MINOR_VERSION(regs->version),
+               CPSW_RTL_VERSION(regs->version));
+
+       for(i = 0; i < 1024; i++, reg += ALE_ENTRY_WORDS) {
+               int type;
+
+               type = cpsw_ale_get_entry_type(reg);
+               switch (type) {
+               case ALE_TYPE_FREE:
+                       break;
+
+               case ALE_TYPE_ADDR:
+                       cpsw_ale_dump_addr(i, reg);
+                       break;
+
+               case ALE_TYPE_VLAN:
+                       cpsw_ale_dump_vlan(i, reg);
+                       break;
+
+               case ALE_TYPE_VLAN_ADDR:
+                       cpsw_ale_dump_vlan_addr(i, reg);
+                       break;
+
+               default:
+                       fprintf(stdout, "%-4d: Invalid Entry type\n", i);
+               }
+       }
+
+       return 0;
+}
+
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 
 #define SWITCH_CONFIG_COMMAND(__var__, __cmd__)                        \
@@ -43,7 +235,7 @@ typedef __int32_t s32;
                return -1;                                      \
        }
 
-static char options[] = "?m:x:i:y:S:GUg:un:V:sk:K:M:N:D:Y:z:Z:J:v";
+static char options[] = "?m:x:i:y:S:GUg:udn:V:sk:K:M:N:D:Y:z:Z:J:v";
 
 static struct option long_options[] =
        {
@@ -57,6 +249,7 @@ static struct option long_options[] =
 {"get-port-config",            no_argument             , 0, 'G'},
 {"set-port-state",             required_argument       , 0, 'g'},
 {"get-port-state",             no_argument             , 0, 'u'},
+{"dump",                       optional_argument       , 0, 'd'},
 {"port",                       required_argument       , 0, 'n'},
 {"vid",                                required_argument       , 0, 'V'},
 {"super",                      no_argument             , 0, 's'},
@@ -318,6 +511,7 @@ void print_help(void)
                "switch-config -g,--get-port-state <disabled/blocked/learn/forward> "
                        "-n,--port <PortNo>\n"
                "switch-config -u,--set-port-state -n,--port <PortNo>\n"
+               "switch-config -d,--dump\n"
                "\n"
                );
 }
@@ -333,6 +527,7 @@ int main(int argc, char **argv)
        unsigned int speed;
        unsigned int duplex;
        unsigned int autoneg;
+       int extended_switch_config;
 
        /* get interface name */
        strncpy(ifr.ifr_name, "eth0", IFNAMSIZ);
@@ -424,6 +619,10 @@ int main(int argc, char **argv)
                                CONFIG_SWITCH_GET_PORT_STATE);
                break;
 
+               /* Extended switch commands */
+               case 'd':
+                       extended_switch_config = EXTENDED_CONFIG_SWITCH_DUMP;
+               break;
 
 /* Command arguments */
                case 'n':
@@ -486,6 +685,8 @@ int main(int argc, char **argv)
 
        switch (cmd_struct.cmd) {
                case CONFIG_SWITCH_INVALID:
+                       if (extended_switch_config != EXTENDED_CONFIG_SWITCH_INVALID)
+                               goto extended_ioctls;
                        print_help();
                        return -1;
                break;
@@ -660,6 +861,47 @@ int main(int argc, char **argv)
                break;
        }
 
+       close(sockfd);
+       return 0;
+
+extended_ioctls:
+       switch (extended_switch_config) {
+               case EXTENDED_CONFIG_SWITCH_DUMP:
+               {
+                       struct ethtool_drvinfo drvinfo;
+                       struct ethtool_regs *regs;
+
+                       drvinfo.cmd = ETHTOOL_GDRVINFO;
+                       ifr.ifr_data = (void *)&drvinfo;
+                       if (ioctl(sockfd, SIOCETHTOOL, &ifr) < 0) {
+                               perror("Cannot get driver information");
+                               return -1;
+                       }
+
+                       regs = calloc(1, sizeof(*regs)+drvinfo.regdump_len);
+                       if (!regs) {
+                               perror("Cannot allocate memory for register dump");
+                               return -1;
+                       }
+
+                       regs->cmd = ETHTOOL_GREGS;
+                       regs->len = drvinfo.regdump_len;
+                       ifr.ifr_data = (void *)regs;
+                       if (ioctl(sockfd, SIOCETHTOOL, &ifr) < 0) {
+                               perror("Cannot get driver information");
+                               return -1;
+                       }
+                       cpsw_dump_regs(&drvinfo, regs);
+               }
+               break;
+
+               case EXTENDED_CONFIG_SWITCH_INVALID:
+               default:
+                       print_help();
+                       return -1;
+               break;
+       }
+
        close(sockfd);
        return 0;
 }