Skip to content

Commit

Permalink
ath6kl: Implement ndo_set_rx_mode()
Browse files Browse the repository at this point in the history
There are maximum of seven multicast filter are supported
by hw. When the requested number of filters exceeds the
maximum supported one, multicast filtering is completely
disabled, the requested filters will be configured in
firmware and the only multicast frames that host is interested
in will be passed to host for further processing otherwise.

Signed-off-by: Vasanthakumar Thiagarajan <vthiagar@qca.qualcomm.com>
  • Loading branch information
Vasanthakumar Thiagarajan authored and Kalle Valo committed Jan 9, 2012
1 parent f914edd commit 80abaf9
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 0 deletions.
8 changes: 8 additions & 0 deletions drivers/net/wireless/ath/ath6kl/cfg80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -2818,12 +2818,15 @@ static int ath6kl_init_if_data(struct ath6kl_vif *vif)
set_bit(WMM_ENABLED, &vif->flags);
spin_lock_init(&vif->if_lock);

INIT_LIST_HEAD(&vif->mc_filter);

return 0;
}

void ath6kl_deinit_if_data(struct ath6kl_vif *vif)
{
struct ath6kl *ar = vif->ar;
struct ath6kl_mc_filter *mc_filter, *tmp;

aggr_module_destroy(vif->aggr_cntxt);

Expand All @@ -2832,6 +2835,11 @@ void ath6kl_deinit_if_data(struct ath6kl_vif *vif)
if (vif->nw_type == ADHOC_NETWORK)
ar->ibss_if_active = false;

list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
list_del(&mc_filter->list);
kfree(mc_filter);
}

unregister_netdevice(vif->ndev);

ar->num_vif--;
Expand Down
9 changes: 9 additions & 0 deletions drivers/net/wireless/ath/ath6kl/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,13 @@ enum ath6kl_hif_type {
ATH6KL_HIF_TYPE_USB,
};

/* Max number of filters that hw supports */
#define ATH6K_MAX_MC_FILTERS_PER_LIST 7
struct ath6kl_mc_filter {
struct list_head list;
char hw_addr[ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE];
};

/*
* Driver's maximum limit, note that some firmwares support only one vif
* and the runtime (current) limit must be checked from ar->vif_max.
Expand Down Expand Up @@ -473,6 +480,8 @@ struct ath6kl_vif {
u8 assoc_bss_dtim_period;
struct net_device_stats net_stats;
struct target_stats target_stats;

struct list_head mc_filter;
};

#define WOW_LIST_ID 0
Expand Down
110 changes: 110 additions & 0 deletions drivers/net/wireless/ath/ath6kl/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1052,12 +1052,122 @@ static int ath6kl_set_features(struct net_device *dev, u32 features)
return err;
}

static void ath6kl_set_multicast_list(struct net_device *ndev)
{
struct ath6kl_vif *vif = netdev_priv(ndev);
bool mc_all_on = false, mc_all_off = false;
int mc_count = netdev_mc_count(ndev);
struct netdev_hw_addr *ha;
bool found;
struct ath6kl_mc_filter *mc_filter, *tmp;
struct list_head mc_filter_new;
int ret;

if (!test_bit(WMI_READY, &vif->ar->flag) ||
!test_bit(WLAN_ENABLED, &vif->flags))
return;

mc_all_on = !!(ndev->flags & IFF_PROMISC) ||
!!(ndev->flags & IFF_ALLMULTI) ||
!!(mc_count > ATH6K_MAX_MC_FILTERS_PER_LIST);

mc_all_off = !(ndev->flags & IFF_MULTICAST) || mc_count == 0;

if (mc_all_on || mc_all_off) {
/* Enable/disable all multicast */
ath6kl_dbg(ATH6KL_DBG_TRC, "%s multicast filter\n",
mc_all_on ? "enabling" : "disabling");
ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi, vif->fw_vif_idx,
mc_all_on);
if (ret)
ath6kl_warn("Failed to %s multicast receive\n",
mc_all_on ? "enable" : "disable");
return;
}

list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
found = false;
netdev_for_each_mc_addr(ha, ndev) {
if (memcmp(ha->addr, mc_filter->hw_addr,
ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE) == 0) {
found = true;
break;
}
}

if (!found) {
/*
* Delete the filter which was previously set
* but not in the new request.
*/
ath6kl_dbg(ATH6KL_DBG_TRC,
"Removing %pM from multicast filter\n",
mc_filter->hw_addr);
ret = ath6kl_wmi_add_del_mcast_filter_cmd(vif->ar->wmi,
vif->fw_vif_idx, mc_filter->hw_addr,
false);
if (ret) {
ath6kl_warn("Failed to remove multicast filter:%pM\n",
mc_filter->hw_addr);
return;
}

list_del(&mc_filter->list);
kfree(mc_filter);
}
}

INIT_LIST_HEAD(&mc_filter_new);

netdev_for_each_mc_addr(ha, ndev) {
found = false;
list_for_each_entry(mc_filter, &vif->mc_filter, list) {
if (memcmp(ha->addr, mc_filter->hw_addr,
ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE) == 0) {
found = true;
break;
}
}

if (!found) {
mc_filter = kzalloc(sizeof(struct ath6kl_mc_filter),
GFP_ATOMIC);
if (!mc_filter) {
WARN_ON(1);
goto out;
}

memcpy(mc_filter->hw_addr, ha->addr,
ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE);
/* Set the multicast filter */
ath6kl_dbg(ATH6KL_DBG_TRC,
"Adding %pM to multicast filter list\n",
mc_filter->hw_addr);
ret = ath6kl_wmi_add_del_mcast_filter_cmd(vif->ar->wmi,
vif->fw_vif_idx, mc_filter->hw_addr,
true);
if (ret) {
ath6kl_warn("Failed to add multicast filter :%pM\n",
mc_filter->hw_addr);
kfree(mc_filter);
goto out;
}

list_add_tail(&mc_filter->list, &mc_filter_new);
}
}

out:
list_splice_tail(&mc_filter_new, &vif->mc_filter);
}

static struct net_device_ops ath6kl_netdev_ops = {
.ndo_open = ath6kl_open,
.ndo_stop = ath6kl_close,
.ndo_start_xmit = ath6kl_data_tx,
.ndo_get_stats = ath6kl_get_stats,
.ndo_set_features = ath6kl_set_features,
.ndo_set_rx_mode = ath6kl_set_multicast_list,
};

void init_netdev(struct net_device *dev)
Expand Down

0 comments on commit 80abaf9

Please sign in to comment.