Skip to content

Commit

Permalink
wl1271: Add support for connection quality monitoring
Browse files Browse the repository at this point in the history
This patch will add support for connection quality monitoring by configuring
rssi triggers to the firmware, and enabling the firmware rssi trigger
functionality.

Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
Reviewed-by: Teemu Paasikivi <ext-teemu.3.paasikivi@nokia.com>
Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Juuso Oikarinen authored and John W. Linville committed Apr 9, 2010
1 parent e197281 commit 00236ae
Show file tree
Hide file tree
Showing 9 changed files with 228 additions and 97 deletions.
3 changes: 3 additions & 0 deletions drivers/net/wireless/wl12xx/wl1271.h
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,9 @@ struct wl1271 {
/* in dBm */
int power_level;

int rssi_thold;
int last_rssi_event;

struct wl1271_stats stats;
struct wl1271_debugfs debugfs;

Expand Down
71 changes: 71 additions & 0 deletions drivers/net/wireless/wl12xx/wl1271_acx.c
Original file line number Diff line number Diff line change
Expand Up @@ -1165,6 +1165,7 @@ int wl1271_acx_keep_alive_mode(struct wl1271 *wl, bool enable)
kfree(acx);
return ret;
}

int wl1271_acx_keep_alive_config(struct wl1271 *wl, u8 index, u8 tpl_valid)
{
struct wl1271_acx_keep_alive_config *acx = NULL;
Expand Down Expand Up @@ -1194,3 +1195,73 @@ int wl1271_acx_keep_alive_config(struct wl1271 *wl, u8 index, u8 tpl_valid)
kfree(acx);
return ret;
}

int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, bool enable,
s16 thold, u8 hyst)
{
struct wl1271_acx_rssi_snr_trigger *acx = NULL;
int ret = 0;

wl1271_debug(DEBUG_ACX, "acx rssi snr trigger");

acx = kzalloc(sizeof(*acx), GFP_KERNEL);
if (!acx) {
ret = -ENOMEM;
goto out;
}

wl->last_rssi_event = -1;

acx->pacing = cpu_to_le16(wl->conf.roam_trigger.trigger_pacing);
acx->metric = WL1271_ACX_TRIG_METRIC_RSSI_BEACON;
acx->type = WL1271_ACX_TRIG_TYPE_EDGE;
if (enable)
acx->enable = WL1271_ACX_TRIG_ENABLE;
else
acx->enable = WL1271_ACX_TRIG_DISABLE;

acx->index = WL1271_ACX_TRIG_IDX_RSSI;
acx->dir = WL1271_ACX_TRIG_DIR_BIDIR;
acx->threshold = cpu_to_le16(thold);
acx->hysteresis = hyst;

ret = wl1271_cmd_configure(wl, ACX_RSSI_SNR_TRIGGER, acx, sizeof(*acx));
if (ret < 0) {
wl1271_warning("acx rssi snr trigger setting failed: %d", ret);
goto out;
}

out:
kfree(acx);
return ret;
}

int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl)
{
struct wl1271_acx_rssi_snr_avg_weights *acx = NULL;
struct conf_roam_trigger_settings *c = &wl->conf.roam_trigger;
int ret = 0;

wl1271_debug(DEBUG_ACX, "acx rssi snr avg weights");

acx = kzalloc(sizeof(*acx), GFP_KERNEL);
if (!acx) {
ret = -ENOMEM;
goto out;
}

acx->rssi_beacon = c->avg_weight_rssi_beacon;
acx->rssi_data = c->avg_weight_rssi_data;
acx->snr_beacon = c->avg_weight_snr_beacon;
acx->snr_data = c->avg_weight_snr_data;

ret = wl1271_cmd_configure(wl, ACX_RSSI_SNR_WEIGHTS, acx, sizeof(*acx));
if (ret < 0) {
wl1271_warning("acx rssi snr trigger weights failed: %d", ret);
goto out;
}

out:
kfree(acx);
return ret;
}
56 changes: 55 additions & 1 deletion drivers/net/wireless/wl12xx/wl1271_acx.h
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,57 @@ struct wl1271_acx_keep_alive_config {
u8 padding;
} __attribute__ ((packed));

enum {
WL1271_ACX_TRIG_TYPE_LEVEL = 0,
WL1271_ACX_TRIG_TYPE_EDGE,
};

enum {
WL1271_ACX_TRIG_DIR_LOW = 0,
WL1271_ACX_TRIG_DIR_HIGH,
WL1271_ACX_TRIG_DIR_BIDIR,
};

enum {
WL1271_ACX_TRIG_ENABLE = 1,
WL1271_ACX_TRIG_DISABLE,
};

enum {
WL1271_ACX_TRIG_METRIC_RSSI_BEACON = 0,
WL1271_ACX_TRIG_METRIC_RSSI_DATA,
WL1271_ACX_TRIG_METRIC_SNR_BEACON,
WL1271_ACX_TRIG_METRIC_SNR_DATA,
};

enum {
WL1271_ACX_TRIG_IDX_RSSI = 0,
WL1271_ACX_TRIG_COUNT = 8,
};

struct wl1271_acx_rssi_snr_trigger {
struct acx_header header;

__le16 threshold;
__le16 pacing; /* 0 - 60000 ms */
u8 metric;
u8 type;
u8 dir;
u8 hysteresis;
u8 index;
u8 enable;
u8 padding[2];
};

struct wl1271_acx_rssi_snr_avg_weights {
struct acx_header header;

u8 rssi_beacon;
u8 rssi_data;
u8 snr_beacon;
u8 snr_data;
};

enum {
ACX_WAKE_UP_CONDITIONS = 0x0002,
ACX_MEM_CFG = 0x0003,
Expand Down Expand Up @@ -990,7 +1041,7 @@ enum {
ACX_FRAG_CFG = 0x004F,
ACX_BET_ENABLE = 0x0050,
ACX_RSSI_SNR_TRIGGER = 0x0051,
ACX_RSSI_SNR_WEIGHTS = 0x0051,
ACX_RSSI_SNR_WEIGHTS = 0x0052,
ACX_KEEP_ALIVE_MODE = 0x0053,
ACX_SET_KEEP_ALIVE_CONFIG = 0x0054,
ACX_BA_SESSION_RESPONDER_POLICY = 0x0055,
Expand Down Expand Up @@ -1060,5 +1111,8 @@ int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, u8 *address,
int wl1271_acx_pm_config(struct wl1271 *wl);
int wl1271_acx_keep_alive_mode(struct wl1271 *wl, bool enable);
int wl1271_acx_keep_alive_config(struct wl1271 *wl, u8 index, u8 tpl_valid);
int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, bool enable,
s16 thold, u8 hyst);
int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl);

#endif /* __WL1271_ACX_H__ */
3 changes: 2 additions & 1 deletion drivers/net/wireless/wl12xx/wl1271_boot.c
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,8 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
SCAN_COMPLETE_EVENT_ID |
PS_REPORT_EVENT_ID |
JOIN_EVENT_COMPLETE_ID |
DISCONNECT_EVENT_COMPLETE_ID;
DISCONNECT_EVENT_COMPLETE_ID |
RSSI_SNR_TRIGGER_0_EVENT_ID;

ret = wl1271_event_unmask(wl);
if (ret < 0) {
Expand Down
103 changes: 38 additions & 65 deletions drivers/net/wireless/wl12xx/wl1271_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -756,65 +756,6 @@ enum {
CONF_TRIG_EVENT_DIR_BIDIR
};


struct conf_sig_trigger {
/*
* The RSSI / SNR threshold value.
*
* FIXME: what is the range?
*/
s16 threshold;

/*
* Minimum delay between two trigger events for this trigger in ms.
*
* Range: 0 - 60000
*/
u16 pacing;

/*
* The measurement data source for this trigger.
*
* Range: CONF_TRIG_METRIC_*
*/
u8 metric;

/*
* The trigger type of this trigger.
*
* Range: CONF_TRIG_EVENT_TYPE_*
*/
u8 type;

/*
* The direction of the trigger.
*
* Range: CONF_TRIG_EVENT_DIR_*
*/
u8 direction;

/*
* Hysteresis range of the trigger around the threshold (in dB)
*
* Range: u8
*/
u8 hysteresis;

/*
* Index of the trigger rule.
*
* Range: 0 - CONF_MAX_RSSI_SNR_TRIGGERS-1
*/
u8 index;

/*
* Enable / disable this rule (to use for clearing rules.)
*
* Range: 1 - Enabled, 2 - Not enabled
*/
u8 enable;
};

struct conf_sig_weights {

/*
Expand Down Expand Up @@ -932,12 +873,6 @@ struct conf_conn_settings {
*/
u8 ps_poll_threshold;

/*
* Configuration of signal (rssi/snr) triggers.
*/
u8 sig_trigger_count;
struct conf_sig_trigger sig_trigger[CONF_MAX_RSSI_SNR_TRIGGERS];

/*
* Configuration of signal average weights.
*/
Expand Down Expand Up @@ -1045,6 +980,43 @@ struct conf_pm_config_settings {
bool host_fast_wakeup_support;
};

struct conf_roam_trigger_settings {
/*
* The minimum interval between two trigger events.
*
* Range: 0 - 60000 ms
*/
u16 trigger_pacing;

/*
* The weight for rssi/beacon average calculation
*
* Range: 0 - 255
*/
u8 avg_weight_rssi_beacon;

/*
* The weight for rssi/data frame average calculation
*
* Range: 0 - 255
*/
u8 avg_weight_rssi_data;

/*
* The weight for snr/beacon average calculation
*
* Range: 0 - 255
*/
u8 avg_weight_snr_beacon;

/*
* The weight for snr/data frame average calculation
*
* Range: 0 - 255
*/
u8 avg_weight_snr_data;
};

struct conf_drv_settings {
struct conf_sg_settings sg;
struct conf_rx_settings rx;
Expand All @@ -1053,6 +1025,7 @@ struct conf_drv_settings {
struct conf_init_settings init;
struct conf_itrim_settings itrim;
struct conf_pm_config_settings pm_config;
struct conf_roam_trigger_settings roam_trigger;
};

#endif
24 changes: 24 additions & 0 deletions drivers/net/wireless/wl12xx/wl1271_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,24 @@ static int wl1271_event_ps_report(struct wl1271 *wl,
return ret;
}

static void wl1271_event_rssi_trigger(struct wl1271 *wl,
struct event_mailbox *mbox)
{
enum nl80211_cqm_rssi_threshold_event event;
s8 metric = mbox->rssi_snr_trigger_metric[0];

wl1271_debug(DEBUG_EVENT, "RSSI trigger metric: %d", metric);

if (metric <= wl->rssi_thold)
event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
else
event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;

if (event != wl->last_rssi_event)
ieee80211_cqm_rssi_notify(wl->vif, event, GFP_KERNEL);
wl->last_rssi_event = event;
}

static void wl1271_event_mbox_dump(struct event_mailbox *mbox)
{
wl1271_debug(DEBUG_EVENT, "MBOX DUMP:");
Expand Down Expand Up @@ -173,6 +191,12 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
return ret;
}

if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) {
wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT");
if (wl->vif)
wl1271_event_rssi_trigger(wl, mbox);
}

if (wl->vif && beacon_loss)
ieee80211_connection_loss(wl->vif);

Expand Down
8 changes: 8 additions & 0 deletions drivers/net/wireless/wl12xx/wl1271_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@
*/

enum {
RSSI_SNR_TRIGGER_0_EVENT_ID = BIT(0),
RSSI_SNR_TRIGGER_1_EVENT_ID = BIT(1),
RSSI_SNR_TRIGGER_2_EVENT_ID = BIT(2),
RSSI_SNR_TRIGGER_3_EVENT_ID = BIT(3),
RSSI_SNR_TRIGGER_4_EVENT_ID = BIT(4),
RSSI_SNR_TRIGGER_5_EVENT_ID = BIT(5),
RSSI_SNR_TRIGGER_6_EVENT_ID = BIT(6),
RSSI_SNR_TRIGGER_7_EVENT_ID = BIT(7),
MEASUREMENT_START_EVENT_ID = BIT(8),
MEASUREMENT_COMPLETE_EVENT_ID = BIT(9),
SCAN_COMPLETE_EVENT_ID = BIT(10),
Expand Down
5 changes: 5 additions & 0 deletions drivers/net/wireless/wl12xx/wl1271_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,11 @@ int wl1271_hw_init(struct wl1271 *wl)
if (ret < 0)
goto out_free_memmap;

/* Configure rssi/snr averaging weights */
ret = wl1271_acx_rssi_snr_avg_weights(wl);
if (ret < 0)
goto out_free_memmap;

return 0;

out_free_memmap:
Expand Down
Loading

0 comments on commit 00236ae

Please sign in to comment.