summaryrefslogtreecommitdiff
path: root/net/mac80211/sta_info.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/sta_info.c')
-rw-r--r--net/mac80211/sta_info.c106
1 files changed, 89 insertions, 17 deletions
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 464bc691644b..fcd9027c6699 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -351,6 +351,38 @@ static int sta_info_insert_check(struct sta_info *sta)
return 0;
}
+static int sta_info_insert_drv_state(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta)
+{
+ enum ieee80211_sta_state state;
+ int err = 0;
+
+ for (state = IEEE80211_STA_NOTEXIST; state < sta->sta_state; state++) {
+ err = drv_sta_state(local, sdata, sta, state, state + 1);
+ if (err)
+ break;
+ }
+
+ if (!err) {
+ sta->uploaded = true;
+ return 0;
+ }
+
+ if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ printk(KERN_DEBUG
+ "%s: failed to move IBSS STA %pM to state %d (%d) - keeping it anyway.\n",
+ sdata->name, sta->sta.addr, state + 1, err);
+ err = 0;
+ }
+
+ /* unwind on error */
+ for (; state > IEEE80211_STA_NOTEXIST; state--)
+ WARN_ON(drv_sta_state(local, sdata, sta, state, state - 1));
+
+ return err;
+}
+
/*
* should be called with sta_mtx locked
* this function replaces the mutex lock
@@ -392,8 +424,11 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to "
"driver (%d) - keeping it anyway.\n",
sdata->name, sta->sta.addr, err);
- } else
- sta->uploaded = true;
+ } else {
+ err = sta_info_insert_drv_state(local, sdata, sta);
+ if (err)
+ goto out_err;
+ }
}
if (!dummy_reinsert) {
@@ -759,15 +794,19 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);
while (sta->sta_state > IEEE80211_STA_NONE) {
- int err = sta_info_move_state(sta, sta->sta_state - 1);
- if (err) {
+ ret = sta_info_move_state(sta, sta->sta_state - 1);
+ if (ret) {
WARN_ON_ONCE(1);
break;
}
}
- if (sta->uploaded)
+ if (sta->uploaded) {
drv_sta_remove(local, sdata, &sta->sta);
+ ret = drv_sta_state(local, sdata, sta, IEEE80211_STA_NONE,
+ IEEE80211_STA_NOTEXIST);
+ WARN_ON_ONCE(ret != 0);
+ }
/*
* At this point, after we wait for an RCU grace period,
@@ -1404,20 +1443,58 @@ int sta_info_move_state(struct sta_info *sta,
if (sta->sta_state == new_state)
return 0;
+ /* check allowed transitions first */
+
+ switch (new_state) {
+ case IEEE80211_STA_NONE:
+ if (sta->sta_state != IEEE80211_STA_AUTH)
+ return -EINVAL;
+ break;
+ case IEEE80211_STA_AUTH:
+ if (sta->sta_state != IEEE80211_STA_NONE &&
+ sta->sta_state != IEEE80211_STA_ASSOC)
+ return -EINVAL;
+ break;
+ case IEEE80211_STA_ASSOC:
+ if (sta->sta_state != IEEE80211_STA_AUTH &&
+ sta->sta_state != IEEE80211_STA_AUTHORIZED)
+ return -EINVAL;
+ break;
+ case IEEE80211_STA_AUTHORIZED:
+ if (sta->sta_state != IEEE80211_STA_ASSOC)
+ return -EINVAL;
+ break;
+ default:
+ WARN(1, "invalid state %d", new_state);
+ return -EINVAL;
+ }
+
+ printk(KERN_DEBUG "%s: moving STA %pM to state %d\n",
+ sta->sdata->name, sta->sta.addr, new_state);
+
+ /*
+ * notify the driver before the actual changes so it can
+ * fail the transition
+ */
+ if (test_sta_flag(sta, WLAN_STA_INSERTED)) {
+ int err = drv_sta_state(sta->local, sta->sdata, sta,
+ sta->sta_state, new_state);
+ if (err)
+ return err;
+ }
+
+ /* reflect the change in all state variables */
+
switch (new_state) {
case IEEE80211_STA_NONE:
if (sta->sta_state == IEEE80211_STA_AUTH)
clear_bit(WLAN_STA_AUTH, &sta->_flags);
- else
- return -EINVAL;
break;
case IEEE80211_STA_AUTH:
if (sta->sta_state == IEEE80211_STA_NONE)
set_bit(WLAN_STA_AUTH, &sta->_flags);
else if (sta->sta_state == IEEE80211_STA_ASSOC)
clear_bit(WLAN_STA_ASSOC, &sta->_flags);
- else
- return -EINVAL;
break;
case IEEE80211_STA_ASSOC:
if (sta->sta_state == IEEE80211_STA_AUTH) {
@@ -1426,24 +1503,19 @@ int sta_info_move_state(struct sta_info *sta,
if (sta->sdata->vif.type == NL80211_IFTYPE_AP)
atomic_dec(&sta->sdata->u.ap.num_sta_authorized);
clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
- } else
- return -EINVAL;
+ }
break;
case IEEE80211_STA_AUTHORIZED:
if (sta->sta_state == IEEE80211_STA_ASSOC) {
if (sta->sdata->vif.type == NL80211_IFTYPE_AP)
atomic_inc(&sta->sdata->u.ap.num_sta_authorized);
set_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
- } else
- return -EINVAL;
+ }
break;
default:
- WARN(1, "invalid state %d", new_state);
- return -EINVAL;
+ break;
}
- printk(KERN_DEBUG "%s: moving STA %pM to state %d\n",
- sta->sdata->name, sta->sta.addr, new_state);
sta->sta_state = new_state;
return 0;