@@ -291,6 +291,60 @@ static void ieee80211_tdls_add_wmm_param_ie(struct ieee80211_sub_if_data *sdata,
291291 }
292292}
293293
294+ static void
295+ ieee80211_tdls_chandef_vht_upgrade (struct ieee80211_sub_if_data * sdata ,
296+ struct sta_info * sta )
297+ {
298+ /* IEEE802.11ac-2013 Table E-4 */
299+ u16 centers_80mhz [] = { 5210 , 5290 , 5530 , 5610 , 5690 , 5775 };
300+ struct cfg80211_chan_def uc = sta -> tdls_chandef ;
301+ enum nl80211_chan_width max_width = ieee80211_get_sta_bw (& sta -> sta );
302+ int i ;
303+
304+ /* only support upgrading non-narrow channels up to 80Mhz */
305+ if (max_width == NL80211_CHAN_WIDTH_5 ||
306+ max_width == NL80211_CHAN_WIDTH_10 )
307+ return ;
308+
309+ if (max_width > NL80211_CHAN_WIDTH_80 )
310+ max_width = NL80211_CHAN_WIDTH_80 ;
311+
312+ if (uc .width == max_width )
313+ return ;
314+ /*
315+ * Channel usage constrains in the IEEE802.11ac-2013 specification only
316+ * allow expanding a 20MHz channel to 80MHz in a single way. In
317+ * addition, there are no 40MHz allowed channels that are not part of
318+ * the allowed 80MHz range in the 5GHz spectrum (the relevant one here).
319+ */
320+ for (i = 0 ; i < ARRAY_SIZE (centers_80mhz ); i ++ )
321+ if (abs (uc .chan -> center_freq - centers_80mhz [i ]) <= 30 ) {
322+ uc .center_freq1 = centers_80mhz [i ];
323+ uc .width = NL80211_CHAN_WIDTH_80 ;
324+ break ;
325+ }
326+
327+ if (!uc .center_freq1 )
328+ return ;
329+
330+ /* proceed to downgrade the chandef until usable or the same */
331+ while (uc .width > max_width &&
332+ !cfg80211_reg_can_beacon (sdata -> local -> hw .wiphy ,
333+ & uc , sdata -> wdev .iftype ))
334+ ieee80211_chandef_downgrade (& uc );
335+
336+ if (!cfg80211_chandef_identical (& uc , & sta -> tdls_chandef )) {
337+ tdls_dbg (sdata , "TDLS ch width upgraded %d -> %d\n" ,
338+ sta -> tdls_chandef .width , uc .width );
339+
340+ /*
341+ * the station is not yet authorized when BW upgrade is done,
342+ * locking is not required
343+ */
344+ sta -> tdls_chandef = uc ;
345+ }
346+ }
347+
294348static void
295349ieee80211_tdls_add_setup_start_ies (struct ieee80211_sub_if_data * sdata ,
296350 struct sk_buff * skb , const u8 * peer ,
@@ -358,15 +412,17 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
358412 offset = noffset ;
359413 }
360414
361- rcu_read_lock ( );
415+ mutex_lock ( & local -> sta_mtx );
362416
363417 /* we should have the peer STA if we're already responding */
364418 if (action_code == WLAN_TDLS_SETUP_RESPONSE ) {
365419 sta = sta_info_get (sdata , peer );
366420 if (WARN_ON_ONCE (!sta )) {
367- rcu_read_unlock ( );
421+ mutex_unlock ( & local -> sta_mtx );
368422 return ;
369423 }
424+
425+ sta -> tdls_chandef = sdata -> vif .bss_conf .chandef ;
370426 }
371427
372428 ieee80211_tdls_add_oper_classes (sdata , skb );
@@ -456,9 +512,16 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
456512
457513 pos = skb_put (skb , sizeof (struct ieee80211_vht_cap ) + 2 );
458514 ieee80211_ie_build_vht_cap (pos , & vht_cap , vht_cap .cap );
515+
516+ /*
517+ * if both peers support WIDER_BW, we can expand the chandef to
518+ * a wider compatible one, up to 80MHz
519+ */
520+ if (test_sta_flag (sta , WLAN_STA_TDLS_WIDER_BW ))
521+ ieee80211_tdls_chandef_vht_upgrade (sdata , sta );
459522 }
460523
461- rcu_read_unlock ( );
524+ mutex_unlock ( & local -> sta_mtx );
462525
463526 /* add any remaining IEs */
464527 if (extra_ies_len ) {
@@ -482,15 +545,17 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
482545 enum ieee80211_band band = ieee80211_get_sdata_band (sdata );
483546 u8 * pos ;
484547
485- rcu_read_lock ( );
548+ mutex_lock ( & local -> sta_mtx );
486549
487550 sta = sta_info_get (sdata , peer );
488551 ap_sta = sta_info_get (sdata , ifmgd -> bssid );
489552 if (WARN_ON_ONCE (!sta || !ap_sta )) {
490- rcu_read_unlock ( );
553+ mutex_unlock ( & local -> sta_mtx );
491554 return ;
492555 }
493556
557+ sta -> tdls_chandef = sdata -> vif .bss_conf .chandef ;
558+
494559 /* add any custom IEs that go before the QoS IE */
495560 if (extra_ies_len ) {
496561 static const u8 before_qos [] = {
@@ -538,12 +603,19 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
538603
539604 /* only include VHT-operation if not on the 2.4GHz band */
540605 if (band != IEEE80211_BAND_2GHZ && sta -> sta .vht_cap .vht_supported ) {
606+ /*
607+ * if both peers support WIDER_BW, we can expand the chandef to
608+ * a wider compatible one, up to 80MHz
609+ */
610+ if (test_sta_flag (sta , WLAN_STA_TDLS_WIDER_BW ))
611+ ieee80211_tdls_chandef_vht_upgrade (sdata , sta );
612+
541613 pos = skb_put (skb , 2 + sizeof (struct ieee80211_vht_operation ));
542614 ieee80211_ie_build_vht_oper (pos , & sta -> sta .vht_cap ,
543- & sdata -> vif . bss_conf . chandef );
615+ & sta -> tdls_chandef );
544616 }
545617
546- rcu_read_unlock ( );
618+ mutex_unlock ( & local -> sta_mtx );
547619
548620 /* add any remaining IEs */
549621 if (extra_ies_len ) {
@@ -1154,6 +1226,22 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
11541226 return ret ;
11551227}
11561228
1229+ static void iee80211_tdls_recalc_chanctx (struct ieee80211_sub_if_data * sdata )
1230+ {
1231+ struct ieee80211_local * local = sdata -> local ;
1232+ struct ieee80211_chanctx_conf * conf ;
1233+ struct ieee80211_chanctx * ctx ;
1234+
1235+ mutex_lock (& local -> chanctx_mtx );
1236+ conf = rcu_dereference_protected (sdata -> vif .chanctx_conf ,
1237+ lockdep_is_held (& local -> chanctx_mtx ));
1238+ if (conf ) {
1239+ ctx = container_of (conf , struct ieee80211_chanctx , conf );
1240+ ieee80211_recalc_chanctx_chantype (local , ctx );
1241+ }
1242+ mutex_unlock (& local -> chanctx_mtx );
1243+ }
1244+
11571245int ieee80211_tdls_oper (struct wiphy * wiphy , struct net_device * dev ,
11581246 const u8 * peer , enum nl80211_tdls_operation oper )
11591247{
@@ -1190,6 +1278,8 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
11901278 break ;
11911279 }
11921280
1281+ iee80211_tdls_recalc_chanctx (sdata );
1282+
11931283 rcu_read_lock ();
11941284 sta = sta_info_get (sdata , peer );
11951285 if (!sta ) {
@@ -1221,6 +1311,7 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
12211311 ieee80211_flush_queues (local , sdata , false);
12221312
12231313 ret = sta_info_destroy_addr (sdata , peer );
1314+ iee80211_tdls_recalc_chanctx (sdata );
12241315 break ;
12251316 default :
12261317 ret = - ENOTSUPP ;
0 commit comments