diff mbox series

brcmfmac: add support for CQM RSSI notifications

Message ID 20210112111253.4176340-1-alsi@bang-olufsen.dk
State Superseded
Headers show
Series brcmfmac: add support for CQM RSSI notifications | expand

Commit Message

Alvin Šipraga Jan. 12, 2021, 11:13 a.m. UTC
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>

---
 .../broadcom/brcm80211/brcmfmac/cfg80211.c    | 82 +++++++++++++++++++
 .../broadcom/brcm80211/brcmfmac/cfg80211.h    |  6 ++
 .../broadcom/brcm80211/brcmfmac/fwil_types.h  | 28 +++++++
 3 files changed, 116 insertions(+)

-- 
2.29.2

Comments

Alvin Šipraga Jan. 14, 2021, 3:04 p.m. UTC | #1
Hi Arend,

Thanks for your comments - I'll prepare a v2 patch. Some 
comments/justification inline below...

On 1/14/21 2:39 PM, Arend van Spriel wrote:
> On 12-01-2021 12:13, 'Alvin Šipraga' via BRCM80211-DEV-LIST,PDL wrote:

>> 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.

> 

> Needs a bit of rework. See my comments below...

> 

>> Signed-off-by: Alvin Šipraga <alsi@bang-olufsen.dk>

>> ---

>>   .../broadcom/brcm80211/brcmfmac/cfg80211.c    | 82 +++++++++++++++++++

>>   .../broadcom/brcm80211/brcmfmac/cfg80211.h    |  6 ++

>>   .../broadcom/brcm80211/brcmfmac/fwil_types.h  | 28 +++++++

>>   3 files changed, 116 insertions(+)

>>

>> diff --git 

>> a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c 

>> b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c

>> index 0ee421f30aa2..21b53bd27f7f 100644

>> --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c

>> +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c

>> @@ -5196,6 +5196,41 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, 

>> struct wireless_dev *wdev,

>>       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) {

>> +        struct brcmf_rssi_event_le config = {

>> +            .rate_limit_msec = cpu_to_le32(0),

>> +            .rssi_level_num = 2,

>> +            .rssi_levels = {

>> +                max_t(s32, rssi_low, S8_MIN),

>> +                min_t(s32, rssi_high, S8_MAX),

> 

> The type should be s8 iso s32.


The idea was to clamp out-of-bounds rssi_low/rssi_high (s32) values to 
S8_MIN/S8_MAX rather than casting an s32 to s8 and hoping for the best. 
But since max_t(s8, x, S8_MIN) will always equal (s8)x, I might as well 
just do:

     .rssi_levels = {
         rssi_low,
         min_t(s8, rssi_high, S8_MAX - 1),
         S8_MAX,
     },

I am inclined to keep it as it was, i.e.:

     .rssi_levels = {
         max_t(s32, rssi_low, S8_MIN),
         min_t(s32, rssi_high, S8_MAX - 1),
         S8_MAX,
     },

What do you think?

> 

>> +            },

>> +        };

> 

> What is the expectation here? The firmware behavior for the above is 

> that you will get an event when the rssi is lower or equal to the level 

> and the previous rssi event was lower or equal to a different level. 


I think I see what you mean - you're concerned about not getting the 
"high" event because an RSSI greater than rssi_high will not be less 
than another level? If the behaviour of the firmware is as you describe 
then I will add an additional level like you suggested to cover this case.

> There is another event RSSI_LQM that would be a better fit although that 

> is not available in every firmware image ("rssi_mon" firmware feature).

> 

> Another option would be to add a level, ie.:

> 

>      .rssi_levels = {

>          max_t(s8, rssi_low, S8_MIN),

>          min_t(s8, rssi_high, 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 +5537,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,

>> @@ -6137,6 +6173,49 @@ 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)

> 

> align to the opening brace in the line above.


Is it not correct already? The 's' in struct is in the same column as 
the 'c' in const. I think it just looks wrong because of the '+' in the 
first column of the diff.

> 

>> +{

>> +    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);

> 

> Bit surprised to see this is BE, but it appears to be correct.

> 

>> +    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);

>> +

>> +    if (rssi != last) {

> 

> Given the firmware behavior I don't think you need this check.


OK.

> 

>> +        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;

>
diff mbox series

Patch

diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 0ee421f30aa2..21b53bd27f7f 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -5196,6 +5196,41 @@  brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 	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) {
+		struct brcmf_rssi_event_le config = {
+			.rate_limit_msec = cpu_to_le32(0),
+			.rssi_level_num = 2,
+			.rssi_levels = {
+				max_t(s32, rssi_low, S8_MIN),
+				min_t(s32, rssi_high, 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 +5537,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,
@@ -6137,6 +6173,49 @@  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);
+
+	if (rssi != 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)
 {
@@ -6235,6 +6314,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)
@@ -7169,6 +7249,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;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
index 17817cdb5de2..e90a30808c22 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
@@ -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 */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
index 2e31cc10c195..ff2ef557f0ea 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
@@ -752,6 +752,34 @@  struct brcmf_assoclist_le {
 	u8 mac[BRCMF_MAX_ASSOCLIST][ETH_ALEN];
 };
 
+/**
+ * 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.