aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-03-26 09:13:58 -0400
committerJohannes Berg <johannes.berg@intel.com>2013-04-16 09:29:43 -0400
commitb4f286a1c0ad0b84c2d502b354d4d98d5a86c64b (patch)
treef2cb487aaab163dff52c0588c1a80b1aaa951a89
parent1ce3e82b0eb472161313183be0033e46d5c4bbaf (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.h12
-rw-r--r--net/mac80211/ieee80211_i.h1
-rw-r--r--net/mac80211/mlme.c77
-rw-r--r--net/mac80211/util.c7
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 */
680struct 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
1024ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, 1024ieee80211_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
1120static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, 1142static 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;