@@ -9473,7 +9473,7 @@ static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
94739473
94749474static const struct nla_policy
94759475nl80211_attr_cqm_policy [NL80211_ATTR_CQM_MAX + 1 ] = {
9476- [NL80211_ATTR_CQM_RSSI_THOLD ] = { .type = NLA_U32 },
9476+ [NL80211_ATTR_CQM_RSSI_THOLD ] = { .type = NLA_BINARY },
94779477 [NL80211_ATTR_CQM_RSSI_HYST ] = { .type = NLA_U32 },
94789478 [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT ] = { .type = NLA_U32 },
94799479 [NL80211_ATTR_CQM_TXE_RATE ] = { .type = NLA_U32 },
@@ -9502,28 +9502,123 @@ static int nl80211_set_cqm_txe(struct genl_info *info,
95029502 return rdev_set_cqm_txe_config (rdev , dev , rate , pkts , intvl );
95039503}
95049504
9505+ static int cfg80211_cqm_rssi_update (struct cfg80211_registered_device * rdev ,
9506+ struct net_device * dev )
9507+ {
9508+ struct wireless_dev * wdev = dev -> ieee80211_ptr ;
9509+ s32 last , low , high ;
9510+ u32 hyst ;
9511+ int i , n ;
9512+ int err ;
9513+
9514+ /* RSSI reporting disabled? */
9515+ if (!wdev -> cqm_config )
9516+ return rdev_set_cqm_rssi_range_config (rdev , dev , 0 , 0 );
9517+
9518+ /*
9519+ * Obtain current RSSI value if possible, if not and no RSSI threshold
9520+ * event has been received yet, we should receive an event after a
9521+ * connection is established and enough beacons received to calculate
9522+ * the average.
9523+ */
9524+ if (!wdev -> cqm_config -> last_rssi_event_value && wdev -> current_bss &&
9525+ rdev -> ops -> get_station ) {
9526+ struct station_info sinfo ;
9527+ u8 * mac_addr ;
9528+
9529+ mac_addr = wdev -> current_bss -> pub .bssid ;
9530+
9531+ err = rdev_get_station (rdev , dev , mac_addr , & sinfo );
9532+ if (err )
9533+ return err ;
9534+
9535+ if (sinfo .filled & BIT (NL80211_STA_INFO_BEACON_SIGNAL_AVG ))
9536+ wdev -> cqm_config -> last_rssi_event_value =
9537+ (s8 ) sinfo .rx_beacon_signal_avg ;
9538+ }
9539+
9540+ last = wdev -> cqm_config -> last_rssi_event_value ;
9541+ hyst = wdev -> cqm_config -> rssi_hyst ;
9542+ n = wdev -> cqm_config -> n_rssi_thresholds ;
9543+
9544+ for (i = 0 ; i < n ; i ++ )
9545+ if (last < wdev -> cqm_config -> rssi_thresholds [i ])
9546+ break ;
9547+
9548+ low = i > 0 ?
9549+ (wdev -> cqm_config -> rssi_thresholds [i - 1 ] - hyst ) : S32_MIN ;
9550+ high = i < n ?
9551+ (wdev -> cqm_config -> rssi_thresholds [i ] + hyst - 1 ) : S32_MAX ;
9552+
9553+ return rdev_set_cqm_rssi_range_config (rdev , dev , low , high );
9554+ }
9555+
95059556static int nl80211_set_cqm_rssi (struct genl_info * info ,
9506- s32 threshold , u32 hysteresis )
9557+ const s32 * thresholds , int n_thresholds ,
9558+ u32 hysteresis )
95079559{
95089560 struct cfg80211_registered_device * rdev = info -> user_ptr [0 ];
95099561 struct net_device * dev = info -> user_ptr [1 ];
95109562 struct wireless_dev * wdev = dev -> ieee80211_ptr ;
9563+ int i , err ;
9564+ s32 prev = S32_MIN ;
95119565
9512- if (threshold > 0 )
9513- return - EINVAL ;
9514-
9515- /* disabling - hysteresis should also be zero then */
9516- if (threshold == 0 )
9517- hysteresis = 0 ;
9566+ /* Check all values negative and sorted */
9567+ for (i = 0 ; i < n_thresholds ; i ++ ) {
9568+ if (thresholds [i ] > 0 || thresholds [i ] <= prev )
9569+ return - EINVAL ;
95189570
9519- if (! rdev -> ops -> set_cqm_rssi_config )
9520- return - EOPNOTSUPP ;
9571+ prev = thresholds [ i ];
9572+ }
95219573
95229574 if (wdev -> iftype != NL80211_IFTYPE_STATION &&
95239575 wdev -> iftype != NL80211_IFTYPE_P2P_CLIENT )
95249576 return - EOPNOTSUPP ;
95259577
9526- return rdev_set_cqm_rssi_config (rdev , dev , threshold , hysteresis );
9578+ wdev_lock (wdev );
9579+ cfg80211_cqm_config_free (wdev );
9580+ wdev_unlock (wdev );
9581+
9582+ if (n_thresholds <= 1 && rdev -> ops -> set_cqm_rssi_config ) {
9583+ if (n_thresholds == 0 || thresholds [0 ] == 0 ) /* Disabling */
9584+ return rdev_set_cqm_rssi_config (rdev , dev , 0 , 0 );
9585+
9586+ return rdev_set_cqm_rssi_config (rdev , dev ,
9587+ thresholds [0 ], hysteresis );
9588+ }
9589+
9590+ if (!wiphy_ext_feature_isset (& rdev -> wiphy ,
9591+ NL80211_EXT_FEATURE_CQM_RSSI_LIST ))
9592+ return - EOPNOTSUPP ;
9593+
9594+ if (n_thresholds == 1 && thresholds [0 ] == 0 ) /* Disabling */
9595+ n_thresholds = 0 ;
9596+
9597+ wdev_lock (wdev );
9598+ if (n_thresholds ) {
9599+ struct cfg80211_cqm_config * cqm_config ;
9600+
9601+ cqm_config = kzalloc (sizeof (struct cfg80211_cqm_config ) +
9602+ n_thresholds * sizeof (s32 ), GFP_KERNEL );
9603+ if (!cqm_config ) {
9604+ err = - ENOMEM ;
9605+ goto unlock ;
9606+ }
9607+
9608+ cqm_config -> rssi_hyst = hysteresis ;
9609+ cqm_config -> n_rssi_thresholds = n_thresholds ;
9610+ memcpy (cqm_config -> rssi_thresholds , thresholds ,
9611+ n_thresholds * sizeof (s32 ));
9612+
9613+ wdev -> cqm_config = cqm_config ;
9614+ }
9615+
9616+ err = cfg80211_cqm_rssi_update (rdev , dev );
9617+
9618+ unlock :
9619+ wdev_unlock (wdev );
9620+
9621+ return err ;
95279622}
95289623
95299624static 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)
95439638
95449639 if (attrs [NL80211_ATTR_CQM_RSSI_THOLD ] &&
95459640 attrs [NL80211_ATTR_CQM_RSSI_HYST ]) {
9546- s32 threshold = nla_get_s32 (attrs [NL80211_ATTR_CQM_RSSI_THOLD ]);
9641+ const s32 * thresholds =
9642+ nla_data (attrs [NL80211_ATTR_CQM_RSSI_THOLD ]);
9643+ int len = nla_len (attrs [NL80211_ATTR_CQM_RSSI_THOLD ]);
95479644 u32 hysteresis = nla_get_u32 (attrs [NL80211_ATTR_CQM_RSSI_HYST ]);
95489645
9549- return nl80211_set_cqm_rssi (info , threshold , hysteresis );
9646+ if (len % 4 )
9647+ return - EINVAL ;
9648+
9649+ return nl80211_set_cqm_rssi (info , thresholds , len / 4 ,
9650+ hysteresis );
95509651 }
95519652
95529653 if (attrs [NL80211_ATTR_CQM_TXE_RATE ] &&
@@ -13983,13 +14084,24 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
1398314084 s32 rssi_level , gfp_t gfp )
1398414085{
1398514086 struct sk_buff * msg ;
14087+ struct wireless_dev * wdev = dev -> ieee80211_ptr ;
14088+ struct cfg80211_registered_device * rdev = wiphy_to_rdev (wdev -> wiphy );
1398614089
1398714090 trace_cfg80211_cqm_rssi_notify (dev , rssi_event , rssi_level );
1398814091
1398914092 if (WARN_ON (rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW &&
1399014093 rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH ))
1399114094 return ;
1399214095
14096+ if (wdev -> cqm_config ) {
14097+ wdev -> cqm_config -> last_rssi_event_value = rssi_level ;
14098+
14099+ cfg80211_cqm_rssi_update (rdev , dev );
14100+
14101+ if (rssi_level == 0 )
14102+ rssi_level = wdev -> cqm_config -> last_rssi_event_value ;
14103+ }
14104+
1399314105 msg = cfg80211_prepare_cqm (dev , NULL , gfp );
1399414106 if (!msg )
1399514107 return ;
0 commit comments