diff options
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r-- | net/mac80211/mlme.c | 56 |
1 files changed, 52 insertions, 4 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 7bfb0ebaaf00..6b74489fb9c6 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -341,7 +341,11 @@ static void ieee80211_chswitch_work(struct work_struct *work) | |||
341 | goto out; | 341 | goto out; |
342 | 342 | ||
343 | sdata->local->oper_channel = sdata->local->csa_channel; | 343 | sdata->local->oper_channel = sdata->local->csa_channel; |
344 | ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL); | 344 | if (!sdata->local->ops->channel_switch) { |
345 | /* call "hw_config" only if doing sw channel switch */ | ||
346 | ieee80211_hw_config(sdata->local, | ||
347 | IEEE80211_CONF_CHANGE_CHANNEL); | ||
348 | } | ||
345 | 349 | ||
346 | /* XXX: shouldn't really modify cfg80211-owned data! */ | 350 | /* XXX: shouldn't really modify cfg80211-owned data! */ |
347 | ifmgd->associated->channel = sdata->local->oper_channel; | 351 | ifmgd->associated->channel = sdata->local->oper_channel; |
@@ -353,6 +357,29 @@ static void ieee80211_chswitch_work(struct work_struct *work) | |||
353 | mutex_unlock(&ifmgd->mtx); | 357 | mutex_unlock(&ifmgd->mtx); |
354 | } | 358 | } |
355 | 359 | ||
360 | void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success) | ||
361 | { | ||
362 | struct ieee80211_sub_if_data *sdata; | ||
363 | struct ieee80211_if_managed *ifmgd; | ||
364 | |||
365 | sdata = vif_to_sdata(vif); | ||
366 | ifmgd = &sdata->u.mgd; | ||
367 | |||
368 | trace_api_chswitch_done(sdata, success); | ||
369 | if (!success) { | ||
370 | /* | ||
371 | * If the channel switch was not successful, stay | ||
372 | * around on the old channel. We currently lack | ||
373 | * good handling of this situation, possibly we | ||
374 | * should just drop the association. | ||
375 | */ | ||
376 | sdata->local->csa_channel = sdata->local->oper_channel; | ||
377 | } | ||
378 | |||
379 | ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); | ||
380 | } | ||
381 | EXPORT_SYMBOL(ieee80211_chswitch_done); | ||
382 | |||
356 | static void ieee80211_chswitch_timer(unsigned long data) | 383 | static void ieee80211_chswitch_timer(unsigned long data) |
357 | { | 384 | { |
358 | struct ieee80211_sub_if_data *sdata = | 385 | struct ieee80211_sub_if_data *sdata = |
@@ -369,7 +396,8 @@ static void ieee80211_chswitch_timer(unsigned long data) | |||
369 | 396 | ||
370 | void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | 397 | void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, |
371 | struct ieee80211_channel_sw_ie *sw_elem, | 398 | struct ieee80211_channel_sw_ie *sw_elem, |
372 | struct ieee80211_bss *bss) | 399 | struct ieee80211_bss *bss, |
400 | u64 timestamp) | ||
373 | { | 401 | { |
374 | struct cfg80211_bss *cbss = | 402 | struct cfg80211_bss *cbss = |
375 | container_of((void *)bss, struct cfg80211_bss, priv); | 403 | container_of((void *)bss, struct cfg80211_bss, priv); |
@@ -397,6 +425,24 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
397 | 425 | ||
398 | sdata->local->csa_channel = new_ch; | 426 | sdata->local->csa_channel = new_ch; |
399 | 427 | ||
428 | if (sdata->local->ops->channel_switch) { | ||
429 | /* use driver's channel switch callback */ | ||
430 | struct ieee80211_channel_switch ch_switch; | ||
431 | memset(&ch_switch, 0, sizeof(ch_switch)); | ||
432 | ch_switch.timestamp = timestamp; | ||
433 | if (sw_elem->mode) { | ||
434 | ch_switch.block_tx = true; | ||
435 | ieee80211_stop_queues_by_reason(&sdata->local->hw, | ||
436 | IEEE80211_QUEUE_STOP_REASON_CSA); | ||
437 | } | ||
438 | ch_switch.channel = new_ch; | ||
439 | ch_switch.count = sw_elem->count; | ||
440 | ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; | ||
441 | drv_channel_switch(sdata->local, &ch_switch); | ||
442 | return; | ||
443 | } | ||
444 | |||
445 | /* channel switch handled in software */ | ||
400 | if (sw_elem->count <= 1) { | 446 | if (sw_elem->count <= 1) { |
401 | ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); | 447 | ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); |
402 | } else { | 448 | } else { |
@@ -1316,7 +1362,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, | |||
1316 | ETH_ALEN) == 0)) { | 1362 | ETH_ALEN) == 0)) { |
1317 | struct ieee80211_channel_sw_ie *sw_elem = | 1363 | struct ieee80211_channel_sw_ie *sw_elem = |
1318 | (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem; | 1364 | (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem; |
1319 | ieee80211_sta_process_chanswitch(sdata, sw_elem, bss); | 1365 | ieee80211_sta_process_chanswitch(sdata, sw_elem, |
1366 | bss, rx_status->mactime); | ||
1320 | } | 1367 | } |
1321 | } | 1368 | } |
1322 | 1369 | ||
@@ -1648,7 +1695,8 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, | |||
1648 | 1695 | ||
1649 | ieee80211_sta_process_chanswitch(sdata, | 1696 | ieee80211_sta_process_chanswitch(sdata, |
1650 | &mgmt->u.action.u.chan_switch.sw_elem, | 1697 | &mgmt->u.action.u.chan_switch.sw_elem, |
1651 | (void *)ifmgd->associated->priv); | 1698 | (void *)ifmgd->associated->priv, |
1699 | rx_status->mactime); | ||
1652 | break; | 1700 | break; |
1653 | } | 1701 | } |
1654 | mutex_unlock(&ifmgd->mtx); | 1702 | mutex_unlock(&ifmgd->mtx); |