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
This commit is contained in:
Luka Perkov 2013-05-24 13:10:22 +00:00
parent 90887b5fb3
commit 0998065a53
2 changed files with 318 additions and 0 deletions

View File

@ -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);
@ -1575,6 +1682,106 @@ unlock:
return ret;
}
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,
@ -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;

View File

@ -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 */