2008-09-08 19:44:28 +04:00
/*
* HT handling
*
* Copyright 2003 , Jouni Malinen < jkmaline @ cc . hut . fi >
2008-09-08 19:44:29 +04:00
* Copyright 2002 - 2005 , Instant802 Networks , Inc .
* Copyright 2005 - 2006 , Devicescape Software , Inc .
2008-09-08 19:44:28 +04:00
* Copyright 2006 - 2007 Jiri Benc < jbenc @ suse . cz >
* Copyright 2007 , Michael Wu < flamingice @ sourmilk . net >
2010-06-10 12:21:48 +04:00
* Copyright 2007 - 2010 , Intel Corporation
2017-05-27 01:27:25 +03:00
* Copyright 2017 Intel Deutschland GmbH
2008-09-08 19:44:28 +04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/ieee80211.h>
2011-07-15 19:47:34 +04:00
# include <linux/export.h>
2008-09-08 19:44:28 +04:00
# include <net/mac80211.h>
# include "ieee80211_i.h"
2009-02-12 09:08:37 +03:00
# include "rate.h"
2008-09-08 19:44:28 +04:00
2013-06-28 12:39:59 +04:00
static void __check_htcap_disable ( struct ieee80211_ht_cap * ht_capa ,
struct ieee80211_ht_cap * ht_capa_mask ,
2011-11-24 23:15:21 +04:00
struct ieee80211_sta_ht_cap * ht_cap ,
u16 flag )
2011-11-18 23:32:00 +04:00
{
__le16 le_flag = cpu_to_le16 ( flag ) ;
2013-06-28 12:39:59 +04:00
if ( ht_capa_mask - > cap_info & le_flag ) {
if ( ! ( ht_capa - > cap_info & le_flag ) )
2011-11-18 23:32:00 +04:00
ht_cap - > cap & = ~ flag ;
}
}
2014-04-15 20:13:56 +04:00
static void __check_htcap_enable ( struct ieee80211_ht_cap * ht_capa ,
struct ieee80211_ht_cap * ht_capa_mask ,
struct ieee80211_sta_ht_cap * ht_cap ,
u16 flag )
{
__le16 le_flag = cpu_to_le16 ( flag ) ;
if ( ( ht_capa_mask - > cap_info & le_flag ) & &
( ht_capa - > cap_info & le_flag ) )
ht_cap - > cap | = flag ;
}
2011-11-18 23:32:00 +04:00
void ieee80211_apply_htcap_overrides ( struct ieee80211_sub_if_data * sdata ,
struct ieee80211_sta_ht_cap * ht_cap )
{
2013-06-28 12:39:59 +04:00
struct ieee80211_ht_cap * ht_capa , * ht_capa_mask ;
u8 * scaps , * smask ;
2011-11-18 23:32:00 +04:00
int i ;
2013-02-07 14:47:44 +04:00
if ( ! ht_cap - > ht_supported )
return ;
2013-06-28 12:39:59 +04:00
switch ( sdata - > vif . type ) {
case NL80211_IFTYPE_STATION :
ht_capa = & sdata - > u . mgd . ht_capa ;
ht_capa_mask = & sdata - > u . mgd . ht_capa_mask ;
break ;
case NL80211_IFTYPE_ADHOC :
ht_capa = & sdata - > u . ibss . ht_capa ;
ht_capa_mask = & sdata - > u . ibss . ht_capa_mask ;
break ;
default :
WARN_ON_ONCE ( 1 ) ;
return ;
}
scaps = ( u8 * ) ( & ht_capa - > mcs . rx_mask ) ;
smask = ( u8 * ) ( & ht_capa_mask - > mcs . rx_mask ) ;
2011-11-18 23:32:00 +04:00
/* NOTE: If you add more over-rides here, update register_hw
2014-04-15 20:13:56 +04:00
* ht_capa_mod_mask logic in main . c as well .
2011-11-18 23:32:00 +04:00
* And , if this method can ever change ht_cap . ht_supported , fix
* the check in ieee80211_add_ht_ie .
*/
/* check for HT over-rides, MCS rates first. */
for ( i = 0 ; i < IEEE80211_HT_MCS_MASK_LEN ; i + + ) {
u8 m = smask [ i ] ;
ht_cap - > mcs . rx_mask [ i ] & = ~ m ; /* turn off all masked bits */
/* Add back rates that are supported */
ht_cap - > mcs . rx_mask [ i ] | = ( m & scaps [ i ] ) ;
}
/* Force removal of HT-40 capabilities? */
2013-06-28 12:39:59 +04:00
__check_htcap_disable ( ht_capa , ht_capa_mask , ht_cap ,
IEEE80211_HT_CAP_SUP_WIDTH_20_40 ) ;
__check_htcap_disable ( ht_capa , ht_capa_mask , ht_cap ,
IEEE80211_HT_CAP_SGI_40 ) ;
2011-11-18 23:32:00 +04:00
2012-12-13 04:56:20 +04:00
/* Allow user to disable SGI-20 (SGI-40 is handled above) */
2013-06-28 12:39:59 +04:00
__check_htcap_disable ( ht_capa , ht_capa_mask , ht_cap ,
IEEE80211_HT_CAP_SGI_20 ) ;
2012-12-13 04:56:20 +04:00
2011-11-18 23:32:00 +04:00
/* Allow user to disable the max-AMSDU bit. */
2013-06-28 12:39:59 +04:00
__check_htcap_disable ( ht_capa , ht_capa_mask , ht_cap ,
IEEE80211_HT_CAP_MAX_AMSDU ) ;
2011-11-18 23:32:00 +04:00
2014-04-01 09:13:21 +04:00
/* Allow user to disable LDPC */
__check_htcap_disable ( ht_capa , ht_capa_mask , ht_cap ,
IEEE80211_HT_CAP_LDPC_CODING ) ;
2014-04-15 20:13:56 +04:00
/* Allow user to enable 40 MHz intolerant bit. */
__check_htcap_enable ( ht_capa , ht_capa_mask , ht_cap ,
IEEE80211_HT_CAP_40MHZ_INTOLERANT ) ;
2011-11-18 23:32:00 +04:00
/* Allow user to decrease AMPDU factor */
2013-06-28 12:39:59 +04:00
if ( ht_capa_mask - > ampdu_params_info &
2011-11-18 23:32:00 +04:00
IEEE80211_HT_AMPDU_PARM_FACTOR ) {
2013-06-28 12:39:59 +04:00
u8 n = ht_capa - > ampdu_params_info &
IEEE80211_HT_AMPDU_PARM_FACTOR ;
2011-11-18 23:32:00 +04:00
if ( n < ht_cap - > ampdu_factor )
ht_cap - > ampdu_factor = n ;
}
/* Allow the user to increase AMPDU density. */
2013-06-28 12:39:59 +04:00
if ( ht_capa_mask - > ampdu_params_info &
2011-11-18 23:32:00 +04:00
IEEE80211_HT_AMPDU_PARM_DENSITY ) {
2013-06-28 12:39:59 +04:00
u8 n = ( ht_capa - > ampdu_params_info &
2011-11-18 23:32:00 +04:00
IEEE80211_HT_AMPDU_PARM_DENSITY )
> > IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT ;
if ( n > ht_cap - > ampdu_density )
ht_cap - > ampdu_density = n ;
}
}
2013-02-07 14:47:44 +04:00
bool ieee80211_ht_cap_ie_to_sta_ht_cap ( struct ieee80211_sub_if_data * sdata ,
2011-11-18 23:32:00 +04:00
struct ieee80211_supported_band * sband ,
2013-02-12 19:43:19 +04:00
const struct ieee80211_ht_cap * ht_cap_ie ,
2013-02-07 14:47:44 +04:00
struct sta_info * sta )
2008-09-08 19:44:28 +04:00
{
2013-03-01 14:54:43 +04:00
struct ieee80211_sta_ht_cap ht_cap , own_cap ;
2008-10-14 18:58:37 +04:00
u8 ampdu_info , tx_mcs_set_cap ;
int i , max_tx_streams ;
2013-02-07 14:47:44 +04:00
bool changed ;
enum ieee80211_sta_rx_bandwidth bw ;
2013-02-12 17:21:00 +04:00
enum ieee80211_smps_mode smps_mode ;
2008-09-08 19:44:28 +04:00
2013-02-07 14:47:44 +04:00
memset ( & ht_cap , 0 , sizeof ( ht_cap ) ) ;
2008-09-08 19:44:28 +04:00
2010-07-16 15:01:24 +04:00
if ( ! ht_cap_ie | | ! sband - > ht_cap . ht_supported )
2013-02-07 14:47:44 +04:00
goto apply ;
2008-10-09 14:13:49 +04:00
2013-02-07 14:47:44 +04:00
ht_cap . ht_supported = true ;
2008-10-09 14:13:49 +04:00
2013-03-01 14:54:43 +04:00
own_cap = sband - > ht_cap ;
/*
* If user has specified capability over - rides , take care
2014-07-17 18:14:26 +04:00
* of that if the station we ' re setting up is the AP or TDLS peer that
2013-03-01 14:54:43 +04:00
* we advertised a restricted capability set to . Override
* our own capabilities and then use those below .
*/
2014-07-17 18:14:26 +04:00
if ( sdata - > vif . type = = NL80211_IFTYPE_STATION | |
sdata - > vif . type = = NL80211_IFTYPE_ADHOC )
2013-03-01 14:54:43 +04:00
ieee80211_apply_htcap_overrides ( sdata , & own_cap ) ;
2009-12-17 15:55:48 +03:00
/*
* The bits listed in this expression should be
* the same for the peer and us , if the station
* advertises more then we can ' t use those thus
* we mask them out .
*/
2013-02-07 14:47:44 +04:00
ht_cap . cap = le16_to_cpu ( ht_cap_ie - > cap_info ) &
2013-03-01 14:54:43 +04:00
( own_cap . cap | ~ ( IEEE80211_HT_CAP_LDPC_CODING |
IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_GRN_FLD |
IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_SGI_40 |
IEEE80211_HT_CAP_DSSSCCK40 ) ) ;
2012-12-28 18:01:57 +04:00
2009-12-17 15:55:48 +03:00
/*
* The STBC bits are asymmetric - - if we don ' t have
* TX then mask out the peer ' s RX and vice versa .
*/
2013-03-01 14:54:43 +04:00
if ( ! ( own_cap . cap & IEEE80211_HT_CAP_TX_STBC ) )
2013-02-07 14:47:44 +04:00
ht_cap . cap & = ~ IEEE80211_HT_CAP_RX_STBC ;
2013-03-01 14:54:43 +04:00
if ( ! ( own_cap . cap & IEEE80211_HT_CAP_RX_STBC ) )
2013-02-07 14:47:44 +04:00
ht_cap . cap & = ~ IEEE80211_HT_CAP_TX_STBC ;
2008-10-09 14:13:49 +04:00
2008-10-14 18:58:37 +04:00
ampdu_info = ht_cap_ie - > ampdu_params_info ;
2013-02-07 14:47:44 +04:00
ht_cap . ampdu_factor =
2008-10-14 18:58:37 +04:00
ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR ;
2013-02-07 14:47:44 +04:00
ht_cap . ampdu_density =
2008-10-14 18:58:37 +04:00
( ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY ) > > 2 ;
2008-09-08 19:44:28 +04:00
2008-10-09 14:13:49 +04:00
/* own MCS TX capabilities */
2013-03-01 14:54:43 +04:00
tx_mcs_set_cap = own_cap . mcs . tx_params ;
2008-10-09 14:13:49 +04:00
2011-02-25 14:24:10 +03:00
/* Copy peer MCS TX capabilities, the driver might need them. */
2013-02-07 14:47:44 +04:00
ht_cap . mcs . tx_params = ht_cap_ie - > mcs . tx_params ;
2011-02-25 14:24:10 +03:00
2008-10-09 14:13:49 +04:00
/* can we TX with MCS rates? */
if ( ! ( tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED ) )
2013-02-07 14:47:44 +04:00
goto apply ;
2008-10-09 14:13:49 +04:00
/* Counting from 0, therefore +1 */
if ( tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF )
max_tx_streams =
( ( tx_mcs_set_cap & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK )
> > IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT ) + 1 ;
else
max_tx_streams = IEEE80211_HT_MCS_TX_MAX_STREAMS ;
/*
2011-02-25 14:24:10 +03:00
* 802.11 n - 2009 20.3 .5 / 20.6 says :
2008-10-09 14:13:49 +04:00
* - indices 0 to 7 and 32 are single spatial stream
* - 8 to 31 are multiple spatial streams using equal modulation
* [ 8. .15 for two streams , 16. .23 for three and 24. .31 for four ]
* - remainder are multiple spatial streams using unequal modulation
*/
for ( i = 0 ; i < max_tx_streams ; i + + )
2013-02-07 14:47:44 +04:00
ht_cap . mcs . rx_mask [ i ] =
2013-03-01 14:54:43 +04:00
own_cap . mcs . rx_mask [ i ] & ht_cap_ie - > mcs . rx_mask [ i ] ;
2008-10-09 14:13:49 +04:00
if ( tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION )
for ( i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE ;
i < IEEE80211_HT_MCS_MASK_LEN ; i + + )
2013-02-07 14:47:44 +04:00
ht_cap . mcs . rx_mask [ i ] =
2013-03-01 14:54:43 +04:00
own_cap . mcs . rx_mask [ i ] &
2008-10-14 18:58:37 +04:00
ht_cap_ie - > mcs . rx_mask [ i ] ;
2008-10-09 14:13:49 +04:00
/* handle MCS rate 32 too */
2013-03-01 14:54:43 +04:00
if ( own_cap . mcs . rx_mask [ 32 / 8 ] & ht_cap_ie - > mcs . rx_mask [ 32 / 8 ] & 1 )
2013-02-07 14:47:44 +04:00
ht_cap . mcs . rx_mask [ 32 / 8 ] | = 1 ;
2011-11-18 23:32:00 +04:00
2014-07-17 18:14:27 +04:00
/* set Rx highest rate */
ht_cap . mcs . rx_highest = ht_cap_ie - > mcs . rx_highest ;
2015-12-13 16:41:05 +03:00
if ( ht_cap . cap & IEEE80211_HT_CAP_MAX_AMSDU )
sta - > sta . max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_7935 ;
else
sta - > sta . max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_3839 ;
2013-02-07 14:47:44 +04:00
apply :
changed = memcmp ( & sta - > sta . ht_cap , & ht_cap , sizeof ( ht_cap ) ) ;
memcpy ( & sta - > sta . ht_cap , & ht_cap , sizeof ( ht_cap ) ) ;
switch ( sdata - > vif . bss_conf . chandef . width ) {
default :
WARN_ON_ONCE ( 1 ) ;
/* fall through */
case NL80211_CHAN_WIDTH_20_NOHT :
case NL80211_CHAN_WIDTH_20 :
bw = IEEE80211_STA_RX_BW_20 ;
break ;
case NL80211_CHAN_WIDTH_40 :
case NL80211_CHAN_WIDTH_80 :
case NL80211_CHAN_WIDTH_80P80 :
case NL80211_CHAN_WIDTH_160 :
bw = ht_cap . cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20 ;
break ;
}
sta - > sta . bandwidth = bw ;
2012-12-27 21:55:36 +04:00
sta - > cur_max_bandwidth =
ht_cap . cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20 ;
2013-02-12 17:21:00 +04:00
switch ( ( ht_cap . cap & IEEE80211_HT_CAP_SM_PS )
> > IEEE80211_HT_CAP_SM_PS_SHIFT ) {
case WLAN_HT_CAP_SM_PS_INVALID :
case WLAN_HT_CAP_SM_PS_STATIC :
smps_mode = IEEE80211_SMPS_STATIC ;
break ;
case WLAN_HT_CAP_SM_PS_DYNAMIC :
smps_mode = IEEE80211_SMPS_DYNAMIC ;
break ;
case WLAN_HT_CAP_SM_PS_DISABLED :
smps_mode = IEEE80211_SMPS_OFF ;
break ;
}
if ( smps_mode ! = sta - > sta . smps_mode )
changed = true ;
sta - > sta . smps_mode = smps_mode ;
2013-02-07 14:47:44 +04:00
return changed ;
2008-10-14 18:58:37 +04:00
}
2012-07-18 15:31:31 +04:00
void ieee80211_sta_tear_down_BA_sessions ( struct sta_info * sta ,
enum ieee80211_agg_stop_reason reason )
2008-09-09 16:42:50 +04:00
{
2009-02-10 23:25:46 +03:00
int i ;
2008-09-09 16:42:50 +04:00
2012-11-15 02:22:21 +04:00
for ( i = 0 ; i < IEEE80211_NUM_TIDS ; i + + ) {
2012-07-18 15:31:31 +04:00
__ieee80211_stop_tx_ba_session ( sta , i , reason ) ;
2009-02-10 23:25:54 +03:00
__ieee80211_stop_rx_ba_session ( sta , i , WLAN_BACK_RECIPIENT ,
2012-07-18 15:31:31 +04:00
WLAN_REASON_QSTA_LEAVE_QBSS ,
reason ! = AGG_STOP_DESTROY_STA & &
reason ! = AGG_STOP_PEER_REQUEST ) ;
2008-09-09 16:42:50 +04:00
}
2017-05-27 01:27:25 +03:00
/* stopping might queue the work again - so cancel only afterwards */
cancel_work_sync ( & sta - > ampdu_mlme . work ) ;
2008-09-09 16:42:50 +04:00
}
2010-06-10 12:21:43 +04:00
void ieee80211_ba_session_work ( struct work_struct * work )
{
struct sta_info * sta =
container_of ( work , struct sta_info , ampdu_mlme . work ) ;
struct tid_ampdu_tx * tid_tx ;
int tid ;
/*
* When this flag is set , new sessions should be
* blocked , and existing sessions will be torn
* down by the code that set the flag , so this
* need not run .
*/
2011-09-29 18:04:36 +04:00
if ( test_sta_flag ( sta , WLAN_STA_BLOCK_BA ) )
2010-06-10 12:21:43 +04:00
return ;
2010-06-10 12:21:46 +04:00
mutex_lock ( & sta - > ampdu_mlme . mtx ) ;
2012-11-15 02:22:21 +04:00
for ( tid = 0 ; tid < IEEE80211_NUM_TIDS ; tid + + ) {
2010-06-10 12:21:44 +04:00
if ( test_and_clear_bit ( tid , sta - > ampdu_mlme . tid_rx_timer_expired ) )
___ieee80211_stop_rx_ba_session (
sta , tid , WLAN_BACK_RECIPIENT ,
2010-10-05 21:37:40 +04:00
WLAN_REASON_QSTA_TIMEOUT , true ) ;
2010-06-10 12:21:44 +04:00
2011-05-22 17:10:21 +04:00
if ( test_and_clear_bit ( tid ,
sta - > ampdu_mlme . tid_rx_stop_requested ) )
___ieee80211_stop_rx_ba_session (
sta , tid , WLAN_BACK_RECIPIENT ,
WLAN_REASON_UNSPECIFIED , true ) ;
2013-06-13 01:08:44 +04:00
spin_lock_bh ( & sta - > lock ) ;
2011-05-13 15:35:40 +04:00
tid_tx = sta - > ampdu_mlme . tid_start_tx [ tid ] ;
if ( tid_tx ) {
/*
* Assign it over to the normal tid_tx array
* where it " goes live " .
*/
sta - > ampdu_mlme . tid_start_tx [ tid ] = NULL ;
/* could there be a race? */
if ( sta - > ampdu_mlme . tid_tx [ tid ] )
kfree ( tid_tx ) ;
else
ieee80211_assign_tid_tx ( sta , tid , tid_tx ) ;
spin_unlock_bh ( & sta - > lock ) ;
2010-06-10 12:21:43 +04:00
ieee80211_tx_ba_session_handle_start ( sta , tid ) ;
2011-05-13 15:35:40 +04:00
continue ;
}
2013-06-13 01:08:44 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2011-05-13 15:35:40 +04:00
2011-05-13 16:15:49 +04:00
tid_tx = rcu_dereference_protected_tid_tx ( sta , tid ) ;
2017-05-27 01:27:25 +03:00
if ( ! tid_tx )
continue ;
if ( test_and_clear_bit ( HT_AGG_STATE_START_CB , & tid_tx - > state ) )
ieee80211_start_tx_ba_cb ( sta , tid , tid_tx ) ;
if ( test_and_clear_bit ( HT_AGG_STATE_WANT_STOP , & tid_tx - > state ) )
2010-06-10 12:21:43 +04:00
___ieee80211_stop_tx_ba_session ( sta , tid ,
2012-07-18 15:31:31 +04:00
AGG_STOP_LOCAL_REQUEST ) ;
2017-05-27 01:27:25 +03:00
if ( test_and_clear_bit ( HT_AGG_STATE_STOP_CB , & tid_tx - > state ) )
ieee80211_stop_tx_ba_cb ( sta , tid , tid_tx ) ;
2010-06-10 12:21:43 +04:00
}
2010-06-10 12:21:48 +04:00
mutex_unlock ( & sta - > ampdu_mlme . mtx ) ;
2010-06-10 12:21:43 +04:00
}
2009-02-10 23:25:46 +03:00
void ieee80211_send_delba ( struct ieee80211_sub_if_data * sdata ,
const u8 * da , u16 tid ,
u16 initiator , u16 reason_code )
2008-09-08 19:44:28 +04:00
{
struct ieee80211_local * local = sdata - > local ;
struct sk_buff * skb ;
struct ieee80211_mgmt * mgmt ;
u16 params ;
skb = dev_alloc_skb ( sizeof ( * mgmt ) + local - > hw . extra_tx_headroom ) ;
2011-08-30 01:17:31 +04:00
if ( ! skb )
2008-09-08 19:44:28 +04:00
return ;
skb_reserve ( skb , local - > hw . extra_tx_headroom ) ;
mgmt = ( struct ieee80211_mgmt * ) skb_put ( skb , 24 ) ;
memset ( mgmt , 0 , 24 ) ;
memcpy ( mgmt - > da , da , ETH_ALEN ) ;
2009-11-25 19:46:19 +03:00
memcpy ( mgmt - > sa , sdata - > vif . addr , ETH_ALEN ) ;
2009-02-10 23:25:47 +03:00
if ( sdata - > vif . type = = NL80211_IFTYPE_AP | |
2011-10-27 01:47:29 +04:00
sdata - > vif . type = = NL80211_IFTYPE_AP_VLAN | |
sdata - > vif . type = = NL80211_IFTYPE_MESH_POINT )
2009-11-25 19:46:19 +03:00
memcpy ( mgmt - > bssid , sdata - > vif . addr , ETH_ALEN ) ;
2009-02-15 14:44:28 +03:00
else if ( sdata - > vif . type = = NL80211_IFTYPE_STATION )
memcpy ( mgmt - > bssid , sdata - > u . mgd . bssid , ETH_ALEN ) ;
2011-11-30 19:56:34 +04:00
else if ( sdata - > vif . type = = NL80211_IFTYPE_ADHOC )
memcpy ( mgmt - > bssid , sdata - > u . ibss . bssid , ETH_ALEN ) ;
2009-02-15 14:44:28 +03:00
2008-09-08 19:44:28 +04:00
mgmt - > frame_control = cpu_to_le16 ( IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION ) ;
skb_put ( skb , 1 + sizeof ( mgmt - > u . action . u . delba ) ) ;
mgmt - > u . action . category = WLAN_CATEGORY_BACK ;
mgmt - > u . action . u . delba . action_code = WLAN_ACTION_DELBA ;
params = ( u16 ) ( initiator < < 11 ) ; /* bit 11 initiator */
params | = ( u16 ) ( tid < < 12 ) ; /* bit 15:12 TID number */
mgmt - > u . action . u . delba . params = cpu_to_le16 ( params ) ;
mgmt - > u . action . u . delba . reason_code = cpu_to_le16 ( reason_code ) ;
mac80211: send {ADD,DEL}BA on AC_VO like other mgmt frames, as per spec
ATM, {ADD,DEL}BA and BAR frames are sent on the AC matching the TID of
the BA parameters. In the discussion [1] about this patch, Johannes
recalled that it fixed some races with the DELBA and indeed this
behavior was introduced in [2].
While [2] is right for the BARs, the part queueing the {ADD,DEL}BAs on
their BA params TID AC violates the spec and is more a workaround for
some drivers. Helmut expressed some concerns wrt such drivers, in
particular DELBAs in rt2x00.
ATM, DELBAs are sent after a driver has called (hence "purposely")
ieee80211_start_tx_ba_cb_irqsafe and Johannes and Emmanuel gave some
details wrt intentions behind the split of the IEEE80211_AMPDU_TX_STOP_*
given to the driver ampdu_action supposed to call this function, which
could prove handy to people trying to do the right thing in faulty
drivers (if their fw/hw don't get in their way).
[1] http://mid.gmane.org/1390391564-18481-1-git-send-email-karl.beldan@gmail.com
[2] Commit: cf6bb79ad828 ("mac80211: Use appropriate TID for sending BAR, ADDBA and DELBA frames")
Signed-off-by: Karl Beldan <karl.beldan@rivierawaves.com>
Cc: Helmut Schaa <helmut.schaa@googlemail.com>
Cc: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2014-01-23 23:06:34 +04:00
ieee80211_tx_skb ( sdata , skb ) ;
2008-09-08 19:44:28 +04:00
}
2008-09-09 16:42:50 +04:00
void ieee80211_process_delba ( struct ieee80211_sub_if_data * sdata ,
struct sta_info * sta ,
struct ieee80211_mgmt * mgmt , size_t len )
{
u16 tid , params ;
u16 initiator ;
params = le16_to_cpu ( mgmt - > u . action . u . delba . params ) ;
tid = ( params & IEEE80211_DELBA_PARAM_TID_MASK ) > > 12 ;
initiator = ( params & IEEE80211_DELBA_PARAM_INITIATOR_MASK ) > > 11 ;
2012-06-22 13:29:50 +04:00
ht_dbg_ratelimited ( sdata , " delba from %pM (%s) tid %d reason code %d \n " ,
mgmt - > sa , initiator ? " initiator " : " recipient " ,
tid ,
le16_to_cpu ( mgmt - > u . action . u . delba . reason_code ) ) ;
2008-09-09 16:42:50 +04:00
if ( initiator = = WLAN_BACK_INITIATOR )
2010-10-05 21:37:40 +04:00
__ieee80211_stop_rx_ba_session ( sta , tid , WLAN_BACK_INITIATOR , 0 ,
true ) ;
2010-06-10 12:21:39 +04:00
else
2012-07-18 15:31:31 +04:00
__ieee80211_stop_tx_ba_session ( sta , tid , AGG_STOP_PEER_REQUEST ) ;
2008-09-09 16:42:50 +04:00
}
2009-12-01 15:37:02 +03:00
int ieee80211_send_smps_action ( struct ieee80211_sub_if_data * sdata ,
enum ieee80211_smps_mode smps , const u8 * da ,
const u8 * bssid )
{
struct ieee80211_local * local = sdata - > local ;
struct sk_buff * skb ;
struct ieee80211_mgmt * action_frame ;
/* 27 = header + category + action + smps mode */
skb = dev_alloc_skb ( 27 + local - > hw . extra_tx_headroom ) ;
if ( ! skb )
return - ENOMEM ;
skb_reserve ( skb , local - > hw . extra_tx_headroom ) ;
action_frame = ( void * ) skb_put ( skb , 27 ) ;
memcpy ( action_frame - > da , da , ETH_ALEN ) ;
memcpy ( action_frame - > sa , sdata - > dev - > dev_addr , ETH_ALEN ) ;
memcpy ( action_frame - > bssid , bssid , ETH_ALEN ) ;
action_frame - > frame_control = cpu_to_le16 ( IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION ) ;
action_frame - > u . action . category = WLAN_CATEGORY_HT ;
action_frame - > u . action . u . ht_smps . action = WLAN_HT_ACTION_SMPS ;
switch ( smps ) {
case IEEE80211_SMPS_AUTOMATIC :
case IEEE80211_SMPS_NUM_MODES :
WARN_ON ( 1 ) ;
case IEEE80211_SMPS_OFF :
action_frame - > u . action . u . ht_smps . smps_control =
WLAN_HT_SMPS_CONTROL_DISABLED ;
break ;
case IEEE80211_SMPS_STATIC :
action_frame - > u . action . u . ht_smps . smps_control =
WLAN_HT_SMPS_CONTROL_STATIC ;
break ;
case IEEE80211_SMPS_DYNAMIC :
action_frame - > u . action . u . ht_smps . smps_control =
WLAN_HT_SMPS_CONTROL_DYNAMIC ;
break ;
}
/* we'll do more on status of this frame */
IEEE80211_SKB_CB ( skb ) - > flags | = IEEE80211_TX_CTL_REQ_TX_STATUS ;
ieee80211_tx_skb ( sdata , skb ) ;
return 0 ;
}
2010-08-05 19:05:55 +04:00
2013-10-01 17:45:43 +04:00
void ieee80211_request_smps_mgd_work ( struct work_struct * work )
2010-08-05 19:05:55 +04:00
{
struct ieee80211_sub_if_data * sdata =
container_of ( work , struct ieee80211_sub_if_data ,
u . mgd . request_smps_work ) ;
2013-05-10 14:32:47 +04:00
sdata_lock ( sdata ) ;
2013-10-01 17:45:43 +04:00
__ieee80211_request_smps_mgd ( sdata , sdata - > u . mgd . driver_smps_mode ) ;
sdata_unlock ( sdata ) ;
}
void ieee80211_request_smps_ap_work ( struct work_struct * work )
{
struct ieee80211_sub_if_data * sdata =
container_of ( work , struct ieee80211_sub_if_data ,
u . ap . request_smps_work ) ;
sdata_lock ( sdata ) ;
2014-01-23 16:28:16 +04:00
if ( sdata_dereference ( sdata - > u . ap . beacon , sdata ) )
__ieee80211_request_smps_ap ( sdata ,
sdata - > u . ap . driver_smps_mode ) ;
2013-05-10 14:32:47 +04:00
sdata_unlock ( sdata ) ;
2010-08-05 19:05:55 +04:00
}
void ieee80211_request_smps ( struct ieee80211_vif * vif ,
enum ieee80211_smps_mode smps_mode )
{
struct ieee80211_sub_if_data * sdata = vif_to_sdata ( vif ) ;
2013-10-01 17:45:43 +04:00
if ( WARN_ON_ONCE ( vif - > type ! = NL80211_IFTYPE_STATION & &
vif - > type ! = NL80211_IFTYPE_AP ) )
2010-08-05 19:05:55 +04:00
return ;
2013-10-01 17:45:43 +04:00
if ( vif - > type = = NL80211_IFTYPE_STATION ) {
if ( sdata - > u . mgd . driver_smps_mode = = smps_mode )
return ;
sdata - > u . mgd . driver_smps_mode = smps_mode ;
ieee80211_queue_work ( & sdata - > local - > hw ,
& sdata - > u . mgd . request_smps_work ) ;
} else {
/* AUTOMATIC is meaningless in AP mode */
if ( WARN_ON_ONCE ( smps_mode = = IEEE80211_SMPS_AUTOMATIC ) )
return ;
if ( sdata - > u . ap . driver_smps_mode = = smps_mode )
return ;
sdata - > u . ap . driver_smps_mode = smps_mode ;
ieee80211_queue_work ( & sdata - > local - > hw ,
& sdata - > u . ap . request_smps_work ) ;
}
2010-08-05 19:05:55 +04:00
}
/* this might change ... don't want non-open drivers using it */
EXPORT_SYMBOL_GPL ( ieee80211_request_smps ) ;