struct ieee80211_if_init_conf conf;
int res;
bool need_hw_reconfig = 0;
+ struct sta_info *sta;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
case IEEE80211_IF_TYPE_WDS:
if (is_zero_ether_addr(sdata->u.wds.remote_addr))
return -ENOLINK;
+
+ /* Create STA entry for the WDS peer */
+ sta = sta_info_alloc(sdata, sdata->u.wds.remote_addr,
+ GFP_KERNEL);
+ if (!sta)
+ return -ENOMEM;
+
+ sta->flags |= WLAN_STA_AUTHORIZED;
+
+ res = sta_info_insert(sta);
+ if (res) {
+ sta_info_destroy(sta);
+ return res;
+ }
break;
case IEEE80211_IF_TYPE_VLAN:
if (!sdata->u.vlan.ap)
static int ieee80211_stop(struct net_device *dev)
{
- struct ieee80211_sub_if_data *sdata;
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
struct ieee80211_if_init_conf conf;
struct sta_info *sta;
int i;
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ /*
+ * Stop TX on this interface first.
+ */
+ netif_stop_queue(dev);
+ /*
+ * Now delete all active aggregation sessions.
+ */
rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list, list) {
rcu_read_unlock();
- netif_stop_queue(dev);
+ /*
+ * Remove all stations associated with this interface.
+ *
+ * This must be done before calling ops->remove_interface()
+ * because otherwise we can later invoke ops->sta_notify()
+ * whenever the STAs are removed, and that invalidates driver
+ * assumptions about always getting a vif pointer that is valid
+ * (because if we remove a STA after ops->remove_interface()
+ * the driver will have removed the vif info already!)
+ *
+ * We could relax this and only unlink the stations from the
+ * hash table and list but keep them on a per-sdata list that
+ * will be inserted back again when the interface is brought
+ * up again, but I don't currently see a use case for that,
+ * except with WDS which gets a STA entry created when it is
+ * brought up.
+ */
+ sta_info_flush(local, sdata);
/*
* Don't count this interface for promisc/allmulti while it
netif_tx_unlock_bh(local->mdev);
break;
case IEEE80211_IF_TYPE_MESH_POINT:
- sta_info_flush(local, sdata);
- /* fall through */
case IEEE80211_IF_TYPE_STA:
case IEEE80211_IF_TYPE_IBSS:
sdata->u.sta.state = IEEE80211_DISABLED;
dev->destructor = ieee80211_if_free;
}
-/* WDS specialties */
-
-int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct sta_info *sta;
- int err;
- DECLARE_MAC_BUF(mac);
-
- might_sleep();
-
- if (compare_ether_addr(remote_addr, sdata->u.wds.remote_addr) == 0)
- return 0;
-
- /* Create STA entry for the new peer */
- sta = sta_info_alloc(sdata, remote_addr, GFP_KERNEL);
- if (!sta)
- return -ENOMEM;
-
- sta->flags |= WLAN_STA_AUTHORIZED;
- err = sta_info_insert(sta);
- if (err) {
- sta_info_destroy(sta);
- return err;
- }
-
- rcu_read_lock();
-
- /* Remove STA entry for the old peer */
- sta = sta_info_get(local, sdata->u.wds.remote_addr);
- if (sta)
- sta_info_unlink(&sta);
- else
- printk(KERN_DEBUG "%s: could not find STA entry for WDS link "
- "peer %s\n",
- dev->name, print_mac(mac, sdata->u.wds.remote_addr));
-
- /* Update WDS link data */
- memcpy(&sdata->u.wds.remote_addr, remote_addr, ETH_ALEN);
-
- rcu_read_unlock();
-
- if (sta) {
- synchronize_rcu();
- sta_info_destroy(sta);
- }
-
- return 0;
-}
-
/* everything else */
static int __ieee80211_if_config(struct net_device *dev,
int ieee80211_if_config(struct net_device *dev);
int ieee80211_if_config_beacon(struct net_device *dev);
void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx);
-int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr);
void ieee80211_if_setup(struct net_device *dev);
int ieee80211_hw_config_ht(struct ieee80211_local *local, int enable_ht,
struct ieee80211_ht_info *req_ht_cap,
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct sta_info *sta;
struct sk_buff *skb;
+ int flushed;
ASSERT_RTNL();
break;
}
case IEEE80211_IF_TYPE_WDS:
- rcu_read_lock();
- sta = sta_info_get(local, sdata->u.wds.remote_addr);
- if (sta) {
- sta_info_unlink(&sta);
- } else {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: Someone had deleted my STA "
- "entry for the WDS link\n", dev->name);
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
- }
- rcu_read_unlock();
- if (sta) {
- synchronize_rcu();
- sta_info_destroy(sta);
- }
+ /* nothing to do */
break;
case IEEE80211_IF_TYPE_MESH_POINT:
case IEEE80211_IF_TYPE_STA:
break;
}
- /* remove all STAs that are bound to this virtual interface */
- sta_info_flush(local, sdata);
+ flushed = sta_info_flush(local, sdata);
+ WARN_ON(flushed);
memset(&sdata->u, 0, sizeof(sdata->u));
ieee80211_if_sdata_init(sdata);
ieee80211_sta_req_auth(dev, &sdata->u.sta);
return 0;
} else if (sdata->vif.type == IEEE80211_IF_TYPE_WDS) {
- if (memcmp(sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data,
- ETH_ALEN) == 0)
- return 0;
- return ieee80211_if_update_wds(dev, (u8 *) &ap_addr->sa_data);
+ /*
+ * If it is necessary to update the WDS peer address
+ * while the interface is running, then we need to do
+ * more work here, namely if it is running we need to
+ * add a new and remove the old STA entry, this is
+ * normally handled by _open() and _stop().
+ */
+ if (netif_running(dev))
+ return -EBUSY;
+
+ memcpy(&sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data,
+ ETH_ALEN);
+
+ return 0;
}
return -EOPNOTSUPP;
unsigned long flags;
DECLARE_MAC_BUF(mac);
+ WARN_ON(!netif_running(sdata->dev));
+
spin_lock_irqsave(&local->sta_lock, flags);
/* check if STA exists already */
if (__sta_info_find(local, sta->addr)) {
/**
* sta_info_flush - flush matching STA entries from the STA table
+ *
+ * Returns the number of removed STA entries.
+ *
* @local: local interface data
* @sdata: matching rule for the net device (sta->dev) or %NULL to match all STAs
*/
-void sta_info_flush(struct ieee80211_local *local,
+int sta_info_flush(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata)
{
struct sta_info *sta, *tmp;
LIST_HEAD(tmp_list);
+ int ret = 0;
unsigned long flags;
might_sleep();
list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
if (!sdata || sdata == sta->sdata) {
__sta_info_unlink(&sta);
- if (sta)
+ if (sta) {
list_add_tail(&sta->list, &tmp_list);
+ ret++;
+ }
}
}
spin_unlock_irqrestore(&local->sta_lock, flags);
list_for_each_entry_safe(sta, tmp, &tmp_list, list)
sta_info_destroy(sta);
+
+ return ret;
}
void sta_info_init(struct ieee80211_local *local);
int sta_info_start(struct ieee80211_local *local);
void sta_info_stop(struct ieee80211_local *local);
-void sta_info_flush(struct ieee80211_local *local,
+int sta_info_flush(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata);
#endif /* STA_INFO_H */