diff options
author | Johannes Berg <johannes.berg@intel.com> | 2013-03-26 09:13:58 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-04-16 09:29:43 -0400 |
commit | b4f286a1c0ad0b84c2d502b354d4d98d5a86c64b (patch) | |
tree | f2cb487aaab163dff52c0588c1a80b1aaa951a89 | |
parent | 1ce3e82b0eb472161313183be0033e46d5c4bbaf (diff) |
mac80211: support extended channel switch
Support extended channel switch when the operating
class is one of the global operating classes as
defined in Annex E of 802.11-2012. If it isn't,
disconnect from the AP instead.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r-- | include/linux/ieee80211.h | 12 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 1 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 77 | ||||
-rw-r--r-- | net/mac80211/util.c | 7 |
4 files changed, 71 insertions, 26 deletions
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 8f80b3a93501..2a10acc65a54 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h | |||
@@ -673,6 +673,18 @@ struct ieee80211_channel_sw_ie { | |||
673 | } __packed; | 673 | } __packed; |
674 | 674 | ||
675 | /** | 675 | /** |
676 | * struct ieee80211_ext_chansw_ie | ||
677 | * | ||
678 | * This structure represents the "Extended Channel Switch Announcement element" | ||
679 | */ | ||
680 | struct ieee80211_ext_chansw_ie { | ||
681 | u8 mode; | ||
682 | u8 new_operating_class; | ||
683 | u8 new_ch_num; | ||
684 | u8 count; | ||
685 | } __packed; | ||
686 | |||
687 | /** | ||
676 | * struct ieee80211_tim | 688 | * struct ieee80211_tim |
677 | * | 689 | * |
678 | * This structure refers to "Traffic Indication Map information element" | 690 | * This structure refers to "Traffic Indication Map information element" |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 373460f9c069..10c3180b165e 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -1178,6 +1178,7 @@ struct ieee802_11_elems { | |||
1178 | const u8 *perr; | 1178 | const u8 *perr; |
1179 | const struct ieee80211_rann_ie *rann; | 1179 | const struct ieee80211_rann_ie *rann; |
1180 | const struct ieee80211_channel_sw_ie *ch_switch_ie; | 1180 | const struct ieee80211_channel_sw_ie *ch_switch_ie; |
1181 | const struct ieee80211_ext_chansw_ie *ext_chansw_ie; | ||
1181 | const u8 *country_elem; | 1182 | const u8 *country_elem; |
1182 | const u8 *pwr_constr_elem; | 1183 | const u8 *pwr_constr_elem; |
1183 | const struct ieee80211_timeout_interval_ie *timeout_int; | 1184 | const struct ieee80211_timeout_interval_ie *timeout_int; |
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index ade3cd6c337d..bc6f87edc624 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -1024,56 +1024,79 @@ static void | |||
1024 | ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | 1024 | ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, |
1025 | u64 timestamp, struct ieee802_11_elems *elems) | 1025 | u64 timestamp, struct ieee802_11_elems *elems) |
1026 | { | 1026 | { |
1027 | struct ieee80211_local *local = sdata->local; | ||
1027 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 1028 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
1028 | struct cfg80211_bss *cbss = ifmgd->associated; | 1029 | struct cfg80211_bss *cbss = ifmgd->associated; |
1029 | struct ieee80211_bss *bss; | 1030 | struct ieee80211_bss *bss; |
1030 | struct ieee80211_channel *new_ch; | 1031 | struct ieee80211_channel *new_ch; |
1031 | int new_freq; | ||
1032 | struct ieee80211_chanctx *chanctx; | 1032 | struct ieee80211_chanctx *chanctx; |
1033 | enum ieee80211_band new_band; | ||
1034 | int new_freq; | ||
1035 | u8 new_chan_no; | ||
1036 | u8 count; | ||
1037 | u8 mode; | ||
1033 | 1038 | ||
1034 | ASSERT_MGD_MTX(ifmgd); | 1039 | ASSERT_MGD_MTX(ifmgd); |
1035 | 1040 | ||
1036 | if (!cbss) | 1041 | if (!cbss) |
1037 | return; | 1042 | return; |
1038 | 1043 | ||
1039 | if (sdata->local->scanning) | 1044 | if (local->scanning) |
1040 | return; | 1045 | return; |
1041 | 1046 | ||
1042 | /* disregard subsequent announcements if we are already processing */ | 1047 | /* disregard subsequent announcements if we are already processing */ |
1043 | if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED) | 1048 | if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED) |
1044 | return; | 1049 | return; |
1045 | 1050 | ||
1046 | if (!elems->ch_switch_ie) | 1051 | if (elems->ext_chansw_ie) { |
1052 | if (!ieee80211_operating_class_to_band( | ||
1053 | elems->ext_chansw_ie->new_operating_class, | ||
1054 | &new_band)) { | ||
1055 | sdata_info(sdata, | ||
1056 | "cannot understand ECSA IE operating class %d, disconnecting\n", | ||
1057 | elems->ext_chansw_ie->new_operating_class); | ||
1058 | ieee80211_queue_work(&local->hw, | ||
1059 | &ifmgd->csa_connection_drop_work); | ||
1060 | } | ||
1061 | new_chan_no = elems->ext_chansw_ie->new_ch_num; | ||
1062 | count = elems->ext_chansw_ie->count; | ||
1063 | mode = elems->ext_chansw_ie->mode; | ||
1064 | } else if (elems->ch_switch_ie) { | ||
1065 | new_band = cbss->channel->band; | ||
1066 | new_chan_no = elems->ch_switch_ie->new_ch_num; | ||
1067 | count = elems->ch_switch_ie->count; | ||
1068 | mode = elems->ch_switch_ie->mode; | ||
1069 | } else { | ||
1070 | /* nothing here we understand */ | ||
1047 | return; | 1071 | return; |
1072 | } | ||
1048 | 1073 | ||
1049 | bss = (void *)cbss->priv; | 1074 | bss = (void *)cbss->priv; |
1050 | 1075 | ||
1051 | new_freq = ieee80211_channel_to_frequency( | 1076 | new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band); |
1052 | elems->ch_switch_ie->new_ch_num, | 1077 | new_ch = ieee80211_get_channel(local->hw.wiphy, new_freq); |
1053 | cbss->channel->band); | ||
1054 | new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); | ||
1055 | if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) { | 1078 | if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) { |
1056 | sdata_info(sdata, | 1079 | sdata_info(sdata, |
1057 | "AP %pM switches to unsupported channel (%d MHz), disconnecting\n", | 1080 | "AP %pM switches to unsupported channel (%d MHz), disconnecting\n", |
1058 | ifmgd->associated->bssid, new_freq); | 1081 | ifmgd->associated->bssid, new_freq); |
1059 | ieee80211_queue_work(&sdata->local->hw, | 1082 | ieee80211_queue_work(&local->hw, |
1060 | &ifmgd->csa_connection_drop_work); | 1083 | &ifmgd->csa_connection_drop_work); |
1061 | return; | 1084 | return; |
1062 | } | 1085 | } |
1063 | 1086 | ||
1064 | ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; | 1087 | ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; |
1065 | 1088 | ||
1066 | if (sdata->local->use_chanctx) { | 1089 | if (local->use_chanctx) { |
1067 | sdata_info(sdata, | 1090 | sdata_info(sdata, |
1068 | "not handling channel switch with channel contexts\n"); | 1091 | "not handling channel switch with channel contexts\n"); |
1069 | ieee80211_queue_work(&sdata->local->hw, | 1092 | ieee80211_queue_work(&local->hw, |
1070 | &ifmgd->csa_connection_drop_work); | 1093 | &ifmgd->csa_connection_drop_work); |
1071 | return; | 1094 | return; |
1072 | } | 1095 | } |
1073 | 1096 | ||
1074 | mutex_lock(&sdata->local->chanctx_mtx); | 1097 | mutex_lock(&local->chanctx_mtx); |
1075 | if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) { | 1098 | if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) { |
1076 | mutex_unlock(&sdata->local->chanctx_mtx); | 1099 | mutex_unlock(&local->chanctx_mtx); |
1077 | return; | 1100 | return; |
1078 | } | 1101 | } |
1079 | chanctx = container_of(rcu_access_pointer(sdata->vif.chanctx_conf), | 1102 | chanctx = container_of(rcu_access_pointer(sdata->vif.chanctx_conf), |
@@ -1081,40 +1104,39 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
1081 | if (chanctx->refcount > 1) { | 1104 | if (chanctx->refcount > 1) { |
1082 | sdata_info(sdata, | 1105 | sdata_info(sdata, |
1083 | "channel switch with multiple interfaces on the same channel, disconnecting\n"); | 1106 | "channel switch with multiple interfaces on the same channel, disconnecting\n"); |
1084 | ieee80211_queue_work(&sdata->local->hw, | 1107 | ieee80211_queue_work(&local->hw, |
1085 | &ifmgd->csa_connection_drop_work); | 1108 | &ifmgd->csa_connection_drop_work); |
1086 | mutex_unlock(&sdata->local->chanctx_mtx); | 1109 | mutex_unlock(&local->chanctx_mtx); |
1087 | return; | 1110 | return; |
1088 | } | 1111 | } |
1089 | mutex_unlock(&sdata->local->chanctx_mtx); | 1112 | mutex_unlock(&local->chanctx_mtx); |
1090 | 1113 | ||
1091 | sdata->local->csa_channel = new_ch; | 1114 | local->csa_channel = new_ch; |
1092 | 1115 | ||
1093 | if (elems->ch_switch_ie->mode) | 1116 | if (mode) |
1094 | ieee80211_stop_queues_by_reason(&sdata->local->hw, | 1117 | ieee80211_stop_queues_by_reason(&local->hw, |
1095 | IEEE80211_MAX_QUEUE_MAP, | 1118 | IEEE80211_MAX_QUEUE_MAP, |
1096 | IEEE80211_QUEUE_STOP_REASON_CSA); | 1119 | IEEE80211_QUEUE_STOP_REASON_CSA); |
1097 | 1120 | ||
1098 | if (sdata->local->ops->channel_switch) { | 1121 | if (local->ops->channel_switch) { |
1099 | /* use driver's channel switch callback */ | 1122 | /* use driver's channel switch callback */ |
1100 | struct ieee80211_channel_switch ch_switch = { | 1123 | struct ieee80211_channel_switch ch_switch = { |
1101 | .timestamp = timestamp, | 1124 | .timestamp = timestamp, |
1102 | .block_tx = elems->ch_switch_ie->mode, | 1125 | .block_tx = mode, |
1103 | .channel = new_ch, | 1126 | .channel = new_ch, |
1104 | .count = elems->ch_switch_ie->count, | 1127 | .count = count, |
1105 | }; | 1128 | }; |
1106 | 1129 | ||
1107 | drv_channel_switch(sdata->local, &ch_switch); | 1130 | drv_channel_switch(local, &ch_switch); |
1108 | return; | 1131 | return; |
1109 | } | 1132 | } |
1110 | 1133 | ||
1111 | /* channel switch handled in software */ | 1134 | /* channel switch handled in software */ |
1112 | if (elems->ch_switch_ie->count <= 1) | 1135 | if (count <= 1) |
1113 | ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); | 1136 | ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work); |
1114 | else | 1137 | else |
1115 | mod_timer(&ifmgd->chswitch_timer, | 1138 | mod_timer(&ifmgd->chswitch_timer, |
1116 | TU_TO_EXP_TIME(elems->ch_switch_ie->count * | 1139 | TU_TO_EXP_TIME(count * cbss->beacon_interval)); |
1117 | cbss->beacon_interval)); | ||
1118 | } | 1140 | } |
1119 | 1141 | ||
1120 | static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, | 1142 | static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, |
@@ -2629,6 +2651,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, | |||
2629 | struct ieee80211_channel *channel; | 2651 | struct ieee80211_channel *channel; |
2630 | bool need_ps = false; | 2652 | bool need_ps = false; |
2631 | 2653 | ||
2654 | lockdep_assert_held(&sdata->u.mgd.mtx); | ||
2655 | |||
2632 | if ((sdata->u.mgd.associated && | 2656 | if ((sdata->u.mgd.associated && |
2633 | ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) || | 2657 | ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) || |
2634 | (sdata->u.mgd.assoc_data && | 2658 | (sdata->u.mgd.assoc_data && |
@@ -2670,6 +2694,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, | |||
2670 | } | 2694 | } |
2671 | 2695 | ||
2672 | ieee80211_sta_process_chanswitch(sdata, rx_status->mactime, elems); | 2696 | ieee80211_sta_process_chanswitch(sdata, rx_status->mactime, elems); |
2697 | |||
2673 | } | 2698 | } |
2674 | 2699 | ||
2675 | 2700 | ||
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 1d6217ac3ba3..e4a6d559372d 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
@@ -863,6 +863,13 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, | |||
863 | } | 863 | } |
864 | elems->ch_switch_ie = (void *)pos; | 864 | elems->ch_switch_ie = (void *)pos; |
865 | break; | 865 | break; |
866 | case WLAN_EID_EXT_CHANSWITCH_ANN: | ||
867 | if (elen != sizeof(struct ieee80211_ext_chansw_ie)) { | ||
868 | elem_parse_failed = true; | ||
869 | break; | ||
870 | } | ||
871 | elems->ext_chansw_ie = (void *)pos; | ||
872 | break; | ||
866 | case WLAN_EID_COUNTRY: | 873 | case WLAN_EID_COUNTRY: |
867 | elems->country_elem = pos; | 874 | elems->country_elem = pos; |
868 | elems->country_elem_len = elen; | 875 | elems->country_elem_len = elen; |