From 1b00f546fc4271d94c1bccb1955ce64d9ace1000 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Thu, 18 Mar 2010 12:26:30 +0200 Subject: wl1271: Add proper WLAN-BT co-ex configuration, and enable co-ex. Add configuration values for the varous WLAN-BT co-ex configuration parameters, and finally enable WLAN-BT co-ex. Based on preliminary measurements, it appears the co-ex feature is not increasing WLAN power consumption, if BT is not activated. Signed-off-by: Juuso Oikarinen Reviewed-by: Teemu Paasikivi Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271_init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl1271_init.c') diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c index 86c30a86a456..d68445c9d333 100644 --- a/drivers/net/wireless/wl12xx/wl1271_init.c +++ b/drivers/net/wireless/wl12xx/wl1271_init.c @@ -160,11 +160,11 @@ int wl1271_init_pta(struct wl1271 *wl) { int ret; - ret = wl1271_acx_sg_enable(wl); + ret = wl1271_acx_sg_cfg(wl); if (ret < 0) return ret; - ret = wl1271_acx_sg_cfg(wl); + ret = wl1271_acx_sg_enable(wl); if (ret < 0) return ret; -- cgit v1.2.3 From 7fc3a8647d2eaa8bc2f7ac7e9baff55199da7be6 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Thu, 18 Mar 2010 12:26:32 +0200 Subject: wl1271: Add sysfs file to control BT co-ex state Add a sysfs file to control the state of the BT co-ex (enable or disable it.) By default, the BT co-ex is enabled. Signed-off-by: Juuso Oikarinen Reviewed-by: Teemu Paasikivi Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271.h | 2 + drivers/net/wireless/wl12xx/wl1271_acx.c | 7 ++- drivers/net/wireless/wl12xx/wl1271_acx.h | 2 +- drivers/net/wireless/wl12xx/wl1271_init.c | 2 +- drivers/net/wireless/wl12xx/wl1271_main.c | 72 +++++++++++++++++++++++++++++++ 5 files changed, 81 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl1271_init.c') diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index 7397999deb19..23a0c7e22939 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -484,6 +484,8 @@ struct wl1271 { /* Current chipset configuration */ struct conf_drv_settings conf; + bool sg_enabled; + struct list_head list; }; diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c index e7c22d3c75ac..7e337cea9905 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.c +++ b/drivers/net/wireless/wl12xx/wl1271_acx.c @@ -534,7 +534,7 @@ out: } -int wl1271_acx_sg_enable(struct wl1271 *wl) +int wl1271_acx_sg_enable(struct wl1271 *wl, bool enable) { struct acx_bt_wlan_coex *pta; int ret; @@ -547,7 +547,10 @@ int wl1271_acx_sg_enable(struct wl1271 *wl) goto out; } - pta->enable = wl->conf.sg.state; + if (enable) + pta->enable = wl->conf.sg.state; + else + pta->enable = CONF_SG_DISABLE; ret = wl1271_cmd_configure(wl, ACX_SG_ENABLE, pta, sizeof(*pta)); if (ret < 0) { diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h index dee3c06a717c..8e5870fa9609 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.h +++ b/drivers/net/wireless/wl12xx/wl1271_acx.h @@ -1005,7 +1005,7 @@ int wl1271_acx_dco_itrim_params(struct wl1271 *wl); int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter); int wl1271_acx_beacon_filter_table(struct wl1271 *wl); int wl1271_acx_conn_monit_params(struct wl1271 *wl); -int wl1271_acx_sg_enable(struct wl1271 *wl); +int wl1271_acx_sg_enable(struct wl1271 *wl, bool enable); int wl1271_acx_sg_cfg(struct wl1271 *wl); int wl1271_acx_cca_threshold(struct wl1271 *wl); int wl1271_acx_bcn_dtim_options(struct wl1271 *wl); diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c index d68445c9d333..d9335fc2a575 100644 --- a/drivers/net/wireless/wl12xx/wl1271_init.c +++ b/drivers/net/wireless/wl12xx/wl1271_init.c @@ -164,7 +164,7 @@ int wl1271_init_pta(struct wl1271 *wl) if (ret < 0) return ret; - ret = wl1271_acx_sg_enable(wl); + ret = wl1271_acx_sg_enable(wl, wl->sg_enabled); if (ret < 0) return ret; diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index ad9c49111678..abf9f22ee968 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -1982,6 +1982,68 @@ static const struct ieee80211_ops wl1271_ops = { CFG80211_TESTMODE_CMD(wl1271_tm_cmd) }; +static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct wl1271 *wl = dev_get_drvdata(dev); + ssize_t len; + + /* FIXME: what's the maximum length of buf? page size?*/ + len = 500; + + mutex_lock(&wl->mutex); + len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n", + wl->sg_enabled); + mutex_unlock(&wl->mutex); + + return len; + +} + +static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wl1271 *wl = dev_get_drvdata(dev); + unsigned long res; + int ret; + + ret = strict_strtoul(buf, 10, &res); + + if (ret < 0) { + wl1271_warning("incorrect value written to bt_coex_mode"); + return count; + } + + mutex_lock(&wl->mutex); + + res = !!res; + + if (res == wl->sg_enabled) + goto out; + + wl->sg_enabled = res; + + if (wl->state == WL1271_STATE_OFF) + goto out; + + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + + wl1271_acx_sg_enable(wl, wl->sg_enabled); + wl1271_ps_elp_sleep(wl); + + out: + mutex_unlock(&wl->mutex); + return count; +} + +static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR, + wl1271_sysfs_show_bt_coex_state, + wl1271_sysfs_store_bt_coex_state); + int wl1271_register_hw(struct wl1271 *wl) { int ret; @@ -2073,6 +2135,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->band = IEEE80211_BAND_2GHZ; wl->vif = NULL; wl->flags = 0; + wl->sg_enabled = true; for (i = 0; i < ACX_TX_DESCRIPTORS; i++) wl->tx_frames[i] = NULL; @@ -2095,9 +2158,18 @@ struct ieee80211_hw *wl1271_alloc_hw(void) } dev_set_drvdata(&wl1271_device.dev, wl); + /* Create sysfs file to control bt coex state */ + ret = device_create_file(&wl1271_device.dev, &dev_attr_bt_coex_state); + if (ret < 0) { + wl1271_error("failed to create sysfs file bt_coex_state"); + goto err_platform; + } return hw; +err_platform: + platform_device_unregister(&wl1271_device); + err_hw: ieee80211_unregister_hw(wl->hw); -- cgit v1.2.3 From 6ccbb92ead9379d7de2cc25cd950d15a8d22e0c9 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Fri, 26 Mar 2010 12:53:23 +0200 Subject: wl1271: Disable connection monitoring while not associated The wl1271 does not support disabling the connection monitor feature. Perform the next best thing by increasing the beacon-loss threshold and BSS_LOSE event timeout to the maximum values. This is needed, because we really don't want any random probe-requests during scanning or especially while in ad-hoc mode and not beaconing. Signed-off-by: Juuso Oikarinen Reviewed-by: Teemu Paasikivi Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271_acx.c | 18 ++++++++++++++---- drivers/net/wireless/wl12xx/wl1271_acx.h | 2 +- drivers/net/wireless/wl12xx/wl1271_init.c | 2 +- drivers/net/wireless/wl12xx/wl1271_main.c | 12 +++++++++++- 4 files changed, 27 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl1271_init.c') diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c index 7e337cea9905..e7d11811a907 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.c +++ b/drivers/net/wireless/wl12xx/wl1271_acx.c @@ -504,12 +504,17 @@ out: return ret; } -int wl1271_acx_conn_monit_params(struct wl1271 *wl) +#define ACX_CONN_MONIT_DISABLE_VALUE 0xffffffff + +int wl1271_acx_conn_monit_params(struct wl1271 *wl, bool enable) { struct acx_conn_monit_params *acx; + u32 threshold = ACX_CONN_MONIT_DISABLE_VALUE; + u32 timeout = ACX_CONN_MONIT_DISABLE_VALUE; int ret; - wl1271_debug(DEBUG_ACX, "acx connection monitor parameters"); + wl1271_debug(DEBUG_ACX, "acx connection monitor parameters: %s", + enable ? "enabled" : "disabled"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { @@ -517,8 +522,13 @@ int wl1271_acx_conn_monit_params(struct wl1271 *wl) goto out; } - acx->synch_fail_thold = cpu_to_le32(wl->conf.conn.synch_fail_thold); - acx->bss_lose_timeout = cpu_to_le32(wl->conf.conn.bss_lose_timeout); + if (enable) { + threshold = wl->conf.conn.synch_fail_thold; + timeout = wl->conf.conn.bss_lose_timeout; + } + + acx->synch_fail_thold = cpu_to_le32(threshold); + acx->bss_lose_timeout = cpu_to_le32(timeout); ret = wl1271_cmd_configure(wl, ACX_CONN_MONIT_PARAMS, acx, sizeof(*acx)); diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h index 8e5870fa9609..b6eb0c1b6c78 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.h +++ b/drivers/net/wireless/wl12xx/wl1271_acx.h @@ -1004,7 +1004,7 @@ int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold); int wl1271_acx_dco_itrim_params(struct wl1271 *wl); int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter); int wl1271_acx_beacon_filter_table(struct wl1271 *wl); -int wl1271_acx_conn_monit_params(struct wl1271 *wl); +int wl1271_acx_conn_monit_params(struct wl1271 *wl, bool enable); int wl1271_acx_sg_enable(struct wl1271 *wl, bool enable); int wl1271_acx_sg_cfg(struct wl1271 *wl); int wl1271_acx_cca_threshold(struct wl1271 *wl); diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c index d9335fc2a575..987978c3bce0 100644 --- a/drivers/net/wireless/wl12xx/wl1271_init.c +++ b/drivers/net/wireless/wl12xx/wl1271_init.c @@ -236,7 +236,7 @@ int wl1271_hw_init(struct wl1271 *wl) goto out_free_memmap; /* Initialize connection monitoring thresholds */ - ret = wl1271_acx_conn_monit_params(wl); + ret = wl1271_acx_conn_monit_params(wl, false); if (ret < 0) goto out_free_memmap; diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index 4a2f1d772adc..00e856c4a611 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -348,7 +348,7 @@ static int wl1271_plt_init(struct wl1271 *wl) goto out_free_memmap; /* Initialize connection monitoring thresholds */ - ret = wl1271_acx_conn_monit_params(wl); + ret = wl1271_acx_conn_monit_params(wl, false); if (ret < 0) goto out_free_memmap; @@ -1651,6 +1651,11 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, if (ret < 0) goto out_sleep; + /* enable the connection monitoring feature */ + ret = wl1271_acx_conn_monit_params(wl, true); + if (ret < 0) + goto out_sleep; + /* If we want to go in PSM but we're not there yet */ if (test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags) && !test_bit(WL1271_FLAG_PSM, &wl->flags)) { @@ -1663,6 +1668,11 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, /* use defaults when not associated */ clear_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags); wl->aid = 0; + + /* disable connection monitor features */ + ret = wl1271_acx_conn_monit_params(wl, false); + if (ret < 0) + goto out_sleep; } } -- cgit v1.2.3 From bfb24c9e16921f0e57fcec5180ffa20929832545 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Fri, 26 Mar 2010 12:53:31 +0200 Subject: wl1271: Add keep-alive frame template support Add support for keep-alive templates, which are indexed. Signed-off-by: Juuso Oikarinen Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271_cmd.c | 35 +++++++++++++++++++++++++------ drivers/net/wireless/wl12xx/wl1271_cmd.h | 8 ++++++- drivers/net/wireless/wl12xx/wl1271_init.c | 31 +++++++++++++++++++-------- drivers/net/wireless/wl12xx/wl1271_main.c | 4 ++-- 4 files changed, 60 insertions(+), 18 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl1271_init.c') diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c index 0dcb3c2afe07..0cb4cbb00395 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.c +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c @@ -698,7 +698,7 @@ out: } int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, - void *buf, size_t buf_len) + void *buf, size_t buf_len, int index) { struct wl1271_cmd_template_set *cmd; int ret = 0; @@ -719,6 +719,7 @@ int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, cmd->enabled_rates = cpu_to_le32(wl->conf.tx.rc_conf.enabled_rates); cmd->short_retry_limit = wl->conf.tx.rc_conf.short_retry_limit; cmd->long_retry_limit = wl->conf.tx.rc_conf.long_retry_limit; + cmd->index = index; if (buf) memcpy(cmd->template_data, buf, buf_len); @@ -755,7 +756,7 @@ int wl1271_cmd_build_null_data(struct wl1271 *wl) ptr = skb->data; } - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, ptr, size); + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, ptr, size, 0); out: dev_kfree_skb(skb); @@ -766,6 +767,28 @@ out: } +int wl1271_cmd_build_klv_null_data(struct wl1271 *wl) +{ + struct sk_buff *skb = NULL; + int ret = -ENOMEM; + + skb = ieee80211_nullfunc_get(wl->hw, wl->vif); + if (!skb) + goto out; + + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_KLV, + skb->data, skb->len, + CMD_TEMPL_KLV_IDX_NULL_DATA); + +out: + dev_kfree_skb(skb); + if (ret) + wl1271_warning("cmd build klv null data failed %d", ret); + + return ret; + +} + int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid) { struct sk_buff *skb; @@ -776,7 +799,7 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid) goto out; ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, skb->data, - skb->len); + skb->len, 0); out: dev_kfree_skb(skb); @@ -801,10 +824,10 @@ int wl1271_cmd_build_probe_req(struct wl1271 *wl, if (band == IEEE80211_BAND_2GHZ) ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, - skb->data, skb->len); + skb->data, skb->len, 0); else ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, - skb->data, skb->len); + skb->data, skb->len, 0); out: dev_kfree_skb(skb); @@ -829,7 +852,7 @@ int wl1271_build_qos_null_data(struct wl1271 *wl) template.qos_ctrl = cpu_to_le16(0); return wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, &template, - sizeof(template)); + sizeof(template), 0); } int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id) diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.h b/drivers/net/wireless/wl12xx/wl1271_cmd.h index bdd7a3d8ece6..e1131bc0d15c 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.h +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.h @@ -45,13 +45,14 @@ int wl1271_cmd_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, u8 active_scan, u8 high_prio, u8 band, u8 probe_requests); int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, - void *buf, size_t buf_len); + void *buf, size_t buf_len, int index); int wl1271_cmd_build_null_data(struct wl1271 *wl); int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid); int wl1271_cmd_build_probe_req(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, u8 band); int wl1271_build_qos_null_data(struct wl1271 *wl); +int wl1271_cmd_build_klv_null_data(struct wl1271 *wl); int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id); int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, u8 key_size, const u8 *key, const u8 *addr, @@ -101,6 +102,11 @@ enum wl1271_commands { #define MAX_CMD_PARAMS 572 +enum { + CMD_TEMPL_KLV_IDX_NULL_DATA = 0, + CMD_TEMPL_KLV_IDX_MAX = 4 +}; + enum cmd_templ { CMD_TEMPL_NULL_DATA = 0, CMD_TEMPL_BEACON, diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c index 987978c3bce0..0106663bc45b 100644 --- a/drivers/net/wireless/wl12xx/wl1271_init.c +++ b/drivers/net/wireless/wl12xx/wl1271_init.c @@ -51,50 +51,63 @@ static int wl1271_init_hwenc_config(struct wl1271 *wl) int wl1271_init_templates_config(struct wl1271 *wl) { - int ret; + int ret, i; /* send empty templates for fw memory reservation */ ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL, - sizeof(struct wl12xx_probe_req_template)); + sizeof(struct wl12xx_probe_req_template), + 0); if (ret < 0) return ret; if (wl1271_11a_enabled()) { + size_t size = sizeof(struct wl12xx_probe_req_template); ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, - NULL, - sizeof(struct wl12xx_probe_req_template)); + NULL, size, 0); if (ret < 0) return ret; } ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, NULL, - sizeof(struct wl12xx_null_data_template)); + sizeof(struct wl12xx_null_data_template), + 0); if (ret < 0) return ret; ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, NULL, - sizeof(struct wl12xx_ps_poll_template)); + sizeof(struct wl12xx_ps_poll_template), + 0); if (ret < 0) return ret; ret = wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, NULL, sizeof - (struct wl12xx_qos_null_data_template)); + (struct wl12xx_qos_null_data_template), + 0); if (ret < 0) return ret; ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PROBE_RESPONSE, NULL, sizeof - (struct wl12xx_probe_resp_template)); + (struct wl12xx_probe_resp_template), + 0); if (ret < 0) return ret; ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, NULL, sizeof - (struct wl12xx_beacon_template)); + (struct wl12xx_beacon_template), + 0); if (ret < 0) return ret; + for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) { + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_KLV, NULL, + WL1271_CMD_TEMPL_MAX_SIZE, i); + if (ret < 0) + return ret; + } + return 0; } diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index 2df00adcf903..afab52bec134 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -1596,7 +1596,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, wl1271_ssid_set(wl, beacon); ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, beacon->data, - beacon->len); + beacon->len, 0); if (ret < 0) { dev_kfree_skb(beacon); @@ -1611,7 +1611,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PROBE_RESPONSE, beacon->data, - beacon->len); + beacon->len, 0); dev_kfree_skb(beacon); if (ret < 0) goto out_sleep; -- cgit v1.2.3 From c18995540cc4d2c84d130581b8b6720b22ca16b5 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Fri, 26 Mar 2010 12:53:32 +0200 Subject: wl1271: Enable hardware keep alive messages This patch will enable the hardware keep-alive mode, configure the required template, configure keep-alive parameters, and re-order JOIN's and ACX_AID in such a way that the keep-alive is activated. Signed-off-by: Juuso Oikarinen Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271_acx.c | 55 +++++++++++++++++++++++++++++++ drivers/net/wireless/wl12xx/wl1271_acx.h | 31 ++++++++++++++++- drivers/net/wireless/wl12xx/wl1271_conf.h | 9 +++++ drivers/net/wireless/wl12xx/wl1271_init.c | 13 ++++++++ drivers/net/wireless/wl12xx/wl1271_main.c | 33 +++++++++++++++++-- 5 files changed, 138 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl1271_init.c') diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c index e7d11811a907..8f0bd5bee6f9 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.c +++ b/drivers/net/wireless/wl12xx/wl1271_acx.c @@ -1139,3 +1139,58 @@ out: kfree(acx); return ret; } + +int wl1271_acx_keep_alive_mode(struct wl1271 *wl, bool enable) +{ + struct wl1271_acx_keep_alive_mode *acx = NULL; + int ret = 0; + + wl1271_debug(DEBUG_ACX, "acx keep alive mode: %d", enable); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->enabled = enable; + + ret = wl1271_cmd_configure(wl, ACX_KEEP_ALIVE_MODE, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx keep alive mode failed: %d", ret); + goto out; + } + +out: + 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; + int ret = 0; + + wl1271_debug(DEBUG_ACX, "acx keep alive config"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->period = cpu_to_le32(wl->conf.conn.keep_alive_interval); + acx->index = index; + acx->tpl_validation = tpl_valid; + acx->trigger = ACX_KEEP_ALIVE_NO_TX; + + ret = wl1271_cmd_configure(wl, ACX_SET_KEEP_ALIVE_CONFIG, + acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx keep alive config failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h index b6eb0c1b6c78..15cc56192de9 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.h +++ b/drivers/net/wireless/wl12xx/wl1271_acx.h @@ -915,6 +915,33 @@ struct wl1271_acx_pm_config { u8 padding[3]; } __attribute__ ((packed)); +struct wl1271_acx_keep_alive_mode { + struct acx_header header; + + u8 enabled; + u8 padding[3]; +} __attribute__ ((packed)); + +enum { + ACX_KEEP_ALIVE_NO_TX = 0, + ACX_KEEP_ALIVE_PERIOD_ONLY +}; + +enum { + ACX_KEEP_ALIVE_TPL_INVALID = 0, + ACX_KEEP_ALIVE_TPL_VALID +}; + +struct wl1271_acx_keep_alive_config { + struct acx_header header; + + __le32 period; + u8 index; + u8 tpl_validation; + u8 trigger; + u8 padding; +} __attribute__ ((packed)); + enum { ACX_WAKE_UP_CONDITIONS = 0x0002, ACX_MEM_CFG = 0x0003, @@ -964,7 +991,7 @@ enum { ACX_BET_ENABLE = 0x0050, ACX_RSSI_SNR_TRIGGER = 0x0051, ACX_RSSI_SNR_WEIGHTS = 0x0051, - ACX_KEEP_ALIVE_MODE = 0x0052, + ACX_KEEP_ALIVE_MODE = 0x0053, ACX_SET_KEEP_ALIVE_CONFIG = 0x0054, ACX_BA_SESSION_RESPONDER_POLICY = 0x0055, ACX_BA_SESSION_INITIATOR_POLICY = 0x0056, @@ -1031,5 +1058,7 @@ int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable); int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, u8 *address, u8 version); 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); #endif /* __WL1271_ACX_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1271_conf.h b/drivers/net/wireless/wl12xx/wl1271_conf.h index f63dd5ed998e..82b5dabb6e8d 100644 --- a/drivers/net/wireless/wl12xx/wl1271_conf.h +++ b/drivers/net/wireless/wl12xx/wl1271_conf.h @@ -955,6 +955,15 @@ struct conf_conn_settings { * Range 0 - 255 */ u8 psm_entry_retries; + + /* + * + * Specifies the interval of the connection keep-alive null-func + * frame in ms. + * + * Range: 1000 - 3600000 + */ + u32 keep_alive_interval; }; enum { diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c index 0106663bc45b..8ec94ac84093 100644 --- a/drivers/net/wireless/wl12xx/wl1271_init.c +++ b/drivers/net/wireless/wl12xx/wl1271_init.c @@ -337,6 +337,19 @@ int wl1271_hw_init(struct wl1271 *wl) if (ret < 0) goto out_free_memmap; + /* disable all keep-alive templates */ + for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) { + ret = wl1271_acx_keep_alive_config(wl, i, + ACX_KEEP_ALIVE_TPL_INVALID); + if (ret < 0) + goto out_free_memmap; + } + + /* disable the keep-alive feature */ + ret = wl1271_acx_keep_alive_mode(wl, false); + if (ret < 0) + goto out_free_memmap; + return 0; out_free_memmap: diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index afab52bec134..033cef01fd43 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -264,7 +264,8 @@ static struct conf_drv_settings default_conf = { }, .bet_enable = CONF_BET_MODE_ENABLE, .bet_max_consecutive = 10, - .psm_entry_retries = 3 + .psm_entry_retries = 3, + .keep_alive_interval = 55000 }, .init = { .radioparam = { @@ -1203,6 +1204,9 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) wl->rate_set = CONF_TX_RATE_MASK_BASIC; wl->sta_rate_set = 0; wl1271_acx_rate_policies(wl); + wl1271_acx_keep_alive_config( + wl, CMD_TEMPL_KLV_IDX_NULL_DATA, + ACX_KEEP_ALIVE_TPL_INVALID); } } @@ -1676,7 +1680,8 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, ret = wl1271_cmd_build_probe_req(wl, NULL, 0, NULL, 0, wl->band); - ret = wl1271_acx_aid(wl, wl->aid); + /* Enable the keep-alive feature */ + ret = wl1271_acx_keep_alive_mode(wl, true); if (ret < 0) goto out_sleep; @@ -1700,6 +1705,10 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, /* disable connection monitor features */ ret = wl1271_acx_conn_monit_params(wl, false); + + /* Disable the keep-alive feature */ + ret = wl1271_acx_keep_alive_mode(wl, false); + if (ret < 0) goto out_sleep; } @@ -1744,6 +1753,26 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, set_bit(WL1271_FLAG_JOINED, &wl->flags); } + /* + * The JOIN operation shuts down the firmware keep-alive as a side + * effect, and the ACX_AID will start the keep-alive as a side effect. + * Hence, for non-IBSS, the ACX_AID must always happen *after* the + * JOIN operation, and the template config after the ACX_AID. + */ + if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) { + ret = wl1271_acx_aid(wl, wl->aid); + if (ret < 0) + goto out_sleep; + ret = wl1271_cmd_build_klv_null_data(wl); + if (ret < 0) + goto out_sleep; + ret = wl1271_acx_keep_alive_config( + wl, CMD_TEMPL_KLV_IDX_NULL_DATA, + ACX_KEEP_ALIVE_TPL_VALID); + if (ret < 0) + goto out_sleep; + } + out_sleep: wl1271_ps_elp_sleep(wl); -- cgit v1.2.3 From 606c1487ac894798121bc2c64d27c1953c5a6210 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Thu, 1 Apr 2010 11:38:21 +0300 Subject: wl1271: Configure rates for templates Configure meaningful rates to be used with the templates. For control frames, use the determined basic rates (currently the lowest rate for the band) and for data-frames (null-funcs) let the firmware use the current rate policy to determine the rate. Signed-off-by: Juuso Oikarinen Reviewed-by: Teemu Paasikivi Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271_cmd.c | 21 +++++++++++++-------- drivers/net/wireless/wl12xx/wl1271_cmd.h | 4 +++- drivers/net/wireless/wl12xx/wl1271_init.c | 18 ++++++++++-------- drivers/net/wireless/wl12xx/wl1271_main.c | 6 ++++-- 4 files changed, 30 insertions(+), 19 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl1271_init.c') diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c index 5cee59fb8c5e..b19090334bc0 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.c +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c @@ -700,7 +700,7 @@ out: } int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, - void *buf, size_t buf_len, int index) + void *buf, size_t buf_len, int index, u32 rates) { struct wl1271_cmd_template_set *cmd; int ret = 0; @@ -718,7 +718,7 @@ int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, cmd->len = cpu_to_le16(buf_len); cmd->template_type = template_id; - cmd->enabled_rates = cpu_to_le32(wl->conf.tx.rc_conf.enabled_rates); + cmd->enabled_rates = cpu_to_le32(rates); cmd->short_retry_limit = wl->conf.tx.rc_conf.short_retry_limit; cmd->long_retry_limit = wl->conf.tx.rc_conf.long_retry_limit; cmd->index = index; @@ -758,7 +758,8 @@ int wl1271_cmd_build_null_data(struct wl1271 *wl) ptr = skb->data; } - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, ptr, size, 0); + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, ptr, size, 0, + WL1271_RATE_AUTOMATIC); out: dev_kfree_skb(skb); @@ -780,7 +781,8 @@ int wl1271_cmd_build_klv_null_data(struct wl1271 *wl) ret = wl1271_cmd_template_set(wl, CMD_TEMPL_KLV, skb->data, skb->len, - CMD_TEMPL_KLV_IDX_NULL_DATA); + CMD_TEMPL_KLV_IDX_NULL_DATA, + WL1271_RATE_AUTOMATIC); out: dev_kfree_skb(skb); @@ -801,7 +803,7 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid) goto out; ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, skb->data, - skb->len, 0); + skb->len, 0, wl->basic_rate); out: dev_kfree_skb(skb); @@ -826,10 +828,12 @@ int wl1271_cmd_build_probe_req(struct wl1271 *wl, if (band == IEEE80211_BAND_2GHZ) ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, - skb->data, skb->len, 0); + skb->data, skb->len, 0, + wl->conf.tx.basic_rate); else ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, - skb->data, skb->len, 0); + skb->data, skb->len, 0, + wl->conf.tx.basic_rate_5); out: dev_kfree_skb(skb); @@ -854,7 +858,8 @@ int wl1271_build_qos_null_data(struct wl1271 *wl) template.qos_ctrl = cpu_to_le16(0); return wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, &template, - sizeof(template), 0); + sizeof(template), 0, + WL1271_RATE_AUTOMATIC); } int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id) diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.h b/drivers/net/wireless/wl12xx/wl1271_cmd.h index d61f3394253f..00f78b7aa384 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.h +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.h @@ -45,7 +45,7 @@ int wl1271_cmd_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, u8 active_scan, u8 high_prio, u8 band, u8 probe_requests); int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, - void *buf, size_t buf_len, int index); + void *buf, size_t buf_len, int index, u32 rates); int wl1271_cmd_build_null_data(struct wl1271 *wl); int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid); int wl1271_cmd_build_probe_req(struct wl1271 *wl, @@ -252,6 +252,8 @@ struct cmd_enabledisable_path { u8 padding[3]; } __attribute__ ((packed)); +#define WL1271_RATE_AUTOMATIC 0 + struct wl1271_cmd_template_set { struct wl1271_cmd_header header; diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c index 8ec94ac84093..9ab336829044 100644 --- a/drivers/net/wireless/wl12xx/wl1271_init.c +++ b/drivers/net/wireless/wl12xx/wl1271_init.c @@ -56,54 +56,56 @@ int wl1271_init_templates_config(struct wl1271 *wl) /* send empty templates for fw memory reservation */ ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL, sizeof(struct wl12xx_probe_req_template), - 0); + 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; if (wl1271_11a_enabled()) { size_t size = sizeof(struct wl12xx_probe_req_template); ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, - NULL, size, 0); + NULL, size, 0, + WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; } ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, NULL, sizeof(struct wl12xx_null_data_template), - 0); + 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, NULL, sizeof(struct wl12xx_ps_poll_template), - 0); + 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; ret = wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, NULL, sizeof (struct wl12xx_qos_null_data_template), - 0); + 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PROBE_RESPONSE, NULL, sizeof (struct wl12xx_probe_resp_template), - 0); + 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, NULL, sizeof (struct wl12xx_beacon_template), - 0); + 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) { ret = wl1271_cmd_template_set(wl, CMD_TEMPL_KLV, NULL, - WL1271_CMD_TEMPL_MAX_SIZE, i); + WL1271_CMD_TEMPL_MAX_SIZE, i, + WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; } diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index ca5cd325fac7..247f4079832e 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -1642,7 +1642,8 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, wl1271_ssid_set(wl, beacon); ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, beacon->data, - beacon->len, 0); + beacon->len, 0, + wl1271_min_rate_get(wl)); if (ret < 0) { dev_kfree_skb(beacon); @@ -1657,7 +1658,8 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PROBE_RESPONSE, beacon->data, - beacon->len, 0); + beacon->len, 0, + wl1271_min_rate_get(wl)); dev_kfree_skb(beacon); if (ret < 0) goto out_sleep; -- cgit v1.2.3 From 00236aedf1d2c49a18ae9ea00698d97705ff7289 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Fri, 9 Apr 2010 11:07:30 +0300 Subject: wl1271: Add support for connection quality monitoring 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 Reviewed-by: Teemu Paasikivi Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271.h | 3 + drivers/net/wireless/wl12xx/wl1271_acx.c | 71 ++++++++++++++++++++ drivers/net/wireless/wl12xx/wl1271_acx.h | 56 +++++++++++++++- drivers/net/wireless/wl12xx/wl1271_boot.c | 3 +- drivers/net/wireless/wl12xx/wl1271_conf.h | 103 +++++++++++------------------ drivers/net/wireless/wl12xx/wl1271_event.c | 24 +++++++ drivers/net/wireless/wl12xx/wl1271_event.h | 8 +++ drivers/net/wireless/wl12xx/wl1271_init.c | 5 ++ drivers/net/wireless/wl12xx/wl1271_main.c | 52 ++++++--------- 9 files changed, 228 insertions(+), 97 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl1271_init.c') diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index 789460074670..75887e74205b 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -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; diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c index 621c94691e7e..1a6b2ec1db58 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.c +++ b/drivers/net/wireless/wl12xx/wl1271_acx.c @@ -1165,6 +1165,7 @@ out: 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; @@ -1194,3 +1195,73 @@ out: 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; +} diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h index 15cc56192de9..420e7e2fc021 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.h +++ b/drivers/net/wireless/wl12xx/wl1271_acx.h @@ -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, @@ -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, @@ -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__ */ diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.c b/drivers/net/wireless/wl12xx/wl1271_boot.c index 7acef88df1fe..f16d15bd5643 100644 --- a/drivers/net/wireless/wl12xx/wl1271_boot.c +++ b/drivers/net/wireless/wl12xx/wl1271_boot.c @@ -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) { diff --git a/drivers/net/wireless/wl12xx/wl1271_conf.h b/drivers/net/wireless/wl12xx/wl1271_conf.h index d76ae03762a3..c44307c4bcf8 100644 --- a/drivers/net/wireless/wl12xx/wl1271_conf.h +++ b/drivers/net/wireless/wl12xx/wl1271_conf.h @@ -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 { /* @@ -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. */ @@ -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; @@ -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 diff --git a/drivers/net/wireless/wl12xx/wl1271_event.c b/drivers/net/wireless/wl12xx/wl1271_event.c index daacf176cf09..cf37aa6eb137 100644 --- a/drivers/net/wireless/wl12xx/wl1271_event.c +++ b/drivers/net/wireless/wl12xx/wl1271_event.c @@ -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:"); @@ -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); diff --git a/drivers/net/wireless/wl12xx/wl1271_event.h b/drivers/net/wireless/wl12xx/wl1271_event.h index 278f9206aa56..58371008f270 100644 --- a/drivers/net/wireless/wl12xx/wl1271_event.h +++ b/drivers/net/wireless/wl12xx/wl1271_event.h @@ -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), diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c index 9ab336829044..b880382cf15d 100644 --- a/drivers/net/wireless/wl12xx/wl1271_init.c +++ b/drivers/net/wireless/wl12xx/wl1271_init.c @@ -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: diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index 551714164ff5..283d5dade1ae 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -234,35 +234,6 @@ static struct conf_drv_settings default_conf = { .broadcast_timeout = 20000, .rx_broadcast_in_ps = 1, .ps_poll_threshold = 20, - .sig_trigger_count = 2, - .sig_trigger = { - [0] = { - .threshold = -75, - .pacing = 500, - .metric = CONF_TRIG_METRIC_RSSI_BEACON, - .type = CONF_TRIG_EVENT_TYPE_EDGE, - .direction = CONF_TRIG_EVENT_DIR_LOW, - .hysteresis = 2, - .index = 0, - .enable = 1 - }, - [1] = { - .threshold = -75, - .pacing = 500, - .metric = CONF_TRIG_METRIC_RSSI_BEACON, - .type = CONF_TRIG_EVENT_TYPE_EDGE, - .direction = CONF_TRIG_EVENT_DIR_HIGH, - .hysteresis = 2, - .index = 1, - .enable = 1 - } - }, - .sig_weights = { - .rssi_bcn_avg_weight = 10, - .rssi_pkt_avg_weight = 10, - .snr_bcn_avg_weight = 10, - .snr_pkt_avg_weight = 10 - }, .bet_enable = CONF_BET_MODE_ENABLE, .bet_max_consecutive = 10, .psm_entry_retries = 3, @@ -281,6 +252,14 @@ static struct conf_drv_settings default_conf = { .pm_config = { .host_clk_settling_time = 5000, .host_fast_wakeup_support = false + }, + .roam_trigger = { + /* FIXME: due to firmware bug, must use value 1 for now */ + .trigger_pacing = 1, + .avg_weight_rssi_beacon = 20, + .avg_weight_rssi_data = 10, + .avg_weight_snr_beacon = 20, + .avg_weight_snr_data = 10 } }; @@ -1703,6 +1682,18 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, do_join = true; } + if (changed & BSS_CHANGED_CQM) { + bool enable = false; + if (bss_conf->cqm_rssi_thold) + enable = true; + ret = wl1271_acx_rssi_snr_trigger(wl, enable, + bss_conf->cqm_rssi_thold, + bss_conf->cqm_rssi_hyst); + if (ret < 0) + goto out; + wl->rssi_thold = bss_conf->cqm_rssi_thold; + } + if ((changed & BSS_CHANGED_BSSID) && /* * Now we know the correct bssid, so we send a new join command @@ -2283,7 +2274,8 @@ int wl1271_init_ieee80211(struct wl1271 *wl) IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_HAS_RATE_CONTROL | - IEEE80211_HW_CONNECTION_MONITOR; + IEEE80211_HW_CONNECTION_MONITOR | + IEEE80211_HW_SUPPORTS_CQM_RSSI; wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); -- cgit v1.2.3