aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArik Nemtsov <arik@wizery.com>2014-11-09 11:50:19 -0500
committerJohannes Berg <johannes.berg@intel.com>2014-11-19 12:45:21 -0500
commita7a6bdd0670feb8bfc26d41cda32b6064dbca50e (patch)
treed343591f508af2d1a15f5b265e7ddaf182e34129
parent53837584438f8899e061ada4663ae1d09b49b96a (diff)
mac80211: introduce TDLS channel switch ops
Implement the cfg80211 TDLS channel switch ops and introduce new mac80211 ones for low-level drivers. Verify low-level driver support for the new ops when using the relevant wiphy feature bit. Also verify the peer supports channel switching before passing the command down. Add a new STA flag to track the off-channel state with the TDLS peer and make sure to cancel the channel-switch if the peer STA is unexpectedly removed. Signed-off-by: Arik Nemtsov <arikx.nemtsov@intel.com> Signed-off-by: Arik Nemtsov <arik@wizery.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r--include/net/mac80211.h19
-rw-r--r--net/mac80211/cfg.c2
-rw-r--r--net/mac80211/debugfs_sta.c10
-rw-r--r--net/mac80211/driver-ops.h41
-rw-r--r--net/mac80211/ieee80211_i.h6
-rw-r--r--net/mac80211/main.c5
-rw-r--r--net/mac80211/sta_info.c9
-rw-r--r--net/mac80211/sta_info.h3
-rw-r--r--net/mac80211/tdls.c234
-rw-r--r--net/mac80211/trace.h57
10 files changed, 381 insertions, 5 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 83232aa2f077..fdedceb7adcb 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -2915,6 +2915,16 @@ enum ieee80211_reconfig_type {
2915 * 2915 *
2916 * @get_txpower: get current maximum tx power (in dBm) based on configuration 2916 * @get_txpower: get current maximum tx power (in dBm) based on configuration
2917 * and hardware limits. 2917 * and hardware limits.
2918 *
2919 * @tdls_channel_switch: Start channel-switching with a TDLS peer. The driver
2920 * is responsible for continually initiating channel-switching operations
2921 * and returning to the base channel for communication with the AP. The
2922 * driver receives a channel-switch request template and the location of
2923 * the switch-timing IE within the template as part of the invocation.
2924 * The template is valid only within the call, and the driver can
2925 * optionally copy the skb for further re-use.
2926 * @tdls_cancel_channel_switch: Stop channel-switching with a TDLS peer. Both
2927 * peers must be on the base channel when the call completes.
2918 */ 2928 */
2919struct ieee80211_ops { 2929struct ieee80211_ops {
2920 void (*tx)(struct ieee80211_hw *hw, 2930 void (*tx)(struct ieee80211_hw *hw,
@@ -3126,6 +3136,15 @@ struct ieee80211_ops {
3126 u32 (*get_expected_throughput)(struct ieee80211_sta *sta); 3136 u32 (*get_expected_throughput)(struct ieee80211_sta *sta);
3127 int (*get_txpower)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 3137 int (*get_txpower)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
3128 int *dbm); 3138 int *dbm);
3139
3140 int (*tdls_channel_switch)(struct ieee80211_hw *hw,
3141 struct ieee80211_vif *vif,
3142 struct ieee80211_sta *sta, u8 oper_class,
3143 struct cfg80211_chan_def *chandef,
3144 struct sk_buff *skb, u32 ch_sw_tm_ie);
3145 void (*tdls_cancel_channel_switch)(struct ieee80211_hw *hw,
3146 struct ieee80211_vif *vif,
3147 struct ieee80211_sta *sta);
3129}; 3148};
3130 3149
3131/** 3150/**
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 8195e65d8a91..e75d5c53e97b 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -3752,6 +3752,8 @@ const struct cfg80211_ops mac80211_config_ops = {
3752 .set_rekey_data = ieee80211_set_rekey_data, 3752 .set_rekey_data = ieee80211_set_rekey_data,
3753 .tdls_oper = ieee80211_tdls_oper, 3753 .tdls_oper = ieee80211_tdls_oper,
3754 .tdls_mgmt = ieee80211_tdls_mgmt, 3754 .tdls_mgmt = ieee80211_tdls_mgmt,
3755 .tdls_channel_switch = ieee80211_tdls_channel_switch,
3756 .tdls_cancel_channel_switch = ieee80211_tdls_cancel_channel_switch,
3755 .probe_client = ieee80211_probe_client, 3757 .probe_client = ieee80211_probe_client,
3756 .set_noack_map = ieee80211_set_noack_map, 3758 .set_noack_map = ieee80211_set_noack_map,
3757#ifdef CONFIG_PM 3759#ifdef CONFIG_PM
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 2ba7f53746da..94c70091bbd7 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -74,7 +74,7 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
74 test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : "" 74 test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : ""
75 75
76 int res = scnprintf(buf, sizeof(buf), 76 int res = scnprintf(buf, sizeof(buf),
77 "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", 77 "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
78 TEST(AUTH), TEST(ASSOC), TEST(PS_STA), 78 TEST(AUTH), TEST(ASSOC), TEST(PS_STA),
79 TEST(PS_DRIVER), TEST(AUTHORIZED), 79 TEST(PS_DRIVER), TEST(AUTHORIZED),
80 TEST(SHORT_PREAMBLE), 80 TEST(SHORT_PREAMBLE),
@@ -83,10 +83,10 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
83 TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL), 83 TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL),
84 TEST(UAPSD), TEST(SP), TEST(TDLS_PEER), 84 TEST(UAPSD), TEST(SP), TEST(TDLS_PEER),
85 TEST(TDLS_PEER_AUTH), TEST(TDLS_INITIATOR), 85 TEST(TDLS_PEER_AUTH), TEST(TDLS_INITIATOR),
86 TEST(TDLS_CHAN_SWITCH), TEST(4ADDR_EVENT), 86 TEST(TDLS_CHAN_SWITCH), TEST(TDLS_OFF_CHANNEL),
87 TEST(INSERTED), TEST(RATE_CONTROL), 87 TEST(4ADDR_EVENT), TEST(INSERTED),
88 TEST(TOFFSET_KNOWN), TEST(MPSP_OWNER), 88 TEST(RATE_CONTROL), TEST(TOFFSET_KNOWN),
89 TEST(MPSP_RECIPIENT)); 89 TEST(MPSP_OWNER), TEST(MPSP_RECIPIENT));
90#undef TEST 90#undef TEST
91 return simple_read_from_buffer(userbuf, count, ppos, buf, res); 91 return simple_read_from_buffer(userbuf, count, ppos, buf, res);
92} 92}
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 9759dd1f0734..ec4ae42ac15f 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -1296,4 +1296,45 @@ static inline int drv_get_txpower(struct ieee80211_local *local,
1296 return ret; 1296 return ret;
1297} 1297}
1298 1298
1299static inline int
1300drv_tdls_channel_switch(struct ieee80211_local *local,
1301 struct ieee80211_sub_if_data *sdata,
1302 struct ieee80211_sta *sta, u8 oper_class,
1303 struct cfg80211_chan_def *chandef,
1304 struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie)
1305{
1306 int ret;
1307
1308 might_sleep();
1309 if (!check_sdata_in_driver(sdata))
1310 return -EIO;
1311
1312 if (!local->ops->tdls_channel_switch)
1313 return -EOPNOTSUPP;
1314
1315 trace_drv_tdls_channel_switch(local, sdata, sta, oper_class, chandef);
1316 ret = local->ops->tdls_channel_switch(&local->hw, &sdata->vif, sta,
1317 oper_class, chandef, tmpl_skb,
1318 ch_sw_tm_ie);
1319 trace_drv_return_int(local, ret);
1320 return ret;
1321}
1322
1323static inline void
1324drv_tdls_cancel_channel_switch(struct ieee80211_local *local,
1325 struct ieee80211_sub_if_data *sdata,
1326 struct ieee80211_sta *sta)
1327{
1328 might_sleep();
1329 if (!check_sdata_in_driver(sdata))
1330 return;
1331
1332 if (!local->ops->tdls_cancel_channel_switch)
1333 return;
1334
1335 trace_drv_tdls_cancel_channel_switch(local, sdata, sta);
1336 local->ops->tdls_cancel_channel_switch(&local->hw, &sdata->vif, sta);
1337 trace_drv_return_void(local);
1338}
1339
1299#endif /* __MAC80211_DRIVER_OPS */ 1340#endif /* __MAC80211_DRIVER_OPS */
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index e786ab6bc72c..2c7abc077b6b 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -2007,6 +2007,12 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
2007int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, 2007int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
2008 const u8 *peer, enum nl80211_tdls_operation oper); 2008 const u8 *peer, enum nl80211_tdls_operation oper);
2009void ieee80211_tdls_peer_del_work(struct work_struct *wk); 2009void ieee80211_tdls_peer_del_work(struct work_struct *wk);
2010int ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev,
2011 const u8 *addr, u8 oper_class,
2012 struct cfg80211_chan_def *chandef);
2013void ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
2014 struct net_device *dev,
2015 const u8 *addr);
2010 2016
2011extern const struct ethtool_ops ieee80211_ethtool_ops; 2017extern const struct ethtool_ops ieee80211_ethtool_ops;
2012 2018
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 282a4f36eb92..774ccb2d9a76 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -764,6 +764,11 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
764 local->hw.offchannel_tx_hw_queue >= local->hw.queues)) 764 local->hw.offchannel_tx_hw_queue >= local->hw.queues))
765 return -EINVAL; 765 return -EINVAL;
766 766
767 if ((hw->wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) &&
768 (!local->ops->tdls_channel_switch ||
769 !local->ops->tdls_cancel_channel_switch))
770 return -EOPNOTSUPP;
771
767#ifdef CONFIG_PM 772#ifdef CONFIG_PM
768 if (hw->wiphy->wowlan && (!local->ops->suspend || !local->ops->resume)) 773 if (hw->wiphy->wowlan && (!local->ops->suspend || !local->ops->resume))
769 return -EINVAL; 774 return -EINVAL;
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 97372514f287..86ca62765699 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -847,6 +847,15 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta)
847 if (WARN_ON(ret)) 847 if (WARN_ON(ret))
848 return ret; 848 return ret;
849 849
850 /*
851 * for TDLS peers, make sure to return to the base channel before
852 * removal.
853 */
854 if (test_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL)) {
855 drv_tdls_cancel_channel_switch(local, sdata, &sta->sta);
856 clear_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL);
857 }
858
850 list_del_rcu(&sta->list); 859 list_del_rcu(&sta->list);
851 860
852 drv_sta_pre_rcu_remove(local, sta->sdata, sta); 861 drv_sta_pre_rcu_remove(local, sta->sdata, sta);
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index b6702c810ad3..00f56eb72c60 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -50,6 +50,8 @@
50 * @WLAN_STA_TDLS_INITIATOR: We are the initiator of the TDLS link with this 50 * @WLAN_STA_TDLS_INITIATOR: We are the initiator of the TDLS link with this
51 * station. 51 * station.
52 * @WLAN_STA_TDLS_CHAN_SWITCH: This TDLS peer supports TDLS channel-switching 52 * @WLAN_STA_TDLS_CHAN_SWITCH: This TDLS peer supports TDLS channel-switching
53 * @WLAN_STA_TDLS_OFF_CHANNEL: The local STA is currently off-channel with this
54 * TDLS peer
53 * @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was 55 * @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was
54 * keeping station in power-save mode, reply when the driver 56 * keeping station in power-save mode, reply when the driver
55 * unblocks the station. 57 * unblocks the station.
@@ -80,6 +82,7 @@ enum ieee80211_sta_info_flags {
80 WLAN_STA_TDLS_PEER_AUTH, 82 WLAN_STA_TDLS_PEER_AUTH,
81 WLAN_STA_TDLS_INITIATOR, 83 WLAN_STA_TDLS_INITIATOR,
82 WLAN_STA_TDLS_CHAN_SWITCH, 84 WLAN_STA_TDLS_CHAN_SWITCH,
85 WLAN_STA_TDLS_OFF_CHANNEL,
83 WLAN_STA_UAPSD, 86 WLAN_STA_UAPSD,
84 WLAN_STA_SP, 87 WLAN_STA_SP,
85 WLAN_STA_4ADDR_EVENT, 88 WLAN_STA_4ADDR_EVENT,
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index fa141aecd986..358f9a4512ad 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -449,6 +449,48 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
449 ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); 449 ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
450} 450}
451 451
452static void
453ieee80211_tdls_add_chan_switch_req_ies(struct ieee80211_sub_if_data *sdata,
454 struct sk_buff *skb, const u8 *peer,
455 bool initiator, const u8 *extra_ies,
456 size_t extra_ies_len, u8 oper_class,
457 struct cfg80211_chan_def *chandef)
458{
459 struct ieee80211_tdls_data *tf;
460 size_t offset = 0, noffset;
461 u8 *pos;
462
463 if (WARN_ON_ONCE(!chandef))
464 return;
465
466 tf = (void *)skb->data;
467 tf->u.chan_switch_req.target_channel =
468 ieee80211_frequency_to_channel(chandef->chan->center_freq);
469 tf->u.chan_switch_req.oper_class = oper_class;
470
471 if (extra_ies_len) {
472 static const u8 before_lnkie[] = {
473 WLAN_EID_SECONDARY_CHANNEL_OFFSET,
474 };
475 noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
476 before_lnkie,
477 ARRAY_SIZE(before_lnkie),
478 offset);
479 pos = skb_put(skb, noffset - offset);
480 memcpy(pos, extra_ies + offset, noffset - offset);
481 offset = noffset;
482 }
483
484 ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
485
486 /* add any remaining IEs */
487 if (extra_ies_len) {
488 noffset = extra_ies_len;
489 pos = skb_put(skb, noffset - offset);
490 memcpy(pos, extra_ies + offset, noffset - offset);
491 }
492}
493
452static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata, 494static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
453 struct sk_buff *skb, const u8 *peer, 495 struct sk_buff *skb, const u8 *peer,
454 u8 action_code, u16 status_code, 496 u8 action_code, u16 status_code,
@@ -481,6 +523,12 @@ static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
481 if (status_code == 0 || action_code == WLAN_TDLS_TEARDOWN) 523 if (status_code == 0 || action_code == WLAN_TDLS_TEARDOWN)
482 ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); 524 ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
483 break; 525 break;
526 case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
527 ieee80211_tdls_add_chan_switch_req_ies(sdata, skb, peer,
528 initiator, extra_ies,
529 extra_ies_len,
530 oper_class, chandef);
531 break;
484 } 532 }
485 533
486} 534}
@@ -547,6 +595,12 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
547 skb_put(skb, sizeof(tf->u.discover_req)); 595 skb_put(skb, sizeof(tf->u.discover_req));
548 tf->u.discover_req.dialog_token = dialog_token; 596 tf->u.discover_req.dialog_token = dialog_token;
549 break; 597 break;
598 case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
599 tf->category = WLAN_CATEGORY_TDLS;
600 tf->action_code = WLAN_TDLS_CHANNEL_SWITCH_REQUEST;
601
602 skb_put(skb, sizeof(tf->u.chan_switch_req));
603 break;
550 default: 604 default:
551 return -EINVAL; 605 return -EINVAL;
552 } 606 }
@@ -626,6 +680,7 @@ ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata,
626 case WLAN_TDLS_SETUP_CONFIRM: 680 case WLAN_TDLS_SETUP_CONFIRM:
627 case WLAN_TDLS_TEARDOWN: 681 case WLAN_TDLS_TEARDOWN:
628 case WLAN_TDLS_DISCOVERY_REQUEST: 682 case WLAN_TDLS_DISCOVERY_REQUEST:
683 case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
629 ret = ieee80211_prep_tdls_encap_data(local->hw.wiphy, 684 ret = ieee80211_prep_tdls_encap_data(local->hw.wiphy,
630 sdata->dev, peer, 685 sdata->dev, peer,
631 action_code, dialog_token, 686 action_code, dialog_token,
@@ -699,6 +754,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
699 initiator = false; 754 initiator = false;
700 break; 755 break;
701 case WLAN_TDLS_TEARDOWN: 756 case WLAN_TDLS_TEARDOWN:
757 case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
702 /* any value is ok */ 758 /* any value is ok */
703 break; 759 break;
704 default: 760 default:
@@ -1046,3 +1102,181 @@ void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer,
1046 cfg80211_tdls_oper_request(sdata->dev, peer, oper, reason_code, gfp); 1102 cfg80211_tdls_oper_request(sdata->dev, peer, oper, reason_code, gfp);
1047} 1103}
1048EXPORT_SYMBOL(ieee80211_tdls_oper_request); 1104EXPORT_SYMBOL(ieee80211_tdls_oper_request);
1105
1106static void
1107iee80211_tdls_add_ch_switch_timing(u8 *buf, u16 switch_time, u16 switch_timeout)
1108{
1109 struct ieee80211_ch_switch_timing *ch_sw;
1110
1111 *buf++ = WLAN_EID_CHAN_SWITCH_TIMING;
1112 *buf++ = sizeof(struct ieee80211_ch_switch_timing);
1113
1114 ch_sw = (void *)buf;
1115 ch_sw->switch_time = cpu_to_le16(switch_time);
1116 ch_sw->switch_timeout = cpu_to_le16(switch_timeout);
1117}
1118
1119/* find switch timing IE in SKB ready for Tx */
1120static const u8 *ieee80211_tdls_find_sw_timing_ie(struct sk_buff *skb)
1121{
1122 struct ieee80211_tdls_data *tf;
1123 const u8 *ie_start;
1124
1125 /*
1126 * Get the offset for the new location of the switch timing IE.
1127 * The SKB network header will now point to the "payload_type"
1128 * element of the TDLS data frame struct.
1129 */
1130 tf = container_of(skb->data + skb_network_offset(skb),
1131 struct ieee80211_tdls_data, payload_type);
1132 ie_start = tf->u.chan_switch_req.variable;
1133 return cfg80211_find_ie(WLAN_EID_CHAN_SWITCH_TIMING, ie_start,
1134 skb->len - (ie_start - skb->data));
1135}
1136
1137static struct sk_buff *
1138ieee80211_tdls_ch_sw_tmpl_get(struct sta_info *sta, u8 oper_class,
1139 struct cfg80211_chan_def *chandef,
1140 u32 *ch_sw_tm_ie_offset)
1141{
1142 struct ieee80211_sub_if_data *sdata = sta->sdata;
1143 u8 extra_ies[2 + sizeof(struct ieee80211_sec_chan_offs_ie) +
1144 2 + sizeof(struct ieee80211_ch_switch_timing)];
1145 int extra_ies_len = 2 + sizeof(struct ieee80211_ch_switch_timing);
1146 u8 *pos = extra_ies;
1147 struct sk_buff *skb;
1148
1149 /*
1150 * if chandef points to a wide channel add a Secondary-Channel
1151 * Offset information element
1152 */
1153 if (chandef->width == NL80211_CHAN_WIDTH_40) {
1154 struct ieee80211_sec_chan_offs_ie *sec_chan_ie;
1155 bool ht40plus;
1156
1157 *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;
1158 *pos++ = sizeof(*sec_chan_ie);
1159 sec_chan_ie = (void *)pos;
1160
1161 ht40plus = cfg80211_get_chandef_type(chandef) ==
1162 NL80211_CHAN_HT40PLUS;
1163 sec_chan_ie->sec_chan_offs = ht40plus ?
1164 IEEE80211_HT_PARAM_CHA_SEC_ABOVE :
1165 IEEE80211_HT_PARAM_CHA_SEC_BELOW;
1166 pos += sizeof(*sec_chan_ie);
1167
1168 extra_ies_len += 2 + sizeof(struct ieee80211_sec_chan_offs_ie);
1169 }
1170
1171 /* just set the values to 0, this is a template */
1172 iee80211_tdls_add_ch_switch_timing(pos, 0, 0);
1173
1174 skb = ieee80211_tdls_build_mgmt_packet_data(sdata, sta->sta.addr,
1175 WLAN_TDLS_CHANNEL_SWITCH_REQUEST,
1176 0, 0, !sta->sta.tdls_initiator,
1177 extra_ies, extra_ies_len,
1178 oper_class, chandef);
1179 if (!skb)
1180 return NULL;
1181
1182 skb = ieee80211_build_data_template(sdata, skb, 0);
1183 if (IS_ERR(skb)) {
1184 tdls_dbg(sdata, "Failed building TDLS channel switch frame\n");
1185 return NULL;
1186 }
1187
1188 if (ch_sw_tm_ie_offset) {
1189 const u8 *tm_ie = ieee80211_tdls_find_sw_timing_ie(skb);
1190
1191 if (!tm_ie) {
1192 tdls_dbg(sdata, "No switch timing IE in TDLS switch\n");
1193 dev_kfree_skb_any(skb);
1194 return NULL;
1195 }
1196
1197 *ch_sw_tm_ie_offset = tm_ie - skb->data;
1198 }
1199
1200 tdls_dbg(sdata,
1201 "TDLS channel switch request template for %pM ch %d width %d\n",
1202 sta->sta.addr, chandef->chan->center_freq, chandef->width);
1203 return skb;
1204}
1205
1206int
1207ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev,
1208 const u8 *addr, u8 oper_class,
1209 struct cfg80211_chan_def *chandef)
1210{
1211 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
1212 struct ieee80211_local *local = sdata->local;
1213 struct sta_info *sta;
1214 struct sk_buff *skb = NULL;
1215 u32 ch_sw_tm_ie;
1216 int ret;
1217
1218 mutex_lock(&local->sta_mtx);
1219 sta = sta_info_get(sdata, addr);
1220 if (!sta) {
1221 tdls_dbg(sdata,
1222 "Invalid TDLS peer %pM for channel switch request\n",
1223 addr);
1224 ret = -ENOENT;
1225 goto out;
1226 }
1227
1228 if (!test_sta_flag(sta, WLAN_STA_TDLS_CHAN_SWITCH)) {
1229 tdls_dbg(sdata, "TDLS channel switch unsupported by %pM\n",
1230 addr);
1231 ret = -ENOTSUPP;
1232 goto out;
1233 }
1234
1235 skb = ieee80211_tdls_ch_sw_tmpl_get(sta, oper_class, chandef,
1236 &ch_sw_tm_ie);
1237 if (!skb) {
1238 ret = -ENOENT;
1239 goto out;
1240 }
1241
1242 ret = drv_tdls_channel_switch(local, sdata, &sta->sta, oper_class,
1243 chandef, skb, ch_sw_tm_ie);
1244 if (!ret)
1245 set_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL);
1246
1247out:
1248 mutex_unlock(&local->sta_mtx);
1249 dev_kfree_skb_any(skb);
1250 return ret;
1251}
1252
1253void
1254ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
1255 struct net_device *dev,
1256 const u8 *addr)
1257{
1258 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
1259 struct ieee80211_local *local = sdata->local;
1260 struct sta_info *sta;
1261
1262 mutex_lock(&local->sta_mtx);
1263 sta = sta_info_get(sdata, addr);
1264 if (!sta) {
1265 tdls_dbg(sdata,
1266 "Invalid TDLS peer %pM for channel switch cancel\n",
1267 addr);
1268 goto out;
1269 }
1270
1271 if (!test_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL)) {
1272 tdls_dbg(sdata, "TDLS channel switch not initiated by %pM\n",
1273 addr);
1274 goto out;
1275 }
1276
1277 drv_tdls_cancel_channel_switch(local, sdata, &sta->sta);
1278 clear_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL);
1279
1280out:
1281 mutex_unlock(&local->sta_mtx);
1282}
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 96847e788488..c0c0fcace9d8 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -2196,6 +2196,63 @@ TRACE_EVENT(drv_get_txpower,
2196 ) 2196 )
2197); 2197);
2198 2198
2199TRACE_EVENT(drv_tdls_channel_switch,
2200 TP_PROTO(struct ieee80211_local *local,
2201 struct ieee80211_sub_if_data *sdata,
2202 struct ieee80211_sta *sta, u8 oper_class,
2203 struct cfg80211_chan_def *chandef),
2204
2205 TP_ARGS(local, sdata, sta, oper_class, chandef),
2206
2207 TP_STRUCT__entry(
2208 LOCAL_ENTRY
2209 VIF_ENTRY
2210 STA_ENTRY
2211 __field(u8, oper_class)
2212 CHANDEF_ENTRY
2213 ),
2214
2215 TP_fast_assign(
2216 LOCAL_ASSIGN;
2217 VIF_ASSIGN;
2218 STA_ASSIGN;
2219 __entry->oper_class = oper_class;
2220 CHANDEF_ASSIGN(chandef)
2221 ),
2222
2223 TP_printk(
2224 LOCAL_PR_FMT VIF_PR_FMT " tdls channel switch to"
2225 CHANDEF_PR_FMT " oper_class:%d " STA_PR_FMT,
2226 LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG, __entry->oper_class,
2227 STA_PR_ARG
2228 )
2229);
2230
2231TRACE_EVENT(drv_tdls_cancel_channel_switch,
2232 TP_PROTO(struct ieee80211_local *local,
2233 struct ieee80211_sub_if_data *sdata,
2234 struct ieee80211_sta *sta),
2235
2236 TP_ARGS(local, sdata, sta),
2237
2238 TP_STRUCT__entry(
2239 LOCAL_ENTRY
2240 VIF_ENTRY
2241 STA_ENTRY
2242 ),
2243
2244 TP_fast_assign(
2245 LOCAL_ASSIGN;
2246 VIF_ASSIGN;
2247 STA_ASSIGN;
2248 ),
2249
2250 TP_printk(
2251 LOCAL_PR_FMT VIF_PR_FMT
2252 " tdls cancel channel switch with " STA_PR_FMT,
2253 LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG
2254 )
2255);
2199 2256
2200#ifdef CONFIG_MAC80211_MESSAGE_TRACING 2257#ifdef CONFIG_MAC80211_MESSAGE_TRACING
2201#undef TRACE_SYSTEM 2258#undef TRACE_SYSTEM