mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-23 01:31:30 +03:00
nvethernet: enable MAC filter
Functions added to support filter setting from Network stack as well as from customized app using ioctl. Bug 200512993 Change-Id: Id9a7712242ef229969d7c476c85171509ab53d73 Signed-off-by: Rakesh Goyal <rgoyal@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/2111084 Reviewed-by: Srinivas Ramachandran <srinivasra@nvidia.com> GVS: Gerrit_Virtual_Submit Reviewed-by: Narayan Reddy <narayanr@nvidia.com> Reviewed-by: Ashutosh Jha <ajha@nvidia.com> Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com> Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
This commit is contained in:
committed by
Revanth Kumar Uppala
parent
3565e0eb75
commit
6a3fdf61b3
@@ -1337,6 +1337,199 @@ static int ether_start_xmit(struct sk_buff *skb, struct net_device *ndev)
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* ether_prepare_mc_list- function to configure the multicast
|
||||
* address in device.
|
||||
*
|
||||
* @dev: Pointer to net_device structure.
|
||||
*
|
||||
* Algorithm:
|
||||
* This function collects all the multicast addresses and updates the
|
||||
* device.
|
||||
*
|
||||
* Dependencies: MAC and PHY need to be initialized.
|
||||
*
|
||||
* Protection: None.
|
||||
*
|
||||
* Return: OSI_PERFECT_FILTER_MODE - perfect filtering is seleted
|
||||
* OSI_HASH_FILTER_MODE - if hash filtering is seleted.
|
||||
*/
|
||||
static int ether_prepare_mc_list(struct net_device *dev)
|
||||
{
|
||||
struct ether_priv_data *pdata = netdev_priv(dev);
|
||||
struct osi_core_priv_data *osi_core = pdata->osi_core;
|
||||
struct netdev_hw_addr *ha;
|
||||
int ret = OSI_PERFECT_FILTER_MODE, i = 1;
|
||||
int cnt;
|
||||
|
||||
if (pdata->l2_filtering_mode == OSI_HASH_FILTER_MODE) {
|
||||
dev_err(pdata->dev,
|
||||
"select HASH FILTERING for mc addresses is not supported in SW\n");
|
||||
/* only perfect filter is supported */
|
||||
} else {
|
||||
dev_dbg(pdata->dev,
|
||||
"select PERFECT FILTERING for mc addresses, mc_count = %d, num_mac_addr_regs = %d\n",
|
||||
netdev_mc_count(dev),
|
||||
pdata->num_mac_addr_regs);
|
||||
/* Clear previously set filters */
|
||||
for (cnt = 1; cnt <= pdata->last_uc_filter_index; cnt++) {
|
||||
osi_update_mac_addr_low_high_reg(osi_core, cnt, NULL);
|
||||
}
|
||||
|
||||
netdev_for_each_mc_addr(ha, dev) {
|
||||
dev_dbg(pdata->dev,
|
||||
"mc addr[%d] = %#x:%#x:%#x:%#x:%#x:%#x\n",
|
||||
i,
|
||||
ha->addr[0], ha->addr[1], ha->addr[2],
|
||||
ha->addr[3], ha->addr[4], ha->addr[5]);
|
||||
osi_update_mac_addr_low_high_reg(osi_core, i, ha->addr);
|
||||
if (i == EQOS_MAX_MAC_ADDRESS_FILTER - 1) {
|
||||
dev_err(pdata->dev, "Configured max number of supported MAC, ignoring it\n");
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
/* preserve last MC filter index to passon to UC */
|
||||
pdata->last_mc_filter_index = i - 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ether_prepare_uc_list- function to configure the unicast address
|
||||
* in device.
|
||||
*
|
||||
* @dev - pointer to net_device structure.
|
||||
*
|
||||
* Algorithm:
|
||||
* This function collects all the unicast addresses and updates the
|
||||
* device.
|
||||
*
|
||||
* Dependencies: MAC and PHY need to be initialized.
|
||||
*
|
||||
* Protection: None.
|
||||
*
|
||||
* Return: OSI_PERFECT_FILTER_MODE - perfect filtering is seleted
|
||||
* OSI_HASH_FILTER_MODE - if hash filtering is seleted.
|
||||
*/
|
||||
static int ether_prepare_uc_list(struct net_device *dev)
|
||||
{
|
||||
struct ether_priv_data *pdata = netdev_priv(dev);
|
||||
struct osi_core_priv_data *osi_core = pdata->osi_core;
|
||||
int i = pdata->last_mc_filter_index + 1;
|
||||
int ret = OSI_PERFECT_FILTER_MODE;
|
||||
struct netdev_hw_addr *ha;
|
||||
int cnt;
|
||||
|
||||
if (pdata->l2_filtering_mode == OSI_HASH_FILTER_MODE) {
|
||||
dev_err(pdata->dev,
|
||||
"select HASH FILTERING for uc addresses not Supported in SW\n");
|
||||
/* only perfect filter is supported */
|
||||
} else {
|
||||
dev_dbg(pdata->dev,
|
||||
"select PERFECT FILTERING for uc addresses: uc_count = %d\n",
|
||||
netdev_uc_count(dev));
|
||||
/* Clear previously set filters */
|
||||
for (cnt = pdata->last_mc_filter_index + 1;
|
||||
cnt <= pdata->last_uc_filter_index; cnt++) {
|
||||
osi_update_mac_addr_low_high_reg(osi_core, cnt, NULL);
|
||||
}
|
||||
|
||||
netdev_for_each_uc_addr(ha, dev) {
|
||||
dev_dbg(pdata->dev,
|
||||
"uc addr[%d] = %#x:%#x:%#x:%#x:%#x:%#x\n",
|
||||
i, ha->addr[0], ha->addr[1], ha->addr[2],
|
||||
ha->addr[3], ha->addr[4], ha->addr[5]);
|
||||
osi_update_mac_addr_low_high_reg(osi_core, i, ha->addr);
|
||||
if (i == EQOS_MAX_MAC_ADDRESS_FILTER - 1) {
|
||||
dev_err(pdata->dev, "Already MAX MAC added\n");
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
pdata->last_uc_filter_index = i - 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ether_set_rx_mode - This function is used to set RX mode.
|
||||
*
|
||||
* @dev - pointer to net_device structure.
|
||||
*
|
||||
* Algorithm:
|
||||
* Based on Network interface flag, MAC registers are programmed to set
|
||||
* mode
|
||||
*
|
||||
* Dependencies: MAC and PHY need to be initialized.
|
||||
*
|
||||
* Protection: Spinlock is used for protection.
|
||||
*
|
||||
* Return: None
|
||||
*/
|
||||
static void ether_set_rx_mode(struct net_device *dev)
|
||||
{
|
||||
struct ether_priv_data *pdata = netdev_priv(dev);
|
||||
struct osi_core_priv_data *osi_core = pdata->osi_core;
|
||||
struct osi_filter filter = {0};
|
||||
int mode, ret;
|
||||
|
||||
spin_lock_bh(&pdata->lock);
|
||||
|
||||
if ((dev->flags & IFF_PROMISC) == IFF_PROMISC) {
|
||||
dev_dbg(pdata->dev, "enabling Promiscuous mode\n");
|
||||
filter.pr_mode = OSI_ENABLE;
|
||||
} else if ((dev->flags & IFF_ALLMULTI) == IFF_ALLMULTI) {
|
||||
dev_dbg(pdata->dev, "pass all multicast pkt\n");
|
||||
filter.pm_mode = OSI_ENABLE;
|
||||
} else if (!netdev_mc_empty(dev)) {
|
||||
dev_dbg(pdata->dev, "pass list of multicast pkt\n");
|
||||
if (netdev_mc_count(dev) > (pdata->num_mac_addr_regs - 1)) {
|
||||
/* switch to PROMISCUOUS mode */
|
||||
filter.pr_mode = OSI_ENABLE;
|
||||
} else {
|
||||
mode = ether_prepare_mc_list(dev);
|
||||
if (mode == OSI_HASH_FILTER_MODE) {
|
||||
/* Hash filtering for multicast */
|
||||
filter.hmc_mode = OSI_ENABLE;
|
||||
} else {
|
||||
/* Perfect filtering for multicast */
|
||||
filter.hmc_mode = OSI_DISABLE;
|
||||
filter.hpf_mode = OSI_ENABLE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pdata->last_mc_filter_index = 0;
|
||||
}
|
||||
|
||||
/* Handle multiple unicast addresses */
|
||||
if (netdev_uc_count(dev) > (pdata->num_mac_addr_regs - 1)) {
|
||||
/* switch to PROMISCUOUS mode */
|
||||
filter.pr_mode = OSI_ENABLE;
|
||||
} else if (!netdev_uc_empty(dev)) {
|
||||
mode = ether_prepare_uc_list(dev);
|
||||
if (mode == OSI_HASH_FILTER_MODE) {
|
||||
/* Hash filtering for unicast */
|
||||
filter.huc_mode = OSI_ENABLE;
|
||||
} else {
|
||||
/* Perfect filtering for unicast */
|
||||
filter.huc_mode = OSI_DISABLE;
|
||||
filter.hpf_mode = OSI_ENABLE;
|
||||
}
|
||||
} else {
|
||||
pdata->last_uc_filter_index = pdata->last_mc_filter_index;
|
||||
}
|
||||
|
||||
ret = osi_config_mac_pkt_filter_reg(osi_core, filter);
|
||||
if (ret != 0) {
|
||||
dev_err(pdata->dev, "osi_config_mac_pkt_filter_reg failed\n");
|
||||
}
|
||||
|
||||
spin_unlock_bh(&pdata->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* ether_ioctl - network stack IOCTL hook to driver
|
||||
* @ndev: network device structure
|
||||
@@ -1356,12 +1549,15 @@ static int ether_start_xmit(struct sk_buff *skb, struct net_device *ndev)
|
||||
static int ether_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
|
||||
{
|
||||
int ret = -EOPNOTSUPP;
|
||||
struct ether_priv_data *pdata = netdev_priv(dev);
|
||||
|
||||
if (!dev || !rq) {
|
||||
dev_err(pdata->dev, "%s: Invalid arg\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!netif_running(dev)) {
|
||||
dev_err(pdata->dev, "%s: Interface not up\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -1507,6 +1703,83 @@ static int ether_set_features(struct net_device *ndev, netdev_features_t feat)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ether_vlan_rx_add_vid- Add VLAN ID. This function is invoked by upper
|
||||
* layer when a new VLAN id is registered. This function updates the HW
|
||||
* filter with new VLAN id. New vlan id can be added with vconfig -
|
||||
* vconfig add <interface_name > <vlan_id>
|
||||
*
|
||||
* @ndev: Network device structure
|
||||
* @proto: VLAN proto VLAN_PROTO_8021Q = 0 VLAN_PROTO_8021AD = 1
|
||||
* @vid: VLAN ID.
|
||||
*
|
||||
* Algorithm:
|
||||
* 1) Check for hash or perfect filtering.
|
||||
* 2) invoke osi call accordingly.
|
||||
*
|
||||
* Dependencies: Ethernet interface should be up
|
||||
*
|
||||
* Protection: None.
|
||||
*
|
||||
* Return: 0 - success Negative - failure
|
||||
*/
|
||||
static int ether_vlan_rx_add_vid(struct net_device *ndev, __be16 vlan_proto,
|
||||
u16 vid)
|
||||
{
|
||||
struct ether_priv_data *pdata = netdev_priv(ndev);
|
||||
struct osi_core_priv_data *osi_core = pdata->osi_core;
|
||||
int ret = -1;
|
||||
|
||||
if (pdata->vlan_hash_filtering == OSI_HASH_FILTER_MODE) {
|
||||
dev_err(pdata->dev,
|
||||
"HASH FILTERING for VLAN tag is not supported in SW\n");
|
||||
} else {
|
||||
ret = osi_update_vlan_id(osi_core, vid);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ether_vlan_rx_kill_vid- Remove VLAN ID. This function is invoked by
|
||||
* upper layer when a new VALN id is removed. This function updates the HW
|
||||
* filter. vlan id can be removed with vconfig -
|
||||
* vconfig rem <interface_name > <vlan_id>
|
||||
*
|
||||
* @ndev: Network device structure
|
||||
* @vlan_proto: VLAN proto VLAN_PROTO_8021Q = 0 VLAN_PROTO_8021AD = 1
|
||||
* @vid: VLAN ID.
|
||||
*
|
||||
* Algorithm:
|
||||
* 1) Check for hash or perfect filtering.
|
||||
* 2) invoke osi call accordingly.
|
||||
*
|
||||
* Dependencies: Ethernet interface should be up
|
||||
*
|
||||
* Protection: None.
|
||||
*
|
||||
* Return: 0 - success Negative - failure
|
||||
*/
|
||||
static int ether_vlan_rx_kill_vid(struct net_device *ndev, __be16 vlan_proto,
|
||||
u16 vid)
|
||||
{
|
||||
struct ether_priv_data *pdata = netdev_priv(ndev);
|
||||
struct osi_core_priv_data *osi_core = pdata->osi_core;
|
||||
int ret = -1;
|
||||
|
||||
if (pdata->vlan_hash_filtering == OSI_HASH_FILTER_MODE) {
|
||||
dev_err(pdata->dev,
|
||||
"HASH FILTERING for VLAN tag is not supported in SW\n");
|
||||
} else {
|
||||
/* By default, receive only VLAN pkt with VID = 1 because
|
||||
* writing 0 will pass all VLAN pkt
|
||||
*/
|
||||
ret = osi_update_vlan_id(osi_core, 0x1U);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct net_device_ops ether_netdev_ops = {
|
||||
.ndo_open = ether_open,
|
||||
.ndo_stop = ether_close,
|
||||
@@ -1516,6 +1789,9 @@ static const struct net_device_ops ether_netdev_ops = {
|
||||
.ndo_change_mtu = ether_change_mtu,
|
||||
.ndo_select_queue = ether_select_queue,
|
||||
.ndo_set_features = ether_set_features,
|
||||
.ndo_set_rx_mode = ether_set_rx_mode,
|
||||
.ndo_vlan_rx_add_vid = ether_vlan_rx_add_vid,
|
||||
.ndo_vlan_rx_kill_vid = ether_vlan_rx_kill_vid,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -2246,7 +2522,8 @@ static int ether_parse_phy_dt(struct ether_priv_data *pdata,
|
||||
* Algorithm: Reads queue priority form DT. Updates
|
||||
* data either by DT values or by default value.
|
||||
*
|
||||
* Dependencies: None
|
||||
* Dependencies: All queue priorities should be different
|
||||
* from DT.
|
||||
*
|
||||
* Protection: None
|
||||
*
|
||||
@@ -2364,6 +2641,11 @@ static int ether_parse_dt(struct ether_priv_data *pdata)
|
||||
ETHER_QUEUE_PRIO_DEFAULT, ETHER_QUEUE_PRIO_MAX,
|
||||
osi_core->num_mtl_queues);
|
||||
|
||||
ether_parse_queue_prio(pdata, "nvidia,rx_queue_prio",
|
||||
osi_core->rxq_prio,
|
||||
ETHER_QUEUE_PRIO_INVALID, ETHER_QUEUE_PRIO_MAX,
|
||||
osi_core->num_mtl_queues);
|
||||
|
||||
ret = ether_parse_phy_dt(pdata, np);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to parse PHY DT\n");
|
||||
@@ -2529,8 +2811,9 @@ static void ether_set_ndev_features(struct net_device *ndev,
|
||||
features |= NETIF_F_HW_VLAN_CTAG_TX;
|
||||
}
|
||||
|
||||
/* Rx VLAN tag detection enabled by default */
|
||||
/* Rx VLAN tag stripping/filtering enabled by default */
|
||||
features |= NETIF_F_HW_VLAN_CTAG_RX;
|
||||
features |= NETIF_F_HW_VLAN_CTAG_FILTER;
|
||||
|
||||
/* Features available in HW */
|
||||
ndev->hw_features = features;
|
||||
@@ -2670,6 +2953,36 @@ static int ether_therm_init(struct ether_priv_data *pdata)
|
||||
}
|
||||
#endif /* THERMAL_CAL */
|
||||
|
||||
/**
|
||||
* init_filter_values- static function to initialize filter reg
|
||||
* count in private data structure
|
||||
*
|
||||
* @ether_priv_data: ethernet private data structure
|
||||
*
|
||||
* Algorithm:
|
||||
* 1) update addr_reg_cnt based on HW feature
|
||||
*
|
||||
* Dependencies: MAC_HW_Feature1 register need to read and store the
|
||||
* value of ADDR64.
|
||||
*
|
||||
* Protection: None.
|
||||
*
|
||||
* Return: None.
|
||||
*
|
||||
*/
|
||||
static void init_filter_values(struct ether_priv_data *pdata)
|
||||
{
|
||||
if (pdata->hw_feat.mac_addr64_sel == OSI_ENABLE) {
|
||||
pdata->num_mac_addr_regs = ETHER_ADDR_REG_CNT_128;
|
||||
} else if (pdata->hw_feat.mac_addr32_sel == OSI_ENABLE) {
|
||||
pdata->num_mac_addr_regs = ETHER_ADDR_REG_CNT_64;
|
||||
} else if (pdata->hw_feat.mac_addr16_sel == OSI_ENABLE) {
|
||||
pdata->num_mac_addr_regs = ETHER_ADDR_REG_CNT_32;
|
||||
} else {
|
||||
pdata->num_mac_addr_regs = ETHER_ADDR_REG_CNT_1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ether_probe - Ethernet platform driver probe.
|
||||
* @pdev: platform device associated with platform driver.
|
||||
@@ -2680,6 +2993,8 @@ static int ether_therm_init(struct ether_priv_data *pdata)
|
||||
* 3) Parse MAC and PHY DT.
|
||||
* 4) Get all required clks/reset/IRQ's
|
||||
* 5) Register MDIO bus and network device.
|
||||
* 6) initialize spinlock
|
||||
* 7) Update filter value based on HW feature
|
||||
*
|
||||
* Dependencies: Device tree need to be updated with proper DT properties.
|
||||
*
|
||||
@@ -2824,6 +3139,10 @@ static int ether_probe(struct platform_device *pdev)
|
||||
goto err_netdev;
|
||||
}
|
||||
|
||||
spin_lock_init(&pdata->lock);
|
||||
spin_lock_init(&pdata->ioctl_lock);
|
||||
init_filter_values(pdata);
|
||||
|
||||
dev_info(&pdev->dev,
|
||||
"%s (HW ver: %02x) created with %u DMA channels\n",
|
||||
netdev_name(ndev), osi_core->mac_ver, num_dma_chans);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_mdio.h>
|
||||
#include <linux/if_vlan.h>
|
||||
@@ -43,6 +44,20 @@
|
||||
#define ETHER_IRQ_NAME_SZ 32
|
||||
#define ETHER_QUEUE_PRIO_DEFAULT 0U
|
||||
#define ETHER_QUEUE_PRIO_MAX 7U
|
||||
#define ETHER_QUEUE_PRIO_INVALID 0xFFU
|
||||
|
||||
#define EQOS_CONFIG_FAIL -3
|
||||
#define EQOS_CONFIG_SUCCESS 0
|
||||
|
||||
#define ETHER_ADDR_REG_CNT_128 128
|
||||
#define ETHER_ADDR_REG_CNT_64 64
|
||||
#define ETHER_ADDR_REG_CNT_32 32
|
||||
#define ETHER_ADDR_REG_CNT_1 1
|
||||
|
||||
#define HW_HASH_TBL_SZ_3 3
|
||||
#define HW_HASH_TBL_SZ_2 2
|
||||
#define HW_HASH_TBL_SZ_1 1
|
||||
#define HW_HASH_TBL_SZ_0 0
|
||||
|
||||
/* Map max. 4KB buffer per Tx descriptor */
|
||||
#define ETHER_MAX_DATA_LEN_PER_TXD_BUF BIT(12)
|
||||
@@ -146,6 +161,17 @@ struct ether_rx_napi {
|
||||
* is tripped.
|
||||
* @therm_state: Atomic variable to hold the current temperature zone
|
||||
* which has triggered.
|
||||
* @lock: Spin lock for filter code
|
||||
* @ioctl_lock: Spin lock for filter code ioctl path
|
||||
* @max_hash_table_size: hash table size; 0, 64,128 or 256
|
||||
* @num_mac_addr_regs: max address register count, 2*mac_addr64_sel
|
||||
* @last_mc_filter_index: Last Multicast address reg filter index, If 0,
|
||||
* no MC address added
|
||||
* @last_uc_filter_index: Last Unicast address reg filter index, If 0, no
|
||||
* MC and UC address added.
|
||||
* @l3_l4_filter: L3_l4 filter enabled 1: enabled
|
||||
* @vlan_hash_filtering: vlan hash filter 1: hash, 0: perfect
|
||||
* @l2_filtering_mode: l2 filter mode 1: hash 0: perfect
|
||||
*/
|
||||
struct ether_priv_data {
|
||||
struct osi_core_priv_data *osi_core;
|
||||
@@ -194,6 +220,17 @@ struct ether_priv_data {
|
||||
struct thermal_cooling_device *tcd;
|
||||
atomic_t therm_state;
|
||||
#endif /* THERMAL_CAL */
|
||||
|
||||
/* for filtering */
|
||||
spinlock_t lock;
|
||||
/* spin lock for ioctl path */
|
||||
spinlock_t ioctl_lock;
|
||||
int num_mac_addr_regs;
|
||||
int last_mc_filter_index;
|
||||
int last_uc_filter_index;
|
||||
unsigned int l3_l4_filter;
|
||||
unsigned int vlan_hash_filtering;
|
||||
unsigned int l2_filtering_mode;
|
||||
};
|
||||
|
||||
void ether_set_ethtool_ops(struct net_device *ndev);
|
||||
|
||||
@@ -162,6 +162,393 @@ static int ether_config_arp_offload(struct ether_priv_data *pdata,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ether_config_l3_l4_filtering- This function is invoked by ioctl
|
||||
* when user issues an ioctl command to enable/disable L3/L4 filtering.
|
||||
*
|
||||
* @dev: pointer to net device structure.
|
||||
* @filter_flags: flag to indicate whether L3/L4 filtering to be
|
||||
* enabled/disabled.
|
||||
*
|
||||
* Algorithm:
|
||||
* 1) check if filter enalbed/disable already and return success.
|
||||
* 2) OSI call to update register
|
||||
*
|
||||
* Dependencies: MAC and PHY need to be initialized.
|
||||
*
|
||||
* Protection: None.
|
||||
*
|
||||
* Return 0- sucessful, non-zero - error
|
||||
*
|
||||
*/
|
||||
static int ether_config_l3_l4_filtering(struct net_device *dev,
|
||||
unsigned int filter_flags)
|
||||
{
|
||||
struct ether_priv_data *pdata = netdev_priv(dev);
|
||||
struct osi_core_priv_data *osi_core = pdata->osi_core;
|
||||
int ret = 0;
|
||||
|
||||
if (filter_flags == pdata->l3_l4_filter) {
|
||||
dev_err(pdata->dev, "L3/L4 filtering is already %d\n",
|
||||
filter_flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = osi_config_l3_l4_filter_enable(osi_core, filter_flags);
|
||||
if (ret == 0) {
|
||||
spin_lock_bh(&pdata->ioctl_lock);
|
||||
pdata->l3_l4_filter = filter_flags;
|
||||
spin_unlock_bh(&pdata->ioctl_lock);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ether_config_ip4_filters - this function is invoked by ioctl function
|
||||
* when user issues an ioctl command to configure L3(IPv4) filtering.
|
||||
*
|
||||
* @dev: Pointer to net device structure.
|
||||
* @ifdata: pointer to IOCTL specific structure.
|
||||
*
|
||||
* Algorithm:
|
||||
* 1) Enable/disable IPv4 filtering.
|
||||
* 2) Select source/destination address matching.
|
||||
* 3) Select perfect/inverse matching.
|
||||
* 4) Update the IPv4 address into MAC register.
|
||||
*
|
||||
* Dependencies: MAC and PHY need to be initialized.
|
||||
*
|
||||
* Protection: None.
|
||||
*
|
||||
* Return 0- sucessful, non-zero - error
|
||||
*/
|
||||
static int ether_config_ip4_filters(struct net_device *dev,
|
||||
struct ether_ifr_data *ifdata)
|
||||
{
|
||||
struct ether_priv_data *pdata = netdev_priv(dev);
|
||||
struct osi_core_priv_data *osi_core = pdata->osi_core;
|
||||
struct osi_l3_l4_filter *u_l3_filter =
|
||||
(struct osi_l3_l4_filter *)ifdata->ptr;
|
||||
struct osi_l3_l4_filter l_l3_filter;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (pdata->hw_feat.l3l4_filter_num == OSI_DISABLE) {
|
||||
dev_err(pdata->dev, "ip4 filter is not supported\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ifdata->ptr == NULL) {
|
||||
dev_err(pdata->dev, "%s: Invalid data for priv ioctl %d\n",
|
||||
__func__, ifdata->ifcmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (copy_from_user(&l_l3_filter, u_l3_filter,
|
||||
sizeof(struct osi_l3_l4_filter)) != 0U) {
|
||||
dev_err(pdata->dev, "%s copy from user failed\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (l_l3_filter.filter_no > (pdata->hw_feat.l3l4_filter_num - 1U)) {
|
||||
dev_err(pdata->dev, "%d filter is not supported in the HW\n",
|
||||
l_l3_filter.filter_no);
|
||||
return ret;
|
||||
}
|
||||
|
||||
spin_lock_bh(&pdata->ioctl_lock);
|
||||
if (pdata->l3_l4_filter == OSI_DISABLE) {
|
||||
ret = osi_config_l3_l4_filter_enable(osi_core, 1);
|
||||
if (ret == 0) {
|
||||
pdata->l3_l4_filter = OSI_ENABLE;
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&pdata->ioctl_lock);
|
||||
|
||||
/* configure the L3 filters */
|
||||
ret = osi_config_l3_filters(osi_core, l_l3_filter.filter_no,
|
||||
l_l3_filter.filter_enb_dis,
|
||||
OSI_IP4_FILTER,
|
||||
l_l3_filter.src_dst_addr_match,
|
||||
l_l3_filter.perfect_inverse_match);
|
||||
if (ret != 0) {
|
||||
dev_err(pdata->dev, "osi_config_l3_filters failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = osi_update_ip4_addr(osi_core, l_l3_filter.filter_no,
|
||||
l_l3_filter.ip4_addr,
|
||||
l_l3_filter.src_dst_addr_match);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ether_config_ip6_filters- This function is invoked by ioctl when user
|
||||
* issues an ioctl command to configure L3(IPv6) filtering.
|
||||
*
|
||||
* @dev: pointer to net device structure.
|
||||
* @ifdata:pointer to IOCTL specific structure.
|
||||
*
|
||||
* Algorithm:
|
||||
* 1) Enable/disable IPv6 filtering.
|
||||
* 2) Select source/destination address matching.
|
||||
* 3) Select perfect/inverse matching.
|
||||
* 4) Update the IPv6 address into MAC register.
|
||||
*
|
||||
* Dependencies: MAC and PHY need to be initialized.
|
||||
*
|
||||
* Protection: None.
|
||||
* Return 0- sucessful, non-zero - error
|
||||
*
|
||||
*/
|
||||
static int ether_config_ip6_filters(struct net_device *dev,
|
||||
struct ether_ifr_data *ifdata)
|
||||
{
|
||||
struct ether_priv_data *pdata = netdev_priv(dev);
|
||||
struct osi_core_priv_data *osi_core = pdata->osi_core;
|
||||
struct osi_l3_l4_filter *u_l3_filter =
|
||||
(struct osi_l3_l4_filter *)ifdata->ptr;
|
||||
struct osi_l3_l4_filter l_l3_filter;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (pdata->hw_feat.l3l4_filter_num == OSI_DISABLE) {
|
||||
dev_err(pdata->dev, "ip6 filter is not supported in the HW\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ifdata->ptr == NULL) {
|
||||
dev_err(pdata->dev, "%s: Invalid data for priv ioctl %d\n",
|
||||
__func__, ifdata->ifcmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (copy_from_user(&l_l3_filter, u_l3_filter,
|
||||
sizeof(struct osi_l3_l4_filter)) != 0U) {
|
||||
dev_err(pdata->dev, "%s copy from user failed\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (l_l3_filter.filter_no > (pdata->hw_feat.l3l4_filter_num - 1U)) {
|
||||
dev_err(pdata->dev, "%d filter is not supported in the HW\n",
|
||||
l_l3_filter.filter_no);
|
||||
return ret;
|
||||
}
|
||||
|
||||
spin_lock_bh(&pdata->ioctl_lock);
|
||||
if (pdata->l3_l4_filter == OSI_DISABLE) {
|
||||
ret = osi_config_l3_l4_filter_enable(osi_core, 1);
|
||||
if (ret == 0) {
|
||||
pdata->l3_l4_filter = OSI_ENABLE;
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&pdata->ioctl_lock);
|
||||
|
||||
/* configure the L3 filters */
|
||||
ret = osi_config_l3_filters(osi_core, l_l3_filter.filter_no,
|
||||
l_l3_filter.filter_enb_dis,
|
||||
OSI_IP6_FILTER,
|
||||
l_l3_filter.src_dst_addr_match,
|
||||
l_l3_filter.perfect_inverse_match);
|
||||
if (ret != 0) {
|
||||
dev_err(pdata->dev, "osi_config_l3_filters failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return osi_update_ip6_addr(osi_core, l_l3_filter.filter_no,
|
||||
l_l3_filter.ip6_addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* ether_config_tcp_udp_filters- This function is invoked by
|
||||
* ioctl function when user issues an ioctl command to configure
|
||||
* L4(TCP/UDP) filtering.
|
||||
*
|
||||
* @dev: pointer to net device structure.
|
||||
* @ifdata: pointer to IOCTL specific structure.
|
||||
* @tcp_udp: flag to indicate TCP/UDP filtering.
|
||||
*
|
||||
* Algorithm:
|
||||
* 1) Enable/disable L4 filtering.
|
||||
* 2) Select TCP/UDP filtering.
|
||||
* 3) Select source/destination port matching.
|
||||
* 4) select perfect/inverse matching.
|
||||
* 5) Update the port number into MAC register.
|
||||
*
|
||||
* Dependencies: MAC and PHY need to be initialized.
|
||||
*
|
||||
* Protection: None.
|
||||
*
|
||||
* Return 0- sucessful, non-zero - error
|
||||
*
|
||||
*/
|
||||
static int ether_config_tcp_udp_filters(struct net_device *dev,
|
||||
struct ether_ifr_data *ifdata,
|
||||
unsigned int tcp_udp)
|
||||
{
|
||||
struct ether_priv_data *pdata = netdev_priv(dev);
|
||||
struct osi_core_priv_data *osi_core = pdata->osi_core;
|
||||
struct osi_l3_l4_filter *u_l4_filter =
|
||||
(struct osi_l3_l4_filter *)ifdata->ptr;
|
||||
struct osi_l3_l4_filter l_l4_filter;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (ifdata->ptr == NULL) {
|
||||
dev_err(pdata->dev, "%s: Invalid data for priv ioctl %d\n",
|
||||
__func__, ifdata->ifcmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (pdata->hw_feat.l3l4_filter_num == OSI_DISABLE) {
|
||||
dev_err(pdata->dev,
|
||||
"L4 is not supported in the HW\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (copy_from_user(&l_l4_filter, u_l4_filter,
|
||||
sizeof(struct osi_l3_l4_filter)) != 0U) {
|
||||
dev_err(pdata->dev, "%s copy from user failed", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (l_l4_filter.filter_no > (pdata->hw_feat.l3l4_filter_num - 1U)) {
|
||||
dev_err(pdata->dev, "%d filter is not supported in the HW\n",
|
||||
l_l4_filter.filter_no);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* configure the L4 filters */
|
||||
ret = osi_config_l4_filters(osi_core, l_l4_filter.filter_no,
|
||||
l_l4_filter.filter_enb_dis,
|
||||
tcp_udp,
|
||||
l_l4_filter.src_dst_addr_match,
|
||||
l_l4_filter.perfect_inverse_match);
|
||||
if (ret != 0) {
|
||||
dev_err(pdata->dev, "osi_config_l4_filters failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = osi_update_l4_port_no(osi_core, l_l4_filter.filter_no,
|
||||
l_l4_filter.port_no,
|
||||
l_l4_filter.src_dst_addr_match);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ether_config_vlan_filter- This function is invoked by ioctl function
|
||||
* when user issues an ioctl command to configure VALN filtering.
|
||||
*
|
||||
* @dev: pointer to net device structure.
|
||||
* @ifdata: pointer to IOCTL specific structure.
|
||||
*
|
||||
* Algorithm:
|
||||
* 1) enable/disable VLAN filtering.
|
||||
* 2) select perfect/hash filtering.
|
||||
*
|
||||
* Dependencies: MAC and PHY need to be initialized.
|
||||
*
|
||||
* Protection: None.
|
||||
*
|
||||
* Return 0- sucessful, non-zero - error
|
||||
*
|
||||
*/
|
||||
static int ether_config_vlan_filter(struct net_device *dev,
|
||||
struct ether_ifr_data *ifdata)
|
||||
{
|
||||
struct ether_priv_data *pdata = netdev_priv(dev);
|
||||
struct osi_core_priv_data *osi_core = pdata->osi_core;
|
||||
struct osi_vlan_filter *u_vlan_filter =
|
||||
(struct osi_vlan_filter *)ifdata->ptr;
|
||||
struct osi_vlan_filter l_vlan_filter;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (ifdata->ptr == NULL) {
|
||||
dev_err(pdata->dev, "%s: Invalid data for priv ioctl %d\n",
|
||||
__func__, ifdata->ifcmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (copy_from_user(&l_vlan_filter, u_vlan_filter,
|
||||
sizeof(struct osi_vlan_filter)) != 0U) {
|
||||
dev_err(pdata->dev, "%s copy from user failed", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/*0 - perfect and 1 - hash filtering */
|
||||
if ((l_vlan_filter.perfect_hash == OSI_HASH_FILTER_MODE) &&
|
||||
(pdata->hw_feat.vlan_hash_en == OSI_DISABLE)) {
|
||||
dev_err(pdata->dev, "VLAN HASH filtering is not supported\n");
|
||||
return ret;
|
||||
}
|
||||
/* configure the vlan filter FIXME: Current code supports VLAN
|
||||
filtering for last VLAN tag/id added or default tag/vid 1. */
|
||||
ret = osi_config_vlan_filtering(osi_core, l_vlan_filter.filter_enb_dis,
|
||||
l_vlan_filter.perfect_hash,
|
||||
l_vlan_filter.perfect_inverse_match);
|
||||
if (ret == 0) {
|
||||
pdata->vlan_hash_filtering = l_vlan_filter.perfect_hash;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ether_config_l2_da_filter- This function is invoked by ioctl function
|
||||
* when user issues an ioctl command to configure L2 destination
|
||||
* addressing filtering mode.
|
||||
*
|
||||
* @dev: Pointer to net device structure.
|
||||
* @ifdata: Pointer to IOCTL specific structure.
|
||||
*
|
||||
* Algorithm:
|
||||
* 1) Selects perfect/hash filtering.
|
||||
* 2) Selects perfect/inverse matching.
|
||||
*
|
||||
* Dependencies: MAC and PHY need to be initialized.
|
||||
*
|
||||
* Protection: None.
|
||||
*
|
||||
* Return 0- sucessful, non-zero - error
|
||||
*/
|
||||
static int ether_config_l2_da_filter(struct net_device *dev,
|
||||
struct ether_ifr_data *ifdata)
|
||||
{
|
||||
struct ether_priv_data *pdata = netdev_priv(dev);
|
||||
struct osi_core_priv_data *osi_core = pdata->osi_core;
|
||||
struct osi_l2_da_filter *u_l2_da_filter =
|
||||
(struct osi_l2_da_filter *)ifdata->ptr;
|
||||
struct osi_l2_da_filter l_l2_da_filter;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (ifdata->ptr == NULL) {
|
||||
dev_err(pdata->dev, "%s: Invalid data for priv ioctl %d\n",
|
||||
__func__, ifdata->ifcmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (copy_from_user(&l_l2_da_filter, u_l2_da_filter,
|
||||
sizeof(struct osi_l2_da_filter)) != 0U) {
|
||||
return -EFAULT;
|
||||
}
|
||||
if (l_l2_da_filter.perfect_hash == OSI_HASH_FILTER_MODE) {
|
||||
dev_err(pdata->dev,
|
||||
"select HASH FILTERING for L2 DA is not Supported in SW\n");
|
||||
return ret;
|
||||
} else {
|
||||
/* FIXME: Need to understand if filtering will work on addr0.
|
||||
* Do we need to have pdata->num_mac_addr_regs > 1 check?
|
||||
*/
|
||||
pdata->l2_filtering_mode = OSI_PERFECT_FILTER_MODE;
|
||||
}
|
||||
|
||||
/* configure L2 DA perfect/inverse_matching */
|
||||
ret = osi_config_l2_da_perfect_inverse_match(osi_core,
|
||||
l_l2_da_filter.perfect_inverse_match);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ether_priv_ioctl - Handle private IOCTLs
|
||||
* @ndev: network device structure
|
||||
@@ -202,6 +589,43 @@ int ether_handle_priv_ioctl(struct net_device *ndev,
|
||||
case ETHER_CONFIG_ARP_OFFLOAD:
|
||||
ret = ether_config_arp_offload(pdata, &ifdata);
|
||||
break;
|
||||
case EQOS_L3_L4_FILTER_CMD:
|
||||
/* flags should be 0x0 or 0x1, discard any other */
|
||||
if (pdata->hw_feat.l3l4_filter_num > 0U &&
|
||||
((ifdata.if_flags == OSI_ENABLE) ||
|
||||
(ifdata.if_flags == OSI_DISABLE))) {
|
||||
ret = ether_config_l3_l4_filtering(ndev,
|
||||
ifdata.if_flags);
|
||||
if (ret == 0) {
|
||||
ret = EQOS_CONFIG_SUCCESS;
|
||||
} else {
|
||||
ret = EQOS_CONFIG_FAIL;
|
||||
}
|
||||
} else {
|
||||
dev_err(pdata->dev, "L3/L4 filters are not supported\n");
|
||||
ret = -EOPNOTSUPP;
|
||||
}
|
||||
break;
|
||||
case EQOS_IPV4_FILTERING_CMD:
|
||||
ret = ether_config_ip4_filters(ndev, &ifdata);
|
||||
break;
|
||||
case EQOS_IPV6_FILTERING_CMD:
|
||||
ret = ether_config_ip6_filters(ndev, &ifdata);
|
||||
break;
|
||||
case EQOS_UDP_FILTERING_CMD:
|
||||
ret = ether_config_tcp_udp_filters(ndev, &ifdata,
|
||||
OSI_L4_FILTER_UDP);
|
||||
break;
|
||||
case EQOS_TCP_FILTERING_CMD:
|
||||
ret = ether_config_tcp_udp_filters(ndev, &ifdata,
|
||||
OSI_L4_FILTER_TCP);
|
||||
break;
|
||||
case EQOS_VLAN_FILTERING_CMD:
|
||||
ret = ether_config_vlan_filter(ndev, &ifdata);
|
||||
break;
|
||||
case EQOS_L2_DA_FILTERING_CMD:
|
||||
ret = ether_config_l2_da_filter(ndev, &ifdata);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -20,12 +20,23 @@
|
||||
#define NUM_BYTES_IN_IPADDR 4
|
||||
#define MAX_IP_ADDR_BYTE 0xFF
|
||||
/* Remote wakeup filter */
|
||||
#define EQOS_RWK_FILTER_LENGTH 8
|
||||
#define EQOS_RWK_FILTER_LENGTH 8
|
||||
|
||||
/* private ioctl number*/
|
||||
#define ETHER_AVB_ALGORITHM 27
|
||||
#define ETHER_GET_AVB_ALGORITHM 46
|
||||
#define ETHER_AVB_ALGORITHM 27
|
||||
/* L3/L4 filter */
|
||||
#define EQOS_L3_L4_FILTER_CMD 29
|
||||
/* IPv4/6 and TCP/UDP filtering */
|
||||
#define EQOS_IPV4_FILTERING_CMD 30
|
||||
#define EQOS_IPV6_FILTERING_CMD 31
|
||||
#define EQOS_UDP_FILTERING_CMD 32
|
||||
#define EQOS_TCP_FILTERING_CMD 33
|
||||
/* VLAN filtering */
|
||||
#define EQOS_VLAN_FILTERING_CMD 34
|
||||
/* L2 DA filtering */
|
||||
#define EQOS_L2_DA_FILTERING_CMD 35
|
||||
#define ETHER_CONFIG_ARP_OFFLOAD 36
|
||||
#define ETHER_GET_AVB_ALGORITHM 46
|
||||
|
||||
/**
|
||||
* struct ether_ifr_data - Private data of struct ifreq
|
||||
|
||||
Reference in New Issue
Block a user