mac80211: disconnect if channel switch fails

Disconnect from the AP if channel switching in the
driver failed or if the new channel is unavailable.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2012-08-01 22:32:45 +02:00
parent 30dd3edf97
commit 882a7c69d3
2 changed files with 38 additions and 18 deletions

View File

@ -411,6 +411,7 @@ struct ieee80211_if_managed {
struct work_struct monitor_work; struct work_struct monitor_work;
struct work_struct chswitch_work; struct work_struct chswitch_work;
struct work_struct beacon_connection_loss_work; struct work_struct beacon_connection_loss_work;
struct work_struct csa_connection_drop_work;
unsigned long beacon_timeout; unsigned long beacon_timeout;
unsigned long probe_timeout; unsigned long probe_timeout;

View File

@ -730,16 +730,13 @@ void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
trace_api_chswitch_done(sdata, success); trace_api_chswitch_done(sdata, success);
if (!success) { if (!success) {
/* sdata_info(sdata,
* If the channel switch was not successful, stay "driver channel switch failed, disconnecting\n");
* around on the old channel. We currently lack ieee80211_queue_work(&sdata->local->hw,
* good handling of this situation, possibly we &ifmgd->csa_connection_drop_work);
* should just drop the association. } else {
*/ ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
sdata->local->csa_channel = sdata->local->oper_channel;
} }
ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
} }
EXPORT_SYMBOL(ieee80211_chswitch_done); EXPORT_SYMBOL(ieee80211_chswitch_done);
@ -784,8 +781,14 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
return; return;
new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) {
sdata_info(sdata,
"AP %pM switches to unsupported channel (%d MHz), disconnecting\n",
ifmgd->associated->bssid, new_freq);
ieee80211_queue_work(&sdata->local->hw,
&ifmgd->csa_connection_drop_work);
return; return;
}
sdata->local->csa_channel = new_ch; sdata->local->csa_channel = new_ch;
@ -1692,7 +1695,8 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
} }
EXPORT_SYMBOL(ieee80211_ap_probereq_get); EXPORT_SYMBOL(ieee80211_ap_probereq_get);
static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata) static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata,
bool transmit_frame)
{ {
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
@ -1704,12 +1708,10 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
return; return;
} }
sdata_info(sdata, "Connection to AP %pM lost\n",
ifmgd->associated->bssid);
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
false, frame_buf); transmit_frame, frame_buf);
ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
mutex_unlock(&ifmgd->mtx); mutex_unlock(&ifmgd->mtx);
/* /*
@ -1739,10 +1741,24 @@ static void ieee80211_beacon_connection_loss_work(struct work_struct *work)
rcu_read_unlock(); rcu_read_unlock();
} }
if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) {
__ieee80211_connection_loss(sdata); sdata_info(sdata, "Connection to AP %pM lost\n",
else ifmgd->bssid);
__ieee80211_disconnect(sdata, false);
} else {
ieee80211_mgd_probe_ap(sdata, true); ieee80211_mgd_probe_ap(sdata, true);
}
}
static void ieee80211_csa_connection_drop_work(struct work_struct *work)
{
struct ieee80211_sub_if_data *sdata =
container_of(work, struct ieee80211_sub_if_data,
u.mgd.csa_connection_drop_work);
ieee80211_wake_queues_by_reason(&sdata->local->hw,
IEEE80211_QUEUE_STOP_REASON_CSA);
__ieee80211_disconnect(sdata, true);
} }
void ieee80211_beacon_loss(struct ieee80211_vif *vif) void ieee80211_beacon_loss(struct ieee80211_vif *vif)
@ -2929,6 +2945,7 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)
cancel_work_sync(&ifmgd->monitor_work); cancel_work_sync(&ifmgd->monitor_work);
cancel_work_sync(&ifmgd->beacon_connection_loss_work); cancel_work_sync(&ifmgd->beacon_connection_loss_work);
cancel_work_sync(&ifmgd->csa_connection_drop_work);
if (del_timer_sync(&ifmgd->timer)) if (del_timer_sync(&ifmgd->timer))
set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
@ -2985,6 +3002,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work); INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);
INIT_WORK(&ifmgd->beacon_connection_loss_work, INIT_WORK(&ifmgd->beacon_connection_loss_work,
ieee80211_beacon_connection_loss_work); ieee80211_beacon_connection_loss_work);
INIT_WORK(&ifmgd->csa_connection_drop_work,
ieee80211_csa_connection_drop_work);
INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work); INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work);
setup_timer(&ifmgd->timer, ieee80211_sta_timer, setup_timer(&ifmgd->timer, ieee80211_sta_timer,
(unsigned long) sdata); (unsigned long) sdata);