@ -9473,7 +9473,7 @@ static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
static const struct nla_policy
nl80211_attr_cqm_policy [ NL80211_ATTR_CQM_MAX + 1 ] = {
[ NL80211_ATTR_CQM_RSSI_THOLD ] = { . type = NLA_U32 } ,
[ NL80211_ATTR_CQM_RSSI_THOLD ] = { . type = NLA_BINARY } ,
[ NL80211_ATTR_CQM_RSSI_HYST ] = { . type = NLA_U32 } ,
[ NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT ] = { . type = NLA_U32 } ,
[ NL80211_ATTR_CQM_TXE_RATE ] = { . type = NLA_U32 } ,
@ -9502,28 +9502,123 @@ static int nl80211_set_cqm_txe(struct genl_info *info,
return rdev_set_cqm_txe_config ( rdev , dev , rate , pkts , intvl ) ;
}
static int cfg80211_cqm_rssi_update ( struct cfg80211_registered_device * rdev ,
struct net_device * dev )
{
struct wireless_dev * wdev = dev - > ieee80211_ptr ;
s32 last , low , high ;
u32 hyst ;
int i , n ;
int err ;
/* RSSI reporting disabled? */
if ( ! wdev - > cqm_config )
return rdev_set_cqm_rssi_range_config ( rdev , dev , 0 , 0 ) ;
/*
* Obtain current RSSI value if possible , if not and no RSSI threshold
* event has been received yet , we should receive an event after a
* connection is established and enough beacons received to calculate
* the average .
*/
if ( ! wdev - > cqm_config - > last_rssi_event_value & & wdev - > current_bss & &
rdev - > ops - > get_station ) {
struct station_info sinfo ;
u8 * mac_addr ;
mac_addr = wdev - > current_bss - > pub . bssid ;
err = rdev_get_station ( rdev , dev , mac_addr , & sinfo ) ;
if ( err )
return err ;
if ( sinfo . filled & BIT ( NL80211_STA_INFO_BEACON_SIGNAL_AVG ) )
wdev - > cqm_config - > last_rssi_event_value =
( s8 ) sinfo . rx_beacon_signal_avg ;
}
last = wdev - > cqm_config - > last_rssi_event_value ;
hyst = wdev - > cqm_config - > rssi_hyst ;
n = wdev - > cqm_config - > n_rssi_thresholds ;
for ( i = 0 ; i < n ; i + + )
if ( last < wdev - > cqm_config - > rssi_thresholds [ i ] )
break ;
low = i > 0 ?
( wdev - > cqm_config - > rssi_thresholds [ i - 1 ] - hyst ) : S32_MIN ;
high = i < n ?
( wdev - > cqm_config - > rssi_thresholds [ i ] + hyst - 1 ) : S32_MAX ;
return rdev_set_cqm_rssi_range_config ( rdev , dev , low , high ) ;
}
static int nl80211_set_cqm_rssi ( struct genl_info * info ,
s32 threshold , u32 hysteresis )
const s32 * thresholds , int n_thresholds ,
u32 hysteresis )
{
struct cfg80211_registered_device * rdev = info - > user_ptr [ 0 ] ;
struct net_device * dev = info - > user_ptr [ 1 ] ;
struct wireless_dev * wdev = dev - > ieee80211_ptr ;
int i , err ;
s32 prev = S32_MIN ;
if ( threshold > 0 )
return - EINVAL ;
/* disabling - hysteresis should also be zero then */
if ( threshold = = 0 )
hysteresis = 0 ;
/* Check all values negative and sorted */
for ( i = 0 ; i < n_thresholds ; i + + ) {
if ( thresholds [ i ] > 0 | | thresholds [ i ] < = prev )
return - EINVAL ;
if ( ! rdev - > ops - > set_cqm_rssi_config )
return - EOPNOTSUPP ;
prev = thresholds [ i ] ;
}
if ( wdev - > iftype ! = NL80211_IFTYPE_STATION & &
wdev - > iftype ! = NL80211_IFTYPE_P2P_CLIENT )
return - EOPNOTSUPP ;
return rdev_set_cqm_rssi_config ( rdev , dev , threshold , hysteresis ) ;
wdev_lock ( wdev ) ;
cfg80211_cqm_config_free ( wdev ) ;
wdev_unlock ( wdev ) ;
if ( n_thresholds < = 1 & & rdev - > ops - > set_cqm_rssi_config ) {
if ( n_thresholds = = 0 | | thresholds [ 0 ] = = 0 ) /* Disabling */
return rdev_set_cqm_rssi_config ( rdev , dev , 0 , 0 ) ;
return rdev_set_cqm_rssi_config ( rdev , dev ,
thresholds [ 0 ] , hysteresis ) ;
}
if ( ! wiphy_ext_feature_isset ( & rdev - > wiphy ,
NL80211_EXT_FEATURE_CQM_RSSI_LIST ) )
return - EOPNOTSUPP ;
if ( n_thresholds = = 1 & & thresholds [ 0 ] = = 0 ) /* Disabling */
n_thresholds = 0 ;
wdev_lock ( wdev ) ;
if ( n_thresholds ) {
struct cfg80211_cqm_config * cqm_config ;
cqm_config = kzalloc ( sizeof ( struct cfg80211_cqm_config ) +
n_thresholds * sizeof ( s32 ) , GFP_KERNEL ) ;
if ( ! cqm_config ) {
err = - ENOMEM ;
goto unlock ;
}
cqm_config - > rssi_hyst = hysteresis ;
cqm_config - > n_rssi_thresholds = n_thresholds ;
memcpy ( cqm_config - > rssi_thresholds , thresholds ,
n_thresholds * sizeof ( s32 ) ) ;
wdev - > cqm_config = cqm_config ;
}
err = cfg80211_cqm_rssi_update ( rdev , dev ) ;
unlock :
wdev_unlock ( wdev ) ;
return err ;
}
static int nl80211_set_cqm ( struct sk_buff * skb , struct genl_info * info )
@ -9543,10 +9638,16 @@ static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
if ( attrs [ NL80211_ATTR_CQM_RSSI_THOLD ] & &
attrs [ NL80211_ATTR_CQM_RSSI_HYST ] ) {
s32 threshold = nla_get_s32 ( attrs [ NL80211_ATTR_CQM_RSSI_THOLD ] ) ;
const s32 * thresholds =
nla_data ( attrs [ NL80211_ATTR_CQM_RSSI_THOLD ] ) ;
int len = nla_len ( attrs [ NL80211_ATTR_CQM_RSSI_THOLD ] ) ;
u32 hysteresis = nla_get_u32 ( attrs [ NL80211_ATTR_CQM_RSSI_HYST ] ) ;
return nl80211_set_cqm_rssi ( info , threshold , hysteresis ) ;
if ( len % 4 )
return - EINVAL ;
return nl80211_set_cqm_rssi ( info , thresholds , len / 4 ,
hysteresis ) ;
}
if ( attrs [ NL80211_ATTR_CQM_TXE_RATE ] & &
@ -13983,6 +14084,8 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
s32 rssi_level , gfp_t gfp )
{
struct sk_buff * msg ;
struct wireless_dev * wdev = dev - > ieee80211_ptr ;
struct cfg80211_registered_device * rdev = wiphy_to_rdev ( wdev - > wiphy ) ;
trace_cfg80211_cqm_rssi_notify ( dev , rssi_event , rssi_level ) ;
@ -13990,6 +14093,15 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
rssi_event ! = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH ) )
return ;
if ( wdev - > cqm_config ) {
wdev - > cqm_config - > last_rssi_event_value = rssi_level ;
cfg80211_cqm_rssi_update ( rdev , dev ) ;
if ( rssi_level = = 0 )
rssi_level = wdev - > cqm_config - > last_rssi_event_value ;
}
msg = cfg80211_prepare_cqm ( dev , NULL , gfp ) ;
if ( ! msg )
return ;