From 59eb21a6504731fc16db4cf9463065dd61093e08 Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Mon, 17 Jan 2011 13:37:28 +0900 Subject: cfg80211: Extend channel to frequency mapping for 802.11j Extend channel to frequency mapping for 802.11j Japan 4.9GHz band, according to IEEE802.11 section 17.3.8.3.2 and Annex J. Because there are now overlapping channel numbers in the 2GHz and 5GHz band we can't map from channel to frequency without knowing the band. This is no problem as in most contexts we know the band. In places where we don't know the band (and WEXT compatibility) we assume the 2GHz band for channels below 14. This patch does not implement all channel to frequency mappings defined in 802.11, it's just an extension for 802.11j 20MHz channels. 5MHz and 10MHz channels as well as 802.11y channels have been omitted. The following drivers have been updated to reflect the API changes: iwl-3945, iwl-agn, iwmc3200wifi, libertas, mwl8k, rt2x00, wl1251, wl12xx. The drivers have been compile-tested only. Signed-off-by: Bruno Randolf Signed-off-by: Brian Prodoehl Acked-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/rx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/rx.c b/drivers/net/wireless/wl12xx/rx.c index 682304c30b81..ec8d843d41cf 100644 --- a/drivers/net/wireless/wl12xx/rx.c +++ b/drivers/net/wireless/wl12xx/rx.c @@ -76,7 +76,7 @@ static void wl1271_rx_status(struct wl1271 *wl, */ wl->noise = desc->rssi - (desc->snr >> 1); - status->freq = ieee80211_channel_to_frequency(desc->channel); + status->freq = ieee80211_channel_to_frequency(desc->channel, desc_band); if (desc->flags & WL1271_RX_DESC_ENCRYPT_MASK) { status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; -- cgit v1.2.3 From 3c2c04a15f5fe5d169fe343c9eb7c1856d3033d3 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 15 Dec 2010 11:47:54 +0200 Subject: wl12xx: remove redundant debugfs_remove_recursive() call Upon rmmod, the /ieee80211/phyX dir is being removed. later, we try to remove /ieee80211/phyX/wl12xx, which might result in NULL dereference. Remove the excessive debugfs_remove_recursive() call. (consequently, there is no more need to save wl->rootdir) Reported-by: Arik Nemtsov Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/debugfs.c | 36 ++++++++++++++++------------------- drivers/net/wireless/wl12xx/wl12xx.h | 1 - 2 files changed, 16 insertions(+), 21 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index ec6077760157..36e7ec1e0c3b 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -293,12 +293,13 @@ static const struct file_operations gpio_power_ops = { .llseek = default_llseek, }; -static int wl1271_debugfs_add_files(struct wl1271 *wl) +static int wl1271_debugfs_add_files(struct wl1271 *wl, + struct dentry *rootdir) { int ret = 0; struct dentry *entry, *stats; - stats = debugfs_create_dir("fw-statistics", wl->rootdir); + stats = debugfs_create_dir("fw-statistics", rootdir); if (!stats || IS_ERR(stats)) { entry = stats; goto err; @@ -395,13 +396,13 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl) DEBUGFS_FWSTATS_ADD(rxpipe, missed_beacon_host_int_trig_rx_data); DEBUGFS_FWSTATS_ADD(rxpipe, tx_xfr_host_int_trig_rx_data); - DEBUGFS_ADD(tx_queue_len, wl->rootdir); - DEBUGFS_ADD(retry_count, wl->rootdir); - DEBUGFS_ADD(excessive_retries, wl->rootdir); + DEBUGFS_ADD(tx_queue_len, rootdir); + DEBUGFS_ADD(retry_count, rootdir); + DEBUGFS_ADD(excessive_retries, rootdir); - DEBUGFS_ADD(gpio_power, wl->rootdir); + DEBUGFS_ADD(gpio_power, rootdir); - entry = debugfs_create_x32("debug_level", 0600, wl->rootdir, + entry = debugfs_create_x32("debug_level", 0600, rootdir, &wl12xx_debug_level); if (!entry || IS_ERR(entry)) goto err; @@ -419,7 +420,7 @@ err: void wl1271_debugfs_reset(struct wl1271 *wl) { - if (!wl->rootdir) + if (!wl->stats.fw_stats) return; memset(wl->stats.fw_stats, 0, sizeof(*wl->stats.fw_stats)); @@ -430,13 +431,13 @@ void wl1271_debugfs_reset(struct wl1271 *wl) int wl1271_debugfs_init(struct wl1271 *wl) { int ret; + struct dentry *rootdir; - wl->rootdir = debugfs_create_dir(KBUILD_MODNAME, - wl->hw->wiphy->debugfsdir); + rootdir = debugfs_create_dir(KBUILD_MODNAME, + wl->hw->wiphy->debugfsdir); - if (IS_ERR(wl->rootdir)) { - ret = PTR_ERR(wl->rootdir); - wl->rootdir = NULL; + if (IS_ERR(rootdir)) { + ret = PTR_ERR(rootdir); goto err; } @@ -450,7 +451,7 @@ int wl1271_debugfs_init(struct wl1271 *wl) wl->stats.fw_stats_update = jiffies; - ret = wl1271_debugfs_add_files(wl); + ret = wl1271_debugfs_add_files(wl, rootdir); if (ret < 0) goto err_file; @@ -462,8 +463,7 @@ err_file: wl->stats.fw_stats = NULL; err_fw: - debugfs_remove_recursive(wl->rootdir); - wl->rootdir = NULL; + debugfs_remove_recursive(rootdir); err: return ret; @@ -473,8 +473,4 @@ void wl1271_debugfs_exit(struct wl1271 *wl) { kfree(wl->stats.fw_stats); wl->stats.fw_stats = NULL; - - debugfs_remove_recursive(wl->rootdir); - wl->rootdir = NULL; - } diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 9050dd9b62d2..c3c30b3abc15 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -378,7 +378,6 @@ struct wl1271 { int last_rssi_event; struct wl1271_stats stats; - struct dentry *rootdir; __le32 buffer_32; u32 buffer_cmd; -- cgit v1.2.3 From 0dd386676497fb3097e6fbb3de6090c948c0df30 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 21 Dec 2010 07:00:13 +0300 Subject: wl12xx: use after free in debug code If debugging is turned on, then wl1271_dump() dereferences a freed variable. Signed-off-by: Dan Carpenter Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/spi.c b/drivers/net/wireless/wl12xx/spi.c index 46714910f98c..8f7ea2c7d567 100644 --- a/drivers/net/wireless/wl12xx/spi.c +++ b/drivers/net/wireless/wl12xx/spi.c @@ -110,9 +110,9 @@ static void wl1271_spi_reset(struct wl1271 *wl) spi_message_add_tail(&t, &m); spi_sync(wl_to_spi(wl), &m); - kfree(cmd); wl1271_dump(DEBUG_SPI, "spi reset -> ", cmd, WSPI_INIT_CMD_LEN); + kfree(cmd); } static void wl1271_spi_init(struct wl1271 *wl) -- cgit v1.2.3 From 6177eaea277527e48753d050723cd138494c98a8 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 22 Dec 2010 12:38:52 +0100 Subject: wl12xx: fix some sparse warnings Note that wl1271_write32() calls cpu_to_le32() by itself, so calling wl1271_write32(addr, cpu_to_le32(val)) is in fact a bug on BE systems. Fix the following sparse warnings: drivers/net/wireless/wl12xx/cmd.c:662:16: warning: incorrect type in assignment (different base types) drivers/net/wireless/wl12xx/cmd.c:662:16: expected unsigned short [unsigned] [addressable] [usertype] llc_type drivers/net/wireless/wl12xx/cmd.c:662:16: got restricted __be16 [usertype] drivers/net/wireless/wl12xx/cmd.c:674:17: warning: incorrect type in assignment (different base types) drivers/net/wireless/wl12xx/cmd.c:674:17: expected unsigned int [unsigned] [addressable] [usertype] sender_ip drivers/net/wireless/wl12xx/cmd.c:674:17: got restricted __be32 [usertype] ip_addr drivers/net/wireless/wl12xx/rx.c:202:4: warning: incorrect type in argument 3 (different base types) drivers/net/wireless/wl12xx/rx.c:202:4: expected unsigned int [unsigned] [usertype] val drivers/net/wireless/wl12xx/rx.c:202:4: got restricted __le32 [usertype] drivers/net/wireless/wl12xx/acx.c:1247:23: warning: incorrect type in assignment (different base types) drivers/net/wireless/wl12xx/acx.c:1247:23: expected restricted __le32 [usertype] ht_capabilites drivers/net/wireless/wl12xx/acx.c:1247:23: got unsigned long drivers/net/wireless/wl12xx/acx.c:1250:24: warning: invalid assignment: |= drivers/net/wireless/wl12xx/acx.c:1250:24: left side has type restricted __le32 drivers/net/wireless/wl12xx/acx.c:1250:24: right side has type unsigned long drivers/net/wireless/wl12xx/acx.c:1253:24: warning: invalid assignment: |= drivers/net/wireless/wl12xx/acx.c:1253:24: left side has type restricted __le32 drivers/net/wireless/wl12xx/acx.c:1253:24: right side has type unsigned long drivers/net/wireless/wl12xx/acx.c:1256:24: warning: invalid assignment: |= drivers/net/wireless/wl12xx/acx.c:1256:24: left side has type restricted __le32 drivers/net/wireless/wl12xx/acx.c:1256:24: right side has type unsigned long Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 13 +++++++------ drivers/net/wireless/wl12xx/cmd.c | 8 ++++---- drivers/net/wireless/wl12xx/rx.c | 3 +-- drivers/net/wireless/wl12xx/wl12xx_80211.h | 6 +++--- 4 files changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index cc4068d2b4a8..17f6a63fbdea 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -1233,6 +1233,7 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl, struct wl1271_acx_ht_capabilities *acx; u8 mac_address[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; int ret = 0; + u32 ht_capabilites = 0; wl1271_debug(DEBUG_ACX, "acx ht capabilities setting"); @@ -1244,16 +1245,16 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl, /* Allow HT Operation ? */ if (allow_ht_operation) { - acx->ht_capabilites = + ht_capabilites = WL1271_ACX_FW_CAP_HT_OPERATION; if (ht_cap->cap & IEEE80211_HT_CAP_GRN_FLD) - acx->ht_capabilites |= + ht_capabilites |= WL1271_ACX_FW_CAP_GREENFIELD_FRAME_FORMAT; if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20) - acx->ht_capabilites |= + ht_capabilites |= WL1271_ACX_FW_CAP_SHORT_GI_FOR_20MHZ_PACKETS; if (ht_cap->cap & IEEE80211_HT_CAP_LSIG_TXOP_PROT) - acx->ht_capabilites |= + ht_capabilites |= WL1271_ACX_FW_CAP_LSIG_TXOP_PROTECTION; /* get data from A-MPDU parameters field */ @@ -1261,10 +1262,10 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl, acx->ampdu_min_spacing = ht_cap->ampdu_density; memcpy(acx->mac_address, mac_address, ETH_ALEN); - } else { /* HT operations are not allowed */ - acx->ht_capabilites = 0; } + acx->ht_capabilites = cpu_to_le32(ht_capabilites); + ret = wl1271_cmd_configure(wl, ACX_PEER_HT_CAP, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("acx ht capabilities setting failed: %d", ret); diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 0106628aa5a2..52a6bcd5c309 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -659,15 +659,15 @@ int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, __be32 ip_addr) /* llc layer */ memcpy(tmpl.llc_hdr, rfc1042_header, sizeof(rfc1042_header)); - tmpl.llc_type = htons(ETH_P_ARP); + tmpl.llc_type = cpu_to_be16(ETH_P_ARP); /* arp header */ arp_hdr = &tmpl.arp_hdr; - arp_hdr->ar_hrd = htons(ARPHRD_ETHER); - arp_hdr->ar_pro = htons(ETH_P_IP); + arp_hdr->ar_hrd = cpu_to_be16(ARPHRD_ETHER); + arp_hdr->ar_pro = cpu_to_be16(ETH_P_IP); arp_hdr->ar_hln = ETH_ALEN; arp_hdr->ar_pln = 4; - arp_hdr->ar_op = htons(ARPOP_REPLY); + arp_hdr->ar_op = cpu_to_be16(ARPOP_REPLY); /* arp payload */ memcpy(tmpl.sender_hw, wl->vif->addr, ETH_ALEN); diff --git a/drivers/net/wireless/wl12xx/rx.c b/drivers/net/wireless/wl12xx/rx.c index ec8d843d41cf..c6402529eac8 100644 --- a/drivers/net/wireless/wl12xx/rx.c +++ b/drivers/net/wireless/wl12xx/rx.c @@ -198,6 +198,5 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status) pkt_offset += pkt_length; } } - wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, - cpu_to_le32(wl->rx_counter)); + wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter); } diff --git a/drivers/net/wireless/wl12xx/wl12xx_80211.h b/drivers/net/wireless/wl12xx/wl12xx_80211.h index be21032f4dc1..e2b26fbf68c1 100644 --- a/drivers/net/wireless/wl12xx/wl12xx_80211.h +++ b/drivers/net/wireless/wl12xx/wl12xx_80211.h @@ -138,13 +138,13 @@ struct wl12xx_arp_rsp_template { struct ieee80211_hdr_3addr hdr; u8 llc_hdr[sizeof(rfc1042_header)]; - u16 llc_type; + __be16 llc_type; struct arphdr arp_hdr; u8 sender_hw[ETH_ALEN]; - u32 sender_ip; + __be32 sender_ip; u8 target_hw[ETH_ALEN]; - u32 target_ip; + __be32 target_ip; } __packed; -- cgit v1.2.3 From 1e05a81888318752e9a6d2158a95ddd6442ae117 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sat, 16 Oct 2010 17:44:51 +0200 Subject: wl12xx: Add AP related configuration to conf_drv_settings Rate class configuration has been split up for AP and STA modes. Template related configuration likewise separated. Signed-off-by: Arik Nemtsov Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 2 +- drivers/net/wireless/wl12xx/cmd.c | 4 ++-- drivers/net/wireless/wl12xx/conf.h | 46 ++++++++++++++++++++++++++++++++++++-- drivers/net/wireless/wl12xx/main.c | 45 +++++++++++++++++++++++++++++++++++-- 4 files changed, 90 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index 17f6a63fbdea..d6885d7c4256 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -754,7 +754,7 @@ int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats) int wl1271_acx_rate_policies(struct wl1271 *wl) { struct acx_rate_policy *acx; - struct conf_tx_rate_class *c = &wl->conf.tx.rc_conf; + struct conf_tx_rate_class *c = &wl->conf.tx.sta_rc_conf; int idx = 0; int ret = 0; diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 52a6bcd5c309..3b7b8f0a200b 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -490,8 +490,8 @@ 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(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->short_retry_limit = wl->conf.tx.tmpl_short_retry_limit; + cmd->long_retry_limit = wl->conf.tx.tmpl_long_retry_limit; cmd->index = index; if (buf) diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index a16b3616e430..7563ce3a9f66 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -496,6 +496,26 @@ struct conf_rx_settings { CONF_HW_BIT_RATE_2MBPS) #define CONF_TX_RATE_RETRY_LIMIT 10 +/* + * Rates supported for data packets when operating as AP. Note the absense + * of the 22Mbps rate. There is a FW limitation on 12 rates so we must drop + * one. The rate dropped is not mandatory under any operating mode. + */ +#define CONF_TX_AP_ENABLED_RATES (CONF_HW_BIT_RATE_1MBPS | \ + CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \ + CONF_HW_BIT_RATE_6MBPS | CONF_HW_BIT_RATE_9MBPS | \ + CONF_HW_BIT_RATE_11MBPS | CONF_HW_BIT_RATE_12MBPS | \ + CONF_HW_BIT_RATE_18MBPS | CONF_HW_BIT_RATE_24MBPS | \ + CONF_HW_BIT_RATE_36MBPS | CONF_HW_BIT_RATE_48MBPS | \ + CONF_HW_BIT_RATE_54MBPS) + +/* + * Default rates for management traffic when operating in AP mode. This + * should be configured according to the basic rate set of the AP + */ +#define CONF_TX_AP_DEFAULT_MGMT_RATES (CONF_HW_BIT_RATE_1MBPS | \ + CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS) + struct conf_tx_rate_class { /* @@ -636,9 +656,9 @@ struct conf_tx_settings { /* * Configuration for rate classes for TX (currently only one - * rate class supported.) + * rate class supported). Used in non-AP mode. */ - struct conf_tx_rate_class rc_conf; + struct conf_tx_rate_class sta_rc_conf; /* * Configuration for access categories for TX rate control. @@ -646,6 +666,22 @@ struct conf_tx_settings { u8 ac_conf_count; struct conf_tx_ac_category ac_conf[CONF_TX_MAX_AC_COUNT]; + /* + * Configuration for rate classes in AP-mode. These rate classes + * are for the AC TX queues + */ + struct conf_tx_rate_class ap_rc_conf[CONF_TX_MAX_AC_COUNT]; + + /* + * Management TX rate class for AP-mode. + */ + struct conf_tx_rate_class ap_mgmt_conf; + + /* + * Broadcast TX rate class for AP-mode. + */ + struct conf_tx_rate_class ap_bcst_conf; + /* * Configuration for TID parameters. */ @@ -687,6 +723,12 @@ struct conf_tx_settings { * Range: CONF_HW_BIT_RATE_* bit mask */ u32 basic_rate_5; + + /* + * TX retry limits for templates + */ + u8 tmpl_short_retry_limit; + u8 tmpl_long_retry_limit; }; enum { diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 062247ef3ad2..788959a5f0de 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -116,11 +116,11 @@ static struct conf_drv_settings default_conf = { }, .tx = { .tx_energy_detection = 0, - .rc_conf = { + .sta_rc_conf = { .enabled_rates = 0, .short_retry_limit = 10, .long_retry_limit = 10, - .aflags = 0 + .aflags = 0, }, .ac_conf_count = 4, .ac_conf = { @@ -153,6 +153,45 @@ static struct conf_drv_settings default_conf = { .tx_op_limit = 1504, }, }, + .ap_rc_conf = { + [0] = { + .enabled_rates = CONF_TX_AP_ENABLED_RATES, + .short_retry_limit = 10, + .long_retry_limit = 10, + .aflags = 0, + }, + [1] = { + .enabled_rates = CONF_TX_AP_ENABLED_RATES, + .short_retry_limit = 10, + .long_retry_limit = 10, + .aflags = 0, + }, + [2] = { + .enabled_rates = CONF_TX_AP_ENABLED_RATES, + .short_retry_limit = 10, + .long_retry_limit = 10, + .aflags = 0, + }, + [3] = { + .enabled_rates = CONF_TX_AP_ENABLED_RATES, + .short_retry_limit = 10, + .long_retry_limit = 10, + .aflags = 0, + }, + }, + .ap_mgmt_conf = { + .enabled_rates = CONF_TX_AP_DEFAULT_MGMT_RATES, + .short_retry_limit = 10, + .long_retry_limit = 10, + .aflags = 0, + }, + .ap_bcst_conf = { + .enabled_rates = CONF_HW_BIT_RATE_1MBPS, + .short_retry_limit = 10, + .long_retry_limit = 10, + .aflags = 0, + }, + .tid_conf_count = 4, .tid_conf = { [CONF_TX_AC_BE] = { @@ -193,6 +232,8 @@ static struct conf_drv_settings default_conf = { .tx_compl_threshold = 4, .basic_rate = CONF_HW_BIT_RATE_1MBPS, .basic_rate_5 = CONF_HW_BIT_RATE_6MBPS, + .tmpl_short_retry_limit = 10, + .tmpl_long_retry_limit = 10, }, .conn = { .wake_up_event = CONF_WAKE_UP_EVENT_DTIM, -- cgit v1.2.3 From 79b223f4c7ce35fba145c504de12be030cc0007e Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sat, 16 Oct 2010 17:52:59 +0200 Subject: wl12xx: AP mode - AP specific CMD_CONFIGURE sub-commands Add AP max retries and rate policy configuration. Rename STA rate policy configuration function. Signed-off-by: Arik Nemtsov Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 60 ++++++++++++++++++++++++++++++++++++-- drivers/net/wireless/wl12xx/acx.h | 29 ++++++++++++++++-- drivers/net/wireless/wl12xx/conf.h | 6 ++++ drivers/net/wireless/wl12xx/init.c | 2 +- drivers/net/wireless/wl12xx/main.c | 10 +++---- drivers/net/wireless/wl12xx/tx.c | 2 +- 6 files changed, 98 insertions(+), 11 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index d6885d7c4256..646d278bd944 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -751,9 +751,9 @@ int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats) return 0; } -int wl1271_acx_rate_policies(struct wl1271 *wl) +int wl1271_acx_sta_rate_policies(struct wl1271 *wl) { - struct acx_rate_policy *acx; + struct acx_sta_rate_policy *acx; struct conf_tx_rate_class *c = &wl->conf.tx.sta_rc_conf; int idx = 0; int ret = 0; @@ -794,6 +794,38 @@ out: return ret; } +int wl1271_acx_ap_rate_policy(struct wl1271 *wl, struct conf_tx_rate_class *c, + u8 idx) +{ + struct acx_ap_rate_policy *acx; + int ret = 0; + + wl1271_debug(DEBUG_ACX, "acx ap rate policy"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->rate_policy.enabled_rates = cpu_to_le32(c->enabled_rates); + acx->rate_policy.short_retry_limit = c->short_retry_limit; + acx->rate_policy.long_retry_limit = c->long_retry_limit; + acx->rate_policy.aflags = c->aflags; + + acx->rate_policy_idx = idx; + + ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("Setting of ap rate policy failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max, u8 aifsn, u16 txop) { @@ -1335,3 +1367,27 @@ out: kfree(tsf_info); return ret; } + +int wl1271_acx_max_tx_retry(struct wl1271 *wl) +{ + struct wl1271_acx_max_tx_retry *acx = NULL; + int ret; + + wl1271_debug(DEBUG_ACX, "acx max tx retry"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) + return -ENOMEM; + + acx->max_tx_retry = cpu_to_le16(wl->conf.tx.ap_max_tx_retries); + + ret = wl1271_cmd_configure(wl, ACX_MAX_TX_FAILURE, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx max tx retry failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index 7bd8e4db4a71..62a269d84ebe 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -747,13 +747,23 @@ struct acx_rate_class { #define ACX_TX_BASIC_RATE 0 #define ACX_TX_AP_FULL_RATE 1 #define ACX_TX_RATE_POLICY_CNT 2 -struct acx_rate_policy { +struct acx_sta_rate_policy { struct acx_header header; __le32 rate_class_cnt; struct acx_rate_class rate_class[CONF_TX_MAX_RATE_CLASSES]; } __packed; + +#define ACX_TX_AP_MODE_MGMT_RATE 4 +#define ACX_TX_AP_MODE_BCST_RATE 5 +struct acx_ap_rate_policy { + struct acx_header header; + + __le32 rate_policy_idx; + struct acx_rate_class rate_policy; +} __packed; + struct acx_ac_cfg { struct acx_header header; u8 ac; @@ -1062,6 +1072,17 @@ struct wl1271_acx_fw_tsf_information { u8 padding[3]; } __packed; +struct wl1271_acx_max_tx_retry { + struct acx_header header; + + /* + * the number of frames transmission failures before + * issuing the aging event. + */ + __le16 max_tx_retry; + u8 padding_1[2]; +} __packed; + enum { ACX_WAKE_UP_CONDITIONS = 0x0002, ACX_MEM_CFG = 0x0003, @@ -1119,6 +1140,7 @@ enum { ACX_HT_BSS_OPERATION = 0x0058, ACX_COEX_ACTIVITY = 0x0059, ACX_SET_DCO_ITRIM_PARAMS = 0x0061, + ACX_MAX_TX_FAILURE = 0x0072, DOT11_RX_MSDU_LIFE_TIME = 0x1004, DOT11_CUR_TX_PWR = 0x100D, DOT11_RX_DOT11_MODE = 0x1012, @@ -1160,7 +1182,9 @@ int wl1271_acx_set_preamble(struct wl1271 *wl, enum acx_preamble_type preamble); int wl1271_acx_cts_protect(struct wl1271 *wl, enum acx_ctsprotect_type ctsprotect); int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats); -int wl1271_acx_rate_policies(struct wl1271 *wl); +int wl1271_acx_sta_rate_policies(struct wl1271 *wl); +int wl1271_acx_ap_rate_policy(struct wl1271 *wl, struct conf_tx_rate_class *c, + u8 idx); int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max, u8 aifsn, u16 txop); int wl1271_acx_tid_cfg(struct wl1271 *wl, u8 queue_id, u8 channel_type, @@ -1186,5 +1210,6 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl, int wl1271_acx_set_ht_information(struct wl1271 *wl, u16 ht_operation_mode); int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime); +int wl1271_acx_max_tx_retry(struct wl1271 *wl); #endif /* __WL1271_ACX_H__ */ diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 7563ce3a9f66..f5c048c9bea4 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -682,6 +682,12 @@ struct conf_tx_settings { */ struct conf_tx_rate_class ap_bcst_conf; + /* + * AP-mode - allow this number of TX retries to a station before an + * event is triggered from FW. + */ + u16 ap_max_tx_retries; + /* * Configuration for TID parameters. */ diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index 785a5304bfc4..f468d7178a9f 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -322,7 +322,7 @@ int wl1271_hw_init(struct wl1271 *wl) } /* Configure TX rate classes */ - ret = wl1271_acx_rate_policies(wl); + ret = wl1271_acx_sta_rate_policies(wl); if (ret < 0) goto out_free_memmap; diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 788959a5f0de..b40568e89eb4 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -191,7 +191,7 @@ static struct conf_drv_settings default_conf = { .long_retry_limit = 10, .aflags = 0, }, - + .ap_max_tx_retries = 100, .tid_conf_count = 4, .tid_conf = { [CONF_TX_AC_BE] = { @@ -1393,7 +1393,7 @@ static int wl1271_handle_idle(struct wl1271 *wl, bool idle) } wl->rate_set = wl1271_min_rate_get(wl); wl->sta_rate_set = 0; - ret = wl1271_acx_rate_policies(wl); + ret = wl1271_acx_sta_rate_policies(wl); if (ret < 0) goto out; ret = wl1271_acx_keep_alive_config( @@ -1468,7 +1468,7 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) wl1271_set_band_rate(wl); wl->basic_rate = wl1271_min_rate_get(wl); - ret = wl1271_acx_rate_policies(wl); + ret = wl1271_acx_sta_rate_policies(wl); if (ret < 0) wl1271_warning("rate policy for update channel " "failed %d", ret); @@ -2017,7 +2017,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates); wl->basic_rate = wl1271_min_rate_get(wl); - ret = wl1271_acx_rate_policies(wl); + ret = wl1271_acx_sta_rate_policies(wl); if (ret < 0) goto out_sleep; @@ -2071,7 +2071,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, /* revert back to minimum rates for the current band */ wl1271_set_band_rate(wl); wl->basic_rate = wl1271_min_rate_get(wl); - ret = wl1271_acx_rate_policies(wl); + ret = wl1271_acx_sta_rate_policies(wl); if (ret < 0) goto out_sleep; diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index b44c75cd8c1e..0cf210d6aa46 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -303,7 +303,7 @@ void wl1271_tx_work_locked(struct wl1271 *wl) woken_up = true; wl->rate_set = wl1271_tx_enabled_rates_get(wl, sta_rates); - wl1271_acx_rate_policies(wl); + wl1271_acx_sta_rate_policies(wl); } while ((skb = wl1271_skb_dequeue(wl))) { -- cgit v1.2.3 From 203c903cbfbdf23bbb3020b9344dd1ffabcfcb53 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Mon, 25 Oct 2010 11:17:44 +0200 Subject: wl12xx: AP mode - add AP specific event Add STA-remove completion event. Unmask it during boot if operating in AP-mode. Ignore unrelated events in AP-mode. Signed-off-by: Arik Nemtsov Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/boot.c | 3 +++ drivers/net/wireless/wl12xx/event.c | 7 ++++--- drivers/net/wireless/wl12xx/event.h | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index 4df04f84d7f1..b504367f281f 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -431,6 +431,9 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) PSPOLL_DELIVERY_FAILURE_EVENT_ID | SOFT_GEMINI_SENSE_EVENT_ID; + if (wl->bss_type == BSS_TYPE_AP_BSS) + wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID; + ret = wl1271_event_unmask(wl); if (ret < 0) { wl1271_error("EVENT mask setting failed"); diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c index f9146f5242fb..3376a5de09d7 100644 --- a/drivers/net/wireless/wl12xx/event.c +++ b/drivers/net/wireless/wl12xx/event.c @@ -186,6 +186,7 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) int ret; u32 vector; bool beacon_loss = false; + bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS); wl1271_event_mbox_dump(mbox); @@ -218,21 +219,21 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) * BSS_LOSE_EVENT, beacon loss has to be reported to the stack. * */ - if (vector & BSS_LOSE_EVENT_ID) { + if ((vector & BSS_LOSE_EVENT_ID) && !is_ap) { wl1271_info("Beacon loss detected."); /* indicate to the stack, that beacons have been lost */ beacon_loss = true; } - if (vector & PS_REPORT_EVENT_ID) { + if ((vector & PS_REPORT_EVENT_ID) && !is_ap) { wl1271_debug(DEBUG_EVENT, "PS_REPORT_EVENT"); ret = wl1271_event_ps_report(wl, mbox, &beacon_loss); if (ret < 0) return ret; } - if (vector & PSPOLL_DELIVERY_FAILURE_EVENT_ID) + if ((vector & PSPOLL_DELIVERY_FAILURE_EVENT_ID) && !is_ap) wl1271_event_pspoll_delivery_fail(wl); if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) { diff --git a/drivers/net/wireless/wl12xx/event.h b/drivers/net/wireless/wl12xx/event.h index 6cce0143adb5..fd955f31cec4 100644 --- a/drivers/net/wireless/wl12xx/event.h +++ b/drivers/net/wireless/wl12xx/event.h @@ -59,6 +59,7 @@ enum { BSS_LOSE_EVENT_ID = BIT(18), REGAINED_BSS_EVENT_ID = BIT(19), ROAMING_TRIGGER_MAX_TX_RETRY_EVENT_ID = BIT(20), + STA_REMOVE_COMPLETE_EVENT_ID = BIT(21), /* AP */ SOFT_GEMINI_SENSE_EVENT_ID = BIT(22), SOFT_GEMINI_PREDICTION_EVENT_ID = BIT(23), SOFT_GEMINI_AVALANCHE_EVENT_ID = BIT(24), -- cgit v1.2.3 From 98bdaabbbced007c7eb89cd373f9cb1640635b46 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sat, 16 Oct 2010 18:08:58 +0200 Subject: wl12xx: AP-mode high level commands Add commands to start/stop BSS, add/remove STA and configure encryption keys. Split the encryption commands "set key" and "set default key" into AP and STA specific versions. Signed-off-by: Arik Nemtsov Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/cmd.c | 276 ++++++++++++++++++++++++++++++++++- drivers/net/wireless/wl12xx/cmd.h | 139 +++++++++++++++++- drivers/net/wireless/wl12xx/init.c | 2 +- drivers/net/wireless/wl12xx/main.c | 6 +- drivers/net/wireless/wl12xx/tx.c | 2 +- drivers/net/wireless/wl12xx/wl12xx.h | 8 + 6 files changed, 419 insertions(+), 14 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 3b7b8f0a200b..f9c5ce91cb12 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -36,6 +36,7 @@ #include "wl12xx_80211.h" #include "cmd.h" #include "event.h" +#include "tx.h" #define WL1271_CMD_FAST_POLL_COUNT 50 @@ -702,9 +703,9 @@ int wl1271_build_qos_null_data(struct wl1271 *wl) wl->basic_rate); } -int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id) +int wl1271_cmd_set_sta_default_wep_key(struct wl1271 *wl, u8 id) { - struct wl1271_cmd_set_keys *cmd; + struct wl1271_cmd_set_sta_keys *cmd; int ret = 0; wl1271_debug(DEBUG_CMD, "cmd set_default_wep_key %d", id); @@ -731,11 +732,42 @@ out: return ret; } -int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, +int wl1271_cmd_set_ap_default_wep_key(struct wl1271 *wl, u8 id) +{ + struct wl1271_cmd_set_ap_keys *cmd; + int ret = 0; + + wl1271_debug(DEBUG_CMD, "cmd set_ap_default_wep_key %d", id); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + cmd->hlid = WL1271_AP_BROADCAST_HLID; + cmd->key_id = id; + cmd->lid_key_type = WEP_DEFAULT_LID_TYPE; + cmd->key_action = cpu_to_le16(KEY_SET_ID); + cmd->key_type = KEY_WEP; + + ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_warning("cmd set_ap_default_wep_key failed: %d", ret); + goto out; + } + +out: + kfree(cmd); + + return ret; +} + +int wl1271_cmd_set_sta_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, u8 key_size, const u8 *key, const u8 *addr, u32 tx_seq_32, u16 tx_seq_16) { - struct wl1271_cmd_set_keys *cmd; + struct wl1271_cmd_set_sta_keys *cmd; int ret = 0; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -788,6 +820,67 @@ out: return ret; } +int wl1271_cmd_set_ap_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, + u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32, + u16 tx_seq_16) +{ + struct wl1271_cmd_set_ap_keys *cmd; + int ret = 0; + u8 lid_type; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + if (hlid == WL1271_AP_BROADCAST_HLID) { + if (key_type == KEY_WEP) + lid_type = WEP_DEFAULT_LID_TYPE; + else + lid_type = BROADCAST_LID_TYPE; + } else { + lid_type = UNICAST_LID_TYPE; + } + + wl1271_debug(DEBUG_CRYPT, "ap key action: %d id: %d lid: %d type: %d" + " hlid: %d", (int)action, (int)id, (int)lid_type, + (int)key_type, (int)hlid); + + cmd->lid_key_type = lid_type; + cmd->hlid = hlid; + cmd->key_action = cpu_to_le16(action); + cmd->key_size = key_size; + cmd->key_type = key_type; + cmd->key_id = id; + cmd->ac_seq_num16[0] = cpu_to_le16(tx_seq_16); + cmd->ac_seq_num32[0] = cpu_to_le32(tx_seq_32); + + if (key_type == KEY_TKIP) { + /* + * We get the key in the following form: + * TKIP (16 bytes) - TX MIC (8 bytes) - RX MIC (8 bytes) + * but the target is expecting: + * TKIP - RX MIC - TX MIC + */ + memcpy(cmd->key, key, 16); + memcpy(cmd->key + 16, key + 24, 8); + memcpy(cmd->key + 24, key + 16, 8); + } else { + memcpy(cmd->key, key, key_size); + } + + wl1271_dump(DEBUG_CRYPT, "TARGET AP KEY: ", cmd, sizeof(*cmd)); + + ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_warning("could not set ap keys"); + goto out; + } + +out: + kfree(cmd); + return ret; +} + int wl1271_cmd_disconnect(struct wl1271 *wl) { struct wl1271_cmd_disconnect *cmd; @@ -850,3 +943,178 @@ out_free: out: return ret; } + +int wl1271_cmd_start_bss(struct wl1271 *wl) +{ + struct wl1271_cmd_bss_start *cmd; + struct ieee80211_bss_conf *bss_conf = &wl->vif->bss_conf; + int ret; + + wl1271_debug(DEBUG_CMD, "cmd start bss"); + + /* + * FIXME: We currently do not support hidden SSID. The real SSID + * should be fetched from mac80211 first. + */ + if (wl->ssid_len == 0) { + wl1271_warning("Hidden SSID currently not supported for AP"); + ret = -EINVAL; + goto out; + } + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + memcpy(cmd->bssid, bss_conf->bssid, ETH_ALEN); + + cmd->aging_period = WL1271_AP_DEF_INACTIV_SEC; + cmd->bss_index = WL1271_AP_BSS_INDEX; + cmd->global_hlid = WL1271_AP_GLOBAL_HLID; + cmd->broadcast_hlid = WL1271_AP_BROADCAST_HLID; + cmd->basic_rate_set = cpu_to_le32(wl->basic_rate_set); + cmd->beacon_interval = cpu_to_le16(wl->beacon_int); + cmd->dtim_interval = bss_conf->dtim_period; + cmd->beacon_expiry = WL1271_AP_DEF_BEACON_EXP; + cmd->channel = wl->channel; + cmd->ssid_len = wl->ssid_len; + cmd->ssid_type = SSID_TYPE_PUBLIC; + memcpy(cmd->ssid, wl->ssid, wl->ssid_len); + + switch (wl->band) { + case IEEE80211_BAND_2GHZ: + cmd->band = RADIO_BAND_2_4GHZ; + break; + case IEEE80211_BAND_5GHZ: + cmd->band = RADIO_BAND_5GHZ; + break; + default: + wl1271_warning("bss start - unknown band: %d", (int)wl->band); + cmd->band = RADIO_BAND_2_4GHZ; + break; + } + + ret = wl1271_cmd_send(wl, CMD_BSS_START, cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("failed to initiate cmd start bss"); + goto out_free; + } + +out_free: + kfree(cmd); + +out: + return ret; +} + +int wl1271_cmd_stop_bss(struct wl1271 *wl) +{ + struct wl1271_cmd_bss_start *cmd; + int ret; + + wl1271_debug(DEBUG_CMD, "cmd stop bss"); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + cmd->bss_index = WL1271_AP_BSS_INDEX; + + ret = wl1271_cmd_send(wl, CMD_BSS_STOP, cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("failed to initiate cmd stop bss"); + goto out_free; + } + +out_free: + kfree(cmd); + +out: + return ret; +} + +int wl1271_cmd_add_sta(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid) +{ + struct wl1271_cmd_add_sta *cmd; + int ret; + + wl1271_debug(DEBUG_CMD, "cmd add sta %d", (int)hlid); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + /* currently we don't support UAPSD */ + cmd->sp_len = 0; + + memcpy(cmd->addr, sta->addr, ETH_ALEN); + cmd->bss_index = WL1271_AP_BSS_INDEX; + cmd->aid = sta->aid; + cmd->hlid = hlid; + + /* + * FIXME: Does STA support QOS? We need to propagate this info from + * hostapd. Currently not that important since this is only used for + * sending the correct flavor of null-data packet in response to a + * trigger. + */ + cmd->wmm = 0; + + cmd->supported_rates = cpu_to_le32(wl1271_tx_enabled_rates_get(wl, + sta->supp_rates[wl->band])); + + wl1271_debug(DEBUG_CMD, "new sta rates: 0x%x", cmd->supported_rates); + + ret = wl1271_cmd_send(wl, CMD_ADD_STA, cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("failed to initiate cmd add sta"); + goto out_free; + } + +out_free: + kfree(cmd); + +out: + return ret; +} + +int wl1271_cmd_remove_sta(struct wl1271 *wl, u8 hlid) +{ + struct wl1271_cmd_remove_sta *cmd; + int ret; + + wl1271_debug(DEBUG_CMD, "cmd remove sta %d", (int)hlid); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + cmd->hlid = hlid; + /* We never send a deauth, mac80211 is in charge of this */ + cmd->reason_opcode = 0; + cmd->send_deauth_flag = 0; + + ret = wl1271_cmd_send(wl, CMD_REMOVE_STA, cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("failed to initiate cmd remove sta"); + goto out_free; + } + + ret = wl1271_cmd_wait_for_event(wl, STA_REMOVE_COMPLETE_EVENT_ID); + if (ret < 0) + wl1271_error("cmd remove sta event completion error"); + +out_free: + kfree(cmd); + +out: + return ret; +} diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h index 2a1d9db7ceb8..072bf3c2f24b 100644 --- a/drivers/net/wireless/wl12xx/cmd.h +++ b/drivers/net/wireless/wl12xx/cmd.h @@ -54,12 +54,20 @@ struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl, int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, __be32 ip_addr); 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, - u32 tx_seq_32, u16 tx_seq_16); +int wl1271_cmd_set_sta_default_wep_key(struct wl1271 *wl, u8 id); +int wl1271_cmd_set_ap_default_wep_key(struct wl1271 *wl, u8 id); +int wl1271_cmd_set_sta_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, + u8 key_size, const u8 *key, const u8 *addr, + u32 tx_seq_32, u16 tx_seq_16); +int wl1271_cmd_set_ap_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, + u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32, + u16 tx_seq_16); int wl1271_cmd_disconnect(struct wl1271 *wl); int wl1271_cmd_set_sta_state(struct wl1271 *wl); +int wl1271_cmd_start_bss(struct wl1271 *wl); +int wl1271_cmd_stop_bss(struct wl1271 *wl); +int wl1271_cmd_add_sta(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid); +int wl1271_cmd_remove_sta(struct wl1271 *wl, u8 hlid); enum wl1271_commands { CMD_INTERROGATE = 1, /*use this to read information elements*/ @@ -98,6 +106,12 @@ enum wl1271_commands { CMD_STOP_PERIODIC_SCAN = 51, CMD_SET_STA_STATE = 52, + /* AP mode commands */ + CMD_BSS_START = 60, + CMD_BSS_STOP = 61, + CMD_ADD_STA = 62, + CMD_REMOVE_STA = 63, + NUM_COMMANDS, MAX_COMMAND_ID = 0xFFFF, }; @@ -289,7 +303,7 @@ enum wl1271_cmd_key_type { /* FIXME: Add description for key-types */ -struct wl1271_cmd_set_keys { +struct wl1271_cmd_set_sta_keys { struct wl1271_cmd_header header; /* Ignored for default WEP key */ @@ -318,6 +332,57 @@ struct wl1271_cmd_set_keys { __le32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY]; } __packed; +enum wl1271_cmd_lid_key_type { + UNICAST_LID_TYPE = 0, + BROADCAST_LID_TYPE = 1, + WEP_DEFAULT_LID_TYPE = 2 +}; + +struct wl1271_cmd_set_ap_keys { + struct wl1271_cmd_header header; + + /* + * Indicates whether the HLID is a unicast key set + * or broadcast key set. A special value 0xFF is + * used to indicate that the HLID is on WEP-default + * (multi-hlids). of type wl1271_cmd_lid_key_type. + */ + u8 hlid; + + /* + * In WEP-default network (hlid == 0xFF) used to + * indicate which network STA/IBSS/AP role should be + * changed + */ + u8 lid_key_type; + + /* + * Key ID - For TKIP and AES key types, this field + * indicates the value that should be inserted into + * the KeyID field of frames transmitted using this + * key entry. For broadcast keys the index use as a + * marker for TX/RX key. + * For WEP default network (HLID=0xFF), this field + * indicates the ID of the key to add or remove. + */ + u8 key_id; + u8 reserved_1; + + /* key_action_e */ + __le16 key_action; + + /* key size in bytes */ + u8 key_size; + + /* key_type_e */ + u8 key_type; + + /* This field holds the security key data to add to the STA table */ + u8 key[MAX_KEY_SIZE]; + __le16 ac_seq_num16[NUM_ACCESS_CATEGORIES_COPY]; + __le32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY]; +} __packed; + struct wl1271_cmd_test_header { u8 id; u8 padding[3]; @@ -412,4 +477,68 @@ struct wl1271_cmd_set_sta_state { u8 padding[3]; } __packed; +enum wl1271_ssid_type { + SSID_TYPE_PUBLIC = 0, + SSID_TYPE_HIDDEN = 1 +}; + +struct wl1271_cmd_bss_start { + struct wl1271_cmd_header header; + + /* wl1271_ssid_type */ + u8 ssid_type; + u8 ssid_len; + u8 ssid[IW_ESSID_MAX_SIZE]; + u8 padding_1[2]; + + /* Basic rate set */ + __le32 basic_rate_set; + /* Aging period in seconds*/ + __le16 aging_period; + + /* + * This field specifies the time between target beacon + * transmission times (TBTTs), in time units (TUs). + * Valid values are 1 to 1024. + */ + __le16 beacon_interval; + u8 bssid[ETH_ALEN]; + u8 bss_index; + /* Radio band */ + u8 band; + u8 channel; + /* The host link id for the AP's global queue */ + u8 global_hlid; + /* The host link id for the AP's broadcast queue */ + u8 broadcast_hlid; + /* DTIM count */ + u8 dtim_interval; + /* Beacon expiry time in ms */ + u8 beacon_expiry; + u8 padding_2[3]; +} __packed; + +struct wl1271_cmd_add_sta { + struct wl1271_cmd_header header; + + u8 addr[ETH_ALEN]; + u8 hlid; + u8 aid; + u8 psd_type[NUM_ACCESS_CATEGORIES_COPY]; + __le32 supported_rates; + u8 bss_index; + u8 sp_len; + u8 wmm; + u8 padding1; +} __packed; + +struct wl1271_cmd_remove_sta { + struct wl1271_cmd_header header; + + u8 hlid; + u8 reason_opcode; + u8 send_deauth_flag; + u8 padding1; +} __packed; + #endif /* __WL1271_CMD_H__ */ diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index f468d7178a9f..799c36914735 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -41,7 +41,7 @@ static int wl1271_init_hwenc_config(struct wl1271 *wl) return ret; } - ret = wl1271_cmd_set_default_wep_key(wl, wl->default_key); + ret = wl1271_cmd_set_sta_default_wep_key(wl, wl->default_key); if (ret < 0) { wl1271_warning("couldn't set default key"); return ret; diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index b40568e89eb4..9785b4c43c86 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1712,7 +1712,7 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, switch (cmd) { case SET_KEY: - ret = wl1271_cmd_set_key(wl, KEY_ADD_OR_REPLACE, + ret = wl1271_cmd_set_sta_key(wl, KEY_ADD_OR_REPLACE, key_conf->keyidx, key_type, key_conf->keylen, key_conf->key, addr, tx_seq_32, tx_seq_16); @@ -1723,7 +1723,7 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, /* the default WEP key needs to be configured at least once */ if (key_type == KEY_WEP) { - ret = wl1271_cmd_set_default_wep_key(wl, + ret = wl1271_cmd_set_sta_default_wep_key(wl, wl->default_key); if (ret < 0) goto out_sleep; @@ -1738,7 +1738,7 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (!is_broadcast_ether_addr(addr)) break; - ret = wl1271_cmd_set_key(wl, KEY_REMOVE, + ret = wl1271_cmd_set_sta_key(wl, KEY_REMOVE, key_conf->keyidx, key_type, key_conf->keylen, key_conf->key, addr, 0, 0); diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 0cf210d6aa46..442a7bd956ea 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -170,7 +170,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, /* FIXME: do we have to do this if we're not using WEP? */ if (unlikely(wl->default_key != idx)) { - ret = wl1271_cmd_set_default_wep_key(wl, idx); + ret = wl1271_cmd_set_sta_default_wep_key(wl, idx); if (ret < 0) return ret; wl->default_key = idx; diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index c3c30b3abc15..340153f609c7 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -129,6 +129,14 @@ extern u32 wl12xx_debug_level; #define WL1271_DEFAULT_BEACON_INT 100 #define WL1271_DEFAULT_DTIM_PERIOD 1 +#define WL1271_AP_GLOBAL_HLID 0 +#define WL1271_AP_BROADCAST_HLID 1 +#define WL1271_AP_STA_HLID_START 2 + +#define WL1271_AP_BSS_INDEX 0 +#define WL1271_AP_DEF_INACTIV_SEC 300 +#define WL1271_AP_DEF_BEACON_EXP 20 + #define ACX_TX_DESCRIPTORS 32 #define WL1271_AGGR_BUFFER_SIZE (4 * PAGE_SIZE) -- cgit v1.2.3 From 05285cf9b581af05813cfaa60e23227b009b7754 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Fri, 12 Nov 2010 17:15:03 +0200 Subject: wl12xx: AP mode - workaround for FW bug on station remove Sometimes an event indicating station removal is not sent up by firmware. We work around this by always indicating success in when a wait for the event timeouts. Temporary workaround until a FW fix is introduced. Signed-off-by: Arik Nemtsov Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/cmd.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index f9c5ce91cb12..e28d9cab1185 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -222,7 +222,7 @@ int wl1271_cmd_ext_radio_parms(struct wl1271 *wl) * Poll the mailbox event field until any of the bits in the mask is set or a * timeout occurs (WL1271_EVENT_TIMEOUT in msecs) */ -static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask) +static int wl1271_cmd_wait_for_event_or_timeout(struct wl1271 *wl, u32 mask) { u32 events_vector, event; unsigned long timeout; @@ -231,7 +231,8 @@ static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask) do { if (time_after(jiffies, timeout)) { - ieee80211_queue_work(wl->hw, &wl->recovery_work); + wl1271_debug(DEBUG_CMD, "timeout waiting for event %d", + (int)mask); return -ETIMEDOUT; } @@ -249,6 +250,19 @@ static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask) return 0; } +static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask) +{ + int ret; + + ret = wl1271_cmd_wait_for_event_or_timeout(wl, mask); + if (ret != 0) { + ieee80211_queue_work(wl->hw, &wl->recovery_work); + return ret; + } + + return 0; +} + int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type) { struct wl1271_cmd_join *join; @@ -1108,9 +1122,11 @@ int wl1271_cmd_remove_sta(struct wl1271 *wl, u8 hlid) goto out_free; } - ret = wl1271_cmd_wait_for_event(wl, STA_REMOVE_COMPLETE_EVENT_ID); - if (ret < 0) - wl1271_error("cmd remove sta event completion error"); + /* + * We are ok with a timeout here. The event is sometimes not sent + * due to a firmware bug. + */ + wl1271_cmd_wait_for_event_or_timeout(wl, STA_REMOVE_COMPLETE_EVENT_ID); out_free: kfree(cmd); -- cgit v1.2.3 From e0fe371b74326a85029fe8720506e021fe73905a Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sat, 16 Oct 2010 18:19:53 +0200 Subject: wl12xx: AP mode - init sequence Split HW init sequence into AP/STA specific parts The AP specific init sequence includes configuration of templates, rate classes, power mode, etc. Also unmask AP specific events in the event mbox. Separate the differences between AP and STA init into mode specific functions called from wl1271_hw_init. The first is called after radio configuration and the second after memory configuration. Signed-off-by: Arik Nemtsov Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/cmd.h | 7 + drivers/net/wireless/wl12xx/init.c | 352 +++++++++++++++++++++++------ drivers/net/wireless/wl12xx/init.h | 2 +- drivers/net/wireless/wl12xx/main.c | 32 +-- drivers/net/wireless/wl12xx/tx.c | 18 ++ drivers/net/wireless/wl12xx/tx.h | 1 + drivers/net/wireless/wl12xx/wl12xx_80211.h | 5 + 7 files changed, 317 insertions(+), 100 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h index 072bf3c2f24b..c9909e09cd9d 100644 --- a/drivers/net/wireless/wl12xx/cmd.h +++ b/drivers/net/wireless/wl12xx/cmd.h @@ -140,6 +140,13 @@ enum cmd_templ { * For CTS-to-self (FastCTS) mechanism * for BT/WLAN coexistence (SoftGemini). */ CMD_TEMPL_ARP_RSP, + + /* AP-mode specific */ + CMD_TEMPL_AP_BEACON = 13, + CMD_TEMPL_AP_PROBE_RESPONSE, + CMD_TEMPL_AP_ARP_RSP, + CMD_TEMPL_DEAUTH_AP, + CMD_TEMPL_MAX = 0xff }; diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index 799c36914735..bdb61232f80c 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -30,27 +30,9 @@ #include "acx.h" #include "cmd.h" #include "reg.h" +#include "tx.h" -static int wl1271_init_hwenc_config(struct wl1271 *wl) -{ - int ret; - - ret = wl1271_acx_feature_cfg(wl); - if (ret < 0) { - wl1271_warning("couldn't set feature config"); - return ret; - } - - ret = wl1271_cmd_set_sta_default_wep_key(wl, wl->default_key); - if (ret < 0) { - wl1271_warning("couldn't set default key"); - return ret; - } - - return 0; -} - -int wl1271_init_templates_config(struct wl1271 *wl) +int wl1271_sta_init_templates_config(struct wl1271 *wl) { int ret, i; @@ -118,6 +100,132 @@ int wl1271_init_templates_config(struct wl1271 *wl) return 0; } +static int wl1271_ap_init_deauth_template(struct wl1271 *wl) +{ + struct wl12xx_disconn_template *tmpl; + int ret; + + tmpl = kzalloc(sizeof(*tmpl), GFP_KERNEL); + if (!tmpl) { + ret = -ENOMEM; + goto out; + } + + tmpl->header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_DEAUTH); + + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_DEAUTH_AP, + tmpl, sizeof(*tmpl), 0, + wl1271_tx_min_rate_get(wl)); + +out: + kfree(tmpl); + return ret; +} + +static int wl1271_ap_init_null_template(struct wl1271 *wl) +{ + struct ieee80211_hdr_3addr *nullfunc; + int ret; + + nullfunc = kzalloc(sizeof(*nullfunc), GFP_KERNEL); + if (!nullfunc) { + ret = -ENOMEM; + goto out; + } + + nullfunc->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_NULLFUNC | + IEEE80211_FCTL_FROMDS); + + /* nullfunc->addr1 is filled by FW */ + + memcpy(nullfunc->addr2, wl->mac_addr, ETH_ALEN); + memcpy(nullfunc->addr3, wl->mac_addr, ETH_ALEN); + + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, nullfunc, + sizeof(*nullfunc), 0, + wl1271_tx_min_rate_get(wl)); + +out: + kfree(nullfunc); + return ret; +} + +static int wl1271_ap_init_qos_null_template(struct wl1271 *wl) +{ + struct ieee80211_qos_hdr *qosnull; + int ret; + + qosnull = kzalloc(sizeof(*qosnull), GFP_KERNEL); + if (!qosnull) { + ret = -ENOMEM; + goto out; + } + + qosnull->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_NULLFUNC | + IEEE80211_FCTL_FROMDS); + + /* qosnull->addr1 is filled by FW */ + + memcpy(qosnull->addr2, wl->mac_addr, ETH_ALEN); + memcpy(qosnull->addr3, wl->mac_addr, ETH_ALEN); + + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, qosnull, + sizeof(*qosnull), 0, + wl1271_tx_min_rate_get(wl)); + +out: + kfree(qosnull); + return ret; +} + +static int wl1271_ap_init_templates_config(struct wl1271 *wl) +{ + int ret; + + /* + * Put very large empty placeholders for all templates. These + * reserve memory for later. + */ + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_AP_PROBE_RESPONSE, NULL, + sizeof + (struct wl12xx_probe_resp_template), + 0, WL1271_RATE_AUTOMATIC); + if (ret < 0) + return ret; + + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_AP_BEACON, NULL, + sizeof + (struct wl12xx_beacon_template), + 0, WL1271_RATE_AUTOMATIC); + if (ret < 0) + return ret; + + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_DEAUTH_AP, NULL, + sizeof + (struct wl12xx_disconn_template), + 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, 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, WL1271_RATE_AUTOMATIC); + if (ret < 0) + return ret; + + return 0; +} + static int wl1271_init_rx_config(struct wl1271 *wl, u32 config, u32 filter) { int ret; @@ -145,10 +253,6 @@ int wl1271_init_phy_config(struct wl1271 *wl) if (ret < 0) return ret; - ret = wl1271_acx_group_address_tbl(wl, true, NULL, 0); - if (ret < 0) - return ret; - ret = wl1271_acx_service_period_timeout(wl); if (ret < 0) return ret; @@ -213,11 +317,150 @@ static int wl1271_init_beacon_broadcast(struct wl1271 *wl) return 0; } +static int wl1271_sta_hw_init(struct wl1271 *wl) +{ + int ret; + + ret = wl1271_cmd_ext_radio_parms(wl); + if (ret < 0) + return ret; + + ret = wl1271_sta_init_templates_config(wl); + if (ret < 0) + return ret; + + ret = wl1271_acx_group_address_tbl(wl, true, NULL, 0); + if (ret < 0) + return ret; + + /* Initialize connection monitoring thresholds */ + ret = wl1271_acx_conn_monit_params(wl, false); + if (ret < 0) + return ret; + + /* Beacon filtering */ + ret = wl1271_init_beacon_filter(wl); + if (ret < 0) + return ret; + + /* Bluetooth WLAN coexistence */ + ret = wl1271_init_pta(wl); + if (ret < 0) + return ret; + + /* Beacons and broadcast settings */ + ret = wl1271_init_beacon_broadcast(wl); + if (ret < 0) + return ret; + + /* Configure for ELP power saving */ + ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP); + if (ret < 0) + return ret; + + /* Configure rssi/snr averaging weights */ + ret = wl1271_acx_rssi_snr_avg_weights(wl); + if (ret < 0) + return ret; + + ret = wl1271_acx_sta_rate_policies(wl); + if (ret < 0) + return ret; + + return 0; +} + +static int wl1271_sta_hw_init_post_mem(struct wl1271 *wl) +{ + int ret, i; + + ret = wl1271_cmd_set_sta_default_wep_key(wl, wl->default_key); + if (ret < 0) { + wl1271_warning("couldn't set default key"); + return ret; + } + + /* 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) + return ret; + } + + /* disable the keep-alive feature */ + ret = wl1271_acx_keep_alive_mode(wl, false); + if (ret < 0) + return ret; + + return 0; +} + +static int wl1271_ap_hw_init(struct wl1271 *wl) +{ + int ret, i; + + ret = wl1271_ap_init_templates_config(wl); + if (ret < 0) + return ret; + + /* Configure for power always on */ + ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); + if (ret < 0) + return ret; + + /* Configure initial TX rate classes */ + for (i = 0; i < wl->conf.tx.ac_conf_count; i++) { + ret = wl1271_acx_ap_rate_policy(wl, + &wl->conf.tx.ap_rc_conf[i], i); + if (ret < 0) + return ret; + } + + ret = wl1271_acx_ap_rate_policy(wl, + &wl->conf.tx.ap_mgmt_conf, + ACX_TX_AP_MODE_MGMT_RATE); + if (ret < 0) + return ret; + + ret = wl1271_acx_ap_rate_policy(wl, + &wl->conf.tx.ap_bcst_conf, + ACX_TX_AP_MODE_BCST_RATE); + if (ret < 0) + return ret; + + ret = wl1271_acx_max_tx_retry(wl); + if (ret < 0) + return ret; + + return 0; +} + +static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl) +{ + int ret; + + ret = wl1271_ap_init_deauth_template(wl); + if (ret < 0) + return ret; + + ret = wl1271_ap_init_null_template(wl); + if (ret < 0) + return ret; + + ret = wl1271_ap_init_qos_null_template(wl); + if (ret < 0) + return ret; + + return 0; +} + int wl1271_hw_init(struct wl1271 *wl) { struct conf_tx_ac_category *conf_ac; struct conf_tx_tid *conf_tid; int ret, i; + bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS); ret = wl1271_cmd_general_parms(wl); if (ret < 0) @@ -227,12 +470,12 @@ int wl1271_hw_init(struct wl1271 *wl) if (ret < 0) return ret; - ret = wl1271_cmd_ext_radio_parms(wl); - if (ret < 0) - return ret; + /* Mode specific init */ + if (is_ap) + ret = wl1271_ap_hw_init(wl); + else + ret = wl1271_sta_hw_init(wl); - /* Template settings */ - ret = wl1271_init_templates_config(wl); if (ret < 0) return ret; @@ -259,16 +502,6 @@ int wl1271_hw_init(struct wl1271 *wl) if (ret < 0) goto out_free_memmap; - /* Initialize connection monitoring thresholds */ - ret = wl1271_acx_conn_monit_params(wl, false); - if (ret < 0) - goto out_free_memmap; - - /* Beacon filtering */ - ret = wl1271_init_beacon_filter(wl); - if (ret < 0) - goto out_free_memmap; - /* Configure TX patch complete interrupt behavior */ ret = wl1271_acx_tx_config_options(wl); if (ret < 0) @@ -279,21 +512,11 @@ int wl1271_hw_init(struct wl1271 *wl) if (ret < 0) goto out_free_memmap; - /* Bluetooth WLAN coexistence */ - ret = wl1271_init_pta(wl); - if (ret < 0) - goto out_free_memmap; - /* Energy detection */ ret = wl1271_init_energy_detection(wl); if (ret < 0) goto out_free_memmap; - /* Beacons and boradcast settings */ - ret = wl1271_init_beacon_broadcast(wl); - if (ret < 0) - goto out_free_memmap; - /* Default fragmentation threshold */ ret = wl1271_acx_frag_threshold(wl, wl->conf.tx.frag_threshold); if (ret < 0) @@ -321,23 +544,13 @@ int wl1271_hw_init(struct wl1271 *wl) goto out_free_memmap; } - /* Configure TX rate classes */ - ret = wl1271_acx_sta_rate_policies(wl); - if (ret < 0) - goto out_free_memmap; - /* Enable data path */ ret = wl1271_cmd_data_path(wl, 1); if (ret < 0) goto out_free_memmap; - /* Configure for ELP power saving */ - ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP); - if (ret < 0) - goto out_free_memmap; - /* Configure HW encryption */ - ret = wl1271_init_hwenc_config(wl); + ret = wl1271_acx_feature_cfg(wl); if (ret < 0) goto out_free_memmap; @@ -346,21 +559,12 @@ 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; - } + /* Mode specific init - post mem init */ + if (is_ap) + ret = wl1271_ap_hw_init_post_mem(wl); + else + ret = wl1271_sta_hw_init_post_mem(wl); - /* disable the keep-alive feature */ - ret = wl1271_acx_keep_alive_mode(wl, false); - 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; diff --git a/drivers/net/wireless/wl12xx/init.h b/drivers/net/wireless/wl12xx/init.h index 7762421f8602..3a8bd3f426d2 100644 --- a/drivers/net/wireless/wl12xx/init.h +++ b/drivers/net/wireless/wl12xx/init.h @@ -27,7 +27,7 @@ #include "wl12xx.h" int wl1271_hw_init_power_auth(struct wl1271 *wl); -int wl1271_init_templates_config(struct wl1271 *wl); +int wl1271_sta_init_templates_config(struct wl1271 *wl); int wl1271_init_phy_config(struct wl1271 *wl); int wl1271_init_pta(struct wl1271 *wl); int wl1271_init_energy_detection(struct wl1271 *wl); diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 9785b4c43c86..67f6db4354f0 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -434,7 +434,7 @@ static int wl1271_plt_init(struct wl1271 *wl) if (ret < 0) return ret; - ret = wl1271_init_templates_config(wl); + ret = wl1271_sta_init_templates_config(wl); if (ret < 0) return ret; @@ -1363,24 +1363,6 @@ static void wl1271_set_band_rate(struct wl1271 *wl) wl->basic_rate_set = wl->conf.tx.basic_rate_5; } -static u32 wl1271_min_rate_get(struct wl1271 *wl) -{ - int i; - u32 rate = 0; - - if (!wl->basic_rate_set) { - WARN_ON(1); - wl->basic_rate_set = wl->conf.tx.basic_rate; - } - - for (i = 0; !rate; i++) { - if ((wl->basic_rate_set >> i) & 0x1) - rate = 1 << i; - } - - return rate; -} - static int wl1271_handle_idle(struct wl1271 *wl, bool idle) { int ret; @@ -1391,7 +1373,7 @@ static int wl1271_handle_idle(struct wl1271 *wl, bool idle) if (ret < 0) goto out; } - wl->rate_set = wl1271_min_rate_get(wl); + wl->rate_set = wl1271_tx_min_rate_get(wl); wl->sta_rate_set = 0; ret = wl1271_acx_sta_rate_policies(wl); if (ret < 0) @@ -1467,7 +1449,7 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) wl1271_set_band_rate(wl); - wl->basic_rate = wl1271_min_rate_get(wl); + wl->basic_rate = wl1271_tx_min_rate_get(wl); ret = wl1271_acx_sta_rate_policies(wl); if (ret < 0) wl1271_warning("rate policy for update channel " @@ -1927,7 +1909,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, beacon->data, beacon->len, 0, - wl1271_min_rate_get(wl)); + wl1271_tx_min_rate_get(wl)); if (ret < 0) { dev_kfree_skb(beacon); @@ -1943,7 +1925,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, CMD_TEMPL_PROBE_RESPONSE, beacon->data, beacon->len, 0, - wl1271_min_rate_get(wl)); + wl1271_tx_min_rate_get(wl)); dev_kfree_skb(beacon); if (ret < 0) goto out_sleep; @@ -2016,7 +1998,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, rates = bss_conf->basic_rates; wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates); - wl->basic_rate = wl1271_min_rate_get(wl); + wl->basic_rate = wl1271_tx_min_rate_get(wl); ret = wl1271_acx_sta_rate_policies(wl); if (ret < 0) goto out_sleep; @@ -2070,7 +2052,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, /* revert back to minimum rates for the current band */ wl1271_set_band_rate(wl); - wl->basic_rate = wl1271_min_rate_get(wl); + wl->basic_rate = wl1271_tx_min_rate_get(wl); ret = wl1271_acx_sta_rate_policies(wl); if (ret < 0) goto out_sleep; diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 442a7bd956ea..a93fc4702f35 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -521,3 +521,21 @@ void wl1271_tx_flush(struct wl1271 *wl) wl1271_warning("Unable to flush all TX buffers, timed out."); } + +u32 wl1271_tx_min_rate_get(struct wl1271 *wl) +{ + int i; + u32 rate = 0; + + if (!wl->basic_rate_set) { + WARN_ON(1); + wl->basic_rate_set = wl->conf.tx.basic_rate; + } + + for (i = 0; !rate; i++) { + if ((wl->basic_rate_set >> i) & 0x1) + rate = 1 << i; + } + + return rate; +} diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h index 903e5dc69b7a..5ccd22eaf074 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/tx.h @@ -146,5 +146,6 @@ void wl1271_tx_reset(struct wl1271 *wl); void wl1271_tx_flush(struct wl1271 *wl); u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band); u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set); +u32 wl1271_tx_min_rate_get(struct wl1271 *wl); #endif diff --git a/drivers/net/wireless/wl12xx/wl12xx_80211.h b/drivers/net/wireless/wl12xx/wl12xx_80211.h index e2b26fbf68c1..67dcf8f28cd3 100644 --- a/drivers/net/wireless/wl12xx/wl12xx_80211.h +++ b/drivers/net/wireless/wl12xx/wl12xx_80211.h @@ -160,4 +160,9 @@ struct wl12xx_probe_resp_template { struct wl12xx_ie_country country; } __packed; +struct wl12xx_disconn_template { + struct ieee80211_header header; + __le16 disconn_reason; +} __packed; + #endif -- cgit v1.2.3 From ae113b57826b40f1962a6e2417efd757b638e6a9 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sat, 16 Oct 2010 18:45:07 +0200 Subject: wl12xx: AP specific RX filter configuration Set filters according to the mode of operation. Signed-off-by: Arik Nemtsov Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/boot.c | 4 ++-- drivers/net/wireless/wl12xx/main.c | 7 +++---- drivers/net/wireless/wl12xx/rx.c | 11 +++++++++++ drivers/net/wireless/wl12xx/rx.h | 1 + drivers/net/wireless/wl12xx/wl12xx.h | 13 +++++++++++-- 5 files changed, 28 insertions(+), 8 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index b504367f281f..2b1019f67d27 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -28,6 +28,7 @@ #include "boot.h" #include "io.h" #include "event.h" +#include "rx.h" static struct wl1271_partition_set part_table[PART_TABLE_LEN] = { [PART_DOWN] = { @@ -598,8 +599,7 @@ int wl1271_boot(struct wl1271 *wl) wl1271_boot_enable_interrupts(wl); /* set the wl1271 default filters */ - wl->rx_config = WL1271_DEFAULT_RX_CONFIG; - wl->rx_filter = WL1271_DEFAULT_RX_FILTER; + wl1271_set_default_filters(wl); wl1271_event_mbox_config(wl); diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 67f6db4354f0..d1075a5ac406 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1227,8 +1227,7 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw, static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters) { - wl->rx_config = WL1271_DEFAULT_RX_CONFIG; - wl->rx_filter = WL1271_DEFAULT_RX_FILTER; + wl1271_set_default_filters(wl); /* combine requested filters with current filter config */ filters = wl->filters | filters; @@ -2758,8 +2757,8 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->beacon_int = WL1271_DEFAULT_BEACON_INT; wl->default_key = 0; wl->rx_counter = 0; - wl->rx_config = WL1271_DEFAULT_RX_CONFIG; - wl->rx_filter = WL1271_DEFAULT_RX_FILTER; + wl->rx_config = WL1271_DEFAULT_STA_RX_CONFIG; + wl->rx_filter = WL1271_DEFAULT_STA_RX_FILTER; wl->psm_entry_retry = 0; wl->power_level = WL1271_DEFAULT_POWER_LEVEL; wl->basic_rate_set = CONF_TX_RATE_MASK_BASIC; diff --git a/drivers/net/wireless/wl12xx/rx.c b/drivers/net/wireless/wl12xx/rx.c index c6402529eac8..b0c6ddc2a945 100644 --- a/drivers/net/wireless/wl12xx/rx.c +++ b/drivers/net/wireless/wl12xx/rx.c @@ -200,3 +200,14 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status) } wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter); } + +void wl1271_set_default_filters(struct wl1271 *wl) +{ + if (wl->bss_type == BSS_TYPE_AP_BSS) { + wl->rx_config = WL1271_DEFAULT_AP_RX_CONFIG; + wl->rx_filter = WL1271_DEFAULT_AP_RX_FILTER; + } else { + wl->rx_config = WL1271_DEFAULT_STA_RX_CONFIG; + wl->rx_filter = WL1271_DEFAULT_STA_RX_FILTER; + } +} diff --git a/drivers/net/wireless/wl12xx/rx.h b/drivers/net/wireless/wl12xx/rx.h index 3abb26fe0364..f695553f31e0 100644 --- a/drivers/net/wireless/wl12xx/rx.h +++ b/drivers/net/wireless/wl12xx/rx.h @@ -117,5 +117,6 @@ struct wl1271_rx_descriptor { void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status); u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band); +void wl1271_set_default_filters(struct wl1271 *wl); #endif diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 340153f609c7..4bbdb89f1bcb 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -103,15 +103,24 @@ extern u32 wl12xx_debug_level; true); \ } while (0) -#define WL1271_DEFAULT_RX_CONFIG (CFG_UNI_FILTER_EN | \ +#define WL1271_DEFAULT_STA_RX_CONFIG (CFG_UNI_FILTER_EN | \ CFG_BSSID_FILTER_EN | \ CFG_MC_FILTER_EN) -#define WL1271_DEFAULT_RX_FILTER (CFG_RX_RCTS_ACK | CFG_RX_PRSP_EN | \ +#define WL1271_DEFAULT_STA_RX_FILTER (CFG_RX_RCTS_ACK | CFG_RX_PRSP_EN | \ CFG_RX_MGMT_EN | CFG_RX_DATA_EN | \ CFG_RX_CTL_EN | CFG_RX_BCN_EN | \ CFG_RX_AUTH_EN | CFG_RX_ASSOC_EN) +#define WL1271_DEFAULT_AP_RX_CONFIG 0 + +#define WL1271_DEFAULT_AP_RX_FILTER (CFG_RX_RCTS_ACK | CFG_RX_PREQ_EN | \ + CFG_RX_MGMT_EN | CFG_RX_DATA_EN | \ + CFG_RX_CTL_EN | CFG_RX_AUTH_EN | \ + CFG_RX_ASSOC_EN) + + + #define WL1271_FW_NAME "wl1271-fw.bin" #define WL1271_NVS_NAME "wl1271-nvs.bin" -- cgit v1.2.3 From beb6c880720073c233633c45792a4bb5d5fedbd5 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sat, 16 Oct 2010 18:53:48 +0200 Subject: wl12xx: Add AP related definitions to HOST-FW interface Change structures in a non-destructive manner. This means no changes in size or location of existing members used by STA. Signed-off-by: Arik Nemtsov Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/event.h | 7 ++++++- drivers/net/wireless/wl12xx/rx.h | 10 +++++++--- drivers/net/wireless/wl12xx/tx.h | 9 +++++++-- drivers/net/wireless/wl12xx/wl12xx.h | 18 +++++++++++++++++- 4 files changed, 37 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/event.h b/drivers/net/wireless/wl12xx/event.h index fd955f31cec4..1d5ef670d480 100644 --- a/drivers/net/wireless/wl12xx/event.h +++ b/drivers/net/wireless/wl12xx/event.h @@ -116,7 +116,12 @@ struct event_mailbox { u8 scheduled_scan_status; u8 ps_status; - u8 reserved_5[29]; + /* AP FW only */ + u8 hlid_removed; + __le16 sta_aging_status; + __le16 sta_tx_retry_exceeded; + + u8 reserved_5[24]; } __packed; int wl1271_event_unmask(struct wl1271 *wl); diff --git a/drivers/net/wireless/wl12xx/rx.h b/drivers/net/wireless/wl12xx/rx.h index f695553f31e0..8d048b36bbba 100644 --- a/drivers/net/wireless/wl12xx/rx.h +++ b/drivers/net/wireless/wl12xx/rx.h @@ -86,8 +86,9 @@ /* * RX Descriptor status * - * Bits 0-2 - status - * Bits 3-7 - reserved + * Bits 0-2 - error code + * Bits 3-5 - process_id tag (AP mode FW) + * Bits 6-7 - reserved */ #define WL1271_RX_DESC_STATUS_MASK 0x07 @@ -110,7 +111,10 @@ struct wl1271_rx_descriptor { u8 snr; __le32 timestamp; u8 packet_class; - u8 process_id; + union { + u8 process_id; /* STA FW */ + u8 hlid; /* AP FW */ + } __packed; u8 pad_len; u8 reserved; } __packed; diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h index 5ccd22eaf074..05722a560d91 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/tx.h @@ -29,6 +29,7 @@ #define TX_HW_BLOCK_SIZE 252 #define TX_HW_MGMT_PKT_LIFETIME_TU 2000 +#define TX_HW_AP_MODE_PKT_LIFETIME_TU 8000 /* The chipset reference driver states, that the "aid" value 1 * is for infra-BSS, but is still always used */ #define TX_HW_DEFAULT_AID 1 @@ -77,8 +78,12 @@ struct wl1271_tx_hw_descr { u8 id; /* The packet TID value (as User-Priority) */ u8 tid; - /* Identifier of the remote STA in IBSS, 1 in infra-BSS */ - u8 aid; + union { + /* STA - Identifier of the remote STA in IBSS, 1 in infra-BSS */ + u8 aid; + /* AP - host link ID (HLID) */ + u8 hlid; + } __packed; u8 reserved; } __packed; diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 4bbdb89f1bcb..2f855443555c 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -195,6 +195,11 @@ struct wl1271_stats { #define NUM_TX_QUEUES 4 #define NUM_RX_PKT_DESC 8 +#define AP_MAX_STATIONS 5 + +/* Broadcast and Global links + links to stations */ +#define AP_MAX_LINKS (AP_MAX_STATIONS + 2) + /* FW status registers */ struct wl1271_fw_status { __le32 intr; @@ -205,7 +210,18 @@ struct wl1271_fw_status { __le32 rx_pkt_descs[NUM_RX_PKT_DESC]; __le32 tx_released_blks[NUM_TX_QUEUES]; __le32 fw_localtime; - __le32 padding[2]; + + /* Next fields valid only in AP FW */ + + /* + * A bitmap (where each bit represents a single HLID) + * to indicate if the station is in PS mode. + */ + __le32 link_ps_bitmap; + + /* Number of freed MBs per HLID */ + u8 tx_lnk_free_blks[AP_MAX_LINKS]; + u8 padding_1[1]; } __packed; struct wl1271_rx_mem_pool_addr { -- cgit v1.2.3 From e78a287ab7eb73d7003b1c54bec4ca94655f62e9 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sat, 16 Oct 2010 19:07:21 +0200 Subject: wl12xx: Configure AP on BSS info change Configure AP-specific beacon and probe response templates. Start the AP when beaconing is enabled. The wl1271_bss_info_changed() function has been split into AP/STA specific handlers. Signed-off-by: Arik Nemtsov Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 340 ++++++++++++++++++++++++----------- drivers/net/wireless/wl12xx/wl12xx.h | 3 + 2 files changed, 234 insertions(+), 109 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index d1075a5ac406..0f16307137a2 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1843,7 +1843,7 @@ out: return ret; } -static void wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb, +static int wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb, int offset) { u8 *ptr = skb->data + offset; @@ -1853,89 +1853,206 @@ static void wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb, if (ptr[0] == WLAN_EID_SSID) { wl->ssid_len = ptr[1]; memcpy(wl->ssid, ptr+2, wl->ssid_len); - return; + return 0; } ptr += (ptr[1] + 2); } + wl1271_error("No SSID in IEs!\n"); + return -ENOENT; } -static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, +static int wl1271_bss_erp_info_changed(struct wl1271 *wl, struct ieee80211_bss_conf *bss_conf, u32 changed) { - enum wl1271_cmd_ps_mode mode; - struct wl1271 *wl = hw->priv; - struct ieee80211_sta *sta = ieee80211_find_sta(vif, bss_conf->bssid); - bool do_join = false; - bool set_assoc = false; - int ret; + int ret = 0; - wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed"); + if (changed & BSS_CHANGED_ERP_SLOT) { + if (bss_conf->use_short_slot) + ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT); + else + ret = wl1271_acx_slot(wl, SLOT_TIME_LONG); + if (ret < 0) { + wl1271_warning("Set slot time failed %d", ret); + goto out; + } + } - mutex_lock(&wl->mutex); + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + if (bss_conf->use_short_preamble) + wl1271_acx_set_preamble(wl, ACX_PREAMBLE_SHORT); + else + wl1271_acx_set_preamble(wl, ACX_PREAMBLE_LONG); + } - if (unlikely(wl->state == WL1271_STATE_OFF)) - goto out; + if (changed & BSS_CHANGED_ERP_CTS_PROT) { + if (bss_conf->use_cts_prot) + ret = wl1271_acx_cts_protect(wl, CTSPROTECT_ENABLE); + else + ret = wl1271_acx_cts_protect(wl, CTSPROTECT_DISABLE); + if (ret < 0) { + wl1271_warning("Set ctsprotect failed %d", ret); + goto out; + } + } - ret = wl1271_ps_elp_wakeup(wl, false); - if (ret < 0) - goto out; +out: + return ret; +} - if ((changed & BSS_CHANGED_BEACON_INT) && - (wl->bss_type == BSS_TYPE_IBSS)) { - wl1271_debug(DEBUG_ADHOC, "ad-hoc beacon interval updated: %d", +static int wl1271_bss_beacon_info_changed(struct wl1271 *wl, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changed) +{ + bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS); + int ret = 0; + + if ((changed & BSS_CHANGED_BEACON_INT)) { + wl1271_debug(DEBUG_MASTER, "beacon interval updated: %d", bss_conf->beacon_int); wl->beacon_int = bss_conf->beacon_int; - do_join = true; } - if ((changed & BSS_CHANGED_BEACON) && - (wl->bss_type == BSS_TYPE_IBSS)) { - struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); + if ((changed & BSS_CHANGED_BEACON)) { + struct ieee80211_hdr *hdr; + int ieoffset = offsetof(struct ieee80211_mgmt, + u.beacon.variable); + struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif); + u16 tmpl_id; + + if (!beacon) + goto out; + + wl1271_debug(DEBUG_MASTER, "beacon updated"); + + ret = wl1271_ssid_set(wl, beacon, ieoffset); + if (ret < 0) { + dev_kfree_skb(beacon); + goto out; + } + tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON : + CMD_TEMPL_BEACON; + ret = wl1271_cmd_template_set(wl, tmpl_id, + beacon->data, + beacon->len, 0, + wl1271_tx_min_rate_get(wl)); + if (ret < 0) { + dev_kfree_skb(beacon); + goto out; + } + + hdr = (struct ieee80211_hdr *) beacon->data; + hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_PROBE_RESP); + + tmpl_id = is_ap ? CMD_TEMPL_AP_PROBE_RESPONSE : + CMD_TEMPL_PROBE_RESPONSE; + ret = wl1271_cmd_template_set(wl, + tmpl_id, + beacon->data, + beacon->len, 0, + wl1271_tx_min_rate_get(wl)); + dev_kfree_skb(beacon); + if (ret < 0) + goto out; + } + +out: + return ret; +} + +/* AP mode changes */ +static void wl1271_bss_info_changed_ap(struct wl1271 *wl, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changed) +{ + int ret = 0; - wl1271_debug(DEBUG_ADHOC, "ad-hoc beacon updated"); + if ((changed & BSS_CHANGED_BASIC_RATES)) { + u32 rates = bss_conf->basic_rates; + struct conf_tx_rate_class mgmt_rc; - if (beacon) { - struct ieee80211_hdr *hdr; - int ieoffset = offsetof(struct ieee80211_mgmt, - u.beacon.variable); + wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates); + wl->basic_rate = wl1271_tx_min_rate_get(wl); + wl1271_debug(DEBUG_AP, "basic rates: 0x%x", + wl->basic_rate_set); + + /* update the AP management rate policy with the new rates */ + mgmt_rc.enabled_rates = wl->basic_rate_set; + mgmt_rc.long_retry_limit = 10; + mgmt_rc.short_retry_limit = 10; + mgmt_rc.aflags = 0; + ret = wl1271_acx_ap_rate_policy(wl, &mgmt_rc, + ACX_TX_AP_MODE_MGMT_RATE); + if (ret < 0) { + wl1271_error("AP mgmt policy change failed %d", ret); + goto out; + } + } - wl1271_ssid_set(wl, beacon, ieoffset); + ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, changed); + if (ret < 0) + goto out; - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, - beacon->data, - beacon->len, 0, - wl1271_tx_min_rate_get(wl)); + if ((changed & BSS_CHANGED_BEACON_ENABLED)) { + if (bss_conf->enable_beacon) { + if (!test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) { + ret = wl1271_cmd_start_bss(wl); + if (ret < 0) + goto out; - if (ret < 0) { - dev_kfree_skb(beacon); - goto out_sleep; + set_bit(WL1271_FLAG_AP_STARTED, &wl->flags); + wl1271_debug(DEBUG_AP, "started AP"); } + } else { + if (test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) { + ret = wl1271_cmd_stop_bss(wl); + if (ret < 0) + goto out; - hdr = (struct ieee80211_hdr *) beacon->data; - hdr->frame_control = cpu_to_le16( - IEEE80211_FTYPE_MGMT | - IEEE80211_STYPE_PROBE_RESP); + clear_bit(WL1271_FLAG_AP_STARTED, &wl->flags); + wl1271_debug(DEBUG_AP, "stopped AP"); + } + } + } - ret = wl1271_cmd_template_set(wl, - CMD_TEMPL_PROBE_RESPONSE, - beacon->data, - beacon->len, 0, - wl1271_tx_min_rate_get(wl)); - dev_kfree_skb(beacon); - if (ret < 0) - goto out_sleep; + ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed); + if (ret < 0) + goto out; +out: + return; +} - /* Need to update the SSID (for filtering etc) */ - do_join = true; - } +/* STA/IBSS mode changes */ +static void wl1271_bss_info_changed_sta(struct wl1271 *wl, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changed) +{ + bool do_join = false, set_assoc = false; + bool is_ibss = (wl->bss_type == BSS_TYPE_IBSS); + int ret; + struct ieee80211_sta *sta = ieee80211_find_sta(vif, bss_conf->bssid); + + if (is_ibss) { + ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, + changed); + if (ret < 0) + goto out; } - if ((changed & BSS_CHANGED_BEACON_ENABLED) && - (wl->bss_type == BSS_TYPE_IBSS)) { + if ((changed & BSS_CHANGED_BEACON_INT) && is_ibss) + do_join = true; + + /* Need to update the SSID (for filtering etc) */ + if ((changed & BSS_CHANGED_BEACON) && is_ibss) + do_join = true; + + if ((changed & BSS_CHANGED_BEACON_ENABLED) && is_ibss) { wl1271_debug(DEBUG_ADHOC, "ad-hoc beaconing: %s", bss_conf->enable_beacon ? "enabled" : "disabled"); @@ -1946,7 +2063,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, do_join = true; } - if (changed & BSS_CHANGED_CQM) { + if ((changed & BSS_CHANGED_CQM)) { bool enable = false; if (bss_conf->cqm_rssi_thold) enable = true; @@ -1964,24 +2081,24 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, * and enable the BSSID filter */ memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) { - memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); + memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); - ret = wl1271_cmd_build_null_data(wl); - if (ret < 0) - goto out_sleep; + ret = wl1271_cmd_build_null_data(wl); + if (ret < 0) + goto out; - ret = wl1271_build_qos_null_data(wl); - if (ret < 0) - goto out_sleep; + ret = wl1271_build_qos_null_data(wl); + if (ret < 0) + goto out; - /* filter out all packets not from this BSSID */ - wl1271_configure_filters(wl, 0); + /* filter out all packets not from this BSSID */ + wl1271_configure_filters(wl, 0); - /* Need to update the BSSID (for filtering etc) */ - do_join = true; + /* Need to update the BSSID (for filtering etc) */ + do_join = true; } - if (changed & BSS_CHANGED_ASSOC) { + if ((changed & BSS_CHANGED_ASSOC)) { if (bss_conf->assoc) { u32 rates; int ieoffset; @@ -2000,7 +2117,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, wl->basic_rate = wl1271_tx_min_rate_get(wl); ret = wl1271_acx_sta_rate_policies(wl); if (ret < 0) - goto out_sleep; + goto out; /* * with wl1271, we don't need to update the @@ -2010,7 +2127,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, */ ret = wl1271_cmd_build_ps_poll(wl, wl->aid); if (ret < 0) - goto out_sleep; + goto out; /* * Get a template for hardware connection maintenance @@ -2024,17 +2141,19 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, /* enable the connection monitoring feature */ ret = wl1271_acx_conn_monit_params(wl, true); if (ret < 0) - goto out_sleep; + goto out; /* 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)) { + enum wl1271_cmd_ps_mode mode; + mode = STATION_POWER_SAVE_MODE; ret = wl1271_ps_set_mode(wl, mode, wl->basic_rate, true); if (ret < 0) - goto out_sleep; + goto out; } } else { /* use defaults when not associated */ @@ -2054,7 +2173,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, wl->basic_rate = wl1271_tx_min_rate_get(wl); ret = wl1271_acx_sta_rate_policies(wl); if (ret < 0) - goto out_sleep; + goto out; /* disable connection monitor features */ ret = wl1271_acx_conn_monit_params(wl, false); @@ -2062,43 +2181,17 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, /* Disable the keep-alive feature */ ret = wl1271_acx_keep_alive_mode(wl, false); if (ret < 0) - goto out_sleep; + goto out; /* restore the bssid filter and go to dummy bssid */ wl1271_unjoin(wl); wl1271_dummy_join(wl); } - - } - - if (changed & BSS_CHANGED_ERP_SLOT) { - if (bss_conf->use_short_slot) - ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT); - else - ret = wl1271_acx_slot(wl, SLOT_TIME_LONG); - if (ret < 0) { - wl1271_warning("Set slot time failed %d", ret); - goto out_sleep; - } - } - - if (changed & BSS_CHANGED_ERP_PREAMBLE) { - if (bss_conf->use_short_preamble) - wl1271_acx_set_preamble(wl, ACX_PREAMBLE_SHORT); - else - wl1271_acx_set_preamble(wl, ACX_PREAMBLE_LONG); } - if (changed & BSS_CHANGED_ERP_CTS_PROT) { - if (bss_conf->use_cts_prot) - ret = wl1271_acx_cts_protect(wl, CTSPROTECT_ENABLE); - else - ret = wl1271_acx_cts_protect(wl, CTSPROTECT_DISABLE); - if (ret < 0) { - wl1271_warning("Set ctsprotect failed %d", ret); - goto out_sleep; - } - } + ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed); + if (ret < 0) + goto out; /* * Takes care of: New association with HT enable, @@ -2110,13 +2203,13 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true); if (ret < 0) { wl1271_warning("Set ht cap true failed %d", ret); - goto out_sleep; + goto out; } ret = wl1271_acx_set_ht_information(wl, bss_conf->ht_operation_mode); if (ret < 0) { wl1271_warning("Set ht information failed %d", ret); - goto out_sleep; + goto out; } } /* @@ -2127,7 +2220,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, false); if (ret < 0) { wl1271_warning("Set ht cap false failed %d", ret); - goto out_sleep; + goto out; } } @@ -2146,7 +2239,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, ret = wl1271_cmd_build_arp_rsp(wl, addr); if (ret < 0) { wl1271_warning("build arp rsp failed: %d", ret); - goto out_sleep; + goto out; } ret = wl1271_acx_arp_ip_filter(wl, @@ -2157,18 +2250,47 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, ret = wl1271_acx_arp_ip_filter(wl, 0, addr); if (ret < 0) - goto out_sleep; + goto out; } if (do_join) { ret = wl1271_join(wl, set_assoc); if (ret < 0) { wl1271_warning("cmd join failed %d", ret); - goto out_sleep; + goto out; } } -out_sleep: +out: + return; +} + +static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changed) +{ + struct wl1271 *wl = hw->priv; + bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS); + int ret; + + wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed 0x%x", + (int)changed); + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state == WL1271_STATE_OFF)) + goto out; + + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + + if (is_ap) + wl1271_bss_info_changed_ap(wl, vif, bss_conf, changed); + else + wl1271_bss_info_changed_sta(wl, vif, bss_conf, changed); + wl1271_ps_elp_sleep(wl); out: diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 2f855443555c..981bb020e535 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -57,6 +57,8 @@ enum { DEBUG_SDIO = BIT(14), DEBUG_FILTERS = BIT(15), DEBUG_ADHOC = BIT(16), + DEBUG_AP = BIT(17), + DEBUG_MASTER = (DEBUG_ADHOC | DEBUG_AP), DEBUG_ALL = ~0, }; @@ -284,6 +286,7 @@ struct wl1271 { #define WL1271_FLAG_PSPOLL_FAILURE (12) #define WL1271_FLAG_STA_STATE_SENT (13) #define WL1271_FLAG_FW_TX_BUSY (14) +#define WL1271_FLAG_AP_STARTED (15) unsigned long flags; struct wl1271_partition_set part; -- cgit v1.2.3 From bee0ffec7766eae8c574cc1b07b739b05ba295c3 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sat, 16 Oct 2010 19:17:02 +0200 Subject: wl12xx: AP mode config in ieee80211_ops.config Separate configuration according to mode. AP has different rate set configuration and no handling of idle-state. Signed-off-by: Arik Nemtsov Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 52 ++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 22 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 0f16307137a2..01f3f0264fb4 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1362,7 +1362,7 @@ static void wl1271_set_band_rate(struct wl1271 *wl) wl->basic_rate_set = wl->conf.tx.basic_rate_5; } -static int wl1271_handle_idle(struct wl1271 *wl, bool idle) +static int wl1271_sta_handle_idle(struct wl1271 *wl, bool idle) { int ret; @@ -1403,14 +1403,17 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) struct wl1271 *wl = hw->priv; struct ieee80211_conf *conf = &hw->conf; int channel, ret = 0; + bool is_ap; channel = ieee80211_frequency_to_channel(conf->channel->center_freq); - wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s", + wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s" + " changed 0x%x", channel, conf->flags & IEEE80211_CONF_PS ? "on" : "off", conf->power_level, - conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use"); + conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use", + changed); /* * mac80211 will go to idle nearly immediately after transmitting some @@ -1428,6 +1431,8 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) goto out; } + is_ap = (wl->bss_type == BSS_TYPE_AP_BSS); + ret = wl1271_ps_elp_wakeup(wl, false); if (ret < 0) goto out; @@ -1439,31 +1444,34 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) wl->band = conf->channel->band; wl->channel = channel; - /* - * FIXME: the mac80211 should really provide a fixed rate - * to use here. for now, just use the smallest possible rate - * for the band as a fixed rate for association frames and - * other control messages. - */ - if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) - wl1271_set_band_rate(wl); - - wl->basic_rate = wl1271_tx_min_rate_get(wl); - ret = wl1271_acx_sta_rate_policies(wl); - if (ret < 0) - wl1271_warning("rate policy for update channel " - "failed %d", ret); + if (!is_ap) { + /* + * FIXME: the mac80211 should really provide a fixed + * rate to use here. for now, just use the smallest + * possible rate for the band as a fixed rate for + * association frames and other control messages. + */ + if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) + wl1271_set_band_rate(wl); - if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) { - ret = wl1271_join(wl, false); + wl->basic_rate = wl1271_tx_min_rate_get(wl); + ret = wl1271_acx_sta_rate_policies(wl); if (ret < 0) - wl1271_warning("cmd join to update channel " + wl1271_warning("rate policy for channel " "failed %d", ret); + + if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) { + ret = wl1271_join(wl, false); + if (ret < 0) + wl1271_warning("cmd join on channel " + "failed %d", ret); + } } } - if (changed & IEEE80211_CONF_CHANGE_IDLE) { - ret = wl1271_handle_idle(wl, conf->flags & IEEE80211_CONF_IDLE); + if (changed & IEEE80211_CONF_CHANGE_IDLE && !is_ap) { + ret = wl1271_sta_handle_idle(wl, + conf->flags & IEEE80211_CONF_IDLE); if (ret < 0) wl1271_warning("idle mode change failed %d", ret); } -- cgit v1.2.3 From 7d0578693107887d52d50b89723be7fa0a41cd36 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sat, 16 Oct 2010 19:25:35 +0200 Subject: wl12xx: AP mode - change filter config Do not configure a group address table in AP mode Signed-off-by: Arik Nemtsov Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 01f3f0264fb4..8e5d435f63f7 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1578,7 +1578,8 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw, struct wl1271 *wl = hw->priv; int ret; - wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter"); + wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter changed %x" + " total %x", changed, *total); mutex_lock(&wl->mutex); @@ -1592,15 +1593,16 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw, if (ret < 0) goto out; - - if (*total & FIF_ALLMULTI) - ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0); - else if (fp) - ret = wl1271_acx_group_address_tbl(wl, fp->enabled, - fp->mc_list, - fp->mc_list_length); - if (ret < 0) - goto out_sleep; + if (wl->bss_type != BSS_TYPE_AP_BSS) { + if (*total & FIF_ALLMULTI) + ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0); + else if (fp) + ret = wl1271_acx_group_address_tbl(wl, fp->enabled, + fp->mc_list, + fp->mc_list_length); + if (ret < 0) + goto out_sleep; + } /* determine, whether supported filter values have changed */ if (changed == 0) -- cgit v1.2.3 From f84f7d78bbfbbb17faa64bcca5799865074e1e7b Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sat, 16 Oct 2010 20:21:23 +0200 Subject: wl12xx: AP mode - add STA add/remove ops Allocate and free host link IDs (HLIDs) for each link. A per-STA data structure keeps the HLID of each STA. Signed-off-by: Arik Nemtsov Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 112 +++++++++++++++++++++++++++++++++++ drivers/net/wireless/wl12xx/wl12xx.h | 7 +++ 2 files changed, 119 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 8e5d435f63f7..739fee640528 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1192,6 +1192,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) wl->flags = 0; wl->vif = NULL; wl->filters = 0; + memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map)); for (i = 0; i < NUM_TX_QUEUES; i++) wl->tx_blocks_freed[i] = 0; @@ -2401,6 +2402,113 @@ static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx, return 0; } +static int wl1271_allocate_hlid(struct wl1271 *wl, + struct ieee80211_sta *sta, + u8 *hlid) +{ + struct wl1271_station *wl_sta; + int id; + + id = find_first_zero_bit(wl->ap_hlid_map, AP_MAX_STATIONS); + if (id >= AP_MAX_STATIONS) { + wl1271_warning("could not allocate HLID - too much stations"); + return -EBUSY; + } + + wl_sta = (struct wl1271_station *)sta->drv_priv; + + __set_bit(id, wl->ap_hlid_map); + wl_sta->hlid = WL1271_AP_STA_HLID_START + id; + *hlid = wl_sta->hlid; + return 0; +} + +static void wl1271_free_hlid(struct wl1271 *wl, u8 hlid) +{ + int id = hlid - WL1271_AP_STA_HLID_START; + + __clear_bit(id, wl->ap_hlid_map); +} + +static int wl1271_op_sta_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct wl1271 *wl = hw->priv; + int ret = 0; + u8 hlid; + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state == WL1271_STATE_OFF)) + goto out; + + if (wl->bss_type != BSS_TYPE_AP_BSS) + goto out; + + wl1271_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid); + + ret = wl1271_allocate_hlid(wl, sta, &hlid); + if (ret < 0) + goto out; + + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + + ret = wl1271_cmd_add_sta(wl, sta, hlid); + if (ret < 0) + goto out_sleep; + +out_sleep: + wl1271_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); + return ret; +} + +static int wl1271_op_sta_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct wl1271 *wl = hw->priv; + struct wl1271_station *wl_sta; + int ret = 0, id; + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state == WL1271_STATE_OFF)) + goto out; + + if (wl->bss_type != BSS_TYPE_AP_BSS) + goto out; + + wl1271_debug(DEBUG_MAC80211, "mac80211 remove sta %d", (int)sta->aid); + + wl_sta = (struct wl1271_station *)sta->drv_priv; + id = wl_sta->hlid - WL1271_AP_STA_HLID_START; + if (WARN_ON(!test_bit(id, wl->ap_hlid_map))) + goto out; + + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + + ret = wl1271_cmd_remove_sta(wl, wl_sta->hlid); + if (ret < 0) + goto out_sleep; + + wl1271_free_hlid(wl, wl_sta->hlid); + +out_sleep: + wl1271_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); + return ret; +} + /* can't be const, mac80211 writes to this */ static struct ieee80211_rate wl1271_rates[] = { { .bitrate = 10, @@ -2647,6 +2755,8 @@ static const struct ieee80211_ops wl1271_ops = { .conf_tx = wl1271_op_conf_tx, .get_tsf = wl1271_op_get_tsf, .get_survey = wl1271_op_get_survey, + .sta_add = wl1271_op_sta_add, + .sta_remove = wl1271_op_sta_remove, CFG80211_TESTMODE_CMD(wl1271_tm_cmd) }; @@ -2840,6 +2950,8 @@ int wl1271_init_ieee80211(struct wl1271 *wl) SET_IEEE80211_DEV(wl->hw, wl1271_wl_to_dev(wl)); + wl->hw->sta_data_size = sizeof(struct wl1271_station); + return 0; } EXPORT_SYMBOL_GPL(wl1271_init_ieee80211); diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 981bb020e535..a87ef8e3cac7 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -435,6 +435,13 @@ struct wl1271 { /* Most recently reported noise in dBm */ s8 noise; + + /* map for HLIDs of associated stations - when operating in AP mode */ + unsigned long ap_hlid_map[BITS_TO_LONGS(AP_MAX_STATIONS)]; +}; + +struct wl1271_station { + u8 hlid; }; int wl1271_plt_start(struct wl1271 *wl); -- cgit v1.2.3 From c6c8a65de6d3aa8daa93beeac390f49a21d0be36 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sat, 16 Oct 2010 20:27:53 +0200 Subject: wl12xx: AP mode - changes in TX path When in AP mode set appropriate HLID and rate policy for each skb. Respond to supported-rates related changes in op_tx only when acting as STA. Signed-off-by: Arik Nemtsov Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 3 ++- drivers/net/wireless/wl12xx/tx.c | 55 +++++++++++++++++++++++++++++++------- 2 files changed, 48 insertions(+), 10 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 739fee640528..ed5e2fc21ea2 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -943,7 +943,8 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) spin_lock_irqsave(&wl->wl_lock, flags); if (sta && (sta->supp_rates[conf->channel->band] != - (wl->sta_rate_set & HW_BG_RATES_MASK))) { + (wl->sta_rate_set & HW_BG_RATES_MASK)) && + wl->bss_type != BSS_TYPE_AP_BSS) { wl->sta_rate_set = sta->supp_rates[conf->channel->band]; set_bit(WL1271_FLAG_STA_RATES_CHANGED, &wl->flags); } diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index a93fc4702f35..3245c240cbdd 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -23,6 +23,7 @@ #include #include +#include #include "wl12xx.h" #include "io.h" @@ -99,7 +100,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, { struct timespec ts; struct wl1271_tx_hw_descr *desc; - int pad, ac; + int pad, ac, rate_idx; s64 hosttime; u16 tx_attr; @@ -117,7 +118,11 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, getnstimeofday(&ts); hosttime = (timespec_to_ns(&ts) >> 10); desc->start_time = cpu_to_le32(hosttime - wl->time_offset); - desc->life_time = cpu_to_le16(TX_HW_MGMT_PKT_LIFETIME_TU); + + if (wl->bss_type != BSS_TYPE_AP_BSS) + desc->life_time = cpu_to_le16(TX_HW_MGMT_PKT_LIFETIME_TU); + else + desc->life_time = cpu_to_le16(TX_HW_AP_MODE_PKT_LIFETIME_TU); /* configure the tx attributes */ tx_attr = wl->session_counter << TX_HW_ATTR_OFST_SESSION_COUNTER; @@ -125,7 +130,41 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, /* queue (we use same identifiers for tid's and ac's */ ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); desc->tid = ac; - desc->aid = TX_HW_DEFAULT_AID; + + if (wl->bss_type != BSS_TYPE_AP_BSS) { + desc->aid = TX_HW_DEFAULT_AID; + + /* if the packets are destined for AP (have a STA entry) + send them with AP rate policies, otherwise use default + basic rates */ + if (control->control.sta) + rate_idx = ACX_TX_AP_FULL_RATE; + else + rate_idx = ACX_TX_BASIC_RATE; + } else { + if (control->control.sta) { + struct wl1271_station *wl_sta; + + wl_sta = (struct wl1271_station *) + control->control.sta->drv_priv; + desc->hlid = wl_sta->hlid; + rate_idx = ac; + } else { + struct ieee80211_hdr *hdr; + + hdr = (struct ieee80211_hdr *) + (skb->data + sizeof(*desc)); + if (ieee80211_is_mgmt(hdr->frame_control)) { + desc->hlid = WL1271_AP_GLOBAL_HLID; + rate_idx = ACX_TX_AP_MODE_MGMT_RATE; + } else { + desc->hlid = WL1271_AP_BROADCAST_HLID; + rate_idx = ACX_TX_AP_MODE_BCST_RATE; + } + } + } + + tx_attr |= rate_idx << TX_HW_ATTR_OFST_RATE_POLICY; desc->reserved = 0; /* align the length (and store in terms of words) */ @@ -136,14 +175,12 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, pad = pad - skb->len; tx_attr |= pad << TX_HW_ATTR_OFST_LAST_WORD_PAD; - /* if the packets are destined for AP (have a STA entry) send them - with AP rate policies, otherwise use default basic rates */ - if (control->control.sta) - tx_attr |= ACX_TX_AP_FULL_RATE << TX_HW_ATTR_OFST_RATE_POLICY; - desc->tx_attr = cpu_to_le16(tx_attr); - wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d", pad); + wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d hlid: %d " + "tx_attr: 0x%x len: %d life: %d mem: %d", pad, (int)desc->hlid, + (int)desc->tx_attr, (int)desc->length, (int)desc->life_time, + (int)desc->total_mem_blocks); } /* caller must hold wl->mutex */ -- cgit v1.2.3 From 488fc540472dee7b8c9fc592e4f024aafe8b605f Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sat, 16 Oct 2010 20:33:45 +0200 Subject: wl12xx: AP mode - record TX configuration settings Record TX configuration settings in the "conf" member of our global structure (struct wl1271) if conf_tx is called when the firmware is not loaded. Later on when the firmware is loaded, we apply the tx conf as part of the init sequence. Important for AP mode since conf_tx is called before add_interface (where the firmware is initialized). Signed-off-by: Arik Nemtsov Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 72 +++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 24 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index ed5e2fc21ea2..8b897e337fbb 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2314,42 +2314,66 @@ static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue, { struct wl1271 *wl = hw->priv; u8 ps_scheme; - int ret; + int ret = 0; mutex_lock(&wl->mutex); wl1271_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue); - if (unlikely(wl->state == WL1271_STATE_OFF)) { - ret = -EAGAIN; - goto out; - } - - ret = wl1271_ps_elp_wakeup(wl, false); - if (ret < 0) - goto out; - - /* the txop is confed in units of 32us by the mac80211, we need us */ - ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue), - params->cw_min, params->cw_max, - params->aifs, params->txop << 5); - if (ret < 0) - goto out_sleep; - if (params->uapsd) ps_scheme = CONF_PS_SCHEME_UPSD_TRIGGER; else ps_scheme = CONF_PS_SCHEME_LEGACY; - ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue), - CONF_CHANNEL_TYPE_EDCF, - wl1271_tx_get_queue(queue), - ps_scheme, CONF_ACK_POLICY_LEGACY, 0, 0); - if (ret < 0) - goto out_sleep; + if (wl->state == WL1271_STATE_OFF) { + /* + * If the state is off, the parameters will be recorded and + * configured on init. This happens in AP-mode. + */ + struct conf_tx_ac_category *conf_ac = + &wl->conf.tx.ac_conf[wl1271_tx_get_queue(queue)]; + struct conf_tx_tid *conf_tid = + &wl->conf.tx.tid_conf[wl1271_tx_get_queue(queue)]; + + conf_ac->ac = wl1271_tx_get_queue(queue); + conf_ac->cw_min = (u8)params->cw_min; + conf_ac->cw_max = params->cw_max; + conf_ac->aifsn = params->aifs; + conf_ac->tx_op_limit = params->txop << 5; + + conf_tid->queue_id = wl1271_tx_get_queue(queue); + conf_tid->channel_type = CONF_CHANNEL_TYPE_EDCF; + conf_tid->tsid = wl1271_tx_get_queue(queue); + conf_tid->ps_scheme = ps_scheme; + conf_tid->ack_policy = CONF_ACK_POLICY_LEGACY; + conf_tid->apsd_conf[0] = 0; + conf_tid->apsd_conf[1] = 0; + } else { + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + + /* + * the txop is confed in units of 32us by the mac80211, + * we need us + */ + ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue), + params->cw_min, params->cw_max, + params->aifs, params->txop << 5); + if (ret < 0) + goto out_sleep; + + ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue), + CONF_CHANNEL_TYPE_EDCF, + wl1271_tx_get_queue(queue), + ps_scheme, CONF_ACK_POLICY_LEGACY, + 0, 0); + if (ret < 0) + goto out_sleep; out_sleep: - wl1271_ps_elp_sleep(wl); + wl1271_ps_elp_sleep(wl); + } out: mutex_unlock(&wl->mutex); -- cgit v1.2.3 From 7f179b468963564aa3faa5729fb3153c08b3d7c1 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sat, 16 Oct 2010 21:39:06 +0200 Subject: wl12xx: AP mode - encryption support Encryption key configuration is different for AP/STA modes. AP encryption keys are recorded when the BSS is not started. On BSS start they are propagated to the AP (in wl1271_ap_init_hwenc). Signed-off-by: Arik Nemtsov Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/cmd.h | 1 - drivers/net/wireless/wl12xx/main.c | 220 +++++++++++++++++++++++++++++------ drivers/net/wireless/wl12xx/tx.c | 30 ++++- drivers/net/wireless/wl12xx/wl12xx.h | 16 +++ 4 files changed, 223 insertions(+), 44 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h index c9909e09cd9d..751281414006 100644 --- a/drivers/net/wireless/wl12xx/cmd.h +++ b/drivers/net/wireless/wl12xx/cmd.h @@ -291,7 +291,6 @@ struct wl1271_cmd_ps_params { /* HW encryption keys */ #define NUM_ACCESS_CATEGORIES_COPY 4 -#define MAX_KEY_SIZE 32 enum wl1271_cmd_key_action { KEY_ADD_OR_REPLACE = 1, diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 8b897e337fbb..4ca46b2d7f34 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -296,6 +296,7 @@ static struct conf_drv_settings default_conf = { }; static void __wl1271_op_remove_interface(struct wl1271 *wl); +static void wl1271_free_ap_keys(struct wl1271 *wl); static void wl1271_device_release(struct device *dev) @@ -1193,6 +1194,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) wl->flags = 0; wl->vif = NULL; wl->filters = 0; + wl1271_free_ap_keys(wl); memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map)); for (i = 0; i < NUM_TX_QUEUES; i++) @@ -1627,38 +1629,192 @@ out: kfree(fp); } +static int wl1271_record_ap_key(struct wl1271 *wl, u8 id, u8 key_type, + u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32, + u16 tx_seq_16) +{ + struct wl1271_ap_key *ap_key; + int i; + + wl1271_debug(DEBUG_CRYPT, "record ap key id %d", (int)id); + + if (key_size > MAX_KEY_SIZE) + return -EINVAL; + + /* + * Find next free entry in ap_keys. Also check we are not replacing + * an existing key. + */ + for (i = 0; i < MAX_NUM_KEYS; i++) { + if (wl->recorded_ap_keys[i] == NULL) + break; + + if (wl->recorded_ap_keys[i]->id == id) { + wl1271_warning("trying to record key replacement"); + return -EINVAL; + } + } + + if (i == MAX_NUM_KEYS) + return -EBUSY; + + ap_key = kzalloc(sizeof(*ap_key), GFP_KERNEL); + if (!ap_key) + return -ENOMEM; + + ap_key->id = id; + ap_key->key_type = key_type; + ap_key->key_size = key_size; + memcpy(ap_key->key, key, key_size); + ap_key->hlid = hlid; + ap_key->tx_seq_32 = tx_seq_32; + ap_key->tx_seq_16 = tx_seq_16; + + wl->recorded_ap_keys[i] = ap_key; + return 0; +} + +static void wl1271_free_ap_keys(struct wl1271 *wl) +{ + int i; + + for (i = 0; i < MAX_NUM_KEYS; i++) { + kfree(wl->recorded_ap_keys[i]); + wl->recorded_ap_keys[i] = NULL; + } +} + +static int wl1271_ap_init_hwenc(struct wl1271 *wl) +{ + int i, ret = 0; + struct wl1271_ap_key *key; + bool wep_key_added = false; + + for (i = 0; i < MAX_NUM_KEYS; i++) { + if (wl->recorded_ap_keys[i] == NULL) + break; + + key = wl->recorded_ap_keys[i]; + ret = wl1271_cmd_set_ap_key(wl, KEY_ADD_OR_REPLACE, + key->id, key->key_type, + key->key_size, key->key, + key->hlid, key->tx_seq_32, + key->tx_seq_16); + if (ret < 0) + goto out; + + if (key->key_type == KEY_WEP) + wep_key_added = true; + } + + if (wep_key_added) { + ret = wl1271_cmd_set_ap_default_wep_key(wl, wl->default_key); + if (ret < 0) + goto out; + } + +out: + wl1271_free_ap_keys(wl); + return ret; +} + +static int wl1271_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, + u8 key_size, const u8 *key, u32 tx_seq_32, + u16 tx_seq_16, struct ieee80211_sta *sta) +{ + int ret; + bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS); + + if (is_ap) { + struct wl1271_station *wl_sta; + u8 hlid; + + if (sta) { + wl_sta = (struct wl1271_station *)sta->drv_priv; + hlid = wl_sta->hlid; + } else { + hlid = WL1271_AP_BROADCAST_HLID; + } + + if (!test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) { + /* + * We do not support removing keys after AP shutdown. + * Pretend we do to make mac80211 happy. + */ + if (action != KEY_ADD_OR_REPLACE) + return 0; + + ret = wl1271_record_ap_key(wl, id, + key_type, key_size, + key, hlid, tx_seq_32, + tx_seq_16); + } else { + ret = wl1271_cmd_set_ap_key(wl, action, + id, key_type, key_size, + key, hlid, tx_seq_32, + tx_seq_16); + } + + if (ret < 0) + return ret; + } else { + const u8 *addr; + static const u8 bcast_addr[ETH_ALEN] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + + addr = sta ? sta->addr : bcast_addr; + + if (is_zero_ether_addr(addr)) { + /* We dont support TX only encryption */ + return -EOPNOTSUPP; + } + + /* The wl1271 does not allow to remove unicast keys - they + will be cleared automatically on next CMD_JOIN. Ignore the + request silently, as we dont want the mac80211 to emit + an error message. */ + if (action == KEY_REMOVE && !is_broadcast_ether_addr(addr)) + return 0; + + ret = wl1271_cmd_set_sta_key(wl, action, + id, key_type, key_size, + key, addr, tx_seq_32, + tx_seq_16); + if (ret < 0) + return ret; + + /* the default WEP key needs to be configured at least once */ + if (key_type == KEY_WEP) { + ret = wl1271_cmd_set_sta_default_wep_key(wl, + wl->default_key); + if (ret < 0) + return ret; + } + } + + return 0; +} + static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key_conf) { struct wl1271 *wl = hw->priv; - const u8 *addr; int ret; u32 tx_seq_32 = 0; u16 tx_seq_16 = 0; u8 key_type; - static const u8 bcast_addr[ETH_ALEN] = - { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - wl1271_debug(DEBUG_MAC80211, "mac80211 set key"); - addr = sta ? sta->addr : bcast_addr; - - wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd); - wl1271_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN); + wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x sta: %p", cmd, sta); wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x", key_conf->cipher, key_conf->keyidx, key_conf->keylen, key_conf->flags); wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen); - if (is_zero_ether_addr(addr)) { - /* We dont support TX only encryption */ - ret = -EOPNOTSUPP; - goto out; - } - mutex_lock(&wl->mutex); if (unlikely(wl->state == WL1271_STATE_OFF)) { @@ -1705,36 +1861,21 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, switch (cmd) { case SET_KEY: - ret = wl1271_cmd_set_sta_key(wl, KEY_ADD_OR_REPLACE, - key_conf->keyidx, key_type, - key_conf->keylen, key_conf->key, - addr, tx_seq_32, tx_seq_16); + ret = wl1271_set_key(wl, KEY_ADD_OR_REPLACE, + key_conf->keyidx, key_type, + key_conf->keylen, key_conf->key, + tx_seq_32, tx_seq_16, sta); if (ret < 0) { wl1271_error("Could not add or replace key"); goto out_sleep; } - - /* the default WEP key needs to be configured at least once */ - if (key_type == KEY_WEP) { - ret = wl1271_cmd_set_sta_default_wep_key(wl, - wl->default_key); - if (ret < 0) - goto out_sleep; - } break; case DISABLE_KEY: - /* The wl1271 does not allow to remove unicast keys - they - will be cleared automatically on next CMD_JOIN. Ignore the - request silently, as we dont want the mac80211 to emit - an error message. */ - if (!is_broadcast_ether_addr(addr)) - break; - - ret = wl1271_cmd_set_sta_key(wl, KEY_REMOVE, - key_conf->keyidx, key_type, - key_conf->keylen, key_conf->key, - addr, 0, 0); + ret = wl1271_set_key(wl, KEY_REMOVE, + key_conf->keyidx, key_type, + key_conf->keylen, key_conf->key, + 0, 0, sta); if (ret < 0) { wl1271_error("Could not remove key"); goto out_sleep; @@ -1753,7 +1894,6 @@ out_sleep: out_unlock: mutex_unlock(&wl->mutex); -out: return ret; } @@ -2019,6 +2159,10 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl, set_bit(WL1271_FLAG_AP_STARTED, &wl->flags); wl1271_debug(DEBUG_AP, "started AP"); + + ret = wl1271_ap_init_hwenc(wl); + if (ret < 0) + goto out; } } else { if (test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) { diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 3245c240cbdd..2347f2552f14 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -31,6 +31,23 @@ #include "ps.h" #include "tx.h" +static int wl1271_set_default_wep_key(struct wl1271 *wl, u8 id) +{ + int ret; + bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS); + + if (is_ap) + ret = wl1271_cmd_set_ap_default_wep_key(wl, id); + else + ret = wl1271_cmd_set_sta_default_wep_key(wl, id); + + if (ret < 0) + return ret; + + wl1271_debug(DEBUG_CRYPT, "default wep key idx: %d", (int)id); + return 0; +} + static int wl1271_alloc_tx_id(struct wl1271 *wl, struct sk_buff *skb) { int id; @@ -190,7 +207,6 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, struct ieee80211_tx_info *info; u32 extra = 0; int ret = 0; - u8 idx; u32 total_len; if (!skb) @@ -203,11 +219,15 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, extra = WL1271_TKIP_IV_SPACE; if (info->control.hw_key) { - idx = info->control.hw_key->hw_key_idx; + bool is_wep; + u8 idx = info->control.hw_key->hw_key_idx; + u32 cipher = info->control.hw_key->cipher; + + is_wep = (cipher == WLAN_CIPHER_SUITE_WEP40) || + (cipher == WLAN_CIPHER_SUITE_WEP104); - /* FIXME: do we have to do this if we're not using WEP? */ - if (unlikely(wl->default_key != idx)) { - ret = wl1271_cmd_set_sta_default_wep_key(wl, idx); + if (unlikely(is_wep && wl->default_key != idx)) { + ret = wl1271_set_default_wep_key(wl, idx); if (ret < 0) return ret; wl->default_key = idx; diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index a87ef8e3cac7..6b2c763e4908 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -253,6 +253,19 @@ struct wl1271_if_operations { void (*disable_irq)(struct wl1271 *wl); }; +#define MAX_NUM_KEYS 14 +#define MAX_KEY_SIZE 32 + +struct wl1271_ap_key { + u8 id; + u8 key_type; + u8 key_size; + u8 key[MAX_KEY_SIZE]; + u8 hlid; + u32 tx_seq_32; + u16 tx_seq_16; +}; + struct wl1271 { struct platform_device *plat_dev; struct ieee80211_hw *hw; @@ -438,6 +451,9 @@ struct wl1271 { /* map for HLIDs of associated stations - when operating in AP mode */ unsigned long ap_hlid_map[BITS_TO_LONGS(AP_MAX_STATIONS)]; + + /* recoreded keys for AP-mode - set here before AP startup */ + struct wl1271_ap_key *recorded_ap_keys[MAX_NUM_KEYS]; }; struct wl1271_station { -- cgit v1.2.3 From 166d504ebaed391f1a411c69a5659632249ba711 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sat, 16 Oct 2010 21:44:57 +0200 Subject: wl12xx: AP mode - fetch appropriate firmware for AP AP and STA modes use different firmwares. Differentiate the firmware files by name and fetch the appropriate one when add_interface is called by mac80211. The STA firmware is chosen for PLT mode. Signed-off-by: Arik Nemtsov Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 33 ++++++++++++++++++++++++++++++--- drivers/net/wireless/wl12xx/wl12xx.h | 3 +++ 2 files changed, 33 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 4ca46b2d7f34..a6de19a4ee0e 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -658,9 +658,26 @@ out: static int wl1271_fetch_firmware(struct wl1271 *wl) { const struct firmware *fw; + const char *fw_name; int ret; - ret = request_firmware(&fw, WL1271_FW_NAME, wl1271_wl_to_dev(wl)); + switch (wl->bss_type) { + case BSS_TYPE_AP_BSS: + fw_name = WL1271_AP_FW_NAME; + break; + case BSS_TYPE_IBSS: + case BSS_TYPE_STA_BSS: + fw_name = WL1271_FW_NAME; + break; + default: + wl1271_error("no compatible firmware for bss_type %d", + wl->bss_type); + return -EINVAL; + } + + wl1271_debug(DEBUG_BOOT, "booting firmware %s", fw_name); + + ret = request_firmware(&fw, fw_name, wl1271_wl_to_dev(wl)); if (ret < 0) { wl1271_error("could not get firmware: %d", ret); @@ -674,6 +691,7 @@ static int wl1271_fetch_firmware(struct wl1271 *wl) goto out; } + vfree(wl->fw); wl->fw_len = fw->size; wl->fw = vmalloc(wl->fw_len); @@ -684,7 +702,7 @@ static int wl1271_fetch_firmware(struct wl1271 *wl) } memcpy(wl->fw, fw->data, wl->fw_len); - + wl->fw_bss_type = wl->bss_type; ret = 0; out: @@ -820,7 +838,8 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) goto out; } - if (wl->fw == NULL) { + /* Make sure the firmware type matches the BSS type */ + if (wl->fw == NULL || wl->fw_bss_type != wl->bss_type) { ret = wl1271_fetch_firmware(wl); if (ret < 0) goto out; @@ -853,6 +872,8 @@ int wl1271_plt_start(struct wl1271 *wl) goto out; } + wl->bss_type = BSS_TYPE_STA_BSS; + while (retries) { retries--; ret = wl1271_chip_wakeup(wl); @@ -1010,6 +1031,9 @@ static int wl1271_op_start(struct ieee80211_hw *hw) * * The MAC address is first known when the corresponding interface * is added. That is where we will initialize the hardware. + * + * In addition, we currently have different firmwares for AP and managed + * operation. We will know which to boot according to interface type. */ return 0; @@ -3183,6 +3207,9 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->flags = 0; wl->sg_enabled = true; wl->hw_pg_ver = -1; + wl->bss_type = MAX_BSS_TYPE; + wl->set_bss_type = MAX_BSS_TYPE; + wl->fw_bss_type = MAX_BSS_TYPE; memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); for (i = 0; i < ACX_TX_DESCRIPTORS; i++) diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 6b2c763e4908..f153e4b81c0d 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -124,6 +124,8 @@ extern u32 wl12xx_debug_level; #define WL1271_FW_NAME "wl1271-fw.bin" +#define WL1271_AP_FW_NAME "wl1271-fw-ap.bin" + #define WL1271_NVS_NAME "wl1271-nvs.bin" #define WL1271_TX_SECURITY_LO16(s) ((u16)((s) & 0xffff)) @@ -311,6 +313,7 @@ struct wl1271 { u8 *fw; size_t fw_len; + u8 fw_bss_type; struct wl1271_nvs_file *nvs; size_t nvs_len; -- cgit v1.2.3 From 31d26ec6992cc05cfd5e50a59b00b0d64c7bb4aa Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sat, 16 Oct 2010 21:49:52 +0200 Subject: wl12xx: Read MAC address from NVS file on HW startup Try to read the MAC address from the on-disk NVS file. A non-zero MAC address is required to add an AP interface. Signed-off-by: Arik Nemtsov Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index a6de19a4ee0e..78d615525980 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -3064,6 +3064,18 @@ int wl1271_register_hw(struct wl1271 *wl) if (wl->mac80211_registered) return 0; + ret = wl1271_fetch_nvs(wl); + if (ret == 0) { + u8 *nvs_ptr = (u8 *)wl->nvs->nvs; + + wl->mac_addr[0] = nvs_ptr[11]; + wl->mac_addr[1] = nvs_ptr[10]; + wl->mac_addr[2] = nvs_ptr[6]; + wl->mac_addr[3] = nvs_ptr[5]; + wl->mac_addr[4] = nvs_ptr[4]; + wl->mac_addr[5] = nvs_ptr[3]; + } + SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr); ret = ieee80211_register_hw(wl->hw); -- cgit v1.2.3 From 038d925bcfed8df4d16bab57c2b5a4de6ede7847 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sat, 16 Oct 2010 21:53:24 +0200 Subject: wl12xx: Enable AP-mode Indicate support for the NL80211_IFTYPE_AP interface mode to enable AP mode operation. Disable 11a when operating in AP-mode (unsupported for now). Signed-off-by: Arik Nemtsov Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/boot.c | 4 +++- drivers/net/wireless/wl12xx/main.c | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index 2b1019f67d27..d7e036f42958 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -232,7 +232,9 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) */ if (wl->nvs_len == sizeof(struct wl1271_nvs_file) || wl->nvs_len == WL1271_INI_LEGACY_NVS_FILE_SIZE) { - if (wl->nvs->general_params.dual_mode_select) + /* for now 11a is unsupported in AP mode */ + if (wl->bss_type != BSS_TYPE_AP_BSS && + wl->nvs->general_params.dual_mode_select) wl->enable_11a = true; } diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 78d615525980..8a3549162999 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1073,6 +1073,9 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, wl->bss_type = BSS_TYPE_IBSS; wl->set_bss_type = BSS_TYPE_STA_BSS; break; + case NL80211_IFTYPE_AP: + wl->bss_type = BSS_TYPE_AP_BSS; + break; default: ret = -EOPNOTSUPP; goto out; @@ -3136,7 +3139,7 @@ int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC); + BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP); wl->hw->wiphy->max_scan_ssids = 1; /* * Maximum length of elements in scanning probe request templates -- cgit v1.2.3 From fa287b8f291d79f080182eb353d1c1f4f374ae87 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 26 Dec 2010 09:27:50 +0100 Subject: wl12xx: don't join upon disassociation wl12xx "rejoins" upon every BSS_CHANGED_BSSID notification. However, there is no need to rejoin after disassociation, so just filter out the case when the new bssid is 00:00:00:00:00:00. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 8a3549162999..a3529f56a3d6 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2266,19 +2266,21 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) { memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); - ret = wl1271_cmd_build_null_data(wl); - if (ret < 0) - goto out; + if (!is_zero_ether_addr(wl->bssid)) { + ret = wl1271_cmd_build_null_data(wl); + if (ret < 0) + goto out; - ret = wl1271_build_qos_null_data(wl); - if (ret < 0) - goto out; + ret = wl1271_build_qos_null_data(wl); + if (ret < 0) + goto out; - /* filter out all packets not from this BSSID */ - wl1271_configure_filters(wl, 0); + /* filter out all packets not from this BSSID */ + wl1271_configure_filters(wl, 0); - /* Need to update the BSSID (for filtering etc) */ - do_join = true; + /* Need to update the BSSID (for filtering etc) */ + do_join = true; + } } if ((changed & BSS_CHANGED_ASSOC)) { -- cgit v1.2.3 From a8aaaf53d5f22f7f60ca5af26fc85c2940575c37 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 11 Jan 2011 18:25:18 +0100 Subject: wl12xx: don't modify the global supported band structures When 11a is not supported, we were modifying the global structure that contains the bands supported by the driver. This causes problems when having more one wl12xx device in the same system because they all use the same global. This also causes problems when the wl12xx_sdio module is removed and the wl12xx module remains. Fix this problem by copying the band structure into the wl12xx instance. Reported-by: Arik Nemtsov Reported-by: Johannes Berg Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 16 ++++++++++++++-- drivers/net/wireless/wl12xx/wl12xx.h | 3 +++ 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index a3529f56a3d6..588e10ee282c 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -3150,8 +3150,20 @@ int wl1271_init_ieee80211(struct wl1271 *wl) */ wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE - sizeof(struct ieee80211_header); - wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1271_band_2ghz; - wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz; + + /* + * We keep local copies of the band structs because we need to + * modify them on a per-device basis. + */ + memcpy(&wl->bands[IEEE80211_BAND_2GHZ], &wl1271_band_2ghz, + sizeof(wl1271_band_2ghz)); + memcpy(&wl->bands[IEEE80211_BAND_5GHZ], &wl1271_band_5ghz, + sizeof(wl1271_band_5ghz)); + + wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = + &wl->bands[IEEE80211_BAND_2GHZ]; + wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = + &wl->bands[IEEE80211_BAND_5GHZ]; wl->hw->queues = 4; wl->hw->max_rates = 1; diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index f153e4b81c0d..ca727e0c4ce9 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -457,6 +457,9 @@ struct wl1271 { /* recoreded keys for AP-mode - set here before AP startup */ struct wl1271_ap_key *recorded_ap_keys[MAX_NUM_KEYS]; + + /* bands supported by this instance of wl12xx */ + struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; }; struct wl1271_station { -- cgit v1.2.3 From 2d6e4e76d1e73886cb087142e70e2beaabb94b79 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 11 Jan 2011 19:07:21 +0100 Subject: wl12xx: lock the RCU when accessing sta via ieee80211_find_sta() We were calling ieee80211_find_sta() and the sta returned by it without locking the RCU, which is required by mac80211. Fix this and reorganize slightly the area of the code where the sta is used. Reported-by: Jonathan DE CESCO Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 60 +++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 27 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 588e10ee282c..2192e4cf62f4 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2219,7 +2219,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, bool do_join = false, set_assoc = false; bool is_ibss = (wl->bss_type == BSS_TYPE_IBSS); int ret; - struct ieee80211_sta *sta = ieee80211_find_sta(vif, bss_conf->bssid); + struct ieee80211_sta *sta; if (is_ibss) { ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, @@ -2378,36 +2378,42 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, if (ret < 0) goto out; - /* - * Takes care of: New association with HT enable, - * HT information change in beacon. - */ - if (sta && - (changed & BSS_CHANGED_HT) && - (bss_conf->channel_type != NL80211_CHAN_NO_HT)) { - ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true); - if (ret < 0) { - wl1271_warning("Set ht cap true failed %d", ret); - goto out; - } + rcu_read_lock(); + sta = ieee80211_find_sta(vif, bss_conf->bssid); + if (sta) { + /* handle new association with HT and HT information change */ + if ((changed & BSS_CHANGED_HT) && + (bss_conf->channel_type != NL80211_CHAN_NO_HT)) { + ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, + true); + if (ret < 0) { + wl1271_warning("Set ht cap true failed %d", + ret); + rcu_read_unlock(); + goto out; + } ret = wl1271_acx_set_ht_information(wl, - bss_conf->ht_operation_mode); - if (ret < 0) { - wl1271_warning("Set ht information failed %d", ret); - goto out; + bss_conf->ht_operation_mode); + if (ret < 0) { + wl1271_warning("Set ht information failed %d", + ret); + rcu_read_unlock(); + goto out; + } } - } - /* - * Takes care of: New association without HT, - * Disassociation. - */ - else if (sta && (changed & BSS_CHANGED_ASSOC)) { - ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, false); - if (ret < 0) { - wl1271_warning("Set ht cap false failed %d", ret); - goto out; + /* handle new association without HT and disassociation */ + else if (changed & BSS_CHANGED_ASSOC) { + ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, + false); + if (ret < 0) { + wl1271_warning("Set ht cap false failed %d", + ret); + rcu_read_unlock(); + goto out; + } } } + rcu_read_unlock(); if (changed & BSS_CHANGED_ARP_FILTER) { __be32 addr = bss_conf->arp_addr_list[0]; -- cgit v1.2.3 From 491bbd6bdddafb49d0d6a9258d53441aee4bb622 Mon Sep 17 00:00:00 2001 From: Guy Eilam Date: Wed, 12 Jan 2011 10:33:29 +0100 Subject: wl12xx: change debug_level module param sysfs permissions changed the visibility of the debug_level module parameter in the filesystem to be readable and writable to the root user. It is now accessible under /sys/module/wl12xx/parameters removed the debug_level debugfs file that was created under /sys/kernel/debug/... Signed-off-by: Guy Eilam Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/debugfs.c | 5 ----- drivers/net/wireless/wl12xx/main.c | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index 36e7ec1e0c3b..dc3ca0031817 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -402,11 +402,6 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl, DEBUGFS_ADD(gpio_power, rootdir); - entry = debugfs_create_x32("debug_level", 0600, rootdir, - &wl12xx_debug_level); - if (!entry || IS_ERR(entry)) - goto err; - return 0; err: diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 2192e4cf62f4..907655504983 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -3334,9 +3334,9 @@ int wl1271_free_hw(struct wl1271 *wl) } EXPORT_SYMBOL_GPL(wl1271_free_hw); -u32 wl12xx_debug_level; +u32 wl12xx_debug_level = DEBUG_NONE; EXPORT_SYMBOL_GPL(wl12xx_debug_level); -module_param_named(debug_level, wl12xx_debug_level, uint, DEBUG_NONE); +module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(debug_level, "wl12xx debugging level"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 4c9cfa780643dc3f609366b85fec2444a67cad64 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Wed, 12 Jan 2011 14:27:03 +0100 Subject: wl12xx: add hw configuration for max supported AMDPU size The wl12xx chips do the AMDPU aggregation work in the firmware, but it supports a maximum of 8 frames per block. Configure the mac80211 hw structure accordingly. Signed-off-by: Luciano Coelho Tested-by: Juuso Oikarinen --- drivers/net/wireless/wl12xx/main.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 907655504983..77f6ac6466b2 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -3180,6 +3180,8 @@ int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->sta_data_size = sizeof(struct wl1271_station); + wl->hw->max_rx_aggregation_subframes = 8; + return 0; } EXPORT_SYMBOL_GPL(wl1271_init_ieee80211); -- cgit v1.2.3 From 1d4801f2689dc2618fdb5e83d4cb7743747491ed Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 16 Jan 2011 10:07:10 +0100 Subject: wl12xx: fix some endianess bugs pointed out by sparse warnings: CHECK drivers/net/wireless/wl12xx/cmd.c drivers/net/wireless/wl12xx/cmd.c:987:20: warning: incorrect type in assignment (different base types) drivers/net/wireless/wl12xx/cmd.c:987:20: expected restricted __le16 [usertype] aging_period drivers/net/wireless/wl12xx/cmd.c:987:20: got int CHECK drivers/net/wireless/wl12xx/tx.c drivers/net/wireless/wl12xx/tx.c:197:2: warning: cast from restricted __le16 drivers/net/wireless/wl12xx/tx.c:197:2: warning: cast from restricted __le16 drivers/net/wireless/wl12xx/tx.c:197:2: warning: cast from restricted __le16 CHECK drivers/net/wireless/wl12xx/acx.c drivers/net/wireless/wl12xx/acx.c:816:23: warning: incorrect type in assignment (different base types) drivers/net/wireless/wl12xx/acx.c:816:23: expected restricted __le32 [usertype] rate_policy_idx drivers/net/wireless/wl12xx/acx.c:816:23: got unsigned char [unsigned] [usertype] idx Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 2 +- drivers/net/wireless/wl12xx/cmd.c | 2 +- drivers/net/wireless/wl12xx/tx.c | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index 646d278bd944..679bb37f0ca4 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -813,7 +813,7 @@ int wl1271_acx_ap_rate_policy(struct wl1271 *wl, struct conf_tx_rate_class *c, acx->rate_policy.long_retry_limit = c->long_retry_limit; acx->rate_policy.aflags = c->aflags; - acx->rate_policy_idx = idx; + acx->rate_policy_idx = cpu_to_le32(idx); ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx)); if (ret < 0) { diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index e28d9cab1185..1bb8be5e805b 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -984,7 +984,7 @@ int wl1271_cmd_start_bss(struct wl1271 *wl) memcpy(cmd->bssid, bss_conf->bssid, ETH_ALEN); - cmd->aging_period = WL1271_AP_DEF_INACTIV_SEC; + cmd->aging_period = cpu_to_le16(WL1271_AP_DEF_INACTIV_SEC); cmd->bss_index = WL1271_AP_BSS_INDEX; cmd->global_hlid = WL1271_AP_GLOBAL_HLID; cmd->broadcast_hlid = WL1271_AP_BROADCAST_HLID; diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 2347f2552f14..3507c81c7500 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -195,9 +195,9 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, desc->tx_attr = cpu_to_le16(tx_attr); wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d hlid: %d " - "tx_attr: 0x%x len: %d life: %d mem: %d", pad, (int)desc->hlid, - (int)desc->tx_attr, (int)desc->length, (int)desc->life_time, - (int)desc->total_mem_blocks); + "tx_attr: 0x%x len: %d life: %d mem: %d", pad, desc->hlid, + le16_to_cpu(desc->tx_attr), le16_to_cpu(desc->length), + le16_to_cpu(desc->life_time), desc->total_mem_blocks); } /* caller must hold wl->mutex */ -- cgit v1.2.3 From 4ae3fa87854862d1724bf8f2f1e1b9b088fa53d7 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Fri, 14 Jan 2011 12:48:46 +0100 Subject: wl12xx: Cleanup PLT mode when module is removed PLT mode start/stop is controlled from userspace. When removing module, the PLT mode state is however not checked, and not cleared. There is the possibility of some unwanted state to left linger and there is even the possiblity of a kernel crash if for instance IRQ work is running when the module is removed. Fix this by stopping PLT mode on module removal, if still running. Signed-off-by: Juuso Oikarinen Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 77f6ac6466b2..9a1d2ff35c2d 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -917,12 +917,10 @@ out: return ret; } -int wl1271_plt_stop(struct wl1271 *wl) +int __wl1271_plt_stop(struct wl1271 *wl) { int ret = 0; - mutex_lock(&wl->mutex); - wl1271_notice("power down"); if (wl->state != WL1271_STATE_PLT) { @@ -938,12 +936,21 @@ int wl1271_plt_stop(struct wl1271 *wl) wl->state = WL1271_STATE_OFF; wl->rx_counter = 0; -out: mutex_unlock(&wl->mutex); - cancel_work_sync(&wl->irq_work); cancel_work_sync(&wl->recovery_work); + mutex_lock(&wl->mutex); +out: + return ret; +} + +int wl1271_plt_stop(struct wl1271 *wl) +{ + int ret; + mutex_lock(&wl->mutex); + ret = __wl1271_plt_stop(wl); + mutex_unlock(&wl->mutex); return ret; } @@ -3109,6 +3116,9 @@ EXPORT_SYMBOL_GPL(wl1271_register_hw); void wl1271_unregister_hw(struct wl1271 *wl) { + if (wl->state == WL1271_STATE_PLT) + __wl1271_plt_stop(wl); + unregister_netdevice_notifier(&wl1271_dev_notifier); ieee80211_unregister_hw(wl->hw); wl->mac80211_registered = false; -- cgit v1.2.3 From d75387ca62b92d837c2a1e626d0d3705a9011228 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Mon, 17 Jan 2011 10:16:00 +0100 Subject: wl12xx: add missing MODULE_FIRMWARE statment for AP-mode FW In wl12xx cards AP-mode requires a separate FW file. Add this file to the module info. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/sdio.c | 1 + drivers/net/wireless/wl12xx/spi.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index 93cbb8d5aba9..d5e874825069 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -345,3 +345,4 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luciano Coelho "); MODULE_AUTHOR("Juuso Oikarinen "); MODULE_FIRMWARE(WL1271_FW_NAME); +MODULE_FIRMWARE(WL1271_AP_FW_NAME); diff --git a/drivers/net/wireless/wl12xx/spi.c b/drivers/net/wireless/wl12xx/spi.c index 8f7ea2c7d567..0132dad756c4 100644 --- a/drivers/net/wireless/wl12xx/spi.c +++ b/drivers/net/wireless/wl12xx/spi.c @@ -495,4 +495,5 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luciano Coelho "); MODULE_AUTHOR("Juuso Oikarinen "); MODULE_FIRMWARE(WL1271_FW_NAME); +MODULE_FIRMWARE(WL1271_AP_FW_NAME); MODULE_ALIAS("spi:wl1271"); -- cgit v1.2.3 From 6c89b7b2f856a2b67f53313ddfa3af4ab52bae28 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 18 Jan 2011 20:39:52 +0100 Subject: wl12xx: Add channel 14 to list of supported 2ghz channels Channel 14 is only supported in Japan (JP country code in regdb). The FW limits tranmissions to CCK only on this channel. Tested in both STA and AP modes to work correctly. Signed-off-by: Arik Nemtsov Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 9a1d2ff35c2d..48194629c00b 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2778,6 +2778,7 @@ static struct ieee80211_channel wl1271_channels[] = { { .hw_value = 11, .center_freq = 2462, .max_power = 25 }, { .hw_value = 12, .center_freq = 2467, .max_power = 25 }, { .hw_value = 13, .center_freq = 2472, .max_power = 25 }, + { .hw_value = 14, .center_freq = 2484, .max_power = 25 }, }; /* mapping to indexes for wl1271_rates */ -- cgit v1.2.3 From 4b7fac77b4c1badac84df3dcbdf07199d94cb1c3 Mon Sep 17 00:00:00 2001 From: "Levi, Shahar" Date: Sun, 23 Jan 2011 07:27:22 +0100 Subject: wl12xx: BA initiator support Add 80211n BA initiator session support wl1271 driver. Include BA supported FW version auto detection mechanism. BA initiator session management included in FW independently. Signed-off-by: Shahar Levi Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 51 ++++++++++++++++++++++++++++++++++++ drivers/net/wireless/wl12xx/acx.h | 41 +++++++++++++++++++++++++++-- drivers/net/wireless/wl12xx/boot.c | 24 ++++++++++++++--- drivers/net/wireless/wl12xx/conf.h | 6 +++++ drivers/net/wireless/wl12xx/init.c | 42 +++++++++++++++++++++++++++++ drivers/net/wireless/wl12xx/main.c | 12 ++++++--- drivers/net/wireless/wl12xx/wl12xx.h | 17 +++++++++++- 7 files changed, 183 insertions(+), 10 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index 679bb37f0ca4..0b9cacc74903 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -1342,6 +1342,57 @@ out: return ret; } +/* Configure BA session initiator/receiver parameters setting in the FW. */ +int wl1271_acx_set_ba_session(struct wl1271 *wl, + enum ieee80211_back_parties direction, + u8 tid_index, u8 policy) +{ + struct wl1271_acx_ba_session_policy *acx; + int ret; + + wl1271_debug(DEBUG_ACX, "acx ba session setting"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + /* ANY role */ + acx->role_id = 0xff; + acx->tid = tid_index; + acx->enable = policy; + acx->ba_direction = direction; + + switch (direction) { + case WLAN_BACK_INITIATOR: + acx->win_size = wl->conf.ht.tx_ba_win_size; + acx->inactivity_timeout = wl->conf.ht.inactivity_timeout; + break; + case WLAN_BACK_RECIPIENT: + acx->win_size = RX_BA_WIN_SIZE; + acx->inactivity_timeout = 0; + break; + default: + wl1271_error("Incorrect acx command id=%x\n", direction); + ret = -EINVAL; + goto out; + } + + ret = wl1271_cmd_configure(wl, + ACX_BA_SESSION_POLICY_CFG, + acx, + sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx ba session setting failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime) { struct wl1271_acx_fw_tsf_information *tsf_info; diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index 62a269d84ebe..f643e60a566b 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -1061,6 +1061,40 @@ struct wl1271_acx_ht_information { u8 padding[3]; } __packed; +#define RX_BA_WIN_SIZE 8 + +struct wl1271_acx_ba_session_policy { + struct acx_header header; + /* + * Specifies role Id, Range 0-7, 0xFF means ANY role. + * Future use. For now this field is irrelevant + */ + u8 role_id; + /* + * Specifies Link Id, Range 0-31, 0xFF means ANY Link Id. + * Not applicable if Role Id is set to ANY. + */ + u8 link_id; + + u8 tid; + + u8 enable; + + /* Windows size in number of packets */ + u16 win_size; + + /* + * As initiator inactivity timeout in time units(TU) of 1024us. + * As receiver reserved + */ + u16 inactivity_timeout; + + /* Initiator = 1/Receiver = 0 */ + u8 ba_direction; + + u8 padding[3]; +} __packed; + struct wl1271_acx_fw_tsf_information { struct acx_header header; @@ -1134,8 +1168,8 @@ enum { ACX_RSSI_SNR_WEIGHTS = 0x0052, ACX_KEEP_ALIVE_MODE = 0x0053, ACX_SET_KEEP_ALIVE_CONFIG = 0x0054, - ACX_BA_SESSION_RESPONDER_POLICY = 0x0055, - ACX_BA_SESSION_INITIATOR_POLICY = 0x0056, + ACX_BA_SESSION_POLICY_CFG = 0x0055, + ACX_BA_SESSION_RX_SETUP = 0x0056, ACX_PEER_HT_CAP = 0x0057, ACX_HT_BSS_OPERATION = 0x0058, ACX_COEX_ACTIVITY = 0x0059, @@ -1209,6 +1243,9 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl, bool allow_ht_operation); int wl1271_acx_set_ht_information(struct wl1271 *wl, u16 ht_operation_mode); +int wl1271_acx_set_ba_session(struct wl1271 *wl, + enum ieee80211_back_parties direction, + u8 tid_index, u8 policy); int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime); int wl1271_acx_max_tx_retry(struct wl1271 *wl); diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index d7e036f42958..1ffbad67d2d8 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -101,6 +101,22 @@ static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag) wl1271_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl); } +static void wl1271_parse_fw_ver(struct wl1271 *wl) +{ + int ret; + + ret = sscanf(wl->chip.fw_ver_str + 4, "%u.%u.%u.%u.%u", + &wl->chip.fw_ver[0], &wl->chip.fw_ver[1], + &wl->chip.fw_ver[2], &wl->chip.fw_ver[3], + &wl->chip.fw_ver[4]); + + if (ret != 5) { + wl1271_warning("fw version incorrect value"); + memset(wl->chip.fw_ver, 0, sizeof(wl->chip.fw_ver)); + return; + } +} + static void wl1271_boot_fw_version(struct wl1271 *wl) { struct wl1271_static_data static_data; @@ -108,11 +124,13 @@ static void wl1271_boot_fw_version(struct wl1271 *wl) wl1271_read(wl, wl->cmd_box_addr, &static_data, sizeof(static_data), false); - strncpy(wl->chip.fw_ver, static_data.fw_version, - sizeof(wl->chip.fw_ver)); + strncpy(wl->chip.fw_ver_str, static_data.fw_version, + sizeof(wl->chip.fw_ver_str)); /* make sure the string is NULL-terminated */ - wl->chip.fw_ver[sizeof(wl->chip.fw_ver) - 1] = '\0'; + wl->chip.fw_ver_str[sizeof(wl->chip.fw_ver_str) - 1] = '\0'; + + wl1271_parse_fw_ver(wl); } static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index f5c048c9bea4..135d837cefb1 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -1138,6 +1138,11 @@ struct conf_rf_settings { u8 tx_per_channel_power_compensation_5[CONF_TX_PWR_COMPENSATION_LEN_5]; }; +struct conf_ht_setting { + u16 tx_ba_win_size; + u16 inactivity_timeout; +}; + struct conf_drv_settings { struct conf_sg_settings sg; struct conf_rx_settings rx; @@ -1148,6 +1153,7 @@ struct conf_drv_settings { struct conf_roam_trigger_settings roam_trigger; struct conf_scan_settings scan; struct conf_rf_settings rf; + struct conf_ht_setting ht; }; #endif diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index bdb61232f80c..2348eadc0de1 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -455,6 +455,43 @@ static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl) return 0; } +static void wl1271_check_ba_support(struct wl1271 *wl) +{ + /* validate FW cose ver x.x.x.50-60.x */ + if ((wl->chip.fw_ver[3] >= WL12XX_BA_SUPPORT_FW_COST_VER2_START) && + (wl->chip.fw_ver[3] < WL12XX_BA_SUPPORT_FW_COST_VER2_END)) { + wl->ba_support = true; + return; + } + + wl->ba_support = false; +} + +static int wl1271_set_ba_policies(struct wl1271 *wl) +{ + u8 tid_index; + u8 ret = 0; + + /* Reset the BA RX indicators */ + wl->ba_allowed = true; + wl->ba_rx_bitmap = 0; + + /* validate that FW support BA */ + wl1271_check_ba_support(wl); + + if (wl->ba_support) + /* 802.11n initiator BA session setting */ + for (tid_index = 0; tid_index < CONF_TX_MAX_TID_COUNT; + ++tid_index) { + ret = wl1271_acx_set_ba_session(wl, WLAN_BACK_INITIATOR, + tid_index, true); + if (ret < 0) + break; + } + + return ret; +} + int wl1271_hw_init(struct wl1271 *wl) { struct conf_tx_ac_category *conf_ac; @@ -568,6 +605,11 @@ int wl1271_hw_init(struct wl1271 *wl) if (ret < 0) goto out_free_memmap; + /* Configure initiator BA sessions policies */ + ret = wl1271_set_ba_policies(wl); + if (ret < 0) + goto out_free_memmap; + return 0; out_free_memmap: diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 48194629c00b..01ca666b6c2d 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -274,7 +274,7 @@ static struct conf_drv_settings default_conf = { .avg_weight_rssi_beacon = 20, .avg_weight_rssi_data = 10, .avg_weight_snr_beacon = 20, - .avg_weight_snr_data = 10 + .avg_weight_snr_data = 10, }, .scan = { .min_dwell_time_active = 7500, @@ -293,6 +293,10 @@ static struct conf_drv_settings default_conf = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, }, + .ht = { + .tx_ba_win_size = 64, + .inactivity_timeout = 10000, + }, }; static void __wl1271_op_remove_interface(struct wl1271 *wl); @@ -890,7 +894,7 @@ int wl1271_plt_start(struct wl1271 *wl) wl->state = WL1271_STATE_PLT; wl1271_notice("firmware booted in PLT mode (%s)", - wl->chip.fw_ver); + wl->chip.fw_ver_str); goto out; irq_disable: @@ -1138,11 +1142,11 @@ power_off: wl->vif = vif; wl->state = WL1271_STATE_ON; - wl1271_info("firmware booted (%s)", wl->chip.fw_ver); + wl1271_info("firmware booted (%s)", wl->chip.fw_ver_str); /* update hw/fw version info in wiphy struct */ wiphy->hw_version = wl->chip.id; - strncpy(wiphy->fw_version, wl->chip.fw_ver, + strncpy(wiphy->fw_version, wl->chip.fw_ver_str, sizeof(wiphy->fw_version)); /* diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index ca727e0c4ce9..e0bac79cd516 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -38,6 +38,13 @@ #define DRIVER_NAME "wl1271" #define DRIVER_PREFIX DRIVER_NAME ": " +/* + * FW versions support BA 11n + * versions marks x.x.x.50-60.x + */ +#define WL12XX_BA_SUPPORT_FW_COST_VER2_START 50 +#define WL12XX_BA_SUPPORT_FW_COST_VER2_END 60 + enum { DEBUG_NONE = 0, DEBUG_IRQ = BIT(0), @@ -182,10 +189,13 @@ struct wl1271_partition_set { struct wl1271; +#define WL12XX_NUM_FW_VER 5 + /* FIXME: I'm not sure about this structure name */ struct wl1271_chip { u32 id; - char fw_ver[21]; + char fw_ver_str[ETHTOOL_BUSINFO_LEN]; + unsigned int fw_ver[WL12XX_NUM_FW_VER]; }; struct wl1271_stats { @@ -460,6 +470,11 @@ struct wl1271 { /* bands supported by this instance of wl12xx */ struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; + + /* RX BA constraint value */ + bool ba_support; + u8 ba_allowed; + u8 ba_rx_bitmap; }; struct wl1271_station { -- cgit v1.2.3 From bbba3e6832ad3e974fb593a98abe03f8b60fc7f3 Mon Sep 17 00:00:00 2001 From: "Levi, Shahar" Date: Sun, 23 Jan 2011 07:27:23 +0100 Subject: wl12xx: BA receiver support Add new ampdu_action ops to support receiver BA. The BA initiator session management in FW independently. Signed-off-by: Shahar Levi Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 34 ++++++++++++++++++++ drivers/net/wireless/wl12xx/acx.h | 25 +++++++++++++-- drivers/net/wireless/wl12xx/init.c | 1 - drivers/net/wireless/wl12xx/main.c | 60 ++++++++++++++++++++++++++++++++++++ drivers/net/wireless/wl12xx/wl12xx.h | 1 - 5 files changed, 117 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index 0b9cacc74903..afdc601aa7ea 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -1393,6 +1393,40 @@ out: return ret; } +/* setup BA session receiver setting in the FW. */ +int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn, + bool enable) +{ + struct wl1271_acx_ba_receiver_setup *acx; + int ret; + + wl1271_debug(DEBUG_ACX, "acx ba receiver session setting"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + /* Single link for now */ + acx->link_id = 1; + acx->tid = tid_index; + acx->enable = enable; + acx->win_size = 0; + acx->ssn = ssn; + + ret = wl1271_cmd_configure(wl, ACX_BA_SESSION_RX_SETUP, acx, + sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx ba receiver session failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime) { struct wl1271_acx_fw_tsf_information *tsf_info; diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index f643e60a566b..4bbaf04f434e 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -1095,6 +1095,25 @@ struct wl1271_acx_ba_session_policy { u8 padding[3]; } __packed; +struct wl1271_acx_ba_receiver_setup { + struct acx_header header; + + /* Specifies Link Id, Range 0-31, 0xFF means ANY Link Id */ + u8 link_id; + + u8 tid; + + u8 enable; + + u8 padding[1]; + + /* Windows size in number of packets */ + u16 win_size; + + /* BA session starting sequence number. RANGE 0-FFF */ + u16 ssn; +} __packed; + struct wl1271_acx_fw_tsf_information { struct acx_header header; @@ -1244,8 +1263,10 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl, int wl1271_acx_set_ht_information(struct wl1271 *wl, u16 ht_operation_mode); int wl1271_acx_set_ba_session(struct wl1271 *wl, - enum ieee80211_back_parties direction, - u8 tid_index, u8 policy); + enum ieee80211_back_parties direction, + u8 tid_index, u8 policy); +int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn, + bool enable); int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime); int wl1271_acx_max_tx_retry(struct wl1271 *wl); diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index 2348eadc0de1..70b3dc88a219 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -473,7 +473,6 @@ static int wl1271_set_ba_policies(struct wl1271 *wl) u8 ret = 0; /* Reset the BA RX indicators */ - wl->ba_allowed = true; wl->ba_rx_bitmap = 0; /* validate that FW support BA */ diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 01ca666b6c2d..eda2f3d8edf1 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2724,6 +2724,65 @@ out: return ret; } +int wl1271_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn) +{ + struct wl1271 *wl = hw->priv; + int ret; + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state == WL1271_STATE_OFF)) { + ret = -EAGAIN; + goto out; + } + + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + + switch (action) { + case IEEE80211_AMPDU_RX_START: + if (wl->ba_support) { + ret = wl1271_acx_set_ba_receiver_session(wl, tid, *ssn, + true); + if (!ret) + wl->ba_rx_bitmap |= BIT(tid); + } else { + ret = -ENOTSUPP; + } + break; + + case IEEE80211_AMPDU_RX_STOP: + ret = wl1271_acx_set_ba_receiver_session(wl, tid, 0, false); + if (!ret) + wl->ba_rx_bitmap &= ~BIT(tid); + break; + + /* + * The BA initiator session management in FW independently. + * Falling break here on purpose for all TX APDU commands. + */ + case IEEE80211_AMPDU_TX_START: + case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_OPERATIONAL: + ret = -EINVAL; + break; + + default: + wl1271_error("Incorrect ampdu action id=%x\n", action); + ret = -EINVAL; + } + + wl1271_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); + + return ret; +} + /* can't be const, mac80211 writes to this */ static struct ieee80211_rate wl1271_rates[] = { { .bitrate = 10, @@ -2973,6 +3032,7 @@ static const struct ieee80211_ops wl1271_ops = { .get_survey = wl1271_op_get_survey, .sta_add = wl1271_op_sta_add, .sta_remove = wl1271_op_sta_remove, + .ampdu_action = wl1271_op_ampdu_action, CFG80211_TESTMODE_CMD(wl1271_tm_cmd) }; diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index e0bac79cd516..d1de13fe7d9a 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -473,7 +473,6 @@ struct wl1271 { /* RX BA constraint value */ bool ba_support; - u8 ba_allowed; u8 ba_rx_bitmap; }; -- cgit v1.2.3 From 8e2de74e781e696636e8b4cd08084d2b310d44d9 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 23 Jan 2011 11:25:27 +0100 Subject: wl12xx: wrong values are returned in gpio_power_write() Return values were assigned to incorrect var / weren't assigned. fix it, and defer mutex_lock after the sanity checks. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/debugfs.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index dc3ca0031817..bebfa28a171a 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -261,27 +261,25 @@ static ssize_t gpio_power_write(struct file *file, unsigned long value; int ret; - mutex_lock(&wl->mutex); - len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) { - ret = -EFAULT; - goto out; + return -EFAULT; } buf[len] = '\0'; ret = strict_strtoul(buf, 0, &value); if (ret < 0) { wl1271_warning("illegal value in gpio_power"); - goto out; + return -EINVAL; } + mutex_lock(&wl->mutex); + if (value) wl1271_power_on(wl); else wl1271_power_off(wl); -out: mutex_unlock(&wl->mutex); return count; } -- cgit v1.2.3 From ea45b2cbf56a4e93e9c5a68b85794e6d79f69fce Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Mon, 24 Jan 2011 07:01:54 +0100 Subject: wl12xx: Increase scan channel dwell time for passive scans The passive scan channel dwell time currently used is 30-60TU. A typical beacon interval for AP's is 100TU. This leads to a ~30% worst-case probability of finding an AP via passive scanning. For 5GHz bands for DFS frequencies passive scanning is the only scanning option. Hence for these, the probability of finding an AP is very low. To fix this, increase the passive channel scan dwell times (also the early leave value, as 5GHz channels are still typically very silent.) Use a value of 100TU, because that covers most typical AP configurations. Based on testing the probability of finding an AP (100TU beacon interval) on a single scan round are as follows (based on 100 iterations): dwell min/max (TU) | probability ---------------------+------------ 30/60 | 35% 60/60 | 56% 80/80 | 77% 100/100 | 100% Total scan times now and after the change: Region | Before (s) | After (s) -------+------------+---------- 00 | 0.77 | 1.48 FI | 0.95 | 2.01 US | 0.91 | 1.76 Signed-off-by: Juuso Oikarinen Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/conf.h | 18 +++++++++--------- drivers/net/wireless/wl12xx/main.c | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 135d837cefb1..fd1dac9ab4db 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -1084,30 +1084,30 @@ struct conf_scan_settings { /* * The minimum time to wait on each channel for active scans * - * Range: 0 - 65536 tu + * Range: u32 tu/1000 */ - u16 min_dwell_time_active; + u32 min_dwell_time_active; /* * The maximum time to wait on each channel for active scans * - * Range: 0 - 65536 tu + * Range: u32 tu/1000 */ - u16 max_dwell_time_active; + u32 max_dwell_time_active; /* - * The maximum time to wait on each channel for passive scans + * The minimum time to wait on each channel for passive scans * - * Range: 0 - 65536 tu + * Range: u32 tu/1000 */ - u16 min_dwell_time_passive; + u32 min_dwell_time_passive; /* * The maximum time to wait on each channel for passive scans * - * Range: 0 - 65536 tu + * Range: u32 tu/1000 */ - u16 max_dwell_time_passive; + u32 max_dwell_time_passive; /* * Number of probe requests to transmit on each active scan channel diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index eda2f3d8edf1..e535e79e269d 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -279,8 +279,8 @@ static struct conf_drv_settings default_conf = { .scan = { .min_dwell_time_active = 7500, .max_dwell_time_active = 30000, - .min_dwell_time_passive = 30000, - .max_dwell_time_passive = 60000, + .min_dwell_time_passive = 100000, + .max_dwell_time_passive = 100000, .num_probe_reqs = 2, }, .rf = { -- cgit v1.2.3 From e5e2f24b3eec67a7a35d43654a997f98ca21aff2 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 24 Jan 2011 19:19:03 +0100 Subject: wl12xx: disable auto-arp The auto-arp feature sometimes has unexpected side effects (e.g. firmware crashes, no ARP replies, etc.) disable it until it will be solved. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index e535e79e269d..dfab21e2e5dc 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2445,8 +2445,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, } ret = wl1271_acx_arp_ip_filter(wl, - (ACX_ARP_FILTER_ARP_FILTERING | - ACX_ARP_FILTER_AUTO_ARP), + ACX_ARP_FILTER_ARP_FILTERING, addr); } else ret = wl1271_acx_arp_ip_filter(wl, 0, addr); -- cgit v1.2.3 From 7c3ee9e3fd065e878a2dbaec9d417441c59799be Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Mon, 31 Jan 2011 09:41:52 +0200 Subject: wl12xx: fix warning due to missing arg in ampdu_action Commit 0b01f030d38e00650e2db42da083d8647aad40a5 added a new argument to the ampdu_action operation. The ampdu_action operation in the wl12xx driver currently doesn't have that argument and this generates a warning. This happened during merging of the latest mac80211 patches with the wl12xx BA patches. CC [M] drivers/net/wireless/wl12xx/main.o drivers/net/wireless/wl12xx/main.c:3035: warning: initialization from incompatible pointer type The wl12xx driver doesn't need to do anything about the buf_size argument since the AMPDU TX is fully handled by the firmware. Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index dfab21e2e5dc..254b7daccee1 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2724,8 +2724,9 @@ out: } int wl1271_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn) + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size) { struct wl1271 *wl = hw->priv; int ret; -- cgit v1.2.3 From ee60833a4f887a09e87be52cdf1247a4963b0aef Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 2 Feb 2011 09:59:34 +0200 Subject: wl12xx: mcp2.5 - add config_ps acx mcp2.5 uses this acx to configure the fw only once, rather than passing the params in every enter psm command. Signed-off-by: Eliad Peller Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 30 ++++++++++++++++++++++++++++++ drivers/net/wireless/wl12xx/acx.h | 11 +++++++++++ drivers/net/wireless/wl12xx/conf.h | 8 ++++++++ drivers/net/wireless/wl12xx/main.c | 1 + 4 files changed, 50 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index afdc601aa7ea..84d94b259900 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -1476,3 +1476,33 @@ out: kfree(acx); return ret; } + +int wl1271_acx_config_ps(struct wl1271 *wl) +{ + struct wl1271_acx_config_ps *config_ps; + int ret; + + wl1271_debug(DEBUG_ACX, "acx config ps"); + + config_ps = kzalloc(sizeof(*config_ps), GFP_KERNEL); + if (!config_ps) { + ret = -ENOMEM; + goto out; + } + + config_ps->exit_retries = wl->conf.conn.psm_exit_retries; + config_ps->enter_retries = wl->conf.conn.psm_entry_retries; + config_ps->null_data_rate = cpu_to_le32(wl->basic_rate); + + ret = wl1271_cmd_configure(wl, ACX_CONFIG_PS, config_ps, + sizeof(*config_ps)); + + if (ret < 0) { + wl1271_warning("acx config ps failed: %d", ret); + goto out; + } + +out: + kfree(config_ps); + return ret; +} diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index 4bbaf04f434e..5bc0ca97bec4 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -1136,6 +1136,15 @@ struct wl1271_acx_max_tx_retry { u8 padding_1[2]; } __packed; +struct wl1271_acx_config_ps { + struct acx_header header; + + u8 exit_retries; + u8 enter_retries; + u8 padding[2]; + __le32 null_data_rate; +} __packed; + enum { ACX_WAKE_UP_CONDITIONS = 0x0002, ACX_MEM_CFG = 0x0003, @@ -1200,6 +1209,7 @@ enum { DOT11_RTS_THRESHOLD = 0x1013, DOT11_GROUP_ADDRESS_TBL = 0x1014, ACX_PM_CONFIG = 0x1016, + ACX_CONFIG_PS = 0x1017, MAX_DOT11_IE = DOT11_GROUP_ADDRESS_TBL, @@ -1269,5 +1279,6 @@ int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn, bool enable); int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime); int wl1271_acx_max_tx_retry(struct wl1271 *wl); +int wl1271_acx_config_ps(struct wl1271 *wl); #endif /* __WL1271_ACX_H__ */ diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index fd1dac9ab4db..c81aecd755e5 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -959,6 +959,14 @@ struct conf_conn_settings { */ u8 psm_entry_retries; + /* + * Specifies the maximum number of times to try PSM exit if it fails + * (if sending the appropriate null-func message fails.) + * + * Range 0 - 255 + */ + u8 psm_exit_retries; + /* * Specifies the maximum number of times to try transmit the PSM entry * null-func frame for each PSM entry attempt diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 254b7daccee1..522bb09c9535 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -256,6 +256,7 @@ static struct conf_drv_settings default_conf = { .bet_enable = CONF_BET_MODE_ENABLE, .bet_max_consecutive = 10, .psm_entry_retries = 5, + .psm_exit_retries = 255, .psm_entry_nullfunc_retries = 3, .psm_entry_hangover_period = 1, .keep_alive_interval = 55000, -- cgit v1.2.3 From c8bde243421d759844264cf11e4248e7862c2722 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 2 Feb 2011 09:59:35 +0200 Subject: wl12xx: move to new firmware (6.1.3.50.49) This patch adds support for the new wl12xx firmware (Rev 6.1.3.50.49) Since this fw is not backward compatible with previous fw versions, a new fw (with different name) is being fetched. (the patch is big because it contains all the required fw api changes. splitting it into multiple patches will result in corrupted intermediate commits) Signed-off-by: Eliad Peller Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 44 +++++++++++++++++++++++++++++++----- drivers/net/wireless/wl12xx/acx.h | 26 ++++++++++++++++----- drivers/net/wireless/wl12xx/cmd.c | 13 +++++++---- drivers/net/wireless/wl12xx/cmd.h | 14 ++++-------- drivers/net/wireless/wl12xx/conf.h | 29 ++++++++++++++++++++++++ drivers/net/wireless/wl12xx/event.c | 14 ------------ drivers/net/wireless/wl12xx/event.h | 2 -- drivers/net/wireless/wl12xx/init.c | 13 +++++++++++ drivers/net/wireless/wl12xx/main.c | 22 ++++++++++++++---- drivers/net/wireless/wl12xx/ps.c | 6 ++--- drivers/net/wireless/wl12xx/rx.c | 6 ++--- drivers/net/wireless/wl12xx/rx.h | 2 +- drivers/net/wireless/wl12xx/wl12xx.h | 31 +++++++++++++++++++++---- 13 files changed, 162 insertions(+), 60 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index 84d94b259900..f2fbda06a129 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -947,9 +947,9 @@ out: return ret; } -int wl1271_acx_mem_cfg(struct wl1271 *wl) +int wl1271_acx_ap_mem_cfg(struct wl1271 *wl) { - struct wl1271_acx_config_memory *mem_conf; + struct wl1271_acx_ap_config_memory *mem_conf; int ret; wl1271_debug(DEBUG_ACX, "wl1271 mem cfg"); @@ -979,13 +979,45 @@ out: return ret; } -int wl1271_acx_init_mem_config(struct wl1271 *wl) +int wl1271_acx_sta_mem_cfg(struct wl1271 *wl) { + struct wl1271_acx_sta_config_memory *mem_conf; int ret; - ret = wl1271_acx_mem_cfg(wl); - if (ret < 0) - return ret; + wl1271_debug(DEBUG_ACX, "wl1271 mem cfg"); + + mem_conf = kzalloc(sizeof(*mem_conf), GFP_KERNEL); + if (!mem_conf) { + ret = -ENOMEM; + goto out; + } + + /* memory config */ + mem_conf->num_stations = DEFAULT_NUM_STATIONS; + mem_conf->rx_mem_block_num = ACX_RX_MEM_BLOCKS; + mem_conf->tx_min_mem_block_num = ACX_TX_MIN_MEM_BLOCKS; + mem_conf->num_ssid_profiles = ACX_NUM_SSID_PROFILES; + mem_conf->total_tx_descriptors = cpu_to_le32(ACX_TX_DESCRIPTORS); + mem_conf->dyn_mem_enable = wl->conf.mem.dynamic_memory; + mem_conf->tx_free_req = wl->conf.mem.min_req_tx_blocks; + mem_conf->rx_free_req = wl->conf.mem.min_req_rx_blocks; + mem_conf->tx_min = wl->conf.mem.tx_min; + + ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf, + sizeof(*mem_conf)); + if (ret < 0) { + wl1271_warning("wl1271 mem config failed: %d", ret); + goto out; + } + +out: + kfree(mem_conf); + return ret; +} + +int wl1271_acx_init_mem_config(struct wl1271 *wl) +{ + int ret; wl->target_mem_map = kzalloc(sizeof(struct wl1271_acx_mem_map), GFP_KERNEL); diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index 5bc0ca97bec4..537fab40ed12 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -802,7 +802,7 @@ struct acx_tx_config_options { #define ACX_TX_DESCRIPTORS 32 #define ACX_NUM_SSID_PROFILES 1 -struct wl1271_acx_config_memory { +struct wl1271_acx_ap_config_memory { struct acx_header header; u8 rx_mem_block_num; @@ -812,6 +812,20 @@ struct wl1271_acx_config_memory { __le32 total_tx_descriptors; } __packed; +struct wl1271_acx_sta_config_memory { + struct acx_header header; + + u8 rx_mem_block_num; + u8 tx_min_mem_block_num; + u8 num_stations; + u8 num_ssid_profiles; + __le32 total_tx_descriptors; + u8 dyn_mem_enable; + u8 tx_free_req; + u8 rx_free_req; + u8 tx_min; +} __packed; + struct wl1271_acx_mem_map { struct acx_header header; @@ -1202,6 +1216,8 @@ enum { ACX_HT_BSS_OPERATION = 0x0058, ACX_COEX_ACTIVITY = 0x0059, ACX_SET_DCO_ITRIM_PARAMS = 0x0061, + ACX_GEN_FW_CMD = 0x0070, + ACX_HOST_IF_CFG_BITMAP = 0x0071, ACX_MAX_TX_FAILURE = 0x0072, DOT11_RX_MSDU_LIFE_TIME = 0x1004, DOT11_CUR_TX_PWR = 0x100D, @@ -1210,10 +1226,7 @@ enum { DOT11_GROUP_ADDRESS_TBL = 0x1014, ACX_PM_CONFIG = 0x1016, ACX_CONFIG_PS = 0x1017, - - MAX_DOT11_IE = DOT11_GROUP_ADDRESS_TBL, - - MAX_IE = 0xFFFF + ACX_CONFIG_HANGOVER = 0x1018, }; @@ -1255,7 +1268,8 @@ int wl1271_acx_tid_cfg(struct wl1271 *wl, u8 queue_id, u8 channel_type, u32 apsd_conf0, u32 apsd_conf1); int wl1271_acx_frag_threshold(struct wl1271 *wl, u16 frag_threshold); int wl1271_acx_tx_config_options(struct wl1271 *wl); -int wl1271_acx_mem_cfg(struct wl1271 *wl); +int wl1271_acx_ap_mem_cfg(struct wl1271 *wl); +int wl1271_acx_sta_mem_cfg(struct wl1271 *wl); int wl1271_acx_init_mem_config(struct wl1271 *wl); int wl1271_acx_init_rx_interrupt(struct wl1271 *wl); int wl1271_acx_smart_reflex(struct wl1271 *wl); diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 1bb8be5e805b..66d15e77da38 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -286,6 +286,13 @@ int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type) join->rx_filter_options = cpu_to_le32(wl->rx_filter); join->bss_type = bss_type; join->basic_rate_set = cpu_to_le32(wl->basic_rate_set); + /* + * for supported_rate_set, we should use wl->rate_set. however, + * it seems that acx_rate_policies doesn't affect full_rate, and + * since we want to avoid additional join, we'll use a 0xffffffff value, + * and let the fw find the actual supported rates + */ + join->supported_rate_set = cpu_to_le32(0xffffffff); if (wl->band == IEEE80211_BAND_5GHZ) join->bss_type |= WL1271_JOIN_CMD_BSS_TYPE_5GHZ; @@ -454,7 +461,7 @@ out: return ret; } -int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode, u32 rates, bool send) +int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode) { struct wl1271_cmd_ps_params *ps_params = NULL; int ret = 0; @@ -468,10 +475,6 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode, u32 rates, bool send) } ps_params->ps_mode = ps_mode; - ps_params->send_null_data = send; - ps_params->retries = wl->conf.conn.psm_entry_nullfunc_retries; - ps_params->hang_over_period = wl->conf.conn.psm_entry_hangover_period; - ps_params->null_data_rate = cpu_to_le32(rates); ret = wl1271_cmd_send(wl, CMD_SET_PS_MODE, ps_params, sizeof(*ps_params), 0); diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h index 751281414006..54c12e71417e 100644 --- a/drivers/net/wireless/wl12xx/cmd.h +++ b/drivers/net/wireless/wl12xx/cmd.h @@ -39,7 +39,7 @@ int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer); int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len); int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len); int wl1271_cmd_data_path(struct wl1271 *wl, bool enable); -int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode, u32 rates, bool send); +int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode); int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer, size_t len); int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, @@ -140,6 +140,7 @@ enum cmd_templ { * For CTS-to-self (FastCTS) mechanism * for BT/WLAN coexistence (SoftGemini). */ CMD_TEMPL_ARP_RSP, + CMD_TEMPL_LINK_MEASUREMENT_REPORT, /* AP-mode specific */ CMD_TEMPL_AP_BEACON = 13, @@ -216,6 +217,7 @@ struct wl1271_cmd_join { * ACK or CTS frames). */ __le32 basic_rate_set; + __le32 supported_rate_set; u8 dtim_interval; /* * bits 0-2: This bitwise field specifies the type @@ -278,15 +280,7 @@ struct wl1271_cmd_ps_params { struct wl1271_cmd_header header; u8 ps_mode; /* STATION_* */ - u8 send_null_data; /* Do we have to send NULL data packet ? */ - u8 retries; /* Number of retires for the initial NULL data packet */ - - /* - * TUs during which the target stays awake after switching - * to power save mode. - */ - u8 hang_over_period; - __le32 null_data_rate; + u8 padding[3]; } __packed; /* HW encryption keys */ diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index c81aecd755e5..d8c124919382 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -1151,6 +1151,34 @@ struct conf_ht_setting { u16 inactivity_timeout; }; +struct conf_memory_settings { + /* Disable/Enable dynamic memory */ + u8 dynamic_memory; + + /* + * Minimum required free tx memory blocks in order to assure optimum + * performence + * + * Range: 0-120 + */ + u8 min_req_tx_blocks; + + /* + * Minimum required free rx memory blocks in order to assure optimum + * performence + * + * Range: 0-120 + */ + u8 min_req_rx_blocks; + + /* + * Minimum number of mem blocks (free+used) guaranteed for TX + * + * Range: 0-120 + */ + u8 tx_min; +}; + struct conf_drv_settings { struct conf_sg_settings sg; struct conf_rx_settings rx; @@ -1162,6 +1190,7 @@ struct conf_drv_settings { struct conf_scan_settings scan; struct conf_rf_settings rf; struct conf_ht_setting ht; + struct conf_memory_settings mem; }; #endif diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c index 3376a5de09d7..1b170c5cc595 100644 --- a/drivers/net/wireless/wl12xx/event.c +++ b/drivers/net/wireless/wl12xx/event.c @@ -135,20 +135,6 @@ static int wl1271_event_ps_report(struct wl1271 *wl, /* go to extremely low power mode */ wl1271_ps_elp_sleep(wl); break; - case EVENT_EXIT_POWER_SAVE_FAIL: - wl1271_debug(DEBUG_PSM, "PSM exit failed"); - - if (test_bit(WL1271_FLAG_PSM, &wl->flags)) { - wl->psm_entry_retry = 0; - break; - } - - /* make sure the firmware goes to active mode - the frame to - be sent next will indicate to the AP, that we are active. */ - ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE, - wl->basic_rate, false); - break; - case EVENT_EXIT_POWER_SAVE_SUCCESS: default: break; } diff --git a/drivers/net/wireless/wl12xx/event.h b/drivers/net/wireless/wl12xx/event.h index 1d5ef670d480..0e80886f3031 100644 --- a/drivers/net/wireless/wl12xx/event.h +++ b/drivers/net/wireless/wl12xx/event.h @@ -75,8 +75,6 @@ enum { enum { EVENT_ENTER_POWER_SAVE_FAIL = 0, EVENT_ENTER_POWER_SAVE_SUCCESS, - EVENT_EXIT_POWER_SAVE_FAIL, - EVENT_EXIT_POWER_SAVE_SUCCESS, }; struct event_debug_report { diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index 70b3dc88a219..62dc9839dd31 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -325,6 +325,11 @@ static int wl1271_sta_hw_init(struct wl1271 *wl) if (ret < 0) return ret; + /* PS config */ + ret = wl1271_acx_config_ps(wl); + if (ret < 0) + return ret; + ret = wl1271_sta_init_templates_config(wl); if (ret < 0) return ret; @@ -367,6 +372,10 @@ static int wl1271_sta_hw_init(struct wl1271 *wl) if (ret < 0) return ret; + ret = wl1271_acx_sta_mem_cfg(wl); + if (ret < 0) + return ret; + return 0; } @@ -433,6 +442,10 @@ static int wl1271_ap_hw_init(struct wl1271 *wl) if (ret < 0) return ret; + ret = wl1271_acx_ap_mem_cfg(wl); + if (ret < 0) + return ret; + return 0; } diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 522bb09c9535..91d681221286 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -298,6 +298,12 @@ static struct conf_drv_settings default_conf = { .tx_ba_win_size = 64, .inactivity_timeout = 10000, }, + .mem = { + .dynamic_memory = 0, + .min_req_tx_blocks = 104, + .min_req_rx_blocks = 22, + .tx_min = 27, + } }; static void __wl1271_op_remove_interface(struct wl1271 *wl); @@ -524,13 +530,19 @@ static int wl1271_plt_init(struct wl1271 *wl) } static void wl1271_fw_status(struct wl1271 *wl, - struct wl1271_fw_status *status) + struct wl1271_fw_full_status *full_status) { + struct wl1271_fw_common_status *status = &full_status->common; struct timespec ts; u32 total = 0; int i; - wl1271_raw_read(wl, FW_STATUS_ADDR, status, sizeof(*status), false); + if (wl->bss_type == BSS_TYPE_AP_BSS) + wl1271_raw_read(wl, FW_STATUS_ADDR, status, + sizeof(struct wl1271_fw_ap_status), false); + else + wl1271_raw_read(wl, FW_STATUS_ADDR, status, + sizeof(struct wl1271_fw_sta_status), false); wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, " "drv_rx_counter = %d, tx_results_counter = %d)", @@ -589,7 +601,7 @@ static void wl1271_irq_work(struct work_struct *work) loopcount--; wl1271_fw_status(wl, wl->fw_status); - intr = le32_to_cpu(wl->fw_status->intr); + intr = le32_to_cpu(wl->fw_status->common.intr); if (!intr) { wl1271_debug(DEBUG_IRQ, "Zero interrupt received."); spin_lock_irqsave(&wl->wl_lock, flags); @@ -611,7 +623,7 @@ static void wl1271_irq_work(struct work_struct *work) wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA"); /* check for tx results */ - if (wl->fw_status->tx_results_counter != + if (wl->fw_status->common.tx_results_counter != (wl->tx_results_count & 0xff)) wl1271_tx_complete(wl); @@ -625,7 +637,7 @@ static void wl1271_irq_work(struct work_struct *work) wl1271_tx_work_locked(wl); } - wl1271_rx(wl, wl->fw_status); + wl1271_rx(wl, &wl->fw_status->common); } if (intr & WL1271_ACX_INTR_EVENT_A) { diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index 60a3738eadb0..2d3086ae6338 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/ps.c @@ -139,8 +139,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, return ret; } - ret = wl1271_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE, - rates, send); + ret = wl1271_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE); if (ret < 0) return ret; @@ -163,8 +162,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, if (ret < 0) return ret; - ret = wl1271_cmd_ps_mode(wl, STATION_ACTIVE_MODE, - rates, send); + ret = wl1271_cmd_ps_mode(wl, STATION_ACTIVE_MODE); if (ret < 0) return ret; diff --git a/drivers/net/wireless/wl12xx/rx.c b/drivers/net/wireless/wl12xx/rx.c index b0c6ddc2a945..00d250d8da18 100644 --- a/drivers/net/wireless/wl12xx/rx.c +++ b/drivers/net/wireless/wl12xx/rx.c @@ -29,14 +29,14 @@ #include "rx.h" #include "io.h" -static u8 wl1271_rx_get_mem_block(struct wl1271_fw_status *status, +static u8 wl1271_rx_get_mem_block(struct wl1271_fw_common_status *status, u32 drv_rx_counter) { return le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]) & RX_MEM_BLOCK_MASK; } -static u32 wl1271_rx_get_buf_size(struct wl1271_fw_status *status, +static u32 wl1271_rx_get_buf_size(struct wl1271_fw_common_status *status, u32 drv_rx_counter) { return (le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]) & @@ -134,7 +134,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) return 0; } -void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status) +void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status) { struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map; u32 buf_size; diff --git a/drivers/net/wireless/wl12xx/rx.h b/drivers/net/wireless/wl12xx/rx.h index 8d048b36bbba..4cef8fa3dee1 100644 --- a/drivers/net/wireless/wl12xx/rx.h +++ b/drivers/net/wireless/wl12xx/rx.h @@ -119,7 +119,7 @@ struct wl1271_rx_descriptor { u8 reserved; } __packed; -void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status); +void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status); u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band); void wl1271_set_default_filters(struct wl1271 *wl); diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index d1de13fe7d9a..140e26f3bae9 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -130,7 +130,7 @@ extern u32 wl12xx_debug_level; -#define WL1271_FW_NAME "wl1271-fw.bin" +#define WL1271_FW_NAME "wl1271-fw-2.bin" #define WL1271_AP_FW_NAME "wl1271-fw-ap.bin" #define WL1271_NVS_NAME "wl1271-nvs.bin" @@ -214,8 +214,8 @@ struct wl1271_stats { /* Broadcast and Global links + links to stations */ #define AP_MAX_LINKS (AP_MAX_STATIONS + 2) -/* FW status registers */ -struct wl1271_fw_status { +/* FW status registers common for AP/STA */ +struct wl1271_fw_common_status { __le32 intr; u8 fw_rx_counter; u8 drv_rx_counter; @@ -224,6 +224,11 @@ struct wl1271_fw_status { __le32 rx_pkt_descs[NUM_RX_PKT_DESC]; __le32 tx_released_blks[NUM_TX_QUEUES]; __le32 fw_localtime; +} __packed; + +/* FW status registers for AP */ +struct wl1271_fw_ap_status { + struct wl1271_fw_common_status common; /* Next fields valid only in AP FW */ @@ -238,6 +243,24 @@ struct wl1271_fw_status { u8 padding_1[1]; } __packed; +/* FW status registers for STA */ +struct wl1271_fw_sta_status { + struct wl1271_fw_common_status common; + + u8 tx_total; + u8 reserved1; + __le16 reserved2; +} __packed; + +struct wl1271_fw_full_status { + union { + struct wl1271_fw_common_status common; + struct wl1271_fw_sta_status sta; + struct wl1271_fw_ap_status ap; + }; +} __packed; + + struct wl1271_rx_mem_pool_addr { u32 addr; u32 addr_extra; @@ -445,7 +468,7 @@ struct wl1271 { u32 buffer_cmd; u32 buffer_busyword[WL1271_BUSY_WORD_CNT]; - struct wl1271_fw_status *fw_status; + struct wl1271_fw_full_status *fw_status; struct wl1271_tx_hw_res_if *tx_res_if; struct ieee80211_vif *vif; -- cgit v1.2.3 From fe5ef090660de340b52823de7cb65b899bb4f012 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 2 Feb 2011 09:59:36 +0200 Subject: wl12xx: use the conf struct instead of macros for memory configuration make the configuration management more flexible by using the conf struct, rather than predefined macros. Signed-off-by: Eliad Peller Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 16 ++++++++-------- drivers/net/wireless/wl12xx/acx.h | 4 ---- drivers/net/wireless/wl12xx/conf.h | 12 ++++++++++++ drivers/net/wireless/wl12xx/main.c | 4 ++++ 4 files changed, 24 insertions(+), 12 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index f2fbda06a129..6ea19d75cecc 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -961,10 +961,10 @@ int wl1271_acx_ap_mem_cfg(struct wl1271 *wl) } /* memory config */ - mem_conf->num_stations = DEFAULT_NUM_STATIONS; - mem_conf->rx_mem_block_num = ACX_RX_MEM_BLOCKS; - mem_conf->tx_min_mem_block_num = ACX_TX_MIN_MEM_BLOCKS; - mem_conf->num_ssid_profiles = ACX_NUM_SSID_PROFILES; + mem_conf->num_stations = wl->conf.mem.num_stations; + mem_conf->rx_mem_block_num = wl->conf.mem.rx_block_num; + mem_conf->tx_min_mem_block_num = wl->conf.mem.tx_min_block_num; + mem_conf->num_ssid_profiles = wl->conf.mem.ssid_profiles; mem_conf->total_tx_descriptors = cpu_to_le32(ACX_TX_DESCRIPTORS); ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf, @@ -993,10 +993,10 @@ int wl1271_acx_sta_mem_cfg(struct wl1271 *wl) } /* memory config */ - mem_conf->num_stations = DEFAULT_NUM_STATIONS; - mem_conf->rx_mem_block_num = ACX_RX_MEM_BLOCKS; - mem_conf->tx_min_mem_block_num = ACX_TX_MIN_MEM_BLOCKS; - mem_conf->num_ssid_profiles = ACX_NUM_SSID_PROFILES; + mem_conf->num_stations = wl->conf.mem.num_stations; + mem_conf->rx_mem_block_num = wl->conf.mem.rx_block_num; + mem_conf->tx_min_mem_block_num = wl->conf.mem.tx_min_block_num; + mem_conf->num_ssid_profiles = wl->conf.mem.ssid_profiles; mem_conf->total_tx_descriptors = cpu_to_le32(ACX_TX_DESCRIPTORS); mem_conf->dyn_mem_enable = wl->conf.mem.dynamic_memory; mem_conf->tx_free_req = wl->conf.mem.min_req_tx_blocks; diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index 537fab40ed12..4e301de916bb 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -133,7 +133,6 @@ enum { #define DEFAULT_UCAST_PRIORITY 0 #define DEFAULT_RX_Q_PRIORITY 0 -#define DEFAULT_NUM_STATIONS 1 #define DEFAULT_RXQ_PRIORITY 0 /* low 0 .. 15 high */ #define DEFAULT_RXQ_TYPE 0x07 /* All frames, Data/Ctrl/Mgmt */ #define TRACE_BUFFER_MAX_SIZE 256 @@ -797,10 +796,7 @@ struct acx_tx_config_options { __le16 tx_compl_threshold; /* number of packets */ } __packed; -#define ACX_RX_MEM_BLOCKS 70 -#define ACX_TX_MIN_MEM_BLOCKS 40 #define ACX_TX_DESCRIPTORS 32 -#define ACX_NUM_SSID_PROFILES 1 struct wl1271_acx_ap_config_memory { struct acx_header header; diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index d8c124919382..856a8a2fff4f 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -1152,6 +1152,18 @@ struct conf_ht_setting { }; struct conf_memory_settings { + /* Number of stations supported in IBSS mode */ + u8 num_stations; + + /* Number of ssid profiles used in IBSS mode */ + u8 ssid_profiles; + + /* Number of memory buffers allocated to rx pool */ + u8 rx_block_num; + + /* Minimum number of blocks allocated to tx pool */ + u8 tx_min_block_num; + /* Disable/Enable dynamic memory */ u8 dynamic_memory; diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 91d681221286..24cdc78c00b3 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -299,6 +299,10 @@ static struct conf_drv_settings default_conf = { .inactivity_timeout = 10000, }, .mem = { + .num_stations = 1, + .ssid_profiles = 1, + .rx_block_num = 70, + .tx_min_block_num = 40, .dynamic_memory = 0, .min_req_tx_blocks = 104, .min_req_rx_blocks = 22, -- cgit v1.2.3 From 72c2d9e511846a4f2759389b38ed8a5553579eb3 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 2 Feb 2011 09:59:37 +0200 Subject: wl12xx: set supported_rates after association Instead of looking for supported_rates change on every tx packet, just extract the supported_rates after association completes (station only). Remove wl1271.sta_rate_set and WL1271_FLAG_STA_RATES_CHANGED which are not used anymore. Signed-off-by: Eliad Peller Reviewed-by: Juuso Oikarinen Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 4 ++ drivers/net/wireless/wl12xx/cmd.c | 11 ++-- drivers/net/wireless/wl12xx/main.c | 116 ++++++++++++++--------------------- drivers/net/wireless/wl12xx/tx.c | 22 ------- drivers/net/wireless/wl12xx/wl12xx.h | 35 ++++++----- 5 files changed, 73 insertions(+), 115 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index 6ea19d75cecc..33840d95d17d 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -783,6 +783,10 @@ int wl1271_acx_sta_rate_policies(struct wl1271 *wl) acx->rate_class_cnt = cpu_to_le32(ACX_TX_RATE_POLICY_CNT); + wl1271_debug(DEBUG_ACX, "basic_rate: 0x%x, full_rate: 0x%x", + acx->rate_class[ACX_TX_BASIC_RATE].enabled_rates, + acx->rate_class[ACX_TX_AP_FULL_RATE].enabled_rates); + ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("Setting of rate policies failed: %d", ret); diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 66d15e77da38..97ffd7aa57a8 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -286,13 +286,7 @@ int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type) join->rx_filter_options = cpu_to_le32(wl->rx_filter); join->bss_type = bss_type; join->basic_rate_set = cpu_to_le32(wl->basic_rate_set); - /* - * for supported_rate_set, we should use wl->rate_set. however, - * it seems that acx_rate_policies doesn't affect full_rate, and - * since we want to avoid additional join, we'll use a 0xffffffff value, - * and let the fw find the actual supported rates - */ - join->supported_rate_set = cpu_to_le32(0xffffffff); + join->supported_rate_set = cpu_to_le32(wl->rate_set); if (wl->band == IEEE80211_BAND_5GHZ) join->bss_type |= WL1271_JOIN_CMD_BSS_TYPE_5GHZ; @@ -310,6 +304,9 @@ int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type) wl->tx_security_last_seq = 0; wl->tx_security_seq = 0; + wl1271_debug(DEBUG_CMD, "cmd join: basic_rate_set=0x%x, rate_set=0x%x", + join->basic_rate_set, join->supported_rate_set); + ret = wl1271_cmd_send(wl, CMD_START_JOIN, join, sizeof(*join), 0); if (ret < 0) { wl1271_error("failed to initiate cmd join"); diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 24cdc78c00b3..61dea73f5fdc 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -978,39 +978,10 @@ int wl1271_plt_stop(struct wl1271 *wl) static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct wl1271 *wl = hw->priv; - struct ieee80211_conf *conf = &hw->conf; - struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb); - struct ieee80211_sta *sta = txinfo->control.sta; unsigned long flags; int q; - /* - * peek into the rates configured in the STA entry. - * The rates set after connection stage, The first block only BG sets: - * the compare is for bit 0-16 of sta_rate_set. The second block add - * HT rates in case of HT supported. - */ spin_lock_irqsave(&wl->wl_lock, flags); - if (sta && - (sta->supp_rates[conf->channel->band] != - (wl->sta_rate_set & HW_BG_RATES_MASK)) && - wl->bss_type != BSS_TYPE_AP_BSS) { - wl->sta_rate_set = sta->supp_rates[conf->channel->band]; - set_bit(WL1271_FLAG_STA_RATES_CHANGED, &wl->flags); - } - -#ifdef CONFIG_WL12XX_HT - if (sta && - sta->ht_cap.ht_supported && - ((wl->sta_rate_set >> HW_HT_RATES_OFFSET) != - sta->ht_cap.mcs.rx_mask[0])) { - /* Clean MCS bits before setting them */ - wl->sta_rate_set &= HW_BG_RATES_MASK; - wl->sta_rate_set |= - (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET); - set_bit(WL1271_FLAG_STA_RATES_CHANGED, &wl->flags); - } -#endif wl->tx_queue_count++; spin_unlock_irqrestore(&wl->wl_lock, flags); @@ -1245,7 +1216,6 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) wl->time_offset = 0; wl->session_counter = 0; wl->rate_set = CONF_TX_RATE_MASK_BASIC; - wl->sta_rate_set = 0; wl->flags = 0; wl->vif = NULL; wl->filters = 0; @@ -1432,7 +1402,6 @@ static int wl1271_sta_handle_idle(struct wl1271 *wl, bool idle) goto out; } wl->rate_set = wl1271_tx_min_rate_get(wl); - wl->sta_rate_set = 0; ret = wl1271_acx_sta_rate_policies(wl); if (ret < 0) goto out; @@ -2246,6 +2215,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, { bool do_join = false, set_assoc = false; bool is_ibss = (wl->bss_type == BSS_TYPE_IBSS); + u32 sta_rate_set = 0; int ret; struct ieee80211_sta *sta; @@ -2311,6 +2281,49 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, } } + rcu_read_lock(); + sta = ieee80211_find_sta(vif, bss_conf->bssid); + if (sta) { + /* save the supp_rates of the ap */ + sta_rate_set = sta->supp_rates[wl->hw->conf.channel->band]; + if (sta->ht_cap.ht_supported) + sta_rate_set |= + (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET); + + /* handle new association with HT and HT information change */ + if ((changed & BSS_CHANGED_HT) && + (bss_conf->channel_type != NL80211_CHAN_NO_HT)) { + ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, + true); + if (ret < 0) { + wl1271_warning("Set ht cap true failed %d", + ret); + rcu_read_unlock(); + goto out; + } + ret = wl1271_acx_set_ht_information(wl, + bss_conf->ht_operation_mode); + if (ret < 0) { + wl1271_warning("Set ht information failed %d", + ret); + rcu_read_unlock(); + goto out; + } + } + /* handle new association without HT and disassociation */ + else if (changed & BSS_CHANGED_ASSOC) { + ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, + false); + if (ret < 0) { + wl1271_warning("Set ht cap false failed %d", + ret); + rcu_read_unlock(); + goto out; + } + } + } + rcu_read_unlock(); + if ((changed & BSS_CHANGED_ASSOC)) { if (bss_conf->assoc) { u32 rates; @@ -2328,6 +2341,9 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates); wl->basic_rate = wl1271_tx_min_rate_get(wl); + if (sta_rate_set) + wl->rate_set = wl1271_tx_enabled_rates_get(wl, + sta_rate_set); ret = wl1271_acx_sta_rate_policies(wl); if (ret < 0) goto out; @@ -2406,43 +2422,6 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, if (ret < 0) goto out; - rcu_read_lock(); - sta = ieee80211_find_sta(vif, bss_conf->bssid); - if (sta) { - /* handle new association with HT and HT information change */ - if ((changed & BSS_CHANGED_HT) && - (bss_conf->channel_type != NL80211_CHAN_NO_HT)) { - ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, - true); - if (ret < 0) { - wl1271_warning("Set ht cap true failed %d", - ret); - rcu_read_unlock(); - goto out; - } - ret = wl1271_acx_set_ht_information(wl, - bss_conf->ht_operation_mode); - if (ret < 0) { - wl1271_warning("Set ht information failed %d", - ret); - rcu_read_unlock(); - goto out; - } - } - /* handle new association without HT and disassociation */ - else if (changed & BSS_CHANGED_ASSOC) { - ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, - false); - if (ret < 0) { - wl1271_warning("Set ht cap false failed %d", - ret); - rcu_read_unlock(); - goto out; - } - } - } - rcu_read_unlock(); - if (changed & BSS_CHANGED_ARP_FILTER) { __be32 addr = bss_conf->arp_addr_list[0]; WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS); @@ -3330,7 +3309,6 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->basic_rate_set = CONF_TX_RATE_MASK_BASIC; wl->basic_rate = CONF_TX_RATE_MASK_BASIC; wl->rate_set = CONF_TX_RATE_MASK_BASIC; - wl->sta_rate_set = 0; wl->band = IEEE80211_BAND_2GHZ; wl->vif = NULL; wl->flags = 0; diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 3507c81c7500..67a00946e3dd 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -334,35 +334,13 @@ void wl1271_tx_work_locked(struct wl1271 *wl) { struct sk_buff *skb; bool woken_up = false; - u32 sta_rates = 0; u32 buf_offset = 0; bool sent_packets = false; int ret; - /* check if the rates supported by the AP have changed */ - if (unlikely(test_and_clear_bit(WL1271_FLAG_STA_RATES_CHANGED, - &wl->flags))) { - unsigned long flags; - - spin_lock_irqsave(&wl->wl_lock, flags); - sta_rates = wl->sta_rate_set; - spin_unlock_irqrestore(&wl->wl_lock, flags); - } - if (unlikely(wl->state == WL1271_STATE_OFF)) goto out; - /* if rates have changed, re-configure the rate policy */ - if (unlikely(sta_rates)) { - ret = wl1271_ps_elp_wakeup(wl, false); - if (ret < 0) - goto out; - woken_up = true; - - wl->rate_set = wl1271_tx_enabled_rates_get(wl, sta_rates); - wl1271_acx_sta_rate_policies(wl); - } - while ((skb = wl1271_skb_dequeue(wl))) { if (!woken_up) { ret = wl1271_ps_elp_wakeup(wl, false); diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 140e26f3bae9..1d6c94304b1a 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -301,6 +301,24 @@ struct wl1271_ap_key { u16 tx_seq_16; }; +enum wl12xx_flags { + WL1271_FLAG_STA_ASSOCIATED, + WL1271_FLAG_JOINED, + WL1271_FLAG_GPIO_POWER, + WL1271_FLAG_TX_QUEUE_STOPPED, + WL1271_FLAG_IN_ELP, + WL1271_FLAG_PSM, + WL1271_FLAG_PSM_REQUESTED, + WL1271_FLAG_IRQ_PENDING, + WL1271_FLAG_IRQ_RUNNING, + WL1271_FLAG_IDLE, + WL1271_FLAG_IDLE_REQUESTED, + WL1271_FLAG_PSPOLL_FAILURE, + WL1271_FLAG_STA_STATE_SENT, + WL1271_FLAG_FW_TX_BUSY, + WL1271_FLAG_AP_STARTED +}; + struct wl1271 { struct platform_device *plat_dev; struct ieee80211_hw *hw; @@ -319,22 +337,6 @@ struct wl1271 { enum wl1271_state state; struct mutex mutex; -#define WL1271_FLAG_STA_RATES_CHANGED (0) -#define WL1271_FLAG_STA_ASSOCIATED (1) -#define WL1271_FLAG_JOINED (2) -#define WL1271_FLAG_GPIO_POWER (3) -#define WL1271_FLAG_TX_QUEUE_STOPPED (4) -#define WL1271_FLAG_IN_ELP (5) -#define WL1271_FLAG_PSM (6) -#define WL1271_FLAG_PSM_REQUESTED (7) -#define WL1271_FLAG_IRQ_PENDING (8) -#define WL1271_FLAG_IRQ_RUNNING (9) -#define WL1271_FLAG_IDLE (10) -#define WL1271_FLAG_IDLE_REQUESTED (11) -#define WL1271_FLAG_PSPOLL_FAILURE (12) -#define WL1271_FLAG_STA_STATE_SENT (13) -#define WL1271_FLAG_FW_TX_BUSY (14) -#define WL1271_FLAG_AP_STARTED (15) unsigned long flags; struct wl1271_partition_set part; @@ -428,7 +430,6 @@ struct wl1271 { * bits 16-23 - 802.11n MCS index mask * support only 1 stream, thus only 8 bits for the MCS rates (0-7). */ - u32 sta_rate_set; u32 basic_rate_set; u32 basic_rate; u32 rate_set; -- cgit v1.2.3 From 0aec516ce4cfd44f48b3ae0c54bc2f1eab007173 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 18 Feb 2011 17:25:36 -0800 Subject: wl12xx: fix sdio_test kconfig/build errors The wl12xx sdio_test code uses wl12xx_get_platform_data, which is only present when WL12*_SDIO is enabled, so make WL12XX_SDIO_TEST depend on WL12XX_SDIO so that the needed interface will be present. sdio_test.c:(.devinit.text+0x13178): undefined reference to `wl12xx_get_platform_data' Signed-off-by: Randy Dunlap Cc: Luciano Coelho Cc: Roger Quadros Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/Kconfig b/drivers/net/wireless/wl12xx/Kconfig index 0e65bce457d6..692ebff38fc8 100644 --- a/drivers/net/wireless/wl12xx/Kconfig +++ b/drivers/net/wireless/wl12xx/Kconfig @@ -54,7 +54,7 @@ config WL12XX_SDIO config WL12XX_SDIO_TEST tristate "TI wl12xx SDIO testing support" - depends on WL12XX && MMC + depends on WL12XX && MMC && WL12XX_SDIO default n ---help--- This module adds support for the SDIO bus testing with the -- cgit v1.2.3 From 03c5a9cf49999ca3431eb9199c9bb831b0020be2 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 7 Feb 2011 19:47:42 +0300 Subject: wl12xx: change type from u8 to int ret is used to store int types. Using an u8 will break the error handling. Signed-off-by: Dan Carpenter Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index 62dc9839dd31..6072fe457135 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -483,7 +483,7 @@ static void wl1271_check_ba_support(struct wl1271 *wl) static int wl1271_set_ba_policies(struct wl1271 *wl) { u8 tid_index; - u8 ret = 0; + int ret = 0; /* Reset the BA RX indicators */ wl->ba_rx_bitmap = 0; -- cgit v1.2.3 From 1ec610ebd6390c2b028434144af204c312a68791 Mon Sep 17 00:00:00 2001 From: Gery Kahn Date: Tue, 1 Feb 2011 03:03:08 -0600 Subject: wl12xx: update PLT initialization for new firmware In revision > 6.1.3.0.0 the firmware expects memory configuration command as part of boot. This was missing if driver boots in PLT mode. The patch adds the memory configuration command, which fixes PLT commands tx continuous and rx statistics. Signed-off-by: Gery Kahn Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 61dea73f5fdc..cf8b3cebe9d6 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -482,6 +482,10 @@ static int wl1271_plt_init(struct wl1271 *wl) if (ret < 0) goto out_free_memmap; + ret = wl1271_acx_sta_mem_cfg(wl); + if (ret < 0) + goto out_free_memmap; + /* Default fragmentation threshold */ ret = wl1271_acx_frag_threshold(wl, wl->conf.tx.frag_threshold); if (ret < 0) -- cgit v1.2.3 From 92fe9b5f112c77dbb63f42f7bed885d709586106 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 9 Feb 2011 12:25:14 +0200 Subject: wl12xx: fix identification of beacon packets (debug) for debugging purposes, wl12xx determines whether a rx packet is a beacon packet. however, it checks only the frame_control subtype without checking the actual packet type, which leads to false identification in some cases. use ieee80211_is_beacon instead. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/rx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/rx.c b/drivers/net/wireless/wl12xx/rx.c index 00d250d8da18..3d13d7a83ea1 100644 --- a/drivers/net/wireless/wl12xx/rx.c +++ b/drivers/net/wireless/wl12xx/rx.c @@ -92,7 +92,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) { struct wl1271_rx_descriptor *desc; struct sk_buff *skb; - u16 *fc; + struct ieee80211_hdr *hdr; u8 *buf; u8 beacon = 0; @@ -118,8 +118,8 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) /* now we pull the descriptor out of the buffer */ skb_pull(skb, sizeof(*desc)); - fc = (u16 *)skb->data; - if ((*fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON) + hdr = (struct ieee80211_hdr *)skb->data; + if (ieee80211_is_beacon(hdr->frame_control)) beacon = 1; wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon); -- cgit v1.2.3 From a100885d9dfd8685e0b4e442afc9041ee4c90586 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sat, 12 Feb 2011 23:24:20 +0200 Subject: wl12xx: avoid blocking while holding rcu lock on bss info change Some blocking functions were called while holding the rcu lock for accessing STA information. This can lead to a deadlock. Save the required info beforehand and release the rcu without blocking. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index cf8b3cebe9d6..d51d55998f4e 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2222,6 +2222,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, u32 sta_rate_set = 0; int ret; struct ieee80211_sta *sta; + bool sta_exists = false; + struct ieee80211_sta_ht_cap sta_ht_cap; if (is_ibss) { ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, @@ -2293,16 +2295,20 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, if (sta->ht_cap.ht_supported) sta_rate_set |= (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET); + sta_ht_cap = sta->ht_cap; + sta_exists = true; + } + rcu_read_unlock(); + if (sta_exists) { /* handle new association with HT and HT information change */ if ((changed & BSS_CHANGED_HT) && (bss_conf->channel_type != NL80211_CHAN_NO_HT)) { - ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, + ret = wl1271_acx_set_ht_capabilities(wl, &sta_ht_cap, true); if (ret < 0) { wl1271_warning("Set ht cap true failed %d", ret); - rcu_read_unlock(); goto out; } ret = wl1271_acx_set_ht_information(wl, @@ -2310,23 +2316,20 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, if (ret < 0) { wl1271_warning("Set ht information failed %d", ret); - rcu_read_unlock(); goto out; } } /* handle new association without HT and disassociation */ else if (changed & BSS_CHANGED_ASSOC) { - ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, + ret = wl1271_acx_set_ht_capabilities(wl, &sta_ht_cap, false); if (ret < 0) { wl1271_warning("Set ht cap false failed %d", ret); - rcu_read_unlock(); goto out; } } } - rcu_read_unlock(); if ((changed & BSS_CHANGED_ASSOC)) { if (bss_conf->assoc) { -- cgit v1.2.3 From b1a48cab6f47de18a989927e24025aab7ea106ff Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 22 Feb 2011 14:19:28 +0200 Subject: wl12xx: fix MODULE_AUTHOR email address Change my old email address to the new one in MODULE_AUTHOR. Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index d51d55998f4e..4bcb848437e6 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -3419,5 +3419,5 @@ module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(debug_level, "wl12xx debugging level"); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Luciano Coelho "); +MODULE_AUTHOR("Luciano Coelho "); MODULE_AUTHOR("Juuso Oikarinen "); -- cgit v1.2.3 From 62c0740c4ff2a4a8850619e0f5ac56a3b6e83cec Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 2 Feb 2011 11:20:05 +0200 Subject: wl12xx: declare support for IEEE80211_HW_REPORTS_TX_ACK_STATUS The wl12xx fw supports ack status reporting for tx frames, so add the IEEE80211_HW_REPORTS_TX_ACK_STATUS flag to our supported features. Since we do the rate control in the fw, we'll probably want to adjust the STA_LOST_PKT_THRESHOLD heuristics in the future, to account for retransmissions as well. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 4bcb848437e6..13c7102f9864 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -3219,7 +3219,8 @@ int wl1271_init_ieee80211(struct wl1271 *wl) IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_HAS_RATE_CONTROL | IEEE80211_HW_CONNECTION_MONITOR | - IEEE80211_HW_SUPPORTS_CQM_RSSI; + IEEE80211_HW_SUPPORTS_CQM_RSSI | + IEEE80211_HW_REPORTS_TX_ACK_STATUS; wl->hw->wiphy->cipher_suites = cipher_suites; wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); -- cgit v1.2.3 From 02ad2d9080266e6d999c00b78610ef6a45be45ea Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 23 Feb 2011 00:27:06 +0200 Subject: wl12xx: use standard ALIGN() macro Use the standard ALIGN() macro instead of redefining similar macros. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/rx.h | 4 ---- drivers/net/wireless/wl12xx/tx.c | 4 ++-- drivers/net/wireless/wl12xx/tx.h | 2 -- 3 files changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/rx.h b/drivers/net/wireless/wl12xx/rx.h index 4cef8fa3dee1..75fabf836491 100644 --- a/drivers/net/wireless/wl12xx/rx.h +++ b/drivers/net/wireless/wl12xx/rx.h @@ -30,10 +30,6 @@ #define WL1271_RX_MAX_RSSI -30 #define WL1271_RX_MIN_RSSI -95 -#define WL1271_RX_ALIGN_TO 4 -#define WL1271_RX_ALIGN(len) (((len) + WL1271_RX_ALIGN_TO - 1) & \ - ~(WL1271_RX_ALIGN_TO - 1)) - #define SHORT_PREAMBLE_BIT BIT(0) #define OFDM_RATE_BIT BIT(6) #define PBCC_RATE_BIT BIT(7) diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 67a00946e3dd..94ff3faf7dde 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -185,7 +185,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, desc->reserved = 0; /* align the length (and store in terms of words) */ - pad = WL1271_TX_ALIGN(skb->len); + pad = ALIGN(skb->len, WL1271_TX_ALIGN_TO); desc->length = cpu_to_le16(pad >> 2); /* calculate number of padding bytes */ @@ -245,7 +245,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, * pad the skb data to make sure its length is aligned. * The number of padding bytes is computed and set in wl1271_tx_fill_hdr */ - total_len = WL1271_TX_ALIGN(skb->len); + total_len = ALIGN(skb->len, WL1271_TX_ALIGN_TO); memcpy(wl->aggr_buf + buf_offset, skb->data, skb->len); memset(wl->aggr_buf + buf_offset + skb->len, 0, total_len - skb->len); diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h index 05722a560d91..db88f58707a3 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/tx.h @@ -53,8 +53,6 @@ #define TX_HW_RESULT_QUEUE_LEN_MASK 0xf #define WL1271_TX_ALIGN_TO 4 -#define WL1271_TX_ALIGN(len) (((len) + WL1271_TX_ALIGN_TO - 1) & \ - ~(WL1271_TX_ALIGN_TO - 1)) #define WL1271_TKIP_IV_SPACE 4 struct wl1271_tx_hw_descr { -- cgit v1.2.3 From 6dc9fb3c78a78982f6418b6cf457140f7afa658d Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 23 Feb 2011 00:27:07 +0200 Subject: wl12xx: always set mac_address when configuring ht caps The mac_address should be set also when ht caps are disabled. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index 33840d95d17d..6d5312990f79 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -1328,10 +1328,9 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl, /* get data from A-MPDU parameters field */ acx->ampdu_max_length = ht_cap->ampdu_factor; acx->ampdu_min_spacing = ht_cap->ampdu_density; - - memcpy(acx->mac_address, mac_address, ETH_ALEN); } + memcpy(acx->mac_address, mac_address, ETH_ALEN); acx->ht_capabilites = cpu_to_le32(ht_capabilites); ret = wl1271_cmd_configure(wl, ACX_PEER_HT_CAP, acx, sizeof(*acx)); -- cgit v1.2.3 From f4d08ddd3e60c79a141be36a5f3a7294c619291d Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 23 Feb 2011 00:22:24 +0200 Subject: wl12xx: fix potential race condition with TX queue watermark Check the conditions for the high/low TX queue watermarks when the spin-lock is taken. This prevents race conditions as tx_queue_count and the flag checked are only modified when the spin-lock is taken. The following race was in mind: - Queues are almost full and wl1271_op_tx() will stop the queues, but it doesn't get the spin-lock yet. - (on another CPU) tx_work_locked() dequeues 15 skbs from this queue and tx_queue_count is updated to reflect this - wl1271_op_tx() does not check tx_queue_count after taking the spin-lock and incorrectly stops the queue. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 13c7102f9864..9bb9ad31c2f5 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -987,6 +987,17 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) spin_lock_irqsave(&wl->wl_lock, flags); wl->tx_queue_count++; + + /* + * The workqueue is slow to process the tx_queue and we need stop + * the queue here, otherwise the queue will get too long. + */ + if (wl->tx_queue_count >= WL1271_TX_QUEUE_HIGH_WATERMARK) { + wl1271_debug(DEBUG_TX, "op_tx: stopping queues"); + ieee80211_stop_queues(wl->hw); + set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags); + } + spin_unlock_irqrestore(&wl->wl_lock, flags); /* queue the packet */ @@ -1001,19 +1012,6 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags)) ieee80211_queue_work(wl->hw, &wl->tx_work); - /* - * The workqueue is slow to process the tx_queue and we need stop - * the queue here, otherwise the queue will get too long. - */ - if (wl->tx_queue_count >= WL1271_TX_QUEUE_HIGH_WATERMARK) { - wl1271_debug(DEBUG_TX, "op_tx: stopping queues"); - - spin_lock_irqsave(&wl->wl_lock, flags); - ieee80211_stop_queues(wl->hw); - set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags); - spin_unlock_irqrestore(&wl->wl_lock, flags); - } - return NETDEV_TX_OK; } -- cgit v1.2.3 From 99a2775d02a7accf4cc661a65c76fd7b379d1c7a Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 23 Feb 2011 00:22:25 +0200 Subject: wl12xx: AP-mode - fix race condition on sta connection If a sta starts transmitting immediately after authentication, sometimes the FW deauthenticates it. Fix this by marking the sta "in-connection" in FW before sending the autentication response. The "in-connection" entry is automatically removed when connection succeeds or after a timeout. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 25 +++++++++++++++++++++++++ drivers/net/wireless/wl12xx/acx.h | 9 +++++++++ drivers/net/wireless/wl12xx/tx.c | 19 +++++++++++++++++++ 3 files changed, 53 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index 6d5312990f79..3badc6bb7866 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -1541,3 +1541,28 @@ out: kfree(config_ps); return ret; } + +int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr) +{ + struct wl1271_acx_inconnection_sta *acx = NULL; + int ret; + + wl1271_debug(DEBUG_ACX, "acx set inconnaction sta %pM", addr); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) + return -ENOMEM; + + memcpy(acx->addr, addr, ETH_ALEN); + + ret = wl1271_cmd_configure(wl, ACX_UPDATE_INCONNECTION_STA_LIST, + acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx set inconnaction sta failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index 4e301de916bb..dd19b01d807b 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -1155,6 +1155,13 @@ struct wl1271_acx_config_ps { __le32 null_data_rate; } __packed; +struct wl1271_acx_inconnection_sta { + struct acx_header header; + + u8 addr[ETH_ALEN]; + u8 padding1[2]; +} __packed; + enum { ACX_WAKE_UP_CONDITIONS = 0x0002, ACX_MEM_CFG = 0x0003, @@ -1215,6 +1222,7 @@ enum { ACX_GEN_FW_CMD = 0x0070, ACX_HOST_IF_CFG_BITMAP = 0x0071, ACX_MAX_TX_FAILURE = 0x0072, + ACX_UPDATE_INCONNECTION_STA_LIST = 0x0073, DOT11_RX_MSDU_LIFE_TIME = 0x1004, DOT11_CUR_TX_PWR = 0x100D, DOT11_RX_DOT11_MODE = 0x1012, @@ -1290,5 +1298,6 @@ int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn, int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime); int wl1271_acx_max_tx_retry(struct wl1271 *wl); int wl1271_acx_config_ps(struct wl1271 *wl); +int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr); #endif /* __WL1271_ACX_H__ */ diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 94ff3faf7dde..0bb57daac889 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -70,6 +70,22 @@ static void wl1271_free_tx_id(struct wl1271 *wl, int id) } } +static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr; + + /* + * add the station to the known list before transmitting the + * authentication response. this way it won't get de-authed by FW + * when transmitting too soon. + */ + hdr = (struct ieee80211_hdr *)(skb->data + + sizeof(struct wl1271_tx_hw_descr)); + if (ieee80211_is_auth(hdr->frame_control)) + wl1271_acx_set_inconnection_sta(wl, hdr->addr1); +} + static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, u32 buf_offset) { @@ -238,6 +254,9 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, if (ret < 0) return ret; + if (wl->bss_type == BSS_TYPE_AP_BSS) + wl1271_tx_ap_update_inconnection_sta(wl, skb); + wl1271_tx_fill_hdr(wl, skb, extra, info); /* -- cgit v1.2.3 From a8c0ddb5ba2889e1e11a033ccbadfc600f236a91 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 23 Feb 2011 00:22:26 +0200 Subject: wl12xx: AP-mode - TX queue per link in AC When operating in AP-mode we require a per link tx-queue. This allows us to implement HW assisted PS mode for links, as well as regulate per-link FW TX blocks consumption. Split each link into ACs to support future QoS for AP-mode. AC queues are emptied in priority and per-link queues are scheduled in a simple round-robin fashion. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 17 ++++- drivers/net/wireless/wl12xx/tx.c | 130 ++++++++++++++++++++++++++++++++--- drivers/net/wireless/wl12xx/tx.h | 3 + drivers/net/wireless/wl12xx/wl12xx.h | 14 ++++ 4 files changed, 151 insertions(+), 13 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 9bb9ad31c2f5..3a4f6069be60 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -984,6 +984,7 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) struct wl1271 *wl = hw->priv; unsigned long flags; int q; + u8 hlid = 0; spin_lock_irqsave(&wl->wl_lock, flags); wl->tx_queue_count++; @@ -1002,7 +1003,13 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) /* queue the packet */ q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); - skb_queue_tail(&wl->tx_queue[q], skb); + if (wl->bss_type == BSS_TYPE_AP_BSS) { + hlid = wl1271_tx_get_hlid(skb); + wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d", hlid, q); + skb_queue_tail(&wl->links[hlid].tx_queue[q], skb); + } else { + skb_queue_tail(&wl->tx_queue[q], skb); + } /* * The chip specific setup must run before the first TX packet - @@ -2643,6 +2650,7 @@ static void wl1271_free_hlid(struct wl1271 *wl, u8 hlid) int id = hlid - WL1271_AP_STA_HLID_START; __clear_bit(id, wl->ap_hlid_map); + wl1271_tx_reset_link_queues(wl, hlid); } static int wl1271_op_sta_add(struct ieee80211_hw *hw, @@ -3270,7 +3278,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) struct ieee80211_hw *hw; struct platform_device *plat_dev = NULL; struct wl1271 *wl; - int i, ret; + int i, j, ret; unsigned int order; hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops); @@ -3298,6 +3306,10 @@ struct ieee80211_hw *wl1271_alloc_hw(void) for (i = 0; i < NUM_TX_QUEUES; i++) skb_queue_head_init(&wl->tx_queue[i]); + for (i = 0; i < NUM_TX_QUEUES; i++) + for (j = 0; j < AP_MAX_LINKS; j++) + skb_queue_head_init(&wl->links[j].tx_queue[i]); + INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work); INIT_DELAYED_WORK(&wl->pspoll_work, wl1271_pspoll_work); INIT_WORK(&wl->irq_work, wl1271_irq_work); @@ -3323,6 +3335,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->bss_type = MAX_BSS_TYPE; wl->set_bss_type = MAX_BSS_TYPE; wl->fw_bss_type = MAX_BSS_TYPE; + wl->last_tx_hlid = 0; memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); for (i = 0; i < ACX_TX_DESCRIPTORS; i++) diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 0bb57daac889..8c769500ec5d 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -86,6 +86,27 @@ static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl, wl1271_acx_set_inconnection_sta(wl, hdr->addr1); } +u8 wl1271_tx_get_hlid(struct sk_buff *skb) +{ + struct ieee80211_tx_info *control = IEEE80211_SKB_CB(skb); + + if (control->control.sta) { + struct wl1271_station *wl_sta; + + wl_sta = (struct wl1271_station *) + control->control.sta->drv_priv; + return wl_sta->hlid; + } else { + struct ieee80211_hdr *hdr; + + hdr = (struct ieee80211_hdr *)skb->data; + if (ieee80211_is_mgmt(hdr->frame_control)) + return WL1271_AP_GLOBAL_HLID; + else + return WL1271_AP_BROADCAST_HLID; + } +} + static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, u32 buf_offset) { @@ -298,7 +319,7 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set) return enabled_rates; } -static void handle_tx_low_watermark(struct wl1271 *wl) +void wl1271_handle_tx_low_watermark(struct wl1271 *wl) { unsigned long flags; @@ -312,7 +333,7 @@ static void handle_tx_low_watermark(struct wl1271 *wl) } } -static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl) +static struct sk_buff *wl1271_sta_skb_dequeue(struct wl1271 *wl) { struct sk_buff *skb = NULL; unsigned long flags; @@ -338,12 +359,69 @@ out: return skb; } +static struct sk_buff *wl1271_ap_skb_dequeue(struct wl1271 *wl) +{ + struct sk_buff *skb = NULL; + unsigned long flags; + int i, h, start_hlid; + + /* start from the link after the last one */ + start_hlid = (wl->last_tx_hlid + 1) % AP_MAX_LINKS; + + /* dequeue according to AC, round robin on each link */ + for (i = 0; i < AP_MAX_LINKS; i++) { + h = (start_hlid + i) % AP_MAX_LINKS; + + skb = skb_dequeue(&wl->links[h].tx_queue[CONF_TX_AC_VO]); + if (skb) + goto out; + skb = skb_dequeue(&wl->links[h].tx_queue[CONF_TX_AC_VI]); + if (skb) + goto out; + skb = skb_dequeue(&wl->links[h].tx_queue[CONF_TX_AC_BE]); + if (skb) + goto out; + skb = skb_dequeue(&wl->links[h].tx_queue[CONF_TX_AC_BK]); + if (skb) + goto out; + } + +out: + if (skb) { + wl->last_tx_hlid = h; + spin_lock_irqsave(&wl->wl_lock, flags); + wl->tx_queue_count--; + spin_unlock_irqrestore(&wl->wl_lock, flags); + } else { + wl->last_tx_hlid = 0; + } + + return skb; +} + +static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl) +{ + if (wl->bss_type == BSS_TYPE_AP_BSS) + return wl1271_ap_skb_dequeue(wl); + + return wl1271_sta_skb_dequeue(wl); +} + static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb) { unsigned long flags; int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); - skb_queue_head(&wl->tx_queue[q], skb); + if (wl->bss_type == BSS_TYPE_AP_BSS) { + u8 hlid = wl1271_tx_get_hlid(skb); + skb_queue_head(&wl->links[hlid].tx_queue[q], skb); + + /* make sure we dequeue the same packet next time */ + wl->last_tx_hlid = (hlid + AP_MAX_LINKS - 1) % AP_MAX_LINKS; + } else { + skb_queue_head(&wl->tx_queue[q], skb); + } + spin_lock_irqsave(&wl->wl_lock, flags); wl->tx_queue_count++; spin_unlock_irqrestore(&wl->wl_lock, flags); @@ -406,7 +484,7 @@ out_ack: if (sent_packets) { /* interrupt the firmware with the new packets */ wl1271_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count); - handle_tx_low_watermark(wl); + wl1271_handle_tx_low_watermark(wl); } out: @@ -523,6 +601,27 @@ void wl1271_tx_complete(struct wl1271 *wl) } } +void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid) +{ + struct sk_buff *skb; + int i, total = 0; + unsigned long flags; + + for (i = 0; i < NUM_TX_QUEUES; i++) { + while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) { + wl1271_debug(DEBUG_TX, "link freeing skb 0x%p", skb); + ieee80211_tx_status(wl->hw, skb); + total++; + } + } + + spin_lock_irqsave(&wl->wl_lock, flags); + wl->tx_queue_count -= total; + spin_unlock_irqrestore(&wl->wl_lock, flags); + + wl1271_handle_tx_low_watermark(wl); +} + /* caller must hold wl->mutex */ void wl1271_tx_reset(struct wl1271 *wl) { @@ -530,19 +629,28 @@ void wl1271_tx_reset(struct wl1271 *wl) struct sk_buff *skb; /* TX failure */ - for (i = 0; i < NUM_TX_QUEUES; i++) { - while ((skb = skb_dequeue(&wl->tx_queue[i]))) { - wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb); - ieee80211_tx_status(wl->hw, skb); + if (wl->bss_type == BSS_TYPE_AP_BSS) { + for (i = 0; i < AP_MAX_LINKS; i++) + wl1271_tx_reset_link_queues(wl, i); + + wl->last_tx_hlid = 0; + } else { + for (i = 0; i < NUM_TX_QUEUES; i++) { + while ((skb = skb_dequeue(&wl->tx_queue[i]))) { + wl1271_debug(DEBUG_TX, "freeing skb 0x%p", + skb); + ieee80211_tx_status(wl->hw, skb); + } } } + wl->tx_queue_count = 0; /* * Make sure the driver is at a consistent state, in case this * function is called from a context other than interface removal. */ - handle_tx_low_watermark(wl); + wl1271_handle_tx_low_watermark(wl); for (i = 0; i < ACX_TX_DESCRIPTORS; i++) if (wl->tx_frames[i] != NULL) { @@ -563,8 +671,8 @@ void wl1271_tx_flush(struct wl1271 *wl) while (!time_after(jiffies, timeout)) { mutex_lock(&wl->mutex); - wl1271_debug(DEBUG_TX, "flushing tx buffer: %d", - wl->tx_frames_cnt); + wl1271_debug(DEBUG_TX, "flushing tx buffer: %d %d", + wl->tx_frames_cnt, wl->tx_queue_count); if ((wl->tx_frames_cnt == 0) && (wl->tx_queue_count == 0)) { mutex_unlock(&wl->mutex); return; diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h index db88f58707a3..02f07fa66e82 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/tx.h @@ -150,5 +150,8 @@ void wl1271_tx_flush(struct wl1271 *wl); u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band); u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set); u32 wl1271_tx_min_rate_get(struct wl1271 *wl); +u8 wl1271_tx_get_hlid(struct sk_buff *skb); +void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid); +void wl1271_handle_tx_low_watermark(struct wl1271 *wl); #endif diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 1d6c94304b1a..9ffac80d3988 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -319,6 +319,11 @@ enum wl12xx_flags { WL1271_FLAG_AP_STARTED }; +struct wl1271_link { + /* AP-mode - TX queue per AC in link */ + struct sk_buff_head tx_queue[NUM_TX_QUEUES]; +}; + struct wl1271 { struct platform_device *plat_dev; struct ieee80211_hw *hw; @@ -498,6 +503,15 @@ struct wl1271 { /* RX BA constraint value */ bool ba_support; u8 ba_rx_bitmap; + + /* + * AP-mode - links indexed by HLID. The global and broadcast links + * are always active. + */ + struct wl1271_link links[AP_MAX_LINKS]; + + /* the hlid of the link where the last transmitted skb came from */ + int last_tx_hlid; }; struct wl1271_station { -- cgit v1.2.3 From 1d36cd892c130a5a781acb282e083b94127f1c50 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 23 Feb 2011 00:22:27 +0200 Subject: wl12xx: report invalid TX rate when returning non-TX-ed skbs Report a TX rate idx of -1 and count 0 when returning untransmitted skbs to mac80211 using ieee80211_tx_status(). Otherwise mac80211 tries to use the returned (essentially garbage) status. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/tx.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 8c769500ec5d..de60b4bc4a72 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -606,10 +606,14 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid) struct sk_buff *skb; int i, total = 0; unsigned long flags; + struct ieee80211_tx_info *info; for (i = 0; i < NUM_TX_QUEUES; i++) { while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) { wl1271_debug(DEBUG_TX, "link freeing skb 0x%p", skb); + info = IEEE80211_SKB_CB(skb); + info->status.rates[0].idx = -1; + info->status.rates[0].count = 0; ieee80211_tx_status(wl->hw, skb); total++; } @@ -627,6 +631,7 @@ void wl1271_tx_reset(struct wl1271 *wl) { int i; struct sk_buff *skb; + struct ieee80211_tx_info *info; /* TX failure */ if (wl->bss_type == BSS_TYPE_AP_BSS) { @@ -639,6 +644,9 @@ void wl1271_tx_reset(struct wl1271 *wl) while ((skb = skb_dequeue(&wl->tx_queue[i]))) { wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb); + info = IEEE80211_SKB_CB(skb); + info->status.rates[0].idx = -1; + info->status.rates[0].count = 0; ieee80211_tx_status(wl->hw, skb); } } @@ -657,6 +665,9 @@ void wl1271_tx_reset(struct wl1271 *wl) skb = wl->tx_frames[i]; wl1271_free_tx_id(wl, i); wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb); + info = IEEE80211_SKB_CB(skb); + info->status.rates[0].idx = -1; + info->status.rates[0].count = 0; ieee80211_tx_status(wl->hw, skb); } } -- cgit v1.2.3 From ba7c082a139178da239a65e6e6cc6bd1c8515d97 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 23 Feb 2011 00:22:28 +0200 Subject: wl12xx: AP-mode - support HW based link PS monitoring When operating in AP mode the wl1271 hardware filters out null-data packets as well as management packets. This makes it impossible for mac80211 to monitor the PS mode by using the PM bit of incoming frames. Disable mac80211 automatic link PS-mode handling by supporting IEEE80211_HW_AP_LINK_PS in HW flags. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 3a4f6069be60..f336e9cbd9e0 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -3226,7 +3226,8 @@ int wl1271_init_ieee80211(struct wl1271 *wl) IEEE80211_HW_HAS_RATE_CONTROL | IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_SUPPORTS_CQM_RSSI | - IEEE80211_HW_REPORTS_TX_ACK_STATUS; + IEEE80211_HW_REPORTS_TX_ACK_STATUS | + IEEE80211_HW_AP_LINK_PS; wl->hw->wiphy->cipher_suites = cipher_suites; wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); -- cgit v1.2.3 From 409622ecc2a3b618b31b1894ed6360fbdca95d62 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 23 Feb 2011 00:22:29 +0200 Subject: wl12xx: AP mode - fix bug in cleanup of wl1271_op_sta_add() Remove an active hlid when chip wakeup fails. In addition rename the involved functions. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index f336e9cbd9e0..92f822043331 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2624,7 +2624,7 @@ static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx, return 0; } -static int wl1271_allocate_hlid(struct wl1271 *wl, +static int wl1271_allocate_sta(struct wl1271 *wl, struct ieee80211_sta *sta, u8 *hlid) { @@ -2645,10 +2645,13 @@ static int wl1271_allocate_hlid(struct wl1271 *wl, return 0; } -static void wl1271_free_hlid(struct wl1271 *wl, u8 hlid) +static void wl1271_free_sta(struct wl1271 *wl, u8 hlid) { int id = hlid - WL1271_AP_STA_HLID_START; + if (WARN_ON(!test_bit(id, wl->ap_hlid_map))) + return; + __clear_bit(id, wl->ap_hlid_map); wl1271_tx_reset_link_queues(wl, hlid); } @@ -2671,13 +2674,13 @@ static int wl1271_op_sta_add(struct ieee80211_hw *hw, wl1271_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid); - ret = wl1271_allocate_hlid(wl, sta, &hlid); + ret = wl1271_allocate_sta(wl, sta, &hlid); if (ret < 0) goto out; ret = wl1271_ps_elp_wakeup(wl, false); if (ret < 0) - goto out; + goto out_free_sta; ret = wl1271_cmd_add_sta(wl, sta, hlid); if (ret < 0) @@ -2686,6 +2689,10 @@ static int wl1271_op_sta_add(struct ieee80211_hw *hw, out_sleep: wl1271_ps_elp_sleep(wl); +out_free_sta: + if (ret < 0) + wl1271_free_sta(wl, hlid); + out: mutex_unlock(&wl->mutex); return ret; @@ -2722,7 +2729,7 @@ static int wl1271_op_sta_remove(struct ieee80211_hw *hw, if (ret < 0) goto out_sleep; - wl1271_free_hlid(wl, wl_sta->hlid); + wl1271_free_sta(wl, wl_sta->hlid); out_sleep: wl1271_ps_elp_sleep(wl); -- cgit v1.2.3 From 09039f42a24084c10e7761ab28ef22932c62a46f Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 23 Feb 2011 00:22:30 +0200 Subject: wl12xx: AP-mode - count free FW TX blocks per link Count the number of FW TX blocks allocated per link. We add blocks to a link counter when allocated for a TX descriptor. We remove blocks according to counters in fw_status indicating the number of freed blocks in FW. These counters are polled after each IRQ. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 11 ++++++++ drivers/net/wireless/wl12xx/ps.c | 2 -- drivers/net/wireless/wl12xx/tx.c | 53 ++++++++++++++++++++---------------- drivers/net/wireless/wl12xx/wl12xx.h | 4 +++ 4 files changed, 44 insertions(+), 26 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 92f822043331..5772a33d79ec 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -574,6 +574,17 @@ static void wl1271_fw_status(struct wl1271 *wl, if (total) clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags); + if (wl->bss_type == BSS_TYPE_AP_BSS) { + for (i = 0; i < AP_MAX_LINKS; i++) { + u8 cnt = status->tx_lnk_free_blks[i] - + wl->links[i].prev_freed_blks; + + wl->links[i].prev_freed_blks = + status->tx_lnk_free_blks[i]; + wl->links[i].allocated_blks -= cnt; + } + } + /* update the host-chipset time offset */ getnstimeofday(&ts); wl->time_offset = (timespec_to_ns(&ts) >> 10) - diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index 2d3086ae6338..b7b3139e00fb 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/ps.c @@ -172,5 +172,3 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, return ret; } - - diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index de60b4bc4a72..ea1382bd38f4 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -108,7 +108,7 @@ u8 wl1271_tx_get_hlid(struct sk_buff *skb) } static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, - u32 buf_offset) + u32 buf_offset, u8 hlid) { struct wl1271_tx_hw_descr *desc; u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra; @@ -137,6 +137,9 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, wl->tx_blocks_available -= total_blocks; + if (wl->bss_type == BSS_TYPE_AP_BSS) + wl->links[hlid].allocated_blks += total_blocks; + ret = 0; wl1271_debug(DEBUG_TX, @@ -150,7 +153,8 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, } static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, - u32 extra, struct ieee80211_tx_info *control) + u32 extra, struct ieee80211_tx_info *control, + u8 hlid) { struct timespec ts; struct wl1271_tx_hw_descr *desc; @@ -186,7 +190,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, desc->tid = ac; if (wl->bss_type != BSS_TYPE_AP_BSS) { - desc->aid = TX_HW_DEFAULT_AID; + desc->aid = hlid; /* if the packets are destined for AP (have a STA entry) send them with AP rate policies, otherwise use default @@ -196,25 +200,17 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, else rate_idx = ACX_TX_BASIC_RATE; } else { - if (control->control.sta) { - struct wl1271_station *wl_sta; - - wl_sta = (struct wl1271_station *) - control->control.sta->drv_priv; - desc->hlid = wl_sta->hlid; + desc->hlid = hlid; + switch (hlid) { + case WL1271_AP_GLOBAL_HLID: + rate_idx = ACX_TX_AP_MODE_MGMT_RATE; + break; + case WL1271_AP_BROADCAST_HLID: + rate_idx = ACX_TX_AP_MODE_BCST_RATE; + break; + default: rate_idx = ac; - } else { - struct ieee80211_hdr *hdr; - - hdr = (struct ieee80211_hdr *) - (skb->data + sizeof(*desc)); - if (ieee80211_is_mgmt(hdr->frame_control)) { - desc->hlid = WL1271_AP_GLOBAL_HLID; - rate_idx = ACX_TX_AP_MODE_MGMT_RATE; - } else { - desc->hlid = WL1271_AP_BROADCAST_HLID; - rate_idx = ACX_TX_AP_MODE_BCST_RATE; - } + break; } } @@ -245,6 +241,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, u32 extra = 0; int ret = 0; u32 total_len; + u8 hlid; if (!skb) return -EINVAL; @@ -271,14 +268,19 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, } } - ret = wl1271_tx_allocate(wl, skb, extra, buf_offset); + if (wl->bss_type == BSS_TYPE_AP_BSS) + hlid = wl1271_tx_get_hlid(skb); + else + hlid = TX_HW_DEFAULT_AID; + + ret = wl1271_tx_allocate(wl, skb, extra, buf_offset, hlid); if (ret < 0) return ret; if (wl->bss_type == BSS_TYPE_AP_BSS) wl1271_tx_ap_update_inconnection_sta(wl, skb); - wl1271_tx_fill_hdr(wl, skb, extra, info); + wl1271_tx_fill_hdr(wl, skb, extra, info, hlid); /* * The length of each packet is stored in terms of words. Thus, we must @@ -635,8 +637,11 @@ void wl1271_tx_reset(struct wl1271 *wl) /* TX failure */ if (wl->bss_type == BSS_TYPE_AP_BSS) { - for (i = 0; i < AP_MAX_LINKS; i++) + for (i = 0; i < AP_MAX_LINKS; i++) { wl1271_tx_reset_link_queues(wl, i); + wl->links[i].allocated_blks = 0; + wl->links[i].prev_freed_blks = 0; + } wl->last_tx_hlid = 0; } else { diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 9ffac80d3988..0e00c5be99d3 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -322,6 +322,10 @@ enum wl12xx_flags { struct wl1271_link { /* AP-mode - TX queue per AC in link */ struct sk_buff_head tx_queue[NUM_TX_QUEUES]; + + /* accounting for allocated / available TX blocks in FW */ + u8 allocated_blks; + u8 prev_freed_blks; }; struct wl1271 { -- cgit v1.2.3 From b622d992c21a85ce590afe2c18977ed28b457e0e Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 23 Feb 2011 00:22:31 +0200 Subject: wl12xx: AP-mode - management of links in PS-mode Update the PS mode of each link according to a bitmap polled from fw_status. Manually notify mac80211 about PS mode changes in connected stations. mac80211 will only be notified about PS start when the station is in PS and there is a small number of TX blocks from this link ready in HW. This is required for waking up the remote station since the TIM is updated entirely by FW. When a station enters mac80211-PS-mode, we drop all the skbs in the low-level TX queues belonging to this sta with STAT_TX_FILTERED to keep our queues clean. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 73 +++++++++++++++++++++++++++----- drivers/net/wireless/wl12xx/ps.c | 80 ++++++++++++++++++++++++++++++++++++ drivers/net/wireless/wl12xx/ps.h | 2 + drivers/net/wireless/wl12xx/tx.c | 24 ++++++++++- drivers/net/wireless/wl12xx/wl12xx.h | 19 +++++++++ 5 files changed, 186 insertions(+), 12 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 5772a33d79ec..95aa19ae84e5 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -537,6 +537,57 @@ static int wl1271_plt_init(struct wl1271 *wl) return ret; } +static void wl1271_irq_ps_regulate_link(struct wl1271 *wl, u8 hlid, u8 tx_blks) +{ + bool fw_ps; + + /* only regulate station links */ + if (hlid < WL1271_AP_STA_HLID_START) + return; + + fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); + + /* + * Wake up from high level PS if the STA is asleep with too little + * blocks in FW or if the STA is awake. + */ + if (!fw_ps || tx_blks < WL1271_PS_STA_MAX_BLOCKS) + wl1271_ps_link_end(wl, hlid); + + /* Start high-level PS if the STA is asleep with enough blocks in FW */ + else if (fw_ps && tx_blks >= WL1271_PS_STA_MAX_BLOCKS) + wl1271_ps_link_start(wl, hlid, true); +} + +static void wl1271_irq_update_links_status(struct wl1271 *wl, + struct wl1271_fw_ap_status *status) +{ + u32 cur_fw_ps_map; + u8 hlid; + + cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap); + if (wl->ap_fw_ps_map != cur_fw_ps_map) { + wl1271_debug(DEBUG_PSM, + "link ps prev 0x%x cur 0x%x changed 0x%x", + wl->ap_fw_ps_map, cur_fw_ps_map, + wl->ap_fw_ps_map ^ cur_fw_ps_map); + + wl->ap_fw_ps_map = cur_fw_ps_map; + } + + for (hlid = WL1271_AP_STA_HLID_START; hlid < AP_MAX_LINKS; hlid++) { + u8 cnt = status->tx_lnk_free_blks[hlid] - + wl->links[hlid].prev_freed_blks; + + wl->links[hlid].prev_freed_blks = + status->tx_lnk_free_blks[hlid]; + wl->links[hlid].allocated_blks -= cnt; + + wl1271_irq_ps_regulate_link(wl, hlid, + wl->links[hlid].allocated_blks); + } +} + static void wl1271_fw_status(struct wl1271 *wl, struct wl1271_fw_full_status *full_status) { @@ -574,16 +625,9 @@ static void wl1271_fw_status(struct wl1271 *wl, if (total) clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags); - if (wl->bss_type == BSS_TYPE_AP_BSS) { - for (i = 0; i < AP_MAX_LINKS; i++) { - u8 cnt = status->tx_lnk_free_blks[i] - - wl->links[i].prev_freed_blks; - - wl->links[i].prev_freed_blks = - status->tx_lnk_free_blks[i]; - wl->links[i].allocated_blks -= cnt; - } - } + /* for AP update num of allocated TX blocks per link and ps status */ + if (wl->bss_type == BSS_TYPE_AP_BSS) + wl1271_irq_update_links_status(wl, &full_status->ap); /* update the host-chipset time offset */ getnstimeofday(&ts); @@ -1241,6 +1285,8 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) wl->filters = 0; wl1271_free_ap_keys(wl); memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map)); + wl->ap_fw_ps_map = 0; + wl->ap_ps_map = 0; for (i = 0; i < NUM_TX_QUEUES; i++) wl->tx_blocks_freed[i] = 0; @@ -2649,10 +2695,10 @@ static int wl1271_allocate_sta(struct wl1271 *wl, } wl_sta = (struct wl1271_station *)sta->drv_priv; - __set_bit(id, wl->ap_hlid_map); wl_sta->hlid = WL1271_AP_STA_HLID_START + id; *hlid = wl_sta->hlid; + memcpy(wl->links[wl_sta->hlid].addr, sta->addr, ETH_ALEN); return 0; } @@ -2664,7 +2710,10 @@ static void wl1271_free_sta(struct wl1271 *wl, u8 hlid) return; __clear_bit(id, wl->ap_hlid_map); + memset(wl->links[hlid].addr, 0, ETH_ALEN); wl1271_tx_reset_link_queues(wl, hlid); + __clear_bit(hlid, &wl->ap_ps_map); + __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); } static int wl1271_op_sta_add(struct ieee80211_hw *hw, @@ -3355,6 +3404,8 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->set_bss_type = MAX_BSS_TYPE; wl->fw_bss_type = MAX_BSS_TYPE; wl->last_tx_hlid = 0; + wl->ap_ps_map = 0; + wl->ap_fw_ps_map = 0; memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); for (i = 0; i < ACX_TX_DESCRIPTORS; i++) diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index b7b3139e00fb..5c347b1bd17f 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/ps.c @@ -24,6 +24,7 @@ #include "reg.h" #include "ps.h" #include "io.h" +#include "tx.h" #define WL1271_WAKEUP_TIMEOUT 500 @@ -172,3 +173,82 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, return ret; } + +static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid) +{ + int i, filtered = 0; + struct sk_buff *skb; + struct ieee80211_tx_info *info; + unsigned long flags; + + /* filter all frames currently the low level queus for this hlid */ + for (i = 0; i < NUM_TX_QUEUES; i++) { + while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) { + info = IEEE80211_SKB_CB(skb); + info->flags |= IEEE80211_TX_STAT_TX_FILTERED; + info->status.rates[0].idx = -1; + ieee80211_tx_status(wl->hw, skb); + filtered++; + } + } + + spin_lock_irqsave(&wl->wl_lock, flags); + wl->tx_queue_count -= filtered; + spin_unlock_irqrestore(&wl->wl_lock, flags); + + wl1271_handle_tx_low_watermark(wl); +} + +void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues) +{ + struct ieee80211_sta *sta; + + if (test_bit(hlid, &wl->ap_ps_map)) + return; + + wl1271_debug(DEBUG_PSM, "start mac80211 PSM on hlid %d blks %d " + "clean_queues %d", hlid, wl->links[hlid].allocated_blks, + clean_queues); + + rcu_read_lock(); + sta = ieee80211_find_sta(wl->vif, wl->links[hlid].addr); + if (!sta) { + wl1271_error("could not find sta %pM for starting ps", + wl->links[hlid].addr); + rcu_read_unlock(); + return; + } + + ieee80211_sta_ps_transition_ni(sta, true); + rcu_read_unlock(); + + /* do we want to filter all frames from this link's queues? */ + if (clean_queues) + wl1271_ps_filter_frames(wl, hlid); + + __set_bit(hlid, &wl->ap_ps_map); +} + +void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid) +{ + struct ieee80211_sta *sta; + + if (!test_bit(hlid, &wl->ap_ps_map)) + return; + + wl1271_debug(DEBUG_PSM, "end mac80211 PSM on hlid %d", hlid); + + __clear_bit(hlid, &wl->ap_ps_map); + + rcu_read_lock(); + sta = ieee80211_find_sta(wl->vif, wl->links[hlid].addr); + if (!sta) { + wl1271_error("could not find sta %pM for ending ps", + wl->links[hlid].addr); + goto end; + } + + ieee80211_sta_ps_transition_ni(sta, false); +end: + rcu_read_unlock(); +} diff --git a/drivers/net/wireless/wl12xx/ps.h b/drivers/net/wireless/wl12xx/ps.h index 8415060f08e5..fc1f4c193593 100644 --- a/drivers/net/wireless/wl12xx/ps.h +++ b/drivers/net/wireless/wl12xx/ps.h @@ -32,5 +32,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, void wl1271_ps_elp_sleep(struct wl1271 *wl); int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake); void wl1271_elp_work(struct work_struct *work); +void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues); +void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid); #endif /* __WL1271_PS_H__ */ diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index ea1382bd38f4..ac60d577319f 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -86,6 +86,26 @@ static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl, wl1271_acx_set_inconnection_sta(wl, hdr->addr1); } +static void wl1271_tx_regulate_link(struct wl1271 *wl, u8 hlid) +{ + bool fw_ps; + u8 tx_blks; + + /* only regulate station links */ + if (hlid < WL1271_AP_STA_HLID_START) + return; + + fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); + tx_blks = wl->links[hlid].allocated_blks; + + /* + * if in FW PS and there is enough data in FW we can put the link + * into high-level PS and clean out its TX queues. + */ + if (fw_ps && tx_blks >= WL1271_PS_STA_MAX_BLOCKS) + wl1271_ps_link_start(wl, hlid, true); +} + u8 wl1271_tx_get_hlid(struct sk_buff *skb) { struct ieee80211_tx_info *control = IEEE80211_SKB_CB(skb); @@ -277,8 +297,10 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, if (ret < 0) return ret; - if (wl->bss_type == BSS_TYPE_AP_BSS) + if (wl->bss_type == BSS_TYPE_AP_BSS) { wl1271_tx_ap_update_inconnection_sta(wl, skb); + wl1271_tx_regulate_link(wl, hlid); + } wl1271_tx_fill_hdr(wl, skb, extra, info, hlid); diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 0e00c5be99d3..338acc9f60b3 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -153,6 +153,17 @@ extern u32 wl12xx_debug_level; #define WL1271_AP_BROADCAST_HLID 1 #define WL1271_AP_STA_HLID_START 2 +/* + * When in AP-mode, we allow (at least) this number of mem-blocks + * to be transmitted to FW for a STA in PS-mode. Only when packets are + * present in the FW buffers it will wake the sleeping STA. We want to put + * enough packets for the driver to transmit all of its buffered data before + * the STA goes to sleep again. But we don't want to take too much mem-blocks + * as it might hurt the throughput of active STAs. + * The number of blocks (18) is enough for 2 large packets. + */ +#define WL1271_PS_STA_MAX_BLOCKS (2 * 9) + #define WL1271_AP_BSS_INDEX 0 #define WL1271_AP_DEF_INACTIV_SEC 300 #define WL1271_AP_DEF_BEACON_EXP 20 @@ -326,6 +337,8 @@ struct wl1271_link { /* accounting for allocated / available TX blocks in FW */ u8 allocated_blks; u8 prev_freed_blks; + + u8 addr[ETH_ALEN]; }; struct wl1271 { @@ -516,6 +529,12 @@ struct wl1271 { /* the hlid of the link where the last transmitted skb came from */ int last_tx_hlid; + + /* AP-mode - a bitmap of links currently in PS mode according to FW */ + u32 ap_fw_ps_map; + + /* AP-mode - a bitmap of links currently in PS mode in mac80211 */ + unsigned long ap_ps_map; }; struct wl1271_station { -- cgit v1.2.3 From 7bb4568372856688bc070917265bce0b88bb7d4d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 24 Feb 2011 14:42:06 +0100 Subject: mac80211: make tx() operation return void The return value of the tx operation is commonly misused by drivers, leading to errors. All drivers will drop frames if they fail to TX the frame, and they must also properly manage the queues (if they didn't, mac80211 would already warn). Removing the ability for drivers to return a BUSY value also allows significant cleanups of the TX TX handling code in mac80211. Note that this also fixes a bug in ath9k_htc, the old "return -1" there was wrong. Signed-off-by: Johannes Berg Tested-by: Sedat Dilek [ath5k] Acked-by: Gertjan van Wingerde [rt2x00] Acked-by: Larry Finger [b43, rtl8187, rtlwifi] Acked-by: Luciano Coelho [wl12xx] Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/main.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 95aa19ae84e5..947491a1d9cc 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1034,7 +1034,7 @@ int wl1271_plt_stop(struct wl1271 *wl) return ret; } -static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct wl1271 *wl = hw->priv; unsigned long flags; @@ -1073,8 +1073,6 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags)) ieee80211_queue_work(wl->hw, &wl->tx_work); - - return NETDEV_TX_OK; } static struct notifier_block wl1271_dev_notifier = { -- cgit v1.2.3 From f62c317c1f7f67c249ee9254e55a02fad0d0f86b Mon Sep 17 00:00:00 2001 From: Sebastien Jan Date: Wed, 23 Feb 2011 14:25:16 +0100 Subject: wl12xx: fix the path to the wl12xx firmwares In the linux-firmware git tree, the firmwares and the NVS are inside the ti-connectivity directory. Fix the filenames that the driver looks for accordingly. [Fixed commit message and merged with the latest changes. -- Luca] Signed-off-by: Sebastien Jan Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/wl12xx.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 338acc9f60b3..7132bc7dd2cf 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -130,10 +130,10 @@ extern u32 wl12xx_debug_level; -#define WL1271_FW_NAME "wl1271-fw-2.bin" -#define WL1271_AP_FW_NAME "wl1271-fw-ap.bin" +#define WL1271_FW_NAME "ti-connectivity/wl1271-fw-2.bin" +#define WL1271_AP_FW_NAME "ti-connectivity/wl1271-fw-ap.bin" -#define WL1271_NVS_NAME "wl1271-nvs.bin" +#define WL1271_NVS_NAME "ti-connectivity/wl1271-nvs.bin" #define WL1271_TX_SECURITY_LO16(s) ((u16)((s) & 0xffff)) #define WL1271_TX_SECURITY_HI32(s) ((u32)(((s) >> 16) & 0xffffffff)) -- cgit v1.2.3 From 11251e7e5c7c5411d1f77dbc7f9bfa2c23626749 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Mon, 28 Feb 2011 00:13:58 +0200 Subject: wl12xx: Don't rely on runtime PM for toggling power Runtime PM might not always be enabled. Even if it is enabled in the running kernel, it can still be temporarily disabled, for instance during suspend. Runtime PM is opportunistic in nature, and should not be relied on for toggling power. In case the interface is removed and re-added while runtime PM is disabled, the FW will fail to boot, as it is mandatory to toggle power between boots. For instance, this can happen during suspend in case one of the devices fails to suspend before the MMC host suspends, but after mac80211 was suspended. The interface will be removed and reactivated without toggling the power. Fix this by calling mmc_power_save_host/mmc_power_restore_host in wl1271_sdio_power_on/off functions. It will toggle the power to the chip even if runtime PM is disabled. The runtime PM functions should still be called to make sure runtime PM does not opportunistically power the chip off (e.g. after resuming from system suspend). Signed-off-by: Ido Yariv Signed-off-by: Ohad Ben-Cohen Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/sdio.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index d5e874825069..f27e91502631 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -163,11 +164,16 @@ static int wl1271_sdio_power_on(struct wl1271 *wl) struct sdio_func *func = wl_to_func(wl); int ret; - /* Power up the card */ + /* Make sure the card will not be powered off by runtime PM */ ret = pm_runtime_get_sync(&func->dev); if (ret < 0) goto out; + /* Runtime PM might be disabled, so power up the card manually */ + ret = mmc_power_restore_host(func->card->host); + if (ret < 0) + goto out; + sdio_claim_host(func); sdio_enable_func(func); sdio_release_host(func); @@ -179,12 +185,18 @@ out: static int wl1271_sdio_power_off(struct wl1271 *wl) { struct sdio_func *func = wl_to_func(wl); + int ret; sdio_claim_host(func); sdio_disable_func(func); sdio_release_host(func); - /* Power down the card */ + /* Runtime PM might be disabled, so power off the card manually */ + ret = mmc_power_save_host(func->card->host); + if (ret < 0) + return ret; + + /* Let runtime PM know the card is powered off */ return pm_runtime_put_sync(&func->dev); } -- cgit v1.2.3 From 50e9f746f63c9b881f2ca4a35dbdfd34b1a8a215 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Mon, 28 Feb 2011 00:16:13 +0200 Subject: wl12xx: Remove private headers in wl1271_tx_reset Frames in the tx_frames array include extra private headers, which must be removed before passing the skbs to ieee80211_tx_status. Fix this by removing any private headers in wl1271_tx_reset, similar to how this is done in wl1271_tx_complete_packet. Signed-off-by: Ido Yariv Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/tx.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index ac60d577319f..37d354ddd58e 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -687,16 +687,30 @@ void wl1271_tx_reset(struct wl1271 *wl) */ wl1271_handle_tx_low_watermark(wl); - for (i = 0; i < ACX_TX_DESCRIPTORS; i++) - if (wl->tx_frames[i] != NULL) { - skb = wl->tx_frames[i]; - wl1271_free_tx_id(wl, i); - wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb); - info = IEEE80211_SKB_CB(skb); - info->status.rates[0].idx = -1; - info->status.rates[0].count = 0; - ieee80211_tx_status(wl->hw, skb); + for (i = 0; i < ACX_TX_DESCRIPTORS; i++) { + if (wl->tx_frames[i] == NULL) + continue; + + skb = wl->tx_frames[i]; + wl1271_free_tx_id(wl, i); + wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb); + + /* Remove private headers before passing the skb to mac80211 */ + info = IEEE80211_SKB_CB(skb); + skb_pull(skb, sizeof(struct wl1271_tx_hw_descr)); + if (info->control.hw_key && + info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { + int hdrlen = ieee80211_get_hdrlen_from_skb(skb); + memmove(skb->data + WL1271_TKIP_IV_SPACE, skb->data, + hdrlen); + skb_pull(skb, WL1271_TKIP_IV_SPACE); } + + info->status.rates[0].idx = -1; + info->status.rates[0].count = 0; + + ieee80211_tx_status(wl->hw, skb); + } } #define WL1271_TX_FLUSH_TIMEOUT 500000 -- cgit v1.2.3 From 8aad24642a7c06832a75f1d20e8e3112b4fbd815 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Tue, 1 Mar 2011 15:14:38 +0200 Subject: wl12xx: Reorder data handling in irq_work The FW has a limited amount of memory for holding frames. In case it runs out of memory reserved for RX frames, it'll have no other choice but to drop packets received from the AP. Thus, it is important to handle RX data interrupts as soon as possible, before handling anything else. In addition, since there are enough TX descriptors to go around, it is better to first send TX frames, and only then handle TX completions. Fix this by changing the order of function calls in wl1271_irq_work. Signed-off-by: Ido Yariv Signed-off-by: Ohad Ben-Cohen Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 947491a1d9cc..65e8a0cc92d0 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -685,10 +685,7 @@ static void wl1271_irq_work(struct work_struct *work) if (intr & WL1271_ACX_INTR_DATA) { wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA"); - /* check for tx results */ - if (wl->fw_status->common.tx_results_counter != - (wl->tx_results_count & 0xff)) - wl1271_tx_complete(wl); + wl1271_rx(wl, &wl->fw_status->common); /* Check if any tx blocks were freed */ if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) && @@ -700,7 +697,10 @@ static void wl1271_irq_work(struct work_struct *work) wl1271_tx_work_locked(wl); } - wl1271_rx(wl, &wl->fw_status->common); + /* check for tx results */ + if (wl->fw_status->common.tx_results_counter != + (wl->tx_results_count & 0xff)) + wl1271_tx_complete(wl); } if (intr & WL1271_ACX_INTR_EVENT_A) { -- cgit v1.2.3 From 606ea9fa0b2c01ffafb6beae92ea8e2b1473520b Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Tue, 1 Mar 2011 15:14:39 +0200 Subject: wl12xx: Do end-of-transactions transfers only if needed On newer hardware revisions, there is no need to write the host's counter at the end of a RX transaction. The same applies to writing the number of packets at the end of a TX transaction. It is generally a good idea to avoid unnecessary SDIO/SPI transfers. Throughput and CPU usage are improved when avoiding these. Send the host's RX counter and the TX packet count only if needed, based on the hardware revision. [Changed WL12XX_QUIRK_END_OF_TRANSACTION to use BIT(0) -- Luca] Signed-off-by: Ido Yariv Signed-off-by: Ohad Ben-Cohen Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/boot.c | 3 +++ drivers/net/wireless/wl12xx/boot.h | 5 +++++ drivers/net/wireless/wl12xx/main.c | 1 + drivers/net/wireless/wl12xx/rx.c | 8 +++++++- drivers/net/wireless/wl12xx/tx.c | 10 ++++++++-- drivers/net/wireless/wl12xx/wl12xx.h | 8 ++++++++ 6 files changed, 32 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index 1ffbad67d2d8..6934dffd5174 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -488,6 +488,9 @@ static void wl1271_boot_hw_version(struct wl1271 *wl) fuse = (fuse & PG_VER_MASK) >> PG_VER_OFFSET; wl->hw_pg_ver = (s8)fuse; + + if (((wl->hw_pg_ver & PG_MAJOR_VER_MASK) >> PG_MAJOR_VER_OFFSET) < 3) + wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION; } /* uploads NVS and firmware */ diff --git a/drivers/net/wireless/wl12xx/boot.h b/drivers/net/wireless/wl12xx/boot.h index d67dcffa31eb..17229b86fc71 100644 --- a/drivers/net/wireless/wl12xx/boot.h +++ b/drivers/net/wireless/wl12xx/boot.h @@ -59,6 +59,11 @@ struct wl1271_static_data { #define PG_VER_MASK 0x3c #define PG_VER_OFFSET 2 +#define PG_MAJOR_VER_MASK 0x3 +#define PG_MAJOR_VER_OFFSET 0x0 +#define PG_MINOR_VER_MASK 0xc +#define PG_MINOR_VER_OFFSET 0x2 + #define CMD_MBOX_ADDRESS 0x407B4 #define POLARITY_LOW BIT(1) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 65e8a0cc92d0..ba34ac3a440d 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -3404,6 +3404,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->last_tx_hlid = 0; wl->ap_ps_map = 0; wl->ap_fw_ps_map = 0; + wl->quirks = 0; memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); for (i = 0; i < ACX_TX_DESCRIPTORS; i++) diff --git a/drivers/net/wireless/wl12xx/rx.c b/drivers/net/wireless/wl12xx/rx.c index 3d13d7a83ea1..4e7a3b311321 100644 --- a/drivers/net/wireless/wl12xx/rx.c +++ b/drivers/net/wireless/wl12xx/rx.c @@ -198,7 +198,13 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status) pkt_offset += pkt_length; } } - wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter); + + /* + * Write the driver's packet counter to the FW. This is only required + * for older hardware revisions + */ + if (wl->quirks & WL12XX_QUIRK_END_OF_TRANSACTION) + wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter); } void wl1271_set_default_filters(struct wl1271 *wl) diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 37d354ddd58e..455954edf83e 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -506,8 +506,14 @@ out_ack: sent_packets = true; } if (sent_packets) { - /* interrupt the firmware with the new packets */ - wl1271_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count); + /* + * Interrupt the firmware with the new packets. This is only + * required for older hardware revisions + */ + if (wl->quirks & WL12XX_QUIRK_END_OF_TRANSACTION) + wl1271_write32(wl, WL1271_HOST_WR_ACCESS, + wl->tx_packets_count); + wl1271_handle_tx_low_watermark(wl); } diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 7132bc7dd2cf..ea1eee7895cf 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -535,6 +535,9 @@ struct wl1271 { /* AP-mode - a bitmap of links currently in PS mode in mac80211 */ unsigned long ap_ps_map; + + /* Quirks of specific hardware revisions */ + unsigned int quirks; }; struct wl1271_station { @@ -562,4 +565,9 @@ int wl1271_plt_stop(struct wl1271 *wl); #define HW_BG_RATES_MASK 0xffff #define HW_HT_RATES_OFFSET 16 +/* Quirks */ + +/* Each RX/TX transaction requires an end-of-transaction transfer */ +#define WL12XX_QUIRK_END_OF_TRANSACTION BIT(0) + #endif -- cgit v1.2.3 From 393fb560d328cc06e6a5c7b7473901ad724f82e7 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Tue, 1 Mar 2011 15:14:40 +0200 Subject: wl12xx: Change claiming of the SDIO bus The SDIO bus is claimed and released for each SDIO transaction. In addition to the few CPU cycles it takes to claim and release the bus, it may also cause undesired side effects such as the MMC host stopping its internal clocks. Since only the wl12xx_sdio driver drives this SDIO card, it is safe to claim the SDIO host once (on power on), and release it only when turning the power off. This patch was inspired by Juuso Oikarinen's (juuso.oikarinen@nokia.com) patch "wl12xx: Change claiming of the (SDIO) bus". Signed-off-by: Ido Yariv Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/sdio.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index f27e91502631..61fdc9e981bd 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -107,8 +107,6 @@ static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf, int ret; struct sdio_func *func = wl_to_func(wl); - sdio_claim_host(func); - if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) { ((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret); wl1271_debug(DEBUG_SDIO, "sdio read 52 addr 0x%x, byte 0x%02x", @@ -124,8 +122,6 @@ static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf, wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len); } - sdio_release_host(func); - if (ret) wl1271_error("sdio read failed (%d)", ret); } @@ -136,8 +132,6 @@ static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf, int ret; struct sdio_func *func = wl_to_func(wl); - sdio_claim_host(func); - if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) { sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret); wl1271_debug(DEBUG_SDIO, "sdio write 52 addr 0x%x, byte 0x%02x", @@ -153,8 +147,6 @@ static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf, ret = sdio_memcpy_toio(func, addr, buf, len); } - sdio_release_host(func); - if (ret) wl1271_error("sdio write failed (%d)", ret); } @@ -176,7 +168,6 @@ static int wl1271_sdio_power_on(struct wl1271 *wl) sdio_claim_host(func); sdio_enable_func(func); - sdio_release_host(func); out: return ret; @@ -187,7 +178,6 @@ static int wl1271_sdio_power_off(struct wl1271 *wl) struct sdio_func *func = wl_to_func(wl); int ret; - sdio_claim_host(func); sdio_disable_func(func); sdio_release_host(func); -- cgit v1.2.3 From a620865edf62ea2d024bbfe62162244473badfcb Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Tue, 1 Mar 2011 15:14:41 +0200 Subject: wl12xx: Switch to a threaded interrupt handler To achieve maximal throughput, it is very important to react to interrupts as soon as possible. Currently the interrupt handler wakes up a worker for handling interrupts in process context. A cleaner and more efficient design would be to request a threaded interrupt handler. This handler's priority is very high, and can do blocking operations such as SDIO/SPI transactions. Some work can be deferred, mostly calls to mac80211 APIs (ieee80211_rx_ni and ieee80211_tx_status). By deferring such work to a different worker, we can keep the irq handler thread more I/O responsive. In addition, on multi-core systems the two threads can be scheduled on different cores, which will improve overall performance. The use of WL1271_FLAG_IRQ_PENDING & WL1271_FLAG_IRQ_RUNNING was changed. For simplicity, always query the FW for more pending interrupts. Since there are relatively long bursts of interrupts, the extra FW status read overhead is negligible. In addition, this enables registering the IRQ handler with the ONESHOT option. Signed-off-by: Ido Yariv Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/debugfs.c | 2 +- drivers/net/wireless/wl12xx/io.h | 1 + drivers/net/wireless/wl12xx/main.c | 127 +++++++++++++++++++++------------- drivers/net/wireless/wl12xx/ps.c | 6 +- drivers/net/wireless/wl12xx/ps.h | 2 +- drivers/net/wireless/wl12xx/rx.c | 3 +- drivers/net/wireless/wl12xx/sdio.c | 16 ++--- drivers/net/wireless/wl12xx/spi.c | 19 ++--- drivers/net/wireless/wl12xx/tx.c | 5 +- drivers/net/wireless/wl12xx/wl12xx.h | 13 +++- 10 files changed, 113 insertions(+), 81 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index bebfa28a171a..8e75b09723b9 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -99,7 +99,7 @@ static void wl1271_debugfs_update_stats(struct wl1271 *wl) mutex_lock(&wl->mutex); - ret = wl1271_ps_elp_wakeup(wl, false); + ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; diff --git a/drivers/net/wireless/wl12xx/io.h b/drivers/net/wireless/wl12xx/io.h index 844b32b170bb..c1aac8292089 100644 --- a/drivers/net/wireless/wl12xx/io.h +++ b/drivers/net/wireless/wl12xx/io.h @@ -168,5 +168,6 @@ void wl1271_unregister_hw(struct wl1271 *wl); int wl1271_init_ieee80211(struct wl1271 *wl); struct ieee80211_hw *wl1271_alloc_hw(void); int wl1271_free_hw(struct wl1271 *wl); +irqreturn_t wl1271_irq(int irq, void *data); #endif diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index ba34ac3a440d..f408c5a84cc9 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -374,7 +374,7 @@ static int wl1271_dev_notify(struct notifier_block *me, unsigned long what, if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) goto out; - ret = wl1271_ps_elp_wakeup(wl, false); + ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; @@ -635,16 +635,39 @@ static void wl1271_fw_status(struct wl1271 *wl, (s64)le32_to_cpu(status->fw_localtime); } -#define WL1271_IRQ_MAX_LOOPS 10 +static void wl1271_flush_deferred_work(struct wl1271 *wl) +{ + struct sk_buff *skb; + + /* Pass all received frames to the network stack */ + while ((skb = skb_dequeue(&wl->deferred_rx_queue))) + ieee80211_rx_ni(wl->hw, skb); + + /* Return sent skbs to the network stack */ + while ((skb = skb_dequeue(&wl->deferred_tx_queue))) + ieee80211_tx_status(wl->hw, skb); +} + +static void wl1271_netstack_work(struct work_struct *work) +{ + struct wl1271 *wl = + container_of(work, struct wl1271, netstack_work); + + do { + wl1271_flush_deferred_work(wl); + } while (skb_queue_len(&wl->deferred_rx_queue)); +} -static void wl1271_irq_work(struct work_struct *work) +#define WL1271_IRQ_MAX_LOOPS 256 + +irqreturn_t wl1271_irq(int irq, void *cookie) { int ret; u32 intr; int loopcount = WL1271_IRQ_MAX_LOOPS; - unsigned long flags; - struct wl1271 *wl = - container_of(work, struct wl1271, irq_work); + struct wl1271 *wl = (struct wl1271 *)cookie; + bool done = false; + unsigned int defer_count; mutex_lock(&wl->mutex); @@ -653,26 +676,27 @@ static void wl1271_irq_work(struct work_struct *work) if (unlikely(wl->state == WL1271_STATE_OFF)) goto out; - ret = wl1271_ps_elp_wakeup(wl, true); + ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; - spin_lock_irqsave(&wl->wl_lock, flags); - while (test_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags) && loopcount) { - clear_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags); - spin_unlock_irqrestore(&wl->wl_lock, flags); - loopcount--; + while (!done && loopcount--) { + /* + * In order to avoid a race with the hardirq, clear the flag + * before acknowledging the chip. Since the mutex is held, + * wl1271_ps_elp_wakeup cannot be called concurrently. + */ + clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags); + smp_mb__after_clear_bit(); wl1271_fw_status(wl, wl->fw_status); intr = le32_to_cpu(wl->fw_status->common.intr); + intr &= WL1271_INTR_MASK; if (!intr) { - wl1271_debug(DEBUG_IRQ, "Zero interrupt received."); - spin_lock_irqsave(&wl->wl_lock, flags); + done = true; continue; } - intr &= WL1271_INTR_MASK; - if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) { wl1271_error("watchdog interrupt received! " "starting recovery."); @@ -682,7 +706,7 @@ static void wl1271_irq_work(struct work_struct *work) goto out; } - if (intr & WL1271_ACX_INTR_DATA) { + if (likely(intr & WL1271_ACX_INTR_DATA)) { wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA"); wl1271_rx(wl, &wl->fw_status->common); @@ -701,6 +725,12 @@ static void wl1271_irq_work(struct work_struct *work) if (wl->fw_status->common.tx_results_counter != (wl->tx_results_count & 0xff)) wl1271_tx_complete(wl); + + /* Make sure the deferred queues don't get too long */ + defer_count = skb_queue_len(&wl->deferred_tx_queue) + + skb_queue_len(&wl->deferred_rx_queue); + if (defer_count > WL1271_DEFERRED_QUEUE_LIMIT) + wl1271_flush_deferred_work(wl); } if (intr & WL1271_ACX_INTR_EVENT_A) { @@ -719,21 +749,16 @@ static void wl1271_irq_work(struct work_struct *work) if (intr & WL1271_ACX_INTR_HW_AVAILABLE) wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE"); - - spin_lock_irqsave(&wl->wl_lock, flags); } - if (test_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags)) - ieee80211_queue_work(wl->hw, &wl->irq_work); - else - clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags); - spin_unlock_irqrestore(&wl->wl_lock, flags); - wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); + + return IRQ_HANDLED; } +EXPORT_SYMBOL_GPL(wl1271_irq); static int wl1271_fetch_firmware(struct wl1271 *wl) { @@ -974,7 +999,6 @@ int wl1271_plt_start(struct wl1271 *wl) goto out; irq_disable: - wl1271_disable_interrupts(wl); mutex_unlock(&wl->mutex); /* Unlocking the mutex in the middle of handling is inherently unsafe. In this case we deem it safe to do, @@ -983,7 +1007,9 @@ irq_disable: work function will not do anything.) Also, any other possible concurrent operations will fail due to the current state, hence the wl1271 struct should be safe. */ - cancel_work_sync(&wl->irq_work); + wl1271_disable_interrupts(wl); + wl1271_flush_deferred_work(wl); + cancel_work_sync(&wl->netstack_work); mutex_lock(&wl->mutex); power_off: wl1271_power_off(wl); @@ -1010,14 +1036,15 @@ int __wl1271_plt_stop(struct wl1271 *wl) goto out; } - wl1271_disable_interrupts(wl); wl1271_power_off(wl); wl->state = WL1271_STATE_OFF; wl->rx_counter = 0; mutex_unlock(&wl->mutex); - cancel_work_sync(&wl->irq_work); + wl1271_disable_interrupts(wl); + wl1271_flush_deferred_work(wl); + cancel_work_sync(&wl->netstack_work); cancel_work_sync(&wl->recovery_work); mutex_lock(&wl->mutex); out: @@ -1169,7 +1196,6 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, break; irq_disable: - wl1271_disable_interrupts(wl); mutex_unlock(&wl->mutex); /* Unlocking the mutex in the middle of handling is inherently unsafe. In this case we deem it safe to do, @@ -1178,7 +1204,9 @@ irq_disable: work function will not do anything.) Also, any other possible concurrent operations will fail due to the current state, hence the wl1271 struct should be safe. */ - cancel_work_sync(&wl->irq_work); + wl1271_disable_interrupts(wl); + wl1271_flush_deferred_work(wl); + cancel_work_sync(&wl->netstack_work); mutex_lock(&wl->mutex); power_off: wl1271_power_off(wl); @@ -1244,12 +1272,12 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) wl->state = WL1271_STATE_OFF; - wl1271_disable_interrupts(wl); - mutex_unlock(&wl->mutex); + wl1271_disable_interrupts(wl); + wl1271_flush_deferred_work(wl); cancel_delayed_work_sync(&wl->scan_complete_work); - cancel_work_sync(&wl->irq_work); + cancel_work_sync(&wl->netstack_work); cancel_work_sync(&wl->tx_work); cancel_delayed_work_sync(&wl->pspoll_work); cancel_delayed_work_sync(&wl->elp_work); @@ -1525,7 +1553,7 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) is_ap = (wl->bss_type == BSS_TYPE_AP_BSS); - ret = wl1271_ps_elp_wakeup(wl, false); + ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; @@ -1681,7 +1709,7 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw, if (unlikely(wl->state == WL1271_STATE_OFF)) goto out; - ret = wl1271_ps_elp_wakeup(wl, false); + ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; @@ -1910,7 +1938,7 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, goto out_unlock; } - ret = wl1271_ps_elp_wakeup(wl, false); + ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out_unlock; @@ -2013,7 +2041,7 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw, goto out; } - ret = wl1271_ps_elp_wakeup(wl, false); + ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; @@ -2039,7 +2067,7 @@ static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value) goto out; } - ret = wl1271_ps_elp_wakeup(wl, false); + ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; @@ -2067,7 +2095,7 @@ static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value) goto out; } - ret = wl1271_ps_elp_wakeup(wl, false); + ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; @@ -2546,7 +2574,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, if (unlikely(wl->state == WL1271_STATE_OFF)) goto out; - ret = wl1271_ps_elp_wakeup(wl, false); + ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; @@ -2601,7 +2629,7 @@ static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue, conf_tid->apsd_conf[0] = 0; conf_tid->apsd_conf[1] = 0; } else { - ret = wl1271_ps_elp_wakeup(wl, false); + ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; @@ -2647,7 +2675,7 @@ static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw) if (unlikely(wl->state == WL1271_STATE_OFF)) goto out; - ret = wl1271_ps_elp_wakeup(wl, false); + ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; @@ -2736,7 +2764,7 @@ static int wl1271_op_sta_add(struct ieee80211_hw *hw, if (ret < 0) goto out; - ret = wl1271_ps_elp_wakeup(wl, false); + ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out_free_sta; @@ -2779,7 +2807,7 @@ static int wl1271_op_sta_remove(struct ieee80211_hw *hw, if (WARN_ON(!test_bit(id, wl->ap_hlid_map))) goto out; - ret = wl1271_ps_elp_wakeup(wl, false); + ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; @@ -2812,7 +2840,7 @@ int wl1271_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, goto out; } - ret = wl1271_ps_elp_wakeup(wl, false); + ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; @@ -3176,7 +3204,7 @@ static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev, if (wl->state == WL1271_STATE_OFF) goto out; - ret = wl1271_ps_elp_wakeup(wl, false); + ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; @@ -3376,9 +3404,12 @@ struct ieee80211_hw *wl1271_alloc_hw(void) for (j = 0; j < AP_MAX_LINKS; j++) skb_queue_head_init(&wl->links[j].tx_queue[i]); + skb_queue_head_init(&wl->deferred_rx_queue); + skb_queue_head_init(&wl->deferred_tx_queue); + INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work); INIT_DELAYED_WORK(&wl->pspoll_work, wl1271_pspoll_work); - INIT_WORK(&wl->irq_work, wl1271_irq_work); + INIT_WORK(&wl->netstack_work, wl1271_netstack_work); INIT_WORK(&wl->tx_work, wl1271_tx_work); INIT_WORK(&wl->recovery_work, wl1271_recovery_work); INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work); diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index 5c347b1bd17f..971f13e792da 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/ps.c @@ -69,7 +69,7 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl) } } -int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake) +int wl1271_ps_elp_wakeup(struct wl1271 *wl) { DECLARE_COMPLETION_ONSTACK(compl); unsigned long flags; @@ -87,7 +87,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake) * the completion variable in one entity. */ spin_lock_irqsave(&wl->wl_lock, flags); - if (work_pending(&wl->irq_work) || chip_awake) + if (test_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags)) pending = true; else wl->elp_compl = &compl; @@ -149,7 +149,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, case STATION_ACTIVE_MODE: default: wl1271_debug(DEBUG_PSM, "leaving psm"); - ret = wl1271_ps_elp_wakeup(wl, false); + ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) return ret; diff --git a/drivers/net/wireless/wl12xx/ps.h b/drivers/net/wireless/wl12xx/ps.h index fc1f4c193593..c41bd0a711bc 100644 --- a/drivers/net/wireless/wl12xx/ps.h +++ b/drivers/net/wireless/wl12xx/ps.h @@ -30,7 +30,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, u32 rates, bool send); void wl1271_ps_elp_sleep(struct wl1271 *wl); -int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake); +int wl1271_ps_elp_wakeup(struct wl1271 *wl); void wl1271_elp_work(struct work_struct *work); void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues); void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid); diff --git a/drivers/net/wireless/wl12xx/rx.c b/drivers/net/wireless/wl12xx/rx.c index 4e7a3b311321..919b59f00301 100644 --- a/drivers/net/wireless/wl12xx/rx.c +++ b/drivers/net/wireless/wl12xx/rx.c @@ -129,7 +129,8 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) skb_trim(skb, skb->len - desc->pad_len); - ieee80211_rx_ni(wl->hw, skb); + skb_queue_tail(&wl->deferred_rx_queue, skb); + ieee80211_queue_work(wl->hw, &wl->netstack_work); return 0; } diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index 61fdc9e981bd..b66abb5ebcf3 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -61,7 +61,7 @@ static struct device *wl1271_sdio_wl_to_dev(struct wl1271 *wl) return &(wl_to_func(wl)->dev); } -static irqreturn_t wl1271_irq(int irq, void *cookie) +static irqreturn_t wl1271_hardirq(int irq, void *cookie) { struct wl1271 *wl = cookie; unsigned long flags; @@ -70,17 +70,14 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) /* complete the ELP completion */ spin_lock_irqsave(&wl->wl_lock, flags); + set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags); if (wl->elp_compl) { complete(wl->elp_compl); wl->elp_compl = NULL; } - - if (!test_and_set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags)) - ieee80211_queue_work(wl->hw, &wl->irq_work); - set_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags); spin_unlock_irqrestore(&wl->wl_lock, flags); - return IRQ_HANDLED; + return IRQ_WAKE_THREAD; } static void wl1271_sdio_disable_interrupts(struct wl1271 *wl) @@ -243,14 +240,14 @@ static int __devinit wl1271_probe(struct sdio_func *func, wl->irq = wlan_data->irq; wl->ref_clock = wlan_data->board_ref_clock; - ret = request_irq(wl->irq, wl1271_irq, 0, DRIVER_NAME, wl); + ret = request_threaded_irq(wl->irq, wl1271_hardirq, wl1271_irq, + IRQF_TRIGGER_RISING, + DRIVER_NAME, wl); if (ret < 0) { wl1271_error("request_irq() failed: %d", ret); goto out_free; } - set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING); - disable_irq(wl->irq); ret = wl1271_init_ieee80211(wl); @@ -273,7 +270,6 @@ static int __devinit wl1271_probe(struct sdio_func *func, out_irq: free_irq(wl->irq, wl); - out_free: wl1271_free_hw(wl); diff --git a/drivers/net/wireless/wl12xx/spi.c b/drivers/net/wireless/wl12xx/spi.c index 0132dad756c4..df5a00f103ea 100644 --- a/drivers/net/wireless/wl12xx/spi.c +++ b/drivers/net/wireless/wl12xx/spi.c @@ -320,28 +320,23 @@ static void wl1271_spi_raw_write(struct wl1271 *wl, int addr, void *buf, spi_sync(wl_to_spi(wl), &m); } -static irqreturn_t wl1271_irq(int irq, void *cookie) +static irqreturn_t wl1271_hardirq(int irq, void *cookie) { - struct wl1271 *wl; + struct wl1271 *wl = cookie; unsigned long flags; wl1271_debug(DEBUG_IRQ, "IRQ"); - wl = cookie; - /* complete the ELP completion */ spin_lock_irqsave(&wl->wl_lock, flags); + set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags); if (wl->elp_compl) { complete(wl->elp_compl); wl->elp_compl = NULL; } - - if (!test_and_set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags)) - ieee80211_queue_work(wl->hw, &wl->irq_work); - set_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags); spin_unlock_irqrestore(&wl->wl_lock, flags); - return IRQ_HANDLED; + return IRQ_WAKE_THREAD; } static int wl1271_spi_set_power(struct wl1271 *wl, bool enable) @@ -413,14 +408,14 @@ static int __devinit wl1271_probe(struct spi_device *spi) goto out_free; } - ret = request_irq(wl->irq, wl1271_irq, 0, DRIVER_NAME, wl); + ret = request_threaded_irq(wl->irq, wl1271_hardirq, wl1271_irq, + IRQF_TRIGGER_RISING, + DRIVER_NAME, wl); if (ret < 0) { wl1271_error("request_irq() failed: %d", ret); goto out_free; } - set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING); - disable_irq(wl->irq); ret = wl1271_init_ieee80211(wl); diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 455954edf83e..5e9ef7d53e7e 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -464,7 +464,7 @@ void wl1271_tx_work_locked(struct wl1271 *wl) while ((skb = wl1271_skb_dequeue(wl))) { if (!woken_up) { - ret = wl1271_ps_elp_wakeup(wl, false); + ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out_ack; woken_up = true; @@ -589,7 +589,8 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, result->rate_class_index, result->status); /* return the packet to the stack */ - ieee80211_tx_status(wl->hw, skb); + skb_queue_tail(&wl->deferred_tx_queue, skb); + ieee80211_queue_work(wl->hw, &wl->netstack_work); wl1271_free_tx_id(wl, result->id); } diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index ea1eee7895cf..e395c0c4ebbd 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -320,7 +320,6 @@ enum wl12xx_flags { WL1271_FLAG_IN_ELP, WL1271_FLAG_PSM, WL1271_FLAG_PSM_REQUESTED, - WL1271_FLAG_IRQ_PENDING, WL1271_FLAG_IRQ_RUNNING, WL1271_FLAG_IDLE, WL1271_FLAG_IDLE_REQUESTED, @@ -404,6 +403,12 @@ struct wl1271 { struct sk_buff_head tx_queue[NUM_TX_QUEUES]; int tx_queue_count; + /* Frames received, not handled yet by mac80211 */ + struct sk_buff_head deferred_rx_queue; + + /* Frames sent, not returned yet to mac80211 */ + struct sk_buff_head deferred_tx_queue; + struct work_struct tx_work; /* Pending TX frames */ @@ -424,8 +429,8 @@ struct wl1271 { /* Intermediate buffer, used for packet aggregation */ u8 *aggr_buf; - /* The target interrupt mask */ - struct work_struct irq_work; + /* Network stack work */ + struct work_struct netstack_work; /* Hardware recovery work */ struct work_struct recovery_work; @@ -556,6 +561,8 @@ int wl1271_plt_stop(struct wl1271 *wl); #define WL1271_TX_QUEUE_LOW_WATERMARK 10 #define WL1271_TX_QUEUE_HIGH_WATERMARK 25 +#define WL1271_DEFERRED_QUEUE_LIMIT 64 + /* WL1271 needs a 200ms sleep after power on, and a 20ms sleep before power on in case is has been shut down shortly before */ #define WL1271_PRE_POWER_ON_SLEEP 20 /* in milliseconds */ -- cgit v1.2.3 From 2da69b890f47852dc368136375f49a5d24e2d9a1 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Tue, 1 Mar 2011 15:14:42 +0200 Subject: wl12xx: Switch to level trigger interrupts The interrupt of the wl12xx is a level interrupt in nature, since the interrupt line is not auto-reset. However, since resetting the interrupt requires bus transactions, this cannot be done from an interrupt context. Thus, requesting a level interrupt would require to disable the irq and re-enable it after the HW is acknowledged. Since we now request a threaded irq, this can also be done by specifying the IRQF_ONESHOT flag. Triggering on an edge can be problematic in some platforms, if the sampling frequency is not sufficient for detecting very frequent interrupts. In case an interrupt is missed, the driver will hang as the interrupt line will stay high until it is acknowledged by the driver, which will never happen. Fix this by requesting a level triggered interrupt, with the IRQF_ONESHOT flag. Signed-off-by: Ido Yariv Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/sdio.c | 2 +- drivers/net/wireless/wl12xx/spi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index b66abb5ebcf3..5b9dbeafec06 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -241,7 +241,7 @@ static int __devinit wl1271_probe(struct sdio_func *func, wl->ref_clock = wlan_data->board_ref_clock; ret = request_threaded_irq(wl->irq, wl1271_hardirq, wl1271_irq, - IRQF_TRIGGER_RISING, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, DRIVER_NAME, wl); if (ret < 0) { wl1271_error("request_irq() failed: %d", ret); diff --git a/drivers/net/wireless/wl12xx/spi.c b/drivers/net/wireless/wl12xx/spi.c index df5a00f103ea..18cf01719ae0 100644 --- a/drivers/net/wireless/wl12xx/spi.c +++ b/drivers/net/wireless/wl12xx/spi.c @@ -409,7 +409,7 @@ static int __devinit wl1271_probe(struct spi_device *spi) } ret = request_threaded_irq(wl->irq, wl1271_hardirq, wl1271_irq, - IRQF_TRIGGER_RISING, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, DRIVER_NAME, wl); if (ret < 0) { wl1271_error("request_irq() failed: %d", ret); -- cgit v1.2.3 From b07d4037051318d47c055384ef887535a0ed2d1e Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Tue, 1 Mar 2011 15:14:43 +0200 Subject: wl12xx: Avoid redundant TX work TX might be handled in the threaded IRQ handler, in which case, TX work might be scheduled just to discover it has nothing to do. Save a few context switches by cancelling redundant TX work in case TX is about to be handled in the threaded IRQ handler. Also, avoid scheduling TX work from wl1271_op_tx if not needed. Signed-off-by: Ido Yariv Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 32 +++++++++++++++++++++++++++----- drivers/net/wireless/wl12xx/wl12xx.h | 1 + 2 files changed, 28 insertions(+), 5 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index f408c5a84cc9..2679abcf5a05 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -668,6 +668,11 @@ irqreturn_t wl1271_irq(int irq, void *cookie) struct wl1271 *wl = (struct wl1271 *)cookie; bool done = false; unsigned int defer_count; + unsigned long flags; + + /* TX might be handled here, avoid redundant work */ + set_bit(WL1271_FLAG_TX_PENDING, &wl->flags); + cancel_work_sync(&wl->tx_work); mutex_lock(&wl->mutex); @@ -712,13 +717,17 @@ irqreturn_t wl1271_irq(int irq, void *cookie) wl1271_rx(wl, &wl->fw_status->common); /* Check if any tx blocks were freed */ + spin_lock_irqsave(&wl->wl_lock, flags); if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) && wl->tx_queue_count) { + spin_unlock_irqrestore(&wl->wl_lock, flags); /* * In order to avoid starvation of the TX path, * call the work function directly. */ wl1271_tx_work_locked(wl); + } else { + spin_unlock_irqrestore(&wl->wl_lock, flags); } /* check for tx results */ @@ -754,6 +763,14 @@ irqreturn_t wl1271_irq(int irq, void *cookie) wl1271_ps_elp_sleep(wl); out: + spin_lock_irqsave(&wl->wl_lock, flags); + /* In case TX was not handled here, queue TX work */ + clear_bit(WL1271_FLAG_TX_PENDING, &wl->flags); + if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) && + wl->tx_queue_count) + ieee80211_queue_work(wl->hw, &wl->tx_work); + spin_unlock_irqrestore(&wl->wl_lock, flags); + mutex_unlock(&wl->mutex); return IRQ_HANDLED; @@ -1068,7 +1085,13 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) int q; u8 hlid = 0; + q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); + + if (wl->bss_type == BSS_TYPE_AP_BSS) + hlid = wl1271_tx_get_hlid(skb); + spin_lock_irqsave(&wl->wl_lock, flags); + wl->tx_queue_count++; /* @@ -1081,12 +1104,8 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags); } - spin_unlock_irqrestore(&wl->wl_lock, flags); - /* queue the packet */ - q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); if (wl->bss_type == BSS_TYPE_AP_BSS) { - hlid = wl1271_tx_get_hlid(skb); wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d", hlid, q); skb_queue_tail(&wl->links[hlid].tx_queue[q], skb); } else { @@ -1098,8 +1117,11 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) * before that, the tx_work will not be initialized! */ - if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags)) + if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) && + !test_bit(WL1271_FLAG_TX_PENDING, &wl->flags)) ieee80211_queue_work(wl->hw, &wl->tx_work); + + spin_unlock_irqrestore(&wl->wl_lock, flags); } static struct notifier_block wl1271_dev_notifier = { diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index e395c0c4ebbd..86be83e25ec5 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -317,6 +317,7 @@ enum wl12xx_flags { WL1271_FLAG_JOINED, WL1271_FLAG_GPIO_POWER, WL1271_FLAG_TX_QUEUE_STOPPED, + WL1271_FLAG_TX_PENDING, WL1271_FLAG_IN_ELP, WL1271_FLAG_PSM, WL1271_FLAG_PSM_REQUESTED, -- cgit v1.2.3 From b16d4b6864e5bd7e5a6e5987f896003175235bca Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Tue, 1 Mar 2011 15:14:44 +0200 Subject: wl12xx: Modify requested number of memory blocks Tests have shown that the requested number of memory blocks is sub-optimal. Slightly modify the requested number of memory blocks for TX. Signed-off-by: Ido Yariv Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 2679abcf5a05..8b3c8d196b03 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -304,7 +304,7 @@ static struct conf_drv_settings default_conf = { .rx_block_num = 70, .tx_min_block_num = 40, .dynamic_memory = 0, - .min_req_tx_blocks = 104, + .min_req_tx_blocks = 100, .min_req_rx_blocks = 22, .tx_min = 27, } -- cgit v1.2.3 From 24225b37bd78d3e2edaa1a39316c54786adaa465 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 1 Mar 2011 12:27:26 +0200 Subject: wl12xx: wakeup chip from ELP during scan Commands are sometimes sent to FW on scan completion. Make sure the chip is awake to receive them. Sending commands while the chip is in ELP can cause SDIO read errors and/or crash the FW. Signed-off-by: Arik Nemtsov Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/cmd.c | 1 + drivers/net/wireless/wl12xx/scan.c | 20 ++++++++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 97ffd7aa57a8..f0aa7ab97bf7 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -63,6 +63,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, cmd->status = 0; WARN_ON(len % 4 != 0); + WARN_ON(test_bit(WL1271_FLAG_IN_ELP, &wl->flags)); wl1271_write(wl, wl->cmd_box_addr, buf, len, false); diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index 6f897b9d90ca..420653a2859c 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c @@ -27,6 +27,7 @@ #include "cmd.h" #include "scan.h" #include "acx.h" +#include "ps.h" void wl1271_scan_complete_work(struct work_struct *work) { @@ -40,10 +41,11 @@ void wl1271_scan_complete_work(struct work_struct *work) mutex_lock(&wl->mutex); - if (wl->scan.state == WL1271_SCAN_STATE_IDLE) { - mutex_unlock(&wl->mutex); - return; - } + if (wl->state == WL1271_STATE_OFF) + goto out; + + if (wl->scan.state == WL1271_SCAN_STATE_IDLE) + goto out; wl->scan.state = WL1271_SCAN_STATE_IDLE; kfree(wl->scan.scanned_ch); @@ -52,13 +54,19 @@ void wl1271_scan_complete_work(struct work_struct *work) ieee80211_scan_completed(wl->hw, false); /* restore hardware connection monitoring template */ - if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) - wl1271_cmd_build_ap_probe_req(wl, wl->probereq); + if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) { + if (wl1271_ps_elp_wakeup(wl) == 0) { + wl1271_cmd_build_ap_probe_req(wl, wl->probereq); + wl1271_ps_elp_sleep(wl); + } + } if (wl->scan.failed) { wl1271_info("Scan completed due to error."); ieee80211_queue_work(wl->hw, &wl->recovery_work); } + +out: mutex_unlock(&wl->mutex); } -- cgit v1.2.3 From 95a776107a131823c87147dff083696d8814c1b3 Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Wed, 2 Mar 2011 10:46:46 +0100 Subject: wl12xx: Correctly set up protection if non-GF STAs are present Set the gf_protection bit when calling ACX_HT_BSS_OPERATION according to the GF bit passed by mac80211 in ht_operation_mode. [Added a proper commit message -- Luca] Signed-off-by: Helmut Schaa Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index 3badc6bb7866..a3db755ceeda 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -1361,7 +1361,8 @@ int wl1271_acx_set_ht_information(struct wl1271 *wl, acx->ht_protection = (u8)(ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION); acx->rifs_mode = 0; - acx->gf_protection = 0; + acx->gf_protection = + !!(ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); acx->ht_tx_burst_limit = 0; acx->dual_cts_protection = 0; -- cgit v1.2.3 From 80751e2b8ffcbbe065e850d943301aa1ab219599 Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Mon, 7 Mar 2011 11:14:23 -0800 Subject: ieee80211: add IEEE80211_COUNTRY_STRING_LEN definition and make use of it in wireless drivers Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl12xx_80211.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/wl12xx_80211.h b/drivers/net/wireless/wl12xx/wl12xx_80211.h index 67dcf8f28cd3..18fe542360f2 100644 --- a/drivers/net/wireless/wl12xx/wl12xx_80211.h +++ b/drivers/net/wireless/wl12xx/wl12xx_80211.h @@ -55,7 +55,6 @@ /* This really should be 8, but not for our firmware */ #define MAX_SUPPORTED_RATES 32 -#define COUNTRY_STRING_LEN 3 #define MAX_COUNTRY_TRIPLETS 32 /* Headers */ @@ -99,7 +98,7 @@ struct country_triplet { struct wl12xx_ie_country { struct wl12xx_ie_header header; - u8 country_string[COUNTRY_STRING_LEN]; + u8 country_string[IEEE80211_COUNTRY_STRING_LEN]; struct country_triplet triplets[MAX_COUNTRY_TRIPLETS]; } __packed; -- cgit v1.2.3