brcmfmac: add support for CQM RSSI notifications
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
This commit is contained in:
parent
1299616023
commit
7dd56ea45a
3 changed files with 121 additions and 0 deletions
|
@ -5196,6 +5196,48 @@ exit:
|
||||||
return err;
|
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
|
static int
|
||||||
brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
|
brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
|
||||||
|
@ -5502,6 +5544,7 @@ static struct cfg80211_ops brcmf_cfg80211_ops = {
|
||||||
.update_mgmt_frame_registrations =
|
.update_mgmt_frame_registrations =
|
||||||
brcmf_cfg80211_update_mgmt_frame_registrations,
|
brcmf_cfg80211_update_mgmt_frame_registrations,
|
||||||
.mgmt_tx = brcmf_cfg80211_mgmt_tx,
|
.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,
|
.remain_on_channel = brcmf_p2p_remain_on_channel,
|
||||||
.cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
|
.cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
|
||||||
.get_channel = brcmf_cfg80211_get_channel,
|
.get_channel = brcmf_cfg80211_get_channel,
|
||||||
|
@ -6140,6 +6183,47 @@ brcmf_notify_mic_status(struct brcmf_if *ifp,
|
||||||
return 0;
|
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,
|
static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
|
||||||
const struct brcmf_event_msg *e, void *data)
|
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_p2p_notify_action_tx_complete);
|
||||||
brcmf_fweh_register(cfg->pub, BRCMF_E_PSK_SUP,
|
brcmf_fweh_register(cfg->pub, BRCMF_E_PSK_SUP,
|
||||||
brcmf_notify_connect_status);
|
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)
|
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,
|
wiphy_ext_feature_set(wiphy,
|
||||||
NL80211_EXT_FEATURE_DFS_OFFLOAD);
|
NL80211_EXT_FEATURE_DFS_OFFLOAD);
|
||||||
|
|
||||||
|
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
|
||||||
|
|
||||||
wiphy_read_of_freq_limits(wiphy);
|
wiphy_read_of_freq_limits(wiphy);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -213,6 +213,9 @@ struct vif_saved_ie {
|
||||||
* @list: linked list.
|
* @list: linked list.
|
||||||
* @mgmt_rx_reg: registered rx mgmt frame types.
|
* @mgmt_rx_reg: registered rx mgmt frame types.
|
||||||
* @mbss: Multiple BSS type, set if not first AP (not relevant for P2P).
|
* @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_cfg80211_vif {
|
||||||
struct brcmf_if *ifp;
|
struct brcmf_if *ifp;
|
||||||
|
@ -224,6 +227,9 @@ struct brcmf_cfg80211_vif {
|
||||||
u16 mgmt_rx_reg;
|
u16 mgmt_rx_reg;
|
||||||
bool mbss;
|
bool mbss;
|
||||||
int is_11d;
|
int is_11d;
|
||||||
|
s32 cqm_rssi_low;
|
||||||
|
s32 cqm_rssi_high;
|
||||||
|
s32 cqm_rssi_last;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* association inform */
|
/* association inform */
|
||||||
|
|
|
@ -752,6 +752,34 @@ struct brcmf_assoclist_le {
|
||||||
u8 mac[BRCMF_MAX_ASSOCLIST][ETH_ALEN];
|
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
|
* struct brcmf_wowl_wakeind_le - Wakeup indicators
|
||||||
* Note: note both fields contain same information.
|
* Note: note both fields contain same information.
|
||||||
|
|
Loading…
Add table
Reference in a new issue