brcmfmac: add support for CQM RSSI notifications
authorAlvin Šipraga <ALSI@bang-olufsen.dk>
Mon, 8 Feb 2021 12:58:20 +0000 (12:58 +0000)
committerKalle Valo <kvalo@codeaurora.org>
Tue, 9 Feb 2021 07:57:02 +0000 (09:57 +0200)
Add support for CQM RSSI measurement reporting and advertise the
NL80211_EXT_FEATURE_CQM_RSSI_LIST feature. This enables a userspace
supplicant such as iwd to be notified of changes in the RSSI for roaming
and signal monitoring purposes.

Signed-off-by: Alvin Šipraga <alsi@bang-olufsen.dk>
Reviewed-by: Arend van Spriel <arend.vanspriel@broadcom.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20210208125738.3546557-1-alsi@bang-olufsen.dk
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h

index 23e6422..f4405d7 100644 (file)
@@ -5196,6 +5196,48 @@ exit:
        return err;
 }
 
+static int brcmf_cfg80211_set_cqm_rssi_range_config(struct wiphy *wiphy,
+                                                   struct net_device *ndev,
+                                                   s32 rssi_low, s32 rssi_high)
+{
+       struct brcmf_cfg80211_vif *vif;
+       struct brcmf_if *ifp;
+       int err = 0;
+
+       brcmf_dbg(TRACE, "low=%d high=%d", rssi_low, rssi_high);
+
+       ifp = netdev_priv(ndev);
+       vif = ifp->vif;
+
+       if (rssi_low != vif->cqm_rssi_low || rssi_high != vif->cqm_rssi_high) {
+               /* The firmware will send an event when the RSSI is less than or
+                * equal to a configured level and the previous RSSI event was
+                * less than or equal to a different level. Set a third level
+                * so that we also detect the transition from rssi <= rssi_high
+                * to rssi > rssi_high.
+                */
+               struct brcmf_rssi_event_le config = {
+                       .rate_limit_msec = cpu_to_le32(0),
+                       .rssi_level_num = 3,
+                       .rssi_levels = {
+                               clamp_val(rssi_low, S8_MIN, S8_MAX - 2),
+                               clamp_val(rssi_high, S8_MIN + 1, S8_MAX - 1),
+                               S8_MAX,
+                       },
+               };
+
+               err = brcmf_fil_iovar_data_set(ifp, "rssi_event", &config,
+                                              sizeof(config));
+               if (err) {
+                       err = -EINVAL;
+               } else {
+                       vif->cqm_rssi_low = rssi_low;
+                       vif->cqm_rssi_high = rssi_high;
+               }
+       }
+
+       return err;
+}
 
 static int
 brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
@@ -5502,6 +5544,7 @@ static struct cfg80211_ops brcmf_cfg80211_ops = {
        .update_mgmt_frame_registrations =
                brcmf_cfg80211_update_mgmt_frame_registrations,
        .mgmt_tx = brcmf_cfg80211_mgmt_tx,
+       .set_cqm_rssi_range_config = brcmf_cfg80211_set_cqm_rssi_range_config,
        .remain_on_channel = brcmf_p2p_remain_on_channel,
        .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
        .get_channel = brcmf_cfg80211_get_channel,
@@ -6140,6 +6183,47 @@ brcmf_notify_mic_status(struct brcmf_if *ifp,
        return 0;
 }
 
+static s32 brcmf_notify_rssi(struct brcmf_if *ifp,
+                            const struct brcmf_event_msg *e, void *data)
+{
+       struct brcmf_cfg80211_vif *vif = ifp->vif;
+       struct brcmf_rssi_be *info = data;
+       s32 rssi, snr, noise;
+       s32 low, high, last;
+
+       if (e->datalen < sizeof(*info)) {
+               brcmf_err("insufficient RSSI event data\n");
+               return 0;
+       }
+
+       rssi = be32_to_cpu(info->rssi);
+       snr = be32_to_cpu(info->snr);
+       noise = be32_to_cpu(info->noise);
+
+       low = vif->cqm_rssi_low;
+       high = vif->cqm_rssi_high;
+       last = vif->cqm_rssi_last;
+
+       brcmf_dbg(TRACE, "rssi=%d snr=%d noise=%d low=%d high=%d last=%d\n",
+                 rssi, snr, noise, low, high, last);
+
+       vif->cqm_rssi_last = rssi;
+
+       if (rssi <= low || rssi == 0) {
+               brcmf_dbg(INFO, "LOW rssi=%d\n", rssi);
+               cfg80211_cqm_rssi_notify(ifp->ndev,
+                                        NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+                                        rssi, GFP_KERNEL);
+       } else if (rssi > high) {
+               brcmf_dbg(INFO, "HIGH rssi=%d\n", rssi);
+               cfg80211_cqm_rssi_notify(ifp->ndev,
+                                        NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+                                        rssi, GFP_KERNEL);
+       }
+
+       return 0;
+}
+
 static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
                                  const struct brcmf_event_msg *e, void *data)
 {
@@ -6238,6 +6322,7 @@ static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
                            brcmf_p2p_notify_action_tx_complete);
        brcmf_fweh_register(cfg->pub, BRCMF_E_PSK_SUP,
                            brcmf_notify_connect_status);
+       brcmf_fweh_register(cfg->pub, BRCMF_E_RSSI, brcmf_notify_rssi);
 }
 
 static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
@@ -7172,6 +7257,8 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
                wiphy_ext_feature_set(wiphy,
                                      NL80211_EXT_FEATURE_DFS_OFFLOAD);
 
+       wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
+
        wiphy_read_of_freq_limits(wiphy);
 
        return 0;
index 17817cd..e90a308 100644 (file)
@@ -213,6 +213,9 @@ struct vif_saved_ie {
  * @list: linked list.
  * @mgmt_rx_reg: registered rx mgmt frame types.
  * @mbss: Multiple BSS type, set if not first AP (not relevant for P2P).
+ * @cqm_rssi_low: Lower RSSI limit for CQM monitoring
+ * @cqm_rssi_high: Upper RSSI limit for CQM monitoring
+ * @cqm_rssi_last: Last RSSI reading for CQM monitoring
  */
 struct brcmf_cfg80211_vif {
        struct brcmf_if *ifp;
@@ -224,6 +227,9 @@ struct brcmf_cfg80211_vif {
        u16 mgmt_rx_reg;
        bool mbss;
        int is_11d;
+       s32 cqm_rssi_low;
+       s32 cqm_rssi_high;
+       s32 cqm_rssi_last;
 };
 
 /* association inform */
index 2e31cc1..ff2ef55 100644 (file)
@@ -753,6 +753,34 @@ struct brcmf_assoclist_le {
 };
 
 /**
+ * struct brcmf_rssi_be - RSSI threshold event format
+ *
+ * @rssi: receive signal strength (in dBm)
+ * @snr: signal-noise ratio
+ * @noise: noise (in dBm)
+ */
+struct brcmf_rssi_be {
+       __be32 rssi;
+       __be32 snr;
+       __be32 noise;
+};
+
+#define BRCMF_MAX_RSSI_LEVELS 8
+
+/**
+ * struct brcm_rssi_event_le - rssi_event IOVAR format
+ *
+ * @rate_limit_msec: RSSI event rate limit
+ * @rssi_level_num: number of supplied RSSI levels
+ * @rssi_levels: RSSI levels in ascending order
+ */
+struct brcmf_rssi_event_le {
+       __le32 rate_limit_msec;
+       s8 rssi_level_num;
+       s8 rssi_levels[BRCMF_MAX_RSSI_LEVELS];
+};
+
+/**
  * struct brcmf_wowl_wakeind_le - Wakeup indicators
  *     Note: note both fields contain same information.
  *