Commit 8964e492 authored by David Gnedt's avatar David Gnedt Committed by John W. Linville

wl1251: implement connection quality monitoring

Implement connection quality monitoring similar to the wl1271 driver.
It triggers ieee80211_cqm_rssi_notify with the corresponding event when
RSSI drops blow RSSI threshold or rises again above the RSSI threshold.
It should be noted that wl1251 doesn't support RSSI hysteresis, instead it
uses RSSI averageing and delays events until a certain count of frames
proved RSSI change.
Signed-off-by: default avatarDavid Gnedt <david.gnedt@davizone.at>
Acked-by: default avatarKalle Valo <kvalo@adurom.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent c3e334d2
...@@ -776,6 +776,31 @@ int wl1251_acx_event_mbox_mask(struct wl1251 *wl, u32 event_mask) ...@@ -776,6 +776,31 @@ int wl1251_acx_event_mbox_mask(struct wl1251 *wl, u32 event_mask)
return ret; return ret;
} }
int wl1251_acx_low_rssi(struct wl1251 *wl, s8 threshold, u8 weight,
u8 depth, enum wl1251_acx_low_rssi_type type)
{
struct acx_low_rssi *rssi;
int ret;
wl1251_debug(DEBUG_ACX, "acx low rssi");
rssi = kzalloc(sizeof(*rssi), GFP_KERNEL);
if (!rssi)
return -ENOMEM;
rssi->threshold = threshold;
rssi->weight = weight;
rssi->depth = depth;
rssi->type = type;
ret = wl1251_cmd_configure(wl, ACX_LOW_RSSI, rssi, sizeof(*rssi));
if (ret < 0)
wl1251_warning("failed to set low rssi threshold: %d", ret);
kfree(rssi);
return ret;
}
int wl1251_acx_set_preamble(struct wl1251 *wl, enum acx_preamble_type preamble) int wl1251_acx_set_preamble(struct wl1251 *wl, enum acx_preamble_type preamble)
{ {
struct acx_preamble *acx; struct acx_preamble *acx;
......
...@@ -399,6 +399,49 @@ struct acx_rts_threshold { ...@@ -399,6 +399,49 @@ struct acx_rts_threshold {
u8 pad[2]; u8 pad[2];
} __packed; } __packed;
enum wl1251_acx_low_rssi_type {
/*
* The event is a "Level" indication which keeps triggering
* as long as the average RSSI is below the threshold.
*/
WL1251_ACX_LOW_RSSI_TYPE_LEVEL = 0,
/*
* The event is an "Edge" indication which triggers
* only when the RSSI threshold is crossed from above.
*/
WL1251_ACX_LOW_RSSI_TYPE_EDGE = 1,
};
struct acx_low_rssi {
struct acx_header header;
/*
* The threshold (in dBm) below (or above after low rssi
* indication) which the firmware generates an interrupt to the
* host. This parameter is signed.
*/
s8 threshold;
/*
* The weight of the current RSSI sample, before adding the new
* sample, that is used to calculate the average RSSI.
*/
u8 weight;
/*
* The number of Beacons/Probe response frames that will be
* received before issuing the Low or Regained RSSI event.
*/
u8 depth;
/*
* Configures how the Low RSSI Event is triggered. Refer to
* enum wl1251_acx_low_rssi_type for more.
*/
u8 type;
} __packed;
struct acx_beacon_filter_option { struct acx_beacon_filter_option {
struct acx_header header; struct acx_header header;
...@@ -1418,6 +1461,8 @@ int wl1251_acx_cca_threshold(struct wl1251 *wl); ...@@ -1418,6 +1461,8 @@ int wl1251_acx_cca_threshold(struct wl1251 *wl);
int wl1251_acx_bcn_dtim_options(struct wl1251 *wl); int wl1251_acx_bcn_dtim_options(struct wl1251 *wl);
int wl1251_acx_aid(struct wl1251 *wl, u16 aid); int wl1251_acx_aid(struct wl1251 *wl, u16 aid);
int wl1251_acx_event_mbox_mask(struct wl1251 *wl, u32 event_mask); int wl1251_acx_event_mbox_mask(struct wl1251 *wl, u32 event_mask);
int wl1251_acx_low_rssi(struct wl1251 *wl, s8 threshold, u8 weight,
u8 depth, enum wl1251_acx_low_rssi_type type);
int wl1251_acx_set_preamble(struct wl1251 *wl, enum acx_preamble_type preamble); int wl1251_acx_set_preamble(struct wl1251 *wl, enum acx_preamble_type preamble);
int wl1251_acx_cts_protect(struct wl1251 *wl, int wl1251_acx_cts_protect(struct wl1251 *wl,
enum acx_ctsprotect_type ctsprotect); enum acx_ctsprotect_type ctsprotect);
......
...@@ -90,6 +90,24 @@ static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox) ...@@ -90,6 +90,24 @@ static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox)
} }
} }
if (wl->vif && wl->rssi_thold) {
if (vector & ROAMING_TRIGGER_LOW_RSSI_EVENT_ID) {
wl1251_debug(DEBUG_EVENT,
"ROAMING_TRIGGER_LOW_RSSI_EVENT");
ieee80211_cqm_rssi_notify(wl->vif,
NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
GFP_KERNEL);
}
if (vector & ROAMING_TRIGGER_REGAINED_RSSI_EVENT_ID) {
wl1251_debug(DEBUG_EVENT,
"ROAMING_TRIGGER_REGAINED_RSSI_EVENT");
ieee80211_cqm_rssi_notify(wl->vif,
NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
GFP_KERNEL);
}
}
return 0; return 0;
} }
......
...@@ -502,6 +502,7 @@ static void wl1251_op_stop(struct ieee80211_hw *hw) ...@@ -502,6 +502,7 @@ static void wl1251_op_stop(struct ieee80211_hw *hw)
wl->psm = 0; wl->psm = 0;
wl->tx_queue_stopped = false; wl->tx_queue_stopped = false;
wl->power_level = WL1251_DEFAULT_POWER_LEVEL; wl->power_level = WL1251_DEFAULT_POWER_LEVEL;
wl->rssi_thold = 0;
wl->channel = WL1251_DEFAULT_CHANNEL; wl->channel = WL1251_DEFAULT_CHANNEL;
wl1251_debugfs_reset(wl); wl1251_debugfs_reset(wl);
...@@ -959,6 +960,16 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, ...@@ -959,6 +960,16 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
if (ret < 0) if (ret < 0)
goto out; goto out;
if (changed & BSS_CHANGED_CQM) {
ret = wl1251_acx_low_rssi(wl, bss_conf->cqm_rssi_thold,
WL1251_DEFAULT_LOW_RSSI_WEIGHT,
WL1251_DEFAULT_LOW_RSSI_DEPTH,
WL1251_ACX_LOW_RSSI_TYPE_EDGE);
if (ret < 0)
goto out;
wl->rssi_thold = bss_conf->cqm_rssi_thold;
}
if (changed & BSS_CHANGED_BSSID) { if (changed & BSS_CHANGED_BSSID) {
memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
...@@ -1310,7 +1321,8 @@ int wl1251_init_ieee80211(struct wl1251 *wl) ...@@ -1310,7 +1321,8 @@ int wl1251_init_ieee80211(struct wl1251 *wl)
wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_BEACON_FILTER | IEEE80211_HW_BEACON_FILTER |
IEEE80211_HW_SUPPORTS_UAPSD; IEEE80211_HW_SUPPORTS_UAPSD |
IEEE80211_HW_SUPPORTS_CQM_RSSI;
wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
wl->hw->wiphy->max_scan_ssids = 1; wl->hw->wiphy->max_scan_ssids = 1;
...@@ -1374,6 +1386,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void) ...@@ -1374,6 +1386,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void)
wl->psm_requested = false; wl->psm_requested = false;
wl->tx_queue_stopped = false; wl->tx_queue_stopped = false;
wl->power_level = WL1251_DEFAULT_POWER_LEVEL; wl->power_level = WL1251_DEFAULT_POWER_LEVEL;
wl->rssi_thold = 0;
wl->beacon_int = WL1251_DEFAULT_BEACON_INT; wl->beacon_int = WL1251_DEFAULT_BEACON_INT;
wl->dtim_period = WL1251_DEFAULT_DTIM_PERIOD; wl->dtim_period = WL1251_DEFAULT_DTIM_PERIOD;
wl->vif = NULL; wl->vif = NULL;
......
...@@ -370,6 +370,8 @@ struct wl1251 { ...@@ -370,6 +370,8 @@ struct wl1251 {
/* in dBm */ /* in dBm */
int power_level; int power_level;
int rssi_thold;
struct wl1251_stats stats; struct wl1251_stats stats;
struct wl1251_debugfs debugfs; struct wl1251_debugfs debugfs;
...@@ -433,4 +435,7 @@ void wl1251_disable_interrupts(struct wl1251 *wl); ...@@ -433,4 +435,7 @@ void wl1251_disable_interrupts(struct wl1251 *wl);
#define WL1251_PART_WORK_REG_START REGISTERS_BASE #define WL1251_PART_WORK_REG_START REGISTERS_BASE
#define WL1251_PART_WORK_REG_SIZE REGISTERS_WORK_SIZE #define WL1251_PART_WORK_REG_SIZE REGISTERS_WORK_SIZE
#define WL1251_DEFAULT_LOW_RSSI_WEIGHT 10
#define WL1251_DEFAULT_LOW_RSSI_DEPTH 10
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment