aboutsummaryrefslogtreecommitdiffstats
path: root/target
diff options
context:
space:
mode:
authorLuka Perkov <luka@openwrt.org>2013-05-24 13:10:22 +0000
committerLuka Perkov <luka@openwrt.org>2013-05-24 13:10:22 +0000
commit0998065a53d15da095e8af2396afe9766a9bfd31 (patch)
tree4dd9a76ada9c8443675d1772cc32efaa35fc5251 /target
parent90887b5fb38d01b0815b851adb8110114be2187a (diff)
downloadupstream-0998065a53d15da095e8af2396afe9766a9bfd31.tar.gz
upstream-0998065a53d15da095e8af2396afe9766a9bfd31.tar.bz2
upstream-0998065a53d15da095e8af2396afe9766a9bfd31.zip
generic: ar8216: add port mirroring
Tested on Buffalo WZR-600DHP with ar8316 switch. Commands used to mirror both RX and TX traffic from LAN port 1 to LAN port 4: $ swconfig dev switch0 set enable_mirror_rx 1 $ swconfig dev switch0 set enable_mirror_tx 1 $ swconfig dev switch0 set mirror_monitor_port 4 $ swconfig dev switch0 set mirror_source_port 1 Signed-off-by: Colin Leitner <colin.leitner@googlemail.com> Tested-by: Luka Perkov <luka@openwrt.org> SVN-Revision: 36713
Diffstat (limited to 'target')
-rw-r--r--target/linux/generic/files/drivers/net/phy/ar8216.c310
-rw-r--r--target/linux/generic/files/drivers/net/phy/ar8216.h8
2 files changed, 318 insertions, 0 deletions
diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.c b/target/linux/generic/files/drivers/net/phy/ar8216.c
index a07dd78488..b6df6425ac 100644
--- a/target/linux/generic/files/drivers/net/phy/ar8216.c
+++ b/target/linux/generic/files/drivers/net/phy/ar8216.c
@@ -120,6 +120,12 @@ struct ar8xxx_priv {
u8 vlan_table[AR8X16_MAX_VLANS];
u8 vlan_tagged;
u16 pvid[AR8X16_MAX_PORTS];
+
+ /* mirroring */
+ bool mirror_rx;
+ bool mirror_tx;
+ int source_port;
+ int monitor_port;
};
#define MIB_DESC(_s , _o, _n) \
@@ -1457,6 +1463,98 @@ ar8xxx_sw_set_ports(struct switch_dev *dev, struct switch_val *val)
return 0;
}
+static void
+ar8327_set_mirror_regs(struct ar8xxx_priv *priv)
+{
+ int port;
+
+ /* reset all mirror registers */
+ ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL0,
+ AR8327_FWD_CTRL0_MIRROR_PORT,
+ (0xF << AR8327_FWD_CTRL0_MIRROR_PORT_S));
+ for (port = 0; port < AR8327_NUM_PORTS; port++) {
+ ar8xxx_rmw(priv, AR8327_REG_PORT_LOOKUP(port),
+ AR8327_PORT_LOOKUP_ING_MIRROR_EN,
+ 0);
+
+ ar8xxx_rmw(priv, AR8327_REG_PORT_HOL_CTRL1(port),
+ AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN,
+ 0);
+ }
+
+ /* now enable mirroring if necessary */
+ if (priv->source_port >= AR8327_NUM_PORTS ||
+ priv->monitor_port >= AR8327_NUM_PORTS ||
+ priv->source_port == priv->monitor_port) {
+ return;
+ }
+
+ ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL0,
+ AR8327_FWD_CTRL0_MIRROR_PORT,
+ (priv->monitor_port << AR8327_FWD_CTRL0_MIRROR_PORT_S));
+
+ if (priv->mirror_rx)
+ ar8xxx_rmw(priv, AR8327_REG_PORT_LOOKUP(priv->source_port),
+ AR8327_PORT_LOOKUP_ING_MIRROR_EN,
+ AR8327_PORT_LOOKUP_ING_MIRROR_EN);
+
+ if (priv->mirror_tx)
+ ar8xxx_rmw(priv, AR8327_REG_PORT_HOL_CTRL1(priv->source_port),
+ AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN,
+ AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN);
+}
+
+static void
+ar8216_set_mirror_regs(struct ar8xxx_priv *priv)
+{
+ int port;
+
+ /* reset all mirror registers */
+ ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CPUPORT,
+ AR8216_GLOBAL_CPUPORT_MIRROR_PORT,
+ (0xF << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S));
+ for (port = 0; port < AR8216_NUM_PORTS; port++) {
+ ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port),
+ AR8216_PORT_CTRL_MIRROR_RX,
+ 0);
+
+ ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port),
+ AR8216_PORT_CTRL_MIRROR_TX,
+ 0);
+ }
+
+ /* now enable mirroring if necessary */
+ if (priv->source_port >= AR8216_NUM_PORTS ||
+ priv->monitor_port >= AR8216_NUM_PORTS ||
+ priv->source_port == priv->monitor_port) {
+ return;
+ }
+
+ ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CPUPORT,
+ AR8216_GLOBAL_CPUPORT_MIRROR_PORT,
+ (priv->monitor_port << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S));
+
+ if (priv->mirror_rx)
+ ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(priv->source_port),
+ AR8216_PORT_CTRL_MIRROR_RX,
+ AR8216_PORT_CTRL_MIRROR_RX);
+
+ if (priv->mirror_tx)
+ ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(priv->source_port),
+ AR8216_PORT_CTRL_MIRROR_TX,
+ AR8216_PORT_CTRL_MIRROR_TX);
+}
+
+static void
+ar8xxx_set_mirror_regs(struct ar8xxx_priv *priv)
+{
+ if (chip_is_ar8327(priv)) {
+ ar8327_set_mirror_regs(priv);
+ } else {
+ ar8216_set_mirror_regs(priv);
+ }
+}
+
static int
ar8xxx_sw_hw_apply(struct switch_dev *dev)
{
@@ -1520,6 +1618,9 @@ ar8xxx_sw_hw_apply(struct switch_dev *dev)
priv->chip->setup_port(priv, i, egress, ingress, portmask[i],
pvid);
}
+
+ ar8xxx_set_mirror_regs(priv);
+
mutex_unlock(&priv->reg_mutex);
return 0;
}
@@ -1541,7 +1642,13 @@ ar8xxx_sw_reset_switch(struct switch_dev *dev)
for (i = 0; i < dev->ports; i++)
priv->chip->init_port(priv, i);
+ priv->mirror_rx = false;
+ priv->mirror_tx = false;
+ priv->source_port = 0;
+ priv->monitor_port = 0;
+
priv->chip->init_globals(priv);
+
mutex_unlock(&priv->reg_mutex);
return ar8xxx_sw_hw_apply(dev);
@@ -1576,6 +1683,106 @@ unlock:
}
static int
+ar8xxx_sw_set_mirror_rx_enable(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+ mutex_lock(&priv->reg_mutex);
+ priv->mirror_rx = !!val->value.i;
+ ar8xxx_set_mirror_regs(priv);
+ mutex_unlock(&priv->reg_mutex);
+
+ return 0;
+}
+
+static int
+ar8xxx_sw_get_mirror_rx_enable(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+ val->value.i = priv->mirror_rx;
+ return 0;
+}
+
+static int
+ar8xxx_sw_set_mirror_tx_enable(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+ mutex_lock(&priv->reg_mutex);
+ priv->mirror_tx = !!val->value.i;
+ ar8xxx_set_mirror_regs(priv);
+ mutex_unlock(&priv->reg_mutex);
+
+ return 0;
+}
+
+static int
+ar8xxx_sw_get_mirror_tx_enable(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+ val->value.i = priv->mirror_tx;
+ return 0;
+}
+
+static int
+ar8xxx_sw_set_mirror_monitor_port(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+ mutex_lock(&priv->reg_mutex);
+ priv->monitor_port = val->value.i;
+ ar8xxx_set_mirror_regs(priv);
+ mutex_unlock(&priv->reg_mutex);
+
+ return 0;
+}
+
+static int
+ar8xxx_sw_get_mirror_monitor_port(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+ val->value.i = priv->monitor_port;
+ return 0;
+}
+
+static int
+ar8xxx_sw_set_mirror_source_port(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+ mutex_lock(&priv->reg_mutex);
+ priv->source_port = val->value.i;
+ ar8xxx_set_mirror_regs(priv);
+ mutex_unlock(&priv->reg_mutex);
+
+ return 0;
+}
+
+static int
+ar8xxx_sw_get_mirror_source_port(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+ val->value.i = priv->source_port;
+ return 0;
+}
+
+static int
ar8xxx_sw_set_port_reset_mib(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val)
@@ -1668,7 +1875,87 @@ static struct switch_attr ar8xxx_sw_attr_globals[] = {
.description = "Reset all MIB counters",
.set = ar8xxx_sw_set_reset_mibs,
},
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "enable_mirror_rx",
+ .description = "Enable mirroring of RX packets",
+ .set = ar8xxx_sw_set_mirror_rx_enable,
+ .get = ar8xxx_sw_get_mirror_rx_enable,
+ .max = 1
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "enable_mirror_tx",
+ .description = "Enable mirroring of TX packets",
+ .set = ar8xxx_sw_set_mirror_tx_enable,
+ .get = ar8xxx_sw_get_mirror_tx_enable,
+ .max = 1
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "mirror_monitor_port",
+ .description = "Mirror monitor port",
+ .set = ar8xxx_sw_set_mirror_monitor_port,
+ .get = ar8xxx_sw_get_mirror_monitor_port,
+ .max = AR8216_NUM_PORTS - 1
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "mirror_source_port",
+ .description = "Mirror source port",
+ .set = ar8xxx_sw_set_mirror_source_port,
+ .get = ar8xxx_sw_get_mirror_source_port,
+ .max = AR8216_NUM_PORTS - 1
+ },
+};
+static struct switch_attr ar8327_sw_attr_globals[] = {
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "enable_vlan",
+ .description = "Enable VLAN mode",
+ .set = ar8xxx_sw_set_vlan,
+ .get = ar8xxx_sw_get_vlan,
+ .max = 1
+ },
+ {
+ .type = SWITCH_TYPE_NOVAL,
+ .name = "reset_mibs",
+ .description = "Reset all MIB counters",
+ .set = ar8xxx_sw_set_reset_mibs,
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "enable_mirror_rx",
+ .description = "Enable mirroring of RX packets",
+ .set = ar8xxx_sw_set_mirror_rx_enable,
+ .get = ar8xxx_sw_get_mirror_rx_enable,
+ .max = 1
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "enable_mirror_tx",
+ .description = "Enable mirroring of TX packets",
+ .set = ar8xxx_sw_set_mirror_tx_enable,
+ .get = ar8xxx_sw_get_mirror_tx_enable,
+ .max = 1
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "mirror_monitor_port",
+ .description = "Mirror monitor port",
+ .set = ar8xxx_sw_set_mirror_monitor_port,
+ .get = ar8xxx_sw_get_mirror_monitor_port,
+ .max = AR8327_NUM_PORTS - 1
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "mirror_source_port",
+ .description = "Mirror source port",
+ .set = ar8xxx_sw_set_mirror_source_port,
+ .get = ar8xxx_sw_get_mirror_source_port,
+ .max = AR8327_NUM_PORTS - 1
+ },
};
static struct switch_attr ar8xxx_sw_attr_port[] = {
@@ -1720,6 +2007,28 @@ static const struct switch_dev_ops ar8xxx_sw_ops = {
.get_port_link = ar8xxx_sw_get_port_link,
};
+static const struct switch_dev_ops ar8327_sw_ops = {
+ .attr_global = {
+ .attr = ar8327_sw_attr_globals,
+ .n_attr = ARRAY_SIZE(ar8327_sw_attr_globals),
+ },
+ .attr_port = {
+ .attr = ar8xxx_sw_attr_port,
+ .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_port),
+ },
+ .attr_vlan = {
+ .attr = ar8xxx_sw_attr_vlan,
+ .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_vlan),
+ },
+ .get_port_pvid = ar8xxx_sw_get_pvid,
+ .set_port_pvid = ar8xxx_sw_set_pvid,
+ .get_vlan_ports = ar8xxx_sw_get_ports,
+ .set_vlan_ports = ar8xxx_sw_set_ports,
+ .apply_config = ar8xxx_sw_hw_apply,
+ .reset_switch = ar8xxx_sw_reset_switch,
+ .get_port_link = ar8xxx_sw_get_port_link,
+};
+
static int
ar8xxx_id_chip(struct ar8xxx_priv *priv)
{
@@ -1900,6 +2209,7 @@ ar8xxx_probe_switch(struct ar8xxx_priv *priv)
swdev->name = "Atheros AR8327";
swdev->vlans = AR8X16_MAX_VLANS;
swdev->ports = AR8327_NUM_PORTS;
+ swdev->ops = &ar8327_sw_ops;
} else {
swdev->name = "Atheros AR8216";
swdev->vlans = AR8216_NUM_VLANS;
diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.h b/target/linux/generic/files/drivers/net/phy/ar8216.h
index 95d9a96e75..086d58fbab 100644
--- a/target/linux/generic/files/drivers/net/phy/ar8216.h
+++ b/target/linux/generic/files/drivers/net/phy/ar8216.h
@@ -105,6 +105,10 @@
#define AR8216_MIB_FUNC_CAPTURE 0x3
#define AR8236_MIB_EN BIT(30)
+#define AR8216_REG_GLOBAL_CPUPORT 0x0078
+#define AR8216_GLOBAL_CPUPORT_MIRROR_PORT BITS(4, 4)
+#define AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S 4
+
#define AR8216_PORT_OFFSET(_i) (0x0100 * (_i + 1))
#define AR8216_REG_PORT_STATUS(_i) (AR8216_PORT_OFFSET(_i) + 0x0000)
#define AR8216_PORT_STATUS_SPEED BITS(0,2)
@@ -433,9 +437,13 @@
#define AR8327_PORT_LOOKUP_STATE BITS(16, 3)
#define AR8327_PORT_LOOKUP_STATE_S 16
#define AR8327_PORT_LOOKUP_LEARN BIT(20)
+#define AR8327_PORT_LOOKUP_ING_MIRROR_EN BIT(25)
#define AR8327_REG_PORT_PRIO(_i) (0x664 + (_i) * 0xc)
+#define AR8327_REG_PORT_HOL_CTRL1(_i) (0x974 + (_i) * 0x8)
+#define AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN BIT(16)
+
#define AR8327_REG_PORT_STATS_BASE(_i) (0x1000 + (_i) * 0x100)
/* port speed */