diff options
Diffstat (limited to 'drivers/net')
217 files changed, 13680 insertions, 7172 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 18ff622703be..6460505ab24c 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2354,20 +2354,6 @@ config GELIC_WIRELESS the driver automatically distinguishes the models, you can safely enable this option even if you have a wireless-less model. -config GELIC_WIRELESS_OLD_PSK_INTERFACE - bool "PS3 Wireless private PSK interface (OBSOLETE)" - depends on GELIC_WIRELESS - select WEXT_PRIV - help - This option retains the obsolete private interface to pass - the PSK from user space programs to the driver. The PSK - stands for 'Pre Shared Key' and is used for WPA[2]-PSK - (WPA-Personal) environment. - If WPA[2]-PSK is used and you need to use old programs that - support only this old interface, say Y. Otherwise N. - - If unsure, say N. - config FSL_PQ_MDIO tristate "Freescale PQ MDIO" depends on FSL_SOC diff --git a/drivers/net/ps3_gelic_wireless.c b/drivers/net/ps3_gelic_wireless.c index 227b141c4fbd..2663b2fdc0bb 100644 --- a/drivers/net/ps3_gelic_wireless.c +++ b/drivers/net/ps3_gelic_wireless.c @@ -1389,113 +1389,6 @@ static int gelic_wl_get_mode(struct net_device *netdev, return 0; } -#ifdef CONFIG_GELIC_WIRELESS_OLD_PSK_INTERFACE -/* SIOCIWFIRSTPRIV */ -static int hex2bin(u8 *str, u8 *bin, unsigned int len) -{ - unsigned int i; - static unsigned char *hex = "0123456789ABCDEF"; - unsigned char *p, *q; - u8 tmp; - - if (len != WPA_PSK_LEN * 2) - return -EINVAL; - - for (i = 0; i < WPA_PSK_LEN * 2; i += 2) { - p = strchr(hex, toupper(str[i])); - q = strchr(hex, toupper(str[i + 1])); - if (!p || !q) { - pr_info("%s: unconvertible PSK digit=%d\n", - __func__, i); - return -EINVAL; - } - tmp = ((p - hex) << 4) + (q - hex); - *bin++ = tmp; - } - return 0; -}; - -static int gelic_wl_priv_set_psk(struct net_device *net_dev, - struct iw_request_info *info, - union iwreq_data *data, char *extra) -{ - struct gelic_wl_info *wl = port_wl(netdev_priv(net_dev)); - unsigned int len; - unsigned long irqflag; - int ret = 0; - - pr_debug("%s:<- len=%d\n", __func__, data->data.length); - len = data->data.length - 1; - if (len <= 2) - return -EINVAL; - - spin_lock_irqsave(&wl->lock, irqflag); - if (extra[0] == '"' && extra[len - 1] == '"') { - pr_debug("%s: passphrase mode\n", __func__); - /* pass phrase */ - if (GELIC_WL_EURUS_PSK_MAX_LEN < (len - 2)) { - pr_info("%s: passphrase too long\n", __func__); - ret = -E2BIG; - goto out; - } - memset(wl->psk, 0, sizeof(wl->psk)); - wl->psk_len = len - 2; - memcpy(wl->psk, &(extra[1]), wl->psk_len); - wl->psk_type = GELIC_EURUS_WPA_PSK_PASSPHRASE; - } else { - ret = hex2bin(extra, wl->psk, len); - if (ret) - goto out; - wl->psk_len = WPA_PSK_LEN; - wl->psk_type = GELIC_EURUS_WPA_PSK_BIN; - } - set_bit(GELIC_WL_STAT_WPA_PSK_SET, &wl->stat); -out: - spin_unlock_irqrestore(&wl->lock, irqflag); - pr_debug("%s:->\n", __func__); - return ret; -} - -static int gelic_wl_priv_get_psk(struct net_device *net_dev, - struct iw_request_info *info, - union iwreq_data *data, char *extra) -{ - struct gelic_wl_info *wl = port_wl(netdev_priv(net_dev)); - char *p; - unsigned long irqflag; - unsigned int i; - - pr_debug("%s:<-\n", __func__); - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - spin_lock_irqsave(&wl->lock, irqflag); - p = extra; - if (test_bit(GELIC_WL_STAT_WPA_PSK_SET, &wl->stat)) { - if (wl->psk_type == GELIC_EURUS_WPA_PSK_BIN) { - for (i = 0; i < wl->psk_len; i++) { - sprintf(p, "%02xu", wl->psk[i]); - p += 2; - } - *p = '\0'; - data->data.length = wl->psk_len * 2; - } else { - *p++ = '"'; - memcpy(p, wl->psk, wl->psk_len); - p += wl->psk_len; - *p++ = '"'; - *p = '\0'; - data->data.length = wl->psk_len + 2; - } - } else - /* no psk set */ - data->data.length = 0; - spin_unlock_irqrestore(&wl->lock, irqflag); - pr_debug("%s:-> %d\n", __func__, data->data.length); - return 0; -} -#endif - /* SIOCGIWNICKN */ static int gelic_wl_get_nick(struct net_device *net_dev, struct iw_request_info *info, @@ -1571,8 +1464,10 @@ static int gelic_wl_start_scan(struct gelic_wl_info *wl, int always_scan, init_completion(&wl->scan_done); /* * If we have already a bss list, don't try to get new + * unless we are doing an ESSID scan */ - if (!always_scan && wl->scan_stat == GELIC_WL_SCAN_STAT_GOT_LIST) { + if ((!essid_len && !always_scan) + && wl->scan_stat == GELIC_WL_SCAN_STAT_GOT_LIST) { pr_debug("%s: already has the list\n", __func__); complete(&wl->scan_done); goto out; @@ -1673,7 +1568,7 @@ static void gelic_wl_scan_complete_event(struct gelic_wl_info *wl) } } - /* put them in the newtork_list */ + /* put them in the network_list */ for (i = 0, scan_info_size = 0, scan_info = buf; scan_info_size < data_len; i++, scan_info_size += be16_to_cpu(scan_info->size), @@ -2009,7 +1904,7 @@ static int gelic_wl_do_wpa_setup(struct gelic_wl_info *wl) /* PSK type */ wpa->psk_type = cpu_to_be16(wl->psk_type); #ifdef DEBUG - pr_debug("%s: sec=%s psktype=%s\nn", __func__, + pr_debug("%s: sec=%s psktype=%s\n", __func__, wpasecstr(wpa->security), (wpa->psk_type == GELIC_EURUS_WPA_PSK_BIN) ? "BIN" : "passphrase"); @@ -2019,9 +1914,9 @@ static int gelic_wl_do_wpa_setup(struct gelic_wl_info *wl) * the debug log because this dumps your precious * passphrase/key. */ - pr_debug("%s: psk=%s\n", + pr_debug("%s: psk=%s\n", __func__, (wpa->psk_type == GELIC_EURUS_WPA_PSK_BIN) ? - (char *)"N/A" : (char *)wpa->psk); + "N/A" : wpa->psk); #endif #endif /* issue wpa setup */ @@ -2406,40 +2301,10 @@ static const iw_handler gelic_wl_wext_handler[] = IW_IOCTL(SIOCGIWNICKN) = gelic_wl_get_nick, }; -#ifdef CONFIG_GELIC_WIRELESS_OLD_PSK_INTERFACE -static struct iw_priv_args gelic_wl_private_args[] = -{ - { - .cmd = GELIC_WL_PRIV_SET_PSK, - .set_args = IW_PRIV_TYPE_CHAR | - (GELIC_WL_EURUS_PSK_MAX_LEN + 2), - .name = "set_psk" - }, - { - .cmd = GELIC_WL_PRIV_GET_PSK, - .get_args = IW_PRIV_TYPE_CHAR | - (GELIC_WL_EURUS_PSK_MAX_LEN + 2), - .name = "get_psk" - } -}; - -static const iw_handler gelic_wl_private_handler[] = -{ - gelic_wl_priv_set_psk, - gelic_wl_priv_get_psk, -}; -#endif - static const struct iw_handler_def gelic_wl_wext_handler_def = { .num_standard = ARRAY_SIZE(gelic_wl_wext_handler), .standard = gelic_wl_wext_handler, .get_wireless_stats = gelic_wl_get_wireless_stats, -#ifdef CONFIG_GELIC_WIRELESS_OLD_PSK_INTERFACE - .num_private = ARRAY_SIZE(gelic_wl_private_handler), - .num_private_args = ARRAY_SIZE(gelic_wl_private_args), - .private = gelic_wl_private_handler, - .private_args = gelic_wl_private_args, -#endif }; static struct net_device * __devinit gelic_wl_alloc(struct gelic_card *card) diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 56dd6650c97a..588943660755 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -112,6 +112,7 @@ config AIRO_CS depends on PCMCIA && (BROKEN || !M32R) select WIRELESS_EXT select WEXT_SPY + select WEXT_PRIV select CRYPTO select CRYPTO_AES ---help--- diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c index 39410016b4ff..6508515feed3 100644 --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -302,18 +302,6 @@ static int adm8211_get_stats(struct ieee80211_hw *dev, return 0; } -static int adm8211_get_tx_stats(struct ieee80211_hw *dev, - struct ieee80211_tx_queue_stats *stats) -{ - struct adm8211_priv *priv = dev->priv; - - stats[0].len = priv->cur_tx - priv->dirty_tx; - stats[0].limit = priv->tx_ring_size - 2; - stats[0].count = priv->dirty_tx; - - return 0; -} - static void adm8211_interrupt_tci(struct ieee80211_hw *dev) { struct adm8211_priv *priv = dev->priv; @@ -1400,15 +1388,15 @@ static void adm8211_configure_filter(struct ieee80211_hw *dev, } static int adm8211_add_interface(struct ieee80211_hw *dev, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct adm8211_priv *priv = dev->priv; if (priv->mode != NL80211_IFTYPE_MONITOR) return -EOPNOTSUPP; - switch (conf->type) { + switch (vif->type) { case NL80211_IFTYPE_STATION: - priv->mode = conf->type; + priv->mode = vif->type; break; default: return -EOPNOTSUPP; @@ -1416,8 +1404,8 @@ static int adm8211_add_interface(struct ieee80211_hw *dev, ADM8211_IDLE(); - ADM8211_CSR_WRITE(PAR0, le32_to_cpu(*(__le32 *)conf->mac_addr)); - ADM8211_CSR_WRITE(PAR1, le16_to_cpu(*(__le16 *)(conf->mac_addr + 4))); + ADM8211_CSR_WRITE(PAR0, le32_to_cpu(*(__le32 *)vif->addr)); + ADM8211_CSR_WRITE(PAR1, le16_to_cpu(*(__le16 *)(vif->addr + 4))); adm8211_update_mode(dev); @@ -1427,7 +1415,7 @@ static int adm8211_add_interface(struct ieee80211_hw *dev, } static void adm8211_remove_interface(struct ieee80211_hw *dev, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct adm8211_priv *priv = dev->priv; priv->mode = NL80211_IFTYPE_MONITOR; @@ -1773,7 +1761,6 @@ static const struct ieee80211_ops adm8211_ops = { .prepare_multicast = adm8211_prepare_multicast, .configure_filter = adm8211_configure_filter, .get_stats = adm8211_get_stats, - .get_tx_stats = adm8211_get_tx_stats, .get_tsf = adm8211_get_tsft }; diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index 4331d675fcc6..260fb9905e97 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -51,6 +51,7 @@ #include <linux/freezer.h> #include <linux/ieee80211.h> +#include <net/iw_handler.h> #include "airo.h" @@ -5254,11 +5255,7 @@ static int set_wep_key(struct airo_info *ai, u16 index, const char *key, WepKeyRid wkr; int rc; - if (keylen == 0) { - airo_print_err(ai->dev->name, "%s: key length to set was zero", - __func__); - return -1; - } + WARN_ON(keylen == 0); memset(&wkr, 0, sizeof(wkr)); wkr.len = cpu_to_le16(sizeof(wkr)); @@ -6405,11 +6402,7 @@ static int airo_set_encode(struct net_device *dev, if (dwrq->length > MIN_KEY_SIZE) key.len = MAX_KEY_SIZE; else - if (dwrq->length > 0) - key.len = MIN_KEY_SIZE; - else - /* Disable the key */ - key.len = 0; + key.len = MIN_KEY_SIZE; /* Check if the key is not marked as invalid */ if(!(dwrq->flags & IW_ENCODE_NOKEY)) { /* Cleanup */ @@ -6590,12 +6583,22 @@ static int airo_set_encodeext(struct net_device *dev, default: return -EINVAL; } - /* Send the key to the card */ - rc = set_wep_key(local, idx, key.key, key.len, perm, 1); - if (rc < 0) { - airo_print_err(local->dev->name, "failed to set WEP key" - " at index %d: %d.", idx, rc); - return rc; + if (key.len == 0) { + rc = set_wep_tx_idx(local, idx, perm, 1); + if (rc < 0) { + airo_print_err(local->dev->name, + "failed to set WEP transmit index to %d: %d.", + idx, rc); + return rc; + } + } else { + rc = set_wep_key(local, idx, key.key, key.len, perm, 1); + if (rc < 0) { + airo_print_err(local->dev->name, + "failed to set WEP key at index %d: %d.", + idx, rc); + return rc; + } } } diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index 2517364d3ebe..0fb419936dff 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -1789,7 +1789,7 @@ static void at76_mac80211_stop(struct ieee80211_hw *hw) } static int at76_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct at76_priv *priv = hw->priv; int ret = 0; @@ -1798,7 +1798,7 @@ static int at76_add_interface(struct ieee80211_hw *hw, mutex_lock(&priv->mtx); - switch (conf->type) { + switch (vif->type) { case NL80211_IFTYPE_STATION: priv->iw_mode = IW_MODE_INFRA; break; @@ -1814,7 +1814,7 @@ exit: } static void at76_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { at76_dbg(DBG_MAC80211, "%s()", __func__); } diff --git a/drivers/net/wireless/ath/ar9170/ar9170.h b/drivers/net/wireless/ath/ar9170/ar9170.h index 9f9459860d82..8c8ce67971e9 100644 --- a/drivers/net/wireless/ath/ar9170/ar9170.h +++ b/drivers/net/wireless/ath/ar9170/ar9170.h @@ -109,7 +109,6 @@ struct ar9170_rxstream_mpdu_merge { bool has_plcp; }; -#define AR9170_NUM_MAX_BA_RETRY 5 #define AR9170_NUM_TID 16 #define WME_BA_BMP_SIZE 64 #define AR9170_NUM_MAX_AGG_LEN (2 * WME_BA_BMP_SIZE) @@ -143,7 +142,12 @@ struct ar9170_sta_tid { u16 tid; enum ar9170_tid_state state; bool active; - u8 retry; +}; + +struct ar9170_tx_queue_stats { + unsigned int len; + unsigned int limit; + unsigned int count; }; #define AR9170_QUEUE_TIMEOUT 64 @@ -154,6 +158,8 @@ struct ar9170_sta_tid { #define AR9170_NUM_TX_STATUS 128 #define AR9170_NUM_TX_AGG_MAX 30 +#define AR9170_NUM_TX_LIMIT_HARD AR9170_TXQ_DEPTH +#define AR9170_NUM_TX_LIMIT_SOFT (AR9170_TXQ_DEPTH - 10) struct ar9170 { struct ieee80211_hw *hw; @@ -211,7 +217,7 @@ struct ar9170 { /* qos queue settings */ spinlock_t tx_stats_lock; - struct ieee80211_tx_queue_stats tx_stats[5]; + struct ar9170_tx_queue_stats tx_stats[5]; struct ieee80211_tx_queue_params edcf[5]; spinlock_t cmdlock; @@ -248,13 +254,8 @@ struct ar9170_sta_info { unsigned int ampdu_max_len; }; -#define AR9170_TX_FLAG_WAIT_FOR_ACK BIT(0) -#define AR9170_TX_FLAG_NO_ACK BIT(1) -#define AR9170_TX_FLAG_BLOCK_ACK BIT(2) - struct ar9170_tx_info { unsigned long timeout; - unsigned int flags; }; #define IS_STARTED(a) (((struct ar9170 *)a)->state >= AR9170_STARTED) diff --git a/drivers/net/wireless/ath/ar9170/hw.h b/drivers/net/wireless/ath/ar9170/hw.h index 701ddb7d8400..0a1d4c28e68a 100644 --- a/drivers/net/wireless/ath/ar9170/hw.h +++ b/drivers/net/wireless/ath/ar9170/hw.h @@ -276,6 +276,7 @@ struct ar9170_tx_control { #define AR9170_TX_MAC_RATE_PROBE 0x8000 /* either-or */ +#define AR9170_TX_PHY_MOD_MASK 0x00000003 #define AR9170_TX_PHY_MOD_CCK 0x00000000 #define AR9170_TX_PHY_MOD_OFDM 0x00000001 #define AR9170_TX_PHY_MOD_HT 0x00000002 diff --git a/drivers/net/wireless/ath/ar9170/mac.c b/drivers/net/wireless/ath/ar9170/mac.c index ddc8c09dc79e..857e86104295 100644 --- a/drivers/net/wireless/ath/ar9170/mac.c +++ b/drivers/net/wireless/ath/ar9170/mac.c @@ -117,7 +117,7 @@ int ar9170_set_qos(struct ar9170 *ar) ar9170_regwrite(AR9170_MAC_REG_AC1_AC0_TXOP, ar->edcf[0].txop | ar->edcf[1].txop << 16); ar9170_regwrite(AR9170_MAC_REG_AC3_AC2_TXOP, - ar->edcf[1].txop | ar->edcf[3].txop << 16); + ar->edcf[2].txop | ar->edcf[3].txop << 16); ar9170_regwrite_finish(); diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c index f9d6db8d013e..8a964f130367 100644 --- a/drivers/net/wireless/ath/ar9170/main.c +++ b/drivers/net/wireless/ath/ar9170/main.c @@ -194,12 +194,15 @@ static inline u16 ar9170_get_seq(struct sk_buff *skb) return ar9170_get_seq_h((void *) txc->frame_data); } +static inline u16 ar9170_get_tid_h(struct ieee80211_hdr *hdr) +{ + return (ieee80211_get_qos_ctl(hdr))[0] & IEEE80211_QOS_CTL_TID_MASK; +} + static inline u16 ar9170_get_tid(struct sk_buff *skb) { struct ar9170_tx_control *txc = (void *) skb->data; - struct ieee80211_hdr *hdr = (void *) txc->frame_data; - - return (ieee80211_get_qos_ctl(hdr))[0] & IEEE80211_QOS_CTL_TID_MASK; + return ar9170_get_tid_h((struct ieee80211_hdr *) txc->frame_data); } #define GET_NEXT_SEQ(seq) ((seq + 1) & 0x0fff) @@ -213,10 +216,10 @@ static void ar9170_print_txheader(struct ar9170 *ar, struct sk_buff *skb) struct ar9170_tx_info *arinfo = (void *) txinfo->rate_driver_data; struct ieee80211_hdr *hdr = (void *) txc->frame_data; - printk(KERN_DEBUG "%s: => FRAME [skb:%p, q:%d, DA:[%pM] flags:%x s:%d " + printk(KERN_DEBUG "%s: => FRAME [skb:%p, q:%d, DA:[%pM] s:%d " "mac_ctrl:%04x, phy_ctrl:%08x, timeout:[%d ms]]\n", wiphy_name(ar->hw->wiphy), skb, skb_get_queue_mapping(skb), - ieee80211_get_DA(hdr), arinfo->flags, ar9170_get_seq_h(hdr), + ieee80211_get_DA(hdr), ar9170_get_seq_h(hdr), le16_to_cpu(txc->mac_control), le32_to_cpu(txc->phy_control), jiffies_to_msecs(arinfo->timeout - jiffies)); } @@ -430,7 +433,7 @@ void ar9170_tx_callback(struct ar9170 *ar, struct sk_buff *skb) spin_lock_irqsave(&ar->tx_stats_lock, flags); ar->tx_stats[queue].len--; - if (skb_queue_empty(&ar->tx_pending[queue])) { + if (ar->tx_stats[queue].len < AR9170_NUM_TX_LIMIT_SOFT) { #ifdef AR9170_QUEUE_STOP_DEBUG printk(KERN_DEBUG "%s: wake queue %d\n", wiphy_name(ar->hw->wiphy), queue); @@ -440,22 +443,17 @@ void ar9170_tx_callback(struct ar9170 *ar, struct sk_buff *skb) } spin_unlock_irqrestore(&ar->tx_stats_lock, flags); - if (arinfo->flags & AR9170_TX_FLAG_BLOCK_ACK) { - ar9170_tx_ampdu_callback(ar, skb); - } else if (arinfo->flags & AR9170_TX_FLAG_WAIT_FOR_ACK) { - arinfo->timeout = jiffies + - msecs_to_jiffies(AR9170_TX_TIMEOUT); - - skb_queue_tail(&ar->tx_status[queue], skb); - } else if (arinfo->flags & AR9170_TX_FLAG_NO_ACK) { + if (info->flags & IEEE80211_TX_CTL_NO_ACK) { ar9170_tx_status(ar, skb, AR9170_TX_STATUS_FAILED); } else { -#ifdef AR9170_QUEUE_DEBUG - printk(KERN_DEBUG "%s: unsupported frame flags!\n", - wiphy_name(ar->hw->wiphy)); - ar9170_print_txheader(ar, skb); -#endif /* AR9170_QUEUE_DEBUG */ - dev_kfree_skb_any(skb); + if (info->flags & IEEE80211_TX_CTL_AMPDU) { + ar9170_tx_ampdu_callback(ar, skb); + } else { + arinfo->timeout = jiffies + + msecs_to_jiffies(AR9170_TX_TIMEOUT); + + skb_queue_tail(&ar->tx_status[queue], skb); + } } if (!ar->tx_stats[queue].len && @@ -1407,17 +1405,6 @@ static int ar9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb) if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && (is_valid_ether_addr(ieee80211_get_DA(hdr)))) { - if (info->flags & IEEE80211_TX_CTL_AMPDU) { - if (unlikely(!info->control.sta)) - goto err_out; - - txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_AGGR); - arinfo->flags = AR9170_TX_FLAG_BLOCK_ACK; - - goto out; - } - - txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_RATE_PROBE); /* * WARNING: * Putting the QoS queue bits into an unexplored territory is @@ -1431,12 +1418,17 @@ static int ar9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb) txc->phy_control |= cpu_to_le32(queue << AR9170_TX_PHY_QOS_SHIFT); - arinfo->flags = AR9170_TX_FLAG_WAIT_FOR_ACK; - } else { - arinfo->flags = AR9170_TX_FLAG_NO_ACK; + + if (info->flags & IEEE80211_TX_CTL_AMPDU) { + if (unlikely(!info->control.sta)) + goto err_out; + + txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_AGGR); + } else { + txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_RATE_PROBE); + } } -out: return 0; err_out: @@ -1671,8 +1663,7 @@ static bool ar9170_tx_ampdu(struct ar9170 *ar) * tell the FW/HW that this is the last frame, * that way it will wait for the immediate block ack. */ - if (likely(skb_peek_tail(&agg))) - ar9170_tx_indicate_immba(ar, skb_peek_tail(&agg)); + ar9170_tx_indicate_immba(ar, skb_peek_tail(&agg)); #ifdef AR9170_TXAGG_DEBUG printk(KERN_DEBUG "%s: generated A-MPDU looks like this:\n", @@ -1716,6 +1707,21 @@ static void ar9170_tx(struct ar9170 *ar) for (i = 0; i < __AR9170_NUM_TXQ; i++) { spin_lock_irqsave(&ar->tx_stats_lock, flags); + frames = min(ar->tx_stats[i].limit - ar->tx_stats[i].len, + skb_queue_len(&ar->tx_pending[i])); + + if (remaining_space < frames) { +#ifdef AR9170_QUEUE_DEBUG + printk(KERN_DEBUG "%s: tx quota reached queue:%d, " + "remaining slots:%d, needed:%d\n", + wiphy_name(ar->hw->wiphy), i, remaining_space, + frames); +#endif /* AR9170_QUEUE_DEBUG */ + frames = remaining_space; + } + + ar->tx_stats[i].len += frames; + ar->tx_stats[i].count += frames; if (ar->tx_stats[i].len >= ar->tx_stats[i].limit) { #ifdef AR9170_QUEUE_DEBUG printk(KERN_DEBUG "%s: queue %d full\n", @@ -1733,25 +1739,8 @@ static void ar9170_tx(struct ar9170 *ar) __ar9170_dump_txstats(ar); #endif /* AR9170_QUEUE_STOP_DEBUG */ ieee80211_stop_queue(ar->hw, i); - spin_unlock_irqrestore(&ar->tx_stats_lock, flags); - continue; - } - - frames = min(ar->tx_stats[i].limit - ar->tx_stats[i].len, - skb_queue_len(&ar->tx_pending[i])); - - if (remaining_space < frames) { -#ifdef AR9170_QUEUE_DEBUG - printk(KERN_DEBUG "%s: tx quota reached queue:%d, " - "remaining slots:%d, needed:%d\n", - wiphy_name(ar->hw->wiphy), i, remaining_space, - frames); -#endif /* AR9170_QUEUE_DEBUG */ - frames = remaining_space; } - ar->tx_stats[i].len += frames; - ar->tx_stats[i].count += frames; spin_unlock_irqrestore(&ar->tx_stats_lock, flags); if (!frames) @@ -1773,7 +1762,7 @@ static void ar9170_tx(struct ar9170 *ar) arinfo->timeout = jiffies + msecs_to_jiffies(AR9170_TX_TIMEOUT); - if (arinfo->flags == AR9170_TX_FLAG_BLOCK_ACK) + if (info->flags & IEEE80211_TX_CTL_AMPDU) atomic_inc(&ar->tx_ampdu_pending); #ifdef AR9170_QUEUE_DEBUG @@ -1784,7 +1773,7 @@ static void ar9170_tx(struct ar9170 *ar) err = ar->tx(ar, skb); if (unlikely(err)) { - if (arinfo->flags == AR9170_TX_FLAG_BLOCK_ACK) + if (info->flags & IEEE80211_TX_CTL_AMPDU) atomic_dec(&ar->tx_ampdu_pending); frames_failed++; @@ -1950,7 +1939,7 @@ err_free: } static int ar9170_op_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct ar9170 *ar = hw->priv; struct ath_common *common = &ar->common; @@ -1963,8 +1952,8 @@ static int ar9170_op_add_interface(struct ieee80211_hw *hw, goto unlock; } - ar->vif = conf->vif; - memcpy(common->macaddr, conf->mac_addr, ETH_ALEN); + ar->vif = vif; + memcpy(common->macaddr, vif->addr, ETH_ALEN); if (modparam_nohwcrypt || (ar->vif->type != NL80211_IFTYPE_STATION)) { ar->rx_software_decryption = true; @@ -1984,7 +1973,7 @@ unlock: } static void ar9170_op_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct ar9170 *ar = hw->priv; @@ -2340,55 +2329,55 @@ out: return err; } -static void ar9170_sta_notify(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum sta_notify_cmd cmd, - struct ieee80211_sta *sta) +static int ar9170_sta_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) { struct ar9170 *ar = hw->priv; struct ar9170_sta_info *sta_info = (void *) sta->drv_priv; unsigned int i; - switch (cmd) { - case STA_NOTIFY_ADD: - memset(sta_info, 0, sizeof(*sta_info)); + memset(sta_info, 0, sizeof(*sta_info)); - if (!sta->ht_cap.ht_supported) - break; + if (!sta->ht_cap.ht_supported) + return 0; - if (sta->ht_cap.ampdu_density > ar->global_ampdu_density) - ar->global_ampdu_density = sta->ht_cap.ampdu_density; + if (sta->ht_cap.ampdu_density > ar->global_ampdu_density) + ar->global_ampdu_density = sta->ht_cap.ampdu_density; - if (sta->ht_cap.ampdu_factor < ar->global_ampdu_factor) - ar->global_ampdu_factor = sta->ht_cap.ampdu_factor; + if (sta->ht_cap.ampdu_factor < ar->global_ampdu_factor) + ar->global_ampdu_factor = sta->ht_cap.ampdu_factor; - for (i = 0; i < AR9170_NUM_TID; i++) { - sta_info->agg[i].state = AR9170_TID_STATE_SHUTDOWN; - sta_info->agg[i].active = false; - sta_info->agg[i].ssn = 0; - sta_info->agg[i].retry = 0; - sta_info->agg[i].tid = i; - INIT_LIST_HEAD(&sta_info->agg[i].list); - skb_queue_head_init(&sta_info->agg[i].queue); - } + for (i = 0; i < AR9170_NUM_TID; i++) { + sta_info->agg[i].state = AR9170_TID_STATE_SHUTDOWN; + sta_info->agg[i].active = false; + sta_info->agg[i].ssn = 0; + sta_info->agg[i].tid = i; + INIT_LIST_HEAD(&sta_info->agg[i].list); + skb_queue_head_init(&sta_info->agg[i].queue); + } - sta_info->ampdu_max_len = 1 << (3 + sta->ht_cap.ampdu_factor); - break; + sta_info->ampdu_max_len = 1 << (3 + sta->ht_cap.ampdu_factor); - case STA_NOTIFY_REMOVE: - if (!sta->ht_cap.ht_supported) - break; + return 0; +} - for (i = 0; i < AR9170_NUM_TID; i++) { - sta_info->agg[i].state = AR9170_TID_STATE_INVALID; - skb_queue_purge(&sta_info->agg[i].queue); - } +static int ar9170_sta_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct ar9170_sta_info *sta_info = (void *) sta->drv_priv; + unsigned int i; - break; + if (!sta->ht_cap.ht_supported) + return 0; - default: - break; + for (i = 0; i < AR9170_NUM_TID; i++) { + sta_info->agg[i].state = AR9170_TID_STATE_INVALID; + skb_queue_purge(&sta_info->agg[i].queue); } + + return 0; } static int ar9170_get_stats(struct ieee80211_hw *hw, @@ -2408,18 +2397,6 @@ static int ar9170_get_stats(struct ieee80211_hw *hw, return 0; } -static int ar9170_get_tx_stats(struct ieee80211_hw *hw, - struct ieee80211_tx_queue_stats *tx_stats) -{ - struct ar9170 *ar = hw->priv; - - spin_lock_bh(&ar->tx_stats_lock); - memcpy(tx_stats, ar->tx_stats, sizeof(tx_stats[0]) * hw->queues); - spin_unlock_bh(&ar->tx_stats_lock); - - return 0; -} - static int ar9170_conf_tx(struct ieee80211_hw *hw, u16 queue, const struct ieee80211_tx_queue_params *param) { @@ -2519,9 +2496,9 @@ static const struct ieee80211_ops ar9170_ops = { .bss_info_changed = ar9170_op_bss_info_changed, .get_tsf = ar9170_op_get_tsf, .set_key = ar9170_set_key, - .sta_notify = ar9170_sta_notify, + .sta_add = ar9170_sta_add, + .sta_remove = ar9170_sta_remove, .get_stats = ar9170_get_stats, - .get_tx_stats = ar9170_get_tx_stats, .ampdu_action = ar9170_ampdu_action, }; diff --git a/drivers/net/wireless/ath/ar9170/usb.c b/drivers/net/wireless/ath/ar9170/usb.c index e0799d924057..0f361186b78f 100644 --- a/drivers/net/wireless/ath/ar9170/usb.c +++ b/drivers/net/wireless/ath/ar9170/usb.c @@ -84,6 +84,8 @@ static struct usb_device_id ar9170_usb_ids[] = { { USB_DEVICE(0x0cde, 0x0023) }, /* Z-Com UB82 ABG */ { USB_DEVICE(0x0cde, 0x0026) }, + /* Sphairon Homelink 1202 */ + { USB_DEVICE(0x0cde, 0x0027) }, /* Arcadyan WN7512 */ { USB_DEVICE(0x083a, 0xf522) }, /* Planex GWUS300 */ diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index 9e05648356fe..71fc960814f0 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -74,7 +74,6 @@ struct ath_common; struct ath_bus_ops { void (*read_cachesize)(struct ath_common *common, int *csz); - void (*cleanup)(struct ath_common *common); bool (*eeprom_read)(struct ath_common *common, u32 off, u16 *data); void (*bt_coex_prep)(struct ath_common *common); }; diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 6a2a96761111..ac67f02e26d8 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -535,13 +535,12 @@ struct ath5k_txq_info { u32 tqi_cbr_period; /* Constant bit rate period */ u32 tqi_cbr_overflow_limit; u32 tqi_burst_time; - u32 tqi_ready_time; /* Not used */ + u32 tqi_ready_time; /* Time queue waits after an event */ }; /* * Transmit packet types. * used on tx control descriptor - * TODO: Use them inside base.c corectly */ enum ath5k_pkt_type { AR5K_PKT_TYPE_NORMAL = 0, @@ -1063,6 +1062,7 @@ struct ath5k_hw { u32 ah_cw_min; u32 ah_cw_max; u32 ah_limit_tx_retries; + u8 ah_coverage_class; /* Antenna Control */ u32 ah_ant_ctl[AR5K_EEPROM_N_MODES][AR5K_ANT_MAX]; @@ -1200,6 +1200,7 @@ extern bool ath5k_eeprom_is_hb63(struct ath5k_hw *ah); /* Protocol Control Unit Functions */ extern int ath5k_hw_set_opmode(struct ath5k_hw *ah); +extern void ath5k_hw_set_coverage_class(struct ath5k_hw *ah, u8 coverage_class); /* BSSID Functions */ extern int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac); extern void ath5k_hw_set_associd(struct ath5k_hw *ah); @@ -1231,6 +1232,10 @@ extern int ath5k_hw_set_ack_timeout(struct ath5k_hw *ah, unsigned int timeout); extern unsigned int ath5k_hw_get_ack_timeout(struct ath5k_hw *ah); extern int ath5k_hw_set_cts_timeout(struct ath5k_hw *ah, unsigned int timeout); extern unsigned int ath5k_hw_get_cts_timeout(struct ath5k_hw *ah); +/* Clock rate related functions */ +unsigned int ath5k_hw_htoclock(struct ath5k_hw *ah, unsigned int usec); +unsigned int ath5k_hw_clocktoh(struct ath5k_hw *ah, unsigned int clock); +unsigned int ath5k_hw_get_clockrate(struct ath5k_hw *ah); /* Key table (WEP) functions */ extern int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry); extern int ath5k_hw_is_key_valid(struct ath5k_hw *ah, u16 entry); @@ -1310,24 +1315,6 @@ extern int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 txpower); * Functions used internaly */ -/* - * Translate usec to hw clock units - * TODO: Half/quarter rate - */ -static inline unsigned int ath5k_hw_htoclock(unsigned int usec, bool turbo) -{ - return turbo ? (usec * 80) : (usec * 40); -} - -/* - * Translate hw clock units to usec - * TODO: Half/quarter rate - */ -static inline unsigned int ath5k_hw_clocktoh(unsigned int clock, bool turbo) -{ - return turbo ? (clock / 80) : (clock / 40); -} - static inline struct ath_common *ath5k_hw_common(struct ath5k_hw *ah) { return &ah->common; diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index e63b7c40d0ee..2468c64d6c12 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -225,9 +225,9 @@ static int ath5k_reset_wake(struct ath5k_softc *sc); static int ath5k_start(struct ieee80211_hw *hw); static void ath5k_stop(struct ieee80211_hw *hw); static int ath5k_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf); + struct ieee80211_vif *vif); static void ath5k_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf); + struct ieee80211_vif *vif); static int ath5k_config(struct ieee80211_hw *hw, u32 changed); static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw, int mc_count, struct dev_addr_list *mc_list); @@ -241,8 +241,6 @@ static int ath5k_set_key(struct ieee80211_hw *hw, struct ieee80211_key_conf *key); static int ath5k_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats); -static int ath5k_get_tx_stats(struct ieee80211_hw *hw, - struct ieee80211_tx_queue_stats *stats); static u64 ath5k_get_tsf(struct ieee80211_hw *hw); static void ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf); static void ath5k_reset_tsf(struct ieee80211_hw *hw); @@ -254,6 +252,8 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw, u32 changes); static void ath5k_sw_scan_start(struct ieee80211_hw *hw); static void ath5k_sw_scan_complete(struct ieee80211_hw *hw); +static void ath5k_set_coverage_class(struct ieee80211_hw *hw, + u8 coverage_class); static const struct ieee80211_ops ath5k_hw_ops = { .tx = ath5k_tx, @@ -267,13 +267,13 @@ static const struct ieee80211_ops ath5k_hw_ops = { .set_key = ath5k_set_key, .get_stats = ath5k_get_stats, .conf_tx = NULL, - .get_tx_stats = ath5k_get_tx_stats, .get_tsf = ath5k_get_tsf, .set_tsf = ath5k_set_tsf, .reset_tsf = ath5k_reset_tsf, .bss_info_changed = ath5k_bss_info_changed, .sw_scan_start = ath5k_sw_scan_start, .sw_scan_complete = ath5k_sw_scan_complete, + .set_coverage_class = ath5k_set_coverage_class, }; /* @@ -1246,6 +1246,29 @@ ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) return 0; } +static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr; + enum ath5k_pkt_type htype; + __le16 fc; + + hdr = (struct ieee80211_hdr *)skb->data; + fc = hdr->frame_control; + + if (ieee80211_is_beacon(fc)) + htype = AR5K_PKT_TYPE_BEACON; + else if (ieee80211_is_probe_resp(fc)) + htype = AR5K_PKT_TYPE_PROBE_RESP; + else if (ieee80211_is_atim(fc)) + htype = AR5K_PKT_TYPE_ATIM; + else if (ieee80211_is_pspoll(fc)) + htype = AR5K_PKT_TYPE_PSPOLL; + else + htype = AR5K_PKT_TYPE_NORMAL; + + return htype; +} + static int ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf, struct ath5k_txq *txq) @@ -1300,7 +1323,8 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf, sc->vif, pktlen, info)); } ret = ah->ah_setup_tx_desc(ah, ds, pktlen, - ieee80211_get_hdrlen_from_skb(skb), AR5K_PKT_TYPE_NORMAL, + ieee80211_get_hdrlen_from_skb(skb), + get_hw_packet_type(skb), (sc->power_level * 2), hw_rate, info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags, @@ -1329,7 +1353,6 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf, spin_lock_bh(&txq->lock); list_add_tail(&bf->list, &txq->q); - sc->tx_stats[txq->qnum].len++; if (txq->link == NULL) /* is this first packet? */ ath5k_hw_set_txdp(ah, txq->qnum, bf->daddr); else /* no, so only link it */ @@ -1513,7 +1536,8 @@ ath5k_beaconq_config(struct ath5k_softc *sc) ret = ath5k_hw_get_tx_queueprops(ah, sc->bhalq, &qi); if (ret) - return ret; + goto err; + if (sc->opmode == NL80211_IFTYPE_AP || sc->opmode == NL80211_IFTYPE_MESH_POINT) { /* @@ -1540,10 +1564,25 @@ ath5k_beaconq_config(struct ath5k_softc *sc) if (ret) { ATH5K_ERR(sc, "%s: unable to update parameters for beacon " "hardware queue!\n", __func__); - return ret; + goto err; } + ret = ath5k_hw_reset_tx_queue(ah, sc->bhalq); /* push to h/w */ + if (ret) + goto err; + + /* reconfigure cabq with ready time to 80% of beacon_interval */ + ret = ath5k_hw_get_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi); + if (ret) + goto err; + + qi.tqi_ready_time = (sc->bintval * 80) / 100; + ret = ath5k_hw_set_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi); + if (ret) + goto err; - return ath5k_hw_reset_tx_queue(ah, sc->bhalq); /* push to h/w */; + ret = ath5k_hw_reset_tx_queue(ah, AR5K_TX_QUEUE_ID_CAB); +err: + return ret; } static void @@ -1562,7 +1601,6 @@ ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq) ath5k_txbuf_free(sc, bf); spin_lock_bh(&sc->txbuflock); - sc->tx_stats[txq->qnum].len--; list_move_tail(&bf->list, &sc->txbuf); sc->txbuf_len++; spin_unlock_bh(&sc->txbuflock); @@ -1992,10 +2030,8 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) } ieee80211_tx_status(sc->hw, skb); - sc->tx_stats[txq->qnum].count++; spin_lock(&sc->txbuflock); - sc->tx_stats[txq->qnum].len--; list_move_tail(&bf->list, &sc->txbuf); sc->txbuf_len++; spin_unlock(&sc->txbuflock); @@ -2773,7 +2809,7 @@ static void ath5k_stop(struct ieee80211_hw *hw) } static int ath5k_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct ath5k_softc *sc = hw->priv; int ret; @@ -2784,22 +2820,22 @@ static int ath5k_add_interface(struct ieee80211_hw *hw, goto end; } - sc->vif = conf->vif; + sc->vif = vif; - switch (conf->type) { + switch (vif->type) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_MONITOR: - sc->opmode = conf->type; + sc->opmode = vif->type; break; default: ret = -EOPNOTSUPP; goto end; } - ath5k_hw_set_lladdr(sc->ah, conf->mac_addr); + ath5k_hw_set_lladdr(sc->ah, vif->addr); ath5k_mode_setup(sc); ret = 0; @@ -2810,13 +2846,13 @@ end: static void ath5k_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct ath5k_softc *sc = hw->priv; u8 mac[ETH_ALEN] = {}; mutex_lock(&sc->lock); - if (sc->vif != conf->vif) + if (sc->vif != vif) goto end; ath5k_hw_set_lladdr(sc->ah, mac); @@ -3097,17 +3133,6 @@ ath5k_get_stats(struct ieee80211_hw *hw, return 0; } -static int -ath5k_get_tx_stats(struct ieee80211_hw *hw, - struct ieee80211_tx_queue_stats *stats) -{ - struct ath5k_softc *sc = hw->priv; - - memcpy(stats, &sc->tx_stats, sizeof(sc->tx_stats)); - - return 0; -} - static u64 ath5k_get_tsf(struct ieee80211_hw *hw) { @@ -3262,3 +3287,22 @@ static void ath5k_sw_scan_complete(struct ieee80211_hw *hw) ath5k_hw_set_ledstate(sc->ah, sc->assoc ? AR5K_LED_ASSOC : AR5K_LED_INIT); } + +/** + * ath5k_set_coverage_class - Set IEEE 802.11 coverage class + * + * @hw: struct ieee80211_hw pointer + * @coverage_class: IEEE 802.11 coverage class number + * + * Mac80211 callback. Sets slot time, ACK timeout and CTS timeout for given + * coverage class. The values are persistent, they are restored after device + * reset. + */ +static void ath5k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class) +{ + struct ath5k_softc *sc = hw->priv; + + mutex_lock(&sc->lock); + ath5k_hw_set_coverage_class(sc->ah, coverage_class); + mutex_unlock(&sc->lock); +} diff --git a/drivers/net/wireless/ath/ath5k/base.h b/drivers/net/wireless/ath/ath5k/base.h index b72338c9bde7..9c2fbf230ce7 100644 --- a/drivers/net/wireless/ath/ath5k/base.h +++ b/drivers/net/wireless/ath/ath5k/base.h @@ -117,7 +117,6 @@ struct ath5k_softc { struct pci_dev *pdev; /* for dma mapping */ void __iomem *iobase; /* address of the device */ struct mutex lock; /* dev-level lock */ - struct ieee80211_tx_queue_stats tx_stats[AR5K_NUM_TX_QUEUES]; struct ieee80211_low_level_stats ll_stats; struct ieee80211_hw *hw; /* IEEE 802.11 common */ struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; diff --git a/drivers/net/wireless/ath/ath5k/led.c b/drivers/net/wireless/ath/ath5k/led.c index 60f547503d75..67aa52e9bf94 100644 --- a/drivers/net/wireless/ath/ath5k/led.c +++ b/drivers/net/wireless/ath/ath5k/led.c @@ -77,6 +77,8 @@ static const struct pci_device_id ath5k_led_devices[] = { { ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137a), ATH_LED(3, 1) }, /* HP Compaq C700 (nitrousnrg@gmail.com) */ { ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137b), ATH_LED(3, 1) }, + /* LiteOn AR5BXB63 (magooz@salug.it) */ + { ATH_SDEVICE(PCI_VENDOR_ID_ATHEROS, 0x3067), ATH_LED(3, 0) }, /* IBM-specific AR5212 (all others) */ { PCI_VDEVICE(ATHEROS, PCI_DEVICE_ID_ATHEROS_AR5212_IBM), ATH_LED(0, 0) }, /* Dell Vostro A860 (shahar@shahar-or.co.il) */ diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c index 64fc1eb9b6d9..aefe84f9c04b 100644 --- a/drivers/net/wireless/ath/ath5k/pcu.c +++ b/drivers/net/wireless/ath/ath5k/pcu.c @@ -187,8 +187,8 @@ unsigned int ath5k_hw_get_ack_timeout(struct ath5k_hw *ah) { ATH5K_TRACE(ah->ah_sc); - return ath5k_hw_clocktoh(AR5K_REG_MS(ath5k_hw_reg_read(ah, - AR5K_TIME_OUT), AR5K_TIME_OUT_ACK), ah->ah_turbo); + return ath5k_hw_clocktoh(ah, AR5K_REG_MS(ath5k_hw_reg_read(ah, + AR5K_TIME_OUT), AR5K_TIME_OUT_ACK)); } /** @@ -200,12 +200,12 @@ unsigned int ath5k_hw_get_ack_timeout(struct ath5k_hw *ah) int ath5k_hw_set_ack_timeout(struct ath5k_hw *ah, unsigned int timeout) { ATH5K_TRACE(ah->ah_sc); - if (ath5k_hw_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_ACK), - ah->ah_turbo) <= timeout) + if (ath5k_hw_clocktoh(ah, AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_ACK)) + <= timeout) return -EINVAL; AR5K_REG_WRITE_BITS(ah, AR5K_TIME_OUT, AR5K_TIME_OUT_ACK, - ath5k_hw_htoclock(timeout, ah->ah_turbo)); + ath5k_hw_htoclock(ah, timeout)); return 0; } @@ -218,8 +218,8 @@ int ath5k_hw_set_ack_timeout(struct ath5k_hw *ah, unsigned int timeout) unsigned int ath5k_hw_get_cts_timeout(struct ath5k_hw *ah) { ATH5K_TRACE(ah->ah_sc); - return ath5k_hw_clocktoh(AR5K_REG_MS(ath5k_hw_reg_read(ah, - AR5K_TIME_OUT), AR5K_TIME_OUT_CTS), ah->ah_turbo); + return ath5k_hw_clocktoh(ah, AR5K_REG_MS(ath5k_hw_reg_read(ah, + AR5K_TIME_OUT), AR5K_TIME_OUT_CTS)); } /** @@ -231,17 +231,97 @@ unsigned int ath5k_hw_get_cts_timeout(struct ath5k_hw *ah) int ath5k_hw_set_cts_timeout(struct ath5k_hw *ah, unsigned int timeout) { ATH5K_TRACE(ah->ah_sc); - if (ath5k_hw_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_CTS), - ah->ah_turbo) <= timeout) + if (ath5k_hw_clocktoh(ah, AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_CTS)) + <= timeout) return -EINVAL; AR5K_REG_WRITE_BITS(ah, AR5K_TIME_OUT, AR5K_TIME_OUT_CTS, - ath5k_hw_htoclock(timeout, ah->ah_turbo)); + ath5k_hw_htoclock(ah, timeout)); return 0; } /** + * ath5k_hw_htoclock - Translate usec to hw clock units + * + * @ah: The &struct ath5k_hw + * @usec: value in microseconds + */ +unsigned int ath5k_hw_htoclock(struct ath5k_hw *ah, unsigned int usec) +{ + return usec * ath5k_hw_get_clockrate(ah); +} + +/** + * ath5k_hw_clocktoh - Translate hw clock units to usec + * @clock: value in hw clock units + */ +unsigned int ath5k_hw_clocktoh(struct ath5k_hw *ah, unsigned int clock) +{ + return clock / ath5k_hw_get_clockrate(ah); +} + +/** + * ath5k_hw_get_clockrate - Get the clock rate for current mode + * + * @ah: The &struct ath5k_hw + */ +unsigned int ath5k_hw_get_clockrate(struct ath5k_hw *ah) +{ + struct ieee80211_channel *channel = ah->ah_current_channel; + int clock; + + if (channel->hw_value & CHANNEL_5GHZ) + clock = 40; /* 802.11a */ + else if (channel->hw_value & CHANNEL_CCK) + clock = 22; /* 802.11b */ + else + clock = 44; /* 802.11g */ + + /* Clock rate in turbo modes is twice the normal rate */ + if (channel->hw_value & CHANNEL_TURBO) + clock *= 2; + + return clock; +} + +/** + * ath5k_hw_get_default_slottime - Get the default slot time for current mode + * + * @ah: The &struct ath5k_hw + */ +unsigned int ath5k_hw_get_default_slottime(struct ath5k_hw *ah) +{ + struct ieee80211_channel *channel = ah->ah_current_channel; + + if (channel->hw_value & CHANNEL_TURBO) + return 6; /* both turbo modes */ + + if (channel->hw_value & CHANNEL_CCK) + return 20; /* 802.11b */ + + return 9; /* 802.11 a/g */ +} + +/** + * ath5k_hw_get_default_sifs - Get the default SIFS for current mode + * + * @ah: The &struct ath5k_hw + */ +unsigned int ath5k_hw_get_default_sifs(struct ath5k_hw *ah) +{ + struct ieee80211_channel *channel = ah->ah_current_channel; + + if (channel->hw_value & CHANNEL_TURBO) + return 8; /* both turbo modes */ + + if (channel->hw_value & CHANNEL_5GHZ) + return 16; /* 802.11a */ + + return 10; /* 802.11 b/g */ +} + +/** * ath5k_hw_set_lladdr - Set station id * * @ah: The &struct ath5k_hw @@ -1050,3 +1130,24 @@ int ath5k_hw_set_key_lladdr(struct ath5k_hw *ah, u16 entry, const u8 *mac) return 0; } +/** + * ath5k_hw_set_coverage_class - Set IEEE 802.11 coverage class + * + * @ah: The &struct ath5k_hw + * @coverage_class: IEEE 802.11 coverage class number + * + * Sets slot time, ACK timeout and CTS timeout for given coverage class. + */ +void ath5k_hw_set_coverage_class(struct ath5k_hw *ah, u8 coverage_class) +{ + /* As defined by IEEE 802.11-2007 17.3.8.6 */ + int slot_time = ath5k_hw_get_default_slottime(ah) + 3 * coverage_class; + int ack_timeout = ath5k_hw_get_default_sifs(ah) + slot_time; + int cts_timeout = ack_timeout; + + ath5k_hw_set_slot_time(ah, slot_time); + ath5k_hw_set_ack_timeout(ah, ack_timeout); + ath5k_hw_set_cts_timeout(ah, cts_timeout); + + ah->ah_coverage_class = coverage_class; +} diff --git a/drivers/net/wireless/ath/ath5k/qcu.c b/drivers/net/wireless/ath/ath5k/qcu.c index eeebb9aef206..9122a8556f45 100644 --- a/drivers/net/wireless/ath/ath5k/qcu.c +++ b/drivers/net/wireless/ath/ath5k/qcu.c @@ -408,12 +408,13 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) break; case AR5K_TX_QUEUE_CAB: + /* XXX: use BCN_SENT_GT, if we can figure out how */ AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), - AR5K_QCU_MISC_FRSHED_BCN_SENT_GT | + AR5K_QCU_MISC_FRSHED_DBA_GT | AR5K_QCU_MISC_CBREXP_DIS | AR5K_QCU_MISC_CBREXP_BCN_DIS); - ath5k_hw_reg_write(ah, ((AR5K_TUNE_BEACON_INTERVAL - + ath5k_hw_reg_write(ah, ((tq->tqi_ready_time - (AR5K_TUNE_SW_BEACON_RESP - AR5K_TUNE_DMA_BEACON_RESP) - AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF) * 1024) | @@ -520,12 +521,16 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) */ unsigned int ath5k_hw_get_slot_time(struct ath5k_hw *ah) { + unsigned int slot_time_clock; + ATH5K_TRACE(ah->ah_sc); + if (ah->ah_version == AR5K_AR5210) - return ath5k_hw_clocktoh(ath5k_hw_reg_read(ah, - AR5K_SLOT_TIME) & 0xffff, ah->ah_turbo); + slot_time_clock = ath5k_hw_reg_read(ah, AR5K_SLOT_TIME); else - return ath5k_hw_reg_read(ah, AR5K_DCU_GBL_IFS_SLOT) & 0xffff; + slot_time_clock = ath5k_hw_reg_read(ah, AR5K_DCU_GBL_IFS_SLOT); + + return ath5k_hw_clocktoh(ah, slot_time_clock & 0xffff); } /* @@ -533,15 +538,17 @@ unsigned int ath5k_hw_get_slot_time(struct ath5k_hw *ah) */ int ath5k_hw_set_slot_time(struct ath5k_hw *ah, unsigned int slot_time) { + u32 slot_time_clock = ath5k_hw_htoclock(ah, slot_time); + ATH5K_TRACE(ah->ah_sc); - if (slot_time < AR5K_SLOT_TIME_9 || slot_time > AR5K_SLOT_TIME_MAX) + + if (slot_time < 6 || slot_time_clock > AR5K_SLOT_TIME_MAX) return -EINVAL; if (ah->ah_version == AR5K_AR5210) - ath5k_hw_reg_write(ah, ath5k_hw_htoclock(slot_time, - ah->ah_turbo), AR5K_SLOT_TIME); + ath5k_hw_reg_write(ah, slot_time_clock, AR5K_SLOT_TIME); else - ath5k_hw_reg_write(ah, slot_time, AR5K_DCU_GBL_IFS_SLOT); + ath5k_hw_reg_write(ah, slot_time_clock, AR5K_DCU_GBL_IFS_SLOT); return 0; } diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c index 62954fc77869..a35a7db0fc4c 100644 --- a/drivers/net/wireless/ath/ath5k/reset.c +++ b/drivers/net/wireless/ath/ath5k/reset.c @@ -60,12 +60,11 @@ static inline int ath5k_hw_write_ofdm_timings(struct ath5k_hw *ah, !(channel->hw_value & CHANNEL_OFDM)); /* Get coefficient - * ALGO: coef = (5 * clock * carrier_freq) / 2) + * ALGO: coef = (5 * clock / carrier_freq) / 2 * we scale coef by shifting clock value by 24 for * better precision since we use integers */ /* TODO: Half/quarter rate */ - clock = ath5k_hw_htoclock(1, channel->hw_value & CHANNEL_TURBO); - + clock = (channel->hw_value & CHANNEL_TURBO) ? 80 : 40; coef_scaled = ((5 * (clock << 24)) / 2) / channel->center_freq; /* Get exponent @@ -1317,6 +1316,10 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, /* Restore antenna mode */ ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode); + /* Restore slot time and ACK timeouts */ + if (ah->ah_coverage_class > 0) + ath5k_hw_set_coverage_class(ah, ah->ah_coverage_class); + /* * Configure QCUs/DCUs */ @@ -1371,8 +1374,9 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, * Set clocks to 32KHz operation and use an * external 32KHz crystal when sleeping if one * exists */ - if (ah->ah_version == AR5K_AR5212) - ath5k_hw_set_sleep_clock(ah, true); + if (ah->ah_version == AR5K_AR5212 && + ah->ah_op_mode != NL80211_IFTYPE_AP) + ath5k_hw_set_sleep_clock(ah, true); /* * Disable beacons and reset the register diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 4985b2b1b0a9..6b50d5eb9ec3 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -1,4 +1,6 @@ ath9k-y += beacon.o \ + gpio.o \ + init.o \ main.o \ recv.o \ xmit.o \ diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c index 329e6bc137ab..ca4994f13151 100644 --- a/drivers/net/wireless/ath/ath9k/ahb.c +++ b/drivers/net/wireless/ath/ath9k/ahb.c @@ -27,12 +27,6 @@ static void ath_ahb_read_cachesize(struct ath_common *common, int *csz) *csz = L1_CACHE_BYTES >> 2; } -static void ath_ahb_cleanup(struct ath_common *common) -{ - struct ath_softc *sc = (struct ath_softc *)common->priv; - iounmap(sc->mem); -} - static bool ath_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data) { struct ath_softc *sc = (struct ath_softc *)common->priv; @@ -54,8 +48,6 @@ static bool ath_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data) static struct ath_bus_ops ath_ahb_bus_ops = { .read_cachesize = ath_ahb_read_cachesize, - .cleanup = ath_ahb_cleanup, - .eeprom_read = ath_ahb_eeprom_read, }; @@ -121,16 +113,19 @@ static int ath_ahb_probe(struct platform_device *pdev) sc->mem = mem; sc->irq = irq; - ret = ath_init_device(AR5416_AR9100_DEVID, sc, 0x0, &ath_ahb_bus_ops); + /* Will be cleared in ath9k_start() */ + sc->sc_flags |= SC_OP_INVALID; + + ret = request_irq(irq, ath_isr, IRQF_SHARED, "ath9k", sc); if (ret) { - dev_err(&pdev->dev, "failed to initialize device\n"); + dev_err(&pdev->dev, "request_irq failed\n"); goto err_free_hw; } - ret = request_irq(irq, ath_isr, IRQF_SHARED, "ath9k", sc); + ret = ath9k_init_device(AR5416_AR9100_DEVID, sc, 0x0, &ath_ahb_bus_ops); if (ret) { - dev_err(&pdev->dev, "request_irq failed\n"); - goto err_detach; + dev_err(&pdev->dev, "failed to initialize device\n"); + goto err_irq; } ah = sc->sc_ah; @@ -143,8 +138,8 @@ static int ath_ahb_probe(struct platform_device *pdev) return 0; - err_detach: - ath_detach(sc); + err_irq: + free_irq(irq, sc); err_free_hw: ieee80211_free_hw(hw); platform_set_drvdata(pdev, NULL); @@ -161,8 +156,12 @@ static int ath_ahb_remove(struct platform_device *pdev) if (hw) { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; + void __iomem *mem = sc->mem; - ath_cleanup(sc); + ath9k_deinit_device(sc); + free_irq(sc->irq, sc); + ieee80211_free_hw(sc->hw); + iounmap(mem); platform_set_drvdata(pdev, NULL); } diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 1597a42731ed..83c7ea4c007f 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -267,6 +267,7 @@ void ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid, u16 *ssn); void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid); void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid); +void ath9k_enable_ps(struct ath_softc *sc); /********/ /* VIFs */ @@ -341,6 +342,12 @@ int ath_beaconq_config(struct ath_softc *sc); #define ATH_LONG_CALINTERVAL 30000 /* 30 seconds */ #define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */ +void ath_ani_calibrate(unsigned long data); + +/**********/ +/* BTCOEX */ +/**********/ + /* Defines the BT AR_BT_COEX_WGHT used */ enum ath_stomp_type { ATH_BTCOEX_NO_STOMP, @@ -358,9 +365,14 @@ struct ath_btcoex { int bt_stomp_type; /* Types of BT stomping */ u32 btcoex_no_stomp; /* in usec */ u32 btcoex_period; /* in usec */ + u32 btscan_no_stomp; /* in usec */ struct ath_gen_timer *no_stomp_timer; /* Timer for no BT stomping */ }; +int ath_init_btcoex_timer(struct ath_softc *sc); +void ath9k_btcoex_timer_resume(struct ath_softc *sc); +void ath9k_btcoex_timer_pause(struct ath_softc *sc); + /********************/ /* LED Control */ /********************/ @@ -385,6 +397,9 @@ struct ath_led { bool registered; }; +void ath_init_leds(struct ath_softc *sc); +void ath_deinit_leds(struct ath_softc *sc); + /********************/ /* Main driver core */ /********************/ @@ -403,26 +418,29 @@ struct ath_led { #define ATH_TXPOWER_MAX 100 /* .5 dBm units */ #define ATH_RATE_DUMMY_MARKER 0 -#define SC_OP_INVALID BIT(0) -#define SC_OP_BEACONS BIT(1) -#define SC_OP_RXAGGR BIT(2) -#define SC_OP_TXAGGR BIT(3) -#define SC_OP_FULL_RESET BIT(4) -#define SC_OP_PREAMBLE_SHORT BIT(5) -#define SC_OP_PROTECT_ENABLE BIT(6) -#define SC_OP_RXFLUSH BIT(7) -#define SC_OP_LED_ASSOCIATED BIT(8) -#define SC_OP_WAIT_FOR_BEACON BIT(12) -#define SC_OP_LED_ON BIT(13) -#define SC_OP_SCANNING BIT(14) -#define SC_OP_TSF_RESET BIT(15) -#define SC_OP_WAIT_FOR_CAB BIT(16) -#define SC_OP_WAIT_FOR_PSPOLL_DATA BIT(17) -#define SC_OP_WAIT_FOR_TX_ACK BIT(18) -#define SC_OP_BEACON_SYNC BIT(19) -#define SC_OP_BT_PRIORITY_DETECTED BIT(21) -#define SC_OP_NULLFUNC_COMPLETED BIT(22) -#define SC_OP_PS_ENABLED BIT(23) +#define SC_OP_INVALID BIT(0) +#define SC_OP_BEACONS BIT(1) +#define SC_OP_RXAGGR BIT(2) +#define SC_OP_TXAGGR BIT(3) +#define SC_OP_FULL_RESET BIT(4) +#define SC_OP_PREAMBLE_SHORT BIT(5) +#define SC_OP_PROTECT_ENABLE BIT(6) +#define SC_OP_RXFLUSH BIT(7) +#define SC_OP_LED_ASSOCIATED BIT(8) +#define SC_OP_LED_ON BIT(9) +#define SC_OP_SCANNING BIT(10) +#define SC_OP_TSF_RESET BIT(11) +#define SC_OP_BT_PRIORITY_DETECTED BIT(12) +#define SC_OP_BT_SCAN BIT(13) + +/* Powersave flags */ +#define PS_WAIT_FOR_BEACON BIT(0) +#define PS_WAIT_FOR_CAB BIT(1) +#define PS_WAIT_FOR_PSPOLL_DATA BIT(2) +#define PS_WAIT_FOR_TX_ACK BIT(3) +#define PS_BEACON_SYNC BIT(4) +#define PS_NULLFUNC_COMPLETED BIT(5) +#define PS_ENABLED BIT(6) struct ath_wiphy; struct ath_rate_table; @@ -453,16 +471,17 @@ struct ath_softc { int irq; spinlock_t sc_resetlock; spinlock_t sc_serial_rw; - spinlock_t ani_lock; spinlock_t sc_pm_lock; struct mutex mutex; u32 intrstatus; u32 sc_flags; /* SC_OP_* */ + u16 ps_flags; /* PS_* */ u16 curtxpow; u8 nbcnvifs; u16 nvifs; bool ps_enabled; + bool ps_idle; unsigned long ps_usecount; enum ath9k_int imask; @@ -509,6 +528,7 @@ struct ath_wiphy { int chan_is_ht; }; +void ath9k_tasklet(unsigned long data); int ath_reset(struct ath_softc *sc, bool retry_tx); int ath_get_hal_qnum(u16 queue, struct ath_softc *sc); int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc); @@ -519,21 +539,16 @@ static inline void ath_read_cachesize(struct ath_common *common, int *csz) common->bus_ops->read_cachesize(common, csz); } -static inline void ath_bus_cleanup(struct ath_common *common) -{ - common->bus_ops->cleanup(common); -} - extern struct ieee80211_ops ath9k_ops; +extern int modparam_nohwcrypt; irqreturn_t ath_isr(int irq, void *dev); -void ath_cleanup(struct ath_softc *sc); -int ath_init_device(u16 devid, struct ath_softc *sc, u16 subsysid, +int ath9k_init_device(u16 devid, struct ath_softc *sc, u16 subsysid, const struct ath_bus_ops *bus_ops); -void ath_detach(struct ath_softc *sc); +void ath9k_deinit_device(struct ath_softc *sc); const char *ath_mac_bb_name(u32 mac_bb_version); const char *ath_rf_name(u16 rf_version); -void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw); +void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw); void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw, struct ath9k_channel *ichan); void ath_update_chainmask(struct ath_softc *sc, int is_ht); @@ -542,6 +557,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw); void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw); +bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode); #ifdef CONFIG_PCI int ath_pci_init(void); @@ -583,4 +599,8 @@ void ath_mac80211_stop_queue(struct ath_softc *sc, u16 skb_queue); void ath_mac80211_start_queue(struct ath_softc *sc, u16 skb_queue); int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype); + +void ath_start_rfkill_poll(struct ath_softc *sc); +extern void ath9k_rfkill_poll_state(struct ieee80211_hw *hw); + #endif /* ATH9K_H */ diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 1660ef17aaf5..b4a31a43a62c 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -62,7 +62,7 @@ int ath_beaconq_config(struct ath_softc *sc) * Beacons are always sent out at the lowest rate, and are not retried. */ static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp, - struct ath_buf *bf) + struct ath_buf *bf, int rateidx) { struct sk_buff *skb = bf->bf_mpdu; struct ath_hw *ah = sc->sc_ah; @@ -96,9 +96,9 @@ static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp, ds->ds_data = bf->bf_buf_addr; sband = &sc->sbands[common->hw->conf.channel->band]; - rate = sband->bitrates[0].hw_value; + rate = sband->bitrates[rateidx].hw_value; if (sc->sc_flags & SC_OP_PREAMBLE_SHORT) - rate |= sband->bitrates[0].hw_value_short; + rate |= sband->bitrates[rateidx].hw_value_short; ath9k_hw_set11n_txdesc(ah, ds, skb->len + FCS_LEN, ATH9K_PKT_TYPE_BEACON, @@ -206,7 +206,7 @@ static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw, } } - ath_beacon_setup(sc, avp, bf); + ath_beacon_setup(sc, avp, bf, info->control.rates[0].idx); while (skb) { ath_tx_cabq(hw, skb); @@ -237,7 +237,7 @@ static void ath_beacon_start_adhoc(struct ath_softc *sc, bf = avp->av_bcbuf; skb = bf->bf_mpdu; - ath_beacon_setup(sc, avp, bf); + ath_beacon_setup(sc, avp, bf, 0); /* NB: caller is known to have already stopped tx dma */ ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bf->bf_daddr); @@ -480,7 +480,8 @@ void ath_beacon_tasklet(unsigned long data) sc->beacon.updateslot = COMMIT; /* commit next beacon */ sc->beacon.slotupdate = slot; } else if (sc->beacon.updateslot == COMMIT && sc->beacon.slotupdate == slot) { - ath9k_hw_setslottime(sc->sc_ah, sc->beacon.slottime); + ah->slottime = sc->beacon.slottime; + ath9k_hw_init_global_settings(ah); sc->beacon.updateslot = OK; } if (bfaddr != 0) { @@ -525,16 +526,13 @@ static void ath_beacon_config_ap(struct ath_softc *sc, { u32 nexttbtt, intval; - /* Configure the timers only when the TSF has to be reset */ - - if (!(sc->sc_flags & SC_OP_TSF_RESET)) - return; - /* NB: the beacon interval is kept internally in TU's */ intval = conf->beacon_interval & ATH9K_BEACON_PERIOD; intval /= ATH_BCBUF; /* for staggered beacons */ nexttbtt = intval; - intval |= ATH9K_BEACON_RESET_TSF; + + if (sc->sc_flags & SC_OP_TSF_RESET) + intval |= ATH9K_BEACON_RESET_TSF; /* * In AP mode we enable the beacon timers and SWBA interrupts to @@ -576,6 +574,13 @@ static void ath_beacon_config_sta(struct ath_softc *sc, u64 tsf; int num_beacons, offset, dtim_dec_count, cfp_dec_count; + /* No need to configure beacon if we are not associated */ + if (!common->curaid) { + ath_print(common, ATH_DBG_BEACON, + "STA is not yet associated..skipping beacon config\n"); + return; + } + memset(&bs, 0, sizeof(bs)); intval = conf->beacon_interval & ATH9K_BEACON_PERIOD; @@ -738,7 +743,6 @@ void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif) enum nl80211_iftype iftype; /* Setup the beacon configuration parameters */ - if (vif) { struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; diff --git a/drivers/net/wireless/ath/ath9k/btcoex.h b/drivers/net/wireless/ath/ath9k/btcoex.h index 1ba31a73317c..1ee5a15ccbb1 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.h +++ b/drivers/net/wireless/ath/ath9k/btcoex.h @@ -25,10 +25,12 @@ #define ATH_BTCOEX_DEF_BT_PERIOD 45 #define ATH_BTCOEX_DEF_DUTY_CYCLE 55 +#define ATH_BTCOEX_BTSCAN_DUTY_CYCLE 90 #define ATH_BTCOEX_BMISS_THRESH 50 #define ATH_BT_PRIORITY_TIME_THRESHOLD 1000 /* ms */ #define ATH_BT_CNT_THRESHOLD 3 +#define ATH_BT_CNT_SCAN_THRESHOLD 15 enum ath_btcoex_scheme { ATH_BTCOEX_CFG_NONE, diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index b66f72dbf7b9..42d2a506845a 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -75,17 +75,24 @@ static const struct file_operations fops_debug = { #endif +#define DMA_BUF_LEN 1024 + static ssize_t read_file_dma(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_hw *ah = sc->sc_ah; - char buf[1024]; + char *buf; + int retval; unsigned int len = 0; u32 val[ATH9K_NUM_DMA_DEBUG_REGS]; int i, qcuOffset = 0, dcuOffset = 0; u32 *qcuBase = &val[0], *dcuBase = &val[4]; + buf = kmalloc(DMA_BUF_LEN, GFP_KERNEL); + if (!buf) + return 0; + ath9k_ps_wakeup(sc); REG_WRITE_D(ah, AR_MACMISC, @@ -93,20 +100,20 @@ static ssize_t read_file_dma(struct file *file, char __user *user_buf, (AR_MACMISC_MISC_OBS_BUS_1 << AR_MACMISC_MISC_OBS_BUS_MSB_S))); - len += snprintf(buf + len, sizeof(buf) - len, + len += snprintf(buf + len, DMA_BUF_LEN - len, "Raw DMA Debug values:\n"); for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) { if (i % 4 == 0) - len += snprintf(buf + len, sizeof(buf) - len, "\n"); + len += snprintf(buf + len, DMA_BUF_LEN - len, "\n"); val[i] = REG_READ_D(ah, AR_DMADBG_0 + (i * sizeof(u32))); - len += snprintf(buf + len, sizeof(buf) - len, "%d: %08x ", + len += snprintf(buf + len, DMA_BUF_LEN - len, "%d: %08x ", i, val[i]); } - len += snprintf(buf + len, sizeof(buf) - len, "\n\n"); - len += snprintf(buf + len, sizeof(buf) - len, + len += snprintf(buf + len, DMA_BUF_LEN - len, "\n\n"); + len += snprintf(buf + len, DMA_BUF_LEN - len, "Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n"); for (i = 0; i < ATH9K_NUM_QUEUES; i++, qcuOffset += 4, dcuOffset += 5) { @@ -120,7 +127,7 @@ static ssize_t read_file_dma(struct file *file, char __user *user_buf, dcuBase++; } - len += snprintf(buf + len, sizeof(buf) - len, + len += snprintf(buf + len, DMA_BUF_LEN - len, "%2d %2x %1x %2x %2x\n", i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset, (*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset + 3), @@ -128,35 +135,37 @@ static ssize_t read_file_dma(struct file *file, char __user *user_buf, (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset); } - len += snprintf(buf + len, sizeof(buf) - len, "\n"); + len += snprintf(buf + len, DMA_BUF_LEN - len, "\n"); - len += snprintf(buf + len, sizeof(buf) - len, + len += snprintf(buf + len, DMA_BUF_LEN - len, "qcu_stitch state: %2x qcu_fetch state: %2x\n", (val[3] & 0x003c0000) >> 18, (val[3] & 0x03c00000) >> 22); - len += snprintf(buf + len, sizeof(buf) - len, + len += snprintf(buf + len, DMA_BUF_LEN - len, "qcu_complete state: %2x dcu_complete state: %2x\n", (val[3] & 0x1c000000) >> 26, (val[6] & 0x3)); - len += snprintf(buf + len, sizeof(buf) - len, + len += snprintf(buf + len, DMA_BUF_LEN - len, "dcu_arb state: %2x dcu_fp state: %2x\n", (val[5] & 0x06000000) >> 25, (val[5] & 0x38000000) >> 27); - len += snprintf(buf + len, sizeof(buf) - len, + len += snprintf(buf + len, DMA_BUF_LEN - len, "chan_idle_dur: %3d chan_idle_dur_valid: %1d\n", (val[6] & 0x000003fc) >> 2, (val[6] & 0x00000400) >> 10); - len += snprintf(buf + len, sizeof(buf) - len, + len += snprintf(buf + len, DMA_BUF_LEN - len, "txfifo_valid_0: %1d txfifo_valid_1: %1d\n", (val[6] & 0x00000800) >> 11, (val[6] & 0x00001000) >> 12); - len += snprintf(buf + len, sizeof(buf) - len, + len += snprintf(buf + len, DMA_BUF_LEN - len, "txfifo_dcu_num_0: %2d txfifo_dcu_num_1: %2d\n", (val[6] & 0x0001e000) >> 13, (val[6] & 0x001e0000) >> 17); - len += snprintf(buf + len, sizeof(buf) - len, "pcu observe: 0x%x \n", + len += snprintf(buf + len, DMA_BUF_LEN - len, "pcu observe: 0x%x \n", REG_READ_D(ah, AR_OBS_BUS_1)); - len += snprintf(buf + len, sizeof(buf) - len, + len += snprintf(buf + len, DMA_BUF_LEN - len, "AR_CR: 0x%x \n", REG_READ_D(ah, AR_CR)); ath9k_ps_restore(sc); - return simple_read_from_buffer(user_buf, count, ppos, buf, len); + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + return retval; } static const struct file_operations fops_dma = { @@ -289,23 +298,49 @@ static ssize_t read_file_rcstat(struct file *file, char __user *user_buf, if (sc->cur_rate_table == NULL) return 0; - max = 80 + sc->cur_rate_table->rate_cnt * 64; + max = 80 + sc->cur_rate_table->rate_cnt * 1024; buf = kmalloc(max + 1, GFP_KERNEL); if (buf == NULL) return 0; buf[max] = 0; - len += sprintf(buf, "%5s %15s %8s %9s %3s\n\n", "Rate", "Success", - "Retries", "XRetries", "PER"); + len += sprintf(buf, "%6s %6s %6s " + "%10s %10s %10s %10s\n", + "HT", "MCS", "Rate", + "Success", "Retries", "XRetries", "PER"); for (i = 0; i < sc->cur_rate_table->rate_cnt; i++) { u32 ratekbps = sc->cur_rate_table->info[i].ratekbps; struct ath_rc_stats *stats = &sc->debug.stats.rcstats[i]; + char mcs[5]; + char htmode[5]; + int used_mcs = 0, used_htmode = 0; + + if (WLAN_RC_PHY_HT(sc->cur_rate_table->info[i].phy)) { + used_mcs = snprintf(mcs, 5, "%d", + sc->cur_rate_table->info[i].ratecode); + + if (WLAN_RC_PHY_40(sc->cur_rate_table->info[i].phy)) + used_htmode = snprintf(htmode, 5, "HT40"); + else if (WLAN_RC_PHY_20(sc->cur_rate_table->info[i].phy)) + used_htmode = snprintf(htmode, 5, "HT20"); + else + used_htmode = snprintf(htmode, 5, "????"); + } + + mcs[used_mcs] = '\0'; + htmode[used_htmode] = '\0'; len += snprintf(buf + len, max - len, - "%3u.%d: %8u %8u %8u %8u\n", ratekbps / 1000, - (ratekbps % 1000) / 100, stats->success, - stats->retries, stats->xretries, + "%6s %6s %3u.%d: " + "%10u %10u %10u %10u\n", + htmode, + mcs, + ratekbps / 1000, + (ratekbps % 1000) / 100, + stats->success, + stats->retries, + stats->xretries, stats->per); } @@ -554,6 +589,116 @@ static const struct file_operations fops_xmit = { .owner = THIS_MODULE }; +static ssize_t read_file_recv(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define PHY_ERR(s, p) \ + len += snprintf(buf + len, size - len, "%18s : %10u\n", s, \ + sc->debug.stats.rxstats.phy_err_stats[p]); + + struct ath_softc *sc = file->private_data; + char *buf; + unsigned int len = 0, size = 1152; + ssize_t retval = 0; + + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return 0; + + len += snprintf(buf + len, size - len, + "%18s : %10u\n", "CRC ERR", + sc->debug.stats.rxstats.crc_err); + len += snprintf(buf + len, size - len, + "%18s : %10u\n", "DECRYPT CRC ERR", + sc->debug.stats.rxstats.decrypt_crc_err); + len += snprintf(buf + len, size - len, + "%18s : %10u\n", "PHY ERR", + sc->debug.stats.rxstats.phy_err); + len += snprintf(buf + len, size - len, + "%18s : %10u\n", "MIC ERR", + sc->debug.stats.rxstats.mic_err); + len += snprintf(buf + len, size - len, + "%18s : %10u\n", "PRE-DELIM CRC ERR", + sc->debug.stats.rxstats.pre_delim_crc_err); + len += snprintf(buf + len, size - len, + "%18s : %10u\n", "POST-DELIM CRC ERR", + sc->debug.stats.rxstats.post_delim_crc_err); + len += snprintf(buf + len, size - len, + "%18s : %10u\n", "DECRYPT BUSY ERR", + sc->debug.stats.rxstats.decrypt_busy_err); + + PHY_ERR("UNDERRUN", ATH9K_PHYERR_UNDERRUN); + PHY_ERR("TIMING", ATH9K_PHYERR_TIMING); + PHY_ERR("PARITY", ATH9K_PHYERR_PARITY); + PHY_ERR("RATE", ATH9K_PHYERR_RATE); + PHY_ERR("LENGTH", ATH9K_PHYERR_LENGTH); + PHY_ERR("RADAR", ATH9K_PHYERR_RADAR); + PHY_ERR("SERVICE", ATH9K_PHYERR_SERVICE); + PHY_ERR("TOR", ATH9K_PHYERR_TOR); + PHY_ERR("OFDM-TIMING", ATH9K_PHYERR_OFDM_TIMING); + PHY_ERR("OFDM-SIGNAL-PARITY", ATH9K_PHYERR_OFDM_SIGNAL_PARITY); + PHY_ERR("OFDM-RATE", ATH9K_PHYERR_OFDM_RATE_ILLEGAL); + PHY_ERR("OFDM-LENGTH", ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL); + PHY_ERR("OFDM-POWER-DROP", ATH9K_PHYERR_OFDM_POWER_DROP); + PHY_ERR("OFDM-SERVICE", ATH9K_PHYERR_OFDM_SERVICE); + PHY_ERR("OFDM-RESTART", ATH9K_PHYERR_OFDM_RESTART); + PHY_ERR("FALSE-RADAR-EXT", ATH9K_PHYERR_FALSE_RADAR_EXT); + PHY_ERR("CCK-TIMING", ATH9K_PHYERR_CCK_TIMING); + PHY_ERR("CCK-HEADER-CRC", ATH9K_PHYERR_CCK_HEADER_CRC); + PHY_ERR("CCK-RATE", ATH9K_PHYERR_CCK_RATE_ILLEGAL); + PHY_ERR("CCK-SERVICE", ATH9K_PHYERR_CCK_SERVICE); + PHY_ERR("CCK-RESTART", ATH9K_PHYERR_CCK_RESTART); + PHY_ERR("CCK-LENGTH", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL); + PHY_ERR("CCK-POWER-DROP", ATH9K_PHYERR_CCK_POWER_DROP); + PHY_ERR("HT-CRC", ATH9K_PHYERR_HT_CRC_ERROR); + PHY_ERR("HT-LENGTH", ATH9K_PHYERR_HT_LENGTH_ILLEGAL); + PHY_ERR("HT-RATE", ATH9K_PHYERR_HT_RATE_ILLEGAL); + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; + +#undef PHY_ERR +} + +void ath_debug_stat_rx(struct ath_softc *sc, struct ath_buf *bf) +{ +#define RX_STAT_INC(c) sc->debug.stats.rxstats.c++ +#define RX_PHY_ERR_INC(c) sc->debug.stats.rxstats.phy_err_stats[c]++ + + struct ath_desc *ds = bf->bf_desc; + u32 phyerr; + + if (ds->ds_rxstat.rs_status & ATH9K_RXERR_CRC) + RX_STAT_INC(crc_err); + if (ds->ds_rxstat.rs_status & ATH9K_RXERR_DECRYPT) + RX_STAT_INC(decrypt_crc_err); + if (ds->ds_rxstat.rs_status & ATH9K_RXERR_MIC) + RX_STAT_INC(mic_err); + if (ds->ds_rxstat.rs_status & ATH9K_RX_DELIM_CRC_PRE) + RX_STAT_INC(pre_delim_crc_err); + if (ds->ds_rxstat.rs_status & ATH9K_RX_DELIM_CRC_POST) + RX_STAT_INC(post_delim_crc_err); + if (ds->ds_rxstat.rs_status & ATH9K_RX_DECRYPT_BUSY) + RX_STAT_INC(decrypt_busy_err); + + if (ds->ds_rxstat.rs_status & ATH9K_RXERR_PHY) { + RX_STAT_INC(phy_err); + phyerr = ds->ds_rxstat.rs_phyerr & 0x24; + RX_PHY_ERR_INC(phyerr); + } + +#undef RX_STAT_INC +#undef RX_PHY_ERR_INC +} + +static const struct file_operations fops_recv = { + .read = read_file_recv, + .open = ath9k_debugfs_open, + .owner = THIS_MODULE +}; + int ath9k_init_debug(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); @@ -606,6 +751,13 @@ int ath9k_init_debug(struct ath_hw *ah) if (!sc->debug.debugfs_xmit) goto err; + sc->debug.debugfs_recv = debugfs_create_file("recv", + S_IRUSR, + sc->debug.debugfs_phy, + sc, &fops_recv); + if (!sc->debug.debugfs_recv) + goto err; + return 0; err: ath9k_exit_debug(ah); @@ -617,6 +769,7 @@ void ath9k_exit_debug(struct ath_hw *ah) struct ath_common *common = ath9k_hw_common(ah); struct ath_softc *sc = (struct ath_softc *) common->priv; + debugfs_remove(sc->debug.debugfs_recv); debugfs_remove(sc->debug.debugfs_xmit); debugfs_remove(sc->debug.debugfs_wiphy); debugfs_remove(sc->debug.debugfs_rcstat); diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 536663e3ee11..86780e68b31e 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -116,10 +116,35 @@ struct ath_tx_stats { u32 delim_underrun; }; +/** + * struct ath_rx_stats - RX Statistics + * @crc_err: No. of frames with incorrect CRC value + * @decrypt_crc_err: No. of frames whose CRC check failed after + decryption process completed + * @phy_err: No. of frames whose reception failed because the PHY + encountered an error + * @mic_err: No. of frames with incorrect TKIP MIC verification failure + * @pre_delim_crc_err: Pre-Frame delimiter CRC error detections + * @post_delim_crc_err: Post-Frame delimiter CRC error detections + * @decrypt_busy_err: Decryption interruptions counter + * @phy_err_stats: Individual PHY error statistics + */ +struct ath_rx_stats { + u32 crc_err; + u32 decrypt_crc_err; + u32 phy_err; + u32 mic_err; + u32 pre_delim_crc_err; + u32 post_delim_crc_err; + u32 decrypt_busy_err; + u32 phy_err_stats[ATH9K_PHYERR_MAX]; +}; + struct ath_stats { struct ath_interrupt_stats istats; struct ath_rc_stats rcstats[RATE_TABLE_SIZE]; struct ath_tx_stats txstats[ATH9K_NUM_TX_QUEUES]; + struct ath_rx_stats rxstats; }; struct ath9k_debug { @@ -130,6 +155,7 @@ struct ath9k_debug { struct dentry *debugfs_rcstat; struct dentry *debugfs_wiphy; struct dentry *debugfs_xmit; + struct dentry *debugfs_recv; struct ath_stats stats; }; @@ -142,6 +168,7 @@ void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status); void ath_debug_stat_rc(struct ath_softc *sc, int final_rate); void ath_debug_stat_tx(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf); +void ath_debug_stat_rx(struct ath_softc *sc, struct ath_buf *bf); void ath_debug_stat_retries(struct ath_softc *sc, int rix, int xretries, int retries, u8 per); @@ -181,6 +208,11 @@ static inline void ath_debug_stat_tx(struct ath_softc *sc, { } +static inline void ath_debug_stat_rx(struct ath_softc *sc, + struct ath_buf *bf) +{ +} + static inline void ath_debug_stat_retries(struct ath_softc *sc, int rix, int xretries, int retries, u8 per) { diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c new file mode 100644 index 000000000000..deab8beb0680 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -0,0 +1,442 @@ +/* + * Copyright (c) 2008-2009 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ath9k.h" + +/********************************/ +/* LED functions */ +/********************************/ + +static void ath_led_blink_work(struct work_struct *work) +{ + struct ath_softc *sc = container_of(work, struct ath_softc, + ath_led_blink_work.work); + + if (!(sc->sc_flags & SC_OP_LED_ASSOCIATED)) + return; + + if ((sc->led_on_duration == ATH_LED_ON_DURATION_IDLE) || + (sc->led_off_duration == ATH_LED_OFF_DURATION_IDLE)) + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0); + else + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, + (sc->sc_flags & SC_OP_LED_ON) ? 1 : 0); + + ieee80211_queue_delayed_work(sc->hw, + &sc->ath_led_blink_work, + (sc->sc_flags & SC_OP_LED_ON) ? + msecs_to_jiffies(sc->led_off_duration) : + msecs_to_jiffies(sc->led_on_duration)); + + sc->led_on_duration = sc->led_on_cnt ? + max((ATH_LED_ON_DURATION_IDLE - sc->led_on_cnt), 25) : + ATH_LED_ON_DURATION_IDLE; + sc->led_off_duration = sc->led_off_cnt ? + max((ATH_LED_OFF_DURATION_IDLE - sc->led_off_cnt), 10) : + ATH_LED_OFF_DURATION_IDLE; + sc->led_on_cnt = sc->led_off_cnt = 0; + if (sc->sc_flags & SC_OP_LED_ON) + sc->sc_flags &= ~SC_OP_LED_ON; + else + sc->sc_flags |= SC_OP_LED_ON; +} + +static void ath_led_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev); + struct ath_softc *sc = led->sc; + + switch (brightness) { + case LED_OFF: + if (led->led_type == ATH_LED_ASSOC || + led->led_type == ATH_LED_RADIO) { + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, + (led->led_type == ATH_LED_RADIO)); + sc->sc_flags &= ~SC_OP_LED_ASSOCIATED; + if (led->led_type == ATH_LED_RADIO) + sc->sc_flags &= ~SC_OP_LED_ON; + } else { + sc->led_off_cnt++; + } + break; + case LED_FULL: + if (led->led_type == ATH_LED_ASSOC) { + sc->sc_flags |= SC_OP_LED_ASSOCIATED; + ieee80211_queue_delayed_work(sc->hw, + &sc->ath_led_blink_work, 0); + } else if (led->led_type == ATH_LED_RADIO) { + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0); + sc->sc_flags |= SC_OP_LED_ON; + } else { + sc->led_on_cnt++; + } + break; + default: + break; + } +} + +static int ath_register_led(struct ath_softc *sc, struct ath_led *led, + char *trigger) +{ + int ret; + + led->sc = sc; + led->led_cdev.name = led->name; + led->led_cdev.default_trigger = trigger; + led->led_cdev.brightness_set = ath_led_brightness; + + ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->led_cdev); + if (ret) + ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL, + "Failed to register led:%s", led->name); + else + led->registered = 1; + return ret; +} + +static void ath_unregister_led(struct ath_led *led) +{ + if (led->registered) { + led_classdev_unregister(&led->led_cdev); + led->registered = 0; + } +} + +void ath_deinit_leds(struct ath_softc *sc) +{ + ath_unregister_led(&sc->assoc_led); + sc->sc_flags &= ~SC_OP_LED_ASSOCIATED; + ath_unregister_led(&sc->tx_led); + ath_unregister_led(&sc->rx_led); + ath_unregister_led(&sc->radio_led); + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1); +} + +void ath_init_leds(struct ath_softc *sc) +{ + char *trigger; + int ret; + + if (AR_SREV_9287(sc->sc_ah)) + sc->sc_ah->led_pin = ATH_LED_PIN_9287; + else + sc->sc_ah->led_pin = ATH_LED_PIN_DEF; + + /* Configure gpio 1 for output */ + ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin, + AR_GPIO_OUTPUT_MUX_AS_OUTPUT); + /* LED off, active low */ + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1); + + INIT_DELAYED_WORK(&sc->ath_led_blink_work, ath_led_blink_work); + + trigger = ieee80211_get_radio_led_name(sc->hw); + snprintf(sc->radio_led.name, sizeof(sc->radio_led.name), + "ath9k-%s::radio", wiphy_name(sc->hw->wiphy)); + ret = ath_register_led(sc, &sc->radio_led, trigger); + sc->radio_led.led_type = ATH_LED_RADIO; + if (ret) + goto fail; + + trigger = ieee80211_get_assoc_led_name(sc->hw); + snprintf(sc->assoc_led.name, sizeof(sc->assoc_led.name), + "ath9k-%s::assoc", wiphy_name(sc->hw->wiphy)); + ret = ath_register_led(sc, &sc->assoc_led, trigger); + sc->assoc_led.led_type = ATH_LED_ASSOC; + if (ret) + goto fail; + + trigger = ieee80211_get_tx_led_name(sc->hw); + snprintf(sc->tx_led.name, sizeof(sc->tx_led.name), + "ath9k-%s::tx", wiphy_name(sc->hw->wiphy)); + ret = ath_register_led(sc, &sc->tx_led, trigger); + sc->tx_led.led_type = ATH_LED_TX; + if (ret) + goto fail; + + trigger = ieee80211_get_rx_led_name(sc->hw); + snprintf(sc->rx_led.name, sizeof(sc->rx_led.name), + "ath9k-%s::rx", wiphy_name(sc->hw->wiphy)); + ret = ath_register_led(sc, &sc->rx_led, trigger); + sc->rx_led.led_type = ATH_LED_RX; + if (ret) + goto fail; + + return; + +fail: + cancel_delayed_work_sync(&sc->ath_led_blink_work); + ath_deinit_leds(sc); +} + +/*******************/ +/* Rfkill */ +/*******************/ + +static bool ath_is_rfkill_set(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + + return ath9k_hw_gpio_get(ah, ah->rfkill_gpio) == + ah->rfkill_polarity; +} + +void ath9k_rfkill_poll_state(struct ieee80211_hw *hw) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + bool blocked = !!ath_is_rfkill_set(sc); + + wiphy_rfkill_set_hw_state(hw->wiphy, blocked); +} + +void ath_start_rfkill_poll(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + + if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) + wiphy_rfkill_start_polling(sc->hw->wiphy); +} + +/******************/ +/* BTCOEX */ +/******************/ + +/* + * Detects if there is any priority bt traffic + */ +static void ath_detect_bt_priority(struct ath_softc *sc) +{ + struct ath_btcoex *btcoex = &sc->btcoex; + struct ath_hw *ah = sc->sc_ah; + + if (ath9k_hw_gpio_get(sc->sc_ah, ah->btcoex_hw.btpriority_gpio)) + btcoex->bt_priority_cnt++; + + if (time_after(jiffies, btcoex->bt_priority_time + + msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) { + sc->sc_flags &= ~(SC_OP_BT_PRIORITY_DETECTED | SC_OP_BT_SCAN); + /* Detect if colocated bt started scanning */ + if (btcoex->bt_priority_cnt >= ATH_BT_CNT_SCAN_THRESHOLD) { + ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_BTCOEX, + "BT scan detected"); + sc->sc_flags |= (SC_OP_BT_SCAN | + SC_OP_BT_PRIORITY_DETECTED); + } else if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) { + ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_BTCOEX, + "BT priority traffic detected"); + sc->sc_flags |= SC_OP_BT_PRIORITY_DETECTED; + } + + btcoex->bt_priority_cnt = 0; + btcoex->bt_priority_time = jiffies; + } +} + +/* + * Configures appropriate weight based on stomp type. + */ +static void ath9k_btcoex_bt_stomp(struct ath_softc *sc, + enum ath_stomp_type stomp_type) +{ + struct ath_hw *ah = sc->sc_ah; + + switch (stomp_type) { + case ATH_BTCOEX_STOMP_ALL: + ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, + AR_STOMP_ALL_WLAN_WGHT); + break; + case ATH_BTCOEX_STOMP_LOW: + ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, + AR_STOMP_LOW_WLAN_WGHT); + break; + case ATH_BTCOEX_STOMP_NONE: + ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, + AR_STOMP_NONE_WLAN_WGHT); + break; + default: + ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX, + "Invalid Stomptype\n"); + break; + } + + ath9k_hw_btcoex_enable(ah); +} + +static void ath9k_gen_timer_start(struct ath_hw *ah, + struct ath_gen_timer *timer, + u32 timer_next, + u32 timer_period) +{ + struct ath_common *common = ath9k_hw_common(ah); + struct ath_softc *sc = (struct ath_softc *) common->priv; + + ath9k_hw_gen_timer_start(ah, timer, timer_next, timer_period); + + if ((sc->imask & ATH9K_INT_GENTIMER) == 0) { + ath9k_hw_set_interrupts(ah, 0); + sc->imask |= ATH9K_INT_GENTIMER; + ath9k_hw_set_interrupts(ah, sc->imask); + } +} + +static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer) +{ + struct ath_common *common = ath9k_hw_common(ah); + struct ath_softc *sc = (struct ath_softc *) common->priv; + struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; + + ath9k_hw_gen_timer_stop(ah, timer); + + /* if no timer is enabled, turn off interrupt mask */ + if (timer_table->timer_mask.val == 0) { + ath9k_hw_set_interrupts(ah, 0); + sc->imask &= ~ATH9K_INT_GENTIMER; + ath9k_hw_set_interrupts(ah, sc->imask); + } +} + +/* + * This is the master bt coex timer which runs for every + * 45ms, bt traffic will be given priority during 55% of this + * period while wlan gets remaining 45% + */ +static void ath_btcoex_period_timer(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *) data; + struct ath_hw *ah = sc->sc_ah; + struct ath_btcoex *btcoex = &sc->btcoex; + u32 timer_period; + bool is_btscan; + + ath_detect_bt_priority(sc); + + is_btscan = sc->sc_flags & SC_OP_BT_SCAN; + + spin_lock_bh(&btcoex->btcoex_lock); + + ath9k_btcoex_bt_stomp(sc, is_btscan ? ATH_BTCOEX_STOMP_ALL : + btcoex->bt_stomp_type); + + spin_unlock_bh(&btcoex->btcoex_lock); + + if (btcoex->btcoex_period != btcoex->btcoex_no_stomp) { + if (btcoex->hw_timer_enabled) + ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer); + + timer_period = is_btscan ? btcoex->btscan_no_stomp : + btcoex->btcoex_no_stomp; + ath9k_gen_timer_start(ah, + btcoex->no_stomp_timer, + (ath9k_hw_gettsf32(ah) + + timer_period), timer_period * 10); + btcoex->hw_timer_enabled = true; + } + + mod_timer(&btcoex->period_timer, jiffies + + msecs_to_jiffies(ATH_BTCOEX_DEF_BT_PERIOD)); +} + +/* + * Generic tsf based hw timer which configures weight + * registers to time slice between wlan and bt traffic + */ +static void ath_btcoex_no_stomp_timer(void *arg) +{ + struct ath_softc *sc = (struct ath_softc *)arg; + struct ath_hw *ah = sc->sc_ah; + struct ath_btcoex *btcoex = &sc->btcoex; + bool is_btscan = sc->sc_flags & SC_OP_BT_SCAN; + + ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX, + "no stomp timer running \n"); + + spin_lock_bh(&btcoex->btcoex_lock); + + if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || is_btscan) + ath9k_btcoex_bt_stomp(sc, ATH_BTCOEX_STOMP_NONE); + else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) + ath9k_btcoex_bt_stomp(sc, ATH_BTCOEX_STOMP_LOW); + + spin_unlock_bh(&btcoex->btcoex_lock); +} + +int ath_init_btcoex_timer(struct ath_softc *sc) +{ + struct ath_btcoex *btcoex = &sc->btcoex; + + btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD * 1000; + btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) * + btcoex->btcoex_period / 100; + btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) * + btcoex->btcoex_period / 100; + + setup_timer(&btcoex->period_timer, ath_btcoex_period_timer, + (unsigned long) sc); + + spin_lock_init(&btcoex->btcoex_lock); + + btcoex->no_stomp_timer = ath_gen_timer_alloc(sc->sc_ah, + ath_btcoex_no_stomp_timer, + ath_btcoex_no_stomp_timer, + (void *) sc, AR_FIRST_NDP_TIMER); + + if (!btcoex->no_stomp_timer) + return -ENOMEM; + + return 0; +} + +/* + * (Re)start btcoex timers + */ +void ath9k_btcoex_timer_resume(struct ath_softc *sc) +{ + struct ath_btcoex *btcoex = &sc->btcoex; + struct ath_hw *ah = sc->sc_ah; + + ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX, + "Starting btcoex timers"); + + /* make sure duty cycle timer is also stopped when resuming */ + if (btcoex->hw_timer_enabled) + ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer); + + btcoex->bt_priority_cnt = 0; + btcoex->bt_priority_time = jiffies; + sc->sc_flags &= ~(SC_OP_BT_PRIORITY_DETECTED | SC_OP_BT_SCAN); + + mod_timer(&btcoex->period_timer, jiffies); +} + + +/* + * Pause btcoex timer and bt duty cycle timer + */ +void ath9k_btcoex_timer_pause(struct ath_softc *sc) +{ + struct ath_btcoex *btcoex = &sc->btcoex; + struct ath_hw *ah = sc->sc_ah; + + del_timer_sync(&btcoex->period_timer); + + if (btcoex->hw_timer_enabled) + ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer); + + btcoex->hw_timer_enabled = false; +} diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index ae371448b5a0..f00f5c744f48 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -52,28 +52,6 @@ module_exit(ath9k_exit); /* Helper Functions */ /********************/ -static u32 ath9k_hw_mac_usec(struct ath_hw *ah, u32 clks) -{ - struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; - - if (!ah->curchan) /* should really check for CCK instead */ - return clks / ATH9K_CLOCK_RATE_CCK; - if (conf->channel->band == IEEE80211_BAND_2GHZ) - return clks / ATH9K_CLOCK_RATE_2GHZ_OFDM; - - return clks / ATH9K_CLOCK_RATE_5GHZ_OFDM; -} - -static u32 ath9k_hw_mac_to_usec(struct ath_hw *ah, u32 clks) -{ - struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; - - if (conf_is_ht40(conf)) - return ath9k_hw_mac_usec(ah, clks) / 2; - else - return ath9k_hw_mac_usec(ah, clks); -} - static u32 ath9k_hw_mac_clks(struct ath_hw *ah, u32 usecs) { struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; @@ -343,30 +321,6 @@ static bool ath9k_hw_chip_test(struct ath_hw *ah) return true; } -static const char *ath9k_hw_devname(u16 devid) -{ - switch (devid) { - case AR5416_DEVID_PCI: - return "Atheros 5416"; - case AR5416_DEVID_PCIE: - return "Atheros 5418"; - case AR9160_DEVID_PCI: - return "Atheros 9160"; - case AR5416_AR9100_DEVID: - return "Atheros 9100"; - case AR9280_DEVID_PCI: - case AR9280_DEVID_PCIE: - return "Atheros 9280"; - case AR9285_DEVID_PCIE: - return "Atheros 9285"; - case AR5416_DEVID_AR9287_PCI: - case AR5416_DEVID_AR9287_PCIE: - return "Atheros 9287"; - } - - return NULL; -} - static void ath9k_hw_init_config(struct ath_hw *ah) { int i; @@ -380,7 +334,6 @@ static void ath9k_hw_init_config(struct ath_hw *ah) ah->config.pcie_clock_req = 0; ah->config.pcie_waen = 0; ah->config.analog_shiftreg = 1; - ah->config.ht_enable = 1; ah->config.ofdm_trig_low = 200; ah->config.ofdm_trig_high = 500; ah->config.cck_trig_high = 200; @@ -392,7 +345,12 @@ static void ath9k_hw_init_config(struct ath_hw *ah) ah->config.spurchans[i][1] = AR_NO_SPUR; } - ah->config.intr_mitigation = true; + if (ah->hw_version.devid != AR2427_DEVID_PCIE) + ah->config.ht_enable = 1; + else + ah->config.ht_enable = 0; + + ah->config.rx_intr_mitigation = true; /* * We need this for PCI devices only (Cardbus, PCI, miniPCI) @@ -437,8 +395,6 @@ static void ath9k_hw_init_defaults(struct ath_hw *ah) ah->beacon_interval = 100; ah->enable_32kHz_clock = DONT_USE_32KHZ; ah->slottime = (u32) -1; - ah->acktimeout = (u32) -1; - ah->ctstimeout = (u32) -1; ah->globaltxtimeout = (u32) -1; ah->power_mode = ATH9K_PM_UNDEFINED; } @@ -590,6 +546,7 @@ static bool ath9k_hw_devid_supported(u16 devid) case AR5416_DEVID_AR9287_PCI: case AR5416_DEVID_AR9287_PCIE: case AR9271_USB: + case AR2427_DEVID_PCIE: return true; default: break; @@ -1183,7 +1140,7 @@ static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah, AR_IMR_RXORN | AR_IMR_BCNMISC; - if (ah->config.intr_mitigation) + if (ah->config.rx_intr_mitigation) ah->mask_reg |= AR_IMR_RXINTM | AR_IMR_RXMINTR; else ah->mask_reg |= AR_IMR_RXOK; @@ -1203,34 +1160,25 @@ static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah, } } -static bool ath9k_hw_set_ack_timeout(struct ath_hw *ah, u32 us) +static void ath9k_hw_setslottime(struct ath_hw *ah, u32 us) { - if (us > ath9k_hw_mac_to_usec(ah, MS(0xffffffff, AR_TIME_OUT_ACK))) { - ath_print(ath9k_hw_common(ah), ATH_DBG_RESET, - "bad ack timeout %u\n", us); - ah->acktimeout = (u32) -1; - return false; - } else { - REG_RMW_FIELD(ah, AR_TIME_OUT, - AR_TIME_OUT_ACK, ath9k_hw_mac_to_clks(ah, us)); - ah->acktimeout = us; - return true; - } + u32 val = ath9k_hw_mac_to_clks(ah, us); + val = min(val, (u32) 0xFFFF); + REG_WRITE(ah, AR_D_GBL_IFS_SLOT, val); } -static bool ath9k_hw_set_cts_timeout(struct ath_hw *ah, u32 us) +static void ath9k_hw_set_ack_timeout(struct ath_hw *ah, u32 us) { - if (us > ath9k_hw_mac_to_usec(ah, MS(0xffffffff, AR_TIME_OUT_CTS))) { - ath_print(ath9k_hw_common(ah), ATH_DBG_RESET, - "bad cts timeout %u\n", us); - ah->ctstimeout = (u32) -1; - return false; - } else { - REG_RMW_FIELD(ah, AR_TIME_OUT, - AR_TIME_OUT_CTS, ath9k_hw_mac_to_clks(ah, us)); - ah->ctstimeout = us; - return true; - } + u32 val = ath9k_hw_mac_to_clks(ah, us); + val = min(val, (u32) MS(0xFFFFFFFF, AR_TIME_OUT_ACK)); + REG_RMW_FIELD(ah, AR_TIME_OUT, AR_TIME_OUT_ACK, val); +} + +static void ath9k_hw_set_cts_timeout(struct ath_hw *ah, u32 us) +{ + u32 val = ath9k_hw_mac_to_clks(ah, us); + val = min(val, (u32) MS(0xFFFFFFFF, AR_TIME_OUT_CTS)); + REG_RMW_FIELD(ah, AR_TIME_OUT, AR_TIME_OUT_CTS, val); } static bool ath9k_hw_set_global_txtimeout(struct ath_hw *ah, u32 tu) @@ -1247,31 +1195,48 @@ static bool ath9k_hw_set_global_txtimeout(struct ath_hw *ah, u32 tu) } } -static void ath9k_hw_init_user_settings(struct ath_hw *ah) +void ath9k_hw_init_global_settings(struct ath_hw *ah) { + struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; + int acktimeout; + int slottime; + int sifstime; + ath_print(ath9k_hw_common(ah), ATH_DBG_RESET, "ah->misc_mode 0x%x\n", ah->misc_mode); if (ah->misc_mode != 0) REG_WRITE(ah, AR_PCU_MISC, REG_READ(ah, AR_PCU_MISC) | ah->misc_mode); - if (ah->slottime != (u32) -1) - ath9k_hw_setslottime(ah, ah->slottime); - if (ah->acktimeout != (u32) -1) - ath9k_hw_set_ack_timeout(ah, ah->acktimeout); - if (ah->ctstimeout != (u32) -1) - ath9k_hw_set_cts_timeout(ah, ah->ctstimeout); + + if (conf->channel && conf->channel->band == IEEE80211_BAND_5GHZ) + sifstime = 16; + else + sifstime = 10; + + /* As defined by IEEE 802.11-2007 17.3.8.6 */ + slottime = ah->slottime + 3 * ah->coverage_class; + acktimeout = slottime + sifstime; + + /* + * Workaround for early ACK timeouts, add an offset to match the + * initval's 64us ack timeout value. + * This was initially only meant to work around an issue with delayed + * BA frames in some implementations, but it has been found to fix ACK + * timeout issues in other cases as well. + */ + if (conf->channel && conf->channel->band == IEEE80211_BAND_2GHZ) + acktimeout += 64 - sifstime - ah->slottime; + + ath9k_hw_setslottime(ah, slottime); + ath9k_hw_set_ack_timeout(ah, acktimeout); + ath9k_hw_set_cts_timeout(ah, acktimeout); if (ah->globaltxtimeout != (u32) -1) ath9k_hw_set_global_txtimeout(ah, ah->globaltxtimeout); } +EXPORT_SYMBOL(ath9k_hw_init_global_settings); -const char *ath9k_hw_probe(u16 vendorid, u16 devid) -{ - return vendorid == ATHEROS_VENDOR_ID ? - ath9k_hw_devname(devid) : NULL; -} - -void ath9k_hw_detach(struct ath_hw *ah) +void ath9k_hw_deinit(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); @@ -1289,7 +1254,7 @@ free_hw: kfree(ah); ah = NULL; } -EXPORT_SYMBOL(ath9k_hw_detach); +EXPORT_SYMBOL(ath9k_hw_deinit); /*******/ /* INI */ @@ -2090,7 +2055,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) ath9k_enable_rfkill(ah); - ath9k_hw_init_user_settings(ah); + ath9k_hw_init_global_settings(ah); if (AR_SREV_9287_12_OR_LATER(ah)) { REG_WRITE(ah, AR_D_GBL_IFS_SIFS, @@ -2120,7 +2085,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, REG_WRITE(ah, AR_OBS, 8); - if (ah->config.intr_mitigation) { + if (ah->config.rx_intr_mitigation) { REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, 500); REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, 2000); } @@ -2780,7 +2745,7 @@ bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked) *masked = isr & ATH9K_INT_COMMON; - if (ah->config.intr_mitigation) { + if (ah->config.rx_intr_mitigation) { if (isr & (AR_ISR_RXMINTR | AR_ISR_RXINTM)) *masked |= ATH9K_INT_RX; } @@ -2913,7 +2878,7 @@ enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints) } if (ints & ATH9K_INT_RX) { mask |= AR_IMR_RXERR; - if (ah->config.intr_mitigation) + if (ah->config.rx_intr_mitigation) mask |= AR_IMR_RXMINTR | AR_IMR_RXINTM; else mask |= AR_IMR_RXOK | AR_IMR_RXDESC; @@ -3687,21 +3652,6 @@ u64 ath9k_hw_extend_tsf(struct ath_hw *ah, u32 rstamp) } EXPORT_SYMBOL(ath9k_hw_extend_tsf); -bool ath9k_hw_setslottime(struct ath_hw *ah, u32 us) -{ - if (us < ATH9K_SLOT_TIME_9 || us > ath9k_hw_mac_to_usec(ah, 0xffff)) { - ath_print(ath9k_hw_common(ah), ATH_DBG_RESET, - "bad slot time %u\n", us); - ah->slottime = (u32) -1; - return false; - } else { - REG_WRITE(ah, AR_D_GBL_IFS_SLOT, ath9k_hw_mac_to_clks(ah, us)); - ah->slottime = us; - return true; - } -} -EXPORT_SYMBOL(ath9k_hw_setslottime); - void ath9k_hw_set11nmac2040(struct ath_hw *ah) { struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index e2b0c73a616f..dbbf7ca5f97d 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -40,6 +40,7 @@ #define AR9280_DEVID_PCI 0x0029 #define AR9280_DEVID_PCIE 0x002a #define AR9285_DEVID_PCIE 0x002b +#define AR2427_DEVID_PCIE 0x002c #define AR5416_AR9100_DEVID 0x000b @@ -212,7 +213,7 @@ struct ath9k_ops_config { u32 cck_trig_low; u32 enable_ani; int serialize_regmode; - bool intr_mitigation; + bool rx_intr_mitigation; #define SPUR_DISABLE 0 #define SPUR_ENABLE_IOCTL 1 #define SPUR_ENABLE_EEPROM 2 @@ -551,10 +552,9 @@ struct ath_hw { u32 *bank6Temp; int16_t txpower_indexoffset; + int coverage_class; u32 beacon_interval; u32 slottime; - u32 acktimeout; - u32 ctstimeout; u32 globaltxtimeout; /* ANI */ @@ -616,7 +616,7 @@ static inline struct ath_regulatory *ath9k_hw_regulatory(struct ath_hw *ah) /* Initialization, Detach, Reset */ const char *ath9k_hw_probe(u16 vendorid, u16 devid); -void ath9k_hw_detach(struct ath_hw *ah); +void ath9k_hw_deinit(struct ath_hw *ah); int ath9k_hw_init(struct ath_hw *ah); int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, bool bChannelChange); @@ -668,7 +668,7 @@ void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64); void ath9k_hw_reset_tsf(struct ath_hw *ah); void ath9k_hw_set_tsfadjust(struct ath_hw *ah, u32 setting); u64 ath9k_hw_extend_tsf(struct ath_hw *ah, u32 rstamp); -bool ath9k_hw_setslottime(struct ath_hw *ah, u32 us); +void ath9k_hw_init_global_settings(struct ath_hw *ah); void ath9k_hw_set11nmac2040(struct ath_hw *ah); void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period); void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c new file mode 100644 index 000000000000..623c2f884987 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -0,0 +1,863 @@ +/* + * Copyright (c) 2008-2009 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ath9k.h" + +static char *dev_info = "ath9k"; + +MODULE_AUTHOR("Atheros Communications"); +MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards."); +MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards"); +MODULE_LICENSE("Dual BSD/GPL"); + +static unsigned int ath9k_debug = ATH_DBG_DEFAULT; +module_param_named(debug, ath9k_debug, uint, 0); +MODULE_PARM_DESC(debug, "Debugging mask"); + +int modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption"); + +/* We use the hw_value as an index into our private channel structure */ + +#define CHAN2G(_freq, _idx) { \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 20, \ +} + +#define CHAN5G(_freq, _idx) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 20, \ +} + +/* Some 2 GHz radios are actually tunable on 2312-2732 + * on 5 MHz steps, we support the channels which we know + * we have calibration data for all cards though to make + * this static */ +static struct ieee80211_channel ath9k_2ghz_chantable[] = { + CHAN2G(2412, 0), /* Channel 1 */ + CHAN2G(2417, 1), /* Channel 2 */ + CHAN2G(2422, 2), /* Channel 3 */ + CHAN2G(2427, 3), /* Channel 4 */ + CHAN2G(2432, 4), /* Channel 5 */ + CHAN2G(2437, 5), /* Channel 6 */ + CHAN2G(2442, 6), /* Channel 7 */ + CHAN2G(2447, 7), /* Channel 8 */ + CHAN2G(2452, 8), /* Channel 9 */ + CHAN2G(2457, 9), /* Channel 10 */ + CHAN2G(2462, 10), /* Channel 11 */ + CHAN2G(2467, 11), /* Channel 12 */ + CHAN2G(2472, 12), /* Channel 13 */ + CHAN2G(2484, 13), /* Channel 14 */ +}; + +/* Some 5 GHz radios are actually tunable on XXXX-YYYY + * on 5 MHz steps, we support the channels which we know + * we have calibration data for all cards though to make + * this static */ +static struct ieee80211_channel ath9k_5ghz_chantable[] = { + /* _We_ call this UNII 1 */ + CHAN5G(5180, 14), /* Channel 36 */ + CHAN5G(5200, 15), /* Channel 40 */ + CHAN5G(5220, 16), /* Channel 44 */ + CHAN5G(5240, 17), /* Channel 48 */ + /* _We_ call this UNII 2 */ + CHAN5G(5260, 18), /* Channel 52 */ + CHAN5G(5280, 19), /* Channel 56 */ + CHAN5G(5300, 20), /* Channel 60 */ + CHAN5G(5320, 21), /* Channel 64 */ + /* _We_ call this "Middle band" */ + CHAN5G(5500, 22), /* Channel 100 */ + CHAN5G(5520, 23), /* Channel 104 */ + CHAN5G(5540, 24), /* Channel 108 */ + CHAN5G(5560, 25), /* Channel 112 */ + CHAN5G(5580, 26), /* Channel 116 */ + CHAN5G(5600, 27), /* Channel 120 */ + CHAN5G(5620, 28), /* Channel 124 */ + CHAN5G(5640, 29), /* Channel 128 */ + CHAN5G(5660, 30), /* Channel 132 */ + CHAN5G(5680, 31), /* Channel 136 */ + CHAN5G(5700, 32), /* Channel 140 */ + /* _We_ call this UNII 3 */ + CHAN5G(5745, 33), /* Channel 149 */ + CHAN5G(5765, 34), /* Channel 153 */ + CHAN5G(5785, 35), /* Channel 157 */ + CHAN5G(5805, 36), /* Channel 161 */ + CHAN5G(5825, 37), /* Channel 165 */ +}; + +/* Atheros hardware rate code addition for short premble */ +#define SHPCHECK(__hw_rate, __flags) \ + ((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate | 0x04 ) : 0) + +#define RATE(_bitrate, _hw_rate, _flags) { \ + .bitrate = (_bitrate), \ + .flags = (_flags), \ + .hw_value = (_hw_rate), \ + .hw_value_short = (SHPCHECK(_hw_rate, _flags)) \ +} + +static struct ieee80211_rate ath9k_legacy_rates[] = { + RATE(10, 0x1b, 0), + RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(60, 0x0b, 0), + RATE(90, 0x0f, 0), + RATE(120, 0x0a, 0), + RATE(180, 0x0e, 0), + RATE(240, 0x09, 0), + RATE(360, 0x0d, 0), + RATE(480, 0x08, 0), + RATE(540, 0x0c, 0), +}; + +static void ath9k_deinit_softc(struct ath_softc *sc); + +/* + * Read and write, they both share the same lock. We do this to serialize + * reads and writes on Atheros 802.11n PCI devices only. This is required + * as the FIFO on these devices can only accept sanely 2 requests. + */ + +static void ath9k_iowrite32(void *hw_priv, u32 val, u32 reg_offset) +{ + struct ath_hw *ah = (struct ath_hw *) hw_priv; + struct ath_common *common = ath9k_hw_common(ah); + struct ath_softc *sc = (struct ath_softc *) common->priv; + + if (ah->config.serialize_regmode == SER_REG_MODE_ON) { + unsigned long flags; + spin_lock_irqsave(&sc->sc_serial_rw, flags); + iowrite32(val, sc->mem + reg_offset); + spin_unlock_irqrestore(&sc->sc_serial_rw, flags); + } else + iowrite32(val, sc->mem + reg_offset); +} + +static unsigned int ath9k_ioread32(void *hw_priv, u32 reg_offset) +{ + struct ath_hw *ah = (struct ath_hw *) hw_priv; + struct ath_common *common = ath9k_hw_common(ah); + struct ath_softc *sc = (struct ath_softc *) common->priv; + u32 val; + + if (ah->config.serialize_regmode == SER_REG_MODE_ON) { + unsigned long flags; + spin_lock_irqsave(&sc->sc_serial_rw, flags); + val = ioread32(sc->mem + reg_offset); + spin_unlock_irqrestore(&sc->sc_serial_rw, flags); + } else + val = ioread32(sc->mem + reg_offset); + return val; +} + +static const struct ath_ops ath9k_common_ops = { + .read = ath9k_ioread32, + .write = ath9k_iowrite32, +}; + +/**************************/ +/* Initialization */ +/**************************/ + +static void setup_ht_cap(struct ath_softc *sc, + struct ieee80211_sta_ht_cap *ht_info) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + u8 tx_streams, rx_streams; + + ht_info->ht_supported = true; + ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_SM_PS | + IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_DSSSCCK40; + + ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_8; + + /* set up supported mcs set */ + memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); + tx_streams = !(common->tx_chainmask & (common->tx_chainmask - 1)) ? + 1 : 2; + rx_streams = !(common->rx_chainmask & (common->rx_chainmask - 1)) ? + 1 : 2; + + if (tx_streams != rx_streams) { + ath_print(common, ATH_DBG_CONFIG, + "TX streams %d, RX streams: %d\n", + tx_streams, rx_streams); + ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; + ht_info->mcs.tx_params |= ((tx_streams - 1) << + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); + } + + ht_info->mcs.rx_mask[0] = 0xff; + if (rx_streams >= 2) + ht_info->mcs.rx_mask[1] = 0xff; + + ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED; +} + +static int ath9k_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ath_regulatory *reg = ath9k_hw_regulatory(sc->sc_ah); + + return ath_reg_notifier_apply(wiphy, request, reg); +} + +/* + * This function will allocate both the DMA descriptor structure, and the + * buffers it contains. These are used to contain the descriptors used + * by the system. +*/ +int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, + struct list_head *head, const char *name, + int nbuf, int ndesc) +{ +#define DS2PHYS(_dd, _ds) \ + ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc)) +#define ATH_DESC_4KB_BOUND_CHECK(_daddr) ((((_daddr) & 0xFFF) > 0xF7F) ? 1 : 0) +#define ATH_DESC_4KB_BOUND_NUM_SKIPPED(_len) ((_len) / 4096) + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_desc *ds; + struct ath_buf *bf; + int i, bsize, error; + + ath_print(common, ATH_DBG_CONFIG, "%s DMA: %u buffers %u desc/buf\n", + name, nbuf, ndesc); + + INIT_LIST_HEAD(head); + /* ath_desc must be a multiple of DWORDs */ + if ((sizeof(struct ath_desc) % 4) != 0) { + ath_print(common, ATH_DBG_FATAL, + "ath_desc not DWORD aligned\n"); + BUG_ON((sizeof(struct ath_desc) % 4) != 0); + error = -ENOMEM; + goto fail; + } + + dd->dd_desc_len = sizeof(struct ath_desc) * nbuf * ndesc; + + /* + * Need additional DMA memory because we can't use + * descriptors that cross the 4K page boundary. Assume + * one skipped descriptor per 4K page. + */ + if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_4KB_SPLITTRANS)) { + u32 ndesc_skipped = + ATH_DESC_4KB_BOUND_NUM_SKIPPED(dd->dd_desc_len); + u32 dma_len; + + while (ndesc_skipped) { + dma_len = ndesc_skipped * sizeof(struct ath_desc); + dd->dd_desc_len += dma_len; + + ndesc_skipped = ATH_DESC_4KB_BOUND_NUM_SKIPPED(dma_len); + }; + } + + /* allocate descriptors */ + dd->dd_desc = dma_alloc_coherent(sc->dev, dd->dd_desc_len, + &dd->dd_desc_paddr, GFP_KERNEL); + if (dd->dd_desc == NULL) { + error = -ENOMEM; + goto fail; + } + ds = dd->dd_desc; + ath_print(common, ATH_DBG_CONFIG, "%s DMA map: %p (%u) -> %llx (%u)\n", + name, ds, (u32) dd->dd_desc_len, + ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len); + + /* allocate buffers */ + bsize = sizeof(struct ath_buf) * nbuf; + bf = kzalloc(bsize, GFP_KERNEL); + if (bf == NULL) { + error = -ENOMEM; + goto fail2; + } + dd->dd_bufptr = bf; + + for (i = 0; i < nbuf; i++, bf++, ds += ndesc) { + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(dd, ds); + + if (!(sc->sc_ah->caps.hw_caps & + ATH9K_HW_CAP_4KB_SPLITTRANS)) { + /* + * Skip descriptor addresses which can cause 4KB + * boundary crossing (addr + length) with a 32 dword + * descriptor fetch. + */ + while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) { + BUG_ON((caddr_t) bf->bf_desc >= + ((caddr_t) dd->dd_desc + + dd->dd_desc_len)); + + ds += ndesc; + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(dd, ds); + } + } + list_add_tail(&bf->list, head); + } + return 0; +fail2: + dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc, + dd->dd_desc_paddr); +fail: + memset(dd, 0, sizeof(*dd)); + return error; +#undef ATH_DESC_4KB_BOUND_CHECK +#undef ATH_DESC_4KB_BOUND_NUM_SKIPPED +#undef DS2PHYS +} + +static void ath9k_init_crypto(struct ath_softc *sc) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + int i = 0; + + /* Get the hardware key cache size. */ + common->keymax = sc->sc_ah->caps.keycache_size; + if (common->keymax > ATH_KEYMAX) { + ath_print(common, ATH_DBG_ANY, + "Warning, using only %u entries in %u key cache\n", + ATH_KEYMAX, common->keymax); + common->keymax = ATH_KEYMAX; + } + + /* + * Reset the key cache since some parts do not + * reset the contents on initial power up. + */ + for (i = 0; i < common->keymax; i++) + ath9k_hw_keyreset(sc->sc_ah, (u16) i); + + if (ath9k_hw_getcapability(sc->sc_ah, ATH9K_CAP_CIPHER, + ATH9K_CIPHER_TKIP, NULL)) { + /* + * Whether we should enable h/w TKIP MIC. + * XXX: if we don't support WME TKIP MIC, then we wouldn't + * report WMM capable, so it's always safe to turn on + * TKIP MIC in this case. + */ + ath9k_hw_setcapability(sc->sc_ah, ATH9K_CAP_TKIP_MIC, 0, 1, NULL); + } + + /* + * Check whether the separate key cache entries + * are required to handle both tx+rx MIC keys. + * With split mic keys the number of stations is limited + * to 27 otherwise 59. + */ + if (ath9k_hw_getcapability(sc->sc_ah, ATH9K_CAP_CIPHER, + ATH9K_CIPHER_TKIP, NULL) + && ath9k_hw_getcapability(sc->sc_ah, ATH9K_CAP_CIPHER, + ATH9K_CIPHER_MIC, NULL) + && ath9k_hw_getcapability(sc->sc_ah, ATH9K_CAP_TKIP_SPLIT, + 0, NULL)) + common->splitmic = 1; + + /* turn on mcast key search if possible */ + if (!ath9k_hw_getcapability(sc->sc_ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL)) + (void)ath9k_hw_setcapability(sc->sc_ah, ATH9K_CAP_MCAST_KEYSRCH, + 1, 1, NULL); + +} + +static int ath9k_init_btcoex(struct ath_softc *sc) +{ + int r, qnum; + + switch (sc->sc_ah->btcoex_hw.scheme) { + case ATH_BTCOEX_CFG_NONE: + break; + case ATH_BTCOEX_CFG_2WIRE: + ath9k_hw_btcoex_init_2wire(sc->sc_ah); + break; + case ATH_BTCOEX_CFG_3WIRE: + ath9k_hw_btcoex_init_3wire(sc->sc_ah); + r = ath_init_btcoex_timer(sc); + if (r) + return -1; + qnum = ath_tx_get_qnum(sc, ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BE); + ath9k_hw_init_btcoex_hw(sc->sc_ah, qnum); + sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; + break; + default: + WARN_ON(1); + break; + } + + return 0; +} + +static int ath9k_init_queues(struct ath_softc *sc) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + int i = 0; + + for (i = 0; i < ARRAY_SIZE(sc->tx.hwq_map); i++) + sc->tx.hwq_map[i] = -1; + + sc->beacon.beaconq = ath9k_hw_beaconq_setup(sc->sc_ah); + if (sc->beacon.beaconq == -1) { + ath_print(common, ATH_DBG_FATAL, + "Unable to setup a beacon xmit queue\n"); + goto err; + } + + sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0); + if (sc->beacon.cabq == NULL) { + ath_print(common, ATH_DBG_FATAL, + "Unable to setup CAB xmit queue\n"); + goto err; + } + + sc->config.cabqReadytime = ATH_CABQ_READY_TIME; + ath_cabq_update(sc); + + if (!ath_tx_setup(sc, ATH9K_WME_AC_BK)) { + ath_print(common, ATH_DBG_FATAL, + "Unable to setup xmit queue for BK traffic\n"); + goto err; + } + + if (!ath_tx_setup(sc, ATH9K_WME_AC_BE)) { + ath_print(common, ATH_DBG_FATAL, + "Unable to setup xmit queue for BE traffic\n"); + goto err; + } + if (!ath_tx_setup(sc, ATH9K_WME_AC_VI)) { + ath_print(common, ATH_DBG_FATAL, + "Unable to setup xmit queue for VI traffic\n"); + goto err; + } + if (!ath_tx_setup(sc, ATH9K_WME_AC_VO)) { + ath_print(common, ATH_DBG_FATAL, + "Unable to setup xmit queue for VO traffic\n"); + goto err; + } + + return 0; + +err: + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) + if (ATH_TXQ_SETUP(sc, i)) + ath_tx_cleanupq(sc, &sc->tx.txq[i]); + + return -EIO; +} + +static void ath9k_init_channels_rates(struct ath_softc *sc) +{ + if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) { + sc->sbands[IEEE80211_BAND_2GHZ].channels = ath9k_2ghz_chantable; + sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; + sc->sbands[IEEE80211_BAND_2GHZ].n_channels = + ARRAY_SIZE(ath9k_2ghz_chantable); + sc->sbands[IEEE80211_BAND_2GHZ].bitrates = ath9k_legacy_rates; + sc->sbands[IEEE80211_BAND_2GHZ].n_bitrates = + ARRAY_SIZE(ath9k_legacy_rates); + } + + if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) { + sc->sbands[IEEE80211_BAND_5GHZ].channels = ath9k_5ghz_chantable; + sc->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ; + sc->sbands[IEEE80211_BAND_5GHZ].n_channels = + ARRAY_SIZE(ath9k_5ghz_chantable); + sc->sbands[IEEE80211_BAND_5GHZ].bitrates = + ath9k_legacy_rates + 4; + sc->sbands[IEEE80211_BAND_5GHZ].n_bitrates = + ARRAY_SIZE(ath9k_legacy_rates) - 4; + } +} + +static void ath9k_init_misc(struct ath_softc *sc) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + int i = 0; + + common->ani.noise_floor = ATH_DEFAULT_NOISE_FLOOR; + setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc); + + sc->config.txpowlimit = ATH_TXPOWER_MAX; + + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) { + sc->sc_flags |= SC_OP_TXAGGR; + sc->sc_flags |= SC_OP_RXAGGR; + } + + common->tx_chainmask = sc->sc_ah->caps.tx_chainmask; + common->rx_chainmask = sc->sc_ah->caps.rx_chainmask; + + ath9k_hw_setcapability(sc->sc_ah, ATH9K_CAP_DIVERSITY, 1, true, NULL); + sc->rx.defant = ath9k_hw_getdefantenna(sc->sc_ah); + + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) + memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); + + sc->beacon.slottime = ATH9K_SLOT_TIME_9; + + for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) { + sc->beacon.bslot[i] = NULL; + sc->beacon.bslot_aphy[i] = NULL; + } +} + +static int ath9k_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid, + const struct ath_bus_ops *bus_ops) +{ + struct ath_hw *ah = NULL; + struct ath_common *common; + int ret = 0, i; + int csz = 0; + + ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL); + if (!ah) + return -ENOMEM; + + ah->hw_version.devid = devid; + ah->hw_version.subsysid = subsysid; + sc->sc_ah = ah; + + common = ath9k_hw_common(ah); + common->ops = &ath9k_common_ops; + common->bus_ops = bus_ops; + common->ah = ah; + common->hw = sc->hw; + common->priv = sc; + common->debug_mask = ath9k_debug; + + spin_lock_init(&sc->wiphy_lock); + spin_lock_init(&sc->sc_resetlock); + spin_lock_init(&sc->sc_serial_rw); + spin_lock_init(&sc->sc_pm_lock); + mutex_init(&sc->mutex); + tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc); + tasklet_init(&sc->bcon_tasklet, ath_beacon_tasklet, + (unsigned long)sc); + + /* + * Cache line size is used to size and align various + * structures used to communicate with the hardware. + */ + ath_read_cachesize(common, &csz); + common->cachelsz = csz << 2; /* convert to bytes */ + + ret = ath9k_hw_init(ah); + if (ret) { + ath_print(common, ATH_DBG_FATAL, + "Unable to initialize hardware; " + "initialization status: %d\n", ret); + goto err_hw; + } + + ret = ath9k_init_debug(ah); + if (ret) { + ath_print(common, ATH_DBG_FATAL, + "Unable to create debugfs files\n"); + goto err_debug; + } + + ret = ath9k_init_queues(sc); + if (ret) + goto err_queues; + + ret = ath9k_init_btcoex(sc); + if (ret) + goto err_btcoex; + + ath9k_init_crypto(sc); + ath9k_init_channels_rates(sc); + ath9k_init_misc(sc); + + return 0; + +err_btcoex: + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) + if (ATH_TXQ_SETUP(sc, i)) + ath_tx_cleanupq(sc, &sc->tx.txq[i]); +err_queues: + ath9k_exit_debug(ah); +err_debug: + ath9k_hw_deinit(ah); +err_hw: + tasklet_kill(&sc->intr_tq); + tasklet_kill(&sc->bcon_tasklet); + + kfree(ah); + sc->sc_ah = NULL; + + return ret; +} + +void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + + hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | + IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK | + IEEE80211_HW_SPECTRUM_MGMT | + IEEE80211_HW_REPORTS_TX_ACK_STATUS; + + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) + hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; + + if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || modparam_nohwcrypt) + hw->flags |= IEEE80211_HW_MFP_CAPABLE; + + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_MESH_POINT); + + hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + + hw->queues = 4; + hw->max_rates = 4; + hw->channel_change_time = 5000; + hw->max_listen_interval = 10; + hw->max_rate_tries = 10; + hw->sta_data_size = sizeof(struct ath_node); + hw->vif_data_size = sizeof(struct ath_vif); + + hw->rate_control_algorithm = "ath9k_rate_control"; + + if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = + &sc->sbands[IEEE80211_BAND_2GHZ]; + if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = + &sc->sbands[IEEE80211_BAND_5GHZ]; + + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) { + if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) + setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap); + if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) + setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap); + } + + SET_IEEE80211_PERM_ADDR(hw, common->macaddr); +} + +int ath9k_init_device(u16 devid, struct ath_softc *sc, u16 subsysid, + const struct ath_bus_ops *bus_ops) +{ + struct ieee80211_hw *hw = sc->hw; + struct ath_common *common; + struct ath_hw *ah; + int error = 0; + struct ath_regulatory *reg; + + /* Bring up device */ + error = ath9k_init_softc(devid, sc, subsysid, bus_ops); + if (error != 0) + goto error_init; + + ah = sc->sc_ah; + common = ath9k_hw_common(ah); + ath9k_set_hw_capab(sc, hw); + + /* Initialize regulatory */ + error = ath_regd_init(&common->regulatory, sc->hw->wiphy, + ath9k_reg_notifier); + if (error) + goto error_regd; + + reg = &common->regulatory; + + /* Setup TX DMA */ + error = ath_tx_init(sc, ATH_TXBUF); + if (error != 0) + goto error_tx; + + /* Setup RX DMA */ + error = ath_rx_init(sc, ATH_RXBUF); + if (error != 0) + goto error_rx; + + /* Register with mac80211 */ + error = ieee80211_register_hw(hw); + if (error) + goto error_register; + + /* Handle world regulatory */ + if (!ath_is_world_regd(reg)) { + error = regulatory_hint(hw->wiphy, reg->alpha2); + if (error) + goto error_world; + } + + INIT_WORK(&sc->chan_work, ath9k_wiphy_chan_work); + INIT_DELAYED_WORK(&sc->wiphy_work, ath9k_wiphy_work); + sc->wiphy_scheduler_int = msecs_to_jiffies(500); + + ath_init_leds(sc); + ath_start_rfkill_poll(sc); + + return 0; + +error_world: + ieee80211_unregister_hw(hw); +error_register: + ath_rx_cleanup(sc); +error_rx: + ath_tx_cleanup(sc); +error_tx: + /* Nothing */ +error_regd: + ath9k_deinit_softc(sc); +error_init: + return error; +} + +/*****************************/ +/* De-Initialization */ +/*****************************/ + +static void ath9k_deinit_softc(struct ath_softc *sc) +{ + int i = 0; + + if ((sc->btcoex.no_stomp_timer) && + sc->sc_ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) + ath_gen_timer_free(sc->sc_ah, sc->btcoex.no_stomp_timer); + + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) + if (ATH_TXQ_SETUP(sc, i)) + ath_tx_cleanupq(sc, &sc->tx.txq[i]); + + ath9k_exit_debug(sc->sc_ah); + ath9k_hw_deinit(sc->sc_ah); + + tasklet_kill(&sc->intr_tq); + tasklet_kill(&sc->bcon_tasklet); +} + +void ath9k_deinit_device(struct ath_softc *sc) +{ + struct ieee80211_hw *hw = sc->hw; + int i = 0; + + ath9k_ps_wakeup(sc); + + wiphy_rfkill_stop_polling(sc->hw->wiphy); + ath_deinit_leds(sc); + + for (i = 0; i < sc->num_sec_wiphy; i++) { + struct ath_wiphy *aphy = sc->sec_wiphy[i]; + if (aphy == NULL) + continue; + sc->sec_wiphy[i] = NULL; + ieee80211_unregister_hw(aphy->hw); + ieee80211_free_hw(aphy->hw); + } + kfree(sc->sec_wiphy); + + ieee80211_unregister_hw(hw); + ath_rx_cleanup(sc); + ath_tx_cleanup(sc); + ath9k_deinit_softc(sc); +} + +void ath_descdma_cleanup(struct ath_softc *sc, + struct ath_descdma *dd, + struct list_head *head) +{ + dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc, + dd->dd_desc_paddr); + + INIT_LIST_HEAD(head); + kfree(dd->dd_bufptr); + memset(dd, 0, sizeof(*dd)); +} + +/************************/ +/* Module Hooks */ +/************************/ + +static int __init ath9k_init(void) +{ + int error; + + /* Register rate control algorithm */ + error = ath_rate_control_register(); + if (error != 0) { + printk(KERN_ERR + "ath9k: Unable to register rate control " + "algorithm: %d\n", + error); + goto err_out; + } + + error = ath9k_debug_create_root(); + if (error) { + printk(KERN_ERR + "ath9k: Unable to create debugfs root: %d\n", + error); + goto err_rate_unregister; + } + + error = ath_pci_init(); + if (error < 0) { + printk(KERN_ERR + "ath9k: No PCI devices found, driver not installed.\n"); + error = -ENODEV; + goto err_remove_root; + } + + error = ath_ahb_init(); + if (error < 0) { + error = -ENODEV; + goto err_pci_exit; + } + + return 0; + + err_pci_exit: + ath_pci_exit(); + + err_remove_root: + ath9k_debug_remove_root(); + err_rate_unregister: + ath_rate_control_unregister(); + err_out: + return error; +} +module_init(ath9k_init); + +static void __exit ath9k_exit(void) +{ + ath_ahb_exit(); + ath_pci_exit(); + ath9k_debug_remove_root(); + ath_rate_control_unregister(); + printk(KERN_INFO "%s: Driver unloaded\n", dev_info); +} +module_exit(ath9k_exit); diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index e185479e295e..29851e6376a9 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -167,6 +167,40 @@ struct ath_rx_status { #define ATH9K_RXKEYIX_INVALID ((u8)-1) #define ATH9K_TXKEYIX_INVALID ((u32)-1) +enum ath9k_phyerr { + ATH9K_PHYERR_UNDERRUN = 0, /* Transmit underrun */ + ATH9K_PHYERR_TIMING = 1, /* Timing error */ + ATH9K_PHYERR_PARITY = 2, /* Illegal parity */ + ATH9K_PHYERR_RATE = 3, /* Illegal rate */ + ATH9K_PHYERR_LENGTH = 4, /* Illegal length */ + ATH9K_PHYERR_RADAR = 5, /* Radar detect */ + ATH9K_PHYERR_SERVICE = 6, /* Illegal service */ + ATH9K_PHYERR_TOR = 7, /* Transmit override receive */ + + ATH9K_PHYERR_OFDM_TIMING = 17, + ATH9K_PHYERR_OFDM_SIGNAL_PARITY = 18, + ATH9K_PHYERR_OFDM_RATE_ILLEGAL = 19, + ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL = 20, + ATH9K_PHYERR_OFDM_POWER_DROP = 21, + ATH9K_PHYERR_OFDM_SERVICE = 22, + ATH9K_PHYERR_OFDM_RESTART = 23, + ATH9K_PHYERR_FALSE_RADAR_EXT = 24, + + ATH9K_PHYERR_CCK_TIMING = 25, + ATH9K_PHYERR_CCK_HEADER_CRC = 26, + ATH9K_PHYERR_CCK_RATE_ILLEGAL = 27, + ATH9K_PHYERR_CCK_SERVICE = 30, + ATH9K_PHYERR_CCK_RESTART = 31, + ATH9K_PHYERR_CCK_LENGTH_ILLEGAL = 32, + ATH9K_PHYERR_CCK_POWER_DROP = 33, + + ATH9K_PHYERR_HT_CRC_ERROR = 34, + ATH9K_PHYERR_HT_LENGTH_ILLEGAL = 35, + ATH9K_PHYERR_HT_RATE_ILLEGAL = 36, + + ATH9K_PHYERR_MAX = 37, +}; + struct ath_desc { u32 ds_link; u32 ds_data; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 643bea35686f..67ca4e5a6017 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -18,118 +18,6 @@ #include "ath9k.h" #include "btcoex.h" -static char *dev_info = "ath9k"; - -MODULE_AUTHOR("Atheros Communications"); -MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards."); -MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards"); -MODULE_LICENSE("Dual BSD/GPL"); - -static int modparam_nohwcrypt; -module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444); -MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption"); - -static unsigned int ath9k_debug = ATH_DBG_DEFAULT; -module_param_named(debug, ath9k_debug, uint, 0); -MODULE_PARM_DESC(debug, "Debugging mask"); - -/* We use the hw_value as an index into our private channel structure */ - -#define CHAN2G(_freq, _idx) { \ - .center_freq = (_freq), \ - .hw_value = (_idx), \ - .max_power = 20, \ -} - -#define CHAN5G(_freq, _idx) { \ - .band = IEEE80211_BAND_5GHZ, \ - .center_freq = (_freq), \ - .hw_value = (_idx), \ - .max_power = 20, \ -} - -/* Some 2 GHz radios are actually tunable on 2312-2732 - * on 5 MHz steps, we support the channels which we know - * we have calibration data for all cards though to make - * this static */ -static struct ieee80211_channel ath9k_2ghz_chantable[] = { - CHAN2G(2412, 0), /* Channel 1 */ - CHAN2G(2417, 1), /* Channel 2 */ - CHAN2G(2422, 2), /* Channel 3 */ - CHAN2G(2427, 3), /* Channel 4 */ - CHAN2G(2432, 4), /* Channel 5 */ - CHAN2G(2437, 5), /* Channel 6 */ - CHAN2G(2442, 6), /* Channel 7 */ - CHAN2G(2447, 7), /* Channel 8 */ - CHAN2G(2452, 8), /* Channel 9 */ - CHAN2G(2457, 9), /* Channel 10 */ - CHAN2G(2462, 10), /* Channel 11 */ - CHAN2G(2467, 11), /* Channel 12 */ - CHAN2G(2472, 12), /* Channel 13 */ - CHAN2G(2484, 13), /* Channel 14 */ -}; - -/* Some 5 GHz radios are actually tunable on XXXX-YYYY - * on 5 MHz steps, we support the channels which we know - * we have calibration data for all cards though to make - * this static */ -static struct ieee80211_channel ath9k_5ghz_chantable[] = { - /* _We_ call this UNII 1 */ - CHAN5G(5180, 14), /* Channel 36 */ - CHAN5G(5200, 15), /* Channel 40 */ - CHAN5G(5220, 16), /* Channel 44 */ - CHAN5G(5240, 17), /* Channel 48 */ - /* _We_ call this UNII 2 */ - CHAN5G(5260, 18), /* Channel 52 */ - CHAN5G(5280, 19), /* Channel 56 */ - CHAN5G(5300, 20), /* Channel 60 */ - CHAN5G(5320, 21), /* Channel 64 */ - /* _We_ call this "Middle band" */ - CHAN5G(5500, 22), /* Channel 100 */ - CHAN5G(5520, 23), /* Channel 104 */ - CHAN5G(5540, 24), /* Channel 108 */ - CHAN5G(5560, 25), /* Channel 112 */ - CHAN5G(5580, 26), /* Channel 116 */ - CHAN5G(5600, 27), /* Channel 120 */ - CHAN5G(5620, 28), /* Channel 124 */ - CHAN5G(5640, 29), /* Channel 128 */ - CHAN5G(5660, 30), /* Channel 132 */ - CHAN5G(5680, 31), /* Channel 136 */ - CHAN5G(5700, 32), /* Channel 140 */ - /* _We_ call this UNII 3 */ - CHAN5G(5745, 33), /* Channel 149 */ - CHAN5G(5765, 34), /* Channel 153 */ - CHAN5G(5785, 35), /* Channel 157 */ - CHAN5G(5805, 36), /* Channel 161 */ - CHAN5G(5825, 37), /* Channel 165 */ -}; - -/* Atheros hardware rate code addition for short premble */ -#define SHPCHECK(__hw_rate, __flags) \ - ((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate | 0x04 ) : 0) - -#define RATE(_bitrate, _hw_rate, _flags) { \ - .bitrate = (_bitrate), \ - .flags = (_flags), \ - .hw_value = (_hw_rate), \ - .hw_value_short = (SHPCHECK(_hw_rate, _flags)) \ -} - -static struct ieee80211_rate ath9k_legacy_rates[] = { - RATE(10, 0x1b, 0), - RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE), - RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE), - RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE), - RATE(60, 0x0b, 0), - RATE(90, 0x0f, 0), - RATE(120, 0x0a, 0), - RATE(180, 0x0e, 0), - RATE(240, 0x09, 0), - RATE(360, 0x0d, 0), - RATE(480, 0x08, 0), - RATE(540, 0x0c, 0), -}; - static void ath_cache_conf_rate(struct ath_softc *sc, struct ieee80211_conf *conf) { @@ -221,7 +109,7 @@ static struct ath9k_channel *ath_get_curchannel(struct ath_softc *sc, return channel; } -static bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode) +bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode) { unsigned long flags; bool ret; @@ -255,11 +143,13 @@ void ath9k_ps_restore(struct ath_softc *sc) if (--sc->ps_usecount != 0) goto unlock; - if (sc->ps_enabled && - !(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON | - SC_OP_WAIT_FOR_CAB | - SC_OP_WAIT_FOR_PSPOLL_DATA | - SC_OP_WAIT_FOR_TX_ACK))) + if (sc->ps_idle) + ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP); + else if (sc->ps_enabled && + !(sc->ps_flags & (PS_WAIT_FOR_BEACON | + PS_WAIT_FOR_CAB | + PS_WAIT_FOR_PSPOLL_DATA | + PS_WAIT_FOR_TX_ACK))) ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP); unlock: @@ -316,7 +206,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, r = ath9k_hw_reset(ah, hchan, fastcc); if (r) { ath_print(common, ATH_DBG_FATAL, - "Unable to reset channel (%u Mhz) " + "Unable to reset channel (%u MHz), " "reset status %d\n", channel->center_freq, r); spin_unlock_bh(&sc->sc_resetlock); @@ -349,7 +239,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, * When the task is complete, it reschedules itself depending on the * appropriate interval that was calculated. */ -static void ath_ani_calibrate(unsigned long data) +void ath_ani_calibrate(unsigned long data) { struct ath_softc *sc = (struct ath_softc *)data; struct ath_hw *ah = sc->sc_ah; @@ -363,14 +253,6 @@ static void ath_ani_calibrate(unsigned long data) short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ? ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL; - /* - * don't calibrate when we're scanning. - * we are most likely not on our home channel. - */ - spin_lock(&sc->ani_lock); - if (sc->sc_flags & SC_OP_SCANNING) - goto set_timer; - /* Only calibrate if awake */ if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE) goto set_timer; @@ -437,7 +319,6 @@ static void ath_ani_calibrate(unsigned long data) ath9k_ps_restore(sc); set_timer: - spin_unlock(&sc->ani_lock); /* * Set timer interval based on previous results. * The interval must be the shortest necessary to satisfy ANI, @@ -513,7 +394,7 @@ static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta) ath_tx_node_cleanup(sc, an); } -static void ath9k_tasklet(unsigned long data) +void ath9k_tasklet(unsigned long data) { struct ath_softc *sc = (struct ath_softc *)data; struct ath_hw *ah = sc->sc_ah; @@ -545,7 +426,7 @@ static void ath9k_tasklet(unsigned long data) */ ath_print(common, ATH_DBG_PS, "TSFOOR - Sync with next Beacon\n"); - sc->sc_flags |= SC_OP_WAIT_FOR_BEACON | SC_OP_BEACON_SYNC; + sc->ps_flags |= PS_WAIT_FOR_BEACON | PS_BEACON_SYNC; } if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) @@ -646,7 +527,7 @@ irqreturn_t ath_isr(int irq, void *dev) * receive frames */ ath9k_setpower(sc, ATH9K_PM_AWAKE); ath9k_hw_setrxabort(sc->sc_ah, 0); - sc->sc_flags |= SC_OP_WAIT_FOR_BEACON; + sc->ps_flags |= PS_WAIT_FOR_BEACON; } chip_reset: @@ -928,49 +809,12 @@ static void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf clear_bit(key->hw_key_idx + 64, common->keymap); if (common->splitmic) { + ath9k_hw_keyreset(ah, key->hw_key_idx + 32); clear_bit(key->hw_key_idx + 32, common->keymap); clear_bit(key->hw_key_idx + 64 + 32, common->keymap); } } -static void setup_ht_cap(struct ath_softc *sc, - struct ieee80211_sta_ht_cap *ht_info) -{ - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - u8 tx_streams, rx_streams; - - ht_info->ht_supported = true; - ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | - IEEE80211_HT_CAP_SM_PS | - IEEE80211_HT_CAP_SGI_40 | - IEEE80211_HT_CAP_DSSSCCK40; - - ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; - ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_8; - - /* set up supported mcs set */ - memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); - tx_streams = !(common->tx_chainmask & (common->tx_chainmask - 1)) ? - 1 : 2; - rx_streams = !(common->rx_chainmask & (common->rx_chainmask - 1)) ? - 1 : 2; - - if (tx_streams != rx_streams) { - ath_print(common, ATH_DBG_CONFIG, - "TX streams %d, RX streams: %d\n", - tx_streams, rx_streams); - ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; - ht_info->mcs.tx_params |= ((tx_streams - 1) << - IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); - } - - ht_info->mcs.rx_mask[0] = 0xff; - if (rx_streams >= 2) - ht_info->mcs.rx_mask[1] = 0xff; - - ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED; -} - static void ath9k_bss_assoc_info(struct ath_softc *sc, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf) @@ -992,7 +836,7 @@ static void ath9k_bss_assoc_info(struct ath_softc *sc, * on the receipt of the first Beacon frame (i.e., * after time sync with the AP). */ - sc->sc_flags |= SC_OP_BEACON_SYNC; + sc->ps_flags |= PS_BEACON_SYNC; /* Configure the beacon */ ath_beacon_config(sc, vif); @@ -1009,174 +853,6 @@ static void ath9k_bss_assoc_info(struct ath_softc *sc, } } -/********************************/ -/* LED functions */ -/********************************/ - -static void ath_led_blink_work(struct work_struct *work) -{ - struct ath_softc *sc = container_of(work, struct ath_softc, - ath_led_blink_work.work); - - if (!(sc->sc_flags & SC_OP_LED_ASSOCIATED)) - return; - - if ((sc->led_on_duration == ATH_LED_ON_DURATION_IDLE) || - (sc->led_off_duration == ATH_LED_OFF_DURATION_IDLE)) - ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0); - else - ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, - (sc->sc_flags & SC_OP_LED_ON) ? 1 : 0); - - ieee80211_queue_delayed_work(sc->hw, - &sc->ath_led_blink_work, - (sc->sc_flags & SC_OP_LED_ON) ? - msecs_to_jiffies(sc->led_off_duration) : - msecs_to_jiffies(sc->led_on_duration)); - - sc->led_on_duration = sc->led_on_cnt ? - max((ATH_LED_ON_DURATION_IDLE - sc->led_on_cnt), 25) : - ATH_LED_ON_DURATION_IDLE; - sc->led_off_duration = sc->led_off_cnt ? - max((ATH_LED_OFF_DURATION_IDLE - sc->led_off_cnt), 10) : - ATH_LED_OFF_DURATION_IDLE; - sc->led_on_cnt = sc->led_off_cnt = 0; - if (sc->sc_flags & SC_OP_LED_ON) - sc->sc_flags &= ~SC_OP_LED_ON; - else - sc->sc_flags |= SC_OP_LED_ON; -} - -static void ath_led_brightness(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev); - struct ath_softc *sc = led->sc; - - switch (brightness) { - case LED_OFF: - if (led->led_type == ATH_LED_ASSOC || - led->led_type == ATH_LED_RADIO) { - ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, - (led->led_type == ATH_LED_RADIO)); - sc->sc_flags &= ~SC_OP_LED_ASSOCIATED; - if (led->led_type == ATH_LED_RADIO) - sc->sc_flags &= ~SC_OP_LED_ON; - } else { - sc->led_off_cnt++; - } - break; - case LED_FULL: - if (led->led_type == ATH_LED_ASSOC) { - sc->sc_flags |= SC_OP_LED_ASSOCIATED; - ieee80211_queue_delayed_work(sc->hw, - &sc->ath_led_blink_work, 0); - } else if (led->led_type == ATH_LED_RADIO) { - ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0); - sc->sc_flags |= SC_OP_LED_ON; - } else { - sc->led_on_cnt++; - } - break; - default: - break; - } -} - -static int ath_register_led(struct ath_softc *sc, struct ath_led *led, - char *trigger) -{ - int ret; - - led->sc = sc; - led->led_cdev.name = led->name; - led->led_cdev.default_trigger = trigger; - led->led_cdev.brightness_set = ath_led_brightness; - - ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->led_cdev); - if (ret) - ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL, - "Failed to register led:%s", led->name); - else - led->registered = 1; - return ret; -} - -static void ath_unregister_led(struct ath_led *led) -{ - if (led->registered) { - led_classdev_unregister(&led->led_cdev); - led->registered = 0; - } -} - -static void ath_deinit_leds(struct ath_softc *sc) -{ - ath_unregister_led(&sc->assoc_led); - sc->sc_flags &= ~SC_OP_LED_ASSOCIATED; - ath_unregister_led(&sc->tx_led); - ath_unregister_led(&sc->rx_led); - ath_unregister_led(&sc->radio_led); - ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1); -} - -static void ath_init_leds(struct ath_softc *sc) -{ - char *trigger; - int ret; - - if (AR_SREV_9287(sc->sc_ah)) - sc->sc_ah->led_pin = ATH_LED_PIN_9287; - else - sc->sc_ah->led_pin = ATH_LED_PIN_DEF; - - /* Configure gpio 1 for output */ - ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin, - AR_GPIO_OUTPUT_MUX_AS_OUTPUT); - /* LED off, active low */ - ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1); - - INIT_DELAYED_WORK(&sc->ath_led_blink_work, ath_led_blink_work); - - trigger = ieee80211_get_radio_led_name(sc->hw); - snprintf(sc->radio_led.name, sizeof(sc->radio_led.name), - "ath9k-%s::radio", wiphy_name(sc->hw->wiphy)); - ret = ath_register_led(sc, &sc->radio_led, trigger); - sc->radio_led.led_type = ATH_LED_RADIO; - if (ret) - goto fail; - - trigger = ieee80211_get_assoc_led_name(sc->hw); - snprintf(sc->assoc_led.name, sizeof(sc->assoc_led.name), - "ath9k-%s::assoc", wiphy_name(sc->hw->wiphy)); - ret = ath_register_led(sc, &sc->assoc_led, trigger); - sc->assoc_led.led_type = ATH_LED_ASSOC; - if (ret) - goto fail; - - trigger = ieee80211_get_tx_led_name(sc->hw); - snprintf(sc->tx_led.name, sizeof(sc->tx_led.name), - "ath9k-%s::tx", wiphy_name(sc->hw->wiphy)); - ret = ath_register_led(sc, &sc->tx_led, trigger); - sc->tx_led.led_type = ATH_LED_TX; - if (ret) - goto fail; - - trigger = ieee80211_get_rx_led_name(sc->hw); - snprintf(sc->rx_led.name, sizeof(sc->rx_led.name), - "ath9k-%s::rx", wiphy_name(sc->hw->wiphy)); - ret = ath_register_led(sc, &sc->rx_led, trigger); - sc->rx_led.led_type = ATH_LED_RX; - if (ret) - goto fail; - - return; - -fail: - cancel_delayed_work_sync(&sc->ath_led_blink_work); - ath_deinit_leds(sc); -} - void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw) { struct ath_hw *ah = sc->sc_ah; @@ -1194,7 +870,7 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw) r = ath9k_hw_reset(ah, ah->curchan, false); if (r) { ath_print(common, ATH_DBG_FATAL, - "Unable to reset channel %u (%uMhz) ", + "Unable to reset channel (%u MHz), " "reset status %d\n", channel->center_freq, r); } @@ -1249,7 +925,7 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw) r = ath9k_hw_reset(ah, ah->curchan, false); if (r) { ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL, - "Unable to reset channel %u (%uMhz) " + "Unable to reset channel (%u MHz), " "reset status %d\n", channel->center_freq, r); } @@ -1261,711 +937,6 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw) ath9k_setpower(sc, ATH9K_PM_FULL_SLEEP); } -/*******************/ -/* Rfkill */ -/*******************/ - -static bool ath_is_rfkill_set(struct ath_softc *sc) -{ - struct ath_hw *ah = sc->sc_ah; - - return ath9k_hw_gpio_get(ah, ah->rfkill_gpio) == - ah->rfkill_polarity; -} - -static void ath9k_rfkill_poll_state(struct ieee80211_hw *hw) -{ - struct ath_wiphy *aphy = hw->priv; - struct ath_softc *sc = aphy->sc; - bool blocked = !!ath_is_rfkill_set(sc); - - wiphy_rfkill_set_hw_state(hw->wiphy, blocked); -} - -static void ath_start_rfkill_poll(struct ath_softc *sc) -{ - struct ath_hw *ah = sc->sc_ah; - - if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) - wiphy_rfkill_start_polling(sc->hw->wiphy); -} - -static void ath9k_uninit_hw(struct ath_softc *sc) -{ - struct ath_hw *ah = sc->sc_ah; - - BUG_ON(!ah); - - ath9k_exit_debug(ah); - ath9k_hw_detach(ah); - sc->sc_ah = NULL; -} - -static void ath_clean_core(struct ath_softc *sc) -{ - struct ieee80211_hw *hw = sc->hw; - struct ath_hw *ah = sc->sc_ah; - int i = 0; - - ath9k_ps_wakeup(sc); - - dev_dbg(sc->dev, "Detach ATH hw\n"); - - ath_deinit_leds(sc); - wiphy_rfkill_stop_polling(sc->hw->wiphy); - - for (i = 0; i < sc->num_sec_wiphy; i++) { - struct ath_wiphy *aphy = sc->sec_wiphy[i]; - if (aphy == NULL) - continue; - sc->sec_wiphy[i] = NULL; - ieee80211_unregister_hw(aphy->hw); - ieee80211_free_hw(aphy->hw); - } - ieee80211_unregister_hw(hw); - ath_rx_cleanup(sc); - ath_tx_cleanup(sc); - - tasklet_kill(&sc->intr_tq); - tasklet_kill(&sc->bcon_tasklet); - - if (!(sc->sc_flags & SC_OP_INVALID)) - ath9k_setpower(sc, ATH9K_PM_AWAKE); - - /* cleanup tx queues */ - for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) - if (ATH_TXQ_SETUP(sc, i)) - ath_tx_cleanupq(sc, &sc->tx.txq[i]); - - if ((sc->btcoex.no_stomp_timer) && - ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) - ath_gen_timer_free(ah, sc->btcoex.no_stomp_timer); -} - -void ath_detach(struct ath_softc *sc) -{ - ath_clean_core(sc); - ath9k_uninit_hw(sc); -} - -void ath_cleanup(struct ath_softc *sc) -{ - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - - ath_clean_core(sc); - free_irq(sc->irq, sc); - ath_bus_cleanup(common); - kfree(sc->sec_wiphy); - ieee80211_free_hw(sc->hw); - - ath9k_uninit_hw(sc); -} - -static int ath9k_reg_notifier(struct wiphy *wiphy, - struct regulatory_request *request) -{ - struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); - struct ath_wiphy *aphy = hw->priv; - struct ath_softc *sc = aphy->sc; - struct ath_regulatory *reg = ath9k_hw_regulatory(sc->sc_ah); - - return ath_reg_notifier_apply(wiphy, request, reg); -} - -/* - * Detects if there is any priority bt traffic - */ -static void ath_detect_bt_priority(struct ath_softc *sc) -{ - struct ath_btcoex *btcoex = &sc->btcoex; - struct ath_hw *ah = sc->sc_ah; - - if (ath9k_hw_gpio_get(sc->sc_ah, ah->btcoex_hw.btpriority_gpio)) - btcoex->bt_priority_cnt++; - - if (time_after(jiffies, btcoex->bt_priority_time + - msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) { - if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) { - ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_BTCOEX, - "BT priority traffic detected"); - sc->sc_flags |= SC_OP_BT_PRIORITY_DETECTED; - } else { - sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED; - } - - btcoex->bt_priority_cnt = 0; - btcoex->bt_priority_time = jiffies; - } -} - -/* - * Configures appropriate weight based on stomp type. - */ -static void ath9k_btcoex_bt_stomp(struct ath_softc *sc, - enum ath_stomp_type stomp_type) -{ - struct ath_hw *ah = sc->sc_ah; - - switch (stomp_type) { - case ATH_BTCOEX_STOMP_ALL: - ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, - AR_STOMP_ALL_WLAN_WGHT); - break; - case ATH_BTCOEX_STOMP_LOW: - ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, - AR_STOMP_LOW_WLAN_WGHT); - break; - case ATH_BTCOEX_STOMP_NONE: - ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, - AR_STOMP_NONE_WLAN_WGHT); - break; - default: - ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX, - "Invalid Stomptype\n"); - break; - } - - ath9k_hw_btcoex_enable(ah); -} - -static void ath9k_gen_timer_start(struct ath_hw *ah, - struct ath_gen_timer *timer, - u32 timer_next, - u32 timer_period) -{ - struct ath_common *common = ath9k_hw_common(ah); - struct ath_softc *sc = (struct ath_softc *) common->priv; - - ath9k_hw_gen_timer_start(ah, timer, timer_next, timer_period); - - if ((sc->imask & ATH9K_INT_GENTIMER) == 0) { - ath9k_hw_set_interrupts(ah, 0); - sc->imask |= ATH9K_INT_GENTIMER; - ath9k_hw_set_interrupts(ah, sc->imask); - } -} - -static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer) -{ - struct ath_common *common = ath9k_hw_common(ah); - struct ath_softc *sc = (struct ath_softc *) common->priv; - struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; - - ath9k_hw_gen_timer_stop(ah, timer); - - /* if no timer is enabled, turn off interrupt mask */ - if (timer_table->timer_mask.val == 0) { - ath9k_hw_set_interrupts(ah, 0); - sc->imask &= ~ATH9K_INT_GENTIMER; - ath9k_hw_set_interrupts(ah, sc->imask); - } -} - -/* - * This is the master bt coex timer which runs for every - * 45ms, bt traffic will be given priority during 55% of this - * period while wlan gets remaining 45% - */ -static void ath_btcoex_period_timer(unsigned long data) -{ - struct ath_softc *sc = (struct ath_softc *) data; - struct ath_hw *ah = sc->sc_ah; - struct ath_btcoex *btcoex = &sc->btcoex; - - ath_detect_bt_priority(sc); - - spin_lock_bh(&btcoex->btcoex_lock); - - ath9k_btcoex_bt_stomp(sc, btcoex->bt_stomp_type); - - spin_unlock_bh(&btcoex->btcoex_lock); - - if (btcoex->btcoex_period != btcoex->btcoex_no_stomp) { - if (btcoex->hw_timer_enabled) - ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer); - - ath9k_gen_timer_start(ah, - btcoex->no_stomp_timer, - (ath9k_hw_gettsf32(ah) + - btcoex->btcoex_no_stomp), - btcoex->btcoex_no_stomp * 10); - btcoex->hw_timer_enabled = true; - } - - mod_timer(&btcoex->period_timer, jiffies + - msecs_to_jiffies(ATH_BTCOEX_DEF_BT_PERIOD)); -} - -/* - * Generic tsf based hw timer which configures weight - * registers to time slice between wlan and bt traffic - */ -static void ath_btcoex_no_stomp_timer(void *arg) -{ - struct ath_softc *sc = (struct ath_softc *)arg; - struct ath_hw *ah = sc->sc_ah; - struct ath_btcoex *btcoex = &sc->btcoex; - - ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX, - "no stomp timer running \n"); - - spin_lock_bh(&btcoex->btcoex_lock); - - if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW) - ath9k_btcoex_bt_stomp(sc, ATH_BTCOEX_STOMP_NONE); - else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) - ath9k_btcoex_bt_stomp(sc, ATH_BTCOEX_STOMP_LOW); - - spin_unlock_bh(&btcoex->btcoex_lock); -} - -static int ath_init_btcoex_timer(struct ath_softc *sc) -{ - struct ath_btcoex *btcoex = &sc->btcoex; - - btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD * 1000; - btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) * - btcoex->btcoex_period / 100; - - setup_timer(&btcoex->period_timer, ath_btcoex_period_timer, - (unsigned long) sc); - - spin_lock_init(&btcoex->btcoex_lock); - - btcoex->no_stomp_timer = ath_gen_timer_alloc(sc->sc_ah, - ath_btcoex_no_stomp_timer, - ath_btcoex_no_stomp_timer, - (void *) sc, AR_FIRST_NDP_TIMER); - - if (!btcoex->no_stomp_timer) - return -ENOMEM; - - return 0; -} - -/* - * Read and write, they both share the same lock. We do this to serialize - * reads and writes on Atheros 802.11n PCI devices only. This is required - * as the FIFO on these devices can only accept sanely 2 requests. After - * that the device goes bananas. Serializing the reads/writes prevents this - * from happening. - */ - -static void ath9k_iowrite32(void *hw_priv, u32 val, u32 reg_offset) -{ - struct ath_hw *ah = (struct ath_hw *) hw_priv; - struct ath_common *common = ath9k_hw_common(ah); - struct ath_softc *sc = (struct ath_softc *) common->priv; - - if (ah->config.serialize_regmode == SER_REG_MODE_ON) { - unsigned long flags; - spin_lock_irqsave(&sc->sc_serial_rw, flags); - iowrite32(val, sc->mem + reg_offset); - spin_unlock_irqrestore(&sc->sc_serial_rw, flags); - } else - iowrite32(val, sc->mem + reg_offset); -} - -static unsigned int ath9k_ioread32(void *hw_priv, u32 reg_offset) -{ - struct ath_hw *ah = (struct ath_hw *) hw_priv; - struct ath_common *common = ath9k_hw_common(ah); - struct ath_softc *sc = (struct ath_softc *) common->priv; - u32 val; - - if (ah->config.serialize_regmode == SER_REG_MODE_ON) { - unsigned long flags; - spin_lock_irqsave(&sc->sc_serial_rw, flags); - val = ioread32(sc->mem + reg_offset); - spin_unlock_irqrestore(&sc->sc_serial_rw, flags); - } else - val = ioread32(sc->mem + reg_offset); - return val; -} - -static const struct ath_ops ath9k_common_ops = { - .read = ath9k_ioread32, - .write = ath9k_iowrite32, -}; - -/* - * Initialize and fill ath_softc, ath_sofct is the - * "Software Carrier" struct. Historically it has existed - * to allow the separation between hardware specific - * variables (now in ath_hw) and driver specific variables. - */ -static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid, - const struct ath_bus_ops *bus_ops) -{ - struct ath_hw *ah = NULL; - struct ath_common *common; - int r = 0, i; - int csz = 0; - int qnum; - - /* XXX: hardware will not be ready until ath_open() being called */ - sc->sc_flags |= SC_OP_INVALID; - - spin_lock_init(&sc->wiphy_lock); - spin_lock_init(&sc->sc_resetlock); - spin_lock_init(&sc->sc_serial_rw); - spin_lock_init(&sc->ani_lock); - spin_lock_init(&sc->sc_pm_lock); - mutex_init(&sc->mutex); - tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc); - tasklet_init(&sc->bcon_tasklet, ath_beacon_tasklet, - (unsigned long)sc); - - ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL); - if (!ah) - return -ENOMEM; - - ah->hw_version.devid = devid; - ah->hw_version.subsysid = subsysid; - sc->sc_ah = ah; - - common = ath9k_hw_common(ah); - common->ops = &ath9k_common_ops; - common->bus_ops = bus_ops; - common->ah = ah; - common->hw = sc->hw; - common->priv = sc; - common->debug_mask = ath9k_debug; - - /* - * Cache line size is used to size and align various - * structures used to communicate with the hardware. - */ - ath_read_cachesize(common, &csz); - /* XXX assert csz is non-zero */ - common->cachelsz = csz << 2; /* convert to bytes */ - - r = ath9k_hw_init(ah); - if (r) { - ath_print(common, ATH_DBG_FATAL, - "Unable to initialize hardware; " - "initialization status: %d\n", r); - goto bad_free_hw; - } - - if (ath9k_init_debug(ah) < 0) { - ath_print(common, ATH_DBG_FATAL, - "Unable to create debugfs files\n"); - goto bad_free_hw; - } - - /* Get the hardware key cache size. */ - common->keymax = ah->caps.keycache_size; - if (common->keymax > ATH_KEYMAX) { - ath_print(common, ATH_DBG_ANY, - "Warning, using only %u entries in %u key cache\n", - ATH_KEYMAX, common->keymax); - common->keymax = ATH_KEYMAX; - } - - /* - * Reset the key cache since some parts do not - * reset the contents on initial power up. - */ - for (i = 0; i < common->keymax; i++) - ath9k_hw_keyreset(ah, (u16) i); - - /* default to MONITOR mode */ - sc->sc_ah->opmode = NL80211_IFTYPE_MONITOR; - - /* - * Allocate hardware transmit queues: one queue for - * beacon frames and one data queue for each QoS - * priority. Note that the hal handles reseting - * these queues at the needed time. - */ - sc->beacon.beaconq = ath9k_hw_beaconq_setup(ah); - if (sc->beacon.beaconq == -1) { - ath_print(common, ATH_DBG_FATAL, - "Unable to setup a beacon xmit queue\n"); - r = -EIO; - goto bad2; - } - sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0); - if (sc->beacon.cabq == NULL) { - ath_print(common, ATH_DBG_FATAL, - "Unable to setup CAB xmit queue\n"); - r = -EIO; - goto bad2; - } - - sc->config.cabqReadytime = ATH_CABQ_READY_TIME; - ath_cabq_update(sc); - - for (i = 0; i < ARRAY_SIZE(sc->tx.hwq_map); i++) - sc->tx.hwq_map[i] = -1; - - /* Setup data queues */ - /* NB: ensure BK queue is the lowest priority h/w queue */ - if (!ath_tx_setup(sc, ATH9K_WME_AC_BK)) { - ath_print(common, ATH_DBG_FATAL, - "Unable to setup xmit queue for BK traffic\n"); - r = -EIO; - goto bad2; - } - - if (!ath_tx_setup(sc, ATH9K_WME_AC_BE)) { - ath_print(common, ATH_DBG_FATAL, - "Unable to setup xmit queue for BE traffic\n"); - r = -EIO; - goto bad2; - } - if (!ath_tx_setup(sc, ATH9K_WME_AC_VI)) { - ath_print(common, ATH_DBG_FATAL, - "Unable to setup xmit queue for VI traffic\n"); - r = -EIO; - goto bad2; - } - if (!ath_tx_setup(sc, ATH9K_WME_AC_VO)) { - ath_print(common, ATH_DBG_FATAL, - "Unable to setup xmit queue for VO traffic\n"); - r = -EIO; - goto bad2; - } - - /* Initializes the noise floor to a reasonable default value. - * Later on this will be updated during ANI processing. */ - - common->ani.noise_floor = ATH_DEFAULT_NOISE_FLOOR; - setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc); - - if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER, - ATH9K_CIPHER_TKIP, NULL)) { - /* - * Whether we should enable h/w TKIP MIC. - * XXX: if we don't support WME TKIP MIC, then we wouldn't - * report WMM capable, so it's always safe to turn on - * TKIP MIC in this case. - */ - ath9k_hw_setcapability(sc->sc_ah, ATH9K_CAP_TKIP_MIC, - 0, 1, NULL); - } - - /* - * Check whether the separate key cache entries - * are required to handle both tx+rx MIC keys. - * With split mic keys the number of stations is limited - * to 27 otherwise 59. - */ - if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER, - ATH9K_CIPHER_TKIP, NULL) - && ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER, - ATH9K_CIPHER_MIC, NULL) - && ath9k_hw_getcapability(ah, ATH9K_CAP_TKIP_SPLIT, - 0, NULL)) - common->splitmic = 1; - - /* turn on mcast key search if possible */ - if (!ath9k_hw_getcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL)) - (void)ath9k_hw_setcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 1, - 1, NULL); - - sc->config.txpowlimit = ATH_TXPOWER_MAX; - - /* 11n Capabilities */ - if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) { - sc->sc_flags |= SC_OP_TXAGGR; - sc->sc_flags |= SC_OP_RXAGGR; - } - - common->tx_chainmask = ah->caps.tx_chainmask; - common->rx_chainmask = ah->caps.rx_chainmask; - - ath9k_hw_setcapability(ah, ATH9K_CAP_DIVERSITY, 1, true, NULL); - sc->rx.defant = ath9k_hw_getdefantenna(ah); - - if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) - memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); - - sc->beacon.slottime = ATH9K_SLOT_TIME_9; /* default to short slot time */ - - /* initialize beacon slots */ - for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) { - sc->beacon.bslot[i] = NULL; - sc->beacon.bslot_aphy[i] = NULL; - } - - /* setup channels and rates */ - - if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) { - sc->sbands[IEEE80211_BAND_2GHZ].channels = ath9k_2ghz_chantable; - sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; - sc->sbands[IEEE80211_BAND_2GHZ].n_channels = - ARRAY_SIZE(ath9k_2ghz_chantable); - sc->sbands[IEEE80211_BAND_2GHZ].bitrates = ath9k_legacy_rates; - sc->sbands[IEEE80211_BAND_2GHZ].n_bitrates = - ARRAY_SIZE(ath9k_legacy_rates); - } - - if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) { - sc->sbands[IEEE80211_BAND_5GHZ].channels = ath9k_5ghz_chantable; - sc->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ; - sc->sbands[IEEE80211_BAND_5GHZ].n_channels = - ARRAY_SIZE(ath9k_5ghz_chantable); - sc->sbands[IEEE80211_BAND_5GHZ].bitrates = - ath9k_legacy_rates + 4; - sc->sbands[IEEE80211_BAND_5GHZ].n_bitrates = - ARRAY_SIZE(ath9k_legacy_rates) - 4; - } - - switch (ah->btcoex_hw.scheme) { - case ATH_BTCOEX_CFG_NONE: - break; - case ATH_BTCOEX_CFG_2WIRE: - ath9k_hw_btcoex_init_2wire(ah); - break; - case ATH_BTCOEX_CFG_3WIRE: - ath9k_hw_btcoex_init_3wire(ah); - r = ath_init_btcoex_timer(sc); - if (r) - goto bad2; - qnum = ath_tx_get_qnum(sc, ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BE); - ath9k_hw_init_btcoex_hw(ah, qnum); - sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; - break; - default: - WARN_ON(1); - break; - } - - return 0; -bad2: - /* cleanup tx queues */ - for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) - if (ATH_TXQ_SETUP(sc, i)) - ath_tx_cleanupq(sc, &sc->tx.txq[i]); - -bad_free_hw: - ath9k_uninit_hw(sc); - return r; -} - -void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) -{ - hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | - IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | - IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_AMPDU_AGGREGATION | - IEEE80211_HW_SUPPORTS_PS | - IEEE80211_HW_PS_NULLFUNC_STACK | - IEEE80211_HW_SPECTRUM_MGMT; - - if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || modparam_nohwcrypt) - hw->flags |= IEEE80211_HW_MFP_CAPABLE; - - hw->wiphy->interface_modes = - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_MESH_POINT); - - hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; - - hw->queues = 4; - hw->max_rates = 4; - hw->channel_change_time = 5000; - hw->max_listen_interval = 10; - /* Hardware supports 10 but we use 4 */ - hw->max_rate_tries = 4; - hw->sta_data_size = sizeof(struct ath_node); - hw->vif_data_size = sizeof(struct ath_vif); - - hw->rate_control_algorithm = "ath9k_rate_control"; - - if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = - &sc->sbands[IEEE80211_BAND_2GHZ]; - if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) - hw->wiphy->bands[IEEE80211_BAND_5GHZ] = - &sc->sbands[IEEE80211_BAND_5GHZ]; -} - -/* Device driver core initialization */ -int ath_init_device(u16 devid, struct ath_softc *sc, u16 subsysid, - const struct ath_bus_ops *bus_ops) -{ - struct ieee80211_hw *hw = sc->hw; - struct ath_common *common; - struct ath_hw *ah; - int error = 0, i; - struct ath_regulatory *reg; - - dev_dbg(sc->dev, "Attach ATH hw\n"); - - error = ath_init_softc(devid, sc, subsysid, bus_ops); - if (error != 0) - return error; - - ah = sc->sc_ah; - common = ath9k_hw_common(ah); - - /* get mac address from hardware and set in mac80211 */ - - SET_IEEE80211_PERM_ADDR(hw, common->macaddr); - - ath_set_hw_capab(sc, hw); - - error = ath_regd_init(&common->regulatory, sc->hw->wiphy, - ath9k_reg_notifier); - if (error) - return error; - - reg = &common->regulatory; - - if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) { - if (test_bit(ATH9K_MODE_11G, ah->caps.wireless_modes)) - setup_ht_cap(sc, - &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap); - if (test_bit(ATH9K_MODE_11A, ah->caps.wireless_modes)) - setup_ht_cap(sc, - &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap); - } - - /* initialize tx/rx engine */ - error = ath_tx_init(sc, ATH_TXBUF); - if (error != 0) - goto error_attach; - - error = ath_rx_init(sc, ATH_RXBUF); - if (error != 0) - goto error_attach; - - INIT_WORK(&sc->chan_work, ath9k_wiphy_chan_work); - INIT_DELAYED_WORK(&sc->wiphy_work, ath9k_wiphy_work); - sc->wiphy_scheduler_int = msecs_to_jiffies(500); - - error = ieee80211_register_hw(hw); - - if (!ath_is_world_regd(reg)) { - error = regulatory_hint(hw->wiphy, reg->alpha2); - if (error) - goto error_attach; - } - - /* Initialize LED control */ - ath_init_leds(sc); - - ath_start_rfkill_poll(sc); - - return 0; - -error_attach: - /* cleanup tx queues */ - for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) - if (ATH_TXQ_SETUP(sc, i)) - ath_tx_cleanupq(sc, &sc->tx.txq[i]); - - ath9k_uninit_hw(sc); - - return error; -} - int ath_reset(struct ath_softc *sc, bool retry_tx) { struct ath_hw *ah = sc->sc_ah; @@ -1976,6 +947,8 @@ int ath_reset(struct ath_softc *sc, bool retry_tx) /* Stop ANI */ del_timer_sync(&common->ani.timer); + ieee80211_stop_queues(hw); + ath9k_hw_set_interrupts(ah, 0); ath_drain_all_txq(sc, retry_tx); ath_stoprecv(sc); @@ -2017,131 +990,14 @@ int ath_reset(struct ath_softc *sc, bool retry_tx) } } + ieee80211_wake_queues(hw); + /* Start ANI */ ath_start_ani(common); return r; } -/* - * This function will allocate both the DMA descriptor structure, and the - * buffers it contains. These are used to contain the descriptors used - * by the system. -*/ -int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, - struct list_head *head, const char *name, - int nbuf, int ndesc) -{ -#define DS2PHYS(_dd, _ds) \ - ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc)) -#define ATH_DESC_4KB_BOUND_CHECK(_daddr) ((((_daddr) & 0xFFF) > 0xF7F) ? 1 : 0) -#define ATH_DESC_4KB_BOUND_NUM_SKIPPED(_len) ((_len) / 4096) - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_desc *ds; - struct ath_buf *bf; - int i, bsize, error; - - ath_print(common, ATH_DBG_CONFIG, "%s DMA: %u buffers %u desc/buf\n", - name, nbuf, ndesc); - - INIT_LIST_HEAD(head); - /* ath_desc must be a multiple of DWORDs */ - if ((sizeof(struct ath_desc) % 4) != 0) { - ath_print(common, ATH_DBG_FATAL, - "ath_desc not DWORD aligned\n"); - BUG_ON((sizeof(struct ath_desc) % 4) != 0); - error = -ENOMEM; - goto fail; - } - - dd->dd_desc_len = sizeof(struct ath_desc) * nbuf * ndesc; - - /* - * Need additional DMA memory because we can't use - * descriptors that cross the 4K page boundary. Assume - * one skipped descriptor per 4K page. - */ - if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_4KB_SPLITTRANS)) { - u32 ndesc_skipped = - ATH_DESC_4KB_BOUND_NUM_SKIPPED(dd->dd_desc_len); - u32 dma_len; - - while (ndesc_skipped) { - dma_len = ndesc_skipped * sizeof(struct ath_desc); - dd->dd_desc_len += dma_len; - - ndesc_skipped = ATH_DESC_4KB_BOUND_NUM_SKIPPED(dma_len); - }; - } - - /* allocate descriptors */ - dd->dd_desc = dma_alloc_coherent(sc->dev, dd->dd_desc_len, - &dd->dd_desc_paddr, GFP_KERNEL); - if (dd->dd_desc == NULL) { - error = -ENOMEM; - goto fail; - } - ds = dd->dd_desc; - ath_print(common, ATH_DBG_CONFIG, "%s DMA map: %p (%u) -> %llx (%u)\n", - name, ds, (u32) dd->dd_desc_len, - ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len); - - /* allocate buffers */ - bsize = sizeof(struct ath_buf) * nbuf; - bf = kzalloc(bsize, GFP_KERNEL); - if (bf == NULL) { - error = -ENOMEM; - goto fail2; - } - dd->dd_bufptr = bf; - - for (i = 0; i < nbuf; i++, bf++, ds += ndesc) { - bf->bf_desc = ds; - bf->bf_daddr = DS2PHYS(dd, ds); - - if (!(sc->sc_ah->caps.hw_caps & - ATH9K_HW_CAP_4KB_SPLITTRANS)) { - /* - * Skip descriptor addresses which can cause 4KB - * boundary crossing (addr + length) with a 32 dword - * descriptor fetch. - */ - while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) { - BUG_ON((caddr_t) bf->bf_desc >= - ((caddr_t) dd->dd_desc + - dd->dd_desc_len)); - - ds += ndesc; - bf->bf_desc = ds; - bf->bf_daddr = DS2PHYS(dd, ds); - } - } - list_add_tail(&bf->list, head); - } - return 0; -fail2: - dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc, - dd->dd_desc_paddr); -fail: - memset(dd, 0, sizeof(*dd)); - return error; -#undef ATH_DESC_4KB_BOUND_CHECK -#undef ATH_DESC_4KB_BOUND_NUM_SKIPPED -#undef DS2PHYS -} - -void ath_descdma_cleanup(struct ath_softc *sc, - struct ath_descdma *dd, - struct list_head *head) -{ - dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc, - dd->dd_desc_paddr); - - INIT_LIST_HEAD(head); - kfree(dd->dd_bufptr); - memset(dd, 0, sizeof(*dd)); -} - int ath_get_hal_qnum(u16 queue, struct ath_softc *sc) { int qnum; @@ -2220,28 +1076,6 @@ void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw, /* mac80211 callbacks */ /**********************/ -/* - * (Re)start btcoex timers - */ -static void ath9k_btcoex_timer_resume(struct ath_softc *sc) -{ - struct ath_btcoex *btcoex = &sc->btcoex; - struct ath_hw *ah = sc->sc_ah; - - ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX, - "Starting btcoex timers"); - - /* make sure duty cycle timer is also stopped when resuming */ - if (btcoex->hw_timer_enabled) - ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer); - - btcoex->bt_priority_cnt = 0; - btcoex->bt_priority_time = jiffies; - sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED; - - mod_timer(&btcoex->period_timer, jiffies); -} - static int ath9k_start(struct ieee80211_hw *hw) { struct ath_wiphy *aphy = hw->priv; @@ -2411,11 +1245,11 @@ static int ath9k_tx(struct ieee80211_hw *hw, if (ieee80211_is_pspoll(hdr->frame_control)) { ath_print(common, ATH_DBG_PS, "Sending PS-Poll to pick a buffered frame\n"); - sc->sc_flags |= SC_OP_WAIT_FOR_PSPOLL_DATA; + sc->ps_flags |= PS_WAIT_FOR_PSPOLL_DATA; } else { ath_print(common, ATH_DBG_PS, "Wake up to complete TX\n"); - sc->sc_flags |= SC_OP_WAIT_FOR_TX_ACK; + sc->ps_flags |= PS_WAIT_FOR_TX_ACK; } /* * The actual restore operation will happen only after @@ -2468,22 +1302,6 @@ exit: return 0; } -/* - * Pause btcoex timer and bt duty cycle timer - */ -static void ath9k_btcoex_timer_pause(struct ath_softc *sc) -{ - struct ath_btcoex *btcoex = &sc->btcoex; - struct ath_hw *ah = sc->sc_ah; - - del_timer_sync(&btcoex->period_timer); - - if (btcoex->hw_timer_enabled) - ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer); - - btcoex->hw_timer_enabled = false; -} - static void ath9k_stop(struct ieee80211_hw *hw) { struct ath_wiphy *aphy = hw->priv; @@ -2550,12 +1368,12 @@ static void ath9k_stop(struct ieee80211_hw *hw) } static int ath9k_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_vif *avp = (void *)conf->vif->drv_priv; + struct ath_vif *avp = (void *)vif->drv_priv; enum nl80211_iftype ic_opmode = NL80211_IFTYPE_UNSPECIFIED; int ret = 0; @@ -2567,7 +1385,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, goto out; } - switch (conf->type) { + switch (vif->type) { case NL80211_IFTYPE_STATION: ic_opmode = NL80211_IFTYPE_STATION; break; @@ -2578,11 +1396,11 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, ret = -ENOBUFS; goto out; } - ic_opmode = conf->type; + ic_opmode = vif->type; break; default: ath_print(common, ATH_DBG_FATAL, - "Interface type %d not yet supported\n", conf->type); + "Interface type %d not yet supported\n", vif->type); ret = -EOPNOTSUPP; goto out; } @@ -2614,18 +1432,18 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, * Enable MIB interrupts when there are hardware phy counters. * Note we only do this (at the moment) for station mode. */ - if ((conf->type == NL80211_IFTYPE_STATION) || - (conf->type == NL80211_IFTYPE_ADHOC) || - (conf->type == NL80211_IFTYPE_MESH_POINT)) { + if ((vif->type == NL80211_IFTYPE_STATION) || + (vif->type == NL80211_IFTYPE_ADHOC) || + (vif->type == NL80211_IFTYPE_MESH_POINT)) { sc->imask |= ATH9K_INT_MIB; sc->imask |= ATH9K_INT_TSFOOR; } ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); - if (conf->type == NL80211_IFTYPE_AP || - conf->type == NL80211_IFTYPE_ADHOC || - conf->type == NL80211_IFTYPE_MONITOR) + if (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MONITOR) ath_start_ani(common); out: @@ -2634,12 +1452,12 @@ out: } static void ath9k_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_vif *avp = (void *)conf->vif->drv_priv; + struct ath_vif *avp = (void *)vif->drv_priv; int i; ath_print(common, ATH_DBG_CONFIG, "Detach Interface\n"); @@ -2662,7 +1480,7 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, sc->sc_flags &= ~SC_OP_BEACONS; for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) { - if (sc->beacon.bslot[i] == conf->vif) { + if (sc->beacon.bslot[i] == vif) { printk(KERN_DEBUG "%s: vif had allocated beacon " "slot\n", __func__); sc->beacon.bslot[i] = NULL; @@ -2675,6 +1493,19 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, mutex_unlock(&sc->mutex); } +void ath9k_enable_ps(struct ath_softc *sc) +{ + sc->ps_enabled = true; + if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { + if ((sc->imask & ATH9K_INT_TIM_TIMER) == 0) { + sc->imask |= ATH9K_INT_TIM_TIMER; + ath9k_hw_set_interrupts(sc->sc_ah, + sc->imask); + } + } + ath9k_hw_setrxabort(sc->sc_ah, 1); +} + static int ath9k_config(struct ieee80211_hw *hw, u32 changed) { struct ath_wiphy *aphy = hw->priv; @@ -2713,6 +1544,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) spin_unlock_bh(&sc->wiphy_lock); if (enable_radio) { + sc->ps_idle = false; ath_radio_enable(sc, hw); ath_print(common, ATH_DBG_CONFIG, "not-idle: enabling radio\n"); @@ -2727,36 +1559,27 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) */ if (changed & IEEE80211_CONF_CHANGE_PS) { if (conf->flags & IEEE80211_CONF_PS) { - sc->sc_flags |= SC_OP_PS_ENABLED; - if (!(ah->caps.hw_caps & - ATH9K_HW_CAP_AUTOSLEEP)) { - if ((sc->imask & ATH9K_INT_TIM_TIMER) == 0) { - sc->imask |= ATH9K_INT_TIM_TIMER; - ath9k_hw_set_interrupts(sc->sc_ah, - sc->imask); - } - } + sc->ps_flags |= PS_ENABLED; /* * At this point we know hardware has received an ACK * of a previously sent null data frame. */ - if ((sc->sc_flags & SC_OP_NULLFUNC_COMPLETED)) { - sc->sc_flags &= ~SC_OP_NULLFUNC_COMPLETED; - sc->ps_enabled = true; - ath9k_hw_setrxabort(sc->sc_ah, 1); + if ((sc->ps_flags & PS_NULLFUNC_COMPLETED)) { + sc->ps_flags &= ~PS_NULLFUNC_COMPLETED; + ath9k_enable_ps(sc); } } else { sc->ps_enabled = false; - sc->sc_flags &= ~(SC_OP_PS_ENABLED | - SC_OP_NULLFUNC_COMPLETED); + sc->ps_flags &= ~(PS_ENABLED | + PS_NULLFUNC_COMPLETED); ath9k_setpower(sc, ATH9K_PM_AWAKE); if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { ath9k_hw_setrxabort(sc->sc_ah, 0); - sc->sc_flags &= ~(SC_OP_WAIT_FOR_BEACON | - SC_OP_WAIT_FOR_CAB | - SC_OP_WAIT_FOR_PSPOLL_DATA | - SC_OP_WAIT_FOR_TX_ACK); + sc->ps_flags &= ~(PS_WAIT_FOR_BEACON | + PS_WAIT_FOR_CAB | + PS_WAIT_FOR_PSPOLL_DATA | + PS_WAIT_FOR_TX_ACK); if (sc->imask & ATH9K_INT_TIM_TIMER) { sc->imask &= ~ATH9K_INT_TIM_TIMER; ath9k_hw_set_interrupts(sc->sc_ah, @@ -2766,6 +1589,14 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) } } + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + if (conf->flags & IEEE80211_CONF_MONITOR) { + ath_print(common, ATH_DBG_CONFIG, + "HW opmode set to Monitor mode\n"); + sc->sc_ah->opmode = NL80211_IFTYPE_MONITOR; + } + } + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { struct ieee80211_channel *curchan = hw->conf.channel; int pos = curchan->hw_value; @@ -2801,8 +1632,10 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) } skip_chan_change: - if (changed & IEEE80211_CONF_CHANGE_POWER) + if (changed & IEEE80211_CONF_CHANGE_POWER) { sc->config.txpowlimit = 2 * conf->power_level; + ath_update_txpow(sc); + } spin_lock_bh(&sc->wiphy_lock); disable_radio = ath9k_all_wiphys_idle(sc); @@ -2810,6 +1643,7 @@ skip_chan_change: if (disable_radio) { ath_print(common, ATH_DBG_CONFIG, "idle: disabling radio\n"); + sc->ps_idle = true; ath_radio_disable(sc, hw); } @@ -2850,24 +1684,28 @@ static void ath9k_configure_filter(struct ieee80211_hw *hw, "Set HW RX filter: 0x%x\n", rfilt); } -static void ath9k_sta_notify(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum sta_notify_cmd cmd, - struct ieee80211_sta *sta) +static int ath9k_sta_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; - switch (cmd) { - case STA_NOTIFY_ADD: - ath_node_attach(sc, sta); - break; - case STA_NOTIFY_REMOVE: - ath_node_detach(sc, sta); - break; - default: - break; - } + ath_node_attach(sc, sta); + + return 0; +} + +static int ath9k_sta_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + + ath_node_detach(sc, sta); + + return 0; } static int ath9k_conf_tx(struct ieee80211_hw *hw, u16 queue, @@ -2966,6 +1804,7 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath_vif *avp = (void *)vif->drv_priv; + int slottime; int error; mutex_lock(&sc->mutex); @@ -3001,6 +1840,25 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, ath_beacon_config(sc, vif); } + if (changed & BSS_CHANGED_ERP_SLOT) { + if (bss_conf->use_short_slot) + slottime = 9; + else + slottime = 20; + if (vif->type == NL80211_IFTYPE_AP) { + /* + * Defer update, so that connected stations can adjust + * their settings at the same time. + * See beacon.c for more details + */ + sc->beacon.slottime = slottime; + sc->beacon.updateslot = UPDATE; + } else { + ah->slottime = slottime; + ath9k_hw_init_global_settings(ah); + } + } + /* Disable transmission of beacons */ if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq); @@ -3133,6 +1991,7 @@ static void ath9k_sw_scan_start(struct ieee80211_hw *hw) { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); mutex_lock(&sc->mutex); if (ath9k_wiphy_scanning(sc)) { @@ -3148,10 +2007,9 @@ static void ath9k_sw_scan_start(struct ieee80211_hw *hw) aphy->state = ATH_WIPHY_SCAN; ath9k_wiphy_pause_all_forced(sc, aphy); - - spin_lock_bh(&sc->ani_lock); sc->sc_flags |= SC_OP_SCANNING; - spin_unlock_bh(&sc->ani_lock); + del_timer_sync(&common->ani.timer); + cancel_delayed_work_sync(&sc->tx_complete_work); mutex_unlock(&sc->mutex); } @@ -3159,17 +2017,30 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw) { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); mutex_lock(&sc->mutex); - spin_lock_bh(&sc->ani_lock); aphy->state = ATH_WIPHY_ACTIVE; sc->sc_flags &= ~SC_OP_SCANNING; sc->sc_flags |= SC_OP_FULL_RESET; - spin_unlock_bh(&sc->ani_lock); + ath_start_ani(common); + ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); ath_beacon_config(sc, NULL); mutex_unlock(&sc->mutex); } +static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ath_hw *ah = sc->sc_ah; + + mutex_lock(&sc->mutex); + ah->coverage_class = coverage_class; + ath9k_hw_init_global_settings(ah); + mutex_unlock(&sc->mutex); +} + struct ieee80211_ops ath9k_ops = { .tx = ath9k_tx, .start = ath9k_start, @@ -3178,7 +2049,8 @@ struct ieee80211_ops ath9k_ops = { .remove_interface = ath9k_remove_interface, .config = ath9k_config, .configure_filter = ath9k_configure_filter, - .sta_notify = ath9k_sta_notify, + .sta_add = ath9k_sta_add, + .sta_remove = ath9k_sta_remove, .conf_tx = ath9k_conf_tx, .bss_info_changed = ath9k_bss_info_changed, .set_key = ath9k_set_key, @@ -3189,64 +2061,5 @@ struct ieee80211_ops ath9k_ops = { .sw_scan_start = ath9k_sw_scan_start, .sw_scan_complete = ath9k_sw_scan_complete, .rfkill_poll = ath9k_rfkill_poll_state, + .set_coverage_class = ath9k_set_coverage_class, }; - -static int __init ath9k_init(void) -{ - int error; - - /* Register rate control algorithm */ - error = ath_rate_control_register(); - if (error != 0) { - printk(KERN_ERR - "ath9k: Unable to register rate control " - "algorithm: %d\n", - error); - goto err_out; - } - - error = ath9k_debug_create_root(); - if (error) { - printk(KERN_ERR - "ath9k: Unable to create debugfs root: %d\n", - error); - goto err_rate_unregister; - } - - error = ath_pci_init(); - if (error < 0) { - printk(KERN_ERR - "ath9k: No PCI devices found, driver not installed.\n"); - error = -ENODEV; - goto err_remove_root; - } - - error = ath_ahb_init(); - if (error < 0) { - error = -ENODEV; - goto err_pci_exit; - } - - return 0; - - err_pci_exit: - ath_pci_exit(); - - err_remove_root: - ath9k_debug_remove_root(); - err_rate_unregister: - ath_rate_control_unregister(); - err_out: - return error; -} -module_init(ath9k_init); - -static void __exit ath9k_exit(void) -{ - ath_ahb_exit(); - ath_pci_exit(); - ath9k_debug_remove_root(); - ath_rate_control_unregister(); - printk(KERN_INFO "%s: Driver unloaded\n", dev_info); -} -module_exit(ath9k_exit); diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index f7af5ea54753..f318b3b1abe9 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -25,6 +25,7 @@ static struct pci_device_id ath_pci_id_table[] __devinitdata = { { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */ { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */ { PCI_VDEVICE(ATHEROS, 0x002B) }, /* PCI-E */ + { PCI_VDEVICE(ATHEROS, 0x002C) }, /* PCI-E 802.11n bonded out */ { PCI_VDEVICE(ATHEROS, 0x002D) }, /* PCI */ { PCI_VDEVICE(ATHEROS, 0x002E) }, /* PCI-E */ { 0 } @@ -49,16 +50,6 @@ static void ath_pci_read_cachesize(struct ath_common *common, int *csz) *csz = DEFAULT_CACHELINE >> 2; /* Use the default size */ } -static void ath_pci_cleanup(struct ath_common *common) -{ - struct ath_softc *sc = (struct ath_softc *) common->priv; - struct pci_dev *pdev = to_pci_dev(sc->dev); - - pci_iounmap(pdev, sc->mem); - pci_disable_device(pdev); - pci_release_region(pdev, 0); -} - static bool ath_pci_eeprom_read(struct ath_common *common, u32 off, u16 *data) { struct ath_hw *ah = (struct ath_hw *) common->ah; @@ -98,7 +89,6 @@ static void ath_pci_bt_coex_prep(struct ath_common *common) static const struct ath_bus_ops ath_pci_bus_ops = { .read_cachesize = ath_pci_read_cachesize, - .cleanup = ath_pci_cleanup, .eeprom_read = ath_pci_eeprom_read, .bt_coex_prep = ath_pci_bt_coex_prep, }; @@ -113,25 +103,22 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) u16 subsysid; u32 val; int ret = 0; - struct ath_hw *ah; char hw_name[64]; if (pci_enable_device(pdev)) return -EIO; ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (ret) { printk(KERN_ERR "ath9k: 32-bit DMA not available\n"); - goto bad; + goto err_dma; } ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); - if (ret) { printk(KERN_ERR "ath9k: 32-bit DMA consistent " "DMA enable failed\n"); - goto bad; + goto err_dma; } /* @@ -171,22 +158,22 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (ret) { dev_err(&pdev->dev, "PCI memory region reserve error\n"); ret = -ENODEV; - goto bad; + goto err_region; } mem = pci_iomap(pdev, 0, 0); if (!mem) { printk(KERN_ERR "PCI memory map error\n") ; ret = -EIO; - goto bad1; + goto err_iomap; } hw = ieee80211_alloc_hw(sizeof(struct ath_wiphy) + sizeof(struct ath_softc), &ath9k_ops); if (!hw) { - dev_err(&pdev->dev, "no memory for ieee80211_hw\n"); + dev_err(&pdev->dev, "No memory for ieee80211_hw\n"); ret = -ENOMEM; - goto bad2; + goto err_alloc_hw; } SET_IEEE80211_DEV(hw, &pdev->dev); @@ -201,25 +188,25 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) sc->dev = &pdev->dev; sc->mem = mem; - pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &subsysid); - ret = ath_init_device(id->device, sc, subsysid, &ath_pci_bus_ops); - if (ret) { - dev_err(&pdev->dev, "failed to initialize device\n"); - goto bad3; - } - - /* setup interrupt service routine */ + /* Will be cleared in ath9k_start() */ + sc->sc_flags |= SC_OP_INVALID; ret = request_irq(pdev->irq, ath_isr, IRQF_SHARED, "ath9k", sc); if (ret) { dev_err(&pdev->dev, "request_irq failed\n"); - goto bad4; + goto err_irq; } sc->irq = pdev->irq; - ah = sc->sc_ah; - ath9k_hw_name(ah, hw_name, sizeof(hw_name)); + pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &subsysid); + ret = ath9k_init_device(id->device, sc, subsysid, &ath_pci_bus_ops); + if (ret) { + dev_err(&pdev->dev, "Failed to initialize device\n"); + goto err_init; + } + + ath9k_hw_name(sc->sc_ah, hw_name, sizeof(hw_name)); printk(KERN_INFO "%s: %s mem=0x%lx, irq=%d\n", wiphy_name(hw->wiphy), @@ -227,15 +214,18 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) (unsigned long)mem, pdev->irq); return 0; -bad4: - ath_detach(sc); -bad3: + +err_init: + free_irq(sc->irq, sc); +err_irq: ieee80211_free_hw(hw); -bad2: +err_alloc_hw: pci_iounmap(pdev, mem); -bad1: +err_iomap: pci_release_region(pdev, 0); -bad: +err_region: + /* Nothing */ +err_dma: pci_disable_device(pdev); return ret; } @@ -245,8 +235,15 @@ static void ath_pci_remove(struct pci_dev *pdev) struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; + void __iomem *mem = sc->mem; + + ath9k_deinit_device(sc); + free_irq(sc->irq, sc); + ieee80211_free_hw(sc->hw); - ath_cleanup(sc); + pci_iounmap(pdev, mem); + pci_disable_device(pdev); + pci_release_region(pdev, 0); } #ifdef CONFIG_PM diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index c915954d4d5b..2880507f9d3f 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -668,7 +668,7 @@ static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_tx_rate *rates = tx_info->control.rates; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; __le16 fc = hdr->frame_control; - u8 try_per_rate, i = 0, rix, nrix; + u8 try_per_rate, i = 0, rix; int is_probe = 0; if (rate_control_send_low(sta, priv_sta, txrc)) @@ -678,48 +678,47 @@ static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, * For Multi Rate Retry we use a different number of * retry attempt counts. This ends up looking like this: * - * MRR[0] = 2 - * MRR[1] = 2 - * MRR[2] = 2 - * MRR[3] = 4 + * MRR[0] = 4 + * MRR[1] = 4 + * MRR[2] = 4 + * MRR[3] = 8 * */ - try_per_rate = sc->hw->max_rate_tries; + try_per_rate = 4; rate_table = sc->cur_rate_table; rix = ath_rc_get_highest_rix(sc, ath_rc_priv, rate_table, &is_probe); - nrix = rix; if (is_probe) { /* set one try for probe rates. For the * probes don't enable rts */ ath_rc_rate_set_series(rate_table, &rates[i++], txrc, - 1, nrix, 0); + 1, rix, 0); /* Get the next tried/allowed rate. No RTS for the next series * after the probe rate */ - ath_rc_get_lower_rix(rate_table, ath_rc_priv, rix, &nrix); + ath_rc_get_lower_rix(rate_table, ath_rc_priv, rix, &rix); ath_rc_rate_set_series(rate_table, &rates[i++], txrc, - try_per_rate, nrix, 0); + try_per_rate, rix, 0); tx_info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; } else { /* Set the choosen rate. No RTS for first series entry. */ ath_rc_rate_set_series(rate_table, &rates[i++], txrc, - try_per_rate, nrix, 0); + try_per_rate, rix, 0); } /* Fill in the other rates for multirate retry */ for ( ; i < 4; i++) { /* Use twice the number of tries for the last MRR segment. */ if (i + 1 == 4) - try_per_rate = 4; + try_per_rate = 8; - ath_rc_get_lower_rix(rate_table, ath_rc_priv, rix, &nrix); + ath_rc_get_lower_rix(rate_table, ath_rc_priv, rix, &rix); /* All other rates in the series have RTS enabled */ ath_rc_rate_set_series(rate_table, &rates[i], txrc, - try_per_rate, nrix, 1); + try_per_rate, rix, 1); } /* diff --git a/drivers/net/wireless/ath/ath9k/rc.h b/drivers/net/wireless/ath/ath9k/rc.h index 9eb96f506998..4f6d6fd442f4 100644 --- a/drivers/net/wireless/ath/ath9k/rc.h +++ b/drivers/net/wireless/ath/ath9k/rc.h @@ -57,6 +57,10 @@ enum { || (_phy == WLAN_RC_PHY_HT_40_DS) \ || (_phy == WLAN_RC_PHY_HT_20_DS_HGI) \ || (_phy == WLAN_RC_PHY_HT_40_DS_HGI)) +#define WLAN_RC_PHY_20(_phy) ((_phy == WLAN_RC_PHY_HT_20_SS) \ + || (_phy == WLAN_RC_PHY_HT_20_DS) \ + || (_phy == WLAN_RC_PHY_HT_20_SS_HGI) \ + || (_phy == WLAN_RC_PHY_HT_20_DS_HGI)) #define WLAN_RC_PHY_40(_phy) ((_phy == WLAN_RC_PHY_HT_40_SS) \ || (_phy == WLAN_RC_PHY_HT_40_DS) \ || (_phy == WLAN_RC_PHY_HT_40_SS_HGI) \ diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 477365e5ae69..1ca42e5148c8 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -364,10 +364,10 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) if (memcmp(common->curbssid, mgmt->bssid, ETH_ALEN) != 0) return; /* not from our current AP */ - sc->sc_flags &= ~SC_OP_WAIT_FOR_BEACON; + sc->ps_flags &= ~PS_WAIT_FOR_BEACON; - if (sc->sc_flags & SC_OP_BEACON_SYNC) { - sc->sc_flags &= ~SC_OP_BEACON_SYNC; + if (sc->ps_flags & PS_BEACON_SYNC) { + sc->ps_flags &= ~PS_BEACON_SYNC; ath_print(common, ATH_DBG_PS, "Reconfigure Beacon timers based on " "timestamp from the AP\n"); @@ -384,17 +384,17 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) */ ath_print(common, ATH_DBG_PS, "Received DTIM beacon indicating " "buffered broadcast/multicast frame(s)\n"); - sc->sc_flags |= SC_OP_WAIT_FOR_CAB | SC_OP_WAIT_FOR_BEACON; + sc->ps_flags |= PS_WAIT_FOR_CAB | PS_WAIT_FOR_BEACON; return; } - if (sc->sc_flags & SC_OP_WAIT_FOR_CAB) { + if (sc->ps_flags & PS_WAIT_FOR_CAB) { /* * This can happen if a broadcast frame is dropped or the AP * fails to send a frame indicating that all CAB frames have * been delivered. */ - sc->sc_flags &= ~SC_OP_WAIT_FOR_CAB; + sc->ps_flags &= ~PS_WAIT_FOR_CAB; ath_print(common, ATH_DBG_PS, "PS wait for CAB frames timed out\n"); } @@ -408,10 +408,10 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb) hdr = (struct ieee80211_hdr *)skb->data; /* Process Beacon and CAB receive in PS state */ - if ((sc->sc_flags & SC_OP_WAIT_FOR_BEACON) && + if ((sc->ps_flags & PS_WAIT_FOR_BEACON) && ieee80211_is_beacon(hdr->frame_control)) ath_rx_ps_beacon(sc, skb); - else if ((sc->sc_flags & SC_OP_WAIT_FOR_CAB) && + else if ((sc->ps_flags & PS_WAIT_FOR_CAB) && (ieee80211_is_data(hdr->frame_control) || ieee80211_is_action(hdr->frame_control)) && is_multicast_ether_addr(hdr->addr1) && @@ -420,20 +420,20 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb) * No more broadcast/multicast frames to be received at this * point. */ - sc->sc_flags &= ~SC_OP_WAIT_FOR_CAB; + sc->ps_flags &= ~PS_WAIT_FOR_CAB; ath_print(common, ATH_DBG_PS, "All PS CAB frames received, back to sleep\n"); - } else if ((sc->sc_flags & SC_OP_WAIT_FOR_PSPOLL_DATA) && + } else if ((sc->ps_flags & PS_WAIT_FOR_PSPOLL_DATA) && !is_multicast_ether_addr(hdr->addr1) && !ieee80211_has_morefrags(hdr->frame_control)) { - sc->sc_flags &= ~SC_OP_WAIT_FOR_PSPOLL_DATA; + sc->ps_flags &= ~PS_WAIT_FOR_PSPOLL_DATA; ath_print(common, ATH_DBG_PS, "Going back to sleep after having received " - "PS-Poll data (0x%x)\n", - sc->sc_flags & (SC_OP_WAIT_FOR_BEACON | - SC_OP_WAIT_FOR_CAB | - SC_OP_WAIT_FOR_PSPOLL_DATA | - SC_OP_WAIT_FOR_TX_ACK)); + "PS-Poll data (0x%lx)\n", + sc->ps_flags & (PS_WAIT_FOR_BEACON | + PS_WAIT_FOR_CAB | + PS_WAIT_FOR_PSPOLL_DATA | + PS_WAIT_FOR_TX_ACK)); } } @@ -571,6 +571,8 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) hw = ath_get_virt_hw(sc, hdr); rx_stats = &ds->ds_rxstat; + ath_debug_stat_rx(sc, bf); + /* * If we're asked to flush receive queue, directly * chain it back at the queue without processing it. @@ -631,9 +633,9 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) sc->rx.rxotherant = 0; } - if (unlikely(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON | - SC_OP_WAIT_FOR_CAB | - SC_OP_WAIT_FOR_PSPOLL_DATA))) + if (unlikely(sc->ps_flags & (PS_WAIT_FOR_BEACON | + PS_WAIT_FOR_CAB | + PS_WAIT_FOR_PSPOLL_DATA))) ath_rx_ps(sc, skb); ath_rx_send_to_mac80211(hw, sc, skb, rxs); diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index 8e653fb937a1..72cfa8ebd9ae 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -1547,9 +1547,9 @@ enum { #define AR_BT_COEX_WEIGHT 0x8174 #define AR_BT_COEX_WGHT 0xff55 -#define AR_STOMP_ALL_WLAN_WGHT 0xffcc -#define AR_STOMP_LOW_WLAN_WGHT 0xaaa8 -#define AR_STOMP_NONE_WLAN_WGHT 0xaa00 +#define AR_STOMP_ALL_WLAN_WGHT 0xfcfc +#define AR_STOMP_LOW_WLAN_WGHT 0xa8a8 +#define AR_STOMP_NONE_WLAN_WGHT 0x0000 #define AR_BTCOEX_BT_WGHT 0x0000ffff #define AR_BTCOEX_BT_WGHT_S 0 #define AR_BTCOEX_WL_WGHT 0xffff0000 diff --git a/drivers/net/wireless/ath/ath9k/virtual.c b/drivers/net/wireless/ath/ath9k/virtual.c index cd26caaf44e7..a43fbf84dab9 100644 --- a/drivers/net/wireless/ath/ath9k/virtual.c +++ b/drivers/net/wireless/ath/ath9k/virtual.c @@ -152,7 +152,7 @@ int ath9k_wiphy_add(struct ath_softc *sc) SET_IEEE80211_PERM_ADDR(hw, addr); - ath_set_hw_capab(sc, hw); + ath9k_set_hw_capab(sc, hw); error = ieee80211_register_hw(hw); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 29bf33692f71..47294f90bbe5 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1498,26 +1498,6 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf) if (sc->sc_flags & SC_OP_PREAMBLE_SHORT) ctsrate |= rate->hw_value_short; - /* - * ATH9K_TXDESC_RTSENA and ATH9K_TXDESC_CTSENA are mutually exclusive. - * Check the first rate in the series to decide whether RTS/CTS - * or CTS-to-self has to be used. - */ - if (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) - flags = ATH9K_TXDESC_CTSENA; - else if (rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) - flags = ATH9K_TXDESC_RTSENA; - - /* FIXME: Handle aggregation protection */ - if (sc->config.ath_aggr_prot && - (!bf_isaggr(bf) || (bf_isaggr(bf) && bf->bf_al < 8192))) { - flags = ATH9K_TXDESC_RTSENA; - } - - /* For AR5416 - RTS cannot be followed by a frame larger than 8K */ - if (bf_isaggr(bf) && (bf->bf_al > sc->sc_ah->caps.rts_aggr_limit)) - flags &= ~(ATH9K_TXDESC_RTSENA); - for (i = 0; i < 4; i++) { bool is_40, is_sgi, is_sp; int phy; @@ -1529,8 +1509,15 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf) series[i].Tries = rates[i].count; series[i].ChSel = common->tx_chainmask; - if (rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS) + if ((sc->config.ath_aggr_prot && bf_isaggr(bf)) || + (rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS)) { series[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS; + flags |= ATH9K_TXDESC_RTSENA; + } else if (rates[i].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { + series[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS; + flags |= ATH9K_TXDESC_CTSENA; + } + if (rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) series[i].RateFlags |= ATH9K_RATESERIES_2040; if (rates[i].flags & IEEE80211_TX_RC_SHORT_GI) @@ -1568,6 +1555,14 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf) phy, rate->bitrate * 100, bf->bf_frmlen, rix, is_sp); } + /* For AR5416 - RTS cannot be followed by a frame larger than 8K */ + if (bf_isaggr(bf) && (bf->bf_al > sc->sc_ah->caps.rts_aggr_limit)) + flags &= ~ATH9K_TXDESC_RTSENA; + + /* ATH9K_TXDESC_RTSENA and ATH9K_TXDESC_CTSENA are mutually exclusive. */ + if (flags & ATH9K_TXDESC_RTSENA) + flags &= ~ATH9K_TXDESC_CTSENA; + /* set dur_update_en for l-sig computation except for PS-Poll frames */ ath9k_hw_set11n_ratescenario(sc->sc_ah, bf->bf_desc, bf->bf_lastbf->bf_desc, @@ -1648,7 +1643,7 @@ static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf, /* tag if this is a nullfunc frame to enable PS when AP acks it */ if (ieee80211_is_nullfunc(fc) && ieee80211_has_pm(fc)) { bf->bf_isnullfunc = true; - sc->sc_flags &= ~SC_OP_NULLFUNC_COMPLETED; + sc->ps_flags &= ~PS_NULLFUNC_COMPLETED; } else bf->bf_isnullfunc = false; @@ -1858,15 +1853,15 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, skb_pull(skb, padsize); } - if (sc->sc_flags & SC_OP_WAIT_FOR_TX_ACK) { - sc->sc_flags &= ~SC_OP_WAIT_FOR_TX_ACK; + if (sc->ps_flags & PS_WAIT_FOR_TX_ACK) { + sc->ps_flags &= ~PS_WAIT_FOR_TX_ACK; ath_print(common, ATH_DBG_PS, "Going back to sleep after having " - "received TX status (0x%x)\n", - sc->sc_flags & (SC_OP_WAIT_FOR_BEACON | - SC_OP_WAIT_FOR_CAB | - SC_OP_WAIT_FOR_PSPOLL_DATA | - SC_OP_WAIT_FOR_TX_ACK)); + "received TX status (0x%lx)\n", + sc->ps_flags & (PS_WAIT_FOR_BEACON | + PS_WAIT_FOR_CAB | + PS_WAIT_FOR_PSPOLL_DATA | + PS_WAIT_FOR_TX_ACK)); } if (unlikely(tx_info->pad[0] & ATH_TX_INFO_FRAME_TYPE_INTERNAL)) @@ -2053,11 +2048,10 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) */ if (bf->bf_isnullfunc && (ds->ds_txstat.ts_status & ATH9K_TX_ACKED)) { - if ((sc->sc_flags & SC_OP_PS_ENABLED)) { - sc->ps_enabled = true; - ath9k_hw_setrxabort(sc->sc_ah, 1); - } else - sc->sc_flags |= SC_OP_NULLFUNC_COMPLETED; + if ((sc->ps_flags & PS_ENABLED)) + ath9k_enable_ps(sc); + else + sc->ps_flags |= PS_NULLFUNC_COMPLETED; } /* diff --git a/drivers/net/wireless/ath/debug.h b/drivers/net/wireless/ath/debug.h index d6b685a06c5e..8263633c003c 100644 --- a/drivers/net/wireless/ath/debug.h +++ b/drivers/net/wireless/ath/debug.h @@ -65,11 +65,11 @@ enum ATH_DEBUG { #define ATH_DBG_DEFAULT (ATH_DBG_FATAL) #ifdef CONFIG_ATH_DEBUG -void ath_print(struct ath_common *common, int dbg_mask, const char *fmt, ...); +void ath_print(struct ath_common *common, int dbg_mask, const char *fmt, ...) + __attribute__ ((format (printf, 3, 4))); #else -static inline void ath_print(struct ath_common *common, - int dbg_mask, - const char *fmt, ...) +static inline void __attribute__ ((format (printf, 3, 4))) +ath_print(struct ath_common *common, int dbg_mask, const char *fmt, ...) { } #endif /* CONFIG_ATH_DEBUG */ diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 039ac490465c..04abd1f556b7 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -110,8 +110,9 @@ static const struct ieee80211_regdomain ath_world_regdom_67_68_6A = { static inline bool is_wwr_sku(u16 regd) { - return ((regd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX) || - (regd == WORLD); + return ((regd & COUNTRY_ERD_FLAG) != COUNTRY_ERD_FLAG) && + (((regd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX) || + (regd == WORLD)); } static u16 ath_regd_get_eepromRD(struct ath_regulatory *reg) diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig index 64c12e1bced3..073be566d05e 100644 --- a/drivers/net/wireless/b43/Kconfig +++ b/drivers/net/wireless/b43/Kconfig @@ -3,6 +3,7 @@ config B43 depends on SSB_POSSIBLE && MAC80211 && HAS_DMA select SSB select FW_LOADER + select SSB_BLOCKIO ---help--- b43 is a driver for the Broadcom 43xx series wireless devices. @@ -78,14 +79,6 @@ config B43_SDIO If unsure, say N. -# Data transfers to the device via PIO -# This is only needed on PCMCIA and SDIO devices. All others can do DMA properly. -config B43_PIO - bool - depends on B43 && (B43_SDIO || B43_PCMCIA || B43_FORCE_PIO) - select SSB_BLOCKIO - default y - config B43_NPHY bool "Pre IEEE 802.11n support (BROKEN)" depends on B43 && EXPERIMENTAL && BROKEN @@ -137,12 +130,4 @@ config B43_DEBUG for production use. Only say Y, if you are debugging a problem in the b43 driver sourcecode. -config B43_FORCE_PIO - bool "Force usage of PIO instead of DMA" - depends on B43 && B43_DEBUG - ---help--- - This will disable DMA and always enable PIO instead. - Say N! - This is only for debugging the PIO engine code. You do - _NOT_ want to enable this. diff --git a/drivers/net/wireless/b43/Makefile b/drivers/net/wireless/b43/Makefile index 84772a2542dc..5e83b6f0a3a0 100644 --- a/drivers/net/wireless/b43/Makefile +++ b/drivers/net/wireless/b43/Makefile @@ -12,7 +12,7 @@ b43-y += xmit.o b43-y += lo.o b43-y += wa.o b43-y += dma.o -b43-$(CONFIG_B43_PIO) += pio.o +b43-y += pio.o b43-y += rfkill.o b43-$(CONFIG_B43_LEDS) += leds.o b43-$(CONFIG_B43_PCMCIA) += pcmcia.o diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h index c484cc253892..6a6ab0f630e5 100644 --- a/drivers/net/wireless/b43/b43.h +++ b/drivers/net/wireless/b43/b43.h @@ -254,6 +254,14 @@ enum { #define B43_SHM_SH_MAXBFRAMES 0x0080 /* Maximum number of frames in a burst */ #define B43_SHM_SH_SPUWKUP 0x0094 /* pre-wakeup for synth PU in us */ #define B43_SHM_SH_PRETBTT 0x0096 /* pre-TBTT in us */ +/* SHM_SHARED tx iq workarounds */ +#define B43_SHM_SH_NPHY_TXIQW0 0x0700 +#define B43_SHM_SH_NPHY_TXIQW1 0x0702 +#define B43_SHM_SH_NPHY_TXIQW2 0x0704 +#define B43_SHM_SH_NPHY_TXIQW3 0x0706 +/* SHM_SHARED tx pwr ctrl */ +#define B43_SHM_SH_NPHY_TXPWR_INDX0 0x0708 +#define B43_SHM_SH_NPHY_TXPWR_INDX1 0x070E /* SHM_SCRATCH offsets */ #define B43_SHM_SC_MINCONT 0x0003 /* Minimum contention window */ @@ -822,11 +830,9 @@ struct b43_wl { /* The device LEDs. */ struct b43_leds leds; -#ifdef CONFIG_B43_PIO /* Kmalloc'ed scratch space for PIO TX/RX. Protected by wl->mutex. */ u8 pio_scratchspace[110] __attribute__((__aligned__(8))); u8 pio_tailspace[4] __attribute__((__aligned__(8))); -#endif /* CONFIG_B43_PIO */ }; static inline struct b43_wl *hw_to_b43_wl(struct ieee80211_hw *hw) @@ -877,20 +883,9 @@ static inline void b43_write32(struct b43_wldev *dev, u16 offset, u32 value) static inline bool b43_using_pio_transfers(struct b43_wldev *dev) { -#ifdef CONFIG_B43_PIO return dev->__using_pio_transfers; -#else - return 0; -#endif } -#ifdef CONFIG_B43_FORCE_PIO -# define B43_FORCE_PIO 1 -#else -# define B43_FORCE_PIO 0 -#endif - - /* Message printing */ void b43info(struct b43_wl *wl, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c index 88d1fd02d40a..be7abf8916ad 100644 --- a/drivers/net/wireless/b43/dma.c +++ b/drivers/net/wireless/b43/dma.c @@ -1369,7 +1369,6 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb) b43err(dev->wl, "DMA tx mapping failure\n"); goto out; } - ring->nr_tx_packets++; if ((free_slots(ring) < TX_SLOTS_PER_FRAME) || should_inject_overflow(ring)) { /* This TX ring is full. */ @@ -1500,22 +1499,6 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev, } } -void b43_dma_get_tx_stats(struct b43_wldev *dev, - struct ieee80211_tx_queue_stats *stats) -{ - const int nr_queues = dev->wl->hw->queues; - struct b43_dmaring *ring; - int i; - - for (i = 0; i < nr_queues; i++) { - ring = select_ring_by_priority(dev, i); - - stats[i].len = ring->used_slots / TX_SLOTS_PER_FRAME; - stats[i].limit = ring->nr_slots / TX_SLOTS_PER_FRAME; - stats[i].count = ring->nr_tx_packets; - } -} - static void dma_rx(struct b43_dmaring *ring, int *slot) { const struct b43_dma_ops *ops = ring->ops; @@ -1653,7 +1636,6 @@ void b43_dma_tx_resume(struct b43_wldev *dev) b43_power_saving_ctl_bits(dev, 0); } -#ifdef CONFIG_B43_PIO static void direct_fifo_rx(struct b43_wldev *dev, enum b43_dmatype type, u16 mmio_base, bool enable) { @@ -1687,4 +1669,3 @@ void b43_dma_direct_fifo_rx(struct b43_wldev *dev, mmio_base = b43_dmacontroller_base(type, engine_index); direct_fifo_rx(dev, type, mmio_base, enable); } -#endif /* CONFIG_B43_PIO */ diff --git a/drivers/net/wireless/b43/dma.h b/drivers/net/wireless/b43/dma.h index f7ab37c4cdbc..dc91944d6022 100644 --- a/drivers/net/wireless/b43/dma.h +++ b/drivers/net/wireless/b43/dma.h @@ -228,8 +228,6 @@ struct b43_dmaring { int used_slots; /* Currently used slot in the ring. */ int current_slot; - /* Total number of packets sent. Statistics only. */ - unsigned int nr_tx_packets; /* Frameoffset in octets. */ u32 frameoffset; /* Descriptor buffer size. */ @@ -278,9 +276,6 @@ void b43_dma_free(struct b43_wldev *dev); void b43_dma_tx_suspend(struct b43_wldev *dev); void b43_dma_tx_resume(struct b43_wldev *dev); -void b43_dma_get_tx_stats(struct b43_wldev *dev, - struct ieee80211_tx_queue_stats *stats); - int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb); void b43_dma_handle_txstatus(struct b43_wldev *dev, diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index fcbf0e27d9f3..8f7a8c0ec27d 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -67,7 +67,12 @@ MODULE_AUTHOR("Gábor Stefanik"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(B43_SUPPORTED_FIRMWARE_ID); - +MODULE_FIRMWARE("b43/ucode11.fw"); +MODULE_FIRMWARE("b43/ucode13.fw"); +MODULE_FIRMWARE("b43/ucode14.fw"); +MODULE_FIRMWARE("b43/ucode15.fw"); +MODULE_FIRMWARE("b43/ucode5.fw"); +MODULE_FIRMWARE("b43/ucode9.fw"); static int modparam_bad_frames_preempt; module_param_named(bad_frames_preempt, modparam_bad_frames_preempt, int, 0444); @@ -102,6 +107,9 @@ int b43_modparam_verbose = B43_VERBOSITY_DEFAULT; module_param_named(verbose, b43_modparam_verbose, int, 0644); MODULE_PARM_DESC(verbose, "Log message verbosity: 0=error, 1=warn, 2=info(default), 3=debug"); +static int modparam_pio; +module_param_named(pio, modparam_pio, int, 0444); +MODULE_PARM_DESC(pio, "enable(1) / disable(0) PIO mode"); static const struct ssb_device_id b43_ssb_tbl[] = { SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 5), @@ -110,6 +118,7 @@ static const struct ssb_device_id b43_ssb_tbl[] = { SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 9), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 10), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 11), + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 12), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 13), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 15), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 16), @@ -842,8 +851,10 @@ static void rx_tkip_phase1_write(struct b43_wldev *dev, u8 index, u32 iv32, } static void b43_op_update_tkip_key(struct ieee80211_hw *hw, - struct ieee80211_key_conf *keyconf, const u8 *addr, - u32 iv32, u16 *phase1key) + struct ieee80211_vif *vif, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta, + u32 iv32, u16 *phase1key) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev; @@ -852,19 +863,19 @@ static void b43_op_update_tkip_key(struct ieee80211_hw *hw, if (B43_WARN_ON(!modparam_hwtkip)) return; - mutex_lock(&wl->mutex); - + /* This is only called from the RX path through mac80211, where + * our mutex is already locked. */ + B43_WARN_ON(!mutex_is_locked(&wl->mutex)); dev = wl->current_dev; - if (!dev || b43_status(dev) < B43_STAT_INITIALIZED) - goto out_unlock; + B43_WARN_ON(!dev || b43_status(dev) < B43_STAT_INITIALIZED); keymac_write(dev, index, NULL); /* First zero out mac to avoid race */ rx_tkip_phase1_write(dev, index, iv32, phase1key); - keymac_write(dev, index, addr); - -out_unlock: - mutex_unlock(&wl->mutex); + /* only pairwise TKIP keys are supported right now */ + if (WARN_ON(!sta)) + return; + keymac_write(dev, index, sta->addr); } static void do_key_write(struct b43_wldev *dev, @@ -1793,8 +1804,8 @@ static void b43_do_interrupt_thread(struct b43_wldev *dev) dma_reason[4], dma_reason[5]); b43err(dev->wl, "This device does not support DMA " "on your system. Please use PIO instead.\n"); - b43err(dev->wl, "CONFIG_B43_FORCE_PIO must be set in " - "your kernel configuration.\n"); + b43err(dev->wl, "Unload the b43 module and reload " + "with 'pio=1'\n"); return; } if (merged_dma_reason & B43_DMAIRQ_NONFATALMASK) { @@ -3345,27 +3356,6 @@ out_unlock: return err; } -static int b43_op_get_tx_stats(struct ieee80211_hw *hw, - struct ieee80211_tx_queue_stats *stats) -{ - struct b43_wl *wl = hw_to_b43_wl(hw); - struct b43_wldev *dev; - int err = -ENODEV; - - mutex_lock(&wl->mutex); - dev = wl->current_dev; - if (dev && b43_status(dev) >= B43_STAT_STARTED) { - if (b43_using_pio_transfers(dev)) - b43_pio_get_tx_stats(dev, stats); - else - b43_dma_get_tx_stats(dev, stats); - err = 0; - } - mutex_unlock(&wl->mutex); - - return err; -} - static int b43_op_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats) { @@ -3569,6 +3559,12 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) dev = wl->current_dev; phy = &dev->phy; + if (conf_is_ht(conf)) + phy->is_40mhz = + (conf_is_ht40_minus(conf) || conf_is_ht40_plus(conf)); + else + phy->is_40mhz = false; + b43_mac_suspend(dev); if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) @@ -3970,6 +3966,7 @@ static int b43_wireless_core_start(struct b43_wldev *dev) } /* We are ready to run. */ + ieee80211_wake_queues(dev->wl->hw); b43_set_status(dev, B43_STAT_STARTED); /* Start data flow (TX/RX). */ @@ -4360,7 +4357,7 @@ static int b43_wireless_core_init(struct b43_wldev *dev) if ((dev->dev->bus->bustype == SSB_BUSTYPE_PCMCIA) || (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) || - B43_FORCE_PIO) { + modparam_pio) { dev->__using_pio_transfers = 1; err = b43_pio_init(dev); } else { @@ -4379,8 +4376,6 @@ static int b43_wireless_core_init(struct b43_wldev *dev) ieee80211_wake_queues(dev->wl->hw); - ieee80211_wake_queues(dev->wl->hw); - b43_set_status(dev, B43_STAT_INITIALIZED); out: @@ -4395,7 +4390,7 @@ err_busdown: } static int b43_op_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev; @@ -4403,24 +4398,24 @@ static int b43_op_add_interface(struct ieee80211_hw *hw, /* TODO: allow WDS/AP devices to coexist */ - if (conf->type != NL80211_IFTYPE_AP && - conf->type != NL80211_IFTYPE_MESH_POINT && - conf->type != NL80211_IFTYPE_STATION && - conf->type != NL80211_IFTYPE_WDS && - conf->type != NL80211_IFTYPE_ADHOC) + if (vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_MESH_POINT && + vif->type != NL80211_IFTYPE_STATION && + vif->type != NL80211_IFTYPE_WDS && + vif->type != NL80211_IFTYPE_ADHOC) return -EOPNOTSUPP; mutex_lock(&wl->mutex); if (wl->operating) goto out_mutex_unlock; - b43dbg(wl, "Adding Interface type %d\n", conf->type); + b43dbg(wl, "Adding Interface type %d\n", vif->type); dev = wl->current_dev; wl->operating = 1; - wl->vif = conf->vif; - wl->if_type = conf->type; - memcpy(wl->mac_addr, conf->mac_addr, ETH_ALEN); + wl->vif = vif; + wl->if_type = vif->type; + memcpy(wl->mac_addr, vif->addr, ETH_ALEN); b43_adjust_opmode(dev); b43_set_pretbtt(dev); @@ -4435,17 +4430,17 @@ static int b43_op_add_interface(struct ieee80211_hw *hw, } static void b43_op_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev = wl->current_dev; - b43dbg(wl, "Removing Interface type %d\n", conf->type); + b43dbg(wl, "Removing Interface type %d\n", vif->type); mutex_lock(&wl->mutex); B43_WARN_ON(!wl->operating); - B43_WARN_ON(wl->vif != conf->vif); + B43_WARN_ON(wl->vif != vif); wl->vif = NULL; wl->operating = 0; @@ -4586,7 +4581,6 @@ static const struct ieee80211_ops b43_hw_ops = { .set_key = b43_op_set_key, .update_tkip_key = b43_op_update_tkip_key, .get_stats = b43_op_get_stats, - .get_tx_stats = b43_op_get_tx_stats, .get_tsf = b43_op_get_tsf, .set_tsf = b43_op_set_tsf, .start = b43_op_start, diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c index 75b26e175e8f..8f7d7eff2d80 100644 --- a/drivers/net/wireless/b43/phy_common.c +++ b/drivers/net/wireless/b43/phy_common.c @@ -421,3 +421,48 @@ void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on) { b43_write16(dev, B43_MMIO_PHY0, on ? 0 : 0xF4); } + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/Cordic */ +struct b43_c32 b43_cordic(int theta) +{ + u32 arctg[] = { 2949120, 1740967, 919879, 466945, 234379, 117304, + 58666, 29335, 14668, 7334, 3667, 1833, 917, 458, + 229, 115, 57, 29, }; + u8 i; + s32 tmp; + s8 signx = 1; + u32 angle = 0; + struct b43_c32 ret = { .i = 39797, .q = 0, }; + + while (theta > (180 << 16)) + theta -= (360 << 16); + while (theta < -(180 << 16)) + theta += (360 << 16); + + if (theta > (90 << 16)) { + theta -= (180 << 16); + signx = -1; + } else if (theta < -(90 << 16)) { + theta += (180 << 16); + signx = -1; + } + + for (i = 0; i <= 17; i++) { + if (theta > angle) { + tmp = ret.i - (ret.q >> i); + ret.q += ret.i >> i; + ret.i = tmp; + angle += arctg[i]; + } else { + tmp = ret.i + (ret.q >> i); + ret.q -= ret.i >> i; + ret.i = tmp; + angle -= arctg[i]; + } + } + + ret.i *= signx; + ret.q *= signx; + + return ret; +} diff --git a/drivers/net/wireless/b43/phy_common.h b/drivers/net/wireless/b43/phy_common.h index 9edd4e8e0c85..bd480b481bfc 100644 --- a/drivers/net/wireless/b43/phy_common.h +++ b/drivers/net/wireless/b43/phy_common.h @@ -5,6 +5,12 @@ struct b43_wldev; +/* Complex number using 2 32-bit signed integers */ +struct b43_c32 { s32 i, q; }; + +#define CORDIC_CONVERT(value) (((value) >= 0) ? \ + ((((value) >> 15) + 1) >> 1) : \ + -((((-(value)) >> 15) + 1) >> 1)) /* PHY register routing bits */ #define B43_PHYROUTE 0x0C00 /* PHY register routing bits mask */ @@ -212,6 +218,9 @@ struct b43_phy { bool supports_2ghz; bool supports_5ghz; + /* HT info */ + bool is_40mhz; + /* GMODE bit enabled? */ bool gmode; @@ -418,5 +427,6 @@ int b43_phy_shm_tssi_read(struct b43_wldev *dev, u16 shm_offset); */ void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on); +struct b43_c32 b43_cordic(int theta); #endif /* LINUX_B43_PHY_COMMON_H_ */ diff --git a/drivers/net/wireless/b43/phy_lp.c b/drivers/net/wireless/b43/phy_lp.c index 3e046ec1ff86..185219e0a552 100644 --- a/drivers/net/wireless/b43/phy_lp.c +++ b/drivers/net/wireless/b43/phy_lp.c @@ -80,6 +80,7 @@ static void b43_lpphy_op_free(struct b43_wldev *dev) dev->phy.lp = NULL; } +/* http://bcm-v4.sipsolutions.net/802.11/PHY/LP/ReadBandSrom */ static void lpphy_read_band_sprom(struct b43_wldev *dev) { struct b43_phy_lp *lpphy = dev->phy.lp; @@ -101,6 +102,12 @@ static void lpphy_read_band_sprom(struct b43_wldev *dev) maxpwr = bus->sprom.maxpwr_bg; lpphy->max_tx_pwr_med_band = maxpwr; cckpo = bus->sprom.cck2gpo; + /* + * We don't read SPROM's opo as specs say. On rev8 SPROMs + * opo == ofdm2gpo and we don't know any SSB with LP-PHY + * and SPROM rev below 8. + */ + B43_WARN_ON(bus->sprom.revision < 8); ofdmpo = bus->sprom.ofdm2gpo; if (cckpo) { for (i = 0; i < 4; i++) { @@ -1703,19 +1710,6 @@ static const struct lpphy_rx_iq_comp lpphy_rev2plus_iq_comp = { .c0 = 0, }; -static u8 lpphy_nbits(s32 val) -{ - u32 tmp = abs(val); - u8 nbits = 0; - - while (tmp != 0) { - nbits++; - tmp >>= 1; - } - - return nbits; -} - static int lpphy_calc_rx_iq_comp(struct b43_wldev *dev, u16 samples) { struct lpphy_iq_est iq_est; @@ -1742,8 +1736,8 @@ static int lpphy_calc_rx_iq_comp(struct b43_wldev *dev, u16 samples) goto out; } - prod_msb = lpphy_nbits(prod); - q_msb = lpphy_nbits(qpwr); + prod_msb = fls(abs(prod)); + q_msb = fls(abs(qpwr)); tmp1 = prod_msb - 20; if (tmp1 >= 0) { @@ -1773,47 +1767,6 @@ out: return ret; } -/* Complex number using 2 32-bit signed integers */ -typedef struct {s32 i, q;} lpphy_c32; - -static lpphy_c32 lpphy_cordic(int theta) -{ - u32 arctg[] = { 2949120, 1740967, 919879, 466945, 234379, 117304, - 58666, 29335, 14668, 7334, 3667, 1833, 917, 458, - 229, 115, 57, 29, }; - int i, tmp, signx = 1, angle = 0; - lpphy_c32 ret = { .i = 39797, .q = 0, }; - - theta = clamp_t(int, theta, -180, 180); - - if (theta > 90) { - theta -= 180; - signx = -1; - } else if (theta < -90) { - theta += 180; - signx = -1; - } - - for (i = 0; i <= 17; i++) { - if (theta > angle) { - tmp = ret.i - (ret.q >> i); - ret.q += ret.i >> i; - ret.i = tmp; - angle += arctg[i]; - } else { - tmp = ret.i + (ret.q >> i); - ret.q -= ret.i >> i; - ret.i = tmp; - angle -= arctg[i]; - } - } - - ret.i *= signx; - ret.q *= signx; - - return ret; -} - static void lpphy_run_samples(struct b43_wldev *dev, u16 samples, u16 loops, u16 wait) { @@ -1831,8 +1784,9 @@ static void lpphy_start_tx_tone(struct b43_wldev *dev, s32 freq, u16 max) { struct b43_phy_lp *lpphy = dev->phy.lp; u16 buf[64]; - int i, samples = 0, angle = 0, rotation = (9 * freq) / 500; - lpphy_c32 sample; + int i, samples = 0, angle = 0; + int rotation = (((36 * freq) / 20) << 16) / 100; + struct b43_c32 sample; lpphy->tx_tone_freq = freq; @@ -1848,10 +1802,10 @@ static void lpphy_start_tx_tone(struct b43_wldev *dev, s32 freq, u16 max) } for (i = 0; i < samples; i++) { - sample = lpphy_cordic(angle); + sample = b43_cordic(angle); angle += rotation; - buf[i] = ((sample.i * max) & 0xFF) << 8; - buf[i] |= (sample.q * max) & 0xFF; + buf[i] = CORDIC_CONVERT((sample.i * max) & 0xFF) << 8; + buf[i] |= CORDIC_CONVERT((sample.q * max) & 0xFF); } b43_lptab_write_bulk(dev, B43_LPTAB16(5, 0), samples, buf); diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index 992318a78077..795bb1e3345d 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -28,7 +28,50 @@ #include "b43.h" #include "phy_n.h" #include "tables_nphy.h" +#include "main.h" +struct nphy_txgains { + u16 txgm[2]; + u16 pga[2]; + u16 pad[2]; + u16 ipa[2]; +}; + +struct nphy_iqcal_params { + u16 txgm; + u16 pga; + u16 pad; + u16 ipa; + u16 cal_gain; + u16 ncorr[5]; +}; + +struct nphy_iq_est { + s32 iq0_prod; + u32 i0_pwr; + u32 q0_pwr; + s32 iq1_prod; + u32 i1_pwr; + u32 q1_pwr; +}; + +enum b43_nphy_rf_sequence { + B43_RFSEQ_RX2TX, + B43_RFSEQ_TX2RX, + B43_RFSEQ_RESET2RX, + B43_RFSEQ_UPDATE_GAINH, + B43_RFSEQ_UPDATE_GAINL, + B43_RFSEQ_UPDATE_GAINU, +}; + +static void b43_nphy_set_rf_sequence(struct b43_wldev *dev, u8 cmd, + u8 *events, u8 *delays, u8 length); +static void b43_nphy_force_rf_sequence(struct b43_wldev *dev, + enum b43_nphy_rf_sequence seq); +static void b43_nphy_rf_control_override(struct b43_wldev *dev, u16 field, + u16 value, u8 core, bool off); +static void b43_nphy_rf_control_intc_override(struct b43_wldev *dev, u8 field, + u16 value, u8 core); void b43_nphy_set_rxantenna(struct b43_wldev *dev, int antenna) {//TODO @@ -197,173 +240,1020 @@ void b43_nphy_radio_turn_off(struct b43_wldev *dev) ~B43_NPHY_RFCTL_CMD_EN); } -#define ntab_upload(dev, offset, data) do { \ - unsigned int i; \ - for (i = 0; i < (offset##_SIZE); i++) \ - b43_ntab_write(dev, (offset) + i, (data)[i]); \ - } while (0) - -/* Upload the N-PHY tables. */ +/* + * Upload the N-PHY tables. + * http://bcm-v4.sipsolutions.net/802.11/PHY/N/InitTables + */ static void b43_nphy_tables_init(struct b43_wldev *dev) { - /* Static tables */ - ntab_upload(dev, B43_NTAB_FRAMESTRUCT, b43_ntab_framestruct); - ntab_upload(dev, B43_NTAB_FRAMELT, b43_ntab_framelookup); - ntab_upload(dev, B43_NTAB_TMAP, b43_ntab_tmap); - ntab_upload(dev, B43_NTAB_TDTRN, b43_ntab_tdtrn); - ntab_upload(dev, B43_NTAB_INTLEVEL, b43_ntab_intlevel); - ntab_upload(dev, B43_NTAB_PILOT, b43_ntab_pilot); - ntab_upload(dev, B43_NTAB_PILOTLT, b43_ntab_pilotlt); - ntab_upload(dev, B43_NTAB_TDI20A0, b43_ntab_tdi20a0); - ntab_upload(dev, B43_NTAB_TDI20A1, b43_ntab_tdi20a1); - ntab_upload(dev, B43_NTAB_TDI40A0, b43_ntab_tdi40a0); - ntab_upload(dev, B43_NTAB_TDI40A1, b43_ntab_tdi40a1); - ntab_upload(dev, B43_NTAB_BDI, b43_ntab_bdi); - ntab_upload(dev, B43_NTAB_CHANEST, b43_ntab_channelest); - ntab_upload(dev, B43_NTAB_MCS, b43_ntab_mcs); - - /* Volatile tables */ - ntab_upload(dev, B43_NTAB_NOISEVAR10, b43_ntab_noisevar10); - ntab_upload(dev, B43_NTAB_NOISEVAR11, b43_ntab_noisevar11); - ntab_upload(dev, B43_NTAB_C0_ESTPLT, b43_ntab_estimatepowerlt0); - ntab_upload(dev, B43_NTAB_C1_ESTPLT, b43_ntab_estimatepowerlt1); - ntab_upload(dev, B43_NTAB_C0_ADJPLT, b43_ntab_adjustpower0); - ntab_upload(dev, B43_NTAB_C1_ADJPLT, b43_ntab_adjustpower1); - ntab_upload(dev, B43_NTAB_C0_GAINCTL, b43_ntab_gainctl0); - ntab_upload(dev, B43_NTAB_C1_GAINCTL, b43_ntab_gainctl1); - ntab_upload(dev, B43_NTAB_C0_IQLT, b43_ntab_iqlt0); - ntab_upload(dev, B43_NTAB_C1_IQLT, b43_ntab_iqlt1); - ntab_upload(dev, B43_NTAB_C0_LOFEEDTH, b43_ntab_loftlt0); - ntab_upload(dev, B43_NTAB_C1_LOFEEDTH, b43_ntab_loftlt1); + if (dev->phy.rev < 3) + b43_nphy_rev0_1_2_tables_init(dev); + else + b43_nphy_rev3plus_tables_init(dev); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/PA%20override */ +static void b43_nphy_pa_override(struct b43_wldev *dev, bool enable) +{ + struct b43_phy_n *nphy = dev->phy.n; + enum ieee80211_band band; + u16 tmp; + + if (!enable) { + nphy->rfctrl_intc1_save = b43_phy_read(dev, + B43_NPHY_RFCTL_INTC1); + nphy->rfctrl_intc2_save = b43_phy_read(dev, + B43_NPHY_RFCTL_INTC2); + band = b43_current_band(dev->wl); + if (dev->phy.rev >= 3) { + if (band == IEEE80211_BAND_5GHZ) + tmp = 0x600; + else + tmp = 0x480; + } else { + if (band == IEEE80211_BAND_5GHZ) + tmp = 0x180; + else + tmp = 0x120; + } + b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, tmp); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, tmp); + } else { + b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, + nphy->rfctrl_intc1_save); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, + nphy->rfctrl_intc2_save); + } } -static void b43_nphy_workarounds(struct b43_wldev *dev) +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxLpFbw */ +static void b43_nphy_tx_lp_fbw(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + u16 tmp; + enum ieee80211_band band = b43_current_band(dev->wl); + bool ipa = (nphy->ipa2g_on && band == IEEE80211_BAND_2GHZ) || + (nphy->ipa5g_on && band == IEEE80211_BAND_5GHZ); + + if (dev->phy.rev >= 3) { + if (ipa) { + tmp = 4; + b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S2, + (((((tmp << 3) | tmp) << 3) | tmp) << 3) | tmp); + } + + tmp = 1; + b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S2, + (((((tmp << 3) | tmp) << 3) | tmp) << 3) | tmp); + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/BmacPhyClkFgc */ +static void b43_nphy_bmac_clock_fgc(struct b43_wldev *dev, bool force) +{ + u32 tmslow; + + if (dev->phy.type != B43_PHYTYPE_N) + return; + + tmslow = ssb_read32(dev->dev, SSB_TMSLOW); + if (force) + tmslow |= SSB_TMSLOW_FGC; + else + tmslow &= ~SSB_TMSLOW_FGC; + ssb_write32(dev->dev, SSB_TMSLOW, tmslow); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CCA */ +static void b43_nphy_reset_cca(struct b43_wldev *dev) +{ + u16 bbcfg; + + b43_nphy_bmac_clock_fgc(dev, 1); + bbcfg = b43_phy_read(dev, B43_NPHY_BBCFG); + b43_phy_write(dev, B43_NPHY_BBCFG, bbcfg | B43_NPHY_BBCFG_RSTCCA); + udelay(1); + b43_phy_write(dev, B43_NPHY_BBCFG, bbcfg & ~B43_NPHY_BBCFG_RSTCCA); + b43_nphy_bmac_clock_fgc(dev, 0); + b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/MIMOConfig */ +static void b43_nphy_update_mimo_config(struct b43_wldev *dev, s32 preamble) +{ + u16 mimocfg = b43_phy_read(dev, B43_NPHY_MIMOCFG); + + mimocfg |= B43_NPHY_MIMOCFG_AUTO; + if (preamble == 1) + mimocfg |= B43_NPHY_MIMOCFG_GFMIX; + else + mimocfg &= ~B43_NPHY_MIMOCFG_GFMIX; + + b43_phy_write(dev, B43_NPHY_MIMOCFG, mimocfg); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/Chains */ +static void b43_nphy_update_txrx_chain(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + + bool override = false; + u16 chain = 0x33; + + if (nphy->txrx_chain == 0) { + chain = 0x11; + override = true; + } else if (nphy->txrx_chain == 1) { + chain = 0x22; + override = true; + } + + b43_phy_maskset(dev, B43_NPHY_RFSEQCA, + ~(B43_NPHY_RFSEQCA_TXEN | B43_NPHY_RFSEQCA_RXEN), + chain); + + if (override) + b43_phy_set(dev, B43_NPHY_RFSEQMODE, + B43_NPHY_RFSEQMODE_CAOVER); + else + b43_phy_mask(dev, B43_NPHY_RFSEQMODE, + ~B43_NPHY_RFSEQMODE_CAOVER); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxIqEst */ +static void b43_nphy_rx_iq_est(struct b43_wldev *dev, struct nphy_iq_est *est, + u16 samps, u8 time, bool wait) +{ + int i; + u16 tmp; + + b43_phy_write(dev, B43_NPHY_IQEST_SAMCNT, samps); + b43_phy_maskset(dev, B43_NPHY_IQEST_WT, ~B43_NPHY_IQEST_WT_VAL, time); + if (wait) + b43_phy_set(dev, B43_NPHY_IQEST_CMD, B43_NPHY_IQEST_CMD_MODE); + else + b43_phy_mask(dev, B43_NPHY_IQEST_CMD, ~B43_NPHY_IQEST_CMD_MODE); + + b43_phy_set(dev, B43_NPHY_IQEST_CMD, B43_NPHY_IQEST_CMD_START); + + for (i = 1000; i; i--) { + tmp = b43_phy_read(dev, B43_NPHY_IQEST_CMD); + if (!(tmp & B43_NPHY_IQEST_CMD_START)) { + est->i0_pwr = (b43_phy_read(dev, B43_NPHY_IQEST_IPACC_HI0) << 16) | + b43_phy_read(dev, B43_NPHY_IQEST_IPACC_LO0); + est->q0_pwr = (b43_phy_read(dev, B43_NPHY_IQEST_QPACC_HI0) << 16) | + b43_phy_read(dev, B43_NPHY_IQEST_QPACC_LO0); + est->iq0_prod = (b43_phy_read(dev, B43_NPHY_IQEST_IQACC_HI0) << 16) | + b43_phy_read(dev, B43_NPHY_IQEST_IQACC_LO0); + + est->i1_pwr = (b43_phy_read(dev, B43_NPHY_IQEST_IPACC_HI1) << 16) | + b43_phy_read(dev, B43_NPHY_IQEST_IPACC_LO1); + est->q1_pwr = (b43_phy_read(dev, B43_NPHY_IQEST_QPACC_HI1) << 16) | + b43_phy_read(dev, B43_NPHY_IQEST_QPACC_LO1); + est->iq1_prod = (b43_phy_read(dev, B43_NPHY_IQEST_IQACC_HI1) << 16) | + b43_phy_read(dev, B43_NPHY_IQEST_IQACC_LO1); + return; + } + udelay(10); + } + memset(est, 0, sizeof(*est)); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxIqCoeffs */ +static void b43_nphy_rx_iq_coeffs(struct b43_wldev *dev, bool write, + struct b43_phy_n_iq_comp *pcomp) +{ + if (write) { + b43_phy_write(dev, B43_NPHY_C1_RXIQ_COMPA0, pcomp->a0); + b43_phy_write(dev, B43_NPHY_C1_RXIQ_COMPB0, pcomp->b0); + b43_phy_write(dev, B43_NPHY_C2_RXIQ_COMPA1, pcomp->a1); + b43_phy_write(dev, B43_NPHY_C2_RXIQ_COMPB1, pcomp->b1); + } else { + pcomp->a0 = b43_phy_read(dev, B43_NPHY_C1_RXIQ_COMPA0); + pcomp->b0 = b43_phy_read(dev, B43_NPHY_C1_RXIQ_COMPB0); + pcomp->a1 = b43_phy_read(dev, B43_NPHY_C2_RXIQ_COMPA1); + pcomp->b1 = b43_phy_read(dev, B43_NPHY_C2_RXIQ_COMPB1); + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxCalPhyCleanup */ +static void b43_nphy_rx_cal_phy_cleanup(struct b43_wldev *dev, u8 core) +{ + u16 *regs = dev->phy.n->tx_rx_cal_phy_saveregs; + + b43_phy_write(dev, B43_NPHY_RFSEQCA, regs[0]); + if (core == 0) { + b43_phy_write(dev, B43_NPHY_AFECTL_C1, regs[1]); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, regs[2]); + } else { + b43_phy_write(dev, B43_NPHY_AFECTL_C2, regs[1]); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER, regs[2]); + } + b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, regs[3]); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, regs[4]); + b43_phy_write(dev, B43_NPHY_RFCTL_RSSIO1, regs[5]); + b43_phy_write(dev, B43_NPHY_RFCTL_RSSIO2, regs[6]); + b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S1, regs[7]); + b43_phy_write(dev, B43_NPHY_RFCTL_OVER, regs[8]); + b43_phy_write(dev, B43_NPHY_PAPD_EN0, regs[9]); + b43_phy_write(dev, B43_NPHY_PAPD_EN1, regs[10]); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxCalPhySetup */ +static void b43_nphy_rx_cal_phy_setup(struct b43_wldev *dev, u8 core) +{ + u8 rxval, txval; + u16 *regs = dev->phy.n->tx_rx_cal_phy_saveregs; + + regs[0] = b43_phy_read(dev, B43_NPHY_RFSEQCA); + if (core == 0) { + regs[1] = b43_phy_read(dev, B43_NPHY_AFECTL_C1); + regs[2] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER1); + } else { + regs[1] = b43_phy_read(dev, B43_NPHY_AFECTL_C2); + regs[2] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); + } + regs[3] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); + regs[4] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); + regs[5] = b43_phy_read(dev, B43_NPHY_RFCTL_RSSIO1); + regs[6] = b43_phy_read(dev, B43_NPHY_RFCTL_RSSIO2); + regs[7] = b43_phy_read(dev, B43_NPHY_TXF_40CO_B1S1); + regs[8] = b43_phy_read(dev, B43_NPHY_RFCTL_OVER); + regs[9] = b43_phy_read(dev, B43_NPHY_PAPD_EN0); + regs[10] = b43_phy_read(dev, B43_NPHY_PAPD_EN1); + + b43_phy_mask(dev, B43_NPHY_PAPD_EN0, ~0x0001); + b43_phy_mask(dev, B43_NPHY_PAPD_EN1, ~0x0001); + + b43_phy_maskset(dev, B43_NPHY_RFSEQCA, (u16)~B43_NPHY_RFSEQCA_RXDIS, + ((1 - core) << B43_NPHY_RFSEQCA_RXDIS_SHIFT)); + b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_TXEN, + ((1 - core) << B43_NPHY_RFSEQCA_TXEN_SHIFT)); + b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_RXEN, + (core << B43_NPHY_RFSEQCA_RXEN_SHIFT)); + b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_TXDIS, + (core << B43_NPHY_RFSEQCA_TXDIS_SHIFT)); + + if (core == 0) { + b43_phy_mask(dev, B43_NPHY_AFECTL_C1, ~0x0007); + b43_phy_set(dev, B43_NPHY_AFECTL_OVER1, 0x0007); + } else { + b43_phy_mask(dev, B43_NPHY_AFECTL_C2, ~0x0007); + b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x0007); + } + + b43_nphy_rf_control_intc_override(dev, 2, 0, 3); + b43_nphy_rf_control_override(dev, 8, 0, 3, false); + b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RX2TX); + + if (core == 0) { + rxval = 1; + txval = 8; + } else { + rxval = 4; + txval = 2; + } + b43_nphy_rf_control_intc_override(dev, 1, rxval, (core + 1)); + b43_nphy_rf_control_intc_override(dev, 1, txval, (2 - core)); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalcRxIqComp */ +static void b43_nphy_calc_rx_iq_comp(struct b43_wldev *dev, u8 mask) +{ + int i; + s32 iq; + u32 ii; + u32 qq; + int iq_nbits, qq_nbits; + int arsh, brsh; + u16 tmp, a, b; + + struct nphy_iq_est est; + struct b43_phy_n_iq_comp old; + struct b43_phy_n_iq_comp new = { }; + bool error = false; + + if (mask == 0) + return; + + b43_nphy_rx_iq_coeffs(dev, false, &old); + b43_nphy_rx_iq_coeffs(dev, true, &new); + b43_nphy_rx_iq_est(dev, &est, 0x4000, 32, false); + new = old; + + for (i = 0; i < 2; i++) { + if (i == 0 && (mask & 1)) { + iq = est.iq0_prod; + ii = est.i0_pwr; + qq = est.q0_pwr; + } else if (i == 1 && (mask & 2)) { + iq = est.iq1_prod; + ii = est.i1_pwr; + qq = est.q1_pwr; + } else { + B43_WARN_ON(1); + continue; + } + + if (ii + qq < 2) { + error = true; + break; + } + + iq_nbits = fls(abs(iq)); + qq_nbits = fls(qq); + + arsh = iq_nbits - 20; + if (arsh >= 0) { + a = -((iq << (30 - iq_nbits)) + (ii >> (1 + arsh))); + tmp = ii >> arsh; + } else { + a = -((iq << (30 - iq_nbits)) + (ii << (-1 - arsh))); + tmp = ii << -arsh; + } + if (tmp == 0) { + error = true; + break; + } + a /= tmp; + + brsh = qq_nbits - 11; + if (brsh >= 0) { + b = (qq << (31 - qq_nbits)); + tmp = ii >> brsh; + } else { + b = (qq << (31 - qq_nbits)); + tmp = ii << -brsh; + } + if (tmp == 0) { + error = true; + break; + } + b = int_sqrt(b / tmp - a * a) - (1 << 10); + + if (i == 0 && (mask & 0x1)) { + if (dev->phy.rev >= 3) { + new.a0 = a & 0x3FF; + new.b0 = b & 0x3FF; + } else { + new.a0 = b & 0x3FF; + new.b0 = a & 0x3FF; + } + } else if (i == 1 && (mask & 0x2)) { + if (dev->phy.rev >= 3) { + new.a1 = a & 0x3FF; + new.b1 = b & 0x3FF; + } else { + new.a1 = b & 0x3FF; + new.b1 = a & 0x3FF; + } + } + } + + if (error) + new = old; + + b43_nphy_rx_iq_coeffs(dev, true, &new); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxIqWar */ +static void b43_nphy_tx_iq_workaround(struct b43_wldev *dev) +{ + u16 array[4]; + int i; + + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x3C50); + for (i = 0; i < 4; i++) + array[i] = b43_phy_read(dev, B43_NPHY_TABLE_DATALO); + + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW0, array[0]); + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW1, array[1]); + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW2, array[2]); + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW3, array[3]); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/clip-detection */ +static void b43_nphy_write_clip_detection(struct b43_wldev *dev, u16 *clip_st) +{ + b43_phy_write(dev, B43_NPHY_C1_CLIP1THRES, clip_st[0]); + b43_phy_write(dev, B43_NPHY_C2_CLIP1THRES, clip_st[1]); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/clip-detection */ +static void b43_nphy_read_clip_detection(struct b43_wldev *dev, u16 *clip_st) +{ + clip_st[0] = b43_phy_read(dev, B43_NPHY_C1_CLIP1THRES); + clip_st[1] = b43_phy_read(dev, B43_NPHY_C2_CLIP1THRES); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/classifier */ +static u16 b43_nphy_classifier(struct b43_wldev *dev, u16 mask, u16 val) +{ + u16 tmp; + + if (dev->dev->id.revision == 16) + b43_mac_suspend(dev); + + tmp = b43_phy_read(dev, B43_NPHY_CLASSCTL); + tmp &= (B43_NPHY_CLASSCTL_CCKEN | B43_NPHY_CLASSCTL_OFDMEN | + B43_NPHY_CLASSCTL_WAITEDEN); + tmp &= ~mask; + tmp |= (val & mask); + b43_phy_maskset(dev, B43_NPHY_CLASSCTL, 0xFFF8, tmp); + + if (dev->dev->id.revision == 16) + b43_mac_enable(dev); + + return tmp; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/carriersearch */ +static void b43_nphy_stay_in_carrier_search(struct b43_wldev *dev, bool enable) { struct b43_phy *phy = &dev->phy; - unsigned int i; + struct b43_phy_n *nphy = phy->n; - b43_phy_set(dev, B43_NPHY_IQFLIP, - B43_NPHY_IQFLIP_ADC1 | B43_NPHY_IQFLIP_ADC2); - if (1 /* FIXME band is 2.4GHz */) { - b43_phy_set(dev, B43_NPHY_CLASSCTL, - B43_NPHY_CLASSCTL_CCKEN); - } else { - b43_phy_mask(dev, B43_NPHY_CLASSCTL, - ~B43_NPHY_CLASSCTL_CCKEN); - } - b43_radio_set(dev, B2055_C1_TX_RF_SPARE, 0x8); - b43_phy_write(dev, B43_NPHY_TXFRAMEDELAY, 8); - - /* Fixup some tables */ - b43_ntab_write(dev, B43_NTAB16(8, 0x00), 0xA); - b43_ntab_write(dev, B43_NTAB16(8, 0x10), 0xA); - b43_ntab_write(dev, B43_NTAB16(8, 0x02), 0xCDAA); - b43_ntab_write(dev, B43_NTAB16(8, 0x12), 0xCDAA); - b43_ntab_write(dev, B43_NTAB16(8, 0x08), 0); - b43_ntab_write(dev, B43_NTAB16(8, 0x18), 0); - b43_ntab_write(dev, B43_NTAB16(8, 0x07), 0x7AAB); - b43_ntab_write(dev, B43_NTAB16(8, 0x17), 0x7AAB); - b43_ntab_write(dev, B43_NTAB16(8, 0x06), 0x800); - b43_ntab_write(dev, B43_NTAB16(8, 0x16), 0x800); - - b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO1, 0x2D8); - b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, 0x301); - b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO2, 0x2D8); - b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, 0x301); - - //TODO set RF sequence - - /* Set narrowband clip threshold */ - b43_phy_write(dev, B43_NPHY_C1_NBCLIPTHRES, 66); - b43_phy_write(dev, B43_NPHY_C2_NBCLIPTHRES, 66); - - /* Set wideband clip 2 threshold */ - b43_phy_maskset(dev, B43_NPHY_C1_CLIPWBTHRES, - ~B43_NPHY_C1_CLIPWBTHRES_CLIP2, - 21 << B43_NPHY_C1_CLIPWBTHRES_CLIP2_SHIFT); - b43_phy_maskset(dev, B43_NPHY_C2_CLIPWBTHRES, - ~B43_NPHY_C2_CLIPWBTHRES_CLIP2, - 21 << B43_NPHY_C2_CLIPWBTHRES_CLIP2_SHIFT); - - /* Set Clip 2 detect */ - b43_phy_set(dev, B43_NPHY_C1_CGAINI, - B43_NPHY_C1_CGAINI_CL2DETECT); - b43_phy_set(dev, B43_NPHY_C2_CGAINI, - B43_NPHY_C2_CGAINI_CL2DETECT); - - if (0 /*FIXME*/) { - /* Set dwell lengths */ - b43_phy_write(dev, B43_NPHY_CLIP1_NBDWELL_LEN, 43); - b43_phy_write(dev, B43_NPHY_CLIP2_NBDWELL_LEN, 43); - b43_phy_write(dev, B43_NPHY_W1CLIP1_DWELL_LEN, 9); - b43_phy_write(dev, B43_NPHY_W1CLIP2_DWELL_LEN, 9); - - /* Set gain backoff */ - b43_phy_maskset(dev, B43_NPHY_C1_CGAINI, - ~B43_NPHY_C1_CGAINI_GAINBKOFF, - 1 << B43_NPHY_C1_CGAINI_GAINBKOFF_SHIFT); - b43_phy_maskset(dev, B43_NPHY_C2_CGAINI, - ~B43_NPHY_C2_CGAINI_GAINBKOFF, - 1 << B43_NPHY_C2_CGAINI_GAINBKOFF_SHIFT); + if (enable) { + u16 clip[] = { 0xFFFF, 0xFFFF }; + if (nphy->deaf_count++ == 0) { + nphy->classifier_state = b43_nphy_classifier(dev, 0, 0); + b43_nphy_classifier(dev, 0x7, 0); + b43_nphy_read_clip_detection(dev, nphy->clip_state); + b43_nphy_write_clip_detection(dev, clip); + } + b43_nphy_reset_cca(dev); + } else { + if (--nphy->deaf_count == 0) { + b43_nphy_classifier(dev, 0x7, nphy->classifier_state); + b43_nphy_write_clip_detection(dev, nphy->clip_state); + } + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/stop-playback */ +static void b43_nphy_stop_playback(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + u16 tmp; + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 1); + + tmp = b43_phy_read(dev, B43_NPHY_SAMP_STAT); + if (tmp & 0x1) + b43_phy_set(dev, B43_NPHY_SAMP_CMD, B43_NPHY_SAMP_CMD_STOP); + else if (tmp & 0x2) + b43_phy_mask(dev, B43_NPHY_IQLOCAL_CMDGCTL, (u16)~0x8000); + + b43_phy_mask(dev, B43_NPHY_SAMP_CMD, ~0x0004); + + if (nphy->bb_mult_save & 0x80000000) { + tmp = nphy->bb_mult_save & 0xFFFF; + b43_ntab_write(dev, B43_NTAB16(15, 87), tmp); + nphy->bb_mult_save = 0; + } + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 0); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SpurWar */ +static void b43_nphy_spur_workaround(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + + unsigned int channel; + int tone[2] = { 57, 58 }; + u32 noise[2] = { 0x3FF, 0x3FF }; + + B43_WARN_ON(dev->phy.rev < 3); + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 1); + + /* FIXME: channel = radio_chanspec */ + + if (nphy->gband_spurwar_en) { + /* TODO: N PHY Adjust Analog Pfbw (7) */ + if (channel == 11 && dev->phy.is_40mhz) + ; /* TODO: N PHY Adjust Min Noise Var(2, tone, noise)*/ + else + ; /* TODO: N PHY Adjust Min Noise Var(0, NULL, NULL)*/ + /* TODO: N PHY Adjust CRS Min Power (0x1E) */ + } + + if (nphy->aband_spurwar_en) { + if (channel == 54) { + tone[0] = 0x20; + noise[0] = 0x25F; + } else if (channel == 38 || channel == 102 || channel == 118) { + if (0 /* FIXME */) { + tone[0] = 0x20; + noise[0] = 0x21F; + } else { + tone[0] = 0; + noise[0] = 0; + } + } else if (channel == 134) { + tone[0] = 0x20; + noise[0] = 0x21F; + } else if (channel == 151) { + tone[0] = 0x10; + noise[0] = 0x23F; + } else if (channel == 153 || channel == 161) { + tone[0] = 0x30; + noise[0] = 0x23F; + } else { + tone[0] = 0; + noise[0] = 0; + } + + if (!tone[0] && !noise[0]) + ; /* TODO: N PHY Adjust Min Noise Var(1, tone, noise)*/ + else + ; /* TODO: N PHY Adjust Min Noise Var(0, NULL, NULL)*/ + } + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 0); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/WorkaroundsGainCtrl */ +static void b43_nphy_gain_crtl_workarounds(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + u8 i, j; + u8 code; + + /* TODO: for PHY >= 3 + s8 *lna1_gain, *lna2_gain; + u8 *gain_db, *gain_bits; + u16 *rfseq_init; + u8 lpf_gain[6] = { 0x00, 0x06, 0x0C, 0x12, 0x12, 0x12 }; + u8 lpf_bits[6] = { 0, 1, 2, 3, 3, 3 }; + */ + + u8 rfseq_events[3] = { 6, 8, 7 }; + u8 rfseq_delays[3] = { 10, 30, 1 }; + + if (dev->phy.rev >= 3) { + /* TODO */ + } else { + /* Set Clip 2 detect */ + b43_phy_set(dev, B43_NPHY_C1_CGAINI, + B43_NPHY_C1_CGAINI_CL2DETECT); + b43_phy_set(dev, B43_NPHY_C2_CGAINI, + B43_NPHY_C2_CGAINI_CL2DETECT); + + /* Set narrowband clip threshold */ + b43_phy_set(dev, B43_NPHY_C1_NBCLIPTHRES, 0x84); + b43_phy_set(dev, B43_NPHY_C2_NBCLIPTHRES, 0x84); + + if (!dev->phy.is_40mhz) { + /* Set dwell lengths */ + b43_phy_set(dev, B43_NPHY_CLIP1_NBDWELL_LEN, 0x002B); + b43_phy_set(dev, B43_NPHY_CLIP2_NBDWELL_LEN, 0x002B); + b43_phy_set(dev, B43_NPHY_W1CLIP1_DWELL_LEN, 0x0009); + b43_phy_set(dev, B43_NPHY_W1CLIP2_DWELL_LEN, 0x0009); + } + + /* Set wideband clip 2 threshold */ + b43_phy_maskset(dev, B43_NPHY_C1_CLIPWBTHRES, + ~B43_NPHY_C1_CLIPWBTHRES_CLIP2, + 21); + b43_phy_maskset(dev, B43_NPHY_C2_CLIPWBTHRES, + ~B43_NPHY_C2_CLIPWBTHRES_CLIP2, + 21); + + if (!dev->phy.is_40mhz) { + b43_phy_maskset(dev, B43_NPHY_C1_CGAINI, + ~B43_NPHY_C1_CGAINI_GAINBKOFF, 0x1); + b43_phy_maskset(dev, B43_NPHY_C2_CGAINI, + ~B43_NPHY_C2_CGAINI_GAINBKOFF, 0x1); + b43_phy_maskset(dev, B43_NPHY_C1_CCK_CGAINI, + ~B43_NPHY_C1_CCK_CGAINI_GAINBKOFF, 0x1); + b43_phy_maskset(dev, B43_NPHY_C2_CCK_CGAINI, + ~B43_NPHY_C2_CCK_CGAINI_GAINBKOFF, 0x1); + } + + b43_phy_set(dev, B43_NPHY_CCK_SHIFTB_REF, 0x809C); + + if (nphy->gain_boost) { + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ && + dev->phy.is_40mhz) + code = 4; + else + code = 5; + } else { + code = dev->phy.is_40mhz ? 6 : 7; + } /* Set HPVGA2 index */ b43_phy_maskset(dev, B43_NPHY_C1_INITGAIN, ~B43_NPHY_C1_INITGAIN_HPVGA2, - 6 << B43_NPHY_C1_INITGAIN_HPVGA2_SHIFT); + code << B43_NPHY_C1_INITGAIN_HPVGA2_SHIFT); b43_phy_maskset(dev, B43_NPHY_C2_INITGAIN, ~B43_NPHY_C2_INITGAIN_HPVGA2, - 6 << B43_NPHY_C2_INITGAIN_HPVGA2_SHIFT); + code << B43_NPHY_C2_INITGAIN_HPVGA2_SHIFT); + + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x1D06); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, + (code << 8 | 0x7C)); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, + (code << 8 | 0x7C)); + + /* TODO: b43_nphy_adjust_lna_gain_table(dev); */ + + if (nphy->elna_gain_config) { + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x0808); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x0); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); + + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x0C08); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x0); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); + + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x1D06); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, + (code << 8 | 0x74)); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, + (code << 8 | 0x74)); + } - //FIXME verify that the specs really mean to use autoinc here. - for (i = 0; i < 3; i++) - b43_ntab_write(dev, B43_NTAB16(7, 0x106) + i, 0x673); + if (dev->phy.rev == 2) { + for (i = 0; i < 4; i++) { + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, + (0x0400 * i) + 0x0020); + for (j = 0; j < 21; j++) + b43_phy_write(dev, + B43_NPHY_TABLE_DATALO, 3 * j); + } + + b43_nphy_set_rf_sequence(dev, 5, + rfseq_events, rfseq_delays, 3); + b43_phy_maskset(dev, B43_NPHY_OVER_DGAIN1, + (u16)~B43_NPHY_OVER_DGAIN_CCKDGECV, + 0x5A << B43_NPHY_OVER_DGAIN_CCKDGECV_SHIFT); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + b43_phy_maskset(dev, B43_PHY_N(0xC5D), + 0xFF80, 4); + } } +} - /* Set minimum gain value */ - b43_phy_maskset(dev, B43_NPHY_C1_MINMAX_GAIN, - ~B43_NPHY_C1_MINGAIN, - 23 << B43_NPHY_C1_MINGAIN_SHIFT); - b43_phy_maskset(dev, B43_NPHY_C2_MINMAX_GAIN, - ~B43_NPHY_C2_MINGAIN, - 23 << B43_NPHY_C2_MINGAIN_SHIFT); +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/Workarounds */ +static void b43_nphy_workarounds(struct b43_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->bus; + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = phy->n; - if (phy->rev < 2) { - b43_phy_mask(dev, B43_NPHY_SCRAM_SIGCTL, - ~B43_NPHY_SCRAM_SIGCTL_SCM); + u8 events1[7] = { 0x0, 0x1, 0x2, 0x8, 0x4, 0x5, 0x3 }; + u8 delays1[7] = { 0x8, 0x6, 0x6, 0x2, 0x4, 0x3C, 0x1 }; + + u8 events2[7] = { 0x0, 0x3, 0x5, 0x4, 0x2, 0x1, 0x8 }; + u8 delays2[7] = { 0x8, 0x6, 0x2, 0x4, 0x4, 0x6, 0x1 }; + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + b43_nphy_classifier(dev, 1, 0); + else + b43_nphy_classifier(dev, 1, 1); + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 1); + + b43_phy_set(dev, B43_NPHY_IQFLIP, + B43_NPHY_IQFLIP_ADC1 | B43_NPHY_IQFLIP_ADC2); + + if (dev->phy.rev >= 3) { + /* TODO */ + } else { + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ && + nphy->band5g_pwrgain) { + b43_radio_mask(dev, B2055_C1_TX_RF_SPARE, ~0x8); + b43_radio_mask(dev, B2055_C2_TX_RF_SPARE, ~0x8); + } else { + b43_radio_set(dev, B2055_C1_TX_RF_SPARE, 0x8); + b43_radio_set(dev, B2055_C2_TX_RF_SPARE, 0x8); + } + + /* TODO: convert to b43_ntab_write? */ + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x2000); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x000A); + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x2010); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x000A); + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x2002); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0xCDAA); + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x2012); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0xCDAA); + + if (dev->phy.rev < 2) { + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x2008); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x0000); + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x2018); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x0000); + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x2007); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x7AAB); + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x2017); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x7AAB); + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x2006); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x0800); + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x2016); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x0800); + } + + b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO1, 0x2D8); + b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, 0x301); + b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO2, 0x2D8); + b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, 0x301); + + if (bus->sprom.boardflags2_lo & 0x100 && + bus->boardinfo.type == 0x8B) { + delays1[0] = 0x1; + delays1[5] = 0x14; + } + b43_nphy_set_rf_sequence(dev, 0, events1, delays1, 7); + b43_nphy_set_rf_sequence(dev, 1, events2, delays2, 7); + + b43_nphy_gain_crtl_workarounds(dev); + + if (dev->phy.rev < 2) { + if (b43_phy_read(dev, B43_NPHY_RXCTL) & 0x2) + ; /*TODO: b43_mhf(dev, 2, 0x0010, 0x0010, 3);*/ + } else if (dev->phy.rev == 2) { + b43_phy_write(dev, B43_NPHY_CRSCHECK2, 0); + b43_phy_write(dev, B43_NPHY_CRSCHECK3, 0); + } + + if (dev->phy.rev < 2) + b43_phy_mask(dev, B43_NPHY_SCRAM_SIGCTL, + ~B43_NPHY_SCRAM_SIGCTL_SCM); + + /* Set phase track alpha and beta */ + b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x125); + b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x1B3); + b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x105); + b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x16E); + b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0xCD); + b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x20); + + b43_phy_mask(dev, B43_NPHY_PIL_DW1, + (u16)~B43_NPHY_PIL_DW_64QAM); + b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B1, 0xB5); + b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B2, 0xA4); + b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B3, 0x00); + + if (dev->phy.rev == 2) + b43_phy_set(dev, B43_NPHY_FINERX2_CGC, + B43_NPHY_FINERX2_CGC_DECGC); } - /* Set phase track alpha and beta */ - b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x125); - b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x1B3); - b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x105); - b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x16E); - b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0xCD); - b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x20); + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 0); } -static void b43_nphy_reset_cca(struct b43_wldev *dev) +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/LoadSampleTable */ +static int b43_nphy_load_samples(struct b43_wldev *dev, + struct b43_c32 *samples, u16 len) { + struct b43_phy_n *nphy = dev->phy.n; + u16 i; + u32 *data; + + data = kzalloc(len * sizeof(u32), GFP_KERNEL); + if (!data) { + b43err(dev->wl, "allocation for samples loading failed\n"); + return -ENOMEM; + } + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 1); + + for (i = 0; i < len; i++) { + data[i] = (samples[i].i & 0x3FF << 10); + data[i] |= samples[i].q & 0x3FF; + } + b43_ntab_write_bulk(dev, B43_NTAB32(17, 0), len, data); + + kfree(data); + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 0); + return 0; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GenLoadSamples */ +static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max, + bool test) { - u16 bbcfg; + int i; + u16 bw, len, rot, angle; + struct b43_c32 *samples; - ssb_write32(dev->dev, SSB_TMSLOW, - ssb_read32(dev->dev, SSB_TMSLOW) | SSB_TMSLOW_FGC); - bbcfg = b43_phy_read(dev, B43_NPHY_BBCFG); - b43_phy_set(dev, B43_NPHY_BBCFG, B43_NPHY_BBCFG_RSTCCA); - b43_phy_write(dev, B43_NPHY_BBCFG, - bbcfg & ~B43_NPHY_BBCFG_RSTCCA); - ssb_write32(dev->dev, SSB_TMSLOW, - ssb_read32(dev->dev, SSB_TMSLOW) & ~SSB_TMSLOW_FGC); + + bw = (dev->phy.is_40mhz) ? 40 : 20; + len = bw << 3; + + if (test) { + if (b43_phy_read(dev, B43_NPHY_BBCFG) & B43_NPHY_BBCFG_RSTRX) + bw = 82; + else + bw = 80; + + if (dev->phy.is_40mhz) + bw <<= 1; + + len = bw << 1; + } + + samples = kzalloc(len * sizeof(struct b43_c32), GFP_KERNEL); + if (!samples) { + b43err(dev->wl, "allocation for samples generation failed\n"); + return 0; + } + rot = (((freq * 36) / bw) << 16) / 100; + angle = 0; + + for (i = 0; i < len; i++) { + samples[i] = b43_cordic(angle); + angle += rot; + samples[i].q = CORDIC_CONVERT(samples[i].q * max); + samples[i].i = CORDIC_CONVERT(samples[i].i * max); + } + + i = b43_nphy_load_samples(dev, samples, len); + kfree(samples); + return (i < 0) ? 0 : len; } -enum b43_nphy_rf_sequence { - B43_RFSEQ_RX2TX, - B43_RFSEQ_TX2RX, - B43_RFSEQ_RESET2RX, - B43_RFSEQ_UPDATE_GAINH, - B43_RFSEQ_UPDATE_GAINL, - B43_RFSEQ_UPDATE_GAINU, -}; +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RunSamples */ +static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops, + u16 wait, bool iqmode, bool dac_test) +{ + struct b43_phy_n *nphy = dev->phy.n; + int i; + u16 seq_mode; + u32 tmp; + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, true); + + if ((nphy->bb_mult_save & 0x80000000) == 0) { + tmp = b43_ntab_read(dev, B43_NTAB16(15, 87)); + nphy->bb_mult_save = (tmp & 0xFFFF) | 0x80000000; + } + + if (!dev->phy.is_40mhz) + tmp = 0x6464; + else + tmp = 0x4747; + b43_ntab_write(dev, B43_NTAB16(15, 87), tmp); + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, false); + + b43_phy_write(dev, B43_NPHY_SAMP_DEPCNT, (samps - 1)); + + if (loops != 0xFFFF) + b43_phy_write(dev, B43_NPHY_SAMP_LOOPCNT, (loops - 1)); + else + b43_phy_write(dev, B43_NPHY_SAMP_LOOPCNT, loops); + + b43_phy_write(dev, B43_NPHY_SAMP_WAITCNT, wait); + + seq_mode = b43_phy_read(dev, B43_NPHY_RFSEQMODE); + + b43_phy_set(dev, B43_NPHY_RFSEQMODE, B43_NPHY_RFSEQMODE_CAOVER); + if (iqmode) { + b43_phy_mask(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x7FFF); + b43_phy_set(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8000); + } else { + if (dac_test) + b43_phy_write(dev, B43_NPHY_SAMP_CMD, 5); + else + b43_phy_write(dev, B43_NPHY_SAMP_CMD, 1); + } + for (i = 0; i < 100; i++) { + if (b43_phy_read(dev, B43_NPHY_RFSEQST) & 1) { + i = 0; + break; + } + udelay(10); + } + if (i) + b43err(dev->wl, "run samples timeout\n"); + + b43_phy_write(dev, B43_NPHY_RFSEQMODE, seq_mode); +} + +/* + * Transmits a known value for LO calibration + * http://bcm-v4.sipsolutions.net/802.11/PHY/N/TXTone + */ +static int b43_nphy_tx_tone(struct b43_wldev *dev, u32 freq, u16 max_val, + bool iqmode, bool dac_test) +{ + u16 samp = b43_nphy_gen_load_samples(dev, freq, max_val, dac_test); + if (samp == 0) + return -1; + b43_nphy_run_samples(dev, samp, 0xFFFF, 0, iqmode, dac_test); + return 0; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlCoefSetup */ +static void b43_nphy_tx_pwr_ctrl_coef_setup(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + int i, j; + u32 tmp; + u32 cur_real, cur_imag, real_part, imag_part; + + u16 buffer[7]; + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, true); + + b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 7, buffer); + + for (i = 0; i < 2; i++) { + tmp = ((buffer[i * 2] & 0x3FF) << 10) | + (buffer[i * 2 + 1] & 0x3FF); + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, + (((i + 26) << 10) | 320)); + for (j = 0; j < 128; j++) { + b43_phy_write(dev, B43_NPHY_TABLE_DATAHI, + ((tmp >> 16) & 0xFFFF)); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, + (tmp & 0xFFFF)); + } + } + + for (i = 0; i < 2; i++) { + tmp = buffer[5 + i]; + real_part = (tmp >> 8) & 0xFF; + imag_part = (tmp & 0xFF); + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, + (((i + 26) << 10) | 448)); + + if (dev->phy.rev >= 3) { + cur_real = real_part; + cur_imag = imag_part; + tmp = ((cur_real & 0xFF) << 8) | (cur_imag & 0xFF); + } + + for (j = 0; j < 128; j++) { + if (dev->phy.rev < 3) { + cur_real = (real_part * loscale[j] + 128) >> 8; + cur_imag = (imag_part * loscale[j] + 128) >> 8; + tmp = ((cur_real & 0xFF) << 8) | + (cur_imag & 0xFF); + } + b43_phy_write(dev, B43_NPHY_TABLE_DATAHI, + ((tmp >> 16) & 0xFFFF)); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, + (tmp & 0xFFFF)); + } + } + + if (dev->phy.rev >= 3) { + b43_shm_write16(dev, B43_SHM_SHARED, + B43_SHM_SH_NPHY_TXPWR_INDX0, 0xFFFF); + b43_shm_write16(dev, B43_SHM_SHARED, + B43_SHM_SH_NPHY_TXPWR_INDX1, 0xFFFF); + } + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, false); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SetRfSeq */ +static void b43_nphy_set_rf_sequence(struct b43_wldev *dev, u8 cmd, + u8 *events, u8 *delays, u8 length) +{ + struct b43_phy_n *nphy = dev->phy.n; + u8 i; + u8 end = (dev->phy.rev >= 3) ? 0x1F : 0x0F; + u16 offset1 = cmd << 4; + u16 offset2 = offset1 + 0x80; + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, true); + + b43_ntab_write_bulk(dev, B43_NTAB8(7, offset1), length, events); + b43_ntab_write_bulk(dev, B43_NTAB8(7, offset2), length, delays); + + for (i = length; i < 16; i++) { + b43_ntab_write(dev, B43_NTAB8(7, offset1 + i), end); + b43_ntab_write(dev, B43_NTAB8(7, offset2 + i), 1); + } + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, false); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ForceRFSeq */ static void b43_nphy_force_rf_sequence(struct b43_wldev *dev, enum b43_nphy_rf_sequence seq) { @@ -376,6 +1266,7 @@ static void b43_nphy_force_rf_sequence(struct b43_wldev *dev, [B43_RFSEQ_UPDATE_GAINU] = B43_NPHY_RFSEQTR_UPGU, }; int i; + u16 seq_mode = b43_phy_read(dev, B43_NPHY_RFSEQMODE); B43_WARN_ON(seq >= ARRAY_SIZE(trigger)); @@ -389,8 +1280,181 @@ static void b43_nphy_force_rf_sequence(struct b43_wldev *dev, } b43err(dev->wl, "RF sequence status timeout\n"); ok: - b43_phy_mask(dev, B43_NPHY_RFSEQMODE, - ~(B43_NPHY_RFSEQMODE_CAOVER | B43_NPHY_RFSEQMODE_TROVER)); + b43_phy_write(dev, B43_NPHY_RFSEQMODE, seq_mode); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlOverride */ +static void b43_nphy_rf_control_override(struct b43_wldev *dev, u16 field, + u16 value, u8 core, bool off) +{ + int i; + u8 index = fls(field); + u8 addr, en_addr, val_addr; + /* we expect only one bit set */ + B43_WARN_ON(field & (~(1 << (index - 1)))); + + if (dev->phy.rev >= 3) { + const struct nphy_rf_control_override_rev3 *rf_ctrl; + for (i = 0; i < 2; i++) { + if (index == 0 || index == 16) { + b43err(dev->wl, + "Unsupported RF Ctrl Override call\n"); + return; + } + + rf_ctrl = &tbl_rf_control_override_rev3[index - 1]; + en_addr = B43_PHY_N((i == 0) ? + rf_ctrl->en_addr0 : rf_ctrl->en_addr1); + val_addr = B43_PHY_N((i == 0) ? + rf_ctrl->val_addr0 : rf_ctrl->val_addr1); + + if (off) { + b43_phy_mask(dev, en_addr, ~(field)); + b43_phy_mask(dev, val_addr, + ~(rf_ctrl->val_mask)); + } else { + if (core == 0 || ((1 << core) & i) != 0) { + b43_phy_set(dev, en_addr, field); + b43_phy_maskset(dev, val_addr, + ~(rf_ctrl->val_mask), + (value << rf_ctrl->val_shift)); + } + } + } + } else { + const struct nphy_rf_control_override_rev2 *rf_ctrl; + if (off) { + b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, ~(field)); + value = 0; + } else { + b43_phy_set(dev, B43_NPHY_RFCTL_OVER, field); + } + + for (i = 0; i < 2; i++) { + if (index <= 1 || index == 16) { + b43err(dev->wl, + "Unsupported RF Ctrl Override call\n"); + return; + } + + if (index == 2 || index == 10 || + (index >= 13 && index <= 15)) { + core = 1; + } + + rf_ctrl = &tbl_rf_control_override_rev2[index - 2]; + addr = B43_PHY_N((i == 0) ? + rf_ctrl->addr0 : rf_ctrl->addr1); + + if ((core & (1 << i)) != 0) + b43_phy_maskset(dev, addr, ~(rf_ctrl->bmask), + (value << rf_ctrl->shift)); + + b43_phy_set(dev, B43_NPHY_RFCTL_OVER, 0x1); + b43_phy_set(dev, B43_NPHY_RFCTL_CMD, + B43_NPHY_RFCTL_CMD_START); + udelay(1); + b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, 0xFFFE); + } + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlIntcOverride */ +static void b43_nphy_rf_control_intc_override(struct b43_wldev *dev, u8 field, + u16 value, u8 core) +{ + u8 i, j; + u16 reg, tmp, val; + + B43_WARN_ON(dev->phy.rev < 3); + B43_WARN_ON(field > 4); + + for (i = 0; i < 2; i++) { + if ((core == 1 && i == 1) || (core == 2 && !i)) + continue; + + reg = (i == 0) ? + B43_NPHY_RFCTL_INTC1 : B43_NPHY_RFCTL_INTC2; + b43_phy_mask(dev, reg, 0xFBFF); + + switch (field) { + case 0: + b43_phy_write(dev, reg, 0); + b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); + break; + case 1: + if (!i) { + b43_phy_maskset(dev, B43_NPHY_RFCTL_INTC1, + 0xFC3F, (value << 6)); + b43_phy_maskset(dev, B43_NPHY_TXF_40CO_B1S1, + 0xFFFE, 1); + b43_phy_set(dev, B43_NPHY_RFCTL_CMD, + B43_NPHY_RFCTL_CMD_START); + for (j = 0; j < 100; j++) { + if (b43_phy_read(dev, B43_NPHY_RFCTL_CMD) & B43_NPHY_RFCTL_CMD_START) { + j = 0; + break; + } + udelay(10); + } + if (j) + b43err(dev->wl, + "intc override timeout\n"); + b43_phy_mask(dev, B43_NPHY_TXF_40CO_B1S1, + 0xFFFE); + } else { + b43_phy_maskset(dev, B43_NPHY_RFCTL_INTC2, + 0xFC3F, (value << 6)); + b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER, + 0xFFFE, 1); + b43_phy_set(dev, B43_NPHY_RFCTL_CMD, + B43_NPHY_RFCTL_CMD_RXTX); + for (j = 0; j < 100; j++) { + if (b43_phy_read(dev, B43_NPHY_RFCTL_CMD) & B43_NPHY_RFCTL_CMD_RXTX) { + j = 0; + break; + } + udelay(10); + } + if (j) + b43err(dev->wl, + "intc override timeout\n"); + b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, + 0xFFFE); + } + break; + case 2: + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + tmp = 0x0020; + val = value << 5; + } else { + tmp = 0x0010; + val = value << 4; + } + b43_phy_maskset(dev, reg, ~tmp, val); + break; + case 3: + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + tmp = 0x0001; + val = value; + } else { + tmp = 0x0004; + val = value << 2; + } + b43_phy_maskset(dev, reg, ~tmp, val); + break; + case 4: + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + tmp = 0x0002; + val = value << 1; + } else { + tmp = 0x0008; + val = value << 3; + } + b43_phy_maskset(dev, reg, ~tmp, val); + break; + } + } } static void b43_nphy_bphy_init(struct b43_wldev *dev) @@ -411,81 +1475,1680 @@ static void b43_nphy_bphy_init(struct b43_wldev *dev) b43_phy_write(dev, B43_PHY_N_BMODE(0x38), 0x668); } -/* RSSI Calibration */ -static void b43_nphy_rssi_cal(struct b43_wldev *dev, u8 type) +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ScaleOffsetRssi */ +static void b43_nphy_scale_offset_rssi(struct b43_wldev *dev, u16 scale, + s8 offset, u8 core, u8 rail, u8 type) { - //TODO + u16 tmp; + bool core1or5 = (core == 1) || (core == 5); + bool core2or5 = (core == 2) || (core == 5); + + offset = clamp_val(offset, -32, 31); + tmp = ((scale & 0x3F) << 8) | (offset & 0x3F); + + if (core1or5 && (rail == 0) && (type == 2)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Z, tmp); + if (core1or5 && (rail == 1) && (type == 2)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Z, tmp); + if (core2or5 && (rail == 0) && (type == 2)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Z, tmp); + if (core2or5 && (rail == 1) && (type == 2)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Z, tmp); + if (core1or5 && (rail == 0) && (type == 0)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_X, tmp); + if (core1or5 && (rail == 1) && (type == 0)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_X, tmp); + if (core2or5 && (rail == 0) && (type == 0)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_X, tmp); + if (core2or5 && (rail == 1) && (type == 0)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_X, tmp); + if (core1or5 && (rail == 0) && (type == 1)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Y, tmp); + if (core1or5 && (rail == 1) && (type == 1)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Y, tmp); + if (core2or5 && (rail == 0) && (type == 1)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Y, tmp); + if (core2or5 && (rail == 1) && (type == 1)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Y, tmp); + if (core1or5 && (rail == 0) && (type == 6)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_TBD, tmp); + if (core1or5 && (rail == 1) && (type == 6)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_TBD, tmp); + if (core2or5 && (rail == 0) && (type == 6)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_TBD, tmp); + if (core2or5 && (rail == 1) && (type == 6)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_TBD, tmp); + if (core1or5 && (rail == 0) && (type == 3)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_PWRDET, tmp); + if (core1or5 && (rail == 1) && (type == 3)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_PWRDET, tmp); + if (core2or5 && (rail == 0) && (type == 3)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_PWRDET, tmp); + if (core2or5 && (rail == 1) && (type == 3)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_PWRDET, tmp); + if (core1or5 && (type == 4)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_TSSI, tmp); + if (core2or5 && (type == 4)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_TSSI, tmp); + if (core1or5 && (type == 5)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_TSSI, tmp); + if (core2or5 && (type == 5)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_TSSI, tmp); +} + +static void b43_nphy_rev2_rssi_select(struct b43_wldev *dev, u8 code, u8 type) +{ + u16 val; + + if (type < 3) + val = 0; + else if (type == 6) + val = 1; + else if (type == 3) + val = 2; + else + val = 3; + + val = (val << 12) | (val << 14); + b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0x0FFF, val); + b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0x0FFF, val); + + if (type < 3) { + b43_phy_maskset(dev, B43_NPHY_RFCTL_RSSIO1, 0xFFCF, + (type + 1) << 4); + b43_phy_maskset(dev, B43_NPHY_RFCTL_RSSIO2, 0xFFCF, + (type + 1) << 4); + } + + /* TODO use some definitions */ + if (code == 0) { + b43_phy_maskset(dev, B43_NPHY_AFECTL_OVER, 0xCFFF, 0); + if (type < 3) { + b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD, 0xFEC7, 0); + b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER, 0xEFDC, 0); + b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD, 0xFFFE, 0); + udelay(20); + b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER, 0xFFFE, 0); + } + } else { + b43_phy_maskset(dev, B43_NPHY_AFECTL_OVER, 0xCFFF, + 0x3000); + if (type < 3) { + b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD, + 0xFEC7, 0x0180); + b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER, + 0xEFDC, (code << 1 | 0x1021)); + b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD, 0xFFFE, 0x1); + udelay(20); + b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER, 0xFFFE, 0); + } + } +} + +static void b43_nphy_rev3_rssi_select(struct b43_wldev *dev, u8 code, u8 type) +{ + struct b43_phy_n *nphy = dev->phy.n; + u8 i; + u16 reg, val; + + if (code == 0) { + b43_phy_mask(dev, B43_NPHY_AFECTL_OVER1, 0xFDFF); + b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, 0xFDFF); + b43_phy_mask(dev, B43_NPHY_AFECTL_C1, 0xFCFF); + b43_phy_mask(dev, B43_NPHY_AFECTL_C2, 0xFCFF); + b43_phy_mask(dev, B43_NPHY_TXF_40CO_B1S0, 0xFFDF); + b43_phy_mask(dev, B43_NPHY_TXF_40CO_B32S1, 0xFFDF); + b43_phy_mask(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, 0xFFC3); + b43_phy_mask(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, 0xFFC3); + } else { + for (i = 0; i < 2; i++) { + if ((code == 1 && i == 1) || (code == 2 && !i)) + continue; + + reg = (i == 0) ? + B43_NPHY_AFECTL_OVER1 : B43_NPHY_AFECTL_OVER; + b43_phy_maskset(dev, reg, 0xFDFF, 0x0200); + + if (type < 3) { + reg = (i == 0) ? + B43_NPHY_AFECTL_C1 : + B43_NPHY_AFECTL_C2; + b43_phy_maskset(dev, reg, 0xFCFF, 0); + + reg = (i == 0) ? + B43_NPHY_RFCTL_LUT_TRSW_UP1 : + B43_NPHY_RFCTL_LUT_TRSW_UP2; + b43_phy_maskset(dev, reg, 0xFFC3, 0); + + if (type == 0) + val = (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) ? 4 : 8; + else if (type == 1) + val = 16; + else + val = 32; + b43_phy_set(dev, reg, val); + + reg = (i == 0) ? + B43_NPHY_TXF_40CO_B1S0 : + B43_NPHY_TXF_40CO_B32S1; + b43_phy_set(dev, reg, 0x0020); + } else { + if (type == 6) + val = 0x0100; + else if (type == 3) + val = 0x0200; + else + val = 0x0300; + + reg = (i == 0) ? + B43_NPHY_AFECTL_C1 : + B43_NPHY_AFECTL_C2; + + b43_phy_maskset(dev, reg, 0xFCFF, val); + b43_phy_maskset(dev, reg, 0xF3FF, val << 2); + + if (type != 3 && type != 6) { + enum ieee80211_band band = + b43_current_band(dev->wl); + + if ((nphy->ipa2g_on && + band == IEEE80211_BAND_2GHZ) || + (nphy->ipa5g_on && + band == IEEE80211_BAND_5GHZ)) + val = (band == IEEE80211_BAND_5GHZ) ? 0xC : 0xE; + else + val = 0x11; + reg = (i == 0) ? 0x2000 : 0x3000; + reg |= B2055_PADDRV; + b43_radio_write16(dev, reg, val); + + reg = (i == 0) ? + B43_NPHY_AFECTL_OVER1 : + B43_NPHY_AFECTL_OVER; + b43_phy_set(dev, reg, 0x0200); + } + } + } + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSISel */ +static void b43_nphy_rssi_select(struct b43_wldev *dev, u8 code, u8 type) +{ + if (dev->phy.rev >= 3) + b43_nphy_rev3_rssi_select(dev, code, type); + else + b43_nphy_rev2_rssi_select(dev, code, type); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SetRssi2055Vcm */ +static void b43_nphy_set_rssi_2055_vcm(struct b43_wldev *dev, u8 type, u8 *buf) +{ + int i; + for (i = 0; i < 2; i++) { + if (type == 2) { + if (i == 0) { + b43_radio_maskset(dev, B2055_C1_B0NB_RSSIVCM, + 0xFC, buf[0]); + b43_radio_maskset(dev, B2055_C1_RX_BB_RSSICTL5, + 0xFC, buf[1]); + } else { + b43_radio_maskset(dev, B2055_C2_B0NB_RSSIVCM, + 0xFC, buf[2 * i]); + b43_radio_maskset(dev, B2055_C2_RX_BB_RSSICTL5, + 0xFC, buf[2 * i + 1]); + } + } else { + if (i == 0) + b43_radio_maskset(dev, B2055_C1_RX_BB_RSSICTL5, + 0xF3, buf[0] << 2); + else + b43_radio_maskset(dev, B2055_C2_RX_BB_RSSICTL5, + 0xF3, buf[2 * i + 1] << 2); + } + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/PollRssi */ +static int b43_nphy_poll_rssi(struct b43_wldev *dev, u8 type, s32 *buf, + u8 nsamp) +{ + int i; + int out; + u16 save_regs_phy[9]; + u16 s[2]; + + if (dev->phy.rev >= 3) { + save_regs_phy[0] = b43_phy_read(dev, + B43_NPHY_RFCTL_LUT_TRSW_UP1); + save_regs_phy[1] = b43_phy_read(dev, + B43_NPHY_RFCTL_LUT_TRSW_UP2); + save_regs_phy[2] = b43_phy_read(dev, B43_NPHY_AFECTL_C1); + save_regs_phy[3] = b43_phy_read(dev, B43_NPHY_AFECTL_C2); + save_regs_phy[4] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER1); + save_regs_phy[5] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); + save_regs_phy[6] = b43_phy_read(dev, B43_NPHY_TXF_40CO_B1S0); + save_regs_phy[7] = b43_phy_read(dev, B43_NPHY_TXF_40CO_B32S1); + } + + b43_nphy_rssi_select(dev, 5, type); + + if (dev->phy.rev < 2) { + save_regs_phy[8] = b43_phy_read(dev, B43_NPHY_GPIO_SEL); + b43_phy_write(dev, B43_NPHY_GPIO_SEL, 5); + } + + for (i = 0; i < 4; i++) + buf[i] = 0; + + for (i = 0; i < nsamp; i++) { + if (dev->phy.rev < 2) { + s[0] = b43_phy_read(dev, B43_NPHY_GPIO_LOOUT); + s[1] = b43_phy_read(dev, B43_NPHY_GPIO_HIOUT); + } else { + s[0] = b43_phy_read(dev, B43_NPHY_RSSI1); + s[1] = b43_phy_read(dev, B43_NPHY_RSSI2); + } + + buf[0] += ((s8)((s[0] & 0x3F) << 2)) >> 2; + buf[1] += ((s8)(((s[0] >> 8) & 0x3F) << 2)) >> 2; + buf[2] += ((s8)((s[1] & 0x3F) << 2)) >> 2; + buf[3] += ((s8)(((s[1] >> 8) & 0x3F) << 2)) >> 2; + } + out = (buf[0] & 0xFF) << 24 | (buf[1] & 0xFF) << 16 | + (buf[2] & 0xFF) << 8 | (buf[3] & 0xFF); + + if (dev->phy.rev < 2) + b43_phy_write(dev, B43_NPHY_GPIO_SEL, save_regs_phy[8]); + + if (dev->phy.rev >= 3) { + b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, + save_regs_phy[0]); + b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, + save_regs_phy[1]); + b43_phy_write(dev, B43_NPHY_AFECTL_C1, save_regs_phy[2]); + b43_phy_write(dev, B43_NPHY_AFECTL_C2, save_regs_phy[3]); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, save_regs_phy[4]); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER, save_regs_phy[5]); + b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S0, save_regs_phy[6]); + b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S1, save_regs_phy[7]); + } + + return out; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICal */ +static void b43_nphy_rev2_rssi_cal(struct b43_wldev *dev, u8 type) +{ + int i, j; + u8 state[4]; + u8 code, val; + u16 class, override; + u8 regs_save_radio[2]; + u16 regs_save_phy[2]; + s8 offset[4]; + + u16 clip_state[2]; + u16 clip_off[2] = { 0xFFFF, 0xFFFF }; + s32 results_min[4] = { }; + u8 vcm_final[4] = { }; + s32 results[4][4] = { }; + s32 miniq[4][2] = { }; + + if (type == 2) { + code = 0; + val = 6; + } else if (type < 2) { + code = 25; + val = 4; + } else { + B43_WARN_ON(1); + return; + } + + class = b43_nphy_classifier(dev, 0, 0); + b43_nphy_classifier(dev, 7, 4); + b43_nphy_read_clip_detection(dev, clip_state); + b43_nphy_write_clip_detection(dev, clip_off); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) + override = 0x140; + else + override = 0x110; + + regs_save_phy[0] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); + regs_save_radio[0] = b43_radio_read16(dev, B2055_C1_PD_RXTX); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, override); + b43_radio_write16(dev, B2055_C1_PD_RXTX, val); + + regs_save_phy[1] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); + regs_save_radio[1] = b43_radio_read16(dev, B2055_C2_PD_RXTX); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, override); + b43_radio_write16(dev, B2055_C2_PD_RXTX, val); + + state[0] = b43_radio_read16(dev, B2055_C1_PD_RSSIMISC) & 0x07; + state[1] = b43_radio_read16(dev, B2055_C2_PD_RSSIMISC) & 0x07; + b43_radio_mask(dev, B2055_C1_PD_RSSIMISC, 0xF8); + b43_radio_mask(dev, B2055_C2_PD_RSSIMISC, 0xF8); + state[2] = b43_radio_read16(dev, B2055_C1_SP_RSSI) & 0x07; + state[3] = b43_radio_read16(dev, B2055_C2_SP_RSSI) & 0x07; + + b43_nphy_rssi_select(dev, 5, type); + b43_nphy_scale_offset_rssi(dev, 0, 0, 5, 0, type); + b43_nphy_scale_offset_rssi(dev, 0, 0, 5, 1, type); + + for (i = 0; i < 4; i++) { + u8 tmp[4]; + for (j = 0; j < 4; j++) + tmp[j] = i; + if (type != 1) + b43_nphy_set_rssi_2055_vcm(dev, type, tmp); + b43_nphy_poll_rssi(dev, type, results[i], 8); + if (type < 2) + for (j = 0; j < 2; j++) + miniq[i][j] = min(results[i][2 * j], + results[i][2 * j + 1]); + } + + for (i = 0; i < 4; i++) { + s32 mind = 40; + u8 minvcm = 0; + s32 minpoll = 249; + s32 curr; + for (j = 0; j < 4; j++) { + if (type == 2) + curr = abs(results[j][i]); + else + curr = abs(miniq[j][i / 2] - code * 8); + + if (curr < mind) { + mind = curr; + minvcm = j; + } + + if (results[j][i] < minpoll) + minpoll = results[j][i]; + } + results_min[i] = minpoll; + vcm_final[i] = minvcm; + } + + if (type != 1) + b43_nphy_set_rssi_2055_vcm(dev, type, vcm_final); + + for (i = 0; i < 4; i++) { + offset[i] = (code * 8) - results[vcm_final[i]][i]; + + if (offset[i] < 0) + offset[i] = -((abs(offset[i]) + 4) / 8); + else + offset[i] = (offset[i] + 4) / 8; + + if (results_min[i] == 248) + offset[i] = code - 32; + + if (i % 2 == 0) + b43_nphy_scale_offset_rssi(dev, 0, offset[i], 1, 0, + type); + else + b43_nphy_scale_offset_rssi(dev, 0, offset[i], 2, 1, + type); + } + + b43_radio_maskset(dev, B2055_C1_PD_RSSIMISC, 0xF8, state[0]); + b43_radio_maskset(dev, B2055_C1_PD_RSSIMISC, 0xF8, state[1]); + + switch (state[2]) { + case 1: + b43_nphy_rssi_select(dev, 1, 2); + break; + case 4: + b43_nphy_rssi_select(dev, 1, 0); + break; + case 2: + b43_nphy_rssi_select(dev, 1, 1); + break; + default: + b43_nphy_rssi_select(dev, 1, 1); + break; + } + + switch (state[3]) { + case 1: + b43_nphy_rssi_select(dev, 2, 2); + break; + case 4: + b43_nphy_rssi_select(dev, 2, 0); + break; + default: + b43_nphy_rssi_select(dev, 2, 1); + break; + } + + b43_nphy_rssi_select(dev, 0, type); + + b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, regs_save_phy[0]); + b43_radio_write16(dev, B2055_C1_PD_RXTX, regs_save_radio[0]); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, regs_save_phy[1]); + b43_radio_write16(dev, B2055_C2_PD_RXTX, regs_save_radio[1]); + + b43_nphy_classifier(dev, 7, class); + b43_nphy_write_clip_detection(dev, clip_state); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICalRev3 */ +static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev) +{ + /* TODO */ +} + +/* + * RSSI Calibration + * http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICal + */ +static void b43_nphy_rssi_cal(struct b43_wldev *dev) +{ + if (dev->phy.rev >= 3) { + b43_nphy_rev3_rssi_cal(dev); + } else { + b43_nphy_rev2_rssi_cal(dev, 2); + b43_nphy_rev2_rssi_cal(dev, 0); + b43_nphy_rev2_rssi_cal(dev, 1); + } } +/* + * Restore RSSI Calibration + * http://bcm-v4.sipsolutions.net/802.11/PHY/N/RestoreRssiCal + */ +static void b43_nphy_restore_rssi_cal(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + + u16 *rssical_radio_regs = NULL; + u16 *rssical_phy_regs = NULL; + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + if (!nphy->rssical_chanspec_2G) + return; + rssical_radio_regs = nphy->rssical_cache.rssical_radio_regs_2G; + rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_2G; + } else { + if (!nphy->rssical_chanspec_5G) + return; + rssical_radio_regs = nphy->rssical_cache.rssical_radio_regs_5G; + rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_5G; + } + + /* TODO use some definitions */ + b43_radio_maskset(dev, 0x602B, 0xE3, rssical_radio_regs[0]); + b43_radio_maskset(dev, 0x702B, 0xE3, rssical_radio_regs[1]); + + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Z, rssical_phy_regs[0]); + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Z, rssical_phy_regs[1]); + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Z, rssical_phy_regs[2]); + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Z, rssical_phy_regs[3]); + + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_X, rssical_phy_regs[4]); + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_X, rssical_phy_regs[5]); + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_X, rssical_phy_regs[6]); + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_X, rssical_phy_regs[7]); + + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Y, rssical_phy_regs[8]); + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Y, rssical_phy_regs[9]); + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Y, rssical_phy_regs[10]); + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Y, rssical_phy_regs[11]); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GetIpaGainTbl */ +static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev) +{ + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + if (dev->phy.rev >= 6) { + /* TODO If the chip is 47162 + return txpwrctrl_tx_gain_ipa_rev5 */ + return txpwrctrl_tx_gain_ipa_rev6; + } else if (dev->phy.rev >= 5) { + return txpwrctrl_tx_gain_ipa_rev5; + } else { + return txpwrctrl_tx_gain_ipa; + } + } else { + return txpwrctrl_tx_gain_ipa_5g; + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxCalRadioSetup */ +static void b43_nphy_tx_cal_radio_setup(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + u16 *save = nphy->tx_rx_cal_radio_saveregs; + u16 tmp; + u8 offset, i; + + if (dev->phy.rev >= 3) { + for (i = 0; i < 2; i++) { + tmp = (i == 0) ? 0x2000 : 0x3000; + offset = i * 11; + + save[offset + 0] = b43_radio_read16(dev, B2055_CAL_RVARCTL); + save[offset + 1] = b43_radio_read16(dev, B2055_CAL_LPOCTL); + save[offset + 2] = b43_radio_read16(dev, B2055_CAL_TS); + save[offset + 3] = b43_radio_read16(dev, B2055_CAL_RCCALRTS); + save[offset + 4] = b43_radio_read16(dev, B2055_CAL_RCALRTS); + save[offset + 5] = b43_radio_read16(dev, B2055_PADDRV); + save[offset + 6] = b43_radio_read16(dev, B2055_XOCTL1); + save[offset + 7] = b43_radio_read16(dev, B2055_XOCTL2); + save[offset + 8] = b43_radio_read16(dev, B2055_XOREGUL); + save[offset + 9] = b43_radio_read16(dev, B2055_XOMISC); + save[offset + 10] = b43_radio_read16(dev, B2055_PLL_LFC1); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + b43_radio_write16(dev, tmp | B2055_CAL_RVARCTL, 0x0A); + b43_radio_write16(dev, tmp | B2055_CAL_LPOCTL, 0x40); + b43_radio_write16(dev, tmp | B2055_CAL_TS, 0x55); + b43_radio_write16(dev, tmp | B2055_CAL_RCCALRTS, 0); + b43_radio_write16(dev, tmp | B2055_CAL_RCALRTS, 0); + if (nphy->ipa5g_on) { + b43_radio_write16(dev, tmp | B2055_PADDRV, 4); + b43_radio_write16(dev, tmp | B2055_XOCTL1, 1); + } else { + b43_radio_write16(dev, tmp | B2055_PADDRV, 0); + b43_radio_write16(dev, tmp | B2055_XOCTL1, 0x2F); + } + b43_radio_write16(dev, tmp | B2055_XOCTL2, 0); + } else { + b43_radio_write16(dev, tmp | B2055_CAL_RVARCTL, 0x06); + b43_radio_write16(dev, tmp | B2055_CAL_LPOCTL, 0x40); + b43_radio_write16(dev, tmp | B2055_CAL_TS, 0x55); + b43_radio_write16(dev, tmp | B2055_CAL_RCCALRTS, 0); + b43_radio_write16(dev, tmp | B2055_CAL_RCALRTS, 0); + b43_radio_write16(dev, tmp | B2055_XOCTL1, 0); + if (nphy->ipa2g_on) { + b43_radio_write16(dev, tmp | B2055_PADDRV, 6); + b43_radio_write16(dev, tmp | B2055_XOCTL2, + (dev->phy.rev < 5) ? 0x11 : 0x01); + } else { + b43_radio_write16(dev, tmp | B2055_PADDRV, 0); + b43_radio_write16(dev, tmp | B2055_XOCTL2, 0); + } + } + b43_radio_write16(dev, tmp | B2055_XOREGUL, 0); + b43_radio_write16(dev, tmp | B2055_XOMISC, 0); + b43_radio_write16(dev, tmp | B2055_PLL_LFC1, 0); + } + } else { + save[0] = b43_radio_read16(dev, B2055_C1_TX_RF_IQCAL1); + b43_radio_write16(dev, B2055_C1_TX_RF_IQCAL1, 0x29); + + save[1] = b43_radio_read16(dev, B2055_C1_TX_RF_IQCAL2); + b43_radio_write16(dev, B2055_C1_TX_RF_IQCAL2, 0x54); + + save[2] = b43_radio_read16(dev, B2055_C2_TX_RF_IQCAL1); + b43_radio_write16(dev, B2055_C2_TX_RF_IQCAL1, 0x29); + + save[3] = b43_radio_read16(dev, B2055_C2_TX_RF_IQCAL2); + b43_radio_write16(dev, B2055_C2_TX_RF_IQCAL2, 0x54); + + save[3] = b43_radio_read16(dev, B2055_C1_PWRDET_RXTX); + save[4] = b43_radio_read16(dev, B2055_C2_PWRDET_RXTX); + + if (!(b43_phy_read(dev, B43_NPHY_BANDCTL) & + B43_NPHY_BANDCTL_5GHZ)) { + b43_radio_write16(dev, B2055_C1_PWRDET_RXTX, 0x04); + b43_radio_write16(dev, B2055_C2_PWRDET_RXTX, 0x04); + } else { + b43_radio_write16(dev, B2055_C1_PWRDET_RXTX, 0x20); + b43_radio_write16(dev, B2055_C2_PWRDET_RXTX, 0x20); + } + + if (dev->phy.rev < 2) { + b43_radio_set(dev, B2055_C1_TX_BB_MXGM, 0x20); + b43_radio_set(dev, B2055_C2_TX_BB_MXGM, 0x20); + } else { + b43_radio_mask(dev, B2055_C1_TX_BB_MXGM, ~0x20); + b43_radio_mask(dev, B2055_C2_TX_BB_MXGM, ~0x20); + } + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/IqCalGainParams */ +static void b43_nphy_iq_cal_gain_params(struct b43_wldev *dev, u16 core, + struct nphy_txgains target, + struct nphy_iqcal_params *params) +{ + int i, j, indx; + u16 gain; + + if (dev->phy.rev >= 3) { + params->txgm = target.txgm[core]; + params->pga = target.pga[core]; + params->pad = target.pad[core]; + params->ipa = target.ipa[core]; + params->cal_gain = (params->txgm << 12) | (params->pga << 8) | + (params->pad << 4) | (params->ipa); + for (j = 0; j < 5; j++) + params->ncorr[j] = 0x79; + } else { + gain = (target.pad[core]) | (target.pga[core] << 4) | + (target.txgm[core] << 8); + + indx = (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) ? + 1 : 0; + for (i = 0; i < 9; i++) + if (tbl_iqcal_gainparams[indx][i][0] == gain) + break; + i = min(i, 8); + + params->txgm = tbl_iqcal_gainparams[indx][i][1]; + params->pga = tbl_iqcal_gainparams[indx][i][2]; + params->pad = tbl_iqcal_gainparams[indx][i][3]; + params->cal_gain = (params->txgm << 7) | (params->pga << 4) | + (params->pad << 2); + for (j = 0; j < 4; j++) + params->ncorr[j] = tbl_iqcal_gainparams[indx][i][4 + j]; + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/UpdateTxCalLadder */ +static void b43_nphy_update_tx_cal_ladder(struct b43_wldev *dev, u16 core) +{ + struct b43_phy_n *nphy = dev->phy.n; + int i; + u16 scale, entry; + + u16 tmp = nphy->txcal_bbmult; + if (core == 0) + tmp >>= 8; + tmp &= 0xff; + + for (i = 0; i < 18; i++) { + scale = (ladder_lo[i].percent * tmp) / 100; + entry = ((scale & 0xFF) << 8) | ladder_lo[i].g_env; + b43_ntab_write(dev, B43_NTAB16(15, i), entry); + + scale = (ladder_iq[i].percent * tmp) / 100; + entry = ((scale & 0xFF) << 8) | ladder_iq[i].g_env; + b43_ntab_write(dev, B43_NTAB16(15, i + 32), entry); + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ExtPaSetTxDigiFilts */ +static void b43_nphy_ext_pa_set_tx_dig_filters(struct b43_wldev *dev) +{ + int i; + for (i = 0; i < 15; i++) + b43_phy_write(dev, B43_PHY_N(0x2C5 + i), + tbl_tx_filter_coef_rev4[2][i]); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/IpaSetTxDigiFilts */ +static void b43_nphy_int_pa_set_tx_dig_filters(struct b43_wldev *dev) +{ + int i, j; + /* B43_NPHY_TXF_20CO_S0A1, B43_NPHY_TXF_40CO_S0A1, unknown */ + u16 offset[] = { 0x186, 0x195, 0x2C5 }; + + for (i = 0; i < 3; i++) + for (j = 0; j < 15; j++) + b43_phy_write(dev, B43_PHY_N(offset[i] + j), + tbl_tx_filter_coef_rev4[i][j]); + + if (dev->phy.is_40mhz) { + for (j = 0; j < 15; j++) + b43_phy_write(dev, B43_PHY_N(offset[0] + j), + tbl_tx_filter_coef_rev4[3][j]); + } else if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + for (j = 0; j < 15; j++) + b43_phy_write(dev, B43_PHY_N(offset[0] + j), + tbl_tx_filter_coef_rev4[5][j]); + } + + if (dev->phy.channel == 14) + for (j = 0; j < 15; j++) + b43_phy_write(dev, B43_PHY_N(offset[0] + j), + tbl_tx_filter_coef_rev4[6][j]); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GetTxGain */ +static struct nphy_txgains b43_nphy_get_tx_gains(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + + u16 curr_gain[2]; + struct nphy_txgains target; + const u32 *table = NULL; + + if (nphy->txpwrctrl == 0) { + int i; + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, true); + b43_ntab_read_bulk(dev, B43_NTAB16(7, 0x110), 2, curr_gain); + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, false); + + for (i = 0; i < 2; ++i) { + if (dev->phy.rev >= 3) { + target.ipa[i] = curr_gain[i] & 0x000F; + target.pad[i] = (curr_gain[i] & 0x00F0) >> 4; + target.pga[i] = (curr_gain[i] & 0x0F00) >> 8; + target.txgm[i] = (curr_gain[i] & 0x7000) >> 12; + } else { + target.ipa[i] = curr_gain[i] & 0x0003; + target.pad[i] = (curr_gain[i] & 0x000C) >> 2; + target.pga[i] = (curr_gain[i] & 0x0070) >> 4; + target.txgm[i] = (curr_gain[i] & 0x0380) >> 7; + } + } + } else { + int i; + u16 index[2]; + index[0] = (b43_phy_read(dev, B43_NPHY_C1_TXPCTL_STAT) & + B43_NPHY_TXPCTL_STAT_BIDX) >> + B43_NPHY_TXPCTL_STAT_BIDX_SHIFT; + index[1] = (b43_phy_read(dev, B43_NPHY_C2_TXPCTL_STAT) & + B43_NPHY_TXPCTL_STAT_BIDX) >> + B43_NPHY_TXPCTL_STAT_BIDX_SHIFT; + + for (i = 0; i < 2; ++i) { + if (dev->phy.rev >= 3) { + enum ieee80211_band band = + b43_current_band(dev->wl); + + if ((nphy->ipa2g_on && + band == IEEE80211_BAND_2GHZ) || + (nphy->ipa5g_on && + band == IEEE80211_BAND_5GHZ)) { + table = b43_nphy_get_ipa_gain_table(dev); + } else { + if (band == IEEE80211_BAND_5GHZ) { + if (dev->phy.rev == 3) + table = b43_ntab_tx_gain_rev3_5ghz; + else if (dev->phy.rev == 4) + table = b43_ntab_tx_gain_rev4_5ghz; + else + table = b43_ntab_tx_gain_rev5plus_5ghz; + } else { + table = b43_ntab_tx_gain_rev3plus_2ghz; + } + } + + target.ipa[i] = (table[index[i]] >> 16) & 0xF; + target.pad[i] = (table[index[i]] >> 20) & 0xF; + target.pga[i] = (table[index[i]] >> 24) & 0xF; + target.txgm[i] = (table[index[i]] >> 28) & 0xF; + } else { + table = b43_ntab_tx_gain_rev0_1_2; + + target.ipa[i] = (table[index[i]] >> 16) & 0x3; + target.pad[i] = (table[index[i]] >> 18) & 0x3; + target.pga[i] = (table[index[i]] >> 20) & 0x7; + target.txgm[i] = (table[index[i]] >> 23) & 0x7; + } + } + } + + return target; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxCalPhyCleanup */ +static void b43_nphy_tx_cal_phy_cleanup(struct b43_wldev *dev) +{ + u16 *regs = dev->phy.n->tx_rx_cal_phy_saveregs; + + if (dev->phy.rev >= 3) { + b43_phy_write(dev, B43_NPHY_AFECTL_C1, regs[0]); + b43_phy_write(dev, B43_NPHY_AFECTL_C2, regs[1]); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, regs[2]); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER, regs[3]); + b43_phy_write(dev, B43_NPHY_BBCFG, regs[4]); + b43_ntab_write(dev, B43_NTAB16(8, 3), regs[5]); + b43_ntab_write(dev, B43_NTAB16(8, 19), regs[6]); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, regs[7]); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, regs[8]); + b43_phy_write(dev, B43_NPHY_PAPD_EN0, regs[9]); + b43_phy_write(dev, B43_NPHY_PAPD_EN1, regs[10]); + b43_nphy_reset_cca(dev); + } else { + b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0x0FFF, regs[0]); + b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0x0FFF, regs[1]); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER, regs[2]); + b43_ntab_write(dev, B43_NTAB16(8, 2), regs[3]); + b43_ntab_write(dev, B43_NTAB16(8, 18), regs[4]); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, regs[5]); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, regs[6]); + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxCalPhySetup */ +static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev) +{ + u16 *regs = dev->phy.n->tx_rx_cal_phy_saveregs; + u16 tmp; + + regs[0] = b43_phy_read(dev, B43_NPHY_AFECTL_C1); + regs[1] = b43_phy_read(dev, B43_NPHY_AFECTL_C2); + if (dev->phy.rev >= 3) { + b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0xF0FF, 0x0A00); + b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0xF0FF, 0x0A00); + + tmp = b43_phy_read(dev, B43_NPHY_AFECTL_OVER1); + regs[2] = tmp; + b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, tmp | 0x0600); + + tmp = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); + regs[3] = tmp; + b43_phy_write(dev, B43_NPHY_AFECTL_OVER, tmp | 0x0600); + + regs[4] = b43_phy_read(dev, B43_NPHY_BBCFG); + b43_phy_mask(dev, B43_NPHY_BBCFG, (u16)~B43_NPHY_BBCFG_RSTRX); + + tmp = b43_ntab_read(dev, B43_NTAB16(8, 3)); + regs[5] = tmp; + b43_ntab_write(dev, B43_NTAB16(8, 3), 0); + + tmp = b43_ntab_read(dev, B43_NTAB16(8, 19)); + regs[6] = tmp; + b43_ntab_write(dev, B43_NTAB16(8, 19), 0); + regs[7] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); + regs[8] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); + + b43_nphy_rf_control_intc_override(dev, 2, 1, 3); + b43_nphy_rf_control_intc_override(dev, 1, 2, 1); + b43_nphy_rf_control_intc_override(dev, 1, 8, 2); + + regs[9] = b43_phy_read(dev, B43_NPHY_PAPD_EN0); + regs[10] = b43_phy_read(dev, B43_NPHY_PAPD_EN1); + b43_phy_mask(dev, B43_NPHY_PAPD_EN0, ~0x0001); + b43_phy_mask(dev, B43_NPHY_PAPD_EN1, ~0x0001); + } else { + b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0x0FFF, 0xA000); + b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0x0FFF, 0xA000); + tmp = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); + regs[2] = tmp; + b43_phy_write(dev, B43_NPHY_AFECTL_OVER, tmp | 0x3000); + tmp = b43_ntab_read(dev, B43_NTAB16(8, 2)); + regs[3] = tmp; + tmp |= 0x2000; + b43_ntab_write(dev, B43_NTAB16(8, 2), tmp); + tmp = b43_ntab_read(dev, B43_NTAB16(8, 18)); + regs[4] = tmp; + tmp |= 0x2000; + b43_ntab_write(dev, B43_NTAB16(8, 18), tmp); + regs[5] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); + regs[6] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) + tmp = 0x0180; + else + tmp = 0x0120; + b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, tmp); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, tmp); + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SaveCal */ +static void b43_nphy_save_cal(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + + struct b43_phy_n_iq_comp *rxcal_coeffs = NULL; + u16 *txcal_radio_regs = NULL; + u8 *iqcal_chanspec; + u16 *table = NULL; + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 1); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_2G; + txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_2G; + iqcal_chanspec = &nphy->iqcal_chanspec_2G; + table = nphy->cal_cache.txcal_coeffs_2G; + } else { + rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_5G; + txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_5G; + iqcal_chanspec = &nphy->iqcal_chanspec_5G; + table = nphy->cal_cache.txcal_coeffs_5G; + } + + b43_nphy_rx_iq_coeffs(dev, false, rxcal_coeffs); + /* TODO use some definitions */ + if (dev->phy.rev >= 3) { + txcal_radio_regs[0] = b43_radio_read(dev, 0x2021); + txcal_radio_regs[1] = b43_radio_read(dev, 0x2022); + txcal_radio_regs[2] = b43_radio_read(dev, 0x3021); + txcal_radio_regs[3] = b43_radio_read(dev, 0x3022); + txcal_radio_regs[4] = b43_radio_read(dev, 0x2023); + txcal_radio_regs[5] = b43_radio_read(dev, 0x2024); + txcal_radio_regs[6] = b43_radio_read(dev, 0x3023); + txcal_radio_regs[7] = b43_radio_read(dev, 0x3024); + } else { + txcal_radio_regs[0] = b43_radio_read(dev, 0x8B); + txcal_radio_regs[1] = b43_radio_read(dev, 0xBA); + txcal_radio_regs[2] = b43_radio_read(dev, 0x8D); + txcal_radio_regs[3] = b43_radio_read(dev, 0xBC); + } + *iqcal_chanspec = nphy->radio_chanspec; + b43_ntab_write_bulk(dev, B43_NTAB16(15, 80), 8, table); + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 0); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RestoreCal */ +static void b43_nphy_restore_cal(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + + u16 coef[4]; + u16 *loft = NULL; + u16 *table = NULL; + + int i; + u16 *txcal_radio_regs = NULL; + struct b43_phy_n_iq_comp *rxcal_coeffs = NULL; + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + if (nphy->iqcal_chanspec_2G == 0) + return; + table = nphy->cal_cache.txcal_coeffs_2G; + loft = &nphy->cal_cache.txcal_coeffs_2G[5]; + } else { + if (nphy->iqcal_chanspec_5G == 0) + return; + table = nphy->cal_cache.txcal_coeffs_5G; + loft = &nphy->cal_cache.txcal_coeffs_5G[5]; + } + + b43_ntab_write_bulk(dev, B43_NTAB16(15, 80), 4, table); + + for (i = 0; i < 4; i++) { + if (dev->phy.rev >= 3) + table[i] = coef[i]; + else + coef[i] = 0; + } + + b43_ntab_write_bulk(dev, B43_NTAB16(15, 88), 4, coef); + b43_ntab_write_bulk(dev, B43_NTAB16(15, 85), 2, loft); + b43_ntab_write_bulk(dev, B43_NTAB16(15, 93), 2, loft); + + if (dev->phy.rev < 2) + b43_nphy_tx_iq_workaround(dev); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_2G; + rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_2G; + } else { + txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_5G; + rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_5G; + } + + /* TODO use some definitions */ + if (dev->phy.rev >= 3) { + b43_radio_write(dev, 0x2021, txcal_radio_regs[0]); + b43_radio_write(dev, 0x2022, txcal_radio_regs[1]); + b43_radio_write(dev, 0x3021, txcal_radio_regs[2]); + b43_radio_write(dev, 0x3022, txcal_radio_regs[3]); + b43_radio_write(dev, 0x2023, txcal_radio_regs[4]); + b43_radio_write(dev, 0x2024, txcal_radio_regs[5]); + b43_radio_write(dev, 0x3023, txcal_radio_regs[6]); + b43_radio_write(dev, 0x3024, txcal_radio_regs[7]); + } else { + b43_radio_write(dev, 0x8B, txcal_radio_regs[0]); + b43_radio_write(dev, 0xBA, txcal_radio_regs[1]); + b43_radio_write(dev, 0x8D, txcal_radio_regs[2]); + b43_radio_write(dev, 0xBC, txcal_radio_regs[3]); + } + b43_nphy_rx_iq_coeffs(dev, true, rxcal_coeffs); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalTxIqlo */ +static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev, + struct nphy_txgains target, + bool full, bool mphase) +{ + struct b43_phy_n *nphy = dev->phy.n; + int i; + int error = 0; + int freq; + bool avoid = false; + u8 length; + u16 tmp, core, type, count, max, numb, last, cmd; + const u16 *table; + bool phy6or5x; + + u16 buffer[11]; + u16 diq_start = 0; + u16 save[2]; + u16 gain[2]; + struct nphy_iqcal_params params[2]; + bool updated[2] = { }; + + b43_nphy_stay_in_carrier_search(dev, true); + + if (dev->phy.rev >= 4) { + avoid = nphy->hang_avoid; + nphy->hang_avoid = 0; + } + + b43_ntab_read_bulk(dev, B43_NTAB16(7, 0x110), 2, save); + + for (i = 0; i < 2; i++) { + b43_nphy_iq_cal_gain_params(dev, i, target, ¶ms[i]); + gain[i] = params[i].cal_gain; + } + + b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x110), 2, gain); + + b43_nphy_tx_cal_radio_setup(dev); + b43_nphy_tx_cal_phy_setup(dev); + + phy6or5x = dev->phy.rev >= 6 || + (dev->phy.rev == 5 && nphy->ipa2g_on && + b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ); + if (phy6or5x) { + if (dev->phy.is_40mhz) { + b43_ntab_write_bulk(dev, B43_NTAB16(15, 0), 18, + tbl_tx_iqlo_cal_loft_ladder_40); + b43_ntab_write_bulk(dev, B43_NTAB16(15, 32), 18, + tbl_tx_iqlo_cal_iqimb_ladder_40); + } else { + b43_ntab_write_bulk(dev, B43_NTAB16(15, 0), 18, + tbl_tx_iqlo_cal_loft_ladder_20); + b43_ntab_write_bulk(dev, B43_NTAB16(15, 32), 18, + tbl_tx_iqlo_cal_iqimb_ladder_20); + } + } + + b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8AA9); + + if (!dev->phy.is_40mhz) + freq = 2500; + else + freq = 5000; + + if (nphy->mphase_cal_phase_id > 2) + b43_nphy_run_samples(dev, (dev->phy.is_40mhz ? 40 : 20) * 8, + 0xFFFF, 0, true, false); + else + error = b43_nphy_tx_tone(dev, freq, 250, true, false); + + if (error == 0) { + if (nphy->mphase_cal_phase_id > 2) { + table = nphy->mphase_txcal_bestcoeffs; + length = 11; + if (dev->phy.rev < 3) + length -= 2; + } else { + if (!full && nphy->txiqlocal_coeffsvalid) { + table = nphy->txiqlocal_bestc; + length = 11; + if (dev->phy.rev < 3) + length -= 2; + } else { + full = true; + if (dev->phy.rev >= 3) { + table = tbl_tx_iqlo_cal_startcoefs_nphyrev3; + length = B43_NTAB_TX_IQLO_CAL_STARTCOEFS_REV3; + } else { + table = tbl_tx_iqlo_cal_startcoefs; + length = B43_NTAB_TX_IQLO_CAL_STARTCOEFS; + } + } + } + + b43_ntab_write_bulk(dev, B43_NTAB16(15, 64), length, table); + + if (full) { + if (dev->phy.rev >= 3) + max = B43_NTAB_TX_IQLO_CAL_CMDS_FULLCAL_REV3; + else + max = B43_NTAB_TX_IQLO_CAL_CMDS_FULLCAL; + } else { + if (dev->phy.rev >= 3) + max = B43_NTAB_TX_IQLO_CAL_CMDS_RECAL_REV3; + else + max = B43_NTAB_TX_IQLO_CAL_CMDS_RECAL; + } + + if (mphase) { + count = nphy->mphase_txcal_cmdidx; + numb = min(max, + (u16)(count + nphy->mphase_txcal_numcmds)); + } else { + count = 0; + numb = max; + } + + for (; count < numb; count++) { + if (full) { + if (dev->phy.rev >= 3) + cmd = tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3[count]; + else + cmd = tbl_tx_iqlo_cal_cmds_fullcal[count]; + } else { + if (dev->phy.rev >= 3) + cmd = tbl_tx_iqlo_cal_cmds_recal_nphyrev3[count]; + else + cmd = tbl_tx_iqlo_cal_cmds_recal[count]; + } + + core = (cmd & 0x3000) >> 12; + type = (cmd & 0x0F00) >> 8; + + if (phy6or5x && updated[core] == 0) { + b43_nphy_update_tx_cal_ladder(dev, core); + updated[core] = 1; + } + + tmp = (params[core].ncorr[type] << 8) | 0x66; + b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDNNUM, tmp); + + if (type == 1 || type == 3 || type == 4) { + buffer[0] = b43_ntab_read(dev, + B43_NTAB16(15, 69 + core)); + diq_start = buffer[0]; + buffer[0] = 0; + b43_ntab_write(dev, B43_NTAB16(15, 69 + core), + 0); + } + + b43_phy_write(dev, B43_NPHY_IQLOCAL_CMD, cmd); + for (i = 0; i < 2000; i++) { + tmp = b43_phy_read(dev, B43_NPHY_IQLOCAL_CMD); + if (tmp & 0xC000) + break; + udelay(10); + } + + b43_ntab_read_bulk(dev, B43_NTAB16(15, 96), length, + buffer); + b43_ntab_write_bulk(dev, B43_NTAB16(15, 64), length, + buffer); + + if (type == 1 || type == 3 || type == 4) + buffer[0] = diq_start; + } + + if (mphase) + nphy->mphase_txcal_cmdidx = (numb >= max) ? 0 : numb; + + last = (dev->phy.rev < 3) ? 6 : 7; + + if (!mphase || nphy->mphase_cal_phase_id == last) { + b43_ntab_write_bulk(dev, B43_NTAB16(15, 96), 4, buffer); + b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 4, buffer); + if (dev->phy.rev < 3) { + buffer[0] = 0; + buffer[1] = 0; + buffer[2] = 0; + buffer[3] = 0; + } + b43_ntab_write_bulk(dev, B43_NTAB16(15, 88), 4, + buffer); + b43_ntab_write_bulk(dev, B43_NTAB16(15, 101), 2, + buffer); + b43_ntab_write_bulk(dev, B43_NTAB16(15, 85), 2, + buffer); + b43_ntab_write_bulk(dev, B43_NTAB16(15, 93), 2, + buffer); + length = 11; + if (dev->phy.rev < 3) + length -= 2; + b43_ntab_read_bulk(dev, B43_NTAB16(15, 96), length, + nphy->txiqlocal_bestc); + nphy->txiqlocal_coeffsvalid = true; + /* TODO: Set nphy->txiqlocal_chanspec to + the current channel */ + } else { + length = 11; + if (dev->phy.rev < 3) + length -= 2; + b43_ntab_read_bulk(dev, B43_NTAB16(15, 96), length, + nphy->mphase_txcal_bestcoeffs); + } + + b43_nphy_stop_playback(dev); + b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0); + } + + b43_nphy_tx_cal_phy_cleanup(dev); + b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x110), 2, save); + + if (dev->phy.rev < 2 && (!mphase || nphy->mphase_cal_phase_id == last)) + b43_nphy_tx_iq_workaround(dev); + + if (dev->phy.rev >= 4) + nphy->hang_avoid = avoid; + + b43_nphy_stay_in_carrier_search(dev, false); + + return error; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ReapplyTxCalCoeffs */ +static void b43_nphy_reapply_tx_cal_coeffs(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + u8 i; + u16 buffer[7]; + bool equal = true; + + if (!nphy->txiqlocal_coeffsvalid || 1 /* FIXME */) + return; + + b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 7, buffer); + for (i = 0; i < 4; i++) { + if (buffer[i] != nphy->txiqlocal_bestc[i]) { + equal = false; + break; + } + } + + if (!equal) { + b43_ntab_write_bulk(dev, B43_NTAB16(15, 80), 4, + nphy->txiqlocal_bestc); + for (i = 0; i < 4; i++) + buffer[i] = 0; + b43_ntab_write_bulk(dev, B43_NTAB16(15, 88), 4, + buffer); + b43_ntab_write_bulk(dev, B43_NTAB16(15, 85), 2, + &nphy->txiqlocal_bestc[5]); + b43_ntab_write_bulk(dev, B43_NTAB16(15, 93), 2, + &nphy->txiqlocal_bestc[5]); + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalRxIqRev2 */ +static int b43_nphy_rev2_cal_rx_iq(struct b43_wldev *dev, + struct nphy_txgains target, u8 type, bool debug) +{ + struct b43_phy_n *nphy = dev->phy.n; + int i, j, index; + u8 rfctl[2]; + u8 afectl_core; + u16 tmp[6]; + u16 cur_hpf1, cur_hpf2, cur_lna; + u32 real, imag; + enum ieee80211_band band; + + u8 use; + u16 cur_hpf; + u16 lna[3] = { 3, 3, 1 }; + u16 hpf1[3] = { 7, 2, 0 }; + u16 hpf2[3] = { 2, 0, 0 }; + u32 power[3] = { }; + u16 gain_save[2]; + u16 cal_gain[2]; + struct nphy_iqcal_params cal_params[2]; + struct nphy_iq_est est; + int ret = 0; + bool playtone = true; + int desired = 13; + + b43_nphy_stay_in_carrier_search(dev, 1); + + if (dev->phy.rev < 2) + b43_nphy_reapply_tx_cal_coeffs(dev); + b43_ntab_read_bulk(dev, B43_NTAB16(7, 0x110), 2, gain_save); + for (i = 0; i < 2; i++) { + b43_nphy_iq_cal_gain_params(dev, i, target, &cal_params[i]); + cal_gain[i] = cal_params[i].cal_gain; + } + b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x110), 2, cal_gain); + + for (i = 0; i < 2; i++) { + if (i == 0) { + rfctl[0] = B43_NPHY_RFCTL_INTC1; + rfctl[1] = B43_NPHY_RFCTL_INTC2; + afectl_core = B43_NPHY_AFECTL_C1; + } else { + rfctl[0] = B43_NPHY_RFCTL_INTC2; + rfctl[1] = B43_NPHY_RFCTL_INTC1; + afectl_core = B43_NPHY_AFECTL_C2; + } + + tmp[1] = b43_phy_read(dev, B43_NPHY_RFSEQCA); + tmp[2] = b43_phy_read(dev, afectl_core); + tmp[3] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); + tmp[4] = b43_phy_read(dev, rfctl[0]); + tmp[5] = b43_phy_read(dev, rfctl[1]); + + b43_phy_maskset(dev, B43_NPHY_RFSEQCA, + (u16)~B43_NPHY_RFSEQCA_RXDIS, + ((1 - i) << B43_NPHY_RFSEQCA_RXDIS_SHIFT)); + b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_TXEN, + (1 - i)); + b43_phy_set(dev, afectl_core, 0x0006); + b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x0006); + + band = b43_current_band(dev->wl); + + if (nphy->rxcalparams & 0xFF000000) { + if (band == IEEE80211_BAND_5GHZ) + b43_phy_write(dev, rfctl[0], 0x140); + else + b43_phy_write(dev, rfctl[0], 0x110); + } else { + if (band == IEEE80211_BAND_5GHZ) + b43_phy_write(dev, rfctl[0], 0x180); + else + b43_phy_write(dev, rfctl[0], 0x120); + } + + if (band == IEEE80211_BAND_5GHZ) + b43_phy_write(dev, rfctl[1], 0x148); + else + b43_phy_write(dev, rfctl[1], 0x114); + + if (nphy->rxcalparams & 0x10000) { + b43_radio_maskset(dev, B2055_C1_GENSPARE2, 0xFC, + (i + 1)); + b43_radio_maskset(dev, B2055_C2_GENSPARE2, 0xFC, + (2 - i)); + } + + for (j = 0; i < 4; j++) { + if (j < 3) { + cur_lna = lna[j]; + cur_hpf1 = hpf1[j]; + cur_hpf2 = hpf2[j]; + } else { + if (power[1] > 10000) { + use = 1; + cur_hpf = cur_hpf1; + index = 2; + } else { + if (power[0] > 10000) { + use = 1; + cur_hpf = cur_hpf1; + index = 1; + } else { + index = 0; + use = 2; + cur_hpf = cur_hpf2; + } + } + cur_lna = lna[index]; + cur_hpf1 = hpf1[index]; + cur_hpf2 = hpf2[index]; + cur_hpf += desired - hweight32(power[index]); + cur_hpf = clamp_val(cur_hpf, 0, 10); + if (use == 1) + cur_hpf1 = cur_hpf; + else + cur_hpf2 = cur_hpf; + } + + tmp[0] = ((cur_hpf2 << 8) | (cur_hpf1 << 4) | + (cur_lna << 2)); + b43_nphy_rf_control_override(dev, 0x400, tmp[0], 3, + false); + b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); + b43_nphy_stop_playback(dev); + + if (playtone) { + ret = b43_nphy_tx_tone(dev, 4000, + (nphy->rxcalparams & 0xFFFF), + false, false); + playtone = false; + } else { + b43_nphy_run_samples(dev, 160, 0xFFFF, 0, + false, false); + } + + if (ret == 0) { + if (j < 3) { + b43_nphy_rx_iq_est(dev, &est, 1024, 32, + false); + if (i == 0) { + real = est.i0_pwr; + imag = est.q0_pwr; + } else { + real = est.i1_pwr; + imag = est.q1_pwr; + } + power[i] = ((real + imag) / 1024) + 1; + } else { + b43_nphy_calc_rx_iq_comp(dev, 1 << i); + } + b43_nphy_stop_playback(dev); + } + + if (ret != 0) + break; + } + + b43_radio_mask(dev, B2055_C1_GENSPARE2, 0xFC); + b43_radio_mask(dev, B2055_C2_GENSPARE2, 0xFC); + b43_phy_write(dev, rfctl[1], tmp[5]); + b43_phy_write(dev, rfctl[0], tmp[4]); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER, tmp[3]); + b43_phy_write(dev, afectl_core, tmp[2]); + b43_phy_write(dev, B43_NPHY_RFSEQCA, tmp[1]); + + if (ret != 0) + break; + } + + b43_nphy_rf_control_override(dev, 0x400, 0, 3, true); + b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); + b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x110), 2, gain_save); + + b43_nphy_stay_in_carrier_search(dev, 0); + + return ret; +} + +static int b43_nphy_rev3_cal_rx_iq(struct b43_wldev *dev, + struct nphy_txgains target, u8 type, bool debug) +{ + return -1; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalRxIq */ +static int b43_nphy_cal_rx_iq(struct b43_wldev *dev, + struct nphy_txgains target, u8 type, bool debug) +{ + if (dev->phy.rev >= 3) + return b43_nphy_rev3_cal_rx_iq(dev, target, type, debug); + else + return b43_nphy_rev2_cal_rx_iq(dev, target, type, debug); +} + +/* + * Init N-PHY + * http://bcm-v4.sipsolutions.net/802.11/PHY/Init/N + */ int b43_phy_initn(struct b43_wldev *dev) { + struct ssb_bus *bus = dev->dev->bus; struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = phy->n; + u8 tx_pwr_state; + struct nphy_txgains target; u16 tmp; + enum ieee80211_band tmp2; + bool do_rssi_cal; - //TODO: Spectral management + u16 clip[2]; + bool do_cal = false; + + if ((dev->phy.rev >= 3) && + (bus->sprom.boardflags_lo & B43_BFL_EXTLNA) && + (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)) { + chipco_set32(&dev->dev->bus->chipco, SSB_CHIPCO_CHIPCTL, 0x40); + } + nphy->deaf_count = 0; b43_nphy_tables_init(dev); + nphy->crsminpwr_adjusted = false; + nphy->noisevars_adjusted = false; /* Clear all overrides */ - b43_phy_write(dev, B43_NPHY_RFCTL_OVER, 0); + if (dev->phy.rev >= 3) { + b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S1, 0); + b43_phy_write(dev, B43_NPHY_RFCTL_OVER, 0); + b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S0, 0); + b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S1, 0); + } else { + b43_phy_write(dev, B43_NPHY_RFCTL_OVER, 0); + } b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, 0); b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, 0); - b43_phy_write(dev, B43_NPHY_RFCTL_INTC3, 0); - b43_phy_write(dev, B43_NPHY_RFCTL_INTC4, 0); + if (dev->phy.rev < 6) { + b43_phy_write(dev, B43_NPHY_RFCTL_INTC3, 0); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC4, 0); + } b43_phy_mask(dev, B43_NPHY_RFSEQMODE, ~(B43_NPHY_RFSEQMODE_CAOVER | B43_NPHY_RFSEQMODE_TROVER)); + if (dev->phy.rev >= 3) + b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, 0); b43_phy_write(dev, B43_NPHY_AFECTL_OVER, 0); - tmp = (phy->rev < 2) ? 64 : 59; - b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, - ~B43_NPHY_BPHY_CTL3_SCALE, - tmp << B43_NPHY_BPHY_CTL3_SCALE_SHIFT); - + if (dev->phy.rev <= 2) { + tmp = (dev->phy.rev == 2) ? 0x3B : 0x40; + b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, + ~B43_NPHY_BPHY_CTL3_SCALE, + tmp << B43_NPHY_BPHY_CTL3_SCALE_SHIFT); + } b43_phy_write(dev, B43_NPHY_AFESEQ_TX2RX_PUD_20M, 0x20); b43_phy_write(dev, B43_NPHY_AFESEQ_TX2RX_PUD_40M, 0x20); - b43_phy_write(dev, B43_NPHY_TXREALFD, 184); - b43_phy_write(dev, B43_NPHY_MIMO_CRSTXEXT, 200); - b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 80); - b43_phy_write(dev, B43_NPHY_C2_BCLIPBKOFF, 511); + if (bus->sprom.boardflags2_lo & 0x100 || + (bus->boardinfo.vendor == PCI_VENDOR_ID_APPLE && + bus->boardinfo.type == 0x8B)) + b43_phy_write(dev, B43_NPHY_TXREALFD, 0xA0); + else + b43_phy_write(dev, B43_NPHY_TXREALFD, 0xB8); + b43_phy_write(dev, B43_NPHY_MIMO_CRSTXEXT, 0xC8); + b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x50); + b43_phy_write(dev, B43_NPHY_TXRIFS_FRDEL, 0x30); - //TODO MIMO-Config - //TODO Update TX/RX chain + b43_nphy_update_mimo_config(dev, nphy->preamble_override); + b43_nphy_update_txrx_chain(dev); if (phy->rev < 2) { b43_phy_write(dev, B43_NPHY_DUP40_GFBL, 0xAA8); b43_phy_write(dev, B43_NPHY_DUP40_BL, 0x9A4); } + + tmp2 = b43_current_band(dev->wl); + if ((nphy->ipa2g_on && tmp2 == IEEE80211_BAND_2GHZ) || + (nphy->ipa5g_on && tmp2 == IEEE80211_BAND_5GHZ)) { + b43_phy_set(dev, B43_NPHY_PAPD_EN0, 0x1); + b43_phy_maskset(dev, B43_NPHY_EPS_TABLE_ADJ0, 0x007F, + nphy->papd_epsilon_offset[0] << 7); + b43_phy_set(dev, B43_NPHY_PAPD_EN1, 0x1); + b43_phy_maskset(dev, B43_NPHY_EPS_TABLE_ADJ1, 0x007F, + nphy->papd_epsilon_offset[1] << 7); + b43_nphy_int_pa_set_tx_dig_filters(dev); + } else if (phy->rev >= 5) { + b43_nphy_ext_pa_set_tx_dig_filters(dev); + } + b43_nphy_workarounds(dev); - b43_nphy_reset_cca(dev); - ssb_write32(dev->dev, SSB_TMSLOW, - ssb_read32(dev->dev, SSB_TMSLOW) | B43_TMSLOW_MACPHYCLKEN); + /* Reset CCA, in init code it differs a little from standard way */ + b43_nphy_bmac_clock_fgc(dev, 1); + tmp = b43_phy_read(dev, B43_NPHY_BBCFG); + b43_phy_write(dev, B43_NPHY_BBCFG, tmp | B43_NPHY_BBCFG_RSTCCA); + b43_phy_write(dev, B43_NPHY_BBCFG, tmp & ~B43_NPHY_BBCFG_RSTCCA); + b43_nphy_bmac_clock_fgc(dev, 0); + + /* TODO N PHY MAC PHY Clock Set with argument 1 */ + + b43_nphy_pa_override(dev, false); b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RX2TX); b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); + b43_nphy_pa_override(dev, true); + + b43_nphy_classifier(dev, 0, 0); + b43_nphy_read_clip_detection(dev, clip); + tx_pwr_state = nphy->txpwrctrl; + /* TODO N PHY TX power control with argument 0 + (turning off power control) */ + /* TODO Fix the TX Power Settings */ + /* TODO N PHY TX Power Control Idle TSSI */ + /* TODO N PHY TX Power Control Setup */ + + if (phy->rev >= 3) { + /* TODO */ + } else { + b43_ntab_write_bulk(dev, B43_NTAB32(26, 192), 128, + b43_ntab_tx_gain_rev0_1_2); + b43_ntab_write_bulk(dev, B43_NTAB32(27, 192), 128, + b43_ntab_tx_gain_rev0_1_2); + } + + if (nphy->phyrxchain != 3) + ;/* TODO N PHY RX Core Set State with phyrxchain as argument */ + if (nphy->mphase_cal_phase_id > 0) + ;/* TODO PHY Periodic Calibration Multi-Phase Restart */ + + do_rssi_cal = false; + if (phy->rev >= 3) { + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + do_rssi_cal = (nphy->rssical_chanspec_2G == 0); + else + do_rssi_cal = (nphy->rssical_chanspec_5G == 0); + + if (do_rssi_cal) + b43_nphy_rssi_cal(dev); + else + b43_nphy_restore_rssi_cal(dev); + } else { + b43_nphy_rssi_cal(dev); + } + + if (!((nphy->measure_hold & 0x6) != 0)) { + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + do_cal = (nphy->iqcal_chanspec_2G == 0); + else + do_cal = (nphy->iqcal_chanspec_5G == 0); + + if (nphy->mute) + do_cal = false; + + if (do_cal) { + target = b43_nphy_get_tx_gains(dev); + + if (nphy->antsel_type == 2) + ;/*TODO NPHY Superswitch Init with argument 1*/ + if (nphy->perical != 2) { + b43_nphy_rssi_cal(dev); + if (phy->rev >= 3) { + nphy->cal_orig_pwr_idx[0] = + nphy->txpwrindex[0].index_internal; + nphy->cal_orig_pwr_idx[1] = + nphy->txpwrindex[1].index_internal; + /* TODO N PHY Pre Calibrate TX Gain */ + target = b43_nphy_get_tx_gains(dev); + } + } + } + } + + if (!b43_nphy_cal_tx_iq_lo(dev, target, true, false)) { + if (b43_nphy_cal_rx_iq(dev, target, 2, 0) == 0) + b43_nphy_save_cal(dev); + else if (nphy->mphase_cal_phase_id == 0) + ;/* N PHY Periodic Calibration with argument 3 */ + } else { + b43_nphy_restore_cal(dev); + } - b43_phy_read(dev, B43_NPHY_CLASSCTL); /* dummy read */ - //TODO read core1/2 clip1 thres regs - - if (1 /* FIXME Band is 2.4GHz */) - b43_nphy_bphy_init(dev); - //TODO disable TX power control - //TODO Fix the TX power settings - //TODO Init periodic calibration with reason 3 - b43_nphy_rssi_cal(dev, 2); - b43_nphy_rssi_cal(dev, 0); - b43_nphy_rssi_cal(dev, 1); - //TODO get TX gain - //TODO init superswitch - //TODO calibrate LO - //TODO idle TSSI TX pctl - //TODO TX power control power setup - //TODO table writes - //TODO TX power control coefficients - //TODO enable TX power control - //TODO control antenna selection - //TODO init radar detection - //TODO reset channel if changed + b43_nphy_tx_pwr_ctrl_coef_setup(dev); + /* TODO N PHY TX Power Control Enable with argument tx_pwr_state */ + b43_phy_write(dev, B43_NPHY_TXMACIF_HOLDOFF, 0x0015); + b43_phy_write(dev, B43_NPHY_TXMACDELAY, 0x0320); + if (phy->rev >= 3 && phy->rev <= 6) + b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x0014); + b43_nphy_tx_lp_fbw(dev); + if (phy->rev >= 3) + b43_nphy_spur_workaround(dev); b43err(dev->wl, "IEEE 802.11n devices are not supported, yet.\n"); return 0; diff --git a/drivers/net/wireless/b43/phy_n.h b/drivers/net/wireless/b43/phy_n.h index 1749aef4147d..403aad3f894f 100644 --- a/drivers/net/wireless/b43/phy_n.h +++ b/drivers/net/wireless/b43/phy_n.h @@ -231,6 +231,7 @@ #define B43_NPHY_C2_TXIQ_COMP_OFF B43_PHY_N(0x088) /* Core 2 TX I/Q comp offset */ #define B43_NPHY_C1_TXCTL B43_PHY_N(0x08B) /* Core 1 TX control */ #define B43_NPHY_C2_TXCTL B43_PHY_N(0x08C) /* Core 2 TX control */ +#define B43_NPHY_AFECTL_OVER1 B43_PHY_N(0x08F) /* AFE control override 1 */ #define B43_NPHY_SCRAM_SIGCTL B43_PHY_N(0x090) /* Scram signal control */ #define B43_NPHY_SCRAM_SIGCTL_INITST 0x007F /* Initial state value */ #define B43_NPHY_SCRAM_SIGCTL_INITST_SHIFT 0 @@ -705,6 +706,10 @@ #define B43_NPHY_TXPCTL_INIT B43_PHY_N(0x222) /* TX power controll init */ #define B43_NPHY_TXPCTL_INIT_PIDXI1 0x00FF /* Power index init 1 */ #define B43_NPHY_TXPCTL_INIT_PIDXI1_SHIFT 0 +#define B43_NPHY_PAPD_EN0 B43_PHY_N(0x297) /* PAPD Enable0 TBD */ +#define B43_NPHY_EPS_TABLE_ADJ0 B43_PHY_N(0x298) /* EPS Table Adj0 TBD */ +#define B43_NPHY_PAPD_EN1 B43_PHY_N(0x29B) /* PAPD Enable1 TBD */ +#define B43_NPHY_EPS_TABLE_ADJ1 B43_PHY_N(0x29C) /* EPS Table Adj1 TBD */ @@ -919,8 +924,99 @@ struct b43_wldev; +struct b43_phy_n_iq_comp { + s16 a0; + s16 b0; + s16 a1; + s16 b1; +}; + +struct b43_phy_n_rssical_cache { + u16 rssical_radio_regs_2G[2]; + u16 rssical_phy_regs_2G[12]; + + u16 rssical_radio_regs_5G[2]; + u16 rssical_phy_regs_5G[12]; +}; + +struct b43_phy_n_cal_cache { + u16 txcal_radio_regs_2G[8]; + u16 txcal_coeffs_2G[8]; + struct b43_phy_n_iq_comp rxcal_coeffs_2G; + + u16 txcal_radio_regs_5G[8]; + u16 txcal_coeffs_5G[8]; + struct b43_phy_n_iq_comp rxcal_coeffs_5G; +}; + +struct b43_phy_n_txpwrindex { + s8 index; + s8 index_internal; + s8 index_internal_save; + u16 AfectrlOverride; + u16 AfeCtrlDacGain; + u16 rad_gain; + u8 bbmult; + u16 iqcomp_a; + u16 iqcomp_b; + u16 locomp; +}; + struct b43_phy_n { - //TODO lots of missing stuff + u8 antsel_type; + u8 cal_orig_pwr_idx[2]; + u8 measure_hold; + u8 phyrxchain; + u8 perical; + u32 deaf_count; + u32 rxcalparams; + bool hang_avoid; + bool mute; + u16 papd_epsilon_offset[2]; + s32 preamble_override; + u32 bb_mult_save; + u16 radio_chanspec; + + bool gain_boost; + bool elna_gain_config; + bool band5g_pwrgain; + + u8 mphase_cal_phase_id; + u16 mphase_txcal_cmdidx; + u16 mphase_txcal_numcmds; + u16 mphase_txcal_bestcoeffs[11]; + + u8 txpwrctrl; + u16 txcal_bbmult; + u16 txiqlocal_bestc[11]; + bool txiqlocal_coeffsvalid; + struct b43_phy_n_txpwrindex txpwrindex[2]; + + u8 txrx_chain; + u16 tx_rx_cal_phy_saveregs[11]; + u16 tx_rx_cal_radio_saveregs[22]; + + u16 rfctrl_intc1_save; + u16 rfctrl_intc2_save; + + u16 classifier_state; + u16 clip_state[2]; + + bool aband_spurwar_en; + bool gband_spurwar_en; + + bool ipa2g_on; + u8 iqcal_chanspec_2G; + u8 rssical_chanspec_2G; + + bool ipa5g_on; + u8 iqcal_chanspec_5G; + u8 rssical_chanspec_5G; + + struct b43_phy_n_rssical_cache rssical_cache; + struct b43_phy_n_cal_cache cal_cache; + bool crsminpwr_adjusted; + bool noisevars_adjusted; }; diff --git a/drivers/net/wireless/b43/pio.c b/drivers/net/wireless/b43/pio.c index c01b8e02412f..a6062c3e89a5 100644 --- a/drivers/net/wireless/b43/pio.c +++ b/drivers/net/wireless/b43/pio.c @@ -559,7 +559,6 @@ int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb) b43err(dev->wl, "PIO transmission failure\n"); goto out; } - q->nr_tx_packets++; B43_WARN_ON(q->buffer_used > q->buffer_size); if (((q->buffer_size - q->buffer_used) < roundup(2 + 2 + 6, 4)) || @@ -605,22 +604,6 @@ void b43_pio_handle_txstatus(struct b43_wldev *dev, } } -void b43_pio_get_tx_stats(struct b43_wldev *dev, - struct ieee80211_tx_queue_stats *stats) -{ - const int nr_queues = dev->wl->hw->queues; - struct b43_pio_txqueue *q; - int i; - - for (i = 0; i < nr_queues; i++) { - q = select_queue_by_priority(dev, i); - - stats[i].len = B43_PIO_MAX_NR_TXPACKETS - q->free_packet_slots; - stats[i].limit = B43_PIO_MAX_NR_TXPACKETS; - stats[i].count = q->nr_tx_packets; - } -} - /* Returns whether we should fetch another frame. */ static bool pio_rx_frame(struct b43_pio_rxqueue *q) { diff --git a/drivers/net/wireless/b43/pio.h b/drivers/net/wireless/b43/pio.h index 7dd649c9ddad..1e516147424f 100644 --- a/drivers/net/wireless/b43/pio.h +++ b/drivers/net/wireless/b43/pio.h @@ -55,8 +55,6 @@ #define B43_PIO_MAX_NR_TXPACKETS 32 -#ifdef CONFIG_B43_PIO - struct b43_pio_txpacket { /* Pointer to the TX queue we belong to. */ struct b43_pio_txqueue *queue; @@ -92,9 +90,6 @@ struct b43_pio_txqueue { struct b43_pio_txpacket packets[B43_PIO_MAX_NR_TXPACKETS]; struct list_head packets_list; - /* Total number of transmitted packets. */ - unsigned int nr_tx_packets; - /* Shortcut to the 802.11 core revision. This is to * avoid horrible pointer dereferencing in the fastpaths. */ u8 rev; @@ -162,49 +157,9 @@ void b43_pio_free(struct b43_wldev *dev); int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb); void b43_pio_handle_txstatus(struct b43_wldev *dev, const struct b43_txstatus *status); -void b43_pio_get_tx_stats(struct b43_wldev *dev, - struct ieee80211_tx_queue_stats *stats); void b43_pio_rx(struct b43_pio_rxqueue *q); void b43_pio_tx_suspend(struct b43_wldev *dev); void b43_pio_tx_resume(struct b43_wldev *dev); - -#else /* CONFIG_B43_PIO */ - - -static inline int b43_pio_init(struct b43_wldev *dev) -{ - return 0; -} -static inline void b43_pio_free(struct b43_wldev *dev) -{ -} -static inline void b43_pio_stop(struct b43_wldev *dev) -{ -} -static inline int b43_pio_tx(struct b43_wldev *dev, - struct sk_buff *skb) -{ - return 0; -} -static inline void b43_pio_handle_txstatus(struct b43_wldev *dev, - const struct b43_txstatus *status) -{ -} -static inline void b43_pio_get_tx_stats(struct b43_wldev *dev, - struct ieee80211_tx_queue_stats *stats) -{ -} -static inline void b43_pio_rx(struct b43_pio_rxqueue *q) -{ -} -static inline void b43_pio_tx_suspend(struct b43_wldev *dev) -{ -} -static inline void b43_pio_tx_resume(struct b43_wldev *dev) -{ -} - -#endif /* CONFIG_B43_PIO */ #endif /* B43_PIO_H_ */ diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/b43/tables_nphy.c index 4e2336315545..a00d509150f7 100644 --- a/drivers/net/wireless/b43/tables_nphy.c +++ b/drivers/net/wireless/b43/tables_nphy.c @@ -1336,7 +1336,7 @@ b43_nphy_get_chantabent(struct b43_wldev *dev, u8 channel) } -const u8 b43_ntab_adjustpower0[] = { +static const u8 b43_ntab_adjustpower0[] = { 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, @@ -1355,7 +1355,7 @@ const u8 b43_ntab_adjustpower0[] = { 0x1E, 0x1E, 0x1E, 0x1E, 0x1F, 0x1F, 0x1F, 0x1F, }; -const u8 b43_ntab_adjustpower1[] = { +static const u8 b43_ntab_adjustpower1[] = { 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, @@ -1374,11 +1374,11 @@ const u8 b43_ntab_adjustpower1[] = { 0x1E, 0x1E, 0x1E, 0x1E, 0x1F, 0x1F, 0x1F, 0x1F, }; -const u16 b43_ntab_bdi[] = { +static const u16 b43_ntab_bdi[] = { 0x0070, 0x0126, 0x012C, 0x0246, 0x048D, 0x04D2, }; -const u32 b43_ntab_channelest[] = { +static const u32 b43_ntab_channelest[] = { 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x10101010, 0x10101010, 0x10101010, 0x10101010, @@ -1405,7 +1405,7 @@ const u32 b43_ntab_channelest[] = { 0x10101010, 0x10101010, 0x10101010, 0x10101010, }; -const u8 b43_ntab_estimatepowerlt0[] = { +static const u8 b43_ntab_estimatepowerlt0[] = { 0x50, 0x4F, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, @@ -1416,7 +1416,7 @@ const u8 b43_ntab_estimatepowerlt0[] = { 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, }; -const u8 b43_ntab_estimatepowerlt1[] = { +static const u8 b43_ntab_estimatepowerlt1[] = { 0x50, 0x4F, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, @@ -1427,14 +1427,14 @@ const u8 b43_ntab_estimatepowerlt1[] = { 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, }; -const u8 b43_ntab_framelookup[] = { +static const u8 b43_ntab_framelookup[] = { 0x02, 0x04, 0x14, 0x14, 0x03, 0x05, 0x16, 0x16, 0x0A, 0x0C, 0x1C, 0x1C, 0x0B, 0x0D, 0x1E, 0x1E, 0x06, 0x08, 0x18, 0x18, 0x07, 0x09, 0x1A, 0x1A, 0x0E, 0x10, 0x20, 0x28, 0x0F, 0x11, 0x22, 0x2A, }; -const u32 b43_ntab_framestruct[] = { +static const u32 b43_ntab_framestruct[] = { 0x08004A04, 0x00100000, 0x01000A05, 0x00100020, 0x09804506, 0x00100030, 0x09804507, 0x00100030, 0x00000000, 0x00000000, 0x00000000, 0x00000000, @@ -1645,7 +1645,7 @@ const u32 b43_ntab_framestruct[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; -const u32 b43_ntab_gainctl0[] = { +static const u32 b43_ntab_gainctl0[] = { 0x007F003F, 0x007E013F, 0x007D023E, 0x007C033E, 0x007B043D, 0x007A053D, 0x0079063C, 0x0078073C, 0x0077083B, 0x0076093B, 0x00750A3A, 0x00740B3A, @@ -1680,7 +1680,7 @@ const u32 b43_ntab_gainctl0[] = { 0x00030C01, 0x00020D01, 0x00010E00, 0x00000F00, }; -const u32 b43_ntab_gainctl1[] = { +static const u32 b43_ntab_gainctl1[] = { 0x007F003F, 0x007E013F, 0x007D023E, 0x007C033E, 0x007B043D, 0x007A053D, 0x0079063C, 0x0078073C, 0x0077083B, 0x0076093B, 0x00750A3A, 0x00740B3A, @@ -1715,12 +1715,12 @@ const u32 b43_ntab_gainctl1[] = { 0x00030C01, 0x00020D01, 0x00010E00, 0x00000F00, }; -const u32 b43_ntab_intlevel[] = { +static const u32 b43_ntab_intlevel[] = { 0x00802070, 0x0671188D, 0x0A60192C, 0x0A300E46, 0x00C1188D, 0x080024D2, 0x00000070, }; -const u32 b43_ntab_iqlt0[] = { +static const u32 b43_ntab_iqlt0[] = { 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, @@ -1755,7 +1755,7 @@ const u32 b43_ntab_iqlt0[] = { 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, }; -const u32 b43_ntab_iqlt1[] = { +static const u32 b43_ntab_iqlt1[] = { 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, @@ -1790,7 +1790,7 @@ const u32 b43_ntab_iqlt1[] = { 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, }; -const u16 b43_ntab_loftlt0[] = { +static const u16 b43_ntab_loftlt0[] = { 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, @@ -1815,7 +1815,7 @@ const u16 b43_ntab_loftlt0[] = { 0x0002, 0x0103, }; -const u16 b43_ntab_loftlt1[] = { +static const u16 b43_ntab_loftlt1[] = { 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, @@ -1840,7 +1840,7 @@ const u16 b43_ntab_loftlt1[] = { 0x0002, 0x0103, }; -const u8 b43_ntab_mcs[] = { +static const u8 b43_ntab_mcs[] = { 0x00, 0x08, 0x0A, 0x10, 0x12, 0x19, 0x1A, 0x1C, 0x40, 0x48, 0x4A, 0x50, 0x52, 0x59, 0x5A, 0x5C, 0x80, 0x88, 0x8A, 0x90, 0x92, 0x99, 0x9A, 0x9C, @@ -1859,7 +1859,7 @@ const u8 b43_ntab_mcs[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; -const u32 b43_ntab_noisevar10[] = { +static const u32 b43_ntab_noisevar10[] = { 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, @@ -1926,7 +1926,7 @@ const u32 b43_ntab_noisevar10[] = { 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, }; -const u32 b43_ntab_noisevar11[] = { +static const u32 b43_ntab_noisevar11[] = { 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, @@ -1993,7 +1993,7 @@ const u32 b43_ntab_noisevar11[] = { 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, }; -const u16 b43_ntab_pilot[] = { +static const u16 b43_ntab_pilot[] = { 0xFF08, 0xFF08, 0xFF08, 0xFF08, 0xFF08, 0xFF08, 0xFF08, 0xFF08, 0x80D5, 0x80D5, 0x80D5, 0x80D5, 0x80D5, 0x80D5, 0x80D5, 0x80D5, 0xFF0A, 0xFF82, @@ -2011,12 +2011,12 @@ const u16 b43_ntab_pilot[] = { 0xF0A0, 0xF028, 0xFFFF, 0xFFFF, }; -const u32 b43_ntab_pilotlt[] = { +static const u32 b43_ntab_pilotlt[] = { 0x76540123, 0x62407351, 0x76543201, 0x76540213, 0x76540123, 0x76430521, }; -const u32 b43_ntab_tdi20a0[] = { +static const u32 b43_ntab_tdi20a0[] = { 0x00091226, 0x000A1429, 0x000B56AD, 0x000C58B0, 0x000D5AB3, 0x000E9CB6, 0x000F9EBA, 0x0000C13D, 0x00020301, 0x00030504, 0x00040708, 0x0005090B, @@ -2033,7 +2033,7 @@ const u32 b43_ntab_tdi20a0[] = { 0x00000000, 0x00000000, 0x00000000, }; -const u32 b43_ntab_tdi20a1[] = { +static const u32 b43_ntab_tdi20a1[] = { 0x00014B26, 0x00028D29, 0x000393AD, 0x00049630, 0x0005D833, 0x0006DA36, 0x00099C3A, 0x000A9E3D, 0x000BC081, 0x000CC284, 0x000DC488, 0x000F068B, @@ -2050,7 +2050,7 @@ const u32 b43_ntab_tdi20a1[] = { 0x00000000, 0x00000000, 0x00000000, }; -const u32 b43_ntab_tdi40a0[] = { +static const u32 b43_ntab_tdi40a0[] = { 0x0011A346, 0x00136CCF, 0x0014F5D9, 0x001641E2, 0x0017CB6B, 0x00195475, 0x001B2383, 0x001CAD0C, 0x001E7616, 0x0000821F, 0x00020BA8, 0x0003D4B2, @@ -2081,7 +2081,7 @@ const u32 b43_ntab_tdi40a0[] = { 0x00000000, 0x00000000, }; -const u32 b43_ntab_tdi40a1[] = { +static const u32 b43_ntab_tdi40a1[] = { 0x001EDB36, 0x000129CA, 0x0002B353, 0x00047CDD, 0x0005C8E6, 0x000791EF, 0x00091BF9, 0x000AAA07, 0x000C3391, 0x000DFD1A, 0x00120923, 0x0013D22D, @@ -2112,7 +2112,7 @@ const u32 b43_ntab_tdi40a1[] = { 0x00000000, 0x00000000, }; -const u32 b43_ntab_tdtrn[] = { +static const u32 b43_ntab_tdtrn[] = { 0x061C061C, 0x0050EE68, 0xF592FE36, 0xFE5212F6, 0x00000C38, 0xFE5212F6, 0xF592FE36, 0x0050EE68, 0x061C061C, 0xEE680050, 0xFE36F592, 0x12F6FE52, @@ -2291,7 +2291,7 @@ const u32 b43_ntab_tdtrn[] = { 0xFA58FC00, 0x0B64FC7E, 0x0800F7B6, 0x00F006BE, }; -const u32 b43_ntab_tmap[] = { +static const u32 b43_ntab_tmap[] = { 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, 0xF1111110, 0x11111111, 0x11F11111, 0x00000111, @@ -2406,6 +2406,544 @@ const u32 b43_ntab_tmap[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; +const u32 b43_ntab_tx_gain_rev0_1_2[] = { + 0x03cc2b44, 0x03cc2b42, 0x03cc2a44, 0x03cc2a42, + 0x03cc2944, 0x03c82b44, 0x03c82b42, 0x03c82a44, + 0x03c82a42, 0x03c82944, 0x03c82942, 0x03c82844, + 0x03c82842, 0x03c42b44, 0x03c42b42, 0x03c42a44, + 0x03c42a42, 0x03c42944, 0x03c42942, 0x03c42844, + 0x03c42842, 0x03c42744, 0x03c42742, 0x03c42644, + 0x03c42642, 0x03c42544, 0x03c42542, 0x03c42444, + 0x03c42442, 0x03c02b44, 0x03c02b42, 0x03c02a44, + 0x03c02a42, 0x03c02944, 0x03c02942, 0x03c02844, + 0x03c02842, 0x03c02744, 0x03c02742, 0x03b02b44, + 0x03b02b42, 0x03b02a44, 0x03b02a42, 0x03b02944, + 0x03b02942, 0x03b02844, 0x03b02842, 0x03b02744, + 0x03b02742, 0x03b02644, 0x03b02642, 0x03b02544, + 0x03b02542, 0x03a02b44, 0x03a02b42, 0x03a02a44, + 0x03a02a42, 0x03a02944, 0x03a02942, 0x03a02844, + 0x03a02842, 0x03a02744, 0x03a02742, 0x03902b44, + 0x03902b42, 0x03902a44, 0x03902a42, 0x03902944, + 0x03902942, 0x03902844, 0x03902842, 0x03902744, + 0x03902742, 0x03902644, 0x03902642, 0x03902544, + 0x03902542, 0x03802b44, 0x03802b42, 0x03802a44, + 0x03802a42, 0x03802944, 0x03802942, 0x03802844, + 0x03802842, 0x03802744, 0x03802742, 0x03802644, + 0x03802642, 0x03802544, 0x03802542, 0x03802444, + 0x03802442, 0x03802344, 0x03802342, 0x03802244, + 0x03802242, 0x03802144, 0x03802142, 0x03802044, + 0x03802042, 0x03801f44, 0x03801f42, 0x03801e44, + 0x03801e42, 0x03801d44, 0x03801d42, 0x03801c44, + 0x03801c42, 0x03801b44, 0x03801b42, 0x03801a44, + 0x03801a42, 0x03801944, 0x03801942, 0x03801844, + 0x03801842, 0x03801744, 0x03801742, 0x03801644, + 0x03801642, 0x03801544, 0x03801542, 0x03801444, + 0x03801442, 0x03801344, 0x03801342, 0x00002b00, +}; + +const u32 b43_ntab_tx_gain_rev3plus_2ghz[] = { + 0x1f410044, 0x1f410042, 0x1f410040, 0x1f41003e, + 0x1f41003c, 0x1f41003b, 0x1f410039, 0x1f410037, + 0x1e410044, 0x1e410042, 0x1e410040, 0x1e41003e, + 0x1e41003c, 0x1e41003b, 0x1e410039, 0x1e410037, + 0x1d410044, 0x1d410042, 0x1d410040, 0x1d41003e, + 0x1d41003c, 0x1d41003b, 0x1d410039, 0x1d410037, + 0x1c410044, 0x1c410042, 0x1c410040, 0x1c41003e, + 0x1c41003c, 0x1c41003b, 0x1c410039, 0x1c410037, + 0x1b410044, 0x1b410042, 0x1b410040, 0x1b41003e, + 0x1b41003c, 0x1b41003b, 0x1b410039, 0x1b410037, + 0x1a410044, 0x1a410042, 0x1a410040, 0x1a41003e, + 0x1a41003c, 0x1a41003b, 0x1a410039, 0x1a410037, + 0x19410044, 0x19410042, 0x19410040, 0x1941003e, + 0x1941003c, 0x1941003b, 0x19410039, 0x19410037, + 0x18410044, 0x18410042, 0x18410040, 0x1841003e, + 0x1841003c, 0x1841003b, 0x18410039, 0x18410037, + 0x17410044, 0x17410042, 0x17410040, 0x1741003e, + 0x1741003c, 0x1741003b, 0x17410039, 0x17410037, + 0x16410044, 0x16410042, 0x16410040, 0x1641003e, + 0x1641003c, 0x1641003b, 0x16410039, 0x16410037, + 0x15410044, 0x15410042, 0x15410040, 0x1541003e, + 0x1541003c, 0x1541003b, 0x15410039, 0x15410037, + 0x14410044, 0x14410042, 0x14410040, 0x1441003e, + 0x1441003c, 0x1441003b, 0x14410039, 0x14410037, + 0x13410044, 0x13410042, 0x13410040, 0x1341003e, + 0x1341003c, 0x1341003b, 0x13410039, 0x13410037, + 0x12410044, 0x12410042, 0x12410040, 0x1241003e, + 0x1241003c, 0x1241003b, 0x12410039, 0x12410037, + 0x11410044, 0x11410042, 0x11410040, 0x1141003e, + 0x1141003c, 0x1141003b, 0x11410039, 0x11410037, + 0x10410044, 0x10410042, 0x10410040, 0x1041003e, + 0x1041003c, 0x1041003b, 0x10410039, 0x10410037, +}; + +const u32 b43_ntab_tx_gain_rev3_5ghz[] = { + 0xcff70044, 0xcff70042, 0xcff70040, 0xcff7003e, + 0xcff7003c, 0xcff7003b, 0xcff70039, 0xcff70037, + 0xcef70044, 0xcef70042, 0xcef70040, 0xcef7003e, + 0xcef7003c, 0xcef7003b, 0xcef70039, 0xcef70037, + 0xcdf70044, 0xcdf70042, 0xcdf70040, 0xcdf7003e, + 0xcdf7003c, 0xcdf7003b, 0xcdf70039, 0xcdf70037, + 0xccf70044, 0xccf70042, 0xccf70040, 0xccf7003e, + 0xccf7003c, 0xccf7003b, 0xccf70039, 0xccf70037, + 0xcbf70044, 0xcbf70042, 0xcbf70040, 0xcbf7003e, + 0xcbf7003c, 0xcbf7003b, 0xcbf70039, 0xcbf70037, + 0xcaf70044, 0xcaf70042, 0xcaf70040, 0xcaf7003e, + 0xcaf7003c, 0xcaf7003b, 0xcaf70039, 0xcaf70037, + 0xc9f70044, 0xc9f70042, 0xc9f70040, 0xc9f7003e, + 0xc9f7003c, 0xc9f7003b, 0xc9f70039, 0xc9f70037, + 0xc8f70044, 0xc8f70042, 0xc8f70040, 0xc8f7003e, + 0xc8f7003c, 0xc8f7003b, 0xc8f70039, 0xc8f70037, + 0xc7f70044, 0xc7f70042, 0xc7f70040, 0xc7f7003e, + 0xc7f7003c, 0xc7f7003b, 0xc7f70039, 0xc7f70037, + 0xc6f70044, 0xc6f70042, 0xc6f70040, 0xc6f7003e, + 0xc6f7003c, 0xc6f7003b, 0xc6f70039, 0xc6f70037, + 0xc5f70044, 0xc5f70042, 0xc5f70040, 0xc5f7003e, + 0xc5f7003c, 0xc5f7003b, 0xc5f70039, 0xc5f70037, + 0xc4f70044, 0xc4f70042, 0xc4f70040, 0xc4f7003e, + 0xc4f7003c, 0xc4f7003b, 0xc4f70039, 0xc4f70037, + 0xc3f70044, 0xc3f70042, 0xc3f70040, 0xc3f7003e, + 0xc3f7003c, 0xc3f7003b, 0xc3f70039, 0xc3f70037, + 0xc2f70044, 0xc2f70042, 0xc2f70040, 0xc2f7003e, + 0xc2f7003c, 0xc2f7003b, 0xc2f70039, 0xc2f70037, + 0xc1f70044, 0xc1f70042, 0xc1f70040, 0xc1f7003e, + 0xc1f7003c, 0xc1f7003b, 0xc1f70039, 0xc1f70037, + 0xc0f70044, 0xc0f70042, 0xc0f70040, 0xc0f7003e, + 0xc0f7003c, 0xc0f7003b, 0xc0f70039, 0xc0f70037, +}; + +const u32 b43_ntab_tx_gain_rev4_5ghz[] = { + 0x2ff20044, 0x2ff20042, 0x2ff20040, 0x2ff2003e, + 0x2ff2003c, 0x2ff2003b, 0x2ff20039, 0x2ff20037, + 0x2ef20044, 0x2ef20042, 0x2ef20040, 0x2ef2003e, + 0x2ef2003c, 0x2ef2003b, 0x2ef20039, 0x2ef20037, + 0x2df20044, 0x2df20042, 0x2df20040, 0x2df2003e, + 0x2df2003c, 0x2df2003b, 0x2df20039, 0x2df20037, + 0x2cf20044, 0x2cf20042, 0x2cf20040, 0x2cf2003e, + 0x2cf2003c, 0x2cf2003b, 0x2cf20039, 0x2cf20037, + 0x2bf20044, 0x2bf20042, 0x2bf20040, 0x2bf2003e, + 0x2bf2003c, 0x2bf2003b, 0x2bf20039, 0x2bf20037, + 0x2af20044, 0x2af20042, 0x2af20040, 0x2af2003e, + 0x2af2003c, 0x2af2003b, 0x2af20039, 0x2af20037, + 0x29f20044, 0x29f20042, 0x29f20040, 0x29f2003e, + 0x29f2003c, 0x29f2003b, 0x29f20039, 0x29f20037, + 0x28f20044, 0x28f20042, 0x28f20040, 0x28f2003e, + 0x28f2003c, 0x28f2003b, 0x28f20039, 0x28f20037, + 0x27f20044, 0x27f20042, 0x27f20040, 0x27f2003e, + 0x27f2003c, 0x27f2003b, 0x27f20039, 0x27f20037, + 0x26f20044, 0x26f20042, 0x26f20040, 0x26f2003e, + 0x26f2003c, 0x26f2003b, 0x26f20039, 0x26f20037, + 0x25f20044, 0x25f20042, 0x25f20040, 0x25f2003e, + 0x25f2003c, 0x25f2003b, 0x25f20039, 0x25f20037, + 0x24f20044, 0x24f20042, 0x24f20040, 0x24f2003e, + 0x24f2003c, 0x24f2003b, 0x24f20039, 0x24f20038, + 0x23f20041, 0x23f20040, 0x23f2003f, 0x23f2003e, + 0x23f2003c, 0x23f2003b, 0x23f20039, 0x23f20037, + 0x22f20044, 0x22f20042, 0x22f20040, 0x22f2003e, + 0x22f2003c, 0x22f2003b, 0x22f20039, 0x22f20037, + 0x21f20044, 0x21f20042, 0x21f20040, 0x21f2003e, + 0x21f2003c, 0x21f2003b, 0x21f20039, 0x21f20037, + 0x20d20043, 0x20d20041, 0x20d2003e, 0x20d2003c, + 0x20d2003a, 0x20d20038, 0x20d20036, 0x20d20034, +}; + +const u32 b43_ntab_tx_gain_rev5plus_5ghz[] = { + 0x0f62004a, 0x0f620048, 0x0f620046, 0x0f620044, + 0x0f620042, 0x0f620040, 0x0f62003e, 0x0f62003c, + 0x0e620044, 0x0e620042, 0x0e620040, 0x0e62003e, + 0x0e62003c, 0x0e62003d, 0x0e62003b, 0x0e62003a, + 0x0d620043, 0x0d620041, 0x0d620040, 0x0d62003e, + 0x0d62003d, 0x0d62003c, 0x0d62003b, 0x0d62003a, + 0x0c620041, 0x0c620040, 0x0c62003f, 0x0c62003e, + 0x0c62003c, 0x0c62003b, 0x0c620039, 0x0c620037, + 0x0b620046, 0x0b620044, 0x0b620042, 0x0b620040, + 0x0b62003e, 0x0b62003c, 0x0b62003b, 0x0b62003a, + 0x0a620041, 0x0a620040, 0x0a62003e, 0x0a62003c, + 0x0a62003b, 0x0a62003a, 0x0a620039, 0x0a620038, + 0x0962003e, 0x0962003d, 0x0962003c, 0x0962003b, + 0x09620039, 0x09620037, 0x09620035, 0x09620033, + 0x08620044, 0x08620042, 0x08620040, 0x0862003e, + 0x0862003c, 0x0862003b, 0x0862003a, 0x08620039, + 0x07620043, 0x07620042, 0x07620040, 0x0762003f, + 0x0762003d, 0x0762003b, 0x0762003a, 0x07620039, + 0x0662003e, 0x0662003d, 0x0662003c, 0x0662003b, + 0x06620039, 0x06620037, 0x06620035, 0x06620033, + 0x05620046, 0x05620044, 0x05620042, 0x05620040, + 0x0562003e, 0x0562003c, 0x0562003b, 0x05620039, + 0x04620044, 0x04620042, 0x04620040, 0x0462003e, + 0x0462003c, 0x0462003b, 0x04620039, 0x04620038, + 0x0362003c, 0x0362003b, 0x0362003a, 0x03620039, + 0x03620038, 0x03620037, 0x03620035, 0x03620033, + 0x0262004c, 0x0262004a, 0x02620048, 0x02620047, + 0x02620046, 0x02620044, 0x02620043, 0x02620042, + 0x0162004a, 0x01620048, 0x01620046, 0x01620044, + 0x01620043, 0x01620042, 0x01620041, 0x01620040, + 0x00620042, 0x00620040, 0x0062003e, 0x0062003c, + 0x0062003b, 0x00620039, 0x00620037, 0x00620035, +}; + +const u32 txpwrctrl_tx_gain_ipa[] = { + 0x5ff7002d, 0x5ff7002b, 0x5ff7002a, 0x5ff70029, + 0x5ff70028, 0x5ff70027, 0x5ff70026, 0x5ff70025, + 0x5ef7002d, 0x5ef7002b, 0x5ef7002a, 0x5ef70029, + 0x5ef70028, 0x5ef70027, 0x5ef70026, 0x5ef70025, + 0x5df7002d, 0x5df7002b, 0x5df7002a, 0x5df70029, + 0x5df70028, 0x5df70027, 0x5df70026, 0x5df70025, + 0x5cf7002d, 0x5cf7002b, 0x5cf7002a, 0x5cf70029, + 0x5cf70028, 0x5cf70027, 0x5cf70026, 0x5cf70025, + 0x5bf7002d, 0x5bf7002b, 0x5bf7002a, 0x5bf70029, + 0x5bf70028, 0x5bf70027, 0x5bf70026, 0x5bf70025, + 0x5af7002d, 0x5af7002b, 0x5af7002a, 0x5af70029, + 0x5af70028, 0x5af70027, 0x5af70026, 0x5af70025, + 0x59f7002d, 0x59f7002b, 0x59f7002a, 0x59f70029, + 0x59f70028, 0x59f70027, 0x59f70026, 0x59f70025, + 0x58f7002d, 0x58f7002b, 0x58f7002a, 0x58f70029, + 0x58f70028, 0x58f70027, 0x58f70026, 0x58f70025, + 0x57f7002d, 0x57f7002b, 0x57f7002a, 0x57f70029, + 0x57f70028, 0x57f70027, 0x57f70026, 0x57f70025, + 0x56f7002d, 0x56f7002b, 0x56f7002a, 0x56f70029, + 0x56f70028, 0x56f70027, 0x56f70026, 0x56f70025, + 0x55f7002d, 0x55f7002b, 0x55f7002a, 0x55f70029, + 0x55f70028, 0x55f70027, 0x55f70026, 0x55f70025, + 0x54f7002d, 0x54f7002b, 0x54f7002a, 0x54f70029, + 0x54f70028, 0x54f70027, 0x54f70026, 0x54f70025, + 0x53f7002d, 0x53f7002b, 0x53f7002a, 0x53f70029, + 0x53f70028, 0x53f70027, 0x53f70026, 0x53f70025, + 0x52f7002d, 0x52f7002b, 0x52f7002a, 0x52f70029, + 0x52f70028, 0x52f70027, 0x52f70026, 0x52f70025, + 0x51f7002d, 0x51f7002b, 0x51f7002a, 0x51f70029, + 0x51f70028, 0x51f70027, 0x51f70026, 0x51f70025, + 0x50f7002d, 0x50f7002b, 0x50f7002a, 0x50f70029, + 0x50f70028, 0x50f70027, 0x50f70026, 0x50f70025, +}; + +const u32 txpwrctrl_tx_gain_ipa_rev5[] = { + 0x1ff7002d, 0x1ff7002b, 0x1ff7002a, 0x1ff70029, + 0x1ff70028, 0x1ff70027, 0x1ff70026, 0x1ff70025, + 0x1ef7002d, 0x1ef7002b, 0x1ef7002a, 0x1ef70029, + 0x1ef70028, 0x1ef70027, 0x1ef70026, 0x1ef70025, + 0x1df7002d, 0x1df7002b, 0x1df7002a, 0x1df70029, + 0x1df70028, 0x1df70027, 0x1df70026, 0x1df70025, + 0x1cf7002d, 0x1cf7002b, 0x1cf7002a, 0x1cf70029, + 0x1cf70028, 0x1cf70027, 0x1cf70026, 0x1cf70025, + 0x1bf7002d, 0x1bf7002b, 0x1bf7002a, 0x1bf70029, + 0x1bf70028, 0x1bf70027, 0x1bf70026, 0x1bf70025, + 0x1af7002d, 0x1af7002b, 0x1af7002a, 0x1af70029, + 0x1af70028, 0x1af70027, 0x1af70026, 0x1af70025, + 0x19f7002d, 0x19f7002b, 0x19f7002a, 0x19f70029, + 0x19f70028, 0x19f70027, 0x19f70026, 0x19f70025, + 0x18f7002d, 0x18f7002b, 0x18f7002a, 0x18f70029, + 0x18f70028, 0x18f70027, 0x18f70026, 0x18f70025, + 0x17f7002d, 0x17f7002b, 0x17f7002a, 0x17f70029, + 0x17f70028, 0x17f70027, 0x17f70026, 0x17f70025, + 0x16f7002d, 0x16f7002b, 0x16f7002a, 0x16f70029, + 0x16f70028, 0x16f70027, 0x16f70026, 0x16f70025, + 0x15f7002d, 0x15f7002b, 0x15f7002a, 0x15f70029, + 0x15f70028, 0x15f70027, 0x15f70026, 0x15f70025, + 0x14f7002d, 0x14f7002b, 0x14f7002a, 0x14f70029, + 0x14f70028, 0x14f70027, 0x14f70026, 0x14f70025, + 0x13f7002d, 0x13f7002b, 0x13f7002a, 0x13f70029, + 0x13f70028, 0x13f70027, 0x13f70026, 0x13f70025, + 0x12f7002d, 0x12f7002b, 0x12f7002a, 0x12f70029, + 0x12f70028, 0x12f70027, 0x12f70026, 0x12f70025, + 0x11f7002d, 0x11f7002b, 0x11f7002a, 0x11f70029, + 0x11f70028, 0x11f70027, 0x11f70026, 0x11f70025, + 0x10f7002d, 0x10f7002b, 0x10f7002a, 0x10f70029, + 0x10f70028, 0x10f70027, 0x10f70026, 0x10f70025, +}; + +const u32 txpwrctrl_tx_gain_ipa_rev6[] = { + 0x0ff7002d, 0x0ff7002b, 0x0ff7002a, 0x0ff70029, + 0x0ff70028, 0x0ff70027, 0x0ff70026, 0x0ff70025, + 0x0ef7002d, 0x0ef7002b, 0x0ef7002a, 0x0ef70029, + 0x0ef70028, 0x0ef70027, 0x0ef70026, 0x0ef70025, + 0x0df7002d, 0x0df7002b, 0x0df7002a, 0x0df70029, + 0x0df70028, 0x0df70027, 0x0df70026, 0x0df70025, + 0x0cf7002d, 0x0cf7002b, 0x0cf7002a, 0x0cf70029, + 0x0cf70028, 0x0cf70027, 0x0cf70026, 0x0cf70025, + 0x0bf7002d, 0x0bf7002b, 0x0bf7002a, 0x0bf70029, + 0x0bf70028, 0x0bf70027, 0x0bf70026, 0x0bf70025, + 0x0af7002d, 0x0af7002b, 0x0af7002a, 0x0af70029, + 0x0af70028, 0x0af70027, 0x0af70026, 0x0af70025, + 0x09f7002d, 0x09f7002b, 0x09f7002a, 0x09f70029, + 0x09f70028, 0x09f70027, 0x09f70026, 0x09f70025, + 0x08f7002d, 0x08f7002b, 0x08f7002a, 0x08f70029, + 0x08f70028, 0x08f70027, 0x08f70026, 0x08f70025, + 0x07f7002d, 0x07f7002b, 0x07f7002a, 0x07f70029, + 0x07f70028, 0x07f70027, 0x07f70026, 0x07f70025, + 0x06f7002d, 0x06f7002b, 0x06f7002a, 0x06f70029, + 0x06f70028, 0x06f70027, 0x06f70026, 0x06f70025, + 0x05f7002d, 0x05f7002b, 0x05f7002a, 0x05f70029, + 0x05f70028, 0x05f70027, 0x05f70026, 0x05f70025, + 0x04f7002d, 0x04f7002b, 0x04f7002a, 0x04f70029, + 0x04f70028, 0x04f70027, 0x04f70026, 0x04f70025, + 0x03f7002d, 0x03f7002b, 0x03f7002a, 0x03f70029, + 0x03f70028, 0x03f70027, 0x03f70026, 0x03f70025, + 0x02f7002d, 0x02f7002b, 0x02f7002a, 0x02f70029, + 0x02f70028, 0x02f70027, 0x02f70026, 0x02f70025, + 0x01f7002d, 0x01f7002b, 0x01f7002a, 0x01f70029, + 0x01f70028, 0x01f70027, 0x01f70026, 0x01f70025, + 0x00f7002d, 0x00f7002b, 0x00f7002a, 0x00f70029, + 0x00f70028, 0x00f70027, 0x00f70026, 0x00f70025, +}; + +const u32 txpwrctrl_tx_gain_ipa_5g[] = { + 0x7ff70035, 0x7ff70033, 0x7ff70032, 0x7ff70031, + 0x7ff7002f, 0x7ff7002e, 0x7ff7002d, 0x7ff7002b, + 0x7ff7002a, 0x7ff70029, 0x7ff70028, 0x7ff70027, + 0x7ff70026, 0x7ff70024, 0x7ff70023, 0x7ff70022, + 0x7ef70028, 0x7ef70027, 0x7ef70026, 0x7ef70025, + 0x7ef70024, 0x7ef70023, 0x7df70028, 0x7df70027, + 0x7df70026, 0x7df70025, 0x7df70024, 0x7df70023, + 0x7df70022, 0x7cf70029, 0x7cf70028, 0x7cf70027, + 0x7cf70026, 0x7cf70025, 0x7cf70023, 0x7cf70022, + 0x7bf70029, 0x7bf70028, 0x7bf70026, 0x7bf70025, + 0x7bf70024, 0x7bf70023, 0x7bf70022, 0x7bf70021, + 0x7af70029, 0x7af70028, 0x7af70027, 0x7af70026, + 0x7af70025, 0x7af70024, 0x7af70023, 0x7af70022, + 0x79f70029, 0x79f70028, 0x79f70027, 0x79f70026, + 0x79f70025, 0x79f70024, 0x79f70023, 0x79f70022, + 0x78f70029, 0x78f70028, 0x78f70027, 0x78f70026, + 0x78f70025, 0x78f70024, 0x78f70023, 0x78f70022, + 0x77f70029, 0x77f70028, 0x77f70027, 0x77f70026, + 0x77f70025, 0x77f70024, 0x77f70023, 0x77f70022, + 0x76f70029, 0x76f70028, 0x76f70027, 0x76f70026, + 0x76f70024, 0x76f70023, 0x76f70022, 0x76f70021, + 0x75f70029, 0x75f70028, 0x75f70027, 0x75f70026, + 0x75f70025, 0x75f70024, 0x75f70023, 0x74f70029, + 0x74f70028, 0x74f70026, 0x74f70025, 0x74f70024, + 0x74f70023, 0x74f70022, 0x73f70029, 0x73f70027, + 0x73f70026, 0x73f70025, 0x73f70024, 0x73f70023, + 0x73f70022, 0x72f70028, 0x72f70027, 0x72f70026, + 0x72f70025, 0x72f70024, 0x72f70023, 0x72f70022, + 0x71f70028, 0x71f70027, 0x71f70026, 0x71f70025, + 0x71f70024, 0x71f70023, 0x70f70028, 0x70f70027, + 0x70f70026, 0x70f70024, 0x70f70023, 0x70f70022, + 0x70f70021, 0x70f70020, 0x70f70020, 0x70f7001f, +}; + +const u16 tbl_iqcal_gainparams[2][9][8] = { + { + { 0x000, 0, 0, 2, 0x69, 0x69, 0x69, 0x69 }, + { 0x700, 7, 0, 0, 0x69, 0x69, 0x69, 0x69 }, + { 0x710, 7, 1, 0, 0x68, 0x68, 0x68, 0x68 }, + { 0x720, 7, 2, 0, 0x67, 0x67, 0x67, 0x67 }, + { 0x730, 7, 3, 0, 0x66, 0x66, 0x66, 0x66 }, + { 0x740, 7, 4, 0, 0x65, 0x65, 0x65, 0x65 }, + { 0x741, 7, 4, 1, 0x65, 0x65, 0x65, 0x65 }, + { 0x742, 7, 4, 2, 0x65, 0x65, 0x65, 0x65 }, + { 0x743, 7, 4, 3, 0x65, 0x65, 0x65, 0x65 } + }, + { + { 0x000, 7, 0, 0, 0x79, 0x79, 0x79, 0x79 }, + { 0x700, 7, 0, 0, 0x79, 0x79, 0x79, 0x79 }, + { 0x710, 7, 1, 0, 0x79, 0x79, 0x79, 0x79 }, + { 0x720, 7, 2, 0, 0x78, 0x78, 0x78, 0x78 }, + { 0x730, 7, 3, 0, 0x78, 0x78, 0x78, 0x78 }, + { 0x740, 7, 4, 0, 0x78, 0x78, 0x78, 0x78 }, + { 0x741, 7, 4, 1, 0x78, 0x78, 0x78, 0x78 }, + { 0x742, 7, 4, 2, 0x78, 0x78, 0x78, 0x78 }, + { 0x743, 7, 4, 3, 0x78, 0x78, 0x78, 0x78 } + } +}; + +const struct nphy_txiqcal_ladder ladder_lo[] = { + { 3, 0 }, + { 4, 0 }, + { 6, 0 }, + { 9, 0 }, + { 13, 0 }, + { 18, 0 }, + { 25, 0 }, + { 25, 1 }, + { 25, 2 }, + { 25, 3 }, + { 25, 4 }, + { 25, 5 }, + { 25, 6 }, + { 25, 7 }, + { 35, 7 }, + { 50, 7 }, + { 71, 7 }, + { 100, 7 } +}; + +const struct nphy_txiqcal_ladder ladder_iq[] = { + { 3, 0 }, + { 4, 0 }, + { 6, 0 }, + { 9, 0 }, + { 13, 0 }, + { 18, 0 }, + { 25, 0 }, + { 35, 0 }, + { 50, 0 }, + { 71, 0 }, + { 100, 0 }, + { 100, 1 }, + { 100, 2 }, + { 100, 3 }, + { 100, 4 }, + { 100, 5 }, + { 100, 6 }, + { 100, 7 } +}; + +const u16 loscale[] = { + 256, 256, 271, 271, + 287, 256, 256, 271, + 271, 287, 287, 304, + 304, 256, 256, 271, + 271, 287, 287, 304, + 304, 322, 322, 341, + 341, 362, 362, 383, + 383, 256, 256, 271, + 271, 287, 287, 304, + 304, 322, 322, 256, + 256, 271, 271, 287, + 287, 304, 304, 322, + 322, 341, 341, 362, + 362, 256, 256, 271, + 271, 287, 287, 304, + 304, 322, 322, 256, + 256, 271, 271, 287, + 287, 304, 304, 322, + 322, 341, 341, 362, + 362, 256, 256, 271, + 271, 287, 287, 304, + 304, 322, 322, 341, + 341, 362, 362, 383, + 383, 406, 406, 430, + 430, 455, 455, 482, + 482, 511, 511, 541, + 541, 573, 573, 607, + 607, 643, 643, 681, + 681, 722, 722, 764, + 764, 810, 810, 858, + 858, 908, 908, 962, + 962, 1019, 1019, 256 +}; + +const u16 tbl_tx_iqlo_cal_loft_ladder_40[] = { + 0x0200, 0x0300, 0x0400, 0x0700, + 0x0900, 0x0c00, 0x1200, 0x1201, + 0x1202, 0x1203, 0x1204, 0x1205, + 0x1206, 0x1207, 0x1907, 0x2307, + 0x3207, 0x4707 +}; + +const u16 tbl_tx_iqlo_cal_loft_ladder_20[] = { + 0x0300, 0x0500, 0x0700, 0x0900, + 0x0d00, 0x1100, 0x1900, 0x1901, + 0x1902, 0x1903, 0x1904, 0x1905, + 0x1906, 0x1907, 0x2407, 0x3207, + 0x4607, 0x6407 +}; + +const u16 tbl_tx_iqlo_cal_iqimb_ladder_40[] = { + 0x0100, 0x0200, 0x0400, 0x0700, + 0x0900, 0x0c00, 0x1200, 0x1900, + 0x2300, 0x3200, 0x4700, 0x4701, + 0x4702, 0x4703, 0x4704, 0x4705, + 0x4706, 0x4707 +}; + +const u16 tbl_tx_iqlo_cal_iqimb_ladder_20[] = { + 0x0200, 0x0300, 0x0600, 0x0900, + 0x0d00, 0x1100, 0x1900, 0x2400, + 0x3200, 0x4600, 0x6400, 0x6401, + 0x6402, 0x6403, 0x6404, 0x6405, + 0x6406, 0x6407 +}; + +const u16 tbl_tx_iqlo_cal_startcoefs_nphyrev3[B43_NTAB_TX_IQLO_CAL_STARTCOEFS_REV3] = { }; + +const u16 tbl_tx_iqlo_cal_startcoefs[B43_NTAB_TX_IQLO_CAL_STARTCOEFS] = { }; + +const u16 tbl_tx_iqlo_cal_cmds_recal_nphyrev3[] = { + 0x8423, 0x8323, 0x8073, 0x8256, + 0x8045, 0x8223, 0x9423, 0x9323, + 0x9073, 0x9256, 0x9045, 0x9223 +}; + +const u16 tbl_tx_iqlo_cal_cmds_recal[] = { + 0x8101, 0x8253, 0x8053, 0x8234, + 0x8034, 0x9101, 0x9253, 0x9053, + 0x9234, 0x9034 +}; + +const u16 tbl_tx_iqlo_cal_cmds_fullcal[] = { + 0x8123, 0x8264, 0x8086, 0x8245, + 0x8056, 0x9123, 0x9264, 0x9086, + 0x9245, 0x9056 +}; + +const u16 tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3[] = { + 0x8434, 0x8334, 0x8084, 0x8267, + 0x8056, 0x8234, 0x9434, 0x9334, + 0x9084, 0x9267, 0x9056, 0x9234 +}; + +const s16 tbl_tx_filter_coef_rev4[7][15] = { + { -377, 137, -407, 208, -1527, + 956, 93, 186, 93, 230, + -44, 230, 20, -191, 201 }, + { -77, 20, -98, 49, -93, + 60, 56, 111, 56, 26, + -5, 26, 34, -32, 34 }, + { -360, 164, -376, 164, -1533, + 576, 308, -314, 308, 121, + -73, 121, 91, 124, 91 }, + { -295, 200, -363, 142, -1391, + 826, 151, 301, 151, 151, + 301, 151, 602, -752, 602 }, + { -92, 58, -96, 49, -104, + 44, 17, 35, 17, 12, + 25, 12, 13, 27, 13 }, + { -375, 136, -399, 209, -1479, + 949, 130, 260, 130, 230, + -44, 230, 201, -191, 201 }, + { 0xed9, 0xc8, 0xe95, 0x8e, 0xa91, + 0x33a, 0x97, 0x12d, 0x97, 0x97, + 0x12d, 0x97, 0x25a, 0xd10, 0x25a } +}; + +/* addr0, addr1, bmask, shift */ +const struct nphy_rf_control_override_rev2 tbl_rf_control_override_rev2[] = { + { 0x78, 0x78, 0x0038, 3 }, /* for field == 0x0002 (fls == 2) */ + { 0x7A, 0x7D, 0x0001, 0 }, /* for field == 0x0004 (fls == 3) */ + { 0x7A, 0x7D, 0x0002, 1 }, /* for field == 0x0008 (fls == 4) */ + { 0x7A, 0x7D, 0x0004, 2 }, /* for field == 0x0010 (fls == 5) */ + { 0x7A, 0x7D, 0x0030, 4 }, /* for field == 0x0020 (fls == 6) */ + { 0x7A, 0x7D, 0x00C0, 6 }, /* for field == 0x0040 (fls == 7) */ + { 0x7A, 0x7D, 0x0100, 8 }, /* for field == 0x0080 (fls == 8) */ + { 0x7A, 0x7D, 0x0200, 9 }, /* for field == 0x0100 (fls == 9) */ + { 0x78, 0x78, 0x0004, 2 }, /* for field == 0x0200 (fls == 10) */ + { 0x7B, 0x7E, 0x01FF, 0 }, /* for field == 0x0400 (fls == 11) */ + { 0x7C, 0x7F, 0x01FF, 0 }, /* for field == 0x0800 (fls == 12) */ + { 0x78, 0x78, 0x0100, 8 }, /* for field == 0x1000 (fls == 13) */ + { 0x78, 0x78, 0x0200, 9 }, /* for field == 0x2000 (fls == 14) */ + { 0x78, 0x78, 0xF000, 12 } /* for field == 0x4000 (fls == 15) */ +}; + +/* val_mask, val_shift, en_addr0, val_addr0, en_addr1, val_addr1 */ +const struct nphy_rf_control_override_rev3 tbl_rf_control_override_rev3[] = { + { 0x8000, 15, 0xE5, 0xF9, 0xE6, 0xFB }, /* field == 0x0001 (fls 1) */ + { 0x0001, 0, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0002 (fls 2) */ + { 0x0002, 1, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0004 (fls 3) */ + { 0x0004, 2, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0008 (fls 4) */ + { 0x0016, 4, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0010 (fls 5) */ + { 0x0020, 5, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0020 (fls 6) */ + { 0x0040, 6, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0040 (fls 7) */ + { 0x0080, 6, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0080 (fls 8) */ + { 0x0100, 7, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0100 (fls 9) */ + { 0x0007, 0, 0xE7, 0xF8, 0xEC, 0xFA }, /* field == 0x0200 (fls 10) */ + { 0x0070, 4, 0xE7, 0xF8, 0xEC, 0xFA }, /* field == 0x0400 (fls 11) */ + { 0xE000, 13, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0800 (fls 12) */ + { 0xFFFF, 0, 0xE7, 0x7B, 0xEC, 0x7E }, /* field == 0x1000 (fls 13) */ + { 0xFFFF, 0, 0xE7, 0x7C, 0xEC, 0x7F }, /* field == 0x2000 (fls 14) */ + { 0x00C0, 6, 0xE7, 0xF9, 0xEC, 0xFB } /* field == 0x4000 (fls 15) */ +}; + static inline void assert_ntab_array_sizes(void) { #undef check @@ -2442,6 +2980,72 @@ static inline void assert_ntab_array_sizes(void) #undef check } +u32 b43_ntab_read(struct b43_wldev *dev, u32 offset) +{ + u32 type, value; + + type = offset & B43_NTAB_TYPEMASK; + offset &= ~B43_NTAB_TYPEMASK; + B43_WARN_ON(offset > 0xFFFF); + + switch (type) { + case B43_NTAB_8BIT: + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); + value = b43_phy_read(dev, B43_NPHY_TABLE_DATALO) & 0xFF; + break; + case B43_NTAB_16BIT: + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); + value = b43_phy_read(dev, B43_NPHY_TABLE_DATALO); + break; + case B43_NTAB_32BIT: + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); + value = b43_phy_read(dev, B43_NPHY_TABLE_DATAHI); + value <<= 16; + value |= b43_phy_read(dev, B43_NPHY_TABLE_DATALO); + break; + default: + B43_WARN_ON(1); + value = 0; + } + + return value; +} + +void b43_ntab_read_bulk(struct b43_wldev *dev, u32 offset, + unsigned int nr_elements, void *_data) +{ + u32 type; + u8 *data = _data; + unsigned int i; + + type = offset & B43_NTAB_TYPEMASK; + offset &= ~B43_NTAB_TYPEMASK; + B43_WARN_ON(offset > 0xFFFF); + + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); + + for (i = 0; i < nr_elements; i++) { + switch (type) { + case B43_NTAB_8BIT: + *data = b43_phy_read(dev, B43_NPHY_TABLE_DATALO) & 0xFF; + data++; + break; + case B43_NTAB_16BIT: + *((u16 *)data) = b43_phy_read(dev, B43_NPHY_TABLE_DATALO); + data += 2; + break; + case B43_NTAB_32BIT: + *((u32 *)data) = b43_phy_read(dev, B43_NPHY_TABLE_DATAHI); + *((u32 *)data) <<= 16; + *((u32 *)data) |= b43_phy_read(dev, B43_NPHY_TABLE_DATALO); + data += 4; + break; + default: + B43_WARN_ON(1); + } + } +} + void b43_ntab_write(struct b43_wldev *dev, u32 offset, u32 value) { u32 type; @@ -2474,3 +3078,91 @@ void b43_ntab_write(struct b43_wldev *dev, u32 offset, u32 value) /* Some compiletime assertions... */ assert_ntab_array_sizes(); } + +void b43_ntab_write_bulk(struct b43_wldev *dev, u32 offset, + unsigned int nr_elements, const void *_data) +{ + u32 type, value; + const u8 *data = _data; + unsigned int i; + + type = offset & B43_NTAB_TYPEMASK; + offset &= ~B43_NTAB_TYPEMASK; + B43_WARN_ON(offset > 0xFFFF); + + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); + + for (i = 0; i < nr_elements; i++) { + switch (type) { + case B43_NTAB_8BIT: + value = *data; + data++; + B43_WARN_ON(value & ~0xFF); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, value); + break; + case B43_NTAB_16BIT: + value = *((u16 *)data); + data += 2; + B43_WARN_ON(value & ~0xFFFF); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, value); + break; + case B43_NTAB_32BIT: + value = *((u32 *)data); + data += 4; + b43_phy_write(dev, B43_NPHY_TABLE_DATAHI, value >> 16); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, + value & 0xFFFF); + break; + default: + B43_WARN_ON(1); + } + } +} + +#define ntab_upload(dev, offset, data) do { \ + unsigned int i; \ + for (i = 0; i < (offset##_SIZE); i++) \ + b43_ntab_write(dev, (offset) + i, (data)[i]); \ + } while (0) + +void b43_nphy_rev0_1_2_tables_init(struct b43_wldev *dev) +{ + /* Static tables */ + ntab_upload(dev, B43_NTAB_FRAMESTRUCT, b43_ntab_framestruct); + ntab_upload(dev, B43_NTAB_FRAMELT, b43_ntab_framelookup); + ntab_upload(dev, B43_NTAB_TMAP, b43_ntab_tmap); + ntab_upload(dev, B43_NTAB_TDTRN, b43_ntab_tdtrn); + ntab_upload(dev, B43_NTAB_INTLEVEL, b43_ntab_intlevel); + ntab_upload(dev, B43_NTAB_PILOT, b43_ntab_pilot); + ntab_upload(dev, B43_NTAB_PILOTLT, b43_ntab_pilotlt); + ntab_upload(dev, B43_NTAB_TDI20A0, b43_ntab_tdi20a0); + ntab_upload(dev, B43_NTAB_TDI20A1, b43_ntab_tdi20a1); + ntab_upload(dev, B43_NTAB_TDI40A0, b43_ntab_tdi40a0); + ntab_upload(dev, B43_NTAB_TDI40A1, b43_ntab_tdi40a1); + ntab_upload(dev, B43_NTAB_BDI, b43_ntab_bdi); + ntab_upload(dev, B43_NTAB_CHANEST, b43_ntab_channelest); + ntab_upload(dev, B43_NTAB_MCS, b43_ntab_mcs); + + /* Volatile tables */ + ntab_upload(dev, B43_NTAB_NOISEVAR10, b43_ntab_noisevar10); + ntab_upload(dev, B43_NTAB_NOISEVAR11, b43_ntab_noisevar11); + ntab_upload(dev, B43_NTAB_C0_ESTPLT, b43_ntab_estimatepowerlt0); + ntab_upload(dev, B43_NTAB_C1_ESTPLT, b43_ntab_estimatepowerlt1); + ntab_upload(dev, B43_NTAB_C0_ADJPLT, b43_ntab_adjustpower0); + ntab_upload(dev, B43_NTAB_C1_ADJPLT, b43_ntab_adjustpower1); + ntab_upload(dev, B43_NTAB_C0_GAINCTL, b43_ntab_gainctl0); + ntab_upload(dev, B43_NTAB_C1_GAINCTL, b43_ntab_gainctl1); + ntab_upload(dev, B43_NTAB_C0_IQLT, b43_ntab_iqlt0); + ntab_upload(dev, B43_NTAB_C1_IQLT, b43_ntab_iqlt1); + ntab_upload(dev, B43_NTAB_C0_LOFEEDTH, b43_ntab_loftlt0); + ntab_upload(dev, B43_NTAB_C1_LOFEEDTH, b43_ntab_loftlt1); +} + +void b43_nphy_rev3plus_tables_init(struct b43_wldev *dev) +{ + /* Static tables */ + /* TODO */ + + /* Volatile tables */ + /* TODO */ +} diff --git a/drivers/net/wireless/b43/tables_nphy.h b/drivers/net/wireless/b43/tables_nphy.h index 4d498b053ec7..9c1c6ecd3672 100644 --- a/drivers/net/wireless/b43/tables_nphy.h +++ b/drivers/net/wireless/b43/tables_nphy.h @@ -46,6 +46,27 @@ struct b43_nphy_channeltab_entry { struct b43_wldev; +struct nphy_txiqcal_ladder { + u8 percent; + u8 g_env; +}; + +struct nphy_rf_control_override_rev2 { + u8 addr0; + u8 addr1; + u16 bmask; + u8 shift; +}; + +struct nphy_rf_control_override_rev3 { + u16 val_mask; + u8 val_shift; + u8 en_addr0; + u8 val_addr0; + u8 en_addr1; + u8 val_addr1; +}; + /* Upload the default register value table. * If "ghz5" is true, we upload the 5Ghz table. Otherwise the 2.4Ghz * table is uploaded. If "ignore_uploadflag" is true, we upload any value @@ -126,34 +147,57 @@ b43_nphy_get_chantabent(struct b43_wldev *dev, u8 channel); #define B43_NTAB_C1_LOFEEDTH B43_NTAB16(0x1B, 0x1C0) /* Local Oscillator Feed Through Lookup Table Core 1 */ #define B43_NTAB_C1_LOFEEDTH_SIZE 128 +#define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_40_SIZE 18 +#define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_20_SIZE 18 +#define B43_NTAB_TX_IQLO_CAL_IQIMB_LADDER_40_SIZE 18 +#define B43_NTAB_TX_IQLO_CAL_IQIMB_LADDER_20_SIZE 18 +#define B43_NTAB_TX_IQLO_CAL_STARTCOEFS_REV3 11 +#define B43_NTAB_TX_IQLO_CAL_STARTCOEFS 9 +#define B43_NTAB_TX_IQLO_CAL_CMDS_RECAL_REV3 12 +#define B43_NTAB_TX_IQLO_CAL_CMDS_RECAL 10 +#define B43_NTAB_TX_IQLO_CAL_CMDS_FULLCAL 10 +#define B43_NTAB_TX_IQLO_CAL_CMDS_FULLCAL_REV3 12 + +u32 b43_ntab_read(struct b43_wldev *dev, u32 offset); +void b43_ntab_read_bulk(struct b43_wldev *dev, u32 offset, + unsigned int nr_elements, void *_data); void b43_ntab_write(struct b43_wldev *dev, u32 offset, u32 value); - -extern const u8 b43_ntab_adjustpower0[]; -extern const u8 b43_ntab_adjustpower1[]; -extern const u16 b43_ntab_bdi[]; -extern const u32 b43_ntab_channelest[]; -extern const u8 b43_ntab_estimatepowerlt0[]; -extern const u8 b43_ntab_estimatepowerlt1[]; -extern const u8 b43_ntab_framelookup[]; -extern const u32 b43_ntab_framestruct[]; -extern const u32 b43_ntab_gainctl0[]; -extern const u32 b43_ntab_gainctl1[]; -extern const u32 b43_ntab_intlevel[]; -extern const u32 b43_ntab_iqlt0[]; -extern const u32 b43_ntab_iqlt1[]; -extern const u16 b43_ntab_loftlt0[]; -extern const u16 b43_ntab_loftlt1[]; -extern const u8 b43_ntab_mcs[]; -extern const u32 b43_ntab_noisevar10[]; -extern const u32 b43_ntab_noisevar11[]; -extern const u16 b43_ntab_pilot[]; -extern const u32 b43_ntab_pilotlt[]; -extern const u32 b43_ntab_tdi20a0[]; -extern const u32 b43_ntab_tdi20a1[]; -extern const u32 b43_ntab_tdi40a0[]; -extern const u32 b43_ntab_tdi40a1[]; -extern const u32 b43_ntab_tdtrn[]; -extern const u32 b43_ntab_tmap[]; - +void b43_ntab_write_bulk(struct b43_wldev *dev, u32 offset, + unsigned int nr_elements, const void *_data); + +void b43_nphy_rev0_1_2_tables_init(struct b43_wldev *dev); +void b43_nphy_rev3plus_tables_init(struct b43_wldev *dev); + +extern const u32 b43_ntab_tx_gain_rev0_1_2[]; +extern const u32 b43_ntab_tx_gain_rev3plus_2ghz[]; +extern const u32 b43_ntab_tx_gain_rev3_5ghz[]; +extern const u32 b43_ntab_tx_gain_rev4_5ghz[]; +extern const u32 b43_ntab_tx_gain_rev5plus_5ghz[]; + +extern const u32 txpwrctrl_tx_gain_ipa[]; +extern const u32 txpwrctrl_tx_gain_ipa_rev5[]; +extern const u32 txpwrctrl_tx_gain_ipa_rev6[]; +extern const u32 txpwrctrl_tx_gain_ipa_5g[]; +extern const u16 tbl_iqcal_gainparams[2][9][8]; +extern const struct nphy_txiqcal_ladder ladder_lo[]; +extern const struct nphy_txiqcal_ladder ladder_iq[]; +extern const u16 loscale[]; + +extern const u16 tbl_tx_iqlo_cal_loft_ladder_40[]; +extern const u16 tbl_tx_iqlo_cal_loft_ladder_20[]; +extern const u16 tbl_tx_iqlo_cal_iqimb_ladder_40[]; +extern const u16 tbl_tx_iqlo_cal_iqimb_ladder_20[]; +extern const u16 tbl_tx_iqlo_cal_startcoefs_nphyrev3[]; +extern const u16 tbl_tx_iqlo_cal_startcoefs[]; +extern const u16 tbl_tx_iqlo_cal_cmds_recal_nphyrev3[]; +extern const u16 tbl_tx_iqlo_cal_cmds_recal[]; +extern const u16 tbl_tx_iqlo_cal_cmds_fullcal[]; +extern const u16 tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3[]; +extern const s16 tbl_tx_filter_coef_rev4[7][15]; + +extern const struct nphy_rf_control_override_rev2 + tbl_rf_control_override_rev2[]; +extern const struct nphy_rf_control_override_rev3 + tbl_rf_control_override_rev3[]; #endif /* B43_TABLES_NPHY_H_ */ diff --git a/drivers/net/wireless/b43legacy/dma.c b/drivers/net/wireless/b43legacy/dma.c index 0a86bdf53154..8b9387c6ff36 100644 --- a/drivers/net/wireless/b43legacy/dma.c +++ b/drivers/net/wireless/b43legacy/dma.c @@ -1411,7 +1411,6 @@ int b43legacy_dma_tx(struct b43legacy_wldev *dev, b43legacyerr(dev->wl, "DMA tx mapping failure\n"); goto out_unlock; } - ring->nr_tx_packets++; if ((free_slots(ring) < SLOTS_PER_PACKET) || should_inject_overflow(ring)) { /* This TX ring is full. */ @@ -1527,25 +1526,6 @@ void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev, spin_unlock(&ring->lock); } -void b43legacy_dma_get_tx_stats(struct b43legacy_wldev *dev, - struct ieee80211_tx_queue_stats *stats) -{ - const int nr_queues = dev->wl->hw->queues; - struct b43legacy_dmaring *ring; - unsigned long flags; - int i; - - for (i = 0; i < nr_queues; i++) { - ring = priority_to_txring(dev, i); - - spin_lock_irqsave(&ring->lock, flags); - stats[i].len = ring->used_slots / SLOTS_PER_PACKET; - stats[i].limit = ring->nr_slots / SLOTS_PER_PACKET; - stats[i].count = ring->nr_tx_packets; - spin_unlock_irqrestore(&ring->lock, flags); - } -} - static void dma_rx(struct b43legacy_dmaring *ring, int *slot) { diff --git a/drivers/net/wireless/b43legacy/dma.h b/drivers/net/wireless/b43legacy/dma.h index 2f186003c31e..f9681041c2d8 100644 --- a/drivers/net/wireless/b43legacy/dma.h +++ b/drivers/net/wireless/b43legacy/dma.h @@ -243,8 +243,6 @@ struct b43legacy_dmaring { int used_slots; /* Currently used slot in the ring. */ int current_slot; - /* Total number of packets sent. Statistics only. */ - unsigned int nr_tx_packets; /* Frameoffset in octets. */ u32 frameoffset; /* Descriptor buffer size. */ @@ -292,9 +290,6 @@ void b43legacy_dma_free(struct b43legacy_wldev *dev); void b43legacy_dma_tx_suspend(struct b43legacy_wldev *dev); void b43legacy_dma_tx_resume(struct b43legacy_wldev *dev); -void b43legacy_dma_get_tx_stats(struct b43legacy_wldev *dev, - struct ieee80211_tx_queue_stats *stats); - int b43legacy_dma_tx(struct b43legacy_wldev *dev, struct sk_buff *skb); void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev, @@ -315,11 +310,6 @@ void b43legacy_dma_free(struct b43legacy_wldev *dev) { } static inline -void b43legacy_dma_get_tx_stats(struct b43legacy_wldev *dev, - struct ieee80211_tx_queue_stats *stats) -{ -} -static inline int b43legacy_dma_tx(struct b43legacy_wldev *dev, struct sk_buff *skb) { diff --git a/drivers/net/wireless/b43legacy/leds.h b/drivers/net/wireless/b43legacy/leds.h index 82167a90088f..9ff6750dc57f 100644 --- a/drivers/net/wireless/b43legacy/leds.h +++ b/drivers/net/wireless/b43legacy/leds.h @@ -45,7 +45,7 @@ enum b43legacy_led_behaviour { void b43legacy_leds_init(struct b43legacy_wldev *dev); void b43legacy_leds_exit(struct b43legacy_wldev *dev); -#else /* CONFIG_B43EGACY_LEDS */ +#else /* CONFIG_B43LEGACY_LEDS */ /* LED support disabled */ struct b43legacy_led { diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index ab6a18c2e9d9..08523a45e23b 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -61,6 +61,8 @@ MODULE_AUTHOR("Michael Buesch"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(B43legacy_SUPPORTED_FIRMWARE_ID); +MODULE_FIRMWARE("b43legacy/ucode2.fw"); +MODULE_FIRMWARE("b43legacy/ucode4.fw"); #if defined(CONFIG_B43LEGACY_DMA) && defined(CONFIG_B43LEGACY_PIO) static int modparam_pio; @@ -2444,29 +2446,6 @@ static int b43legacy_op_conf_tx(struct ieee80211_hw *hw, u16 queue, return 0; } -static int b43legacy_op_get_tx_stats(struct ieee80211_hw *hw, - struct ieee80211_tx_queue_stats *stats) -{ - struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); - struct b43legacy_wldev *dev = wl->current_dev; - unsigned long flags; - int err = -ENODEV; - - if (!dev) - goto out; - spin_lock_irqsave(&wl->irq_lock, flags); - if (likely(b43legacy_status(dev) >= B43legacy_STAT_STARTED)) { - if (b43legacy_using_pio(dev)) - b43legacy_pio_get_tx_stats(dev, stats); - else - b43legacy_dma_get_tx_stats(dev, stats); - err = 0; - } - spin_unlock_irqrestore(&wl->irq_lock, flags); -out: - return err; -} - static int b43legacy_op_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats) { @@ -2921,6 +2900,7 @@ static int b43legacy_wireless_core_start(struct b43legacy_wldev *dev) goto out; } /* We are ready to run. */ + ieee80211_wake_queues(dev->wl->hw); b43legacy_set_status(dev, B43legacy_STAT_STARTED); /* Start data flow (TX/RX) */ @@ -3341,6 +3321,7 @@ static int b43legacy_wireless_core_init(struct b43legacy_wldev *dev) b43legacy_security_init(dev); b43legacy_rng_init(wl); + ieee80211_wake_queues(dev->wl->hw); b43legacy_set_status(dev, B43legacy_STAT_INITIALIZED); b43legacy_leds_init(dev); @@ -3361,7 +3342,7 @@ err_kfree_lo_control: } static int b43legacy_op_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); struct b43legacy_wldev *dev; @@ -3370,23 +3351,23 @@ static int b43legacy_op_add_interface(struct ieee80211_hw *hw, /* TODO: allow WDS/AP devices to coexist */ - if (conf->type != NL80211_IFTYPE_AP && - conf->type != NL80211_IFTYPE_STATION && - conf->type != NL80211_IFTYPE_WDS && - conf->type != NL80211_IFTYPE_ADHOC) + if (vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_STATION && + vif->type != NL80211_IFTYPE_WDS && + vif->type != NL80211_IFTYPE_ADHOC) return -EOPNOTSUPP; mutex_lock(&wl->mutex); if (wl->operating) goto out_mutex_unlock; - b43legacydbg(wl, "Adding Interface type %d\n", conf->type); + b43legacydbg(wl, "Adding Interface type %d\n", vif->type); dev = wl->current_dev; wl->operating = 1; - wl->vif = conf->vif; - wl->if_type = conf->type; - memcpy(wl->mac_addr, conf->mac_addr, ETH_ALEN); + wl->vif = vif; + wl->if_type = vif->type; + memcpy(wl->mac_addr, vif->addr, ETH_ALEN); spin_lock_irqsave(&wl->irq_lock, flags); b43legacy_adjust_opmode(dev); @@ -3403,18 +3384,18 @@ static int b43legacy_op_add_interface(struct ieee80211_hw *hw, } static void b43legacy_op_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); struct b43legacy_wldev *dev = wl->current_dev; unsigned long flags; - b43legacydbg(wl, "Removing Interface type %d\n", conf->type); + b43legacydbg(wl, "Removing Interface type %d\n", vif->type); mutex_lock(&wl->mutex); B43legacy_WARN_ON(!wl->operating); - B43legacy_WARN_ON(wl->vif != conf->vif); + B43legacy_WARN_ON(wl->vif != vif); wl->vif = NULL; wl->operating = 0; @@ -3509,7 +3490,6 @@ static const struct ieee80211_ops b43legacy_hw_ops = { .bss_info_changed = b43legacy_op_bss_info_changed, .configure_filter = b43legacy_op_configure_filter, .get_stats = b43legacy_op_get_stats, - .get_tx_stats = b43legacy_op_get_tx_stats, .start = b43legacy_op_start, .stop = b43legacy_op_stop, .set_tim = b43legacy_op_beacon_set_tim, @@ -3960,7 +3940,7 @@ static struct ssb_driver b43legacy_ssb_driver = { static void b43legacy_print_driverinfo(void) { - const char *feat_pci = "", *feat_leds = "", *feat_rfkill = "", + const char *feat_pci = "", *feat_leds = "", *feat_pio = "", *feat_dma = ""; #ifdef CONFIG_B43LEGACY_PCI_AUTOSELECT @@ -3969,9 +3949,6 @@ static void b43legacy_print_driverinfo(void) #ifdef CONFIG_B43LEGACY_LEDS feat_leds = "L"; #endif -#ifdef CONFIG_B43LEGACY_RFKILL - feat_rfkill = "R"; -#endif #ifdef CONFIG_B43LEGACY_PIO feat_pio = "I"; #endif @@ -3979,9 +3956,9 @@ static void b43legacy_print_driverinfo(void) feat_dma = "D"; #endif printk(KERN_INFO "Broadcom 43xx-legacy driver loaded " - "[ Features: %s%s%s%s%s, Firmware-ID: " + "[ Features: %s%s%s%s, Firmware-ID: " B43legacy_SUPPORTED_FIRMWARE_ID " ]\n", - feat_pci, feat_leds, feat_rfkill, feat_pio, feat_dma); + feat_pci, feat_leds, feat_pio, feat_dma); } static int __init b43legacy_init(void) diff --git a/drivers/net/wireless/b43legacy/pio.c b/drivers/net/wireless/b43legacy/pio.c index 51866c9a2769..017c0e9c37ef 100644 --- a/drivers/net/wireless/b43legacy/pio.c +++ b/drivers/net/wireless/b43legacy/pio.c @@ -477,7 +477,6 @@ int b43legacy_pio_tx(struct b43legacy_wldev *dev, list_move_tail(&packet->list, &queue->txqueue); queue->nr_txfree--; - queue->nr_tx_packets++; B43legacy_WARN_ON(queue->nr_txfree >= B43legacy_PIO_MAXTXPACKETS); tasklet_schedule(&queue->txtask); @@ -546,18 +545,6 @@ void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev, tasklet_schedule(&queue->txtask); } -void b43legacy_pio_get_tx_stats(struct b43legacy_wldev *dev, - struct ieee80211_tx_queue_stats *stats) -{ - struct b43legacy_pio *pio = &dev->pio; - struct b43legacy_pioqueue *queue; - - queue = pio->queue1; - stats[0].len = B43legacy_PIO_MAXTXPACKETS - queue->nr_txfree; - stats[0].limit = B43legacy_PIO_MAXTXPACKETS; - stats[0].count = queue->nr_tx_packets; -} - static void pio_rx_error(struct b43legacy_pioqueue *queue, int clear_buffers, const char *error) diff --git a/drivers/net/wireless/b43legacy/pio.h b/drivers/net/wireless/b43legacy/pio.h index 464fec05a06d..8e6773ea6e75 100644 --- a/drivers/net/wireless/b43legacy/pio.h +++ b/drivers/net/wireless/b43legacy/pio.h @@ -74,10 +74,6 @@ struct b43legacy_pioqueue { * posted to the device. We are waiting for the txstatus. */ struct list_head txrunning; - /* Total number or packets sent. - * (This counter can obviously wrap). - */ - unsigned int nr_tx_packets; struct tasklet_struct txtask; struct b43legacy_pio_txpacket tx_packets_cache[B43legacy_PIO_MAXTXPACKETS]; @@ -106,8 +102,6 @@ int b43legacy_pio_tx(struct b43legacy_wldev *dev, struct sk_buff *skb); void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev, const struct b43legacy_txstatus *status); -void b43legacy_pio_get_tx_stats(struct b43legacy_wldev *dev, - struct ieee80211_tx_queue_stats *stats); void b43legacy_pio_rx(struct b43legacy_pioqueue *queue); /* Suspend TX queue in hardware. */ @@ -140,11 +134,6 @@ void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev, { } static inline -void b43legacy_pio_get_tx_stats(struct b43legacy_wldev *dev, - struct ieee80211_tx_queue_stats *stats) -{ -} -static inline void b43legacy_pio_rx(struct b43legacy_pioqueue *queue) { } diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c index c9640a3e02c9..d19748d90aaf 100644 --- a/drivers/net/wireless/hostap/hostap_cs.c +++ b/drivers/net/wireless/hostap/hostap_cs.c @@ -794,13 +794,6 @@ static struct pcmcia_device_id hostap_cs_ids[] = { PCMCIA_MFC_DEVICE_PROD_ID12(0, "SanDisk", "ConnectPlus", 0x7a954bd9, 0x74be00c6), PCMCIA_DEVICE_PROD_ID123( - "Intersil", "PRISM 2_5 PCMCIA ADAPTER", "ISL37300P", - 0x4b801a17, 0x6345a0bf, 0xc9049a39), - /* D-Link DWL-650 Rev. P1; manfid 0x000b, 0x7110 */ - PCMCIA_DEVICE_PROD_ID123( - "D-Link", "DWL-650 Wireless PC Card RevP", "ISL37101P-10", - 0x1a424a1c, 0x6ea57632, 0xdd97a26b), - PCMCIA_DEVICE_PROD_ID123( "Addtron", "AWP-100 Wireless PCMCIA", "Version 01.02", 0xe6ec52ce, 0x08649af2, 0x4b74baa0), PCMCIA_DEVICE_PROD_ID123( @@ -834,14 +827,12 @@ static struct pcmcia_device_id hostap_cs_ids[] = { "Ver. 1.00", 0x5cd01705, 0x4271660f, 0x9d08ee12), PCMCIA_DEVICE_PROD_ID123( - "corega", "WL PCCL-11", "ISL37300P", - 0xa21501a, 0x59868926, 0xc9049a39), - PCMCIA_DEVICE_PROD_ID123( - "The Linksys Group, Inc.", "Wireless Network CF Card", "ISL37300P", - 0xa5f472c2, 0x9c05598d, 0xc9049a39), - PCMCIA_DEVICE_PROD_ID123( "Wireless LAN" , "11Mbps PC Card", "Version 01.02", 0x4b8870ff, 0x70e946d1, 0x4b74baa0), + PCMCIA_DEVICE_PROD_ID3("HFA3863", 0x355cb092), + PCMCIA_DEVICE_PROD_ID3("ISL37100P", 0x630d52b2), + PCMCIA_DEVICE_PROD_ID3("ISL37101P-10", 0xdd97a26b), + PCMCIA_DEVICE_PROD_ID3("ISL37300P", 0xc9049a39), PCMCIA_DEVICE_NULL }; MODULE_DEVICE_TABLE(pcmcia, hostap_cs_ids); diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c index ff9b5c882184..d70732819423 100644 --- a/drivers/net/wireless/hostap/hostap_hw.c +++ b/drivers/net/wireless/hostap/hostap_hw.c @@ -2618,6 +2618,15 @@ static irqreturn_t prism2_interrupt(int irq, void *dev_id) int events = 0; u16 ev; + /* Detect early interrupt before driver is fully configued */ + if (!dev->base_addr) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: Interrupt, but dev not configured\n", + dev->name); + } + return IRQ_HANDLED; + } + iface = netdev_priv(dev); local = iface->local; diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig index b16b06c2031f..dc8ed1527666 100644 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -1,14 +1,8 @@ config IWLWIFI tristate "Intel Wireless Wifi" - depends on PCI && MAC80211 && EXPERIMENTAL + depends on PCI && MAC80211 select FW_LOADER -config IWLWIFI_SPECTRUM_MEASUREMENT - bool "Enable Spectrum Measurement in iwlagn driver" - depends on IWLWIFI - ---help--- - This option will enable spectrum measurement for the iwlagn driver. - config IWLWIFI_DEBUG bool "Enable full debugging output in iwlagn and iwl3945 drivers" depends on IWLWIFI @@ -120,9 +114,3 @@ config IWL3945 inserted in and removed from the running kernel whenever you want), say M here and read <file:Documentation/kbuild/modules.txt>. The module will be called iwl3945. - -config IWL3945_SPECTRUM_MEASUREMENT - bool "Enable Spectrum Measurement in iwl3945 driver" - depends on IWL3945 - ---help--- - This option will enable spectrum measurement for the iwl3945 driver. diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile index 7f82044af242..4e378faee650 100644 --- a/drivers/net/wireless/iwlwifi/Makefile +++ b/drivers/net/wireless/iwlwifi/Makefile @@ -3,7 +3,6 @@ iwlcore-objs := iwl-core.o iwl-eeprom.o iwl-hcmd.o iwl-power.o iwlcore-objs += iwl-rx.o iwl-tx.o iwl-sta.o iwl-calib.o iwlcore-objs += iwl-scan.o iwl-led.o iwlcore-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o -iwlcore-$(CONFIG_IWLWIFI_SPECTRUM_MEASUREMENT) += iwl-spectrum.o iwlcore-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o CFLAGS_iwl-devtrace.o := -I$(src) @@ -20,3 +19,5 @@ iwlagn-$(CONFIG_IWL5000) += iwl-1000.o # 3945 obj-$(CONFIG_IWL3945) += iwl3945.o iwl3945-objs := iwl3945-base.o iwl-3945.o iwl-3945-rs.o iwl-3945-led.o + +ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index 8414178bcff4..3bf2e6e9b2d9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2008-2009 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -89,8 +89,78 @@ static void iwl1000_nic_config(struct iwl_priv *priv) ~APMG_SVR_VOLTAGE_CONFIG_BIT_MSK); } +static struct iwl_sensitivity_ranges iwl1000_sensitivity = { + .min_nrg_cck = 95, + .max_nrg_cck = 0, /* not used, set to 0 */ + .auto_corr_min_ofdm = 90, + .auto_corr_min_ofdm_mrc = 170, + .auto_corr_min_ofdm_x1 = 120, + .auto_corr_min_ofdm_mrc_x1 = 240, + + .auto_corr_max_ofdm = 120, + .auto_corr_max_ofdm_mrc = 210, + .auto_corr_max_ofdm_x1 = 155, + .auto_corr_max_ofdm_mrc_x1 = 290, + + .auto_corr_min_cck = 125, + .auto_corr_max_cck = 200, + .auto_corr_min_cck_mrc = 170, + .auto_corr_max_cck_mrc = 400, + .nrg_th_cck = 95, + .nrg_th_ofdm = 95, + + .barker_corr_th_min = 190, + .barker_corr_th_min_mrc = 390, + .nrg_th_cca = 62, +}; + +static int iwl1000_hw_set_hw_params(struct iwl_priv *priv) +{ + if (priv->cfg->mod_params->num_of_queues >= IWL_MIN_NUM_QUEUES && + priv->cfg->mod_params->num_of_queues <= IWL50_NUM_QUEUES) + priv->cfg->num_of_queues = + priv->cfg->mod_params->num_of_queues; + + priv->hw_params.max_txq_num = priv->cfg->num_of_queues; + priv->hw_params.dma_chnl_num = FH50_TCSR_CHNL_NUM; + priv->hw_params.scd_bc_tbls_size = + priv->cfg->num_of_queues * + sizeof(struct iwl5000_scd_bc_tbl); + priv->hw_params.tfd_size = sizeof(struct iwl_tfd); + priv->hw_params.max_stations = IWL5000_STATION_COUNT; + priv->hw_params.bcast_sta_id = IWL5000_BROADCAST_ID; + + priv->hw_params.max_data_size = IWL50_RTC_DATA_SIZE; + priv->hw_params.max_inst_size = IWL50_RTC_INST_SIZE; + + priv->hw_params.max_bsm_size = 0; + priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) | + BIT(IEEE80211_BAND_5GHZ); + priv->hw_params.rx_wrt_ptr_reg = FH_RSCSR_CHNL0_WPTR; + + priv->hw_params.tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant); + priv->hw_params.rx_chains_num = num_of_ant(priv->cfg->valid_rx_ant); + priv->hw_params.valid_tx_ant = priv->cfg->valid_tx_ant; + priv->hw_params.valid_rx_ant = priv->cfg->valid_rx_ant; + + if (priv->cfg->ops->lib->temp_ops.set_ct_kill) + priv->cfg->ops->lib->temp_ops.set_ct_kill(priv); + + /* Set initial sensitivity parameters */ + /* Set initial calibration set */ + priv->hw_params.sens = &iwl1000_sensitivity; + priv->hw_params.calib_init_cfg = + BIT(IWL_CALIB_XTAL) | + BIT(IWL_CALIB_LO) | + BIT(IWL_CALIB_TX_IQ) | + BIT(IWL_CALIB_TX_IQ_PERD) | + BIT(IWL_CALIB_BASE_BAND); + + return 0; +} + static struct iwl_lib_ops iwl1000_lib = { - .set_hw_params = iwl5000_hw_set_hw_params, + .set_hw_params = iwl1000_hw_set_hw_params, .txq_update_byte_cnt_tbl = iwl5000_txq_update_byte_cnt_tbl, .txq_inval_byte_cnt_tbl = iwl5000_txq_inval_byte_cnt_tbl, .txq_set_sched = iwl5000_txq_set_sched, @@ -105,6 +175,8 @@ static struct iwl_lib_ops iwl1000_lib = { .load_ucode = iwl5000_load_ucode, .dump_nic_event_log = iwl_dump_nic_event_log, .dump_nic_error_log = iwl_dump_nic_error_log, + .dump_csr = iwl_dump_csr, + .dump_fh = iwl_dump_fh, .init_alive_start = iwl5000_init_alive_start, .alive_notify = iwl5000_alive_notify, .send_tx_power = iwl5000_send_tx_power, @@ -138,9 +210,10 @@ static struct iwl_lib_ops iwl1000_lib = { .temperature = iwl5000_temperature, .set_ct_kill = iwl1000_set_ct_threshold, }, + .add_bcast_station = iwl_add_bcast_station, }; -static struct iwl_ops iwl1000_ops = { +static const struct iwl_ops iwl1000_ops = { .ucode = &iwl5000_ucode, .lib = &iwl1000_lib, .hcmd = &iwl5000_hcmd, @@ -173,7 +246,8 @@ struct iwl_cfg iwl1000_bgn_cfg = { .use_rts_for_ht = true, /* use rts/cts protection */ .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .support_ct_kill_exit = true, - .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, }; struct iwl_cfg iwl1000_bg_cfg = { @@ -200,6 +274,8 @@ struct iwl_cfg iwl1000_bg_cfg = { .led_compensation = 51, .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, }; MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-fh.h b/drivers/net/wireless/iwlwifi/iwl-3945-fh.h index 08ce259a0e60..042f6bc0df13 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-fh.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945-fh.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h index 6fd10d443ba3..3a876a8ece38 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-led.c b/drivers/net/wireless/iwlwifi/iwl-3945-led.c index a871d09d598f..abe2b739c4dc 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-led.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945-led.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-led.h b/drivers/net/wireless/iwlwifi/iwl-3945-led.h index 5a1033ca7aaa..ce990adc51e7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-led.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945-led.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c index d4b49883b30e..47909f94271e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index 234891d8cc10..521584b99fd1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -45,8 +45,8 @@ #include "iwl-sta.h" #include "iwl-3945.h" #include "iwl-eeprom.h" -#include "iwl-helpers.h" #include "iwl-core.h" +#include "iwl-helpers.h" #include "iwl-led.h" #include "iwl-3945-led.h" @@ -1951,11 +1951,7 @@ static int iwl3945_commit_rxon(struct iwl_priv *priv) } /* Add the broadcast address so we can send broadcast frames */ - if (iwl_add_station(priv, iwl_bcast_addr, false, CMD_SYNC, NULL) == - IWL_INVALID_STATION) { - IWL_ERR(priv, "Error adding BROADCAST address for transmit.\n"); - return -EIO; - } + priv->cfg->ops->lib->add_bcast_station(priv); /* If we have set the ASSOC_MSK and we are in BSS mode then * add the IWL_AP_ID to the station rate table */ @@ -2474,11 +2470,9 @@ int iwl3945_hw_set_hw_params(struct iwl_priv *priv) memset((void *)&priv->hw_params, 0, sizeof(struct iwl_hw_params)); - priv->shared_virt = - pci_alloc_consistent(priv->pci_dev, - sizeof(struct iwl3945_shared), - &priv->shared_phys); - + priv->shared_virt = dma_alloc_coherent(&priv->pci_dev->dev, + sizeof(struct iwl3945_shared), + &priv->shared_phys, GFP_KERNEL); if (!priv->shared_virt) { IWL_ERR(priv, "failed to allocate pci memory\n"); mutex_unlock(&priv->mutex); @@ -2796,6 +2790,7 @@ static struct iwl_lib_ops iwl3945_lib = { .post_associate = iwl3945_post_associate, .isr = iwl_isr_legacy, .config_ap = iwl3945_config_ap, + .add_bcast_station = iwl3945_add_bcast_station, }; static struct iwl_hcmd_utils_ops iwl3945_hcmd_utils = { @@ -2804,7 +2799,7 @@ static struct iwl_hcmd_utils_ops iwl3945_hcmd_utils = { .rts_tx_cmd_flag = iwlcore_rts_tx_cmd_flag, }; -static struct iwl_ops iwl3945_ops = { +static const struct iwl_ops iwl3945_ops = { .ucode = &iwl3945_ucode, .lib = &iwl3945_lib, .hcmd = &iwl3945_hcmd, @@ -2830,6 +2825,7 @@ static struct iwl_cfg iwl3945_bg_cfg = { .ht_greenfield_support = false, .led_compensation = 64, .broken_powersave = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, }; static struct iwl_cfg iwl3945_abg_cfg = { @@ -2847,6 +2843,7 @@ static struct iwl_cfg iwl3945_abg_cfg = { .ht_greenfield_support = false, .led_compensation = 64, .broken_powersave = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, }; struct pci_device_id iwl3945_hw_card_ids[] = { diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.h b/drivers/net/wireless/iwlwifi/iwl-3945.h index 531fa125f5a6..ae94babe595b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -171,24 +171,6 @@ struct iwl3945_frame { #define SCAN_INTERVAL 100 -#define STATUS_HCMD_ACTIVE 0 /* host command in progress */ -#define STATUS_HCMD_SYNC_ACTIVE 1 /* sync host command in progress */ -#define STATUS_INT_ENABLED 2 -#define STATUS_RF_KILL_HW 3 -#define STATUS_INIT 5 -#define STATUS_ALIVE 6 -#define STATUS_READY 7 -#define STATUS_TEMPERATURE 8 -#define STATUS_GEO_CONFIGURED 9 -#define STATUS_EXIT_PENDING 10 -#define STATUS_STATISTICS 12 -#define STATUS_SCANNING 13 -#define STATUS_SCAN_ABORTING 14 -#define STATUS_SCAN_HW 15 -#define STATUS_POWER_PMI 16 -#define STATUS_FW_ERROR 17 -#define STATUS_CONF_PENDING 18 - #define MAX_TID_COUNT 9 #define IWL_INVALID_RATE 0xFF @@ -226,7 +208,8 @@ extern void iwl3945_rx_replenish(void *data); extern void iwl3945_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq); extern unsigned int iwl3945_fill_beacon_frame(struct iwl_priv *priv, struct ieee80211_hdr *hdr,int left); -extern void iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log); +extern int iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log, + char **buf, bool display); extern void iwl3945_dump_nic_error_log(struct iwl_priv *priv); /* diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-hw.h b/drivers/net/wireless/iwlwifi/iwl-4965-hw.h index c606366b582c..67ef562e8db1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965-hw.h +++ b/drivers/net/wireless/iwlwifi/iwl-4965-hw.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index 31462813bac0..1bd2cd836026 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -581,6 +581,13 @@ static int iwl4965_alive_notify(struct iwl_priv *priv) iwl4965_set_wr_ptrs(priv, IWL_CMD_QUEUE_NUM, 0); + /* make sure all queue are not stopped */ + memset(&priv->queue_stopped[0], 0, sizeof(priv->queue_stopped)); + for (i = 0; i < 4; i++) + atomic_set(&priv->queue_stop_count[i], 0); + + /* reset to 0 to enable all the queue first */ + priv->txq_ctx_active_msk = 0; /* Map each Tx/cmd queue to its corresponding fifo */ for (i = 0; i < ARRAY_SIZE(default_queue_to_tx_fifo); i++) { int ac = default_queue_to_tx_fifo[i]; @@ -2206,9 +2213,10 @@ static struct iwl_lib_ops iwl4965_lib = { .temperature = iwl4965_temperature_calib, .set_ct_kill = iwl4965_set_ct_threshold, }, + .add_bcast_station = iwl_add_bcast_station, }; -static struct iwl_ops iwl4965_ops = { +static const struct iwl_ops iwl4965_ops = { .ucode = &iwl4965_ucode, .lib = &iwl4965_lib, .hcmd = &iwl4965_hcmd, @@ -2239,7 +2247,7 @@ struct iwl_cfg iwl4965_agn_cfg = { .broken_powersave = true, .led_compensation = 61, .chain_noise_num_beacons = IWL4965_CAL_NUM_BEACONS, - .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, }; /* Module firmware */ diff --git a/drivers/net/wireless/iwlwifi/iwl-5000-hw.h b/drivers/net/wireless/iwlwifi/iwl-5000-hw.h index bc056e9ab85f..714e032f6217 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000-hw.h +++ b/drivers/net/wireless/iwlwifi/iwl-5000-hw.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2007 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index cffaae772d51..e476acb53aa7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2007 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -179,14 +179,24 @@ static void iwl5000_gain_computation(struct iwl_priv *priv, data->delta_gain_code[i] = 0; continue; } - delta_g = (1000 * ((s32)average_noise[default_chain] - + + delta_g = (priv->cfg->chain_noise_scale * + ((s32)average_noise[default_chain] - (s32)average_noise[i])) / 1500; + /* bound gain by 2 bits value max, 3rd bit is sign */ data->delta_gain_code[i] = min(abs(delta_g), (long) CHAIN_NOISE_MAX_DELTA_GAIN_CODE); if (delta_g < 0) - /* set negative sign */ + /* + * set negative sign ... + * note to Intel developers: This is uCode API format, + * not the format of any internal device registers. + * Do not change this format for e.g. 6050 or similar + * devices. Change format only if more resolution + * (i.e. more than 2 bits magnitude) is needed. + */ data->delta_gain_code[i] |= (1 << 2); } @@ -263,8 +273,8 @@ static struct iwl_sensitivity_ranges iwl5000_sensitivity = { .auto_corr_max_ofdm = 120, .auto_corr_max_ofdm_mrc = 210, - .auto_corr_max_ofdm_x1 = 155, - .auto_corr_max_ofdm_mrc_x1 = 290, + .auto_corr_max_ofdm_x1 = 120, + .auto_corr_max_ofdm_mrc_x1 = 240, .auto_corr_min_cck = 125, .auto_corr_max_cck = 200, @@ -412,12 +422,14 @@ static void iwl5000_rx_calib_complete(struct iwl_priv *priv, /* * ucode */ -static int iwl5000_load_section(struct iwl_priv *priv, - struct fw_desc *image, - u32 dst_addr) +static int iwl5000_load_section(struct iwl_priv *priv, const char *name, + struct fw_desc *image, u32 dst_addr) { dma_addr_t phy_addr = image->p_addr; u32 byte_cnt = image->len; + int ret; + + priv->ucode_write_complete = 0; iwl_write_direct32(priv, FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), @@ -447,57 +459,36 @@ static int iwl5000_load_section(struct iwl_priv *priv, FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); - return 0; -} - -static int iwl5000_load_given_ucode(struct iwl_priv *priv, - struct fw_desc *inst_image, - struct fw_desc *data_image) -{ - int ret = 0; - - ret = iwl5000_load_section(priv, inst_image, - IWL50_RTC_INST_LOWER_BOUND); - if (ret) - return ret; - - IWL_DEBUG_INFO(priv, "INST uCode section being loaded...\n"); + IWL_DEBUG_INFO(priv, "%s uCode section being loaded...\n", name); ret = wait_event_interruptible_timeout(priv->wait_command_queue, priv->ucode_write_complete, 5 * HZ); if (ret == -ERESTARTSYS) { - IWL_ERR(priv, "Could not load the INST uCode section due " - "to interrupt\n"); + IWL_ERR(priv, "Could not load the %s uCode section due " + "to interrupt\n", name); return ret; } if (!ret) { - IWL_ERR(priv, "Could not load the INST uCode section\n"); + IWL_ERR(priv, "Could not load the %s uCode section\n", + name); return -ETIMEDOUT; } - priv->ucode_write_complete = 0; - - ret = iwl5000_load_section( - priv, data_image, IWL50_RTC_DATA_LOWER_BOUND); - if (ret) - return ret; + return 0; +} - IWL_DEBUG_INFO(priv, "DATA uCode section being loaded...\n"); +static int iwl5000_load_given_ucode(struct iwl_priv *priv, + struct fw_desc *inst_image, + struct fw_desc *data_image) +{ + int ret = 0; - ret = wait_event_interruptible_timeout(priv->wait_command_queue, - priv->ucode_write_complete, 5 * HZ); - if (ret == -ERESTARTSYS) { - IWL_ERR(priv, "Could not load the INST uCode section due " - "to interrupt\n"); + ret = iwl5000_load_section(priv, "INST", inst_image, + IWL50_RTC_INST_LOWER_BOUND); + if (ret) return ret; - } else if (!ret) { - IWL_ERR(priv, "Could not load the DATA uCode section\n"); - return -ETIMEDOUT; - } else - ret = 0; - priv->ucode_write_complete = 0; - - return ret; + return iwl5000_load_section(priv, "DATA", data_image, + IWL50_RTC_DATA_LOWER_BOUND); } int iwl5000_load_ucode(struct iwl_priv *priv) @@ -657,6 +648,13 @@ int iwl5000_alive_notify(struct iwl_priv *priv) iwl5000_set_wr_ptrs(priv, IWL_CMD_QUEUE_NUM, 0); + /* make sure all queue are not stopped */ + memset(&priv->queue_stopped[0], 0, sizeof(priv->queue_stopped)); + for (i = 0; i < 4; i++) + atomic_set(&priv->queue_stop_count[i], 0); + + /* reset to 0 to enable all the queue first */ + priv->txq_ctx_active_msk = 0; /* map qos queues to fifos one-to-one */ for (i = 0; i < ARRAY_SIZE(iwl5000_default_queue_to_tx_fifo); i++) { int ac = iwl5000_default_queue_to_tx_fifo[i]; @@ -781,7 +779,7 @@ void iwl5000_txq_update_byte_cnt_tbl(struct iwl_priv *priv, scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent; - if (txq->q.write_ptr < TFD_QUEUE_SIZE_BC_DUP) + if (write_ptr < TFD_QUEUE_SIZE_BC_DUP) scd_bc_tbl[txq_id]. tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent; } @@ -800,12 +798,12 @@ void iwl5000_txq_inval_byte_cnt_tbl(struct iwl_priv *priv, if (txq_id != IWL_CMD_QUEUE_NUM) sta_id = txq->cmd[read_ptr]->cmd.tx.sta_id; - bc_ent = cpu_to_le16(1 | (sta_id << 12)); + bc_ent = cpu_to_le16(1 | (sta_id << 12)); scd_bc_tbl[txq_id].tfd_offset[read_ptr] = bc_ent; - if (txq->q.write_ptr < TFD_QUEUE_SIZE_BC_DUP) + if (read_ptr < TFD_QUEUE_SIZE_BC_DUP) scd_bc_tbl[txq_id]. - tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent; + tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent; } static int iwl5000_tx_queue_set_q2ratid(struct iwl_priv *priv, u16 ra_tid, @@ -1464,6 +1462,8 @@ struct iwl_lib_ops iwl5000_lib = { .is_valid_rtc_data_addr = iwl5000_hw_valid_rtc_data_addr, .dump_nic_event_log = iwl_dump_nic_event_log, .dump_nic_error_log = iwl_dump_nic_error_log, + .dump_csr = iwl_dump_csr, + .dump_fh = iwl_dump_fh, .load_ucode = iwl5000_load_ucode, .init_alive_start = iwl5000_init_alive_start, .alive_notify = iwl5000_alive_notify, @@ -1499,6 +1499,7 @@ struct iwl_lib_ops iwl5000_lib = { .temperature = iwl5000_temperature, .set_ct_kill = iwl5000_set_ct_threshold, }, + .add_bcast_station = iwl_add_bcast_station, }; static struct iwl_lib_ops iwl5150_lib = { @@ -1516,6 +1517,7 @@ static struct iwl_lib_ops iwl5150_lib = { .is_valid_rtc_data_addr = iwl5000_hw_valid_rtc_data_addr, .dump_nic_event_log = iwl_dump_nic_event_log, .dump_nic_error_log = iwl_dump_nic_error_log, + .dump_csr = iwl_dump_csr, .load_ucode = iwl5000_load_ucode, .init_alive_start = iwl5000_init_alive_start, .alive_notify = iwl5000_alive_notify, @@ -1551,9 +1553,10 @@ static struct iwl_lib_ops iwl5150_lib = { .temperature = iwl5150_temperature, .set_ct_kill = iwl5150_set_ct_threshold, }, + .add_bcast_station = iwl_add_bcast_station, }; -static struct iwl_ops iwl5000_ops = { +static const struct iwl_ops iwl5000_ops = { .ucode = &iwl5000_ucode, .lib = &iwl5000_lib, .hcmd = &iwl5000_hcmd, @@ -1561,7 +1564,7 @@ static struct iwl_ops iwl5000_ops = { .led = &iwlagn_led_ops, }; -static struct iwl_ops iwl5150_ops = { +static const struct iwl_ops iwl5150_ops = { .ucode = &iwl5000_ucode, .lib = &iwl5150_lib, .hcmd = &iwl5000_hcmd, @@ -1598,7 +1601,8 @@ struct iwl_cfg iwl5300_agn_cfg = { .led_compensation = 51, .use_rts_for_ht = true, /* use rts/cts protection */ .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, - .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, }; struct iwl_cfg iwl5100_bgn_cfg = { @@ -1623,6 +1627,8 @@ struct iwl_cfg iwl5100_bgn_cfg = { .led_compensation = 51, .use_rts_for_ht = true, /* use rts/cts protection */ .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, }; struct iwl_cfg iwl5100_abg_cfg = { @@ -1645,6 +1651,8 @@ struct iwl_cfg iwl5100_abg_cfg = { .use_bsm = false, .led_compensation = 51, .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, }; struct iwl_cfg iwl5100_agn_cfg = { @@ -1669,7 +1677,8 @@ struct iwl_cfg iwl5100_agn_cfg = { .led_compensation = 51, .use_rts_for_ht = true, /* use rts/cts protection */ .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, - .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, }; struct iwl_cfg iwl5350_agn_cfg = { @@ -1694,7 +1703,8 @@ struct iwl_cfg iwl5350_agn_cfg = { .led_compensation = 51, .use_rts_for_ht = true, /* use rts/cts protection */ .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, - .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, }; struct iwl_cfg iwl5150_agn_cfg = { @@ -1719,7 +1729,8 @@ struct iwl_cfg iwl5150_agn_cfg = { .led_compensation = 51, .use_rts_for_ht = true, /* use rts/cts protection */ .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, - .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, }; struct iwl_cfg iwl5150_abg_cfg = { @@ -1742,6 +1753,8 @@ struct iwl_cfg iwl5150_abg_cfg = { .use_bsm = false, .led_compensation = 51, .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, }; MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/iwlwifi/iwl-6000-hw.h b/drivers/net/wireless/iwlwifi/iwl-6000-hw.h index 90185777d98b..ddba39999997 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000-hw.h +++ b/drivers/net/wireless/iwlwifi/iwl-6000-hw.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2007 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index 74e571049273..c4844adff92a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2008-2009 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -70,6 +70,14 @@ static void iwl6000_set_ct_threshold(struct iwl_priv *priv) priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD; } +/* Indicate calibration version to uCode. */ +static void iwl6050_set_calib_version(struct iwl_priv *priv) +{ + if (priv->cfg->ops->lib->eeprom_ops.calib_version(priv) >= 6) + iwl_set_bit(priv, CSR_GP_DRIVER_REG, + CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6); +} + /* NIC configuration for 6000 series */ static void iwl6000_nic_config(struct iwl_priv *priv) { @@ -96,6 +104,8 @@ static void iwl6000_nic_config(struct iwl_priv *priv) CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA); } /* else do nothing, uCode configured */ + if (priv->cfg->ops->lib->temp_ops.set_calib_version) + priv->cfg->ops->lib->temp_ops.set_calib_version(priv); } static struct iwl_sensitivity_ranges iwl6000_sensitivity = { @@ -108,7 +118,7 @@ static struct iwl_sensitivity_ranges iwl6000_sensitivity = { .auto_corr_max_ofdm = 145, .auto_corr_max_ofdm_mrc = 232, - .auto_corr_max_ofdm_x1 = 145, + .auto_corr_max_ofdm_x1 = 110, .auto_corr_max_ofdm_mrc_x1 = 232, .auto_corr_min_cck = 125, @@ -158,11 +168,25 @@ static int iwl6000_hw_set_hw_params(struct iwl_priv *priv) /* Set initial sensitivity parameters */ /* Set initial calibration set */ priv->hw_params.sens = &iwl6000_sensitivity; - priv->hw_params.calib_init_cfg = + switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) { + case CSR_HW_REV_TYPE_6x50: + priv->hw_params.calib_init_cfg = + BIT(IWL_CALIB_XTAL) | + BIT(IWL_CALIB_DC) | + BIT(IWL_CALIB_LO) | + BIT(IWL_CALIB_TX_IQ) | + BIT(IWL_CALIB_BASE_BAND); + + break; + default: + priv->hw_params.calib_init_cfg = BIT(IWL_CALIB_XTAL) | BIT(IWL_CALIB_LO) | BIT(IWL_CALIB_TX_IQ) | BIT(IWL_CALIB_BASE_BAND); + break; + } + return 0; } @@ -215,6 +239,8 @@ static struct iwl_lib_ops iwl6000_lib = { .load_ucode = iwl5000_load_ucode, .dump_nic_event_log = iwl_dump_nic_event_log, .dump_nic_error_log = iwl_dump_nic_error_log, + .dump_csr = iwl_dump_csr, + .dump_fh = iwl_dump_fh, .init_alive_start = iwl5000_init_alive_start, .alive_notify = iwl5000_alive_notify, .send_tx_power = iwl5000_send_tx_power, @@ -250,9 +276,10 @@ static struct iwl_lib_ops iwl6000_lib = { .temperature = iwl5000_temperature, .set_ct_kill = iwl6000_set_ct_threshold, }, + .add_bcast_station = iwl_add_bcast_station, }; -static struct iwl_ops iwl6000_ops = { +static const struct iwl_ops iwl6000_ops = { .ucode = &iwl5000_ucode, .lib = &iwl6000_lib, .hcmd = &iwl5000_hcmd, @@ -260,18 +287,68 @@ static struct iwl_ops iwl6000_ops = { .led = &iwlagn_led_ops, }; -static struct iwl_hcmd_utils_ops iwl6050_hcmd_utils = { - .get_hcmd_size = iwl5000_get_hcmd_size, - .build_addsta_hcmd = iwl5000_build_addsta_hcmd, - .rts_tx_cmd_flag = iwl5000_rts_tx_cmd_flag, - .calc_rssi = iwl5000_calc_rssi, +static struct iwl_lib_ops iwl6050_lib = { + .set_hw_params = iwl6000_hw_set_hw_params, + .txq_update_byte_cnt_tbl = iwl5000_txq_update_byte_cnt_tbl, + .txq_inval_byte_cnt_tbl = iwl5000_txq_inval_byte_cnt_tbl, + .txq_set_sched = iwl5000_txq_set_sched, + .txq_agg_enable = iwl5000_txq_agg_enable, + .txq_agg_disable = iwl5000_txq_agg_disable, + .txq_attach_buf_to_tfd = iwl_hw_txq_attach_buf_to_tfd, + .txq_free_tfd = iwl_hw_txq_free_tfd, + .txq_init = iwl_hw_tx_queue_init, + .rx_handler_setup = iwl5000_rx_handler_setup, + .setup_deferred_work = iwl5000_setup_deferred_work, + .is_valid_rtc_data_addr = iwl5000_hw_valid_rtc_data_addr, + .load_ucode = iwl5000_load_ucode, + .dump_nic_event_log = iwl_dump_nic_event_log, + .dump_nic_error_log = iwl_dump_nic_error_log, + .dump_csr = iwl_dump_csr, + .dump_fh = iwl_dump_fh, + .init_alive_start = iwl5000_init_alive_start, + .alive_notify = iwl5000_alive_notify, + .send_tx_power = iwl5000_send_tx_power, + .update_chain_flags = iwl_update_chain_flags, + .set_channel_switch = iwl6000_hw_channel_switch, + .apm_ops = { + .init = iwl_apm_init, + .stop = iwl_apm_stop, + .config = iwl6000_nic_config, + .set_pwr_src = iwl_set_pwr_src, + }, + .eeprom_ops = { + .regulatory_bands = { + EEPROM_5000_REG_BAND_1_CHANNELS, + EEPROM_5000_REG_BAND_2_CHANNELS, + EEPROM_5000_REG_BAND_3_CHANNELS, + EEPROM_5000_REG_BAND_4_CHANNELS, + EEPROM_5000_REG_BAND_5_CHANNELS, + EEPROM_5000_REG_BAND_24_HT40_CHANNELS, + EEPROM_5000_REG_BAND_52_HT40_CHANNELS + }, + .verify_signature = iwlcore_eeprom_verify_signature, + .acquire_semaphore = iwlcore_eeprom_acquire_semaphore, + .release_semaphore = iwlcore_eeprom_release_semaphore, + .calib_version = iwl5000_eeprom_calib_version, + .query_addr = iwl5000_eeprom_query_addr, + .update_enhanced_txpower = iwlcore_eeprom_enhanced_txpower, + }, + .post_associate = iwl_post_associate, + .isr = iwl_isr_ict, + .config_ap = iwl_config_ap, + .temp_ops = { + .temperature = iwl5000_temperature, + .set_ct_kill = iwl6000_set_ct_threshold, + .set_calib_version = iwl6050_set_calib_version, + }, + .add_bcast_station = iwl_add_bcast_station, }; -static struct iwl_ops iwl6050_ops = { +static const struct iwl_ops iwl6050_ops = { .ucode = &iwl5000_ucode, - .lib = &iwl6000_lib, + .lib = &iwl6050_lib, .hcmd = &iwl5000_hcmd, - .utils = &iwl6050_hcmd_utils, + .utils = &iwl5000_hcmd_utils, .led = &iwlagn_led_ops, }; @@ -306,7 +383,8 @@ struct iwl_cfg iwl6000i_2agn_cfg = { .supports_idle = true, .adv_thermal_throttle = true, .support_ct_kill_exit = true, - .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, }; struct iwl_cfg iwl6000i_2abg_cfg = { @@ -336,6 +414,8 @@ struct iwl_cfg iwl6000i_2abg_cfg = { .supports_idle = true, .adv_thermal_throttle = true, .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, }; struct iwl_cfg iwl6000i_2bg_cfg = { @@ -365,6 +445,8 @@ struct iwl_cfg iwl6000i_2bg_cfg = { .supports_idle = true, .adv_thermal_throttle = true, .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, }; struct iwl_cfg iwl6050_2agn_cfg = { @@ -395,7 +477,8 @@ struct iwl_cfg iwl6050_2agn_cfg = { .supports_idle = true, .adv_thermal_throttle = true, .support_ct_kill_exit = true, - .sm_ps_mode = WLAN_HT_CAP_SM_PS_DYNAMIC, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1500, }; struct iwl_cfg iwl6050_2abg_cfg = { @@ -425,6 +508,8 @@ struct iwl_cfg iwl6050_2abg_cfg = { .supports_idle = true, .adv_thermal_throttle = true, .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1500, }; struct iwl_cfg iwl6000_3agn_cfg = { @@ -455,7 +540,8 @@ struct iwl_cfg iwl6000_3agn_cfg = { .supports_idle = true, .adv_thermal_throttle = true, .support_ct_kill_exit = true, - .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, }; MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-led.c b/drivers/net/wireless/iwlwifi/iwl-agn-led.c index 3bccba20f6da..1a24946bc203 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-led.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-led.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-led.h b/drivers/net/wireless/iwlwifi/iwl-agn-led.h index ab55f92a161d..a594e4fdc6b8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-led.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn-led.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c index b93e49158196..8bf7c20b9d39 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -298,10 +298,23 @@ static void rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv, struct iwl_lq_sta *lq_data, u8 tid, struct ieee80211_sta *sta) { + int ret; + if (rs_tl_get_load(lq_data, tid) > IWL_AGG_LOAD_THRESHOLD) { IWL_DEBUG_HT(priv, "Starting Tx agg: STA: %pM tid: %d\n", sta->addr, tid); - ieee80211_start_tx_ba_session(sta, tid); + ret = ieee80211_start_tx_ba_session(sta, tid); + if (ret == -EAGAIN) { + /* + * driver and mac80211 is out of sync + * this might be cause by reloading firmware + * stop the tx ba session here + */ + IWL_DEBUG_HT(priv, "Fail start Tx agg on tid: %d\n", + tid); + ret = ieee80211_stop_tx_ba_session(sta, tid, + WLAN_BACK_INITIATOR); + } } } diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h index affc0c5a2f2c..e71923961e69 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -191,7 +191,7 @@ enum { IWL_RATE_2M_MASK) #define IWL_CCK_RATES_MASK \ - (IWL_BASIC_RATES_MASK | \ + (IWL_CCK_BASIC_RATES_MASK | \ IWL_RATE_5M_MASK | \ IWL_RATE_11M_MASK) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 1c9866daf815..c5b724eaf306 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -73,13 +73,7 @@ #define VD #endif -#ifdef CONFIG_IWLWIFI_SPECTRUM_MEASUREMENT -#define VS "s" -#else -#define VS -#endif - -#define DRV_VERSION IWLWIFI_VERSION VD VS +#define DRV_VERSION IWLWIFI_VERSION VD MODULE_DESCRIPTION(DRV_DESCRIPTION); @@ -203,7 +197,8 @@ int iwl_commit_rxon(struct iwl_priv *priv) priv->start_calib = 0; /* Add the broadcast address so we can send broadcast frames */ - iwl_add_bcast_station(priv); + priv->cfg->ops->lib->add_bcast_station(priv); + /* If we have set the ASSOC_MSK and we are in BSS mode then * add the IWL_AP_ID to the station rate table */ @@ -657,6 +652,131 @@ static void iwl_bg_statistics_periodic(unsigned long data) iwl_send_statistics_request(priv, CMD_ASYNC, false); } + +static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base, + u32 start_idx, u32 num_events, + u32 mode) +{ + u32 i; + u32 ptr; /* SRAM byte address of log data */ + u32 ev, time, data; /* event log data */ + unsigned long reg_flags; + + if (mode == 0) + ptr = base + (4 * sizeof(u32)) + (start_idx * 2 * sizeof(u32)); + else + ptr = base + (4 * sizeof(u32)) + (start_idx * 3 * sizeof(u32)); + + /* Make sure device is powered up for SRAM reads */ + spin_lock_irqsave(&priv->reg_lock, reg_flags); + if (iwl_grab_nic_access(priv)) { + spin_unlock_irqrestore(&priv->reg_lock, reg_flags); + return; + } + + /* Set starting address; reads will auto-increment */ + _iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR, ptr); + rmb(); + + /* + * "time" is actually "data" for mode 0 (no timestamp). + * place event id # at far right for easier visual parsing. + */ + for (i = 0; i < num_events; i++) { + ev = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT); + time = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT); + if (mode == 0) { + trace_iwlwifi_dev_ucode_cont_event(priv, + 0, time, ev); + } else { + data = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT); + trace_iwlwifi_dev_ucode_cont_event(priv, + time, data, ev); + } + } + /* Allow device to power down */ + iwl_release_nic_access(priv); + spin_unlock_irqrestore(&priv->reg_lock, reg_flags); +} + +static void iwl_continuous_event_trace(struct iwl_priv *priv) +{ + u32 capacity; /* event log capacity in # entries */ + u32 base; /* SRAM byte address of event log header */ + u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */ + u32 num_wraps; /* # times uCode wrapped to top of log */ + u32 next_entry; /* index of next entry to be written by uCode */ + + if (priv->ucode_type == UCODE_INIT) + base = le32_to_cpu(priv->card_alive_init.error_event_table_ptr); + else + base = le32_to_cpu(priv->card_alive.log_event_table_ptr); + if (priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) { + capacity = iwl_read_targ_mem(priv, base); + num_wraps = iwl_read_targ_mem(priv, base + (2 * sizeof(u32))); + mode = iwl_read_targ_mem(priv, base + (1 * sizeof(u32))); + next_entry = iwl_read_targ_mem(priv, base + (3 * sizeof(u32))); + } else + return; + + if (num_wraps == priv->event_log.num_wraps) { + iwl_print_cont_event_trace(priv, + base, priv->event_log.next_entry, + next_entry - priv->event_log.next_entry, + mode); + priv->event_log.non_wraps_count++; + } else { + if ((num_wraps - priv->event_log.num_wraps) > 1) + priv->event_log.wraps_more_count++; + else + priv->event_log.wraps_once_count++; + trace_iwlwifi_dev_ucode_wrap_event(priv, + num_wraps - priv->event_log.num_wraps, + next_entry, priv->event_log.next_entry); + if (next_entry < priv->event_log.next_entry) { + iwl_print_cont_event_trace(priv, base, + priv->event_log.next_entry, + capacity - priv->event_log.next_entry, + mode); + + iwl_print_cont_event_trace(priv, base, 0, + next_entry, mode); + } else { + iwl_print_cont_event_trace(priv, base, + next_entry, capacity - next_entry, + mode); + + iwl_print_cont_event_trace(priv, base, 0, + next_entry, mode); + } + } + priv->event_log.num_wraps = num_wraps; + priv->event_log.next_entry = next_entry; +} + +/** + * iwl_bg_ucode_trace - Timer callback to log ucode event + * + * The timer is continually set to execute every + * UCODE_TRACE_PERIOD milliseconds after the last timer expired + * this function is to perform continuous uCode event logging operation + * if enabled + */ +static void iwl_bg_ucode_trace(unsigned long data) +{ + struct iwl_priv *priv = (struct iwl_priv *)data; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (priv->event_log.ucode_trace) { + iwl_continuous_event_trace(priv); + /* Reschedule the timer to occur in UCODE_TRACE_PERIOD */ + mod_timer(&priv->ucode_trace, + jiffies + msecs_to_jiffies(UCODE_TRACE_PERIOD)); + } +} + static void iwl_rx_beacon_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { @@ -689,12 +809,14 @@ static void iwl_rx_card_state_notif(struct iwl_priv *priv, u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags); unsigned long status = priv->status; - IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s\n", + IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s CT:%s\n", (flags & HW_CARD_DISABLED) ? "Kill" : "On", - (flags & SW_CARD_DISABLED) ? "Kill" : "On"); + (flags & SW_CARD_DISABLED) ? "Kill" : "On", + (flags & CT_CARD_DISABLED) ? + "Reached" : "Not reached"); if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED | - RF_CARD_DISABLED)) { + CT_CARD_DISABLED)) { iwl_write32(priv, CSR_UCODE_DRV_GP1_SET, CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); @@ -708,10 +830,10 @@ static void iwl_rx_card_state_notif(struct iwl_priv *priv, iwl_write_direct32(priv, HBUS_TARG_MBX_C, HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); } - if (flags & RF_CARD_DISABLED) + if (flags & CT_CARD_DISABLED) iwl_tt_enter_ct_kill(priv); } - if (!(flags & RF_CARD_DISABLED)) + if (!(flags & CT_CARD_DISABLED)) iwl_tt_exit_ct_kill(priv); if (flags & HW_CARD_DISABLED) @@ -761,6 +883,8 @@ static void iwl_setup_rx_handlers(struct iwl_priv *priv) priv->rx_handlers[REPLY_ALIVE] = iwl_rx_reply_alive; priv->rx_handlers[REPLY_ERROR] = iwl_rx_reply_error; priv->rx_handlers[CHANNEL_SWITCH_NOTIFICATION] = iwl_rx_csa; + priv->rx_handlers[SPECTRUM_MEASURE_NOTIFICATION] = + iwl_rx_spectrum_measure_notif; priv->rx_handlers[PM_SLEEP_NOTIFICATION] = iwl_rx_pm_sleep_notif; priv->rx_handlers[PM_DEBUG_STATISTIC_NOTIFIC] = iwl_rx_pm_debug_statistics_notif; @@ -774,7 +898,6 @@ static void iwl_setup_rx_handlers(struct iwl_priv *priv) priv->rx_handlers[REPLY_STATISTICS_CMD] = iwl_reply_statistics; priv->rx_handlers[STATISTICS_NOTIFICATION] = iwl_rx_statistics; - iwl_setup_spectrum_handlers(priv); iwl_setup_rx_scan_handlers(priv); /* status change handler */ @@ -1634,7 +1757,7 @@ static const char *desc_lookup_text[] = { "DEBUG_1", "DEBUG_2", "DEBUG_3", - "UNKNOWN" + "ADVANCED SYSASSERT" }; static const char *desc_lookup(int i) @@ -1705,8 +1828,9 @@ void iwl_dump_nic_error_log(struct iwl_priv *priv) * iwl_print_event_log - Dump error event log to syslog * */ -static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx, - u32 num_events, u32 mode) +static int iwl_print_event_log(struct iwl_priv *priv, u32 start_idx, + u32 num_events, u32 mode, + int pos, char **buf, size_t bufsz) { u32 i; u32 base; /* SRAM byte address of event log header */ @@ -1716,7 +1840,7 @@ static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx, unsigned long reg_flags; if (num_events == 0) - return; + return pos; if (priv->ucode_type == UCODE_INIT) base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr); else @@ -1744,27 +1868,44 @@ static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx, time = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT); if (mode == 0) { /* data, ev */ - trace_iwlwifi_dev_ucode_event(priv, 0, time, ev); - IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n", time, ev); + if (bufsz) { + pos += scnprintf(*buf + pos, bufsz - pos, + "EVT_LOG:0x%08x:%04u\n", + time, ev); + } else { + trace_iwlwifi_dev_ucode_event(priv, 0, + time, ev); + IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n", + time, ev); + } } else { data = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT); - IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n", + if (bufsz) { + pos += scnprintf(*buf + pos, bufsz - pos, + "EVT_LOGT:%010u:0x%08x:%04u\n", + time, data, ev); + } else { + IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n", time, data, ev); - trace_iwlwifi_dev_ucode_event(priv, time, data, ev); + trace_iwlwifi_dev_ucode_event(priv, time, + data, ev); + } } } /* Allow device to power down */ iwl_release_nic_access(priv); spin_unlock_irqrestore(&priv->reg_lock, reg_flags); + return pos; } /** * iwl_print_last_event_logs - Dump the newest # of event log to syslog */ -static void iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity, - u32 num_wraps, u32 next_entry, - u32 size, u32 mode) +static int iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity, + u32 num_wraps, u32 next_entry, + u32 size, u32 mode, + int pos, char **buf, size_t bufsz) { /* * display the newest DEFAULT_LOG_ENTRIES entries @@ -1772,21 +1913,26 @@ static void iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity, */ if (num_wraps) { if (next_entry < size) { - iwl_print_event_log(priv, - capacity - (size - next_entry), - size - next_entry, mode); - iwl_print_event_log(priv, 0, - next_entry, mode); + pos = iwl_print_event_log(priv, + capacity - (size - next_entry), + size - next_entry, mode, + pos, buf, bufsz); + pos = iwl_print_event_log(priv, 0, + next_entry, mode, + pos, buf, bufsz); } else - iwl_print_event_log(priv, next_entry - size, - size, mode); + pos = iwl_print_event_log(priv, next_entry - size, + size, mode, pos, buf, bufsz); } else { - if (next_entry < size) - iwl_print_event_log(priv, 0, next_entry, mode); - else - iwl_print_event_log(priv, next_entry - size, - size, mode); + if (next_entry < size) { + pos = iwl_print_event_log(priv, 0, next_entry, + mode, pos, buf, bufsz); + } else { + pos = iwl_print_event_log(priv, next_entry - size, + size, mode, pos, buf, bufsz); + } } + return pos; } /* For sanity check only. Actual size is determined by uCode, typ. 512 */ @@ -1794,7 +1940,8 @@ static void iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity, #define DEFAULT_DUMP_EVENT_LOG_ENTRIES (20) -void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log) +int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log, + char **buf, bool display) { u32 base; /* SRAM byte address of event log header */ u32 capacity; /* event log capacity in # entries */ @@ -1802,6 +1949,8 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log) u32 num_wraps; /* # times uCode wrapped to top of log */ u32 next_entry; /* index of next entry to be written by uCode */ u32 size; /* # entries that we'll print */ + int pos = 0; + size_t bufsz = 0; if (priv->ucode_type == UCODE_INIT) base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr); @@ -1812,7 +1961,7 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log) IWL_ERR(priv, "Invalid event log pointer 0x%08X for %s uCode\n", base, (priv->ucode_type == UCODE_INIT) ? "Init" : "RT"); - return; + return -EINVAL; } /* event log header */ @@ -1838,7 +1987,7 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log) /* bail out if nothing in log */ if (size == 0) { IWL_ERR(priv, "Start IWL Event Log Dump: nothing in log\n"); - return; + return pos; } #ifdef CONFIG_IWLWIFI_DEBUG @@ -1853,6 +2002,15 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log) size); #ifdef CONFIG_IWLWIFI_DEBUG + if (display) { + if (full_log) + bufsz = capacity * 48; + else + bufsz = size * 48; + *buf = kmalloc(bufsz, GFP_KERNEL); + if (!*buf) + return -ENOMEM; + } if ((iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) || full_log) { /* * if uCode has wrapped back to top of log, @@ -1860,17 +2018,22 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log) * i.e the next one that uCode would fill. */ if (num_wraps) - iwl_print_event_log(priv, next_entry, - capacity - next_entry, mode); + pos = iwl_print_event_log(priv, next_entry, + capacity - next_entry, mode, + pos, buf, bufsz); /* (then/else) start at top of log */ - iwl_print_event_log(priv, 0, next_entry, mode); + pos = iwl_print_event_log(priv, 0, + next_entry, mode, pos, buf, bufsz); } else - iwl_print_last_event_logs(priv, capacity, num_wraps, - next_entry, size, mode); + pos = iwl_print_last_event_logs(priv, capacity, num_wraps, + next_entry, size, mode, + pos, buf, bufsz); #else - iwl_print_last_event_logs(priv, capacity, num_wraps, - next_entry, size, mode); + pos = iwl_print_last_event_logs(priv, capacity, num_wraps, + next_entry, size, mode, + pos, buf, bufsz); #endif + return pos; } /** @@ -2276,18 +2439,6 @@ static void iwl_bg_run_time_calib_work(struct work_struct *work) return; } -static void iwl_bg_up(struct work_struct *data) -{ - struct iwl_priv *priv = container_of(data, struct iwl_priv, up); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - mutex_lock(&priv->mutex); - __iwl_up(priv); - mutex_unlock(&priv->mutex); -} - static void iwl_bg_restart(struct work_struct *data) { struct iwl_priv *priv = container_of(data, struct iwl_priv, restart); @@ -2304,7 +2455,13 @@ static void iwl_bg_restart(struct work_struct *data) ieee80211_restart_hw(priv->hw); } else { iwl_down(priv); - queue_work(priv->workqueue, &priv->up); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + __iwl_up(priv); + mutex_unlock(&priv->mutex); } } @@ -2440,7 +2597,7 @@ void iwl_post_associate(struct iwl_priv *priv) * Not a mac80211 entry point function, but it fits in with all the * other mac80211 functions grouped here. */ -static int iwl_setup_mac(struct iwl_priv *priv) +static int iwl_mac_setup_register(struct iwl_priv *priv) { int ret; struct ieee80211_hw *hw = priv->hw; @@ -2456,6 +2613,10 @@ static int iwl_setup_mac(struct iwl_priv *priv) hw->flags |= IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS; + if (priv->cfg->sku & IWL_SKU_N) + hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | + IEEE80211_HW_SUPPORTS_STATIC_SMPS; + hw->sta_data_size = sizeof(struct iwl_station_priv); hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | @@ -2470,7 +2631,7 @@ static int iwl_setup_mac(struct iwl_priv *priv) */ hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; - hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; + hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX + 1; /* we create the 802.11 header and a zero-length SSID element */ hw->wiphy->max_scan_ie_len = IWL_MAX_PROBE_REQUEST - 24 - 2; @@ -2668,14 +2829,18 @@ void iwl_config_ap(struct iwl_priv *priv) } static void iwl_mac_update_tkip_key(struct ieee80211_hw *hw, - struct ieee80211_key_conf *keyconf, const u8 *addr, - u32 iv32, u16 *phase1key) + struct ieee80211_vif *vif, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta, + u32 iv32, u16 *phase1key) { struct iwl_priv *priv = hw->priv; IWL_DEBUG_MAC80211(priv, "enter\n"); - iwl_update_tkip_key(priv, keyconf, addr, iv32, phase1key); + iwl_update_tkip_key(priv, keyconf, + sta ? sta->addr : iwl_bcast_addr, + iv32, phase1key); IWL_DEBUG_MAC80211(priv, "leave\n"); } @@ -2776,14 +2941,28 @@ static int iwl_mac_ampdu_action(struct ieee80211_hw *hw, return ret; case IEEE80211_AMPDU_TX_START: IWL_DEBUG_HT(priv, "start Tx\n"); - return iwl_tx_agg_start(priv, sta->addr, tid, ssn); + ret = iwl_tx_agg_start(priv, sta->addr, tid, ssn); + if (ret == 0) { + priv->agg_tids_count++; + IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n", + priv->agg_tids_count); + } + return ret; case IEEE80211_AMPDU_TX_STOP: IWL_DEBUG_HT(priv, "stop Tx\n"); ret = iwl_tx_agg_stop(priv, sta->addr, tid); + if ((ret == 0) && (priv->agg_tids_count > 0)) { + priv->agg_tids_count--; + IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n", + priv->agg_tids_count); + } if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return 0; else return ret; + case IEEE80211_AMPDU_TX_OPERATIONAL: + /* do nothing */ + return -EOPNOTSUPP; default: IWL_DEBUG_HT(priv, "unknown\n"); return -EINVAL; @@ -2833,6 +3012,8 @@ static void iwl_mac_sta_notify(struct ieee80211_hw *hw, break; case STA_NOTIFY_AWAKE: WARN_ON(!sta_priv->client); + if (!sta_priv->asleep) + break; sta_priv->asleep = false; sta_id = iwl_find_station(priv, sta->addr); if (sta_id != IWL_INVALID_STATION) @@ -3109,7 +3290,6 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv) init_waitqueue_head(&priv->wait_command_queue); - INIT_WORK(&priv->up, iwl_bg_up); INIT_WORK(&priv->restart, iwl_bg_restart); INIT_WORK(&priv->rx_replenish, iwl_bg_rx_replenish); INIT_WORK(&priv->beacon_update, iwl_bg_beacon_update); @@ -3126,6 +3306,10 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv) priv->statistics_periodic.data = (unsigned long)priv; priv->statistics_periodic.function = iwl_bg_statistics_periodic; + init_timer(&priv->ucode_trace); + priv->ucode_trace.data = (unsigned long)priv; + priv->ucode_trace.function = iwl_bg_ucode_trace; + if (!priv->cfg->use_isr_legacy) tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) iwl_irq_tasklet, (unsigned long)priv); @@ -3144,6 +3328,7 @@ static void iwl_cancel_deferred_work(struct iwl_priv *priv) cancel_delayed_work(&priv->alive_start); cancel_work_sync(&priv->beacon_update); del_timer_sync(&priv->statistics_periodic); + del_timer_sync(&priv->ucode_trace); } static void iwl_init_hw_rates(struct iwl_priv *priv, @@ -3179,6 +3364,7 @@ static int iwl_init_drv(struct iwl_priv *priv) INIT_LIST_HEAD(&priv->free_frames); mutex_init(&priv->mutex); + mutex_init(&priv->sync_cmd_mutex); /* Clear the driver's (not device's) station table */ iwl_clear_stations_table(priv); @@ -3188,6 +3374,15 @@ static int iwl_init_drv(struct iwl_priv *priv) priv->band = IEEE80211_BAND_2GHZ; priv->iw_mode = NL80211_IFTYPE_STATION; + priv->current_ht_config.smps = IEEE80211_SMPS_STATIC; + priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF; + priv->agg_tids_count = 0; + + /* initialize force reset */ + priv->force_reset[IWL_RF_RESET].reset_duration = + IWL_DELAY_NEXT_FORCE_RF_RESET; + priv->force_reset[IWL_FW_RESET].reset_duration = + IWL_DELAY_NEXT_FORCE_FW_RELOAD; /* Choose which receivers/antennas to use */ if (priv->cfg->ops->hcmd->set_rxon_chain) @@ -3264,7 +3459,6 @@ static struct ieee80211_ops iwl_hw_ops = { .set_key = iwl_mac_set_key, .update_tkip_key = iwl_mac_update_tkip_key, .get_stats = iwl_mac_get_stats, - .get_tx_stats = iwl_mac_get_tx_stats, .conf_tx = iwl_mac_conf_tx, .reset_tsf = iwl_mac_reset_tsf, .bss_info_changed = iwl_bss_info_changed, @@ -3365,6 +3559,14 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) */ spin_lock_init(&priv->reg_lock); spin_lock_init(&priv->lock); + + /* + * stop and reset the on-board processor just in case it is in a + * strange state ... like being left stranded by a primary kernel + * and this is now the kdump kernel trying to start up + */ + iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); + iwl_hw_detect(priv); IWL_INFO(priv, "Detected Intel Wireless WiFi Link %s REV=0x%X\n", priv->cfg->name, priv->hw_rev); @@ -3439,9 +3641,9 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) iwl_setup_deferred_work(priv); iwl_setup_rx_handlers(priv); - /********************************** - * 8. Setup and register mac80211 - **********************************/ + /********************************************* + * 8. Enable interrupts and read RFKILL state + *********************************************/ /* enable interrupts if needed: hw bug w/a */ pci_read_config_word(priv->pci_dev, PCI_COMMAND, &pci_cmd); @@ -3452,14 +3654,6 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) iwl_enable_interrupts(priv); - err = iwl_setup_mac(priv); - if (err) - goto out_remove_sysfs; - - err = iwl_dbgfs_register(priv, DRV_NAME); - if (err) - IWL_ERR(priv, "failed to create debugfs files. Ignoring error: %d\n", err); - /* If platform's RF_KILL switch is NOT set to KILL */ if (iwl_read32(priv, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) clear_bit(STATUS_RF_KILL_HW, &priv->status); @@ -3471,6 +3665,18 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) iwl_power_initialize(priv); iwl_tt_initialize(priv); + + /************************************************** + * 9. Setup and register with mac80211 and debugfs + **************************************************/ + err = iwl_mac_setup_register(priv); + if (err) + goto out_remove_sysfs; + + err = iwl_dbgfs_register(priv, DRV_NAME); + if (err) + IWL_ERR(priv, "failed to create debugfs files. Ignoring error: %d\n", err); + return 0; out_remove_sysfs: diff --git a/drivers/net/wireless/iwlwifi/iwl-calib.c b/drivers/net/wireless/iwlwifi/iwl-calib.c index 95a57b36a7ea..845831ac053e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-calib.c +++ b/drivers/net/wireless/iwlwifi/iwl-calib.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -414,7 +414,6 @@ static int iwl_sens_auto_corr_ofdm(struct iwl_priv *priv, /* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */ static int iwl_sensitivity_write(struct iwl_priv *priv) { - int ret = 0; struct iwl_sensitivity_cmd cmd ; struct iwl_sensitivity_data *data = NULL; struct iwl_host_cmd cmd_out = { @@ -477,11 +476,7 @@ static int iwl_sensitivity_write(struct iwl_priv *priv) memcpy(&(priv->sensitivity_tbl[0]), &(cmd.table[0]), sizeof(u16)*HD_TABLE_SIZE); - ret = iwl_send_cmd(priv, &cmd_out); - if (ret) - IWL_ERR(priv, "SENSITIVITY_CMD failed\n"); - - return ret; + return iwl_send_cmd(priv, &cmd_out); } void iwl_init_sensitivity(struct iwl_priv *priv) diff --git a/drivers/net/wireless/iwlwifi/iwl-calib.h b/drivers/net/wireless/iwlwifi/iwl-calib.h index b6cef989a796..2b7b1df83ba0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-calib.h +++ b/drivers/net/wireless/iwlwifi/iwl-calib.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index e91507531923..ab3c77b92cc8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -120,7 +120,6 @@ enum { CALIBRATION_COMPLETE_NOTIFICATION = 0x67, /* 802.11h related */ - RADAR_NOTIFICATION = 0x70, /* not used */ REPLY_QUIET_CMD = 0x71, /* not used */ REPLY_CHANNEL_SWITCH = 0x72, CHANNEL_SWITCH_NOTIFICATION = 0x73, @@ -2248,10 +2247,22 @@ struct iwl_link_quality_cmd { __le32 reserved2; } __attribute__ ((packed)); +/* + * BT configuration enable flags: + * bit 0 - 1: BT channel announcement enabled + * 0: disable + * bit 1 - 1: priority of BT device enabled + * 0: disable + * bit 2 - 1: BT 2 wire support enabled + * 0: disable + */ +#define BT_COEX_DISABLE (0x0) +#define BT_ENABLE_CHANNEL_ANNOUNCE BIT(0) +#define BT_ENABLE_PRIORITY BIT(1) +#define BT_ENABLE_2_WIRE BIT(2) + #define BT_COEX_DISABLE (0x0) -#define BT_COEX_MODE_2W (0x1) -#define BT_COEX_MODE_3W (0x2) -#define BT_COEX_MODE_4W (0x3) +#define BT_COEX_ENABLE (BT_ENABLE_CHANNEL_ANNOUNCE | BT_ENABLE_PRIORITY) #define BT_LEAD_TIME_MIN (0x0) #define BT_LEAD_TIME_DEF (0x1E) @@ -2510,7 +2521,7 @@ struct iwl_card_state_notif { #define HW_CARD_DISABLED 0x01 #define SW_CARD_DISABLED 0x02 -#define RF_CARD_DISABLED 0x04 +#define CT_CARD_DISABLED 0x04 #define RXON_CARD_DISABLED 0x10 struct iwl_ct_kill_config { @@ -2984,7 +2995,7 @@ struct statistics_rx_ht_phy { __le32 agg_crc32_good; __le32 agg_mpdu_cnt; __le32 agg_cnt; - __le32 reserved2; + __le32 unsupport_mcs; } __attribute__ ((packed)); #define INTERFERENCE_DATA_AVAILABLE cpu_to_le32(1) @@ -3087,8 +3098,8 @@ struct statistics_div { } __attribute__ ((packed)); struct statistics_general { - __le32 temperature; - __le32 temperature_m; + __le32 temperature; /* radio temperature */ + __le32 temperature_m; /* for 5000 and up, this is radio voltage */ struct statistics_dbg dbg; __le32 sleep_time; __le32 slots_out; @@ -3096,7 +3107,12 @@ struct statistics_general { __le32 ttl_timestamp; struct statistics_div div; __le32 rx_enable_counter; - __le32 reserved1; + /* + * num_of_sos_states: + * count the number of times we have to re-tune + * in order to get out of bad PHY status + */ + __le32 num_of_sos_states; __le32 reserved2; __le32 reserved3; } __attribute__ ((packed)); @@ -3161,13 +3177,30 @@ struct iwl_notif_statistics { /* * MISSED_BEACONS_NOTIFICATION = 0xa2 (notification only, not a command) + * + * uCode send MISSED_BEACONS_NOTIFICATION to driver when detect beacon missed + * in regardless of how many missed beacons, which mean when driver receive the + * notification, inside the command, it can find all the beacons information + * which include number of total missed beacons, number of consecutive missed + * beacons, number of beacons received and number of beacons expected to + * receive. + * + * If uCode detected consecutive_missed_beacons > 5, it will reset the radio + * in order to bring the radio/PHY back to working state; which has no relation + * to when driver will perform sensitivity calibration. + * + * Driver should set it own missed_beacon_threshold to decide when to perform + * sensitivity calibration based on number of consecutive missed beacons in + * order to improve overall performance, especially in noisy environment. + * */ -/* if ucode missed CONSECUTIVE_MISSED_BCONS_TH beacons in a row, - * then this notification will be sent. */ -#define CONSECUTIVE_MISSED_BCONS_TH 20 + +#define IWL_MISSED_BEACON_THRESHOLD_MIN (1) +#define IWL_MISSED_BEACON_THRESHOLD_DEF (5) +#define IWL_MISSED_BEACON_THRESHOLD_MAX IWL_MISSED_BEACON_THRESHOLD_DEF struct iwl_missed_beacon_notif { - __le32 consequtive_missed_beacons; + __le32 consecutive_missed_beacons; __le32 total_missed_becons; __le32 num_expected_beacons; __le32 num_recvd_beacons; @@ -3437,11 +3470,7 @@ enum { IWL_PHY_CALIBRATE_DIFF_GAIN_CMD = 7, IWL_PHY_CALIBRATE_DC_CMD = 8, IWL_PHY_CALIBRATE_LO_CMD = 9, - IWL_PHY_CALIBRATE_RX_BB_CMD = 10, IWL_PHY_CALIBRATE_TX_IQ_CMD = 11, - IWL_PHY_CALIBRATE_RX_IQ_CMD = 12, - IWL_PHY_CALIBRATION_NOISE_CMD = 13, - IWL_PHY_CALIBRATE_AGC_TABLE_CMD = 14, IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD = 15, IWL_PHY_CALIBRATE_BASE_BAND_CMD = 16, IWL_PHY_CALIBRATE_TX_IQ_PERD_CMD = 17, diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index f36f804804fc..112149e9b31e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -2,7 +2,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -47,6 +47,26 @@ MODULE_VERSION(IWLWIFI_VERSION); MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); MODULE_LICENSE("GPL"); +/* + * set bt_coex_active to true, uCode will do kill/defer + * every time the priority line is asserted (BT is sending signals on the + * priority line in the PCIx). + * set bt_coex_active to false, uCode will ignore the BT activity and + * perform the normal operation + * + * User might experience transmit issue on some platform due to WiFi/BT + * co-exist problem. The possible behaviors are: + * Able to scan and finding all the available AP + * Not able to associate with any AP + * On those platforms, WiFi communication can be restored by set + * "bt_coex_active" module parameter to "false" + * + * default: bt_coex_active = true (BT_COEX_ENABLE) + */ +static bool bt_coex_active = true; +module_param(bt_coex_active, bool, S_IRUGO); +MODULE_PARM_DESC(bt_coex_active, "enable wifi/bluetooth co-exist\n"); + static struct iwl_wimax_coex_event_entry cu_priorities[COEX_NUM_OF_EVENTS] = { {COEX_CU_UNASSOC_IDLE_RP, COEX_CU_UNASSOC_IDLE_WP, 0, COEX_UNASSOC_IDLE_FLAGS}, @@ -257,8 +277,8 @@ int iwl_hw_nic_init(struct iwl_priv *priv) spin_lock_irqsave(&priv->lock, flags); priv->cfg->ops->lib->apm_ops.init(priv); - /* Set interrupt coalescing timer to 512 usecs */ - iwl_write8(priv, CSR_INT_COALESCING, 512 / 32); + /* Set interrupt coalescing calibration timer to default (512 usecs) */ + iwl_write8(priv, CSR_INT_COALESCING, IWL_HOST_INT_CALIB_TIMEOUT_DEF); spin_unlock_irqrestore(&priv->lock, flags); @@ -450,8 +470,6 @@ static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv, if (priv->cfg->ht_greenfield_support) ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; ht_info->cap |= IEEE80211_HT_CAP_SGI_20; - ht_info->cap |= (IEEE80211_HT_CAP_SM_PS & - (priv->cfg->sm_ps_mode << 2)); max_bit_rate = MAX_BIT_RATE_20_MHZ; if (priv->hw_params.ht40_channel & BIT(band)) { ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; @@ -636,7 +654,7 @@ EXPORT_SYMBOL(iwlcore_rts_tx_cmd_flag); static bool is_single_rx_stream(struct iwl_priv *priv) { - return !priv->current_ht_config.is_ht || + return priv->current_ht_config.smps == IEEE80211_SMPS_STATIC || priv->current_ht_config.single_chain_sufficient; } @@ -1003,28 +1021,18 @@ static int iwl_get_active_rx_chain_count(struct iwl_priv *priv) */ static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt) { - int idle_cnt = active_cnt; - bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status); - - /* # Rx chains when idling and maybe trying to save power */ - switch (priv->cfg->sm_ps_mode) { - case WLAN_HT_CAP_SM_PS_STATIC: - idle_cnt = (is_cam) ? active_cnt : IWL_NUM_IDLE_CHAINS_SINGLE; - break; - case WLAN_HT_CAP_SM_PS_DYNAMIC: - idle_cnt = (is_cam) ? IWL_NUM_IDLE_CHAINS_DUAL : - IWL_NUM_IDLE_CHAINS_SINGLE; - break; - case WLAN_HT_CAP_SM_PS_DISABLED: - break; - case WLAN_HT_CAP_SM_PS_INVALID: + /* # Rx chains when idling, depending on SMPS mode */ + switch (priv->current_ht_config.smps) { + case IEEE80211_SMPS_STATIC: + case IEEE80211_SMPS_DYNAMIC: + return IWL_NUM_IDLE_CHAINS_SINGLE; + case IEEE80211_SMPS_OFF: + return active_cnt; default: - IWL_ERR(priv, "invalid sm_ps mode %u\n", - priv->cfg->sm_ps_mode); - WARN_ON(1); - break; + WARN(1, "invalid SMPS mode %d", + priv->current_ht_config.smps); + return active_cnt; } - return idle_cnt; } /* up to 4 chains */ @@ -1363,7 +1371,11 @@ void iwl_irq_handle_error(struct iwl_priv *priv) clear_bit(STATUS_HCMD_ACTIVE, &priv->status); priv->cfg->ops->lib->dump_nic_error_log(priv); - priv->cfg->ops->lib->dump_nic_event_log(priv, false); + if (priv->cfg->ops->lib->dump_csr) + priv->cfg->ops->lib->dump_csr(priv); + if (priv->cfg->ops->lib->dump_fh) + priv->cfg->ops->lib->dump_fh(priv, NULL, false); + priv->cfg->ops->lib->dump_nic_event_log(priv, false, NULL, false); #ifdef CONFIG_IWLWIFI_DEBUG if (iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) iwl_print_rx_config_cmd(priv); @@ -1658,9 +1670,9 @@ EXPORT_SYMBOL(iwl_set_tx_power); void iwl_free_isr_ict(struct iwl_priv *priv) { if (priv->ict_tbl_vir) { - pci_free_consistent(priv->pci_dev, (sizeof(u32) * ICT_COUNT) + - PAGE_SIZE, priv->ict_tbl_vir, - priv->ict_tbl_dma); + dma_free_coherent(&priv->pci_dev->dev, + (sizeof(u32) * ICT_COUNT) + PAGE_SIZE, + priv->ict_tbl_vir, priv->ict_tbl_dma); priv->ict_tbl_vir = NULL; } } @@ -1676,9 +1688,9 @@ int iwl_alloc_isr_ict(struct iwl_priv *priv) if (priv->cfg->use_isr_legacy) return 0; /* allocate shrared data table */ - priv->ict_tbl_vir = pci_alloc_consistent(priv->pci_dev, (sizeof(u32) * - ICT_COUNT) + PAGE_SIZE, - &priv->ict_tbl_dma); + priv->ict_tbl_vir = dma_alloc_coherent(&priv->pci_dev->dev, + (sizeof(u32) * ICT_COUNT) + PAGE_SIZE, + &priv->ict_tbl_dma, GFP_KERNEL); if (!priv->ict_tbl_vir) return -ENOMEM; @@ -1813,6 +1825,16 @@ irqreturn_t iwl_isr_ict(int irq, void *data) if (val == 0xffffffff) val = 0; + /* + * this is a w/a for a h/w bug. the h/w bug may cause the Rx bit + * (bit 15 before shifting it to 31) to clear when using interrupt + * coalescing. fortunately, bits 18 and 19 stay set when this happens + * so we use them to decide on the real state of the Rx bit. + * In order words, bit 15 is set if bit 18 or bit 19 are set. + */ + if (val & 0xC0000) + val |= 0x8000; + inta = (0xff & val) | ((0xff00 & val) << 16); IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x ict 0x%08x\n", inta, inta_mask, val); @@ -1975,13 +1997,20 @@ EXPORT_SYMBOL(iwl_isr_legacy); int iwl_send_bt_config(struct iwl_priv *priv) { struct iwl_bt_cmd bt_cmd = { - .flags = BT_COEX_MODE_4W, .lead_time = BT_LEAD_TIME_DEF, .max_kill = BT_MAX_KILL_DEF, .kill_ack_mask = 0, .kill_cts_mask = 0, }; + if (!bt_coex_active) + bt_cmd.flags = BT_COEX_DISABLE; + else + bt_cmd.flags = BT_COEX_ENABLE; + + IWL_DEBUG_INFO(priv, "BT coex %s\n", + (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active"); + return iwl_send_cmd_pdu(priv, REPLY_BT_CONFIG, sizeof(struct iwl_bt_cmd), &bt_cmd); } @@ -2599,44 +2628,43 @@ int iwl_set_mode(struct iwl_priv *priv, int mode) EXPORT_SYMBOL(iwl_set_mode); int iwl_mac_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct iwl_priv *priv = hw->priv; - unsigned long flags; + int err = 0; - IWL_DEBUG_MAC80211(priv, "enter: type %d\n", conf->type); + IWL_DEBUG_MAC80211(priv, "enter: type %d\n", vif->type); + + mutex_lock(&priv->mutex); if (priv->vif) { IWL_DEBUG_MAC80211(priv, "leave - vif != NULL\n"); - return -EOPNOTSUPP; + err = -EOPNOTSUPP; + goto out; } - spin_lock_irqsave(&priv->lock, flags); - priv->vif = conf->vif; - priv->iw_mode = conf->type; - - spin_unlock_irqrestore(&priv->lock, flags); - - mutex_lock(&priv->mutex); + priv->vif = vif; + priv->iw_mode = vif->type; - if (conf->mac_addr) { - IWL_DEBUG_MAC80211(priv, "Set %pM\n", conf->mac_addr); - memcpy(priv->mac_addr, conf->mac_addr, ETH_ALEN); + if (vif->addr) { + IWL_DEBUG_MAC80211(priv, "Set %pM\n", vif->addr); + memcpy(priv->mac_addr, vif->addr, ETH_ALEN); } - if (iwl_set_mode(priv, conf->type) == -EAGAIN) + if (iwl_set_mode(priv, vif->type) == -EAGAIN) /* we are not ready, will run again when ready */ set_bit(STATUS_MODE_PENDING, &priv->status); + out: mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); - return 0; + return err; } EXPORT_SYMBOL(iwl_mac_add_interface); void iwl_mac_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct iwl_priv *priv = hw->priv; @@ -2649,7 +2677,7 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw, priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; iwlcore_commit_rxon(priv); } - if (priv->vif == conf->vif) { + if (priv->vif == vif) { priv->vif = NULL; memset(priv->bssid, 0, ETH_ALEN); } @@ -2689,6 +2717,21 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed) IWL_DEBUG_MAC80211(priv, "leave - scanning\n"); } + if (changed & (IEEE80211_CONF_CHANGE_SMPS | + IEEE80211_CONF_CHANGE_CHANNEL)) { + /* mac80211 uses static for non-HT which is what we want */ + priv->current_ht_config.smps = conf->smps_mode; + + /* + * Recalculate chain counts. + * + * If monitor mode is enabled then mac80211 will + * set up the SM PS mode to OFF if an HT channel is + * configured. + */ + if (priv->cfg->ops->hcmd->set_rxon_chain) + priv->cfg->ops->hcmd->set_rxon_chain(priv); + } /* during scanning mac80211 will delay channel setting until * scan finish with changed = 0 @@ -2786,10 +2829,6 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed) iwl_set_tx_power(priv, conf->power_level, false); } - /* call to ensure that 4965 rx_chain is set properly in monitor mode */ - if (priv->cfg->ops->hcmd->set_rxon_chain) - priv->cfg->ops->hcmd->set_rxon_chain(priv); - if (!iwl_is_ready(priv)) { IWL_DEBUG_MAC80211(priv, "leave - not ready\n"); goto out; @@ -2812,42 +2851,6 @@ out: } EXPORT_SYMBOL(iwl_mac_config); -int iwl_mac_get_tx_stats(struct ieee80211_hw *hw, - struct ieee80211_tx_queue_stats *stats) -{ - struct iwl_priv *priv = hw->priv; - int i, avail; - struct iwl_tx_queue *txq; - struct iwl_queue *q; - unsigned long flags; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - if (!iwl_is_ready_rf(priv)) { - IWL_DEBUG_MAC80211(priv, "leave - RF not ready\n"); - return -EIO; - } - - spin_lock_irqsave(&priv->lock, flags); - - for (i = 0; i < AC_NUM; i++) { - txq = &priv->txq[i]; - q = &txq->q; - avail = iwl_queue_space(q); - - stats[i].len = q->n_window - avail; - stats[i].limit = q->n_window - q->high_mark; - stats[i].count = q->n_window; - - } - spin_unlock_irqrestore(&priv->lock, flags); - - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return 0; -} -EXPORT_SYMBOL(iwl_mac_get_tx_stats); - void iwl_mac_reset_tsf(struct ieee80211_hw *hw) { struct iwl_priv *priv = hw->priv; @@ -3197,6 +3200,207 @@ void iwl_update_stats(struct iwl_priv *priv, bool is_tx, __le16 fc, u16 len) EXPORT_SYMBOL(iwl_update_stats); #endif +const static char *get_csr_string(int cmd) +{ + switch (cmd) { + IWL_CMD(CSR_HW_IF_CONFIG_REG); + IWL_CMD(CSR_INT_COALESCING); + IWL_CMD(CSR_INT); + IWL_CMD(CSR_INT_MASK); + IWL_CMD(CSR_FH_INT_STATUS); + IWL_CMD(CSR_GPIO_IN); + IWL_CMD(CSR_RESET); + IWL_CMD(CSR_GP_CNTRL); + IWL_CMD(CSR_HW_REV); + IWL_CMD(CSR_EEPROM_REG); + IWL_CMD(CSR_EEPROM_GP); + IWL_CMD(CSR_OTP_GP_REG); + IWL_CMD(CSR_GIO_REG); + IWL_CMD(CSR_GP_UCODE_REG); + IWL_CMD(CSR_GP_DRIVER_REG); + IWL_CMD(CSR_UCODE_DRV_GP1); + IWL_CMD(CSR_UCODE_DRV_GP2); + IWL_CMD(CSR_LED_REG); + IWL_CMD(CSR_DRAM_INT_TBL_REG); + IWL_CMD(CSR_GIO_CHICKEN_BITS); + IWL_CMD(CSR_ANA_PLL_CFG); + IWL_CMD(CSR_HW_REV_WA_REG); + IWL_CMD(CSR_DBG_HPET_MEM_REG); + default: + return "UNKNOWN"; + + } +} + +void iwl_dump_csr(struct iwl_priv *priv) +{ + int i; + u32 csr_tbl[] = { + CSR_HW_IF_CONFIG_REG, + CSR_INT_COALESCING, + CSR_INT, + CSR_INT_MASK, + CSR_FH_INT_STATUS, + CSR_GPIO_IN, + CSR_RESET, + CSR_GP_CNTRL, + CSR_HW_REV, + CSR_EEPROM_REG, + CSR_EEPROM_GP, + CSR_OTP_GP_REG, + CSR_GIO_REG, + CSR_GP_UCODE_REG, + CSR_GP_DRIVER_REG, + CSR_UCODE_DRV_GP1, + CSR_UCODE_DRV_GP2, + CSR_LED_REG, + CSR_DRAM_INT_TBL_REG, + CSR_GIO_CHICKEN_BITS, + CSR_ANA_PLL_CFG, + CSR_HW_REV_WA_REG, + CSR_DBG_HPET_MEM_REG + }; + IWL_ERR(priv, "CSR values:\n"); + IWL_ERR(priv, "(2nd byte of CSR_INT_COALESCING is " + "CSR_INT_PERIODIC_REG)\n"); + for (i = 0; i < ARRAY_SIZE(csr_tbl); i++) { + IWL_ERR(priv, " %25s: 0X%08x\n", + get_csr_string(csr_tbl[i]), + iwl_read32(priv, csr_tbl[i])); + } +} +EXPORT_SYMBOL(iwl_dump_csr); + +const static char *get_fh_string(int cmd) +{ + switch (cmd) { + IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG); + IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG); + IWL_CMD(FH_RSCSR_CHNL0_WPTR); + IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG); + IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG); + IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG); + IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV); + IWL_CMD(FH_TSSR_TX_STATUS_REG); + IWL_CMD(FH_TSSR_TX_ERROR_REG); + default: + return "UNKNOWN"; + + } +} + +int iwl_dump_fh(struct iwl_priv *priv, char **buf, bool display) +{ + int i; +#ifdef CONFIG_IWLWIFI_DEBUG + int pos = 0; + size_t bufsz = 0; +#endif + u32 fh_tbl[] = { + FH_RSCSR_CHNL0_STTS_WPTR_REG, + FH_RSCSR_CHNL0_RBDCB_BASE_REG, + FH_RSCSR_CHNL0_WPTR, + FH_MEM_RCSR_CHNL0_CONFIG_REG, + FH_MEM_RSSR_SHARED_CTRL_REG, + FH_MEM_RSSR_RX_STATUS_REG, + FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV, + FH_TSSR_TX_STATUS_REG, + FH_TSSR_TX_ERROR_REG + }; +#ifdef CONFIG_IWLWIFI_DEBUG + if (display) { + bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40; + *buf = kmalloc(bufsz, GFP_KERNEL); + if (!*buf) + return -ENOMEM; + pos += scnprintf(*buf + pos, bufsz - pos, + "FH register values:\n"); + for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) { + pos += scnprintf(*buf + pos, bufsz - pos, + " %34s: 0X%08x\n", + get_fh_string(fh_tbl[i]), + iwl_read_direct32(priv, fh_tbl[i])); + } + return pos; + } +#endif + IWL_ERR(priv, "FH register values:\n"); + for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) { + IWL_ERR(priv, " %34s: 0X%08x\n", + get_fh_string(fh_tbl[i]), + iwl_read_direct32(priv, fh_tbl[i])); + } + return 0; +} +EXPORT_SYMBOL(iwl_dump_fh); + +static void iwl_force_rf_reset(struct iwl_priv *priv) +{ + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (!iwl_is_associated(priv)) { + IWL_DEBUG_SCAN(priv, "force reset rejected: not associated\n"); + return; + } + /* + * There is no easy and better way to force reset the radio, + * the only known method is switching channel which will force to + * reset and tune the radio. + * Use internal short scan (single channel) operation to should + * achieve this objective. + * Driver should reset the radio when number of consecutive missed + * beacon, or any other uCode error condition detected. + */ + IWL_DEBUG_INFO(priv, "perform radio reset.\n"); + iwl_internal_short_hw_scan(priv); + return; +} + + +int iwl_force_reset(struct iwl_priv *priv, int mode) +{ + struct iwl_force_reset *force_reset; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return -EINVAL; + + if (mode >= IWL_MAX_FORCE_RESET) { + IWL_DEBUG_INFO(priv, "invalid reset request.\n"); + return -EINVAL; + } + force_reset = &priv->force_reset[mode]; + force_reset->reset_request_count++; + if (force_reset->last_force_reset_jiffies && + time_after(force_reset->last_force_reset_jiffies + + force_reset->reset_duration, jiffies)) { + IWL_DEBUG_INFO(priv, "force reset rejected\n"); + force_reset->reset_reject_count++; + return -EAGAIN; + } + force_reset->reset_success_count++; + force_reset->last_force_reset_jiffies = jiffies; + IWL_DEBUG_INFO(priv, "perform force reset (%d)\n", mode); + switch (mode) { + case IWL_RF_RESET: + iwl_force_rf_reset(priv); + break; + case IWL_FW_RESET: + IWL_ERR(priv, "On demand firmware reload\n"); + /* Set the FW error flag -- cleared on iwl_down */ + set_bit(STATUS_FW_ERROR, &priv->status); + wake_up_interruptible(&priv->wait_command_queue); + /* + * Keep the restart process from trying to send host + * commands by clearing the INIT status bit + */ + clear_bit(STATUS_READY, &priv->status); + queue_work(priv->workqueue, &priv->restart); + break; + } + return 0; +} + #ifdef CONFIG_PM int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state) diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index fe37d6a6bf97..4ef7739f9e8e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -63,8 +63,6 @@ #ifndef __iwl_core_h__ #define __iwl_core_h__ -#include <linux/utsrelease.h> - /************************ * forward declarations * ************************/ @@ -72,8 +70,8 @@ struct iwl_host_cmd; struct iwl_cmd; -#define IWLWIFI_VERSION UTS_RELEASE "-k" -#define DRV_COPYRIGHT "Copyright(c) 2003-2009 Intel Corporation" +#define IWLWIFI_VERSION "in-tree:" +#define DRV_COPYRIGHT "Copyright(c) 2003-2010 Intel Corporation" #define DRV_AUTHOR "<ilw@linux.intel.com>" #define IWL_PCI_DEVICE(dev, subdev, cfg) \ @@ -119,6 +117,7 @@ struct iwl_apm_ops { struct iwl_temp_ops { void (*temperature)(struct iwl_priv *priv); void (*set_ct_kill)(struct iwl_priv *priv); + void (*set_calib_version)(struct iwl_priv *priv); }; struct iwl_ucode_ops { @@ -169,8 +168,11 @@ struct iwl_lib_ops { int (*is_valid_rtc_data_addr)(u32 addr); /* 1st ucode load */ int (*load_ucode)(struct iwl_priv *priv); - void (*dump_nic_event_log)(struct iwl_priv *priv, bool full_log); + int (*dump_nic_event_log)(struct iwl_priv *priv, + bool full_log, char **buf, bool display); void (*dump_nic_error_log)(struct iwl_priv *priv); + void (*dump_csr)(struct iwl_priv *priv); + int (*dump_fh)(struct iwl_priv *priv, char **buf, bool display); int (*set_channel_switch)(struct iwl_priv *priv, u16 channel); /* power management */ struct iwl_apm_ops apm_ops; @@ -187,6 +189,8 @@ struct iwl_lib_ops { /* temperature */ struct iwl_temp_ops temp_ops; + /* station management */ + void (*add_bcast_station)(struct iwl_priv *priv); }; struct iwl_led_ops { @@ -230,8 +234,9 @@ struct iwl_mod_params { * @chain_noise_num_beacons: number of beacons used to compute chain noise * @adv_thermal_throttle: support advance thermal throttle * @support_ct_kill_exit: support ct kill exit condition - * @sm_ps_mode: spatial multiplexing power save mode * @support_wimax_coexist: support wimax/wifi co-exist + * @plcp_delta_threshold: plcp error rate threshold used to trigger + * radio tuning when there is a high receiving plcp error rate * * We enable the driver to be backward compatible wrt API version. The * driver specifies which APIs it supports (with @ucode_api_max being the @@ -287,8 +292,9 @@ struct iwl_cfg { const bool supports_idle; bool adv_thermal_throttle; bool support_ct_kill_exit; - u8 sm_ps_mode; const bool support_wimax_coexist; + u8 plcp_delta_threshold; + s32 chain_noise_scale; }; /*************************** @@ -332,13 +338,11 @@ int iwl_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb); int iwl_commit_rxon(struct iwl_priv *priv); int iwl_set_mode(struct iwl_priv *priv, int mode); int iwl_mac_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf); + struct ieee80211_vif *vif); void iwl_mac_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf); + struct ieee80211_vif *vif); int iwl_mac_config(struct ieee80211_hw *hw, u32 changed); void iwl_config_ap(struct iwl_priv *priv); -int iwl_mac_get_tx_stats(struct ieee80211_hw *hw, - struct ieee80211_tx_queue_stats *stats); void iwl_mac_reset_tsf(struct ieee80211_hw *hw); int iwl_alloc_txq_mem(struct iwl_priv *priv); void iwl_free_txq_mem(struct iwl_priv *priv); @@ -411,13 +415,13 @@ void iwl_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rxq); void iwl_cmd_queue_free(struct iwl_priv *priv); int iwl_rx_queue_alloc(struct iwl_priv *priv); void iwl_rx_handle(struct iwl_priv *priv); -int iwl_rx_queue_update_write_ptr(struct iwl_priv *priv, +void iwl_rx_queue_update_write_ptr(struct iwl_priv *priv, struct iwl_rx_queue *q); void iwl_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq); void iwl_rx_replenish(struct iwl_priv *priv); void iwl_rx_replenish_now(struct iwl_priv *priv); int iwl_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq); -int iwl_rx_queue_restock(struct iwl_priv *priv); +void iwl_rx_queue_restock(struct iwl_priv *priv); int iwl_rx_queue_space(const struct iwl_rx_queue *q); void iwl_rx_allocate(struct iwl_priv *priv, gfp_t priority); void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); @@ -425,6 +429,8 @@ int iwl_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index); /* Handlers */ void iwl_rx_missed_beacon_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); +void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb); void iwl_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); void iwl_reply_statistics(struct iwl_priv *priv, @@ -445,9 +451,9 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb); void iwl_hw_txq_ctx_free(struct iwl_priv *priv); int iwl_hw_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq); -int iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq); void iwl_free_tfds_in_queue(struct iwl_priv *priv, int sta_id, int tid, int freed); +void iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq); int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq, int slots_num, u32 txq_id); void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id); @@ -497,6 +503,8 @@ void iwl_init_scan_params(struct iwl_priv *priv); int iwl_scan_cancel(struct iwl_priv *priv); int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms); int iwl_mac_hw_scan(struct ieee80211_hw *hw, struct cfg80211_scan_request *req); +int iwl_internal_short_hw_scan(struct iwl_priv *priv); +int iwl_force_reset(struct iwl_priv *priv, int mode); u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame, const u8 *ie, int ie_len, int left); void iwl_setup_rx_scan_handlers(struct iwl_priv *priv); @@ -527,14 +535,6 @@ int iwl_send_calib_results(struct iwl_priv *priv); int iwl_calib_set(struct iwl_calib_result *res, const u8 *buf, int len); void iwl_calib_free_results(struct iwl_priv *priv); -/******************************************************************************* - * Spectrum Measureemtns in iwl-spectrum.c - ******************************************************************************/ -#ifdef CONFIG_IWLWIFI_SPECTRUM_MEASUREMENT -void iwl_setup_spectrum_handlers(struct iwl_priv *priv); -#else -static inline void iwl_setup_spectrum_handlers(struct iwl_priv *priv) {} -#endif /***************************************************** * S e n d i n g H o s t C o m m a n d s * *****************************************************/ @@ -583,7 +583,10 @@ int iwl_pci_resume(struct pci_dev *pdev); * Error Handling Debugging ******************************************************/ void iwl_dump_nic_error_log(struct iwl_priv *priv); -void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log); +int iwl_dump_nic_event_log(struct iwl_priv *priv, + bool full_log, char **buf, bool display); +void iwl_dump_csr(struct iwl_priv *priv); +int iwl_dump_fh(struct iwl_priv *priv, char **buf, bool display); #ifdef CONFIG_IWLWIFI_DEBUG void iwl_print_rx_config_cmd(struct iwl_priv *priv); #else @@ -603,7 +606,7 @@ void iwlcore_free_geos(struct iwl_priv *priv); /*************** DRIVER STATUS FUNCTIONS *****/ #define STATUS_HCMD_ACTIVE 0 /* host command in progress */ -#define STATUS_HCMD_SYNC_ACTIVE 1 /* sync host command in progress */ +/* 1 is unused (used to be STATUS_HCMD_SYNC_ACTIVE) */ #define STATUS_INT_ENABLED 2 #define STATUS_RF_KILL_HW 3 #define STATUS_CT_KILL 4 diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h index 1ec8cb4d5eae..808b7146bead 100644 --- a/drivers/net/wireless/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/iwlwifi/iwl-csr.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -369,7 +369,7 @@ #define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_3x3_HYB (0x00000000) #define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_HYB (0x00000001) #define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA (0x00000002) - +#define CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6 (0x00000004) /* GIO Chicken Bits (PCI Express bus link power management) */ #define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX (0x00800000) diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h index d61293ab67c9..1c7b53d511c7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/iwlwifi/iwl-debug.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project. * @@ -67,57 +67,6 @@ do { \ DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \ } while (0) -#ifdef CONFIG_IWLWIFI_DEBUGFS -struct iwl_debugfs { - const char *name; - struct dentry *dir_drv; - struct dentry *dir_data; - struct dentry *dir_debug; - struct dentry *dir_rf; - struct dir_data_files { - struct dentry *file_sram; - struct dentry *file_nvm; - struct dentry *file_stations; - struct dentry *file_log_event; - struct dentry *file_channels; - struct dentry *file_status; - struct dentry *file_interrupt; - struct dentry *file_qos; - struct dentry *file_thermal_throttling; - struct dentry *file_led; - struct dentry *file_disable_ht40; - struct dentry *file_sleep_level_override; - struct dentry *file_current_sleep_command; - } dbgfs_data_files; - struct dir_rf_files { - struct dentry *file_disable_sensitivity; - struct dentry *file_disable_chain_noise; - struct dentry *file_disable_tx_power; - } dbgfs_rf_files; - struct dir_debug_files { - struct dentry *file_rx_statistics; - struct dentry *file_tx_statistics; - struct dentry *file_traffic_log; - struct dentry *file_rx_queue; - struct dentry *file_tx_queue; - struct dentry *file_ucode_rx_stats; - struct dentry *file_ucode_tx_stats; - struct dentry *file_ucode_general_stats; - struct dentry *file_sensitivity; - struct dentry *file_chain_noise; - struct dentry *file_tx_power; - struct dentry *file_power_save_status; - struct dentry *file_clear_ucode_statistics; - struct dentry *file_clear_traffic_statistics; - } dbgfs_debug_files; - u32 sram_offset; - u32 sram_len; -}; - -int iwl_dbgfs_register(struct iwl_priv *priv, const char *name); -void iwl_dbgfs_unregister(struct iwl_priv *priv); -#endif - #else #define IWL_DEBUG(__priv, level, fmt, args...) #define IWL_DEBUG_LIMIT(__priv, level, fmt, args...) @@ -126,9 +75,10 @@ static inline void iwl_print_hex_dump(struct iwl_priv *priv, int level, {} #endif /* CONFIG_IWLWIFI_DEBUG */ - - -#ifndef CONFIG_IWLWIFI_DEBUGFS +#ifdef CONFIG_IWLWIFI_DEBUGFS +int iwl_dbgfs_register(struct iwl_priv *priv, const char *name); +void iwl_dbgfs_unregister(struct iwl_priv *priv); +#else static inline int iwl_dbgfs_register(struct iwl_priv *priv, const char *name) { return 0; diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c index 21e0f6699daf..7bf44f146799 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c +++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c @@ -2,7 +2,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -41,43 +41,28 @@ #include "iwl-calib.h" /* create and remove of files */ -#define DEBUGFS_ADD_DIR(name, parent) do { \ - dbgfs->dir_##name = debugfs_create_dir(#name, parent); \ - if (!(dbgfs->dir_##name)) \ - goto err; \ +#define DEBUGFS_ADD_FILE(name, parent, mode) do { \ + if (!debugfs_create_file(#name, mode, parent, priv, \ + &iwl_dbgfs_##name##_ops)) \ + goto err; \ } while (0) -#define DEBUGFS_ADD_FILE(name, parent, mode) do { \ - dbgfs->dbgfs_##parent##_files.file_##name = \ - debugfs_create_file(#name, mode, \ - dbgfs->dir_##parent, priv, \ - &iwl_dbgfs_##name##_ops); \ - if (!(dbgfs->dbgfs_##parent##_files.file_##name)) \ - goto err; \ +#define DEBUGFS_ADD_BOOL(name, parent, ptr) do { \ + struct dentry *__tmp; \ + __tmp = debugfs_create_bool(#name, S_IWUSR | S_IRUSR, \ + parent, ptr); \ + if (IS_ERR(__tmp) || !__tmp) \ + goto err; \ } while (0) -#define DEBUGFS_ADD_BOOL(name, parent, ptr) do { \ - dbgfs->dbgfs_##parent##_files.file_##name = \ - debugfs_create_bool(#name, S_IWUSR | S_IRUSR, \ - dbgfs->dir_##parent, ptr); \ - if (IS_ERR(dbgfs->dbgfs_##parent##_files.file_##name) \ - || !dbgfs->dbgfs_##parent##_files.file_##name) \ - goto err; \ +#define DEBUGFS_ADD_X32(name, parent, ptr) do { \ + struct dentry *__tmp; \ + __tmp = debugfs_create_x32(#name, S_IWUSR | S_IRUSR, \ + parent, ptr); \ + if (IS_ERR(__tmp) || !__tmp) \ + goto err; \ } while (0) -#define DEBUGFS_ADD_X32(name, parent, ptr) do { \ - dbgfs->dbgfs_##parent##_files.file_##name = \ - debugfs_create_x32(#name, S_IRUSR, dbgfs->dir_##parent, ptr); \ - if (IS_ERR(dbgfs->dbgfs_##parent##_files.file_##name) \ - || !dbgfs->dbgfs_##parent##_files.file_##name) \ - goto err; \ -} while (0) - -#define DEBUGFS_REMOVE(name) do { \ - debugfs_remove(name); \ - name = NULL; \ -} while (0); - /* file operation */ #define DEBUGFS_READ_FUNC(name) \ static ssize_t iwl_dbgfs_##name##_read(struct file *file, \ @@ -125,7 +110,7 @@ static ssize_t iwl_dbgfs_tx_statistics_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; char *buf; int pos = 0; @@ -184,7 +169,7 @@ static ssize_t iwl_dbgfs_rx_statistics_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; char *buf; int pos = 0; int cnt; @@ -232,28 +217,28 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, ssize_t ret; int i; int pos = 0; - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; size_t bufsz; /* default is to dump the entire data segment */ - if (!priv->dbgfs->sram_offset && !priv->dbgfs->sram_len) { - priv->dbgfs->sram_offset = 0x800000; + if (!priv->dbgfs_sram_offset && !priv->dbgfs_sram_len) { + priv->dbgfs_sram_offset = 0x800000; if (priv->ucode_type == UCODE_INIT) - priv->dbgfs->sram_len = priv->ucode_init_data.len; + priv->dbgfs_sram_len = priv->ucode_init_data.len; else - priv->dbgfs->sram_len = priv->ucode_data.len; + priv->dbgfs_sram_len = priv->ucode_data.len; } - bufsz = 30 + priv->dbgfs->sram_len * sizeof(char) * 10; + bufsz = 30 + priv->dbgfs_sram_len * sizeof(char) * 10; buf = kmalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", - priv->dbgfs->sram_len); + priv->dbgfs_sram_len); pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n", - priv->dbgfs->sram_offset); - for (i = priv->dbgfs->sram_len; i > 0; i -= 4) { - val = iwl_read_targ_mem(priv, priv->dbgfs->sram_offset + \ - priv->dbgfs->sram_len - i); + priv->dbgfs_sram_offset); + for (i = priv->dbgfs_sram_len; i > 0; i -= 4) { + val = iwl_read_targ_mem(priv, priv->dbgfs_sram_offset + \ + priv->dbgfs_sram_len - i); if (i < 4) { switch (i) { case 1: @@ -293,11 +278,11 @@ static ssize_t iwl_dbgfs_sram_write(struct file *file, return -EFAULT; if (sscanf(buf, "%x,%x", &offset, &len) == 2) { - priv->dbgfs->sram_offset = offset; - priv->dbgfs->sram_len = len; + priv->dbgfs_sram_offset = offset; + priv->dbgfs_sram_len = len; } else { - priv->dbgfs->sram_offset = 0; - priv->dbgfs->sram_len = 0; + priv->dbgfs_sram_offset = 0; + priv->dbgfs_sram_len = 0; } return count; @@ -306,7 +291,7 @@ static ssize_t iwl_dbgfs_sram_write(struct file *file, static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; struct iwl_station_entry *station; int max_sta = priv->hw_params.max_stations; char *buf; @@ -376,7 +361,7 @@ static ssize_t iwl_dbgfs_nvm_read(struct file *file, loff_t *ppos) { ssize_t ret; - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; int pos = 0, ofs = 0, buf_size = 0; const u8 *ptr; char *buf; @@ -420,6 +405,24 @@ static ssize_t iwl_dbgfs_nvm_read(struct file *file, return ret; } +static ssize_t iwl_dbgfs_log_event_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char *buf; + int pos = 0; + ssize_t ret = -ENOMEM; + + ret = pos = priv->cfg->ops->lib->dump_nic_event_log( + priv, true, &buf, true); + if (buf) { + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + } + return ret; +} + static ssize_t iwl_dbgfs_log_event_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) @@ -436,7 +439,8 @@ static ssize_t iwl_dbgfs_log_event_write(struct file *file, if (sscanf(buf, "%d", &event_log_flag) != 1) return -EFAULT; if (event_log_flag == 1) - priv->cfg->ops->lib->dump_nic_event_log(priv, true); + priv->cfg->ops->lib->dump_nic_event_log(priv, true, + NULL, false); return count; } @@ -446,7 +450,7 @@ static ssize_t iwl_dbgfs_log_event_write(struct file *file, static ssize_t iwl_dbgfs_channels_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; struct ieee80211_channel *channels = NULL; const struct ieee80211_supported_band *supp_band = NULL; int pos = 0, i, bufsz = PAGE_SIZE; @@ -519,15 +523,13 @@ static ssize_t iwl_dbgfs_status_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; char buf[512]; int pos = 0; const size_t bufsz = sizeof(buf); pos += scnprintf(buf + pos, bufsz - pos, "STATUS_HCMD_ACTIVE:\t %d\n", test_bit(STATUS_HCMD_ACTIVE, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_HCMD_SYNC_ACTIVE: %d\n", - test_bit(STATUS_HCMD_SYNC_ACTIVE, &priv->status)); pos += scnprintf(buf + pos, bufsz - pos, "STATUS_INT_ENABLED:\t %d\n", test_bit(STATUS_INT_ENABLED, &priv->status)); pos += scnprintf(buf + pos, bufsz - pos, "STATUS_RF_KILL_HW:\t %d\n", @@ -567,7 +569,7 @@ static ssize_t iwl_dbgfs_interrupt_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; int pos = 0; int cnt = 0; char *buf; @@ -654,7 +656,7 @@ static ssize_t iwl_dbgfs_interrupt_write(struct file *file, static ssize_t iwl_dbgfs_qos_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; int pos = 0, i; char buf[256]; const size_t bufsz = sizeof(buf); @@ -677,7 +679,7 @@ static ssize_t iwl_dbgfs_qos_read(struct file *file, char __user *user_buf, static ssize_t iwl_dbgfs_led_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; int pos = 0; char buf[256]; const size_t bufsz = sizeof(buf); @@ -703,7 +705,7 @@ static ssize_t iwl_dbgfs_thermal_throttling_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; struct iwl_tt_mgmt *tt = &priv->thermal_throttle; struct iwl_tt_restriction *restriction; char buf[100]; @@ -763,7 +765,7 @@ static ssize_t iwl_dbgfs_disable_ht40_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; char buf[100]; int pos = 0; const size_t bufsz = sizeof(buf); @@ -811,7 +813,9 @@ static ssize_t iwl_dbgfs_sleep_level_override_write(struct file *file, priv->power_data.debug_sleep_level_override = value; + mutex_lock(&priv->mutex); iwl_power_update_mode(priv, true); + mutex_unlock(&priv->mutex); return count; } @@ -820,7 +824,7 @@ static ssize_t iwl_dbgfs_sleep_level_override_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; char buf[10]; int pos, value; const size_t bufsz = sizeof(buf); @@ -838,7 +842,7 @@ static ssize_t iwl_dbgfs_current_sleep_command_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; char buf[200]; int pos = 0, i; const size_t bufsz = sizeof(buf); @@ -859,7 +863,7 @@ static ssize_t iwl_dbgfs_current_sleep_command_read(struct file *file, } DEBUGFS_READ_WRITE_FILE_OPS(sram); -DEBUGFS_WRITE_FILE_OPS(log_event); +DEBUGFS_READ_WRITE_FILE_OPS(log_event); DEBUGFS_READ_FILE_OPS(nvm); DEBUGFS_READ_FILE_OPS(stations); DEBUGFS_READ_FILE_OPS(channels); @@ -976,7 +980,7 @@ static ssize_t iwl_dbgfs_tx_queue_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; struct iwl_tx_queue *txq; struct iwl_queue *q; char *buf; @@ -1022,7 +1026,7 @@ static ssize_t iwl_dbgfs_rx_queue_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; struct iwl_rx_queue *rxq = &priv->rxq; char buf[256]; int pos = 0; @@ -1063,36 +1067,33 @@ static int iwl_dbgfs_statistics_flag(struct iwl_priv *priv, char *buf, return p; } +static const char ucode_stats_header[] = + "%-32s current acumulative delta max\n"; +static const char ucode_stats_short_format[] = + " %-30s %10u\n"; +static const char ucode_stats_format[] = + " %-30s %10u %10u %10u %10u\n"; static ssize_t iwl_dbgfs_ucode_rx_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; int pos = 0; char *buf; - int bufsz = sizeof(struct statistics_rx_phy) * 20 + - sizeof(struct statistics_rx_non_phy) * 20 + - sizeof(struct statistics_rx_ht_phy) * 20 + 400; + int bufsz = sizeof(struct statistics_rx_phy) * 40 + + sizeof(struct statistics_rx_non_phy) * 40 + + sizeof(struct statistics_rx_ht_phy) * 40 + 400; ssize_t ret; - struct statistics_rx_phy *ofdm, *accum_ofdm; - struct statistics_rx_phy *cck, *accum_cck; + struct statistics_rx_phy *ofdm, *accum_ofdm, *delta_ofdm, *max_ofdm; + struct statistics_rx_phy *cck, *accum_cck, *delta_cck, *max_cck; struct statistics_rx_non_phy *general, *accum_general; - struct statistics_rx_ht_phy *ht, *accum_ht; + struct statistics_rx_non_phy *delta_general, *max_general; + struct statistics_rx_ht_phy *ht, *accum_ht, *delta_ht, *max_ht; if (!iwl_is_alive(priv)) return -EAGAIN; - /* make request to uCode to retrieve statistics information */ - mutex_lock(&priv->mutex); - ret = iwl_send_statistics_request(priv, CMD_SYNC, false); - mutex_unlock(&priv->mutex); - - if (ret) { - IWL_ERR(priv, - "Error sending statistics request: %zd\n", ret); - return -EAGAIN; - } buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) { IWL_ERR(priv, "Can not allocate Buffer\n"); @@ -1111,264 +1112,401 @@ static ssize_t iwl_dbgfs_ucode_rx_stats_read(struct file *file, accum_cck = &priv->accum_statistics.rx.cck; accum_general = &priv->accum_statistics.rx.general; accum_ht = &priv->accum_statistics.rx.ofdm_ht; + delta_ofdm = &priv->delta_statistics.rx.ofdm; + delta_cck = &priv->delta_statistics.rx.cck; + delta_general = &priv->delta_statistics.rx.general; + delta_ht = &priv->delta_statistics.rx.ofdm_ht; + max_ofdm = &priv->max_delta.rx.ofdm; + max_cck = &priv->max_delta.rx.cck; + max_general = &priv->max_delta.rx.general; + max_ht = &priv->max_delta.rx.ofdm_ht; + pos += iwl_dbgfs_statistics_flag(priv, buf, bufsz); - pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Rx - OFDM:\n"); - pos += scnprintf(buf + pos, bufsz - pos, - "\t\t\tcurrent\t\t\taccumulative\n"); - pos += scnprintf(buf + pos, bufsz - pos, "ina_cnt:\t\t%u\t\t\t%u\n", - le32_to_cpu(ofdm->ina_cnt), accum_ofdm->ina_cnt); - pos += scnprintf(buf + pos, bufsz - pos, "fina_cnt:\t\t%u\t\t\t%u\n", - le32_to_cpu(ofdm->fina_cnt), accum_ofdm->fina_cnt); - pos += scnprintf(buf + pos, bufsz - pos, "plcp_err:\t\t%u\t\t\t%u\n", - le32_to_cpu(ofdm->plcp_err), accum_ofdm->plcp_err); - pos += scnprintf(buf + pos, bufsz - pos, "crc32_err:\t\t%u\t\t\t%u\n", - le32_to_cpu(ofdm->crc32_err), accum_ofdm->crc32_err); - pos += scnprintf(buf + pos, bufsz - pos, - "overrun_err:\t\t%u\t\t\t%u\n", + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_header, + "Statistics_Rx - OFDM:"); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "ina_cnt:", le32_to_cpu(ofdm->ina_cnt), + accum_ofdm->ina_cnt, + delta_ofdm->ina_cnt, max_ofdm->ina_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "fina_cnt:", + le32_to_cpu(ofdm->fina_cnt), accum_ofdm->fina_cnt, + delta_ofdm->fina_cnt, max_ofdm->fina_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "plcp_err:", + le32_to_cpu(ofdm->plcp_err), accum_ofdm->plcp_err, + delta_ofdm->plcp_err, max_ofdm->plcp_err); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "crc32_err:", + le32_to_cpu(ofdm->crc32_err), accum_ofdm->crc32_err, + delta_ofdm->crc32_err, max_ofdm->crc32_err); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "overrun_err:", le32_to_cpu(ofdm->overrun_err), - accum_ofdm->overrun_err); - pos += scnprintf(buf + pos, bufsz - pos, - "early_overrun_err:\t%u\t\t\t%u\n", + accum_ofdm->overrun_err, + delta_ofdm->overrun_err, max_ofdm->overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "early_overrun_err:", le32_to_cpu(ofdm->early_overrun_err), - accum_ofdm->early_overrun_err); - pos += scnprintf(buf + pos, bufsz - pos, "crc32_good:\t\t%u\t\t\t%u\n", + accum_ofdm->early_overrun_err, + delta_ofdm->early_overrun_err, + max_ofdm->early_overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "crc32_good:", le32_to_cpu(ofdm->crc32_good), - accum_ofdm->crc32_good); - pos += scnprintf(buf + pos, bufsz - pos, - "false_alarm_cnt:\t%u\t\t\t%u\n", + accum_ofdm->crc32_good, + delta_ofdm->crc32_good, max_ofdm->crc32_good); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "false_alarm_cnt:", le32_to_cpu(ofdm->false_alarm_cnt), - accum_ofdm->false_alarm_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "fina_sync_err_cnt:\t%u\t\t\t%u\n", + accum_ofdm->false_alarm_cnt, + delta_ofdm->false_alarm_cnt, + max_ofdm->false_alarm_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "fina_sync_err_cnt:", le32_to_cpu(ofdm->fina_sync_err_cnt), - accum_ofdm->fina_sync_err_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "sfd_timeout:\t\t%u\t\t\t%u\n", + accum_ofdm->fina_sync_err_cnt, + delta_ofdm->fina_sync_err_cnt, + max_ofdm->fina_sync_err_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "sfd_timeout:", le32_to_cpu(ofdm->sfd_timeout), - accum_ofdm->sfd_timeout); - pos += scnprintf(buf + pos, bufsz - pos, - "fina_timeout:\t\t%u\t\t\t%u\n", + accum_ofdm->sfd_timeout, + delta_ofdm->sfd_timeout, + max_ofdm->sfd_timeout); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "fina_timeout:", le32_to_cpu(ofdm->fina_timeout), - accum_ofdm->fina_timeout); - pos += scnprintf(buf + pos, bufsz - pos, - "unresponded_rts:\t%u\t\t\t%u\n", + accum_ofdm->fina_timeout, + delta_ofdm->fina_timeout, + max_ofdm->fina_timeout); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "unresponded_rts:", le32_to_cpu(ofdm->unresponded_rts), - accum_ofdm->unresponded_rts); - pos += scnprintf(buf + pos, bufsz - pos, - "rxe_frame_lmt_ovrun:\t%u\t\t\t%u\n", + accum_ofdm->unresponded_rts, + delta_ofdm->unresponded_rts, + max_ofdm->unresponded_rts); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "rxe_frame_lmt_ovrun:", le32_to_cpu(ofdm->rxe_frame_limit_overrun), - accum_ofdm->rxe_frame_limit_overrun); - pos += scnprintf(buf + pos, bufsz - pos, - "sent_ack_cnt:\t\t%u\t\t\t%u\n", + accum_ofdm->rxe_frame_limit_overrun, + delta_ofdm->rxe_frame_limit_overrun, + max_ofdm->rxe_frame_limit_overrun); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "sent_ack_cnt:", le32_to_cpu(ofdm->sent_ack_cnt), - accum_ofdm->sent_ack_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "sent_cts_cnt:\t\t%u\t\t\t%u\n", + accum_ofdm->sent_ack_cnt, + delta_ofdm->sent_ack_cnt, + max_ofdm->sent_ack_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "sent_cts_cnt:", le32_to_cpu(ofdm->sent_cts_cnt), - accum_ofdm->sent_cts_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "sent_ba_rsp_cnt:\t%u\t\t\t%u\n", + accum_ofdm->sent_cts_cnt, + delta_ofdm->sent_cts_cnt, max_ofdm->sent_cts_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "sent_ba_rsp_cnt:", le32_to_cpu(ofdm->sent_ba_rsp_cnt), - accum_ofdm->sent_ba_rsp_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "dsp_self_kill:\t\t%u\t\t\t%u\n", + accum_ofdm->sent_ba_rsp_cnt, + delta_ofdm->sent_ba_rsp_cnt, + max_ofdm->sent_ba_rsp_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "dsp_self_kill:", le32_to_cpu(ofdm->dsp_self_kill), - accum_ofdm->dsp_self_kill); - pos += scnprintf(buf + pos, bufsz - pos, - "mh_format_err:\t\t%u\t\t\t%u\n", + accum_ofdm->dsp_self_kill, + delta_ofdm->dsp_self_kill, + max_ofdm->dsp_self_kill); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "mh_format_err:", le32_to_cpu(ofdm->mh_format_err), - accum_ofdm->mh_format_err); - pos += scnprintf(buf + pos, bufsz - pos, - "re_acq_main_rssi_sum:\t%u\t\t\t%u\n", + accum_ofdm->mh_format_err, + delta_ofdm->mh_format_err, + max_ofdm->mh_format_err); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "re_acq_main_rssi_sum:", le32_to_cpu(ofdm->re_acq_main_rssi_sum), - accum_ofdm->re_acq_main_rssi_sum); - - pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Rx - CCK:\n"); - pos += scnprintf(buf + pos, bufsz - pos, - "\t\t\tcurrent\t\t\taccumulative\n"); - pos += scnprintf(buf + pos, bufsz - pos, "ina_cnt:\t\t%u\t\t\t%u\n", - le32_to_cpu(cck->ina_cnt), accum_cck->ina_cnt); - pos += scnprintf(buf + pos, bufsz - pos, "fina_cnt:\t\t%u\t\t\t%u\n", - le32_to_cpu(cck->fina_cnt), accum_cck->fina_cnt); - pos += scnprintf(buf + pos, bufsz - pos, "plcp_err:\t\t%u\t\t\t%u\n", - le32_to_cpu(cck->plcp_err), accum_cck->plcp_err); - pos += scnprintf(buf + pos, bufsz - pos, "crc32_err:\t\t%u\t\t\t%u\n", - le32_to_cpu(cck->crc32_err), accum_cck->crc32_err); - pos += scnprintf(buf + pos, bufsz - pos, - "overrun_err:\t\t%u\t\t\t%u\n", + accum_ofdm->re_acq_main_rssi_sum, + delta_ofdm->re_acq_main_rssi_sum, + max_ofdm->re_acq_main_rssi_sum); + + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_header, + "Statistics_Rx - CCK:"); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "ina_cnt:", + le32_to_cpu(cck->ina_cnt), accum_cck->ina_cnt, + delta_cck->ina_cnt, max_cck->ina_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "fina_cnt:", + le32_to_cpu(cck->fina_cnt), accum_cck->fina_cnt, + delta_cck->fina_cnt, max_cck->fina_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "plcp_err:", + le32_to_cpu(cck->plcp_err), accum_cck->plcp_err, + delta_cck->plcp_err, max_cck->plcp_err); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "crc32_err:", + le32_to_cpu(cck->crc32_err), accum_cck->crc32_err, + delta_cck->crc32_err, max_cck->crc32_err); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "overrun_err:", le32_to_cpu(cck->overrun_err), - accum_cck->overrun_err); - pos += scnprintf(buf + pos, bufsz - pos, - "early_overrun_err:\t%u\t\t\t%u\n", + accum_cck->overrun_err, + delta_cck->overrun_err, max_cck->overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "early_overrun_err:", le32_to_cpu(cck->early_overrun_err), - accum_cck->early_overrun_err); - pos += scnprintf(buf + pos, bufsz - pos, "crc32_good:\t\t%u\t\t\t%u\n", - le32_to_cpu(cck->crc32_good), accum_cck->crc32_good); - pos += scnprintf(buf + pos, bufsz - pos, - "false_alarm_cnt:\t%u\t\t\t%u\n", + accum_cck->early_overrun_err, + delta_cck->early_overrun_err, + max_cck->early_overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "crc32_good:", + le32_to_cpu(cck->crc32_good), accum_cck->crc32_good, + delta_cck->crc32_good, + max_cck->crc32_good); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "false_alarm_cnt:", le32_to_cpu(cck->false_alarm_cnt), - accum_cck->false_alarm_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "fina_sync_err_cnt:\t%u\t\t\t%u\n", + accum_cck->false_alarm_cnt, + delta_cck->false_alarm_cnt, max_cck->false_alarm_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "fina_sync_err_cnt:", le32_to_cpu(cck->fina_sync_err_cnt), - accum_cck->fina_sync_err_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "sfd_timeout:\t\t%u\t\t\t%u\n", + accum_cck->fina_sync_err_cnt, + delta_cck->fina_sync_err_cnt, + max_cck->fina_sync_err_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "sfd_timeout:", le32_to_cpu(cck->sfd_timeout), - accum_cck->sfd_timeout); - pos += scnprintf(buf + pos, bufsz - pos, - "fina_timeout:\t\t%u\t\t\t%u\n", + accum_cck->sfd_timeout, + delta_cck->sfd_timeout, max_cck->sfd_timeout); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "fina_timeout:", le32_to_cpu(cck->fina_timeout), - accum_cck->fina_timeout); - pos += scnprintf(buf + pos, bufsz - pos, - "unresponded_rts:\t%u\t\t\t%u\n", + accum_cck->fina_timeout, + delta_cck->fina_timeout, max_cck->fina_timeout); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "unresponded_rts:", le32_to_cpu(cck->unresponded_rts), - accum_cck->unresponded_rts); - pos += scnprintf(buf + pos, bufsz - pos, - "rxe_frame_lmt_ovrun:\t%u\t\t\t%u\n", + accum_cck->unresponded_rts, + delta_cck->unresponded_rts, + max_cck->unresponded_rts); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "rxe_frame_lmt_ovrun:", le32_to_cpu(cck->rxe_frame_limit_overrun), - accum_cck->rxe_frame_limit_overrun); - pos += scnprintf(buf + pos, bufsz - pos, - "sent_ack_cnt:\t\t%u\t\t\t%u\n", + accum_cck->rxe_frame_limit_overrun, + delta_cck->rxe_frame_limit_overrun, + max_cck->rxe_frame_limit_overrun); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "sent_ack_cnt:", le32_to_cpu(cck->sent_ack_cnt), - accum_cck->sent_ack_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "sent_cts_cnt:\t\t%u\t\t\t%u\n", + accum_cck->sent_ack_cnt, + delta_cck->sent_ack_cnt, + max_cck->sent_ack_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "sent_cts_cnt:", le32_to_cpu(cck->sent_cts_cnt), - accum_cck->sent_cts_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "sent_ba_rsp_cnt:\t%u\t\t\t%u\n", + accum_cck->sent_cts_cnt, + delta_cck->sent_cts_cnt, + max_cck->sent_cts_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "sent_ba_rsp_cnt:", le32_to_cpu(cck->sent_ba_rsp_cnt), - accum_cck->sent_ba_rsp_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "dsp_self_kill:\t\t%u\t\t\t%u\n", + accum_cck->sent_ba_rsp_cnt, + delta_cck->sent_ba_rsp_cnt, + max_cck->sent_ba_rsp_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "dsp_self_kill:", le32_to_cpu(cck->dsp_self_kill), - accum_cck->dsp_self_kill); - pos += scnprintf(buf + pos, bufsz - pos, - "mh_format_err:\t\t%u\t\t\t%u\n", + accum_cck->dsp_self_kill, + delta_cck->dsp_self_kill, + max_cck->dsp_self_kill); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "mh_format_err:", le32_to_cpu(cck->mh_format_err), - accum_cck->mh_format_err); - pos += scnprintf(buf + pos, bufsz - pos, - "re_acq_main_rssi_sum:\t%u\t\t\t%u\n", + accum_cck->mh_format_err, + delta_cck->mh_format_err, max_cck->mh_format_err); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "re_acq_main_rssi_sum:", le32_to_cpu(cck->re_acq_main_rssi_sum), - accum_cck->re_acq_main_rssi_sum); - - pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Rx - GENERAL:\n"); - pos += scnprintf(buf + pos, bufsz - pos, - "\t\t\tcurrent\t\t\taccumulative\n"); - pos += scnprintf(buf + pos, bufsz - pos, "bogus_cts:\t\t%u\t\t\t%u\n", + accum_cck->re_acq_main_rssi_sum, + delta_cck->re_acq_main_rssi_sum, + max_cck->re_acq_main_rssi_sum); + + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_header, + "Statistics_Rx - GENERAL:"); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "bogus_cts:", le32_to_cpu(general->bogus_cts), - accum_general->bogus_cts); - pos += scnprintf(buf + pos, bufsz - pos, "bogus_ack:\t\t%u\t\t\t%u\n", + accum_general->bogus_cts, + delta_general->bogus_cts, max_general->bogus_cts); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "bogus_ack:", le32_to_cpu(general->bogus_ack), - accum_general->bogus_ack); - pos += scnprintf(buf + pos, bufsz - pos, - "non_bssid_frames:\t%u\t\t\t%u\n", + accum_general->bogus_ack, + delta_general->bogus_ack, max_general->bogus_ack); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "non_bssid_frames:", le32_to_cpu(general->non_bssid_frames), - accum_general->non_bssid_frames); - pos += scnprintf(buf + pos, bufsz - pos, - "filtered_frames:\t%u\t\t\t%u\n", + accum_general->non_bssid_frames, + delta_general->non_bssid_frames, + max_general->non_bssid_frames); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "filtered_frames:", le32_to_cpu(general->filtered_frames), - accum_general->filtered_frames); - pos += scnprintf(buf + pos, bufsz - pos, - "non_channel_beacons:\t%u\t\t\t%u\n", + accum_general->filtered_frames, + delta_general->filtered_frames, + max_general->filtered_frames); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "non_channel_beacons:", le32_to_cpu(general->non_channel_beacons), - accum_general->non_channel_beacons); - pos += scnprintf(buf + pos, bufsz - pos, - "channel_beacons:\t%u\t\t\t%u\n", + accum_general->non_channel_beacons, + delta_general->non_channel_beacons, + max_general->non_channel_beacons); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "channel_beacons:", le32_to_cpu(general->channel_beacons), - accum_general->channel_beacons); - pos += scnprintf(buf + pos, bufsz - pos, - "num_missed_bcon:\t%u\t\t\t%u\n", + accum_general->channel_beacons, + delta_general->channel_beacons, + max_general->channel_beacons); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "num_missed_bcon:", le32_to_cpu(general->num_missed_bcon), - accum_general->num_missed_bcon); - pos += scnprintf(buf + pos, bufsz - pos, - "adc_rx_saturation_time:\t%u\t\t\t%u\n", + accum_general->num_missed_bcon, + delta_general->num_missed_bcon, + max_general->num_missed_bcon); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "adc_rx_saturation_time:", le32_to_cpu(general->adc_rx_saturation_time), - accum_general->adc_rx_saturation_time); - pos += scnprintf(buf + pos, bufsz - pos, - "ina_detect_search_tm:\t%u\t\t\t%u\n", + accum_general->adc_rx_saturation_time, + delta_general->adc_rx_saturation_time, + max_general->adc_rx_saturation_time); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "ina_detect_search_tm:", le32_to_cpu(general->ina_detection_search_time), - accum_general->ina_detection_search_time); - pos += scnprintf(buf + pos, bufsz - pos, - "beacon_silence_rssi_a:\t%u\t\t\t%u\n", + accum_general->ina_detection_search_time, + delta_general->ina_detection_search_time, + max_general->ina_detection_search_time); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "beacon_silence_rssi_a:", le32_to_cpu(general->beacon_silence_rssi_a), - accum_general->beacon_silence_rssi_a); - pos += scnprintf(buf + pos, bufsz - pos, - "beacon_silence_rssi_b:\t%u\t\t\t%u\n", + accum_general->beacon_silence_rssi_a, + delta_general->beacon_silence_rssi_a, + max_general->beacon_silence_rssi_a); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "beacon_silence_rssi_b:", le32_to_cpu(general->beacon_silence_rssi_b), - accum_general->beacon_silence_rssi_b); - pos += scnprintf(buf + pos, bufsz - pos, - "beacon_silence_rssi_c:\t%u\t\t\t%u\n", + accum_general->beacon_silence_rssi_b, + delta_general->beacon_silence_rssi_b, + max_general->beacon_silence_rssi_b); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "beacon_silence_rssi_c:", le32_to_cpu(general->beacon_silence_rssi_c), - accum_general->beacon_silence_rssi_c); - pos += scnprintf(buf + pos, bufsz - pos, - "interference_data_flag:\t%u\t\t\t%u\n", + accum_general->beacon_silence_rssi_c, + delta_general->beacon_silence_rssi_c, + max_general->beacon_silence_rssi_c); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "interference_data_flag:", le32_to_cpu(general->interference_data_flag), - accum_general->interference_data_flag); - pos += scnprintf(buf + pos, bufsz - pos, - "channel_load:\t\t%u\t\t\t%u\n", + accum_general->interference_data_flag, + delta_general->interference_data_flag, + max_general->interference_data_flag); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "channel_load:", le32_to_cpu(general->channel_load), - accum_general->channel_load); - pos += scnprintf(buf + pos, bufsz - pos, - "dsp_false_alarms:\t%u\t\t\t%u\n", + accum_general->channel_load, + delta_general->channel_load, + max_general->channel_load); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "dsp_false_alarms:", le32_to_cpu(general->dsp_false_alarms), - accum_general->dsp_false_alarms); - pos += scnprintf(buf + pos, bufsz - pos, - "beacon_rssi_a:\t\t%u\t\t\t%u\n", + accum_general->dsp_false_alarms, + delta_general->dsp_false_alarms, + max_general->dsp_false_alarms); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "beacon_rssi_a:", le32_to_cpu(general->beacon_rssi_a), - accum_general->beacon_rssi_a); - pos += scnprintf(buf + pos, bufsz - pos, - "beacon_rssi_b:\t\t%u\t\t\t%u\n", + accum_general->beacon_rssi_a, + delta_general->beacon_rssi_a, + max_general->beacon_rssi_a); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "beacon_rssi_b:", le32_to_cpu(general->beacon_rssi_b), - accum_general->beacon_rssi_b); - pos += scnprintf(buf + pos, bufsz - pos, - "beacon_rssi_c:\t\t%u\t\t\t%u\n", + accum_general->beacon_rssi_b, + delta_general->beacon_rssi_b, + max_general->beacon_rssi_b); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "beacon_rssi_c:", le32_to_cpu(general->beacon_rssi_c), - accum_general->beacon_rssi_c); - pos += scnprintf(buf + pos, bufsz - pos, - "beacon_energy_a:\t%u\t\t\t%u\n", + accum_general->beacon_rssi_c, + delta_general->beacon_rssi_c, + max_general->beacon_rssi_c); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "beacon_energy_a:", le32_to_cpu(general->beacon_energy_a), - accum_general->beacon_energy_a); - pos += scnprintf(buf + pos, bufsz - pos, - "beacon_energy_b:\t%u\t\t\t%u\n", + accum_general->beacon_energy_a, + delta_general->beacon_energy_a, + max_general->beacon_energy_a); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "beacon_energy_b:", le32_to_cpu(general->beacon_energy_b), - accum_general->beacon_energy_b); - pos += scnprintf(buf + pos, bufsz - pos, - "beacon_energy_c:\t%u\t\t\t%u\n", + accum_general->beacon_energy_b, + delta_general->beacon_energy_b, + max_general->beacon_energy_b); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "beacon_energy_c:", le32_to_cpu(general->beacon_energy_c), - accum_general->beacon_energy_c); + accum_general->beacon_energy_c, + delta_general->beacon_energy_c, + max_general->beacon_energy_c); pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Rx - OFDM_HT:\n"); - pos += scnprintf(buf + pos, bufsz - pos, - "\t\t\tcurrent\t\t\taccumulative\n"); - pos += scnprintf(buf + pos, bufsz - pos, "plcp_err:\t\t%u\t\t\t%u\n", - le32_to_cpu(ht->plcp_err), accum_ht->plcp_err); - pos += scnprintf(buf + pos, bufsz - pos, - "overrun_err:\t\t%u\t\t\t%u\n", - le32_to_cpu(ht->overrun_err), accum_ht->overrun_err); - pos += scnprintf(buf + pos, bufsz - pos, - "early_overrun_err:\t%u\t\t\t%u\n", + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_header, + "Statistics_Rx - OFDM_HT:"); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "plcp_err:", + le32_to_cpu(ht->plcp_err), accum_ht->plcp_err, + delta_ht->plcp_err, max_ht->plcp_err); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "overrun_err:", + le32_to_cpu(ht->overrun_err), accum_ht->overrun_err, + delta_ht->overrun_err, max_ht->overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "early_overrun_err:", le32_to_cpu(ht->early_overrun_err), - accum_ht->early_overrun_err); - pos += scnprintf(buf + pos, bufsz - pos, "crc32_good:\t\t%u\t\t\t%u\n", - le32_to_cpu(ht->crc32_good), accum_ht->crc32_good); - pos += scnprintf(buf + pos, bufsz - pos, "crc32_err:\t\t%u\t\t\t%u\n", - le32_to_cpu(ht->crc32_err), accum_ht->crc32_err); - pos += scnprintf(buf + pos, bufsz - pos, - "mh_format_err:\t\t%u\t\t\t%u\n", + accum_ht->early_overrun_err, + delta_ht->early_overrun_err, + max_ht->early_overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "crc32_good:", + le32_to_cpu(ht->crc32_good), accum_ht->crc32_good, + delta_ht->crc32_good, max_ht->crc32_good); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "crc32_err:", + le32_to_cpu(ht->crc32_err), accum_ht->crc32_err, + delta_ht->crc32_err, max_ht->crc32_err); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "mh_format_err:", le32_to_cpu(ht->mh_format_err), - accum_ht->mh_format_err); - pos += scnprintf(buf + pos, bufsz - pos, - "agg_crc32_good:\t\t%u\t\t\t%u\n", + accum_ht->mh_format_err, + delta_ht->mh_format_err, max_ht->mh_format_err); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "agg_crc32_good:", le32_to_cpu(ht->agg_crc32_good), - accum_ht->agg_crc32_good); - pos += scnprintf(buf + pos, bufsz - pos, - "agg_mpdu_cnt:\t\t%u\t\t\t%u\n", + accum_ht->agg_crc32_good, + delta_ht->agg_crc32_good, max_ht->agg_crc32_good); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "agg_mpdu_cnt:", le32_to_cpu(ht->agg_mpdu_cnt), - accum_ht->agg_mpdu_cnt); - pos += scnprintf(buf + pos, bufsz - pos, "agg_cnt:\t\t%u\t\t\t%u\n", - le32_to_cpu(ht->agg_cnt), accum_ht->agg_cnt); + accum_ht->agg_mpdu_cnt, + delta_ht->agg_mpdu_cnt, max_ht->agg_mpdu_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "agg_cnt:", + le32_to_cpu(ht->agg_cnt), accum_ht->agg_cnt, + delta_ht->agg_cnt, max_ht->agg_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "unsupport_mcs:", + le32_to_cpu(ht->unsupport_mcs), + accum_ht->unsupport_mcs, + delta_ht->unsupport_mcs, max_ht->unsupport_mcs); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); @@ -1379,26 +1517,16 @@ static ssize_t iwl_dbgfs_ucode_tx_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; int pos = 0; char *buf; - int bufsz = (sizeof(struct statistics_tx) * 24) + 250; + int bufsz = (sizeof(struct statistics_tx) * 48) + 250; ssize_t ret; - struct statistics_tx *tx, *accum_tx; + struct statistics_tx *tx, *accum_tx, *delta_tx, *max_tx; if (!iwl_is_alive(priv)) return -EAGAIN; - /* make request to uCode to retrieve statistics information */ - mutex_lock(&priv->mutex); - ret = iwl_send_statistics_request(priv, CMD_SYNC, false); - mutex_unlock(&priv->mutex); - - if (ret) { - IWL_ERR(priv, - "Error sending statistics request: %zd\n", ret); - return -EAGAIN; - } buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) { IWL_ERR(priv, "Can not allocate Buffer\n"); @@ -1411,106 +1539,148 @@ static ssize_t iwl_dbgfs_ucode_tx_stats_read(struct file *file, */ tx = &priv->statistics.tx; accum_tx = &priv->accum_statistics.tx; + delta_tx = &priv->delta_statistics.tx; + max_tx = &priv->max_delta.tx; pos += iwl_dbgfs_statistics_flag(priv, buf, bufsz); - pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Tx:\n"); - pos += scnprintf(buf + pos, bufsz - pos, - "\t\t\tcurrent\t\t\taccumulative\n"); - pos += scnprintf(buf + pos, bufsz - pos, "preamble:\t\t\t%u\t\t\t%u\n", + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_header, + "Statistics_Tx:"); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "preamble:", le32_to_cpu(tx->preamble_cnt), - accum_tx->preamble_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "rx_detected_cnt:\t\t%u\t\t\t%u\n", + accum_tx->preamble_cnt, + delta_tx->preamble_cnt, max_tx->preamble_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "rx_detected_cnt:", le32_to_cpu(tx->rx_detected_cnt), - accum_tx->rx_detected_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "bt_prio_defer_cnt:\t\t%u\t\t\t%u\n", + accum_tx->rx_detected_cnt, + delta_tx->rx_detected_cnt, max_tx->rx_detected_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "bt_prio_defer_cnt:", le32_to_cpu(tx->bt_prio_defer_cnt), - accum_tx->bt_prio_defer_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "bt_prio_kill_cnt:\t\t%u\t\t\t%u\n", + accum_tx->bt_prio_defer_cnt, + delta_tx->bt_prio_defer_cnt, + max_tx->bt_prio_defer_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "bt_prio_kill_cnt:", le32_to_cpu(tx->bt_prio_kill_cnt), - accum_tx->bt_prio_kill_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "few_bytes_cnt:\t\t\t%u\t\t\t%u\n", + accum_tx->bt_prio_kill_cnt, + delta_tx->bt_prio_kill_cnt, + max_tx->bt_prio_kill_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "few_bytes_cnt:", le32_to_cpu(tx->few_bytes_cnt), - accum_tx->few_bytes_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "cts_timeout:\t\t\t%u\t\t\t%u\n", - le32_to_cpu(tx->cts_timeout), accum_tx->cts_timeout); - pos += scnprintf(buf + pos, bufsz - pos, - "ack_timeout:\t\t\t%u\t\t\t%u\n", + accum_tx->few_bytes_cnt, + delta_tx->few_bytes_cnt, max_tx->few_bytes_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "cts_timeout:", + le32_to_cpu(tx->cts_timeout), accum_tx->cts_timeout, + delta_tx->cts_timeout, max_tx->cts_timeout); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "ack_timeout:", le32_to_cpu(tx->ack_timeout), - accum_tx->ack_timeout); - pos += scnprintf(buf + pos, bufsz - pos, - "expected_ack_cnt:\t\t%u\t\t\t%u\n", + accum_tx->ack_timeout, + delta_tx->ack_timeout, max_tx->ack_timeout); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "expected_ack_cnt:", le32_to_cpu(tx->expected_ack_cnt), - accum_tx->expected_ack_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "actual_ack_cnt:\t\t\t%u\t\t\t%u\n", + accum_tx->expected_ack_cnt, + delta_tx->expected_ack_cnt, + max_tx->expected_ack_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "actual_ack_cnt:", le32_to_cpu(tx->actual_ack_cnt), - accum_tx->actual_ack_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "dump_msdu_cnt:\t\t\t%u\t\t\t%u\n", + accum_tx->actual_ack_cnt, + delta_tx->actual_ack_cnt, + max_tx->actual_ack_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "dump_msdu_cnt:", le32_to_cpu(tx->dump_msdu_cnt), - accum_tx->dump_msdu_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "abort_nxt_frame_mismatch:" - "\t%u\t\t\t%u\n", + accum_tx->dump_msdu_cnt, + delta_tx->dump_msdu_cnt, + max_tx->dump_msdu_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "abort_nxt_frame_mismatch:", le32_to_cpu(tx->burst_abort_next_frame_mismatch_cnt), - accum_tx->burst_abort_next_frame_mismatch_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "abort_missing_nxt_frame:" - "\t%u\t\t\t%u\n", + accum_tx->burst_abort_next_frame_mismatch_cnt, + delta_tx->burst_abort_next_frame_mismatch_cnt, + max_tx->burst_abort_next_frame_mismatch_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "abort_missing_nxt_frame:", le32_to_cpu(tx->burst_abort_missing_next_frame_cnt), - accum_tx->burst_abort_missing_next_frame_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "cts_timeout_collision:\t\t%u\t\t\t%u\n", + accum_tx->burst_abort_missing_next_frame_cnt, + delta_tx->burst_abort_missing_next_frame_cnt, + max_tx->burst_abort_missing_next_frame_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "cts_timeout_collision:", le32_to_cpu(tx->cts_timeout_collision), - accum_tx->cts_timeout_collision); - pos += scnprintf(buf + pos, bufsz - pos, - "ack_ba_timeout_collision:\t%u\t\t\t%u\n", + accum_tx->cts_timeout_collision, + delta_tx->cts_timeout_collision, + max_tx->cts_timeout_collision); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "ack_ba_timeout_collision:", le32_to_cpu(tx->ack_or_ba_timeout_collision), - accum_tx->ack_or_ba_timeout_collision); - pos += scnprintf(buf + pos, bufsz - pos, - "agg ba_timeout:\t\t\t%u\t\t\t%u\n", + accum_tx->ack_or_ba_timeout_collision, + delta_tx->ack_or_ba_timeout_collision, + max_tx->ack_or_ba_timeout_collision); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "agg ba_timeout:", le32_to_cpu(tx->agg.ba_timeout), - accum_tx->agg.ba_timeout); - pos += scnprintf(buf + pos, bufsz - pos, - "agg ba_resched_frames:\t\t%u\t\t\t%u\n", + accum_tx->agg.ba_timeout, + delta_tx->agg.ba_timeout, + max_tx->agg.ba_timeout); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "agg ba_resched_frames:", le32_to_cpu(tx->agg.ba_reschedule_frames), - accum_tx->agg.ba_reschedule_frames); - pos += scnprintf(buf + pos, bufsz - pos, - "agg scd_query_agg_frame:\t%u\t\t\t%u\n", + accum_tx->agg.ba_reschedule_frames, + delta_tx->agg.ba_reschedule_frames, + max_tx->agg.ba_reschedule_frames); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "agg scd_query_agg_frame:", le32_to_cpu(tx->agg.scd_query_agg_frame_cnt), - accum_tx->agg.scd_query_agg_frame_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "agg scd_query_no_agg:\t\t%u\t\t\t%u\n", + accum_tx->agg.scd_query_agg_frame_cnt, + delta_tx->agg.scd_query_agg_frame_cnt, + max_tx->agg.scd_query_agg_frame_cnt); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "agg scd_query_no_agg:", le32_to_cpu(tx->agg.scd_query_no_agg), - accum_tx->agg.scd_query_no_agg); - pos += scnprintf(buf + pos, bufsz - pos, - "agg scd_query_agg:\t\t%u\t\t\t%u\n", + accum_tx->agg.scd_query_no_agg, + delta_tx->agg.scd_query_no_agg, + max_tx->agg.scd_query_no_agg); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "agg scd_query_agg:", le32_to_cpu(tx->agg.scd_query_agg), - accum_tx->agg.scd_query_agg); - pos += scnprintf(buf + pos, bufsz - pos, - "agg scd_query_mismatch:\t\t%u\t\t\t%u\n", + accum_tx->agg.scd_query_agg, + delta_tx->agg.scd_query_agg, + max_tx->agg.scd_query_agg); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "agg scd_query_mismatch:", le32_to_cpu(tx->agg.scd_query_mismatch), - accum_tx->agg.scd_query_mismatch); - pos += scnprintf(buf + pos, bufsz - pos, - "agg frame_not_ready:\t\t%u\t\t\t%u\n", + accum_tx->agg.scd_query_mismatch, + delta_tx->agg.scd_query_mismatch, + max_tx->agg.scd_query_mismatch); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "agg frame_not_ready:", le32_to_cpu(tx->agg.frame_not_ready), - accum_tx->agg.frame_not_ready); - pos += scnprintf(buf + pos, bufsz - pos, - "agg underrun:\t\t\t%u\t\t\t%u\n", + accum_tx->agg.frame_not_ready, + delta_tx->agg.frame_not_ready, + max_tx->agg.frame_not_ready); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "agg underrun:", le32_to_cpu(tx->agg.underrun), - accum_tx->agg.underrun); - pos += scnprintf(buf + pos, bufsz - pos, - "agg bt_prio_kill:\t\t%u\t\t\t%u\n", + accum_tx->agg.underrun, + delta_tx->agg.underrun, max_tx->agg.underrun); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "agg bt_prio_kill:", le32_to_cpu(tx->agg.bt_prio_kill), - accum_tx->agg.bt_prio_kill); - pos += scnprintf(buf + pos, bufsz - pos, - "agg rx_ba_rsp_cnt:\t\t%u\t\t\t%u\n", + accum_tx->agg.bt_prio_kill, + delta_tx->agg.bt_prio_kill, + max_tx->agg.bt_prio_kill); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "agg rx_ba_rsp_cnt:", le32_to_cpu(tx->agg.rx_ba_rsp_cnt), - accum_tx->agg.rx_ba_rsp_cnt); + accum_tx->agg.rx_ba_rsp_cnt, + delta_tx->agg.rx_ba_rsp_cnt, + max_tx->agg.rx_ba_rsp_cnt); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); @@ -1521,28 +1691,19 @@ static ssize_t iwl_dbgfs_ucode_general_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; int pos = 0; char *buf; - int bufsz = sizeof(struct statistics_general) * 4 + 250; + int bufsz = sizeof(struct statistics_general) * 10 + 300; ssize_t ret; struct statistics_general *general, *accum_general; - struct statistics_dbg *dbg, *accum_dbg; - struct statistics_div *div, *accum_div; + struct statistics_general *delta_general, *max_general; + struct statistics_dbg *dbg, *accum_dbg, *delta_dbg, *max_dbg; + struct statistics_div *div, *accum_div, *delta_div, *max_div; if (!iwl_is_alive(priv)) return -EAGAIN; - /* make request to uCode to retrieve statistics information */ - mutex_lock(&priv->mutex); - ret = iwl_send_statistics_request(priv, CMD_SYNC, false); - mutex_unlock(&priv->mutex); - - if (ret) { - IWL_ERR(priv, - "Error sending statistics request: %zd\n", ret); - return -EAGAIN; - } buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) { IWL_ERR(priv, "Can not allocate Buffer\n"); @@ -1557,52 +1718,78 @@ static ssize_t iwl_dbgfs_ucode_general_stats_read(struct file *file, dbg = &priv->statistics.general.dbg; div = &priv->statistics.general.div; accum_general = &priv->accum_statistics.general; + delta_general = &priv->delta_statistics.general; + max_general = &priv->max_delta.general; accum_dbg = &priv->accum_statistics.general.dbg; + delta_dbg = &priv->delta_statistics.general.dbg; + max_dbg = &priv->max_delta.general.dbg; accum_div = &priv->accum_statistics.general.div; + delta_div = &priv->delta_statistics.general.div; + max_div = &priv->max_delta.general.div; pos += iwl_dbgfs_statistics_flag(priv, buf, bufsz); - pos += scnprintf(buf + pos, bufsz - pos, "Statistics_General:\n"); - pos += scnprintf(buf + pos, bufsz - pos, - "\t\t\tcurrent\t\t\taccumulative\n"); - pos += scnprintf(buf + pos, bufsz - pos, "temperature:\t\t\t%u\n", + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_header, + "Statistics_General:"); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_short_format, + "temperature:", le32_to_cpu(general->temperature)); - pos += scnprintf(buf + pos, bufsz - pos, "temperature_m:\t\t\t%u\n", + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_short_format, + "temperature_m:", le32_to_cpu(general->temperature_m)); - pos += scnprintf(buf + pos, bufsz - pos, - "burst_check:\t\t\t%u\t\t\t%u\n", + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "burst_check:", le32_to_cpu(dbg->burst_check), - accum_dbg->burst_check); - pos += scnprintf(buf + pos, bufsz - pos, - "burst_count:\t\t\t%u\t\t\t%u\n", + accum_dbg->burst_check, + delta_dbg->burst_check, max_dbg->burst_check); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "burst_count:", le32_to_cpu(dbg->burst_count), - accum_dbg->burst_count); - pos += scnprintf(buf + pos, bufsz - pos, - "sleep_time:\t\t\t%u\t\t\t%u\n", + accum_dbg->burst_count, + delta_dbg->burst_count, max_dbg->burst_count); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "sleep_time:", le32_to_cpu(general->sleep_time), - accum_general->sleep_time); - pos += scnprintf(buf + pos, bufsz - pos, - "slots_out:\t\t\t%u\t\t\t%u\n", + accum_general->sleep_time, + delta_general->sleep_time, max_general->sleep_time); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "slots_out:", le32_to_cpu(general->slots_out), - accum_general->slots_out); - pos += scnprintf(buf + pos, bufsz - pos, - "slots_idle:\t\t\t%u\t\t\t%u\n", + accum_general->slots_out, + delta_general->slots_out, max_general->slots_out); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "slots_idle:", le32_to_cpu(general->slots_idle), - accum_general->slots_idle); + accum_general->slots_idle, + delta_general->slots_idle, max_general->slots_idle); pos += scnprintf(buf + pos, bufsz - pos, "ttl_timestamp:\t\t\t%u\n", le32_to_cpu(general->ttl_timestamp)); - pos += scnprintf(buf + pos, bufsz - pos, "tx_on_a:\t\t\t%u\t\t\t%u\n", - le32_to_cpu(div->tx_on_a), accum_div->tx_on_a); - pos += scnprintf(buf + pos, bufsz - pos, "tx_on_b:\t\t\t%u\t\t\t%u\n", - le32_to_cpu(div->tx_on_b), accum_div->tx_on_b); - pos += scnprintf(buf + pos, bufsz - pos, - "exec_time:\t\t\t%u\t\t\t%u\n", - le32_to_cpu(div->exec_time), accum_div->exec_time); - pos += scnprintf(buf + pos, bufsz - pos, - "probe_time:\t\t\t%u\t\t\t%u\n", - le32_to_cpu(div->probe_time), accum_div->probe_time); - pos += scnprintf(buf + pos, bufsz - pos, - "rx_enable_counter:\t\t%u\t\t\t%u\n", + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "tx_on_a:", + le32_to_cpu(div->tx_on_a), accum_div->tx_on_a, + delta_div->tx_on_a, max_div->tx_on_a); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "tx_on_b:", + le32_to_cpu(div->tx_on_b), accum_div->tx_on_b, + delta_div->tx_on_b, max_div->tx_on_b); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "exec_time:", + le32_to_cpu(div->exec_time), accum_div->exec_time, + delta_div->exec_time, max_div->exec_time); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "probe_time:", + le32_to_cpu(div->probe_time), accum_div->probe_time, + delta_div->probe_time, max_div->probe_time); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "rx_enable_counter:", le32_to_cpu(general->rx_enable_counter), - accum_general->rx_enable_counter); + accum_general->rx_enable_counter, + delta_general->rx_enable_counter, + max_general->rx_enable_counter); + pos += scnprintf(buf + pos, bufsz - pos, ucode_stats_format, + "num_of_sos_states:", + le32_to_cpu(general->num_of_sos_states), + accum_general->num_of_sos_states, + delta_general->num_of_sos_states, + max_general->num_of_sos_states); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; @@ -1612,7 +1799,7 @@ static ssize_t iwl_dbgfs_sensitivity_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; int pos = 0; int cnt = 0; char *buf; @@ -1693,7 +1880,7 @@ static ssize_t iwl_dbgfs_chain_noise_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; int pos = 0; int cnt = 0; char *buf; @@ -1751,26 +1938,15 @@ static ssize_t iwl_dbgfs_tx_power_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; char buf[128]; int pos = 0; - ssize_t ret; const size_t bufsz = sizeof(buf); struct statistics_tx *tx; if (!iwl_is_alive(priv)) pos += scnprintf(buf + pos, bufsz - pos, "N/A\n"); else { - /* make request to uCode to retrieve statistics information */ - mutex_lock(&priv->mutex); - ret = iwl_send_statistics_request(priv, CMD_SYNC, false); - mutex_unlock(&priv->mutex); - - if (ret) { - IWL_ERR(priv, "Error sending statistics request: %zd\n", - ret); - return -EAGAIN; - } tx = &priv->statistics.tx; if (tx->tx_power.ant_a || tx->tx_power.ant_b || @@ -1802,7 +1978,7 @@ static ssize_t iwl_dbgfs_power_save_status_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; char buf[60]; int pos = 0; const size_t bufsz = sizeof(buf); @@ -1845,6 +2021,262 @@ static ssize_t iwl_dbgfs_clear_ucode_statistics_write(struct file *file, return count; } +static ssize_t iwl_dbgfs_csr_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int csr; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &csr) != 1) + return -EFAULT; + + if (priv->cfg->ops->lib->dump_csr) + priv->cfg->ops->lib->dump_csr(priv); + + return count; +} + +static ssize_t iwl_dbgfs_ucode_tracing_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + int pos = 0; + char buf[128]; + const size_t bufsz = sizeof(buf); + ssize_t ret; + + pos += scnprintf(buf + pos, bufsz - pos, "ucode trace timer is %s\n", + priv->event_log.ucode_trace ? "On" : "Off"); + pos += scnprintf(buf + pos, bufsz - pos, "non_wraps_count:\t\t %u\n", + priv->event_log.non_wraps_count); + pos += scnprintf(buf + pos, bufsz - pos, "wraps_once_count:\t\t %u\n", + priv->event_log.wraps_once_count); + pos += scnprintf(buf + pos, bufsz - pos, "wraps_more_count:\t\t %u\n", + priv->event_log.wraps_more_count); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + return ret; +} + +static ssize_t iwl_dbgfs_ucode_tracing_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int trace; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &trace) != 1) + return -EFAULT; + + if (trace) { + priv->event_log.ucode_trace = true; + /* schedule the ucode timer to occur in UCODE_TRACE_PERIOD */ + mod_timer(&priv->ucode_trace, + jiffies + msecs_to_jiffies(UCODE_TRACE_PERIOD)); + } else { + priv->event_log.ucode_trace = false; + del_timer_sync(&priv->ucode_trace); + } + + return count; +} + +static ssize_t iwl_dbgfs_fh_reg_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + char *buf; + int pos = 0; + ssize_t ret = -EFAULT; + + if (priv->cfg->ops->lib->dump_fh) { + ret = pos = priv->cfg->ops->lib->dump_fh(priv, &buf, true); + if (buf) { + ret = simple_read_from_buffer(user_buf, + count, ppos, buf, pos); + kfree(buf); + } + } + + return ret; +} + +static ssize_t iwl_dbgfs_missed_beacon_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int pos = 0; + char buf[12]; + const size_t bufsz = sizeof(buf); + ssize_t ret; + + pos += scnprintf(buf + pos, bufsz - pos, "%d\n", + priv->missed_beacon_threshold); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + return ret; +} + +static ssize_t iwl_dbgfs_missed_beacon_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int missed; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &missed) != 1) + return -EINVAL; + + if (missed < IWL_MISSED_BEACON_THRESHOLD_MIN || + missed > IWL_MISSED_BEACON_THRESHOLD_MAX) + priv->missed_beacon_threshold = + IWL_MISSED_BEACON_THRESHOLD_DEF; + else + priv->missed_beacon_threshold = missed; + + return count; +} + +static ssize_t iwl_dbgfs_internal_scan_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int scan; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &scan) != 1) + return -EINVAL; + + iwl_internal_short_hw_scan(priv); + + return count; +} + +static ssize_t iwl_dbgfs_plcp_delta_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + int pos = 0; + char buf[12]; + const size_t bufsz = sizeof(buf); + ssize_t ret; + + pos += scnprintf(buf + pos, bufsz - pos, "%u\n", + priv->cfg->plcp_delta_threshold); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + return ret; +} + +static ssize_t iwl_dbgfs_plcp_delta_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int plcp; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &plcp) != 1) + return -EINVAL; + if ((plcp <= IWL_MAX_PLCP_ERR_THRESHOLD_MIN) || + (plcp > IWL_MAX_PLCP_ERR_THRESHOLD_MAX)) + priv->cfg->plcp_delta_threshold = + IWL_MAX_PLCP_ERR_THRESHOLD_DEF; + else + priv->cfg->plcp_delta_threshold = plcp; + return count; +} + +static ssize_t iwl_dbgfs_force_reset_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int i, pos = 0; + char buf[300]; + const size_t bufsz = sizeof(buf); + struct iwl_force_reset *force_reset; + + for (i = 0; i < IWL_MAX_FORCE_RESET; i++) { + force_reset = &priv->force_reset[i]; + pos += scnprintf(buf + pos, bufsz - pos, + "Force reset method %d\n", i); + pos += scnprintf(buf + pos, bufsz - pos, + "\tnumber of reset request: %d\n", + force_reset->reset_request_count); + pos += scnprintf(buf + pos, bufsz - pos, + "\tnumber of reset request success: %d\n", + force_reset->reset_success_count); + pos += scnprintf(buf + pos, bufsz - pos, + "\tnumber of reset request reject: %d\n", + force_reset->reset_reject_count); + pos += scnprintf(buf + pos, bufsz - pos, + "\treset duration: %lu\n", + force_reset->reset_duration); + } + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_force_reset_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int reset, ret; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &reset) != 1) + return -EINVAL; + switch (reset) { + case IWL_RF_RESET: + case IWL_FW_RESET: + ret = iwl_force_reset(priv, reset); + break; + default: + return -EINVAL; + } + return ret ? ret : count; +} + DEBUGFS_READ_FILE_OPS(rx_statistics); DEBUGFS_READ_FILE_OPS(tx_statistics); DEBUGFS_READ_WRITE_FILE_OPS(traffic_log); @@ -1859,6 +2291,13 @@ DEBUGFS_READ_FILE_OPS(tx_power); DEBUGFS_READ_FILE_OPS(power_save_status); DEBUGFS_WRITE_FILE_OPS(clear_ucode_statistics); DEBUGFS_WRITE_FILE_OPS(clear_traffic_statistics); +DEBUGFS_WRITE_FILE_OPS(csr); +DEBUGFS_READ_WRITE_FILE_OPS(ucode_tracing); +DEBUGFS_READ_FILE_OPS(fh_reg); +DEBUGFS_READ_WRITE_FILE_OPS(missed_beacon); +DEBUGFS_WRITE_FILE_OPS(internal_scan); +DEBUGFS_READ_WRITE_FILE_OPS(plcp_delta); +DEBUGFS_READ_WRITE_FILE_OPS(force_reset); /* * Create the debugfs files and directories @@ -1866,69 +2305,74 @@ DEBUGFS_WRITE_FILE_OPS(clear_traffic_statistics); */ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name) { - struct iwl_debugfs *dbgfs; struct dentry *phyd = priv->hw->wiphy->debugfsdir; - int ret = 0; + struct dentry *dir_drv, *dir_data, *dir_rf, *dir_debug; - dbgfs = kzalloc(sizeof(struct iwl_debugfs), GFP_KERNEL); - if (!dbgfs) { - ret = -ENOMEM; - goto err; - } + dir_drv = debugfs_create_dir(name, phyd); + if (!dir_drv) + return -ENOMEM; + + priv->debugfs_dir = dir_drv; - priv->dbgfs = dbgfs; - dbgfs->name = name; - dbgfs->dir_drv = debugfs_create_dir(name, phyd); - if (!dbgfs->dir_drv || IS_ERR(dbgfs->dir_drv)) { - ret = -ENOENT; + dir_data = debugfs_create_dir("data", dir_drv); + if (!dir_data) + goto err; + dir_rf = debugfs_create_dir("rf", dir_drv); + if (!dir_rf) + goto err; + dir_debug = debugfs_create_dir("debug", dir_drv); + if (!dir_debug) goto err; - } - DEBUGFS_ADD_DIR(data, dbgfs->dir_drv); - DEBUGFS_ADD_DIR(rf, dbgfs->dir_drv); - DEBUGFS_ADD_DIR(debug, dbgfs->dir_drv); - DEBUGFS_ADD_FILE(nvm, data, S_IRUSR); - DEBUGFS_ADD_FILE(sram, data, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(log_event, data, S_IWUSR); - DEBUGFS_ADD_FILE(stations, data, S_IRUSR); - DEBUGFS_ADD_FILE(channels, data, S_IRUSR); - DEBUGFS_ADD_FILE(status, data, S_IRUSR); - DEBUGFS_ADD_FILE(interrupt, data, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(qos, data, S_IRUSR); - DEBUGFS_ADD_FILE(led, data, S_IRUSR); - DEBUGFS_ADD_FILE(sleep_level_override, data, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(current_sleep_command, data, S_IRUSR); - DEBUGFS_ADD_FILE(thermal_throttling, data, S_IRUSR); - DEBUGFS_ADD_FILE(disable_ht40, data, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(rx_statistics, debug, S_IRUSR); - DEBUGFS_ADD_FILE(tx_statistics, debug, S_IRUSR); - DEBUGFS_ADD_FILE(traffic_log, debug, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(rx_queue, debug, S_IRUSR); - DEBUGFS_ADD_FILE(tx_queue, debug, S_IRUSR); - DEBUGFS_ADD_FILE(tx_power, debug, S_IRUSR); - DEBUGFS_ADD_FILE(power_save_status, debug, S_IRUSR); - DEBUGFS_ADD_FILE(clear_ucode_statistics, debug, S_IWUSR); - DEBUGFS_ADD_FILE(clear_traffic_statistics, debug, S_IWUSR); + DEBUGFS_ADD_FILE(nvm, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(sram, dir_data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(log_event, dir_data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(stations, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(channels, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(status, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(interrupt, dir_data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(qos, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(led, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(sleep_level_override, dir_data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(current_sleep_command, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(thermal_throttling, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(disable_ht40, dir_data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(rx_statistics, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(tx_statistics, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(traffic_log, dir_debug, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(rx_queue, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(tx_queue, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(tx_power, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(power_save_status, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(clear_ucode_statistics, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(clear_traffic_statistics, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(csr, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(fh_reg, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(missed_beacon, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(internal_scan, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(plcp_delta, dir_debug, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(force_reset, dir_debug, S_IWUSR | S_IRUSR); if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != CSR_HW_REV_TYPE_3945) { - DEBUGFS_ADD_FILE(ucode_rx_stats, debug, S_IRUSR); - DEBUGFS_ADD_FILE(ucode_tx_stats, debug, S_IRUSR); - DEBUGFS_ADD_FILE(ucode_general_stats, debug, S_IRUSR); - DEBUGFS_ADD_FILE(sensitivity, debug, S_IRUSR); - DEBUGFS_ADD_FILE(chain_noise, debug, S_IRUSR); + DEBUGFS_ADD_FILE(ucode_rx_stats, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(ucode_tx_stats, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(ucode_general_stats, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(sensitivity, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(chain_noise, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(ucode_tracing, dir_debug, S_IWUSR | S_IRUSR); } - DEBUGFS_ADD_BOOL(disable_sensitivity, rf, &priv->disable_sens_cal); - DEBUGFS_ADD_BOOL(disable_chain_noise, rf, + DEBUGFS_ADD_BOOL(disable_sensitivity, dir_rf, &priv->disable_sens_cal); + DEBUGFS_ADD_BOOL(disable_chain_noise, dir_rf, &priv->disable_chain_noise_cal); if (((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_4965) || ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_3945)) - DEBUGFS_ADD_BOOL(disable_tx_power, rf, + DEBUGFS_ADD_BOOL(disable_tx_power, dir_rf, &priv->disable_tx_power_cal); return 0; err: - IWL_ERR(priv, "Can't open the debugfs directory\n"); + IWL_ERR(priv, "Can't create the debugfs directory\n"); iwl_dbgfs_unregister(priv); - return ret; + return -ENOMEM; } EXPORT_SYMBOL(iwl_dbgfs_register); @@ -1938,56 +2382,11 @@ EXPORT_SYMBOL(iwl_dbgfs_register); */ void iwl_dbgfs_unregister(struct iwl_priv *priv) { - if (!priv->dbgfs) + if (!priv->debugfs_dir) return; - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_sleep_level_override); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_current_sleep_command); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_nvm); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_sram); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_log_event); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_stations); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_channels); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_status); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_interrupt); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_qos); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_led); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_thermal_throttling); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_disable_ht40); - DEBUGFS_REMOVE(priv->dbgfs->dir_data); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_rx_statistics); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_tx_statistics); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_traffic_log); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_rx_queue); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_tx_queue); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_tx_power); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_power_save_status); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. - file_clear_ucode_statistics); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. - file_clear_traffic_statistics); - if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != CSR_HW_REV_TYPE_3945) { - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. - file_ucode_rx_stats); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. - file_ucode_tx_stats); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. - file_ucode_general_stats); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. - file_sensitivity); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. - file_chain_noise); - } - DEBUGFS_REMOVE(priv->dbgfs->dir_debug); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_rf_files.file_disable_sensitivity); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_rf_files.file_disable_chain_noise); - if (((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_4965) || - ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_3945)) - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_rf_files.file_disable_tx_power); - DEBUGFS_REMOVE(priv->dbgfs->dir_rf); - DEBUGFS_REMOVE(priv->dbgfs->dir_drv); - kfree(priv->dbgfs); - priv->dbgfs = NULL; + debugfs_remove_recursive(priv->debugfs_dir); + priv->debugfs_dir = NULL; } EXPORT_SYMBOL(iwl_dbgfs_unregister); diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 3822cf53e368..7914d65a5a55 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -512,6 +512,7 @@ struct iwl_ht_config { bool is_ht; bool is_40mhz; bool single_chain_sufficient; + enum ieee80211_smps_mode smps; /* current smps mode */ /* BSS related data */ u8 extension_chan_offset; u8 ht_protection; @@ -984,6 +985,74 @@ struct iwl_switch_rxon { __le16 channel; }; +/* + * schedule the timer to wake up every UCODE_TRACE_PERIOD milliseconds + * to perform continuous uCode event logging operation if enabled + */ +#define UCODE_TRACE_PERIOD (100) + +/* + * iwl_event_log: current uCode event log position + * + * @ucode_trace: enable/disable ucode continuous trace timer + * @num_wraps: how many times the event buffer wraps + * @next_entry: the entry just before the next one that uCode would fill + * @non_wraps_count: counter for no wrap detected when dump ucode events + * @wraps_once_count: counter for wrap once detected when dump ucode events + * @wraps_more_count: counter for wrap more than once detected + * when dump ucode events + */ +struct iwl_event_log { + bool ucode_trace; + u32 num_wraps; + u32 next_entry; + int non_wraps_count; + int wraps_once_count; + int wraps_more_count; +}; + +/* + * host interrupt timeout value + * used with setting interrupt coalescing timer + * the CSR_INT_COALESCING is an 8 bit register in 32-usec unit + * + * default interrupt coalescing timer is 64 x 32 = 2048 usecs + * default interrupt coalescing calibration timer is 16 x 32 = 512 usecs + */ +#define IWL_HOST_INT_TIMEOUT_MAX (0xFF) +#define IWL_HOST_INT_TIMEOUT_DEF (0x40) +#define IWL_HOST_INT_TIMEOUT_MIN (0x0) +#define IWL_HOST_INT_CALIB_TIMEOUT_MAX (0xFF) +#define IWL_HOST_INT_CALIB_TIMEOUT_DEF (0x10) +#define IWL_HOST_INT_CALIB_TIMEOUT_MIN (0x0) + +/* + * This is the threshold value of plcp error rate per 100mSecs. It is + * used to set and check for the validity of plcp_delta. + */ +#define IWL_MAX_PLCP_ERR_THRESHOLD_MIN (0) +#define IWL_MAX_PLCP_ERR_THRESHOLD_DEF (50) +#define IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF (100) +#define IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF (200) +#define IWL_MAX_PLCP_ERR_THRESHOLD_MAX (255) + +#define IWL_DELAY_NEXT_FORCE_RF_RESET (HZ*3) +#define IWL_DELAY_NEXT_FORCE_FW_RELOAD (HZ*5) + +enum iwl_reset { + IWL_RF_RESET = 0, + IWL_FW_RESET, + IWL_MAX_FORCE_RESET, +}; + +struct iwl_force_reset { + int reset_request_count; + int reset_success_count; + int reset_reject_count; + unsigned long reset_duration; + unsigned long last_force_reset_jiffies; +}; + struct iwl_priv { /* ieee device used by generic ieee processing code */ @@ -1004,13 +1073,22 @@ struct iwl_priv { struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; -#if defined(CONFIG_IWLWIFI_SPECTRUM_MEASUREMENT) || defined(CONFIG_IWL3945_SPECTRUM_MEASUREMENT) /* spectrum measurement report caching */ struct iwl_spectrum_notification measure_report; u8 measurement_status; -#endif + /* ucode beacon time */ u32 ucode_beacon_time; + int missed_beacon_threshold; + + /* storing the jiffies when the plcp error rate is received */ + unsigned long plcp_jiffies; + + /* reporting the number of tids has AGG on. 0 means no AGGREGATION */ + u8 agg_tids_count; + + /* force reset */ + struct iwl_force_reset force_reset[IWL_MAX_FORCE_RESET]; /* we allocate array of iwl4965_channel_info for NIC's valid channels. * Access via channel # using indirect index array */ @@ -1029,7 +1107,6 @@ struct iwl_priv { struct iwl_calib_result calib_results[IWL_CALIB_MAX]; /* Scan related variables */ - unsigned long last_scan_jiffies; unsigned long next_scan_jiffies; unsigned long scan_start; unsigned long scan_pass_start; @@ -1037,6 +1114,7 @@ struct iwl_priv { void *scan; int scan_bands; struct cfg80211_scan_request *scan_request; + bool is_internal_short_scan; u8 scan_tx_ant[IEEE80211_NUM_BANDS]; u8 mgmt_tx_ant; @@ -1045,6 +1123,7 @@ struct iwl_priv { spinlock_t hcmd_lock; /* protect hcmd */ spinlock_t reg_lock; /* protect hw register access */ struct mutex mutex; + struct mutex sync_cmd_mutex; /* enable serialization of sync commands */ /* basic pci-network driver stuff */ struct pci_dev *pci_dev; @@ -1135,6 +1214,8 @@ struct iwl_priv { struct iwl_notif_statistics statistics; #ifdef CONFIG_IWLWIFI_DEBUG struct iwl_notif_statistics accum_statistics; + struct iwl_notif_statistics delta_statistics; + struct iwl_notif_statistics max_delta; #endif /* context information */ @@ -1207,15 +1288,10 @@ struct iwl_priv { struct workqueue_struct *workqueue; - struct work_struct up; struct work_struct restart; - struct work_struct calibrated_work; struct work_struct scan_completed; struct work_struct rx_replenish; struct work_struct abort_scan; - struct work_struct update_link_led; - struct work_struct auth_work; - struct work_struct report_work; struct work_struct request_scan; struct work_struct beacon_update; struct work_struct tt_work; @@ -1251,7 +1327,8 @@ struct iwl_priv { u16 rx_traffic_idx; u8 *tx_traffic; u8 *rx_traffic; - struct iwl_debugfs *dbgfs; + struct dentry *debugfs_dir; + u32 dbgfs_sram_offset, dbgfs_sram_len; #endif /* CONFIG_IWLWIFI_DEBUGFS */ #endif /* CONFIG_IWLWIFI_DEBUG */ @@ -1261,6 +1338,7 @@ struct iwl_priv { u32 disable_tx_power_cal; struct work_struct run_time_calib_work; struct timer_list statistics_periodic; + struct timer_list ucode_trace; bool hw_ready; /*For 3945*/ #define IWL_DEFAULT_TX_POWER 0x0F @@ -1268,6 +1346,8 @@ struct iwl_priv { struct iwl3945_notif_statistics statistics_39; u32 sta_supp_rates; + + struct iwl_event_log event_log; }; /*iwl_priv */ static inline void iwl_txq_ctx_activate(struct iwl_priv *priv, int txq_id) diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.c b/drivers/net/wireless/iwlwifi/iwl-devtrace.c index 83cc4e500a96..36580d8d8b8d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.c +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.c @@ -37,4 +37,6 @@ EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_iowrite32); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_rx); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_event); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_error); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_cont_event); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_wrap_event); #endif diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h index d9c7363b1bbb..ff4d012ce260 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.h @@ -91,6 +91,50 @@ TRACE_EVENT(iwlwifi_dev_iowrite32, ); #undef TRACE_SYSTEM +#define TRACE_SYSTEM iwlwifi_ucode + +TRACE_EVENT(iwlwifi_dev_ucode_cont_event, + TP_PROTO(struct iwl_priv *priv, u32 time, u32 data, u32 ev), + TP_ARGS(priv, time, data, ev), + TP_STRUCT__entry( + PRIV_ENTRY + + __field(u32, time) + __field(u32, data) + __field(u32, ev) + ), + TP_fast_assign( + PRIV_ASSIGN; + __entry->time = time; + __entry->data = data; + __entry->ev = ev; + ), + TP_printk("[%p] EVT_LOGT:%010u:0x%08x:%04u", + __entry->priv, __entry->time, __entry->data, __entry->ev) +); + +TRACE_EVENT(iwlwifi_dev_ucode_wrap_event, + TP_PROTO(struct iwl_priv *priv, u32 wraps, u32 n_entry, u32 p_entry), + TP_ARGS(priv, wraps, n_entry, p_entry), + TP_STRUCT__entry( + PRIV_ENTRY + + __field(u32, wraps) + __field(u32, n_entry) + __field(u32, p_entry) + ), + TP_fast_assign( + PRIV_ASSIGN; + __entry->wraps = wraps; + __entry->n_entry = n_entry; + __entry->p_entry = p_entry; + ), + TP_printk("[%p] wraps=#%02d n=0x%X p=0x%X", + __entry->priv, __entry->wraps, __entry->n_entry, + __entry->p_entry) +); + +#undef TRACE_SYSTEM #define TRACE_SYSTEM iwlwifi TRACE_EVENT(iwlwifi_dev_hcmd, diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c index 4a30969689ff..fd37152abae3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.h b/drivers/net/wireless/iwlwifi/iwl-eeprom.h index 0cd9c02ee044..4e1ba824dc50 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.h +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-fh.h b/drivers/net/wireless/iwlwifi/iwl-fh.h index 65fa8a69fd5a..113c3669b9ce 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fh.h +++ b/drivers/net/wireless/iwlwifi/iwl-fh.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -379,6 +379,25 @@ #define FH_TSSR_TX_STATUS_REG (FH_TSSR_LOWER_BOUND + 0x010) +/** + * Bit fields for TSSR(Tx Shared Status & Control) error status register: + * 31: Indicates an address error when accessed to internal memory + * uCode/driver must write "1" in order to clear this flag + * 30: Indicates that Host did not send the expected number of dwords to FH + * uCode/driver must write "1" in order to clear this flag + * 16-9:Each status bit is for one channel. Indicates that an (Error) ActDMA + * command was received from the scheduler while the TRB was already full + * with previous command + * uCode/driver must write "1" in order to clear this flag + * 7-0: Each status bit indicates a channel's TxCredit error. When an error + * bit is set, it indicates that the FH has received a full indication + * from the RTC TxFIFO and the current value of the TxCredit counter was + * not equal to zero. This mean that the credit mechanism was not + * synchronized to the TxFIFO status + * uCode/driver must write "1" in order to clear this flag + */ +#define FH_TSSR_TX_ERROR_REG (FH_TSSR_LOWER_BOUND + 0x018) + #define FH_TSSR_TX_STATUS_REG_BIT_BUFS_EMPTY(_chnl) ((1 << (_chnl)) << 24) #define FH_TSSR_TX_STATUS_REG_BIT_NO_PEND_REQ(_chnl) ((1 << (_chnl)) << 16) diff --git a/drivers/net/wireless/iwlwifi/iwl-hcmd.c b/drivers/net/wireless/iwlwifi/iwl-hcmd.c index 30e9ea6d54ec..73681c4fefe7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-hcmd.c +++ b/drivers/net/wireless/iwlwifi/iwl-hcmd.c @@ -2,7 +2,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -58,7 +58,6 @@ const char *get_cmd_string(u8 cmd) IWL_CMD(COEX_PRIORITY_TABLE_CMD); IWL_CMD(COEX_MEDIUM_NOTIFICATION); IWL_CMD(COEX_EVENT_CMD); - IWL_CMD(RADAR_NOTIFICATION); IWL_CMD(REPLY_QUIET_CMD); IWL_CMD(REPLY_CHANNEL_SWITCH); IWL_CMD(CHANNEL_SWITCH_NOTIFICATION); @@ -165,15 +164,13 @@ int iwl_send_cmd_sync(struct iwl_priv *priv, struct iwl_host_cmd *cmd) /* A synchronous command can not have a callback set. */ BUG_ON(cmd->callback); - if (test_and_set_bit(STATUS_HCMD_SYNC_ACTIVE, &priv->status)) { - IWL_ERR(priv, - "Error sending %s: Already sending a host command\n", + IWL_DEBUG_INFO(priv, "Attempting to send sync command %s\n", get_cmd_string(cmd->id)); - ret = -EBUSY; - goto out; - } + mutex_lock(&priv->sync_cmd_mutex); set_bit(STATUS_HCMD_ACTIVE, &priv->status); + IWL_DEBUG_INFO(priv, "Setting HCMD_ACTIVE for command %s \n", + get_cmd_string(cmd->id)); cmd_idx = iwl_enqueue_hcmd(priv, cmd); if (cmd_idx < 0) { @@ -194,6 +191,8 @@ int iwl_send_cmd_sync(struct iwl_priv *priv, struct iwl_host_cmd *cmd) jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); clear_bit(STATUS_HCMD_ACTIVE, &priv->status); + IWL_DEBUG_INFO(priv, "Clearing HCMD_ACTIVE for command %s \n", + get_cmd_string(cmd->id)); ret = -ETIMEDOUT; goto cancel; } @@ -238,7 +237,7 @@ fail: cmd->reply_page = 0; } out: - clear_bit(STATUS_HCMD_SYNC_ACTIVE, &priv->status); + mutex_unlock(&priv->sync_cmd_mutex); return ret; } EXPORT_SYMBOL(iwl_send_cmd_sync); diff --git a/drivers/net/wireless/iwlwifi/iwl-helpers.h b/drivers/net/wireless/iwlwifi/iwl-helpers.h index bd0b12efb5c7..51a67fb2e185 100644 --- a/drivers/net/wireless/iwlwifi/iwl-helpers.h +++ b/drivers/net/wireless/iwlwifi/iwl-helpers.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -80,8 +80,8 @@ static inline void iwl_free_fw_desc(struct pci_dev *pci_dev, struct fw_desc *desc) { if (desc->v_addr) - pci_free_consistent(pci_dev, desc->len, - desc->v_addr, desc->p_addr); + dma_free_coherent(&pci_dev->dev, desc->len, + desc->v_addr, desc->p_addr); desc->v_addr = NULL; desc->len = 0; } @@ -89,7 +89,8 @@ static inline void iwl_free_fw_desc(struct pci_dev *pci_dev, static inline int iwl_alloc_fw_desc(struct pci_dev *pci_dev, struct fw_desc *desc) { - desc->v_addr = pci_alloc_consistent(pci_dev, desc->len, &desc->p_addr); + desc->v_addr = dma_alloc_coherent(&pci_dev->dev, desc->len, + &desc->p_addr, GFP_KERNEL); return (desc->v_addr != NULL) ? 0 : -ENOMEM; } diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h index e552d4c4bdbe..c719baf2585a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.h +++ b/drivers/net/wireless/iwlwifi/iwl-io.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project. * diff --git a/drivers/net/wireless/iwlwifi/iwl-led.c b/drivers/net/wireless/iwlwifi/iwl-led.c index 46c7a95b88f0..a6f9c918aabc 100644 --- a/drivers/net/wireless/iwlwifi/iwl-led.c +++ b/drivers/net/wireless/iwlwifi/iwl-led.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as diff --git a/drivers/net/wireless/iwlwifi/iwl-led.h b/drivers/net/wireless/iwlwifi/iwl-led.h index f47f053f02ea..49a70baa3fb6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-led.h +++ b/drivers/net/wireless/iwlwifi/iwl-led.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as diff --git a/drivers/net/wireless/iwlwifi/iwl-power.c b/drivers/net/wireless/iwlwifi/iwl-power.c index 8ccc0bb1d9ed..1a1a9f081cc7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-power.c +++ b/drivers/net/wireless/iwlwifi/iwl-power.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2007 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 2010 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -303,13 +303,12 @@ static int iwl_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd) sizeof(struct iwl_powertable_cmd), cmd); } - +/* priv->mutex must be held */ int iwl_power_update_mode(struct iwl_priv *priv, bool force) { int ret = 0; struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - bool enabled = (priv->iw_mode == NL80211_IFTYPE_STATION) && - (priv->hw->conf.flags & IEEE80211_CONF_PS); + bool enabled = priv->hw->conf.flags & IEEE80211_CONF_PS; bool update_chains; struct iwl_powertable_cmd cmd; int dtimper; @@ -319,7 +318,7 @@ int iwl_power_update_mode(struct iwl_priv *priv, bool force) priv->chain_noise_data.state == IWL_CHAIN_NOISE_ALIVE; if (priv->vif) - dtimper = priv->vif->bss_conf.dtim_period; + dtimper = priv->hw->conf.ps_dtim_period; else dtimper = 1; diff --git a/drivers/net/wireless/iwlwifi/iwl-power.h b/drivers/net/wireless/iwlwifi/iwl-power.h index 310c32e8f698..5db91c10dcc8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-power.h +++ b/drivers/net/wireless/iwlwifi/iwl-power.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2007 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 2010 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index 6d95832db06d..d2d2a9174900 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index 2dbce85404aa..0d09f571e185 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -123,12 +123,11 @@ EXPORT_SYMBOL(iwl_rx_queue_space); /** * iwl_rx_queue_update_write_ptr - Update the write pointer for the RX queue */ -int iwl_rx_queue_update_write_ptr(struct iwl_priv *priv, struct iwl_rx_queue *q) +void iwl_rx_queue_update_write_ptr(struct iwl_priv *priv, struct iwl_rx_queue *q) { unsigned long flags; u32 rx_wrt_ptr_reg = priv->hw_params.rx_wrt_ptr_reg; u32 reg; - int ret = 0; spin_lock_irqsave(&q->lock, flags); @@ -161,7 +160,6 @@ int iwl_rx_queue_update_write_ptr(struct iwl_priv *priv, struct iwl_rx_queue *q) exit_unlock: spin_unlock_irqrestore(&q->lock, flags); - return ret; } EXPORT_SYMBOL(iwl_rx_queue_update_write_ptr); /** @@ -184,14 +182,13 @@ static inline __le32 iwl_dma_addr2rbd_ptr(struct iwl_priv *priv, * also updates the memory address in the firmware to reference the new * target buffer. */ -int iwl_rx_queue_restock(struct iwl_priv *priv) +void iwl_rx_queue_restock(struct iwl_priv *priv) { struct iwl_rx_queue *rxq = &priv->rxq; struct list_head *element; struct iwl_rx_mem_buffer *rxb; unsigned long flags; int write; - int ret = 0; spin_lock_irqsave(&rxq->lock, flags); write = rxq->write & ~0x7; @@ -220,10 +217,8 @@ int iwl_rx_queue_restock(struct iwl_priv *priv) spin_lock_irqsave(&rxq->lock, flags); rxq->need_update = 1; spin_unlock_irqrestore(&rxq->lock, flags); - ret = iwl_rx_queue_update_write_ptr(priv, rxq); + iwl_rx_queue_update_write_ptr(priv, rxq); } - - return ret; } EXPORT_SYMBOL(iwl_rx_queue_restock); @@ -350,10 +345,10 @@ void iwl_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rxq) } } - pci_free_consistent(priv->pci_dev, 4 * RX_QUEUE_SIZE, rxq->bd, - rxq->dma_addr); - pci_free_consistent(priv->pci_dev, sizeof(struct iwl_rb_status), - rxq->rb_stts, rxq->rb_stts_dma); + dma_free_coherent(&priv->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd, + rxq->dma_addr); + dma_free_coherent(&priv->pci_dev->dev, sizeof(struct iwl_rb_status), + rxq->rb_stts, rxq->rb_stts_dma); rxq->bd = NULL; rxq->rb_stts = NULL; } @@ -362,7 +357,7 @@ EXPORT_SYMBOL(iwl_rx_queue_free); int iwl_rx_queue_alloc(struct iwl_priv *priv) { struct iwl_rx_queue *rxq = &priv->rxq; - struct pci_dev *dev = priv->pci_dev; + struct device *dev = &priv->pci_dev->dev; int i; spin_lock_init(&rxq->lock); @@ -370,12 +365,13 @@ int iwl_rx_queue_alloc(struct iwl_priv *priv) INIT_LIST_HEAD(&rxq->rx_used); /* Alloc the circular buffer of Read Buffer Descriptors (RBDs) */ - rxq->bd = pci_alloc_consistent(dev, 4 * RX_QUEUE_SIZE, &rxq->dma_addr); + rxq->bd = dma_alloc_coherent(dev, 4 * RX_QUEUE_SIZE, &rxq->dma_addr, + GFP_KERNEL); if (!rxq->bd) goto err_bd; - rxq->rb_stts = pci_alloc_consistent(dev, sizeof(struct iwl_rb_status), - &rxq->rb_stts_dma); + rxq->rb_stts = dma_alloc_coherent(dev, sizeof(struct iwl_rb_status), + &rxq->rb_stts_dma, GFP_KERNEL); if (!rxq->rb_stts) goto err_rb; @@ -392,8 +388,8 @@ int iwl_rx_queue_alloc(struct iwl_priv *priv) return 0; err_rb: - pci_free_consistent(priv->pci_dev, 4 * RX_QUEUE_SIZE, rxq->bd, - rxq->dma_addr); + dma_free_coherent(&priv->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd, + rxq->dma_addr); err_bd: return -ENOMEM; } @@ -473,8 +469,8 @@ int iwl_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq) (rb_timeout << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)| (rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS)); - /* Set interrupt coalescing timer to 64 x 32 = 2048 usecs */ - iwl_write8(priv, CSR_INT_COALESCING, 0x40); + /* Set interrupt coalescing timer to default (2048 usecs) */ + iwl_write8(priv, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF); return 0; } @@ -499,9 +495,10 @@ void iwl_rx_missed_beacon_notif(struct iwl_priv *priv, struct iwl_missed_beacon_notif *missed_beacon; missed_beacon = &pkt->u.missed_beacon; - if (le32_to_cpu(missed_beacon->consequtive_missed_beacons) > 5) { + if (le32_to_cpu(missed_beacon->consecutive_missed_beacons) > + priv->missed_beacon_threshold) { IWL_DEBUG_CALIB(priv, "missed bcn cnsq %d totl %d rcd %d expctd %d\n", - le32_to_cpu(missed_beacon->consequtive_missed_beacons), + le32_to_cpu(missed_beacon->consecutive_missed_beacons), le32_to_cpu(missed_beacon->total_missed_becons), le32_to_cpu(missed_beacon->num_recvd_beacons), le32_to_cpu(missed_beacon->num_expected_beacons)); @@ -511,6 +508,24 @@ void iwl_rx_missed_beacon_notif(struct iwl_priv *priv, } EXPORT_SYMBOL(iwl_rx_missed_beacon_notif); +void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_spectrum_notification *report = &(pkt->u.spectrum_notif); + + if (!report->state) { + IWL_DEBUG_11H(priv, + "Spectrum Measure Notification: Start\n"); + return; + } + + memcpy(&priv->measure_report, report, sizeof(*report)); + priv->measurement_status |= MEASUREMENT_READY; +} +EXPORT_SYMBOL(iwl_rx_spectrum_measure_notif); + + /* Calculate noise level, based on measurements during network silence just * before arriving beacon. This measurement can be done only if we know @@ -564,15 +579,24 @@ static void iwl_accumulative_statistics(struct iwl_priv *priv, int i; __le32 *prev_stats; u32 *accum_stats; + u32 *delta, *max_delta; prev_stats = (__le32 *)&priv->statistics; accum_stats = (u32 *)&priv->accum_statistics; + delta = (u32 *)&priv->delta_statistics; + max_delta = (u32 *)&priv->max_delta; for (i = sizeof(__le32); i < sizeof(struct iwl_notif_statistics); - i += sizeof(__le32), stats++, prev_stats++, accum_stats++) - if (le32_to_cpu(*stats) > le32_to_cpu(*prev_stats)) - *accum_stats += (le32_to_cpu(*stats) - + i += sizeof(__le32), stats++, prev_stats++, delta++, + max_delta++, accum_stats++) { + if (le32_to_cpu(*stats) > le32_to_cpu(*prev_stats)) { + *delta = (le32_to_cpu(*stats) - le32_to_cpu(*prev_stats)); + *accum_stats += *delta; + if (*delta > *max_delta) + *max_delta = *delta; + } + } /* reset accumulative statistics for "no-counter" type statistics */ priv->accum_statistics.general.temperature = @@ -592,11 +616,23 @@ static void iwl_accumulative_statistics(struct iwl_priv *priv, #define REG_RECALIB_PERIOD (60) +/* the threshold ratio of actual_ack_cnt to expected_ack_cnt in percent */ +#define ACK_CNT_RATIO (50) +#define BA_TIMEOUT_CNT (5) +#define BA_TIMEOUT_MAX (16) + +#define PLCP_MSG "plcp_err exceeded %u, %u, %u, %u, %u, %d, %u mSecs\n" void iwl_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { int change; struct iwl_rx_packet *pkt = rxb_addr(rxb); + int combined_plcp_delta; + unsigned int plcp_msec; + unsigned long plcp_received_jiffies; + int actual_ack_cnt_delta; + int expected_ack_cnt_delta; + int ba_timeout_delta; IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n", (int)sizeof(priv->statistics), @@ -611,6 +647,94 @@ void iwl_rx_statistics(struct iwl_priv *priv, #ifdef CONFIG_IWLWIFI_DEBUG iwl_accumulative_statistics(priv, (__le32 *)&pkt->u.stats); #endif + actual_ack_cnt_delta = le32_to_cpu(pkt->u.stats.tx.actual_ack_cnt) - + le32_to_cpu(priv->statistics.tx.actual_ack_cnt); + expected_ack_cnt_delta = le32_to_cpu( + pkt->u.stats.tx.expected_ack_cnt) - + le32_to_cpu(priv->statistics.tx.expected_ack_cnt); + ba_timeout_delta = le32_to_cpu( + pkt->u.stats.tx.agg.ba_timeout) - + le32_to_cpu(priv->statistics.tx.agg.ba_timeout); + if ((priv->agg_tids_count > 0) && + (expected_ack_cnt_delta > 0) && + (((actual_ack_cnt_delta * 100) / expected_ack_cnt_delta) < + ACK_CNT_RATIO) && + (ba_timeout_delta > BA_TIMEOUT_CNT)) { + IWL_DEBUG_RADIO(priv, + "actual_ack_cnt delta = %d, expected_ack_cnt = %d\n", + actual_ack_cnt_delta, expected_ack_cnt_delta); + +#ifdef CONFIG_IWLWIFI_DEBUG + IWL_DEBUG_RADIO(priv, "rx_detected_cnt delta = %d\n", + priv->delta_statistics.tx.rx_detected_cnt); + IWL_DEBUG_RADIO(priv, + "ack_or_ba_timeout_collision delta = %d\n", + priv->delta_statistics.tx.ack_or_ba_timeout_collision); +#endif + IWL_DEBUG_RADIO(priv, "agg ba_timeout delta = %d\n", + ba_timeout_delta); + if ((actual_ack_cnt_delta == 0) && + (ba_timeout_delta >= + BA_TIMEOUT_MAX)) { + IWL_DEBUG_RADIO(priv, + "call iwl_force_reset(IWL_FW_RESET)\n"); + iwl_force_reset(priv, IWL_FW_RESET); + } else { + IWL_DEBUG_RADIO(priv, + "call iwl_force_reset(IWL_RF_RESET)\n"); + iwl_force_reset(priv, IWL_RF_RESET); + } + } + /* + * check for plcp_err and trigger radio reset if it exceeds + * the plcp error threshold plcp_delta. + */ + plcp_received_jiffies = jiffies; + plcp_msec = jiffies_to_msecs((long) plcp_received_jiffies - + (long) priv->plcp_jiffies); + priv->plcp_jiffies = plcp_received_jiffies; + /* + * check to make sure plcp_msec is not 0 to prevent division + * by zero. + */ + if (plcp_msec) { + combined_plcp_delta = + (le32_to_cpu(pkt->u.stats.rx.ofdm.plcp_err) - + le32_to_cpu(priv->statistics.rx.ofdm.plcp_err)) + + (le32_to_cpu(pkt->u.stats.rx.ofdm_ht.plcp_err) - + le32_to_cpu(priv->statistics.rx.ofdm_ht.plcp_err)); + + if ((combined_plcp_delta > 0) && + ((combined_plcp_delta * 100) / plcp_msec) > + priv->cfg->plcp_delta_threshold) { + /* + * if plcp_err exceed the threshold, the following + * data is printed in csv format: + * Text: plcp_err exceeded %d, + * Received ofdm.plcp_err, + * Current ofdm.plcp_err, + * Received ofdm_ht.plcp_err, + * Current ofdm_ht.plcp_err, + * combined_plcp_delta, + * plcp_msec + */ + IWL_DEBUG_RADIO(priv, PLCP_MSG, + priv->cfg->plcp_delta_threshold, + le32_to_cpu(pkt->u.stats.rx.ofdm.plcp_err), + le32_to_cpu(priv->statistics.rx.ofdm.plcp_err), + le32_to_cpu(pkt->u.stats.rx.ofdm_ht.plcp_err), + le32_to_cpu( + priv->statistics.rx.ofdm_ht.plcp_err), + combined_plcp_delta, plcp_msec); + + /* + * Reset the RF radio due to the high plcp + * error rate + */ + iwl_force_reset(priv, IWL_RF_RESET); + } + } + memcpy(&priv->statistics, &pkt->u.stats, sizeof(priv->statistics)); set_bit(STATUS_STATISTICS, &priv->status); @@ -638,11 +762,13 @@ void iwl_reply_statistics(struct iwl_priv *priv, struct iwl_rx_packet *pkt = rxb_addr(rxb); if (le32_to_cpu(pkt->u.stats.flag) & UCODE_STATISTICS_CLEAR_MSK) { - memset(&priv->statistics, 0, - sizeof(struct iwl_notif_statistics)); #ifdef CONFIG_IWLWIFI_DEBUG memset(&priv->accum_statistics, 0, sizeof(struct iwl_notif_statistics)); + memset(&priv->delta_statistics, 0, + sizeof(struct iwl_notif_statistics)); + memset(&priv->max_delta, 0, + sizeof(struct iwl_notif_statistics)); #endif IWL_DEBUG_RX(priv, "Statistics have been cleared\n"); } diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index fa1c89ba6459..dd9ff2ed645a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c @@ -2,7 +2,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -192,19 +192,17 @@ static void iwl_rx_scan_results_notif(struct iwl_priv *priv, IWL_DEBUG_SCAN(priv, "Scan ch.res: " "%d [802.11%s] " "(TSF: 0x%08X:%08X) - %d " - "elapsed=%lu usec (%dms since last)\n", + "elapsed=%lu usec\n", notif->channel, notif->band ? "bg" : "a", le32_to_cpu(notif->tsf_high), le32_to_cpu(notif->tsf_low), le32_to_cpu(notif->statistics[0]), - le32_to_cpu(notif->tsf_low) - priv->scan_start_tsf, - jiffies_to_msecs(elapsed_jiffies - (priv->last_scan_jiffies, jiffies))); + le32_to_cpu(notif->tsf_low) - priv->scan_start_tsf); #endif - priv->last_scan_jiffies = jiffies; - priv->next_scan_jiffies = 0; + if (!priv->is_internal_short_scan) + priv->next_scan_jiffies = 0; } /* Service SCAN_COMPLETE_NOTIFICATION (0x84) */ @@ -250,8 +248,9 @@ static void iwl_rx_scan_complete_notif(struct iwl_priv *priv, goto reschedule; } - priv->last_scan_jiffies = jiffies; - priv->next_scan_jiffies = 0; + if (!priv->is_internal_short_scan) + priv->next_scan_jiffies = 0; + IWL_DEBUG_INFO(priv, "Setting scan to off\n"); clear_bit(STATUS_SCANNING, &priv->status); @@ -314,6 +313,72 @@ u16 iwl_get_passive_dwell_time(struct iwl_priv *priv, } EXPORT_SYMBOL(iwl_get_passive_dwell_time); +static int iwl_get_single_channel_for_scan(struct iwl_priv *priv, + enum ieee80211_band band, + struct iwl_scan_channel *scan_ch) +{ + const struct ieee80211_supported_band *sband; + const struct iwl_channel_info *ch_info; + u16 passive_dwell = 0; + u16 active_dwell = 0; + int i, added = 0; + u16 channel = 0; + + sband = iwl_get_hw_mode(priv, band); + if (!sband) { + IWL_ERR(priv, "invalid band\n"); + return added; + } + + active_dwell = iwl_get_active_dwell_time(priv, band, 0); + passive_dwell = iwl_get_passive_dwell_time(priv, band); + + if (passive_dwell <= active_dwell) + passive_dwell = active_dwell + 1; + + /* only scan single channel, good enough to reset the RF */ + /* pick the first valid not in-use channel */ + if (band == IEEE80211_BAND_5GHZ) { + for (i = 14; i < priv->channel_count; i++) { + if (priv->channel_info[i].channel != + le16_to_cpu(priv->staging_rxon.channel)) { + channel = priv->channel_info[i].channel; + ch_info = iwl_get_channel_info(priv, + band, channel); + if (is_channel_valid(ch_info)) + break; + } + } + } else { + for (i = 0; i < 14; i++) { + if (priv->channel_info[i].channel != + le16_to_cpu(priv->staging_rxon.channel)) { + channel = + priv->channel_info[i].channel; + ch_info = iwl_get_channel_info(priv, + band, channel); + if (is_channel_valid(ch_info)) + break; + } + } + } + if (channel) { + scan_ch->channel = cpu_to_le16(channel); + scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; + scan_ch->active_dwell = cpu_to_le16(active_dwell); + scan_ch->passive_dwell = cpu_to_le16(passive_dwell); + /* Set txpower levels to defaults */ + scan_ch->dsp_atten = 110; + if (band == IEEE80211_BAND_5GHZ) + scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; + else + scan_ch->tx_gain = ((1 << 5) | (5 << 3)); + added++; + } else + IWL_ERR(priv, "no valid channel found\n"); + return added; +} + static int iwl_get_channels_for_scan(struct iwl_priv *priv, enum ieee80211_band band, u8 is_active, u8 n_probes, @@ -404,23 +469,9 @@ EXPORT_SYMBOL(iwl_init_scan_params); static int iwl_scan_initiate(struct iwl_priv *priv) { - if (!iwl_is_ready_rf(priv)) { - IWL_DEBUG_SCAN(priv, "Aborting scan due to not ready.\n"); - return -EIO; - } - - if (test_bit(STATUS_SCANNING, &priv->status)) { - IWL_DEBUG_SCAN(priv, "Scan already in progress.\n"); - return -EAGAIN; - } - - if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) { - IWL_DEBUG_SCAN(priv, "Scan request while abort pending\n"); - return -EAGAIN; - } - IWL_DEBUG_INFO(priv, "Starting scan...\n"); set_bit(STATUS_SCANNING, &priv->status); + priv->is_internal_short_scan = false; priv->scan_start = jiffies; priv->scan_pass_start = priv->scan_start; @@ -449,6 +500,18 @@ int iwl_mac_hw_scan(struct ieee80211_hw *hw, goto out_unlock; } + if (test_bit(STATUS_SCANNING, &priv->status)) { + IWL_DEBUG_SCAN(priv, "Scan already in progress.\n"); + ret = -EAGAIN; + goto out_unlock; + } + + if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) { + IWL_DEBUG_SCAN(priv, "Scan request while abort pending\n"); + ret = -EAGAIN; + goto out_unlock; + } + /* We don't schedule scan within next_scan_jiffies period. * Avoid scanning during possible EAPOL exchange, return * success immediately. @@ -461,15 +524,6 @@ int iwl_mac_hw_scan(struct ieee80211_hw *hw, goto out_unlock; } - /* if we just finished scan ask for delay */ - if (iwl_is_associated(priv) && priv->last_scan_jiffies && - time_after(priv->last_scan_jiffies + IWL_DELAY_NEXT_SCAN, jiffies)) { - IWL_DEBUG_SCAN(priv, "scan rejected: within previous scan period\n"); - queue_work(priv->workqueue, &priv->scan_completed); - ret = 0; - goto out_unlock; - } - priv->scan_bands = 0; for (i = 0; i < req->n_channels; i++) priv->scan_bands |= BIT(req->channels[i]->band); @@ -488,6 +542,46 @@ out_unlock: } EXPORT_SYMBOL(iwl_mac_hw_scan); +/* + * internal short scan, this function should only been called while associated. + * It will reset and tune the radio to prevent possible RF related problem + */ +int iwl_internal_short_hw_scan(struct iwl_priv *priv) +{ + int ret = 0; + + if (!iwl_is_ready_rf(priv)) { + ret = -EIO; + IWL_DEBUG_SCAN(priv, "not ready or exit pending\n"); + goto out; + } + if (test_bit(STATUS_SCANNING, &priv->status)) { + IWL_DEBUG_SCAN(priv, "Scan already in progress.\n"); + ret = -EAGAIN; + goto out; + } + if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) { + IWL_DEBUG_SCAN(priv, "Scan request while abort pending\n"); + ret = -EAGAIN; + goto out; + } + + priv->scan_bands = 0; + if (priv->band == IEEE80211_BAND_5GHZ) + priv->scan_bands |= BIT(IEEE80211_BAND_5GHZ); + else + priv->scan_bands |= BIT(IEEE80211_BAND_2GHZ); + + IWL_DEBUG_SCAN(priv, "Start internal short scan...\n"); + set_bit(STATUS_SCANNING, &priv->status); + priv->is_internal_short_scan = true; + queue_work(priv->workqueue, &priv->request_scan); + +out: + return ret; +} +EXPORT_SYMBOL(iwl_internal_short_hw_scan); + #define IWL_SCAN_CHECK_WATCHDOG (7 * HZ) void iwl_bg_scan_check(struct work_struct *data) @@ -544,14 +638,26 @@ u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame, if (left < 0) return 0; *pos++ = WLAN_EID_SSID; - *pos++ = 0; - - len += 2; + if (!priv->is_internal_short_scan && + priv->scan_request->n_ssids) { + struct cfg80211_ssid *ssid = + priv->scan_request->ssids; + + /* Broadcast if ssid_len is 0 */ + *pos++ = ssid->ssid_len; + memcpy(pos, ssid->ssid, ssid->ssid_len); + pos += ssid->ssid_len; + len += 2 + ssid->ssid_len; + } else { + *pos++ = 0; + len += 2; + } if (WARN_ON(left < ie_len)) return len; - memcpy(pos, ies, ie_len); + if (ies) + memcpy(pos, ies, ie_len); len += ie_len; left -= ie_len; @@ -654,7 +760,6 @@ static void iwl_bg_request_scan(struct work_struct *data) unsigned long flags; IWL_DEBUG_INFO(priv, "Scanning while associated...\n"); - spin_lock_irqsave(&priv->lock, flags); interval = priv->beacon_int; spin_unlock_irqrestore(&priv->lock, flags); @@ -672,21 +777,29 @@ static void iwl_bg_request_scan(struct work_struct *data) scan_suspend_time, interval); } - if (priv->scan_request->n_ssids) { - int i, p = 0; + if (priv->is_internal_short_scan) { + IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n"); + } else if (priv->scan_request->n_ssids) { IWL_DEBUG_SCAN(priv, "Kicking off active scan\n"); - for (i = 0; i < priv->scan_request->n_ssids; i++) { - /* always does wildcard anyway */ - if (!priv->scan_request->ssids[i].ssid_len) - continue; - scan->direct_scan[p].id = WLAN_EID_SSID; - scan->direct_scan[p].len = - priv->scan_request->ssids[i].ssid_len; - memcpy(scan->direct_scan[p].ssid, - priv->scan_request->ssids[i].ssid, - priv->scan_request->ssids[i].ssid_len); - n_probes++; - p++; + /* + * The first SSID to scan is stuffed into the probe request + * template and the remaining ones are handled through the + * direct_scan array. + */ + if (priv->scan_request->n_ssids > 1) { + int i, p = 0; + for (i = 1; i < priv->scan_request->n_ssids; i++) { + if (!priv->scan_request->ssids[i].ssid_len) + continue; + scan->direct_scan[p].id = WLAN_EID_SSID; + scan->direct_scan[p].len = + priv->scan_request->ssids[i].ssid_len; + memcpy(scan->direct_scan[p].ssid, + priv->scan_request->ssids[i].ssid, + priv->scan_request->ssids[i].ssid_len); + n_probes++; + p++; + } } is_active = true; } else @@ -753,24 +866,38 @@ static void iwl_bg_request_scan(struct work_struct *data) rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS; rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS; scan->rx_chain = cpu_to_le16(rx_chain); - cmd_len = iwl_fill_probe_req(priv, - (struct ieee80211_mgmt *)scan->data, - priv->scan_request->ie, - priv->scan_request->ie_len, - IWL_MAX_SCAN_SIZE - sizeof(*scan)); + if (!priv->is_internal_short_scan) { + cmd_len = iwl_fill_probe_req(priv, + (struct ieee80211_mgmt *)scan->data, + priv->scan_request->ie, + priv->scan_request->ie_len, + IWL_MAX_SCAN_SIZE - sizeof(*scan)); + } else { + cmd_len = iwl_fill_probe_req(priv, + (struct ieee80211_mgmt *)scan->data, + NULL, 0, + IWL_MAX_SCAN_SIZE - sizeof(*scan)); + } scan->tx_cmd.len = cpu_to_le16(cmd_len); - if (iwl_is_monitor_mode(priv)) scan->filter_flags = RXON_FILTER_PROMISC_MSK; scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK | RXON_FILTER_BCON_AWARE_MSK); - scan->channel_count = - iwl_get_channels_for_scan(priv, band, is_active, n_probes, - (void *)&scan->data[le16_to_cpu(scan->tx_cmd.len)]); - + if (priv->is_internal_short_scan) { + scan->channel_count = + iwl_get_single_channel_for_scan(priv, band, + (void *)&scan->data[le16_to_cpu( + scan->tx_cmd.len)]); + } else { + scan->channel_count = + iwl_get_channels_for_scan(priv, band, + is_active, n_probes, + (void *)&scan->data[le16_to_cpu( + scan->tx_cmd.len)]); + } if (scan->channel_count == 0) { IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count); goto done; @@ -831,7 +958,12 @@ void iwl_bg_scan_completed(struct work_struct *work) cancel_delayed_work(&priv->scan_check); - ieee80211_scan_completed(priv->hw, false); + if (!priv->is_internal_short_scan) + ieee80211_scan_completed(priv->hw, false); + else { + priv->is_internal_short_scan = false; + IWL_DEBUG_SCAN(priv, "internal short scan completed\n"); + } if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; diff --git a/drivers/net/wireless/iwlwifi/iwl-spectrum.c b/drivers/net/wireless/iwlwifi/iwl-spectrum.c deleted file mode 100644 index 1ea5cd345fe8..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-spectrum.c +++ /dev/null @@ -1,198 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless <ilw@linux.intel.com> - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/pci.h> -#include <linux/delay.h> -#include <linux/skbuff.h> -#include <linux/netdevice.h> -#include <linux/wireless.h> - -#include <net/mac80211.h> - -#include "iwl-eeprom.h" -#include "iwl-dev.h" -#include "iwl-core.h" -#include "iwl-io.h" -#include "iwl-spectrum.h" - -#define BEACON_TIME_MASK_LOW 0x00FFFFFF -#define BEACON_TIME_MASK_HIGH 0xFF000000 -#define TIME_UNIT 1024 - -/* - * extended beacon time format - * time in usec will be changed into a 32-bit value in 8:24 format - * the high 1 byte is the beacon counts - * the lower 3 bytes is the time in usec within one beacon interval - */ - -/* TOOD: was used in sysfs debug interface need to add to mac */ -#if 0 -static u32 iwl_usecs_to_beacons(u32 usec, u32 beacon_interval) -{ - u32 quot; - u32 rem; - u32 interval = beacon_interval * 1024; - - if (!interval || !usec) - return 0; - - quot = (usec / interval) & (BEACON_TIME_MASK_HIGH >> 24); - rem = (usec % interval) & BEACON_TIME_MASK_LOW; - - return (quot << 24) + rem; -} - -/* base is usually what we get from ucode with each received frame, - * the same as HW timer counter counting down - */ - -static __le32 iwl_add_beacon_time(u32 base, u32 addon, u32 beacon_interval) -{ - u32 base_low = base & BEACON_TIME_MASK_LOW; - u32 addon_low = addon & BEACON_TIME_MASK_LOW; - u32 interval = beacon_interval * TIME_UNIT; - u32 res = (base & BEACON_TIME_MASK_HIGH) + - (addon & BEACON_TIME_MASK_HIGH); - - if (base_low > addon_low) - res += base_low - addon_low; - else if (base_low < addon_low) { - res += interval + base_low - addon_low; - res += (1 << 24); - } else - res += (1 << 24); - - return cpu_to_le32(res); -} -static int iwl_get_measurement(struct iwl_priv *priv, - struct ieee80211_measurement_params *params, - u8 type) -{ - struct iwl4965_spectrum_cmd spectrum; - struct iwl_rx_packet *res; - struct iwl_host_cmd cmd = { - .id = REPLY_SPECTRUM_MEASUREMENT_CMD, - .data = (void *)&spectrum, - .meta.flags = CMD_WANT_SKB, - }; - u32 add_time = le64_to_cpu(params->start_time); - int rc; - int spectrum_resp_status; - int duration = le16_to_cpu(params->duration); - - if (iwl_is_associated(priv)) - add_time = - iwl_usecs_to_beacons( - le64_to_cpu(params->start_time) - priv->last_tsf, - le16_to_cpu(priv->rxon_timing.beacon_interval)); - - memset(&spectrum, 0, sizeof(spectrum)); - - spectrum.channel_count = cpu_to_le16(1); - spectrum.flags = - RXON_FLG_TSF2HOST_MSK | RXON_FLG_ANT_A_MSK | RXON_FLG_DIS_DIV_MSK; - spectrum.filter_flags = MEASUREMENT_FILTER_FLAG; - cmd.len = sizeof(spectrum); - spectrum.len = cpu_to_le16(cmd.len - sizeof(spectrum.len)); - - if (iwl_is_associated(priv)) - spectrum.start_time = - iwl_add_beacon_time(priv->last_beacon_time, - add_time, - le16_to_cpu(priv->rxon_timing.beacon_interval)); - else - spectrum.start_time = 0; - - spectrum.channels[0].duration = cpu_to_le32(duration * TIME_UNIT); - spectrum.channels[0].channel = params->channel; - spectrum.channels[0].type = type; - if (priv->active_rxon.flags & RXON_FLG_BAND_24G_MSK) - spectrum.flags |= RXON_FLG_BAND_24G_MSK | - RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_TGG_PROTECT_MSK; - - rc = iwl_send_cmd_sync(priv, &cmd); - if (rc) - return rc; - - res = (struct iwl_rx_packet *)cmd.meta.u.skb->data; - if (res->hdr.flags & IWL_CMD_FAILED_MSK) { - IWL_ERR(priv, "Bad return from REPLY_RX_ON_ASSOC command\n"); - rc = -EIO; - } - - spectrum_resp_status = le16_to_cpu(res->u.spectrum.status); - switch (spectrum_resp_status) { - case 0: /* Command will be handled */ - if (res->u.spectrum.id != 0xff) { - IWL_DEBUG_INFO(priv, - "Replaced existing measurement: %d\n", - res->u.spectrum.id); - priv->measurement_status &= ~MEASUREMENT_READY; - } - priv->measurement_status |= MEASUREMENT_ACTIVE; - rc = 0; - break; - - case 1: /* Command will not be handled */ - rc = -EAGAIN; - break; - } - - dev_kfree_skb_any(cmd.meta.u.skb); - - return rc; -} -#endif - -static void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_spectrum_notification *report = &(pkt->u.spectrum_notif); - - if (!report->state) { - IWL_DEBUG_11H(priv, - "Spectrum Measure Notification: Start\n"); - return; - } - - memcpy(&priv->measure_report, report, sizeof(*report)); - priv->measurement_status |= MEASUREMENT_READY; -} - -void iwl_setup_spectrum_handlers(struct iwl_priv *priv) -{ - priv->rx_handlers[SPECTRUM_MEASURE_NOTIFICATION] = - iwl_rx_spectrum_measure_notif; -} -EXPORT_SYMBOL(iwl_setup_spectrum_handlers); diff --git a/drivers/net/wireless/iwlwifi/iwl-spectrum.h b/drivers/net/wireless/iwlwifi/iwl-spectrum.h index a77c1e619062..af6babee2891 100644 --- a/drivers/net/wireless/iwlwifi/iwl-spectrum.h +++ b/drivers/net/wireless/iwlwifi/iwl-spectrum.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ieee80211 subsystem header files. * diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c index 90fbdb25399e..4a6686fa6b36 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.c +++ b/drivers/net/wireless/iwlwifi/iwl-sta.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -80,46 +80,103 @@ int iwl_get_ra_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr) } EXPORT_SYMBOL(iwl_get_ra_sta_id); +/* priv->sta_lock must be held */ static void iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id) { - unsigned long flags; - - spin_lock_irqsave(&priv->sta_lock, flags); if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) - IWL_ERR(priv, "ACTIVATE a non DRIVER active station %d\n", - sta_id); - - priv->stations[sta_id].used |= IWL_STA_UCODE_ACTIVE; - IWL_DEBUG_ASSOC(priv, "Added STA to Ucode: %pM\n", - priv->stations[sta_id].sta.sta.addr); + IWL_ERR(priv, "ACTIVATE a non DRIVER active station id %u addr %pM\n", + sta_id, priv->stations[sta_id].sta.sta.addr); - spin_unlock_irqrestore(&priv->sta_lock, flags); + if (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) { + IWL_DEBUG_ASSOC(priv, + "STA id %u addr %pM already present in uCode (according to driver)\n", + sta_id, priv->stations[sta_id].sta.sta.addr); + } else { + priv->stations[sta_id].used |= IWL_STA_UCODE_ACTIVE; + IWL_DEBUG_ASSOC(priv, "Added STA id %u addr %pM to uCode\n", + sta_id, priv->stations[sta_id].sta.sta.addr); + } } -static void iwl_add_sta_callback(struct iwl_priv *priv, - struct iwl_device_cmd *cmd, - struct iwl_rx_packet *pkt) +static void iwl_process_add_sta_resp(struct iwl_priv *priv, + struct iwl_addsta_cmd *addsta, + struct iwl_rx_packet *pkt, + bool sync) { - struct iwl_addsta_cmd *addsta = - (struct iwl_addsta_cmd *)cmd->cmd.payload; u8 sta_id = addsta->sta.sta_id; + unsigned long flags; if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from REPLY_ADD_STA (0x%08X)\n", - pkt->hdr.flags); + pkt->hdr.flags); return; } + IWL_DEBUG_INFO(priv, "Processing response for adding station %u\n", + sta_id); + + spin_lock_irqsave(&priv->sta_lock, flags); + switch (pkt->u.add_sta.status) { case ADD_STA_SUCCESS_MSK: + IWL_DEBUG_INFO(priv, "REPLY_ADD_STA PASSED\n"); iwl_sta_ucode_activate(priv, sta_id); - /* fall through */ + break; + case ADD_STA_NO_ROOM_IN_TABLE: + IWL_ERR(priv, "Adding station %d failed, no room in table.\n", + sta_id); + break; + case ADD_STA_NO_BLOCK_ACK_RESOURCE: + IWL_ERR(priv, "Adding station %d failed, no block ack resource.\n", + sta_id); + break; + case ADD_STA_MODIFY_NON_EXIST_STA: + IWL_ERR(priv, "Attempting to modify non-existing station %d \n", + sta_id); + break; default: - IWL_DEBUG_HC(priv, "Received REPLY_ADD_STA:(0x%08X)\n", - pkt->u.add_sta.status); + IWL_DEBUG_ASSOC(priv, "Received REPLY_ADD_STA:(0x%08X)\n", + pkt->u.add_sta.status); break; } + + IWL_DEBUG_INFO(priv, "%s station id %u addr %pM\n", + priv->stations[sta_id].sta.mode == + STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", + sta_id, priv->stations[sta_id].sta.sta.addr); + + /* + * XXX: The MAC address in the command buffer is often changed from + * the original sent to the device. That is, the MAC address + * written to the command buffer often is not the same MAC adress + * read from the command buffer when the command returns. This + * issue has not yet been resolved and this debugging is left to + * observe the problem. + */ + IWL_DEBUG_INFO(priv, "%s station according to cmd buffer %pM\n", + priv->stations[sta_id].sta.mode == + STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", + addsta->sta.addr); + + /* + * Determine if we wanted to modify or add a station, + * if adding a station succeeded we have some more initialization + * to do when using station notification. TODO + */ + + spin_unlock_irqrestore(&priv->sta_lock, flags); +} + +static void iwl_add_sta_callback(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct iwl_rx_packet *pkt) +{ + struct iwl_addsta_cmd *addsta = + (struct iwl_addsta_cmd *)cmd->cmd.payload; + + iwl_process_add_sta_resp(priv, addsta, pkt, false); + } int iwl_send_add_sta(struct iwl_priv *priv, @@ -145,24 +202,9 @@ int iwl_send_add_sta(struct iwl_priv *priv, if (ret || (flags & CMD_ASYNC)) return ret; - pkt = (struct iwl_rx_packet *)cmd.reply_page; - if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { - IWL_ERR(priv, "Bad return from REPLY_ADD_STA (0x%08X)\n", - pkt->hdr.flags); - ret = -EIO; - } - if (ret == 0) { - switch (pkt->u.add_sta.status) { - case ADD_STA_SUCCESS_MSK: - iwl_sta_ucode_activate(priv, sta->sta.sta_id); - IWL_DEBUG_INFO(priv, "REPLY_ADD_STA PASSED\n"); - break; - default: - ret = -EIO; - IWL_WARN(priv, "REPLY_ADD_STA failed\n"); - break; - } + pkt = (struct iwl_rx_packet *)cmd.reply_page; + iwl_process_add_sta_resp(priv, sta, pkt, true); } iwl_free_pages(priv, cmd.reply_page); @@ -1003,24 +1045,19 @@ int iwl_rxon_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap) struct ieee80211_sta_ht_cap *cur_ht_config = NULL; u8 sta_id; - /* Add station to device's station table */ - /* - * XXX: This check is definitely not correct, if we're an AP - * it'll always be false which is not what we want, but - * it doesn't look like iwlagn is prepared to be an HT - * AP anyway. + * Set HT capabilities. It is ok to set this struct even if not using + * HT config: the priv->current_ht_config.is_ht flag will just be false */ - if (priv->current_ht_config.is_ht) { - rcu_read_lock(); - sta = ieee80211_find_sta(priv->vif, addr); - if (sta) { - memcpy(&ht_config, &sta->ht_cap, sizeof(ht_config)); - cur_ht_config = &ht_config; - } - rcu_read_unlock(); + rcu_read_lock(); + sta = ieee80211_find_sta(priv->vif, addr); + if (sta) { + memcpy(&ht_config, &sta->ht_cap, sizeof(ht_config)); + cur_ht_config = &ht_config; } + rcu_read_unlock(); + /* Add station to device's station table */ sta_id = iwl_add_station(priv, addr, is_ap, CMD_SYNC, cur_ht_config); /* Set up default rate scaling table in device's station table */ @@ -1085,6 +1122,7 @@ static void iwl_sta_init_bcast_lq(struct iwl_priv *priv) */ void iwl_add_bcast_station(struct iwl_priv *priv) { + IWL_DEBUG_INFO(priv, "Adding broadcast station to station table\n"); iwl_add_station(priv, iwl_bcast_addr, false, CMD_SYNC, NULL); /* Set up default rate scaling table in device's station table */ @@ -1093,6 +1131,16 @@ void iwl_add_bcast_station(struct iwl_priv *priv) EXPORT_SYMBOL(iwl_add_bcast_station); /** + * iwl3945_add_bcast_station - add broadcast station into station table. + */ +void iwl3945_add_bcast_station(struct iwl_priv *priv) +{ + IWL_DEBUG_INFO(priv, "Adding broadcast station to station table\n"); + iwl_add_station(priv, iwl_bcast_addr, false, CMD_SYNC, NULL); +} +EXPORT_SYMBOL(iwl3945_add_bcast_station); + +/** * iwl_get_sta_id - Find station's index within station table * * If new IBSS station, create new entry in station table diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.h b/drivers/net/wireless/iwlwifi/iwl-sta.h index 8d052de2d405..2dc35fe28f56 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.h +++ b/drivers/net/wireless/iwlwifi/iwl-sta.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -53,6 +53,7 @@ void iwl_update_tkip_key(struct iwl_priv *priv, int iwl_rxon_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap); void iwl_add_bcast_station(struct iwl_priv *priv); +void iwl3945_add_bcast_station(struct iwl_priv *priv); int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, bool is_ap); void iwl_clear_stations_table(struct iwl_priv *priv); int iwl_get_free_ucode_key_index(struct iwl_priv *priv); diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index 8f4071562857..10701b8eef23 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -60,7 +60,8 @@ static const u16 default_tid_to_tx_fifo[] = { static inline int iwl_alloc_dma_ptr(struct iwl_priv *priv, struct iwl_dma_ptr *ptr, size_t size) { - ptr->addr = pci_alloc_consistent(priv->pci_dev, size, &ptr->dma); + ptr->addr = dma_alloc_coherent(&priv->pci_dev->dev, size, &ptr->dma, + GFP_KERNEL); if (!ptr->addr) return -ENOMEM; ptr->size = size; @@ -73,21 +74,20 @@ static inline void iwl_free_dma_ptr(struct iwl_priv *priv, if (unlikely(!ptr->addr)) return; - pci_free_consistent(priv->pci_dev, ptr->size, ptr->addr, ptr->dma); + dma_free_coherent(&priv->pci_dev->dev, ptr->size, ptr->addr, ptr->dma); memset(ptr, 0, sizeof(*ptr)); } /** * iwl_txq_update_write_ptr - Send new write index to hardware */ -int iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq) +void iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq) { u32 reg = 0; - int ret = 0; int txq_id = txq->q.id; if (txq->need_update == 0) - return ret; + return; /* if we're trying to save power */ if (test_bit(STATUS_POWER_PMI, &priv->status)) { @@ -101,7 +101,7 @@ int iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq) txq_id, reg); iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - return ret; + return; } iwl_write_direct32(priv, HBUS_TARG_WRPTR, @@ -114,8 +114,6 @@ int iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq) txq->q.write_ptr | (txq_id << 8)); txq->need_update = 0; - - return ret; } EXPORT_SYMBOL(iwl_txq_update_write_ptr); @@ -146,7 +144,7 @@ void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id) { struct iwl_tx_queue *txq = &priv->txq[txq_id]; struct iwl_queue *q = &txq->q; - struct pci_dev *dev = priv->pci_dev; + struct device *dev = &priv->pci_dev->dev; int i; if (q->n_bd == 0) @@ -163,8 +161,8 @@ void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id) /* De-alloc circular buffer of TFDs */ if (txq->q.n_bd) - pci_free_consistent(dev, priv->hw_params.tfd_size * - txq->q.n_bd, txq->tfds, txq->q.dma_addr); + dma_free_coherent(dev, priv->hw_params.tfd_size * + txq->q.n_bd, txq->tfds, txq->q.dma_addr); /* De-alloc array of per-TFD driver data */ kfree(txq->txb); @@ -193,7 +191,7 @@ void iwl_cmd_queue_free(struct iwl_priv *priv) { struct iwl_tx_queue *txq = &priv->txq[IWL_CMD_QUEUE_NUM]; struct iwl_queue *q = &txq->q; - struct pci_dev *dev = priv->pci_dev; + struct device *dev = &priv->pci_dev->dev; int i; if (q->n_bd == 0) @@ -205,8 +203,8 @@ void iwl_cmd_queue_free(struct iwl_priv *priv) /* De-alloc circular buffer of TFDs */ if (txq->q.n_bd) - pci_free_consistent(dev, priv->hw_params.tfd_size * - txq->q.n_bd, txq->tfds, txq->q.dma_addr); + dma_free_coherent(dev, priv->hw_params.tfd_size * txq->q.n_bd, + txq->tfds, txq->q.dma_addr); /* deallocate arrays */ kfree(txq->cmd); @@ -297,7 +295,7 @@ static int iwl_queue_init(struct iwl_priv *priv, struct iwl_queue *q, static int iwl_tx_queue_alloc(struct iwl_priv *priv, struct iwl_tx_queue *txq, u32 id) { - struct pci_dev *dev = priv->pci_dev; + struct device *dev = &priv->pci_dev->dev; size_t tfd_sz = priv->hw_params.tfd_size * TFD_QUEUE_SIZE_MAX; /* Driver private data, only for Tx (not command) queues, @@ -316,8 +314,8 @@ static int iwl_tx_queue_alloc(struct iwl_priv *priv, /* Circular buffer of transmit frame descriptors (TFDs), * shared with device */ - txq->tfds = pci_alloc_consistent(dev, tfd_sz, &txq->q.dma_addr); - + txq->tfds = dma_alloc_coherent(dev, tfd_sz, &txq->q.dma_addr, + GFP_KERNEL); if (!txq->tfds) { IWL_ERR(priv, "pci_alloc_consistent(%zd) failed\n", tfd_sz); goto error; @@ -745,7 +743,6 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) u8 tid = 0; u8 *qc = NULL; unsigned long flags; - int ret; spin_lock_irqsave(&priv->lock, flags); if (iwl_is_rfkill(priv)) { @@ -820,8 +817,10 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) hdr->seq_ctrl |= cpu_to_le16(seq_number); seq_number += 0x10; /* aggregation is on for this <sta,tid> */ - if (info->flags & IEEE80211_TX_CTL_AMPDU) + if (info->flags & IEEE80211_TX_CTL_AMPDU && + priv->stations[sta_id].tid[tid].agg.state == IWL_AGG_ON) { txq_id = priv->stations[sta_id].tid[tid].agg.txq_id; + } } txq = &priv->txq[txq_id]; @@ -963,7 +962,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) /* Tell device the write index *just past* this latest filled TFD */ q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); - ret = iwl_txq_update_write_ptr(priv, txq); + iwl_txq_update_write_ptr(priv, txq); spin_unlock_irqrestore(&priv->lock, flags); /* @@ -977,9 +976,6 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) if (sta_priv && sta_priv->client) atomic_inc(&sta_priv->pending_frames); - if (ret) - return ret; - if ((iwl_queue_space(q) < q->high_mark) && priv->mac80211_registered) { if (wait_write_ptr) { spin_lock_irqsave(&priv->lock, flags); @@ -1018,7 +1014,7 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) struct iwl_cmd_meta *out_meta; dma_addr_t phys_addr; unsigned long flags; - int len, ret; + int len; u32 idx; u16 fix_size; @@ -1115,10 +1111,10 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) /* Increment and update queue's write index */ q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); - ret = iwl_txq_update_write_ptr(priv, txq); + iwl_txq_update_write_ptr(priv, txq); spin_unlock_irqrestore(&priv->hcmd_lock, flags); - return ret ? ret : idx; + return idx; } static void iwl_tx_status(struct iwl_priv *priv, struct sk_buff *skb) @@ -1260,6 +1256,8 @@ void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) if (!(meta->flags & CMD_ASYNC)) { clear_bit(STATUS_HCMD_ACTIVE, &priv->status); + IWL_DEBUG_INFO(priv, "Clearing HCMD_ACTIVE for command %s \n", + get_cmd_string(cmd->hdr.cmd)); wake_up_interruptible(&priv->wait_command_queue); } } @@ -1346,7 +1344,7 @@ int iwl_tx_agg_stop(struct iwl_priv *priv , const u8 *ra, u16 tid) { int tx_fifo_id, txq_id, sta_id, ssn = -1; struct iwl_tid_data *tid_data; - int ret, write_ptr, read_ptr; + int write_ptr, read_ptr; unsigned long flags; if (!ra) { @@ -1398,13 +1396,17 @@ int iwl_tx_agg_stop(struct iwl_priv *priv , const u8 *ra, u16 tid) priv->stations[sta_id].tid[tid].agg.state = IWL_AGG_OFF; spin_lock_irqsave(&priv->lock, flags); - ret = priv->cfg->ops->lib->txq_agg_disable(priv, txq_id, ssn, + /* + * the only reason this call can fail is queue number out of range, + * which can happen if uCode is reloaded and all the station + * information are lost. if it is outside the range, there is no need + * to deactivate the uCode queue, just return "success" to allow + * mac80211 to clean up it own data. + */ + priv->cfg->ops->lib->txq_agg_disable(priv, txq_id, ssn, tx_fifo_id); spin_unlock_irqrestore(&priv->lock, flags); - if (ret) - return ret; - ieee80211_stop_tx_ba_cb_irqsafe(priv->vif, ra, tid); return 0; diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index f8e4e4b18d02..54daa38ecba3 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -53,9 +53,10 @@ #include "iwl-commands.h" #include "iwl-sta.h" #include "iwl-3945.h" -#include "iwl-helpers.h" #include "iwl-core.h" +#include "iwl-helpers.h" #include "iwl-dev.h" +#include "iwl-spectrum.h" /* * module name, copyright, version, etc. @@ -70,14 +71,13 @@ #define VD #endif -#ifdef CONFIG_IWL3945_SPECTRUM_MEASUREMENT -#define VS "s" -#else -#define VS -#endif - -#define DRV_VERSION IWLWIFI_VERSION VD VS -#define DRV_COPYRIGHT "Copyright(c) 2003-2009 Intel Corporation" +/* + * add "s" to indicate spectrum measurement included. + * we add it here to be consistent with previous releases in which + * this was configurable. + */ +#define DRV_VERSION IWLWIFI_VERSION VD "s" +#define DRV_COPYRIGHT "Copyright(c) 2003-2010 Intel Corporation" #define DRV_AUTHOR "<ilw@linux.intel.com>" MODULE_DESCRIPTION(DRV_DESCRIPTION); @@ -352,10 +352,10 @@ static int iwl3945_send_beacon_cmd(struct iwl_priv *priv) static void iwl3945_unset_hw_params(struct iwl_priv *priv) { if (priv->shared_virt) - pci_free_consistent(priv->pci_dev, - sizeof(struct iwl3945_shared), - priv->shared_virt, - priv->shared_phys); + dma_free_coherent(&priv->pci_dev->dev, + sizeof(struct iwl3945_shared), + priv->shared_virt, + priv->shared_phys); } static void iwl3945_build_tx_cmd_hwcrypto(struct iwl_priv *priv, @@ -478,7 +478,6 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) u8 wait_write_ptr = 0; u8 *qc = NULL; unsigned long flags; - int rc; spin_lock_irqsave(&priv->lock, flags); if (iwl_is_rfkill(priv)) { @@ -663,12 +662,9 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) /* Tell device the write index *just past* this latest filled TFD */ q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); - rc = iwl_txq_update_write_ptr(priv, txq); + iwl_txq_update_write_ptr(priv, txq); spin_unlock_irqrestore(&priv->lock, flags); - if (rc) - return rc; - if ((iwl_queue_space(q) < q->high_mark) && priv->mac80211_registered) { if (wait_write_ptr) { @@ -689,10 +685,6 @@ drop: return -1; } -#ifdef CONFIG_IWL3945_SPECTRUM_MEASUREMENT - -#include "iwl-spectrum.h" - #define BEACON_TIME_MASK_LOW 0x00FFFFFF #define BEACON_TIME_MASK_HIGH 0xFF000000 #define TIME_UNIT 1024 @@ -819,7 +811,6 @@ static int iwl3945_get_measurement(struct iwl_priv *priv, return rc; } -#endif static void iwl3945_rx_reply_alive(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) @@ -962,6 +953,8 @@ static void iwl3945_setup_rx_handlers(struct iwl_priv *priv) priv->rx_handlers[REPLY_ADD_STA] = iwl3945_rx_reply_add_sta; priv->rx_handlers[REPLY_ERROR] = iwl_rx_reply_error; priv->rx_handlers[CHANNEL_SWITCH_NOTIFICATION] = iwl_rx_csa; + priv->rx_handlers[SPECTRUM_MEASURE_NOTIFICATION] = + iwl_rx_spectrum_measure_notif; priv->rx_handlers[PM_SLEEP_NOTIFICATION] = iwl_rx_pm_sleep_notif; priv->rx_handlers[PM_DEBUG_STATISTIC_NOTIFIC] = iwl_rx_pm_debug_statistics_notif; @@ -975,7 +968,6 @@ static void iwl3945_setup_rx_handlers(struct iwl_priv *priv) priv->rx_handlers[REPLY_STATISTICS_CMD] = iwl3945_hw_rx_statistics; priv->rx_handlers[STATISTICS_NOTIFICATION] = iwl3945_hw_rx_statistics; - iwl_setup_spectrum_handlers(priv); iwl_setup_rx_scan_handlers(priv); priv->rx_handlers[CARD_STATE_NOTIFICATION] = iwl3945_rx_card_state_notif; @@ -1067,13 +1059,13 @@ static inline __le32 iwl3945_dma_addr2rbd_ptr(struct iwl_priv *priv, * also updates the memory address in the firmware to reference the new * target buffer. */ -static int iwl3945_rx_queue_restock(struct iwl_priv *priv) +static void iwl3945_rx_queue_restock(struct iwl_priv *priv) { struct iwl_rx_queue *rxq = &priv->rxq; struct list_head *element; struct iwl_rx_mem_buffer *rxb; unsigned long flags; - int write, rc; + int write; spin_lock_irqsave(&rxq->lock, flags); write = rxq->write & ~0x7; @@ -1103,12 +1095,8 @@ static int iwl3945_rx_queue_restock(struct iwl_priv *priv) spin_lock_irqsave(&rxq->lock, flags); rxq->need_update = 1; spin_unlock_irqrestore(&rxq->lock, flags); - rc = iwl_rx_queue_update_write_ptr(priv, rxq); - if (rc) - return rc; + iwl_rx_queue_update_write_ptr(priv, rxq); } - - return 0; } /** @@ -1253,10 +1241,10 @@ static void iwl3945_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rx } } - pci_free_consistent(priv->pci_dev, 4 * RX_QUEUE_SIZE, rxq->bd, - rxq->dma_addr); - pci_free_consistent(priv->pci_dev, sizeof(struct iwl_rb_status), - rxq->rb_stts, rxq->rb_stts_dma); + dma_free_coherent(&priv->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd, + rxq->dma_addr); + dma_free_coherent(&priv->pci_dev->dev, sizeof(struct iwl_rb_status), + rxq->rb_stts, rxq->rb_stts_dma); rxq->bd = NULL; rxq->rb_stts = NULL; } @@ -1518,8 +1506,9 @@ void iwl3945_dump_nic_error_log(struct iwl_priv *priv) * iwl3945_print_event_log - Dump error event log to syslog * */ -static void iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx, - u32 num_events, u32 mode) +static int iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx, + u32 num_events, u32 mode, + int pos, char **buf, size_t bufsz) { u32 i; u32 base; /* SRAM byte address of event log header */ @@ -1529,7 +1518,7 @@ static void iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx, unsigned long reg_flags; if (num_events == 0) - return; + return pos; base = le32_to_cpu(priv->card_alive.log_event_table_ptr); @@ -1555,26 +1544,43 @@ static void iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx, time = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT); if (mode == 0) { /* data, ev */ - IWL_ERR(priv, "0x%08x\t%04u\n", time, ev); - trace_iwlwifi_dev_ucode_event(priv, 0, time, ev); + if (bufsz) { + pos += scnprintf(*buf + pos, bufsz - pos, + "0x%08x:%04u\n", + time, ev); + } else { + IWL_ERR(priv, "0x%08x\t%04u\n", time, ev); + trace_iwlwifi_dev_ucode_event(priv, 0, + time, ev); + } } else { data = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT); - IWL_ERR(priv, "%010u\t0x%08x\t%04u\n", time, data, ev); - trace_iwlwifi_dev_ucode_event(priv, time, data, ev); + if (bufsz) { + pos += scnprintf(*buf + pos, bufsz - pos, + "%010u:0x%08x:%04u\n", + time, data, ev); + } else { + IWL_ERR(priv, "%010u\t0x%08x\t%04u\n", + time, data, ev); + trace_iwlwifi_dev_ucode_event(priv, time, + data, ev); + } } } /* Allow device to power down */ iwl_release_nic_access(priv); spin_unlock_irqrestore(&priv->reg_lock, reg_flags); + return pos; } /** * iwl3945_print_last_event_logs - Dump the newest # of event log to syslog */ -static void iwl3945_print_last_event_logs(struct iwl_priv *priv, u32 capacity, +static int iwl3945_print_last_event_logs(struct iwl_priv *priv, u32 capacity, u32 num_wraps, u32 next_entry, - u32 size, u32 mode) + u32 size, u32 mode, + int pos, char **buf, size_t bufsz) { /* * display the newest DEFAULT_LOG_ENTRIES entries @@ -1582,21 +1588,28 @@ static void iwl3945_print_last_event_logs(struct iwl_priv *priv, u32 capacity, */ if (num_wraps) { if (next_entry < size) { - iwl3945_print_event_log(priv, - capacity - (size - next_entry), - size - next_entry, mode); - iwl3945_print_event_log(priv, 0, - next_entry, mode); + pos = iwl3945_print_event_log(priv, + capacity - (size - next_entry), + size - next_entry, mode, + pos, buf, bufsz); + pos = iwl3945_print_event_log(priv, 0, + next_entry, mode, + pos, buf, bufsz); } else - iwl3945_print_event_log(priv, next_entry - size, - size, mode); + pos = iwl3945_print_event_log(priv, next_entry - size, + size, mode, + pos, buf, bufsz); } else { if (next_entry < size) - iwl3945_print_event_log(priv, 0, next_entry, mode); + pos = iwl3945_print_event_log(priv, 0, + next_entry, mode, + pos, buf, bufsz); else - iwl3945_print_event_log(priv, next_entry - size, - size, mode); + pos = iwl3945_print_event_log(priv, next_entry - size, + size, mode, + pos, buf, bufsz); } + return pos; } /* For sanity check only. Actual size is determined by uCode, typ. 512 */ @@ -1604,7 +1617,8 @@ static void iwl3945_print_last_event_logs(struct iwl_priv *priv, u32 capacity, #define DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES (20) -void iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log) +int iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log, + char **buf, bool display) { u32 base; /* SRAM byte address of event log header */ u32 capacity; /* event log capacity in # entries */ @@ -1612,11 +1626,13 @@ void iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log) u32 num_wraps; /* # times uCode wrapped to top of log */ u32 next_entry; /* index of next entry to be written by uCode */ u32 size; /* # entries that we'll print */ + int pos = 0; + size_t bufsz = 0; base = le32_to_cpu(priv->card_alive.log_event_table_ptr); if (!iwl3945_hw_valid_rtc_data_addr(base)) { IWL_ERR(priv, "Invalid event log pointer 0x%08X\n", base); - return; + return -EINVAL; } /* event log header */ @@ -1642,7 +1658,7 @@ void iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log) /* bail out if nothing in log */ if (size == 0) { IWL_ERR(priv, "Start IWL Event Log Dump: nothing in log\n"); - return; + return pos; } #ifdef CONFIG_IWLWIFI_DEBUG @@ -1658,25 +1674,38 @@ void iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log) size); #ifdef CONFIG_IWLWIFI_DEBUG + if (display) { + if (full_log) + bufsz = capacity * 48; + else + bufsz = size * 48; + *buf = kmalloc(bufsz, GFP_KERNEL); + if (!*buf) + return -ENOMEM; + } if ((iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) || full_log) { /* if uCode has wrapped back to top of log, * start at the oldest entry, * i.e the next one that uCode would fill. */ if (num_wraps) - iwl3945_print_event_log(priv, next_entry, - capacity - next_entry, mode); + pos = iwl3945_print_event_log(priv, next_entry, + capacity - next_entry, mode, + pos, buf, bufsz); /* (then/else) start at top of log */ - iwl3945_print_event_log(priv, 0, next_entry, mode); + pos = iwl3945_print_event_log(priv, 0, next_entry, mode, + pos, buf, bufsz); } else - iwl3945_print_last_event_logs(priv, capacity, num_wraps, - next_entry, size, mode); + pos = iwl3945_print_last_event_logs(priv, capacity, num_wraps, + next_entry, size, mode, + pos, buf, bufsz); #else - iwl3945_print_last_event_logs(priv, capacity, num_wraps, - next_entry, size, mode); + pos = iwl3945_print_last_event_logs(priv, capacity, num_wraps, + next_entry, size, mode, + pos, buf, bufsz); #endif - + return pos; } static void iwl3945_irq_tasklet(struct iwl_priv *priv) @@ -2996,18 +3025,6 @@ static void iwl3945_bg_request_scan(struct work_struct *data) mutex_unlock(&priv->mutex); } -static void iwl3945_bg_up(struct work_struct *data) -{ - struct iwl_priv *priv = container_of(data, struct iwl_priv, up); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - mutex_lock(&priv->mutex); - __iwl3945_up(priv); - mutex_unlock(&priv->mutex); -} - static void iwl3945_bg_restart(struct work_struct *data) { struct iwl_priv *priv = container_of(data, struct iwl_priv, restart); @@ -3024,7 +3041,13 @@ static void iwl3945_bg_restart(struct work_struct *data) ieee80211_restart_hw(priv->hw); } else { iwl3945_down(priv); - queue_work(priv->workqueue, &priv->up); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + __iwl3945_up(priv); + mutex_unlock(&priv->mutex); } } @@ -3528,8 +3551,6 @@ static ssize_t store_filter_flags(struct device *d, static DEVICE_ATTR(filter_flags, S_IWUSR | S_IRUGO, show_filter_flags, store_filter_flags); -#ifdef CONFIG_IWL3945_SPECTRUM_MEASUREMENT - static ssize_t show_measurement(struct device *d, struct device_attribute *attr, char *buf) { @@ -3599,7 +3620,6 @@ static ssize_t store_measurement(struct device *d, static DEVICE_ATTR(measurement, S_IRUSR | S_IWUSR, show_measurement, store_measurement); -#endif /* CONFIG_IWL3945_SPECTRUM_MEASUREMENT */ static ssize_t store_retry_rate(struct device *d, struct device_attribute *attr, @@ -3748,7 +3768,6 @@ static void iwl3945_setup_deferred_work(struct iwl_priv *priv) init_waitqueue_head(&priv->wait_command_queue); - INIT_WORK(&priv->up, iwl3945_bg_up); INIT_WORK(&priv->restart, iwl3945_bg_restart); INIT_WORK(&priv->rx_replenish, iwl3945_bg_rx_replenish); INIT_WORK(&priv->beacon_update, iwl3945_bg_beacon_update); @@ -3782,9 +3801,7 @@ static struct attribute *iwl3945_sysfs_entries[] = { &dev_attr_dump_errors.attr, &dev_attr_flags.attr, &dev_attr_filter_flags.attr, -#ifdef CONFIG_IWL3945_SPECTRUM_MEASUREMENT &dev_attr_measurement.attr, -#endif &dev_attr_retry_rate.attr, &dev_attr_statistics.attr, &dev_attr_status.attr, @@ -3810,7 +3827,6 @@ static struct ieee80211_ops iwl3945_hw_ops = { .config = iwl_mac_config, .configure_filter = iwl_configure_filter, .set_key = iwl3945_mac_set_key, - .get_tx_stats = iwl_mac_get_tx_stats, .conf_tx = iwl_mac_conf_tx, .reset_tsf = iwl_mac_reset_tsf, .bss_info_changed = iwl_bss_info_changed, @@ -3831,6 +3847,7 @@ static int iwl3945_init_drv(struct iwl_priv *priv) INIT_LIST_HEAD(&priv->free_frames); mutex_init(&priv->mutex); + mutex_init(&priv->sync_cmd_mutex); /* Clear the driver's (not device's) station table */ iwl_clear_stations_table(priv); @@ -3840,6 +3857,7 @@ static int iwl3945_init_drv(struct iwl_priv *priv) priv->band = IEEE80211_BAND_2GHZ; priv->iw_mode = NL80211_IFTYPE_STATION; + priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF; iwl_reset_qos(priv); @@ -4022,6 +4040,13 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e spin_lock_init(&priv->reg_lock); spin_lock_init(&priv->lock); + /* + * stop and reset the on-board processor just in case it is in a + * strange state ... like being left stranded by a primary kernel + * and this is now the kdump kernel trying to start up + */ + iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); + /*********************** * 4. Read EEPROM * ********************/ diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h index 842811142bef..79ffa3b98d73 100644 --- a/drivers/net/wireless/iwmc3200wifi/iwm.h +++ b/drivers/net/wireless/iwmc3200wifi/iwm.h @@ -268,7 +268,7 @@ struct iwm_priv { struct sk_buff_head rx_list; struct list_head rx_tickets; - struct list_head rx_packets[IWM_RX_ID_HASH + 1]; + struct list_head rx_packets[IWM_RX_ID_HASH]; struct workqueue_struct *rx_wq; struct work_struct rx_worker; diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c index 64d16fe37f9a..6b53ae122680 100644 --- a/drivers/net/wireless/iwmc3200wifi/rx.c +++ b/drivers/net/wireless/iwmc3200wifi/rx.c @@ -868,36 +868,35 @@ static int iwm_mlme_mgt_frame(struct iwm_priv *iwm, u8 *buf, struct iwm_umac_notif_mgt_frame *mgt_frame = (struct iwm_umac_notif_mgt_frame *)buf; struct ieee80211_mgmt *mgt = (struct ieee80211_mgmt *)mgt_frame->frame; - u8 *ie; IWM_HEXDUMP(iwm, DBG, MLME, "MGT: ", mgt_frame->frame, le16_to_cpu(mgt_frame->len)); if (ieee80211_is_assoc_req(mgt->frame_control)) { - ie = mgt->u.assoc_req.variable;; - iwm->req_ie_len = - le16_to_cpu(mgt_frame->len) - (ie - (u8 *)mgt); + iwm->req_ie_len = le16_to_cpu(mgt_frame->len) + - offsetof(struct ieee80211_mgmt, + u.assoc_req.variable); kfree(iwm->req_ie); iwm->req_ie = kmemdup(mgt->u.assoc_req.variable, iwm->req_ie_len, GFP_KERNEL); } else if (ieee80211_is_reassoc_req(mgt->frame_control)) { - ie = mgt->u.reassoc_req.variable;; - iwm->req_ie_len = - le16_to_cpu(mgt_frame->len) - (ie - (u8 *)mgt); + iwm->req_ie_len = le16_to_cpu(mgt_frame->len) + - offsetof(struct ieee80211_mgmt, + u.reassoc_req.variable); kfree(iwm->req_ie); iwm->req_ie = kmemdup(mgt->u.reassoc_req.variable, iwm->req_ie_len, GFP_KERNEL); } else if (ieee80211_is_assoc_resp(mgt->frame_control)) { - ie = mgt->u.assoc_resp.variable;; - iwm->resp_ie_len = - le16_to_cpu(mgt_frame->len) - (ie - (u8 *)mgt); + iwm->resp_ie_len = le16_to_cpu(mgt_frame->len) + - offsetof(struct ieee80211_mgmt, + u.assoc_resp.variable); kfree(iwm->resp_ie); iwm->resp_ie = kmemdup(mgt->u.assoc_resp.variable, iwm->resp_ie_len, GFP_KERNEL); } else if (ieee80211_is_reassoc_resp(mgt->frame_control)) { - ie = mgt->u.reassoc_resp.variable;; - iwm->resp_ie_len = - le16_to_cpu(mgt_frame->len) - (ie - (u8 *)mgt); + iwm->resp_ie_len = le16_to_cpu(mgt_frame->len) + - offsetof(struct ieee80211_mgmt, + u.reassoc_resp.variable); kfree(iwm->resp_ie); iwm->resp_ie = kmemdup(mgt->u.reassoc_resp.variable, iwm->resp_ie_len, GFP_KERNEL); @@ -1534,6 +1533,33 @@ static void classify8023(struct sk_buff *skb) } } +static void iwm_rx_process_amsdu(struct iwm_priv *iwm, struct sk_buff *skb) +{ + struct wireless_dev *wdev = iwm_to_wdev(iwm); + struct net_device *ndev = iwm_to_ndev(iwm); + struct sk_buff_head list; + struct sk_buff *frame; + + IWM_HEXDUMP(iwm, DBG, RX, "A-MSDU: ", skb->data, skb->len); + + __skb_queue_head_init(&list); + ieee80211_amsdu_to_8023s(skb, &list, ndev->dev_addr, wdev->iftype, 0); + + while ((frame = __skb_dequeue(&list))) { + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += frame->len; + + frame->protocol = eth_type_trans(frame, ndev); + frame->ip_summed = CHECKSUM_NONE; + memset(frame->cb, 0, sizeof(frame->cb)); + + if (netif_rx_ni(frame) == NET_RX_DROP) { + IWM_ERR(iwm, "Packet dropped\n"); + ndev->stats.rx_dropped++; + } + } +} + static void iwm_rx_process_packet(struct iwm_priv *iwm, struct iwm_rx_packet *packet, struct iwm_rx_ticket_node *ticket_node) @@ -1548,25 +1574,34 @@ static void iwm_rx_process_packet(struct iwm_priv *iwm, switch (le16_to_cpu(ticket_node->ticket->action)) { case IWM_RX_TICKET_RELEASE: IWM_DBG_RX(iwm, DBG, "RELEASE packet\n"); - classify8023(skb); + iwm_rx_adjust_packet(iwm, packet, ticket_node); + skb->dev = iwm_to_ndev(iwm); + classify8023(skb); + + if (le16_to_cpu(ticket_node->ticket->flags) & + IWM_RX_TICKET_AMSDU_MSK) { + iwm_rx_process_amsdu(iwm, skb); + break; + } + ret = ieee80211_data_to_8023(skb, ndev->dev_addr, wdev->iftype); if (ret < 0) { IWM_DBG_RX(iwm, DBG, "Couldn't convert 802.11 header - " "%d\n", ret); + kfree_skb(packet->skb); break; } IWM_HEXDUMP(iwm, DBG, RX, "802.3: ", skb->data, skb->len); - skb->dev = iwm_to_ndev(iwm); + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += skb->len; + skb->protocol = eth_type_trans(skb, ndev); skb->ip_summed = CHECKSUM_NONE; memset(skb->cb, 0, sizeof(skb->cb)); - ndev->stats.rx_packets++; - ndev->stats.rx_bytes += skb->len; - if (netif_rx_ni(skb) == NET_RX_DROP) { IWM_ERR(iwm, "Packet dropped\n"); ndev->stats.rx_dropped++; diff --git a/drivers/net/wireless/libertas/Kconfig b/drivers/net/wireless/libertas/Kconfig index 30aa9d48d67e..0485c9957575 100644 --- a/drivers/net/wireless/libertas/Kconfig +++ b/drivers/net/wireless/libertas/Kconfig @@ -37,3 +37,9 @@ config LIBERTAS_DEBUG depends on LIBERTAS ---help--- Debugging support. + +config LIBERTAS_MESH + bool "Enable mesh support" + depends on LIBERTAS + help + This enables Libertas' MESH support, used by e.g. the OLPC people. diff --git a/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/libertas/Makefile index b188cd97a053..45e870e33117 100644 --- a/drivers/net/wireless/libertas/Makefile +++ b/drivers/net/wireless/libertas/Makefile @@ -5,11 +5,11 @@ libertas-y += cmdresp.o libertas-y += debugfs.o libertas-y += ethtool.o libertas-y += main.o -libertas-y += mesh.o libertas-y += rx.o libertas-y += scan.o libertas-y += tx.o libertas-y += wext.o +libertas-$(CONFIG_LIBERTAS_MESH) += mesh.o usb8xxx-objs += if_usb.o libertas_cs-objs += if_cs.o diff --git a/drivers/net/wireless/libertas/assoc.c b/drivers/net/wireless/libertas/assoc.c index 751067369ba8..f03d5e4e59c3 100644 --- a/drivers/net/wireless/libertas/assoc.c +++ b/drivers/net/wireless/libertas/assoc.c @@ -390,10 +390,8 @@ int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv, cmd.enablehwauto = cpu_to_le16(priv->enablehwauto); cmd.bitmap = lbs_rate_to_fw_bitmap(priv->cur_rate, priv->enablehwauto); ret = lbs_cmd_with_response(priv, CMD_802_11_RATE_ADAPT_RATESET, &cmd); - if (!ret && cmd_action == CMD_ACT_GET) { - priv->ratebitmap = le16_to_cpu(cmd.bitmap); + if (!ret && cmd_action == CMD_ACT_GET) priv->enablehwauto = le16_to_cpu(cmd.enablehwauto); - } lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); return ret; @@ -807,8 +805,7 @@ static int lbs_try_associate(struct lbs_private *priv, } /* Use short preamble only when both the BSS and firmware support it */ - if ((priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) && - (assoc_req->bss.capability & WLAN_CAPABILITY_SHORT_PREAMBLE)) + if (assoc_req->bss.capability & WLAN_CAPABILITY_SHORT_PREAMBLE) preamble = RADIO_PREAMBLE_SHORT; ret = lbs_set_radio(priv, preamble, 1); @@ -939,8 +936,7 @@ static int lbs_adhoc_join(struct lbs_private *priv, } /* Use short preamble only when both the BSS and firmware support it */ - if ((priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) && - (bss->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)) { + if (bss->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) { lbs_deb_join("AdhocJoin: Short preamble\n"); preamble = RADIO_PREAMBLE_SHORT; } @@ -1049,7 +1045,7 @@ static int lbs_adhoc_start(struct lbs_private *priv, struct assoc_request *assoc_req) { struct cmd_ds_802_11_ad_hoc_start cmd; - u8 preamble = RADIO_PREAMBLE_LONG; + u8 preamble = RADIO_PREAMBLE_SHORT; size_t ratesize = 0; u16 tmpcap = 0; int ret = 0; @@ -1057,11 +1053,6 @@ static int lbs_adhoc_start(struct lbs_private *priv, lbs_deb_enter(LBS_DEB_ASSOC); - if (priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) { - lbs_deb_join("ADHOC_START: Will use short preamble\n"); - preamble = RADIO_PREAMBLE_SHORT; - } - ret = lbs_set_radio(priv, preamble, 1); if (ret) goto out; @@ -1169,11 +1160,11 @@ int lbs_adhoc_stop(struct lbs_private *priv) static inline int match_bss_no_security(struct lbs_802_11_security *secinfo, struct bss_descriptor *match_bss) { - if (!secinfo->wep_enabled && !secinfo->WPAenabled - && !secinfo->WPA2enabled - && match_bss->wpa_ie[0] != WLAN_EID_GENERIC - && match_bss->rsn_ie[0] != WLAN_EID_RSN - && !(match_bss->capability & WLAN_CAPABILITY_PRIVACY)) + if (!secinfo->wep_enabled && + !secinfo->WPAenabled && !secinfo->WPA2enabled && + match_bss->wpa_ie[0] != WLAN_EID_GENERIC && + match_bss->rsn_ie[0] != WLAN_EID_RSN && + !(match_bss->capability & WLAN_CAPABILITY_PRIVACY)) return 1; else return 0; @@ -1182,9 +1173,9 @@ static inline int match_bss_no_security(struct lbs_802_11_security *secinfo, static inline int match_bss_static_wep(struct lbs_802_11_security *secinfo, struct bss_descriptor *match_bss) { - if (secinfo->wep_enabled && !secinfo->WPAenabled - && !secinfo->WPA2enabled - && (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) + if (secinfo->wep_enabled && + !secinfo->WPAenabled && !secinfo->WPA2enabled && + (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) return 1; else return 0; @@ -1193,8 +1184,8 @@ static inline int match_bss_static_wep(struct lbs_802_11_security *secinfo, static inline int match_bss_wpa(struct lbs_802_11_security *secinfo, struct bss_descriptor *match_bss) { - if (!secinfo->wep_enabled && secinfo->WPAenabled - && (match_bss->wpa_ie[0] == WLAN_EID_GENERIC) + if (!secinfo->wep_enabled && secinfo->WPAenabled && + (match_bss->wpa_ie[0] == WLAN_EID_GENERIC) /* privacy bit may NOT be set in some APs like LinkSys WRT54G && (match_bss->capability & WLAN_CAPABILITY_PRIVACY) */ ) @@ -1219,11 +1210,11 @@ static inline int match_bss_wpa2(struct lbs_802_11_security *secinfo, static inline int match_bss_dynamic_wep(struct lbs_802_11_security *secinfo, struct bss_descriptor *match_bss) { - if (!secinfo->wep_enabled && !secinfo->WPAenabled - && !secinfo->WPA2enabled - && (match_bss->wpa_ie[0] != WLAN_EID_GENERIC) - && (match_bss->rsn_ie[0] != WLAN_EID_RSN) - && (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) + if (!secinfo->wep_enabled && + !secinfo->WPAenabled && !secinfo->WPA2enabled && + (match_bss->wpa_ie[0] != WLAN_EID_GENERIC) && + (match_bss->rsn_ie[0] != WLAN_EID_RSN) && + (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) return 1; else return 0; @@ -1534,8 +1525,8 @@ static int assoc_helper_associate(struct lbs_private *priv, /* If we're given and 'any' BSSID, try associating based on SSID */ if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { - if (compare_ether_addr(bssid_any, assoc_req->bssid) - && compare_ether_addr(bssid_off, assoc_req->bssid)) { + if (compare_ether_addr(bssid_any, assoc_req->bssid) && + compare_ether_addr(bssid_off, assoc_req->bssid)) { ret = assoc_helper_bssid(priv, assoc_req); done = 1; } @@ -1621,11 +1612,9 @@ static int assoc_helper_channel(struct lbs_private *priv, goto restore_mesh; } - if ( assoc_req->secinfo.wep_enabled - && (assoc_req->wep_keys[0].len - || assoc_req->wep_keys[1].len - || assoc_req->wep_keys[2].len - || assoc_req->wep_keys[3].len)) { + if (assoc_req->secinfo.wep_enabled && + (assoc_req->wep_keys[0].len || assoc_req->wep_keys[1].len || + assoc_req->wep_keys[2].len || assoc_req->wep_keys[3].len)) { /* Make sure WEP keys are re-sent to firmware */ set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); } @@ -1992,14 +1981,14 @@ void lbs_association_worker(struct work_struct *work) assoc_req->secinfo.auth_mode); /* If 'any' SSID was specified, find an SSID to associate with */ - if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags) - && !assoc_req->ssid_len) + if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags) && + !assoc_req->ssid_len) find_any_ssid = 1; /* But don't use 'any' SSID if there's a valid locked BSSID to use */ if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { - if (compare_ether_addr(assoc_req->bssid, bssid_any) - && compare_ether_addr(assoc_req->bssid, bssid_off)) + if (compare_ether_addr(assoc_req->bssid, bssid_any) && + compare_ether_addr(assoc_req->bssid, bssid_off)) find_any_ssid = 0; } @@ -2061,13 +2050,6 @@ void lbs_association_worker(struct work_struct *work) goto out; } - if ( test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags) - || test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) { - ret = assoc_helper_wep_keys(priv, assoc_req); - if (ret) - goto out; - } - if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { ret = assoc_helper_secinfo(priv, assoc_req); if (ret) @@ -2080,18 +2062,31 @@ void lbs_association_worker(struct work_struct *work) goto out; } - if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags) - || test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { + /* + * v10 FW wants WPA keys to be set/cleared before WEP key operations, + * otherwise it will fail to correctly associate to WEP networks. + * Other firmware versions don't appear to care. + */ + if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags) || + test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { ret = assoc_helper_wpa_keys(priv, assoc_req); if (ret) goto out; } + if (test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags) || + test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) { + ret = assoc_helper_wep_keys(priv, assoc_req); + if (ret) + goto out; + } + + /* SSID/BSSID should be the _last_ config option set, because they * trigger the association attempt. */ - if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags) - || test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { + if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags) || + test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { int success = 1; ret = assoc_helper_associate(priv, assoc_req); diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index b9b371bfa30f..42051f7cad6d 100644 --- a/drivers/net/wireless/libertas/cmd.c +++ b/drivers/net/wireless/libertas/cmd.c @@ -143,19 +143,6 @@ int lbs_update_hw_spec(struct lbs_private *priv) lbs_deb_cmd("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n", cmd.hwifversion, cmd.version); - /* Determine mesh_fw_ver from fwrelease and fwcapinfo */ - /* 5.0.16p0 9.0.0.p0 is known to NOT support any mesh */ - /* 5.110.22 have mesh command with 0xa3 command id */ - /* 10.0.0.p0 FW brings in mesh config command with different id */ - /* Check FW version MSB and initialize mesh_fw_ver */ - if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) - priv->mesh_fw_ver = MESH_FW_OLD; - else if ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) && - (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)) - priv->mesh_fw_ver = MESH_FW_NEW; - else - priv->mesh_fw_ver = MESH_NONE; - /* Clamp region code to 8-bit since FW spec indicates that it should * only ever be 8-bit, even though the field size is 16-bit. Some firmware * returns non-zero high 8 bits here. @@ -855,9 +842,6 @@ int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on) if (priv->fwrelease < 0x09000000) { switch (preamble) { case RADIO_PREAMBLE_SHORT: - if (!(priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)) - goto out; - /* Fall through */ case RADIO_PREAMBLE_AUTO: case RADIO_PREAMBLE_LONG: cmd.control = cpu_to_le16(preamble); @@ -1011,6 +995,8 @@ int lbs_prepare_and_send_command(struct lbs_private *priv, ret = 0; break; +#ifdef CONFIG_LIBERTAS_MESH + case CMD_BT_ACCESS: ret = lbs_cmd_bt_access(cmdptr, cmd_action, pdata_buf); break; @@ -1019,6 +1005,8 @@ int lbs_prepare_and_send_command(struct lbs_private *priv, ret = lbs_cmd_fwt_access(cmdptr, cmd_action, pdata_buf); break; +#endif + case CMD_802_11_BEACON_CTRL: ret = lbs_cmd_bcn_ctrl(priv, cmdptr, cmd_action); break; @@ -1317,7 +1305,7 @@ int lbs_execute_next_command(struct lbs_private *priv) if ((priv->psmode != LBS802_11POWERMODECAM) && (priv->psstate == PS_STATE_FULL_POWER) && ((priv->connect_status == LBS_CONNECTED) || - (priv->mesh_connect_status == LBS_CONNECTED))) { + lbs_mesh_connected(priv))) { if (priv->secinfo.WPAenabled || priv->secinfo.WPA2enabled) { /* check for valid WPA group keys */ diff --git a/drivers/net/wireless/libertas/cmd.h b/drivers/net/wireless/libertas/cmd.h index 2862748aef70..cb4138a55fdf 100644 --- a/drivers/net/wireless/libertas/cmd.h +++ b/drivers/net/wireless/libertas/cmd.h @@ -110,18 +110,6 @@ int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val); int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val); -/* Mesh related */ - -int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action, - struct cmd_ds_mesh_access *cmd); - -int lbs_mesh_config_send(struct lbs_private *priv, - struct cmd_ds_mesh_config *cmd, - uint16_t action, uint16_t type); - -int lbs_mesh_config(struct lbs_private *priv, uint16_t enable, uint16_t chan); - - /* Commands only used in wext.c, assoc. and scan.c */ int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0, diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c index 21d57690c20a..e7470442f76b 100644 --- a/drivers/net/wireless/libertas/cmdresp.c +++ b/drivers/net/wireless/libertas/cmdresp.c @@ -240,11 +240,6 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len) /* Now we got response from FW, cancel the command timer */ del_timer(&priv->command_timer); priv->cmd_timed_out = 0; - if (priv->nr_retries) { - lbs_pr_info("Received result %x to command %x after %d retries\n", - result, curcmd, priv->nr_retries); - priv->nr_retries = 0; - } /* Store the response code to cur_cmd_retcode. */ priv->cur_cmd_retcode = result; @@ -485,20 +480,8 @@ int lbs_process_event(struct lbs_private *priv, u32 event) break; case MACREG_INT_CODE_MESH_AUTO_STARTED: - /* Ignore spurious autostart events if autostart is disabled */ - if (!priv->mesh_autostart_enabled) { - lbs_pr_info("EVENT: MESH_AUTO_STARTED (ignoring)\n"); - break; - } - lbs_pr_info("EVENT: MESH_AUTO_STARTED\n"); - priv->mesh_connect_status = LBS_CONNECTED; - if (priv->mesh_open) { - netif_carrier_on(priv->mesh_dev); - if (!priv->tx_pending_len) - netif_wake_queue(priv->mesh_dev); - } - priv->mode = IW_MODE_ADHOC; - schedule_work(&priv->sync_channel); + /* Ignore spurious autostart events */ + lbs_pr_info("EVENT: MESH_AUTO_STARTED (ignoring)\n"); break; default: diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h index 6b6ea9f7bf5b..ea3f10ef4e00 100644 --- a/drivers/net/wireless/libertas/defs.h +++ b/drivers/net/wireless/libertas/defs.h @@ -397,13 +397,6 @@ enum KEY_INFO_WPA { KEY_INFO_WPA_ENABLED = 0x04 }; -/** mesh_fw_ver */ -enum _mesh_fw_ver { - MESH_NONE = 0, /* MESH is not supported */ - MESH_FW_OLD, /* MESH is supported in FW V5 */ - MESH_FW_NEW, /* MESH is supported in FW V10 and newer */ -}; - /* Default values for fwt commands. */ #define FWT_DEFAULT_METRIC 0 #define FWT_DEFAULT_DIR 1 diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h index 6a8d2b291d8c..efb2519d9d74 100644 --- a/drivers/net/wireless/libertas/dev.h +++ b/drivers/net/wireless/libertas/dev.h @@ -39,15 +39,14 @@ struct lbs_private { /* Mesh */ struct net_device *mesh_dev; /* Virtual device */ +#ifdef CONFIG_LIBERTAS_MESH u32 mesh_connect_status; struct lbs_mesh_stats mstats; int mesh_open; - int mesh_fw_ver; - int mesh_autostart_enabled; uint16_t mesh_tlv; u8 mesh_ssid[IEEE80211_MAX_SSID_LEN + 1]; u8 mesh_ssid_len; - struct work_struct sync_channel; +#endif /* Monitor mode */ struct net_device *rtap_net_dev; @@ -110,7 +109,6 @@ struct lbs_private { struct list_head cmdpendingq; /* pending command buffers */ wait_queue_head_t cmd_pending; struct timer_list command_timer; - int nr_retries; int cmd_timed_out; /* Command responses sent from the hardware to the driver */ @@ -176,9 +174,7 @@ struct lbs_private { struct bss_descriptor *networks; struct assoc_request * pending_assoc_req; struct assoc_request * in_progress_assoc_req; - u16 capability; uint16_t enablehwauto; - uint16_t ratebitmap; /* ADHOC */ u16 beacon_period; diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c index 63d020374c2b..3804a58d7f4e 100644 --- a/drivers/net/wireless/libertas/ethtool.c +++ b/drivers/net/wireless/libertas/ethtool.c @@ -114,9 +114,11 @@ const struct ethtool_ops lbs_ethtool_ops = { .get_drvinfo = lbs_ethtool_get_drvinfo, .get_eeprom = lbs_ethtool_get_eeprom, .get_eeprom_len = lbs_ethtool_get_eeprom_len, +#ifdef CONFIG_LIBERTAS_MESH .get_sset_count = lbs_mesh_ethtool_get_sset_count, .get_ethtool_stats = lbs_mesh_ethtool_get_stats, .get_strings = lbs_mesh_ethtool_get_strings, +#endif .get_wol = lbs_ethtool_get_wol, .set_wol = lbs_ethtool_set_wol, }; diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c index bf4bfbae6227..3ea03f259ee7 100644 --- a/drivers/net/wireless/libertas/if_spi.c +++ b/drivers/net/wireless/libertas/if_spi.c @@ -23,6 +23,7 @@ #include <linux/kthread.h> #include <linux/list.h> #include <linux/netdevice.h> +#include <linux/semaphore.h> #include <linux/spi/libertas_spi.h> #include <linux/spi/spi.h> diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index db38a5a719fa..f5d641efd50a 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -123,7 +123,7 @@ static ssize_t lbs_rtap_set(struct device *dev, if (priv->monitormode == monitor_mode) return strlen(buf); if (!priv->monitormode) { - if (priv->infra_open || priv->mesh_open) + if (priv->infra_open || lbs_mesh_open(priv)) return -EBUSY; if (priv->mode == IW_MODE_INFRA) lbs_cmd_80211_deauthenticate(priv, @@ -533,31 +533,14 @@ static int lbs_thread(void *data) if (priv->cmd_timed_out && priv->cur_cmd) { struct cmd_ctrl_node *cmdnode = priv->cur_cmd; - if (++priv->nr_retries > 3) { - lbs_pr_info("Excessive timeouts submitting " - "command 0x%04x\n", - le16_to_cpu(cmdnode->cmdbuf->command)); - lbs_complete_command(priv, cmdnode, -ETIMEDOUT); - priv->nr_retries = 0; - if (priv->reset_card) - priv->reset_card(priv); - } else { - priv->cur_cmd = NULL; - priv->dnld_sent = DNLD_RES_RECEIVED; - lbs_pr_info("requeueing command 0x%04x due " - "to timeout (#%d)\n", - le16_to_cpu(cmdnode->cmdbuf->command), - priv->nr_retries); - - /* Stick it back at the _top_ of the pending queue - for immediate resubmission */ - list_add(&cmdnode->list, &priv->cmdpendingq); - } + lbs_pr_info("Timeout submitting command 0x%04x\n", + le16_to_cpu(cmdnode->cmdbuf->command)); + lbs_complete_command(priv, cmdnode, -ETIMEDOUT); + if (priv->reset_card) + priv->reset_card(priv); } priv->cmd_timed_out = 0; - - if (!priv->fw_ready) continue; @@ -619,7 +602,7 @@ static int lbs_thread(void *data) if (priv->connect_status == LBS_CONNECTED) netif_wake_queue(priv->dev); if (priv->mesh_dev && - priv->mesh_connect_status == LBS_CONNECTED) + lbs_mesh_connected(priv)) netif_wake_queue(priv->mesh_dev); } } @@ -729,7 +712,7 @@ done: * This function handles the timeout of command sending. * It will re-send the same command again. */ -static void command_timer_fn(unsigned long data) +static void lbs_cmd_timeout_handler(unsigned long data) { struct lbs_private *priv = (struct lbs_private *)data; unsigned long flags; @@ -806,18 +789,6 @@ int lbs_exit_auto_deep_sleep(struct lbs_private *priv) return 0; } -static void lbs_sync_channel_worker(struct work_struct *work) -{ - struct lbs_private *priv = container_of(work, struct lbs_private, - sync_channel); - - lbs_deb_enter(LBS_DEB_MAIN); - if (lbs_update_channel(priv)) - lbs_pr_info("Channel synchronization failed."); - lbs_deb_leave(LBS_DEB_MAIN); -} - - static int lbs_init_adapter(struct lbs_private *priv) { size_t bufsize; @@ -845,14 +816,12 @@ static int lbs_init_adapter(struct lbs_private *priv) memset(priv->current_addr, 0xff, ETH_ALEN); priv->connect_status = LBS_DISCONNECTED; - priv->mesh_connect_status = LBS_DISCONNECTED; priv->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; priv->mode = IW_MODE_INFRA; priv->channel = DEFAULT_AD_HOC_CHANNEL; priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON; priv->radio_on = 1; priv->enablehwauto = 1; - priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE; priv->psmode = LBS802_11POWERMODECAM; priv->psstate = PS_STATE_FULL_POWER; priv->is_deep_sleep = 0; @@ -862,7 +831,7 @@ static int lbs_init_adapter(struct lbs_private *priv) mutex_init(&priv->lock); - setup_timer(&priv->command_timer, command_timer_fn, + setup_timer(&priv->command_timer, lbs_cmd_timeout_handler, (unsigned long)priv); setup_timer(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn, (unsigned long)priv); @@ -997,11 +966,6 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev) INIT_DELAYED_WORK(&priv->assoc_work, lbs_association_worker); INIT_DELAYED_WORK(&priv->scan_work, lbs_scan_worker); INIT_WORK(&priv->mcast_work, lbs_set_mcast_worker); - INIT_WORK(&priv->sync_channel, lbs_sync_channel_worker); - - priv->mesh_open = 0; - sprintf(priv->mesh_ssid, "mesh"); - priv->mesh_ssid_len = 4; priv->wol_criteria = 0xffffffff; priv->wol_gpio = 0xff; @@ -1075,6 +1039,17 @@ void lbs_remove_card(struct lbs_private *priv) EXPORT_SYMBOL_GPL(lbs_remove_card); +static int lbs_rtap_supported(struct lbs_private *priv) +{ + if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) + return 1; + + /* newer firmware use a capability mask */ + return ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) && + (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)); +} + + int lbs_start_card(struct lbs_private *priv) { struct net_device *dev = priv->dev; @@ -1094,12 +1069,14 @@ int lbs_start_card(struct lbs_private *priv) lbs_update_channel(priv); + lbs_init_mesh(priv); + /* * While rtap isn't related to mesh, only mesh-enabled * firmware implements the rtap functionality via * CMD_802_11_MONITOR_MODE. */ - if (lbs_init_mesh(priv)) { + if (lbs_rtap_supported(priv)) { if (device_create_file(&dev->dev, &dev_attr_lbs_rtap)) lbs_pr_err("cannot register lbs_rtap attribute\n"); } @@ -1133,7 +1110,9 @@ void lbs_stop_card(struct lbs_private *priv) netif_carrier_off(dev); lbs_debugfs_remove_one(priv); - if (lbs_deinit_mesh(priv)) + lbs_deinit_mesh(priv); + + if (lbs_rtap_supported(priv)) device_remove_file(&dev->dev, &dev_attr_lbs_rtap); /* Delete the timeout of the currently processing command */ diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/libertas/mesh.c index 2f91c9b808af..954cd00f7452 100644 --- a/drivers/net/wireless/libertas/mesh.c +++ b/drivers/net/wireless/libertas/mesh.c @@ -1,4 +1,3 @@ -#include <linux/moduleparam.h> #include <linux/delay.h> #include <linux/etherdevice.h> #include <linux/netdevice.h> @@ -196,7 +195,14 @@ int lbs_init_mesh(struct lbs_private *priv) lbs_deb_enter(LBS_DEB_MESH); - if (priv->mesh_fw_ver == MESH_FW_OLD) { + priv->mesh_connect_status = LBS_DISCONNECTED; + + /* Determine mesh_fw_ver from fwrelease and fwcapinfo */ + /* 5.0.16p0 9.0.0.p0 is known to NOT support any mesh */ + /* 5.110.22 have mesh command with 0xa3 command id */ + /* 10.0.0.p0 FW brings in mesh config command with different id */ + /* Check FW version MSB and initialize mesh_fw_ver */ + if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) { /* Enable mesh, if supported, and work out which TLV it uses. 0x100 + 291 is an unofficial value used in 5.110.20.pXX 0x100 + 37 is the official value used in 5.110.21.pXX @@ -218,7 +224,9 @@ int lbs_init_mesh(struct lbs_private *priv) priv->channel)) priv->mesh_tlv = 0; } - } else if (priv->mesh_fw_ver == MESH_FW_NEW) { + } else + if ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) && + (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)) { /* 10.0.0.pXX new firmwares should succeed with TLV * 0x100+37; Do not invoke command with old TLV. */ @@ -227,7 +235,12 @@ int lbs_init_mesh(struct lbs_private *priv) priv->channel)) priv->mesh_tlv = 0; } + + if (priv->mesh_tlv) { + sprintf(priv->mesh_ssid, "mesh"); + priv->mesh_ssid_len = 4; + lbs_add_mesh(priv); if (device_create_file(&dev->dev, &dev_attr_lbs_mesh)) @@ -416,10 +429,10 @@ struct net_device *lbs_mesh_set_dev(struct lbs_private *priv, struct net_device *dev, struct rxpd *rxpd) { if (priv->mesh_dev) { - if (priv->mesh_fw_ver == MESH_FW_OLD) { + if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) { if (rxpd->rx_control & RxPD_MESH_FRAME) dev = priv->mesh_dev; - } else if (priv->mesh_fw_ver == MESH_FW_NEW) { + } else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) { if (rxpd->u.bss.bss_num == MESH_IFACE_ID) dev = priv->mesh_dev; } @@ -432,9 +445,9 @@ void lbs_mesh_set_txpd(struct lbs_private *priv, struct net_device *dev, struct txpd *txpd) { if (dev == priv->mesh_dev) { - if (priv->mesh_fw_ver == MESH_FW_OLD) + if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME); - else if (priv->mesh_fw_ver == MESH_FW_NEW) + else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) txpd->u.bss.bss_num = MESH_IFACE_ID; } } @@ -538,7 +551,7 @@ static int __lbs_mesh_config_send(struct lbs_private *priv, * Command id is 0xac for v10 FW along with mesh interface * id in bits 14-13-12. */ - if (priv->mesh_fw_ver == MESH_FW_NEW) + if (priv->mesh_tlv == TLV_TYPE_MESH_ID) command = CMD_MESH_CONFIG | (MESH_IFACE_ID << MESH_IFACE_BIT_OFFSET); diff --git a/drivers/net/wireless/libertas/mesh.h b/drivers/net/wireless/libertas/mesh.h index fea9b5d005fc..e2573303a328 100644 --- a/drivers/net/wireless/libertas/mesh.h +++ b/drivers/net/wireless/libertas/mesh.h @@ -9,6 +9,8 @@ #include <net/lib80211.h> +#ifdef CONFIG_LIBERTAS_MESH + /* Mesh statistics */ struct lbs_mesh_stats { u32 fwd_bcast_cnt; /* Fwd: Broadcast counter */ @@ -46,11 +48,20 @@ void lbs_mesh_set_txpd(struct lbs_private *priv, /* Command handling */ struct cmd_ds_command; +struct cmd_ds_mesh_access; +struct cmd_ds_mesh_config; int lbs_cmd_bt_access(struct cmd_ds_command *cmd, u16 cmd_action, void *pdata_buf); int lbs_cmd_fwt_access(struct cmd_ds_command *cmd, u16 cmd_action, void *pdata_buf); +int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action, + struct cmd_ds_mesh_access *cmd); +int lbs_mesh_config_send(struct lbs_private *priv, + struct cmd_ds_mesh_config *cmd, + uint16_t action, uint16_t type); +int lbs_mesh_config(struct lbs_private *priv, uint16_t enable, uint16_t chan); + /* Persistent configuration */ @@ -75,4 +86,25 @@ void lbs_mesh_ethtool_get_strings(struct net_device *dev, uint32_t stringset, uint8_t *s); +/* Accessors */ + +#define lbs_mesh_open(priv) (priv->mesh_open) +#define lbs_mesh_connected(priv) (priv->mesh_connect_status == LBS_CONNECTED) + +#else + +#define lbs_init_mesh(priv) +#define lbs_deinit_mesh(priv) +#define lbs_add_mesh(priv) +#define lbs_remove_mesh(priv) +#define lbs_mesh_set_dev(priv, dev, rxpd) (dev) +#define lbs_mesh_set_txpd(priv, dev, txpd) +#define lbs_mesh_config(priv, enable, chan) +#define lbs_mesh_open(priv) (0) +#define lbs_mesh_connected(priv) (0) + +#endif + + + #endif diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c index b0b1c7841500..220361e69cd3 100644 --- a/drivers/net/wireless/libertas/scan.c +++ b/drivers/net/wireless/libertas/scan.c @@ -635,7 +635,7 @@ out: if (priv->connect_status == LBS_CONNECTED && !priv->tx_pending_len) netif_wake_queue(priv->dev); - if (priv->mesh_dev && (priv->mesh_connect_status == LBS_CONNECTED) && + if (priv->mesh_dev && lbs_mesh_connected(priv) && !priv->tx_pending_len) netif_wake_queue(priv->mesh_dev); diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c index 315d1ce286ca..52d244ea3d97 100644 --- a/drivers/net/wireless/libertas/tx.c +++ b/drivers/net/wireless/libertas/tx.c @@ -198,7 +198,7 @@ void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count) if (priv->connect_status == LBS_CONNECTED) netif_wake_queue(priv->dev); - if (priv->mesh_dev && (priv->mesh_connect_status == LBS_CONNECTED)) + if (priv->mesh_dev && lbs_mesh_connected(priv)) netif_wake_queue(priv->mesh_dev); } EXPORT_SYMBOL_GPL(lbs_send_tx_feedback); diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c index 4b1aab593a84..71f88a08e090 100644 --- a/drivers/net/wireless/libertas/wext.c +++ b/drivers/net/wireless/libertas/wext.c @@ -192,7 +192,7 @@ static void copy_active_data_rates(struct lbs_private *priv, u8 *rates) lbs_deb_enter(LBS_DEB_WEXT); if ((priv->connect_status != LBS_CONNECTED) && - (priv->mesh_connect_status != LBS_CONNECTED)) + !lbs_mesh_connected(priv)) memcpy(rates, lbs_bg_rates, MAX_RATES); else memcpy(rates, priv->curbssparams.rates, MAX_RATES); @@ -298,6 +298,7 @@ static int lbs_get_nick(struct net_device *dev, struct iw_request_info *info, return 0; } +#ifdef CONFIG_LIBERTAS_MESH static int mesh_get_nick(struct net_device *dev, struct iw_request_info *info, struct iw_point *dwrq, char *extra) { @@ -307,7 +308,7 @@ static int mesh_get_nick(struct net_device *dev, struct iw_request_info *info, /* Use nickname to indicate that mesh is on */ - if (priv->mesh_connect_status == LBS_CONNECTED) { + if (lbs_mesh_connected(priv)) { strncpy(extra, "Mesh", 12); extra[12] = '\0'; dwrq->length = strlen(extra); @@ -321,6 +322,7 @@ static int mesh_get_nick(struct net_device *dev, struct iw_request_info *info, lbs_deb_leave(LBS_DEB_WEXT); return 0; } +#endif static int lbs_set_rts(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra) @@ -422,6 +424,7 @@ static int lbs_get_mode(struct net_device *dev, return 0; } +#ifdef CONFIG_LIBERTAS_MESH static int mesh_wlan_get_mode(struct net_device *dev, struct iw_request_info *info, u32 * uwrq, char *extra) @@ -433,6 +436,7 @@ static int mesh_wlan_get_mode(struct net_device *dev, lbs_deb_leave(LBS_DEB_WEXT); return 0; } +#endif static int lbs_get_txpow(struct net_device *dev, struct iw_request_info *info, @@ -863,7 +867,7 @@ static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev) /* If we're not associated, all quality values are meaningless */ if ((priv->connect_status != LBS_CONNECTED) && - (priv->mesh_connect_status != LBS_CONNECTED)) + !lbs_mesh_connected(priv)) goto out; /* Quality by RSSI */ @@ -1010,6 +1014,7 @@ out: return ret; } +#ifdef CONFIG_LIBERTAS_MESH static int lbs_mesh_set_freq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *fwrq, char *extra) @@ -1061,6 +1066,7 @@ out: lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); return ret; } +#endif static int lbs_set_rate(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra) @@ -2108,6 +2114,7 @@ out: return ret; } +#ifdef CONFIG_LIBERTAS_MESH static int lbs_mesh_get_essid(struct net_device *dev, struct iw_request_info *info, struct iw_point *dwrq, char *extra) @@ -2161,6 +2168,7 @@ static int lbs_mesh_set_essid(struct net_device *dev, lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); return ret; } +#endif /** * @brief Connect to the AP or Ad-hoc Network with specific bssid @@ -2267,7 +2275,13 @@ static const iw_handler lbs_handler[] = { (iw_handler) lbs_get_encodeext,/* SIOCGIWENCODEEXT */ (iw_handler) NULL, /* SIOCSIWPMKSA */ }; +struct iw_handler_def lbs_handler_def = { + .num_standard = ARRAY_SIZE(lbs_handler), + .standard = (iw_handler *) lbs_handler, + .get_wireless_stats = lbs_get_wireless_stats, +}; +#ifdef CONFIG_LIBERTAS_MESH static const iw_handler mesh_wlan_handler[] = { (iw_handler) NULL, /* SIOCSIWCOMMIT */ (iw_handler) lbs_get_name, /* SIOCGIWNAME */ @@ -2325,14 +2339,10 @@ static const iw_handler mesh_wlan_handler[] = { (iw_handler) lbs_get_encodeext,/* SIOCGIWENCODEEXT */ (iw_handler) NULL, /* SIOCSIWPMKSA */ }; -struct iw_handler_def lbs_handler_def = { - .num_standard = ARRAY_SIZE(lbs_handler), - .standard = (iw_handler *) lbs_handler, - .get_wireless_stats = lbs_get_wireless_stats, -}; struct iw_handler_def mesh_handler_def = { .num_standard = ARRAY_SIZE(mesh_wlan_handler), .standard = (iw_handler *) mesh_wlan_handler, .get_wireless_stats = lbs_get_wireless_stats, }; +#endif diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/libertas_tf/main.c index 26a1abd5bb03..6ab30033c26c 100644 --- a/drivers/net/wireless/libertas_tf/main.c +++ b/drivers/net/wireless/libertas_tf/main.c @@ -318,14 +318,14 @@ static void lbtf_op_stop(struct ieee80211_hw *hw) } static int lbtf_op_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct lbtf_private *priv = hw->priv; if (priv->vif != NULL) return -EOPNOTSUPP; - priv->vif = conf->vif; - switch (conf->type) { + priv->vif = vif; + switch (vif->type) { case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: lbtf_set_mode(priv, LBTF_AP_MODE); @@ -337,12 +337,12 @@ static int lbtf_op_add_interface(struct ieee80211_hw *hw, priv->vif = NULL; return -EOPNOTSUPP; } - lbtf_set_mac_address(priv, (u8 *) conf->mac_addr); + lbtf_set_mac_address(priv, (u8 *) vif->addr); return 0; } static void lbtf_op_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct lbtf_private *priv = hw->priv; @@ -555,6 +555,9 @@ struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev) priv->band.n_channels = ARRAY_SIZE(lbtf_channels); priv->band.channels = priv->channels; hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC); skb_queue_head_init(&priv->bc_ps_buf); SET_IEEE80211_DEV(hw, dmdev); diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 88e41176e7fd..6ea77e95277b 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -32,6 +32,10 @@ static int radios = 2; module_param(radios, int, 0444); MODULE_PARM_DESC(radios, "Number of simulated radios"); +static bool fake_hw_scan; +module_param(fake_hw_scan, bool, 0444); +MODULE_PARM_DESC(fake_hw_scan, "Install fake (no-op) hw-scan handler"); + /** * enum hwsim_regtest - the type of regulatory tests we offer * @@ -281,6 +285,8 @@ struct mac80211_hwsim_data { struct ieee80211_channel channels_5ghz[ARRAY_SIZE(hwsim_channels_5ghz)]; struct ieee80211_rate rates[ARRAY_SIZE(hwsim_rates)]; + struct mac_address addresses[2]; + struct ieee80211_channel *channel; unsigned long beacon_int; /* in jiffies unit */ unsigned int rx_filter; @@ -436,6 +442,38 @@ static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data, } +struct mac80211_hwsim_addr_match_data { + bool ret; + const u8 *addr; +}; + +static void mac80211_hwsim_addr_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mac80211_hwsim_addr_match_data *md = data; + if (memcmp(mac, md->addr, ETH_ALEN) == 0) + md->ret = true; +} + + +static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data, + const u8 *addr) +{ + struct mac80211_hwsim_addr_match_data md; + + if (memcmp(addr, data->hw->wiphy->perm_addr, ETH_ALEN) == 0) + return true; + + md.ret = false; + md.addr = addr; + ieee80211_iterate_active_interfaces_atomic(data->hw, + mac80211_hwsim_addr_iter, + &md); + + return md.ret; +} + + static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, struct sk_buff *skb) { @@ -488,8 +526,7 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, if (nskb == NULL) continue; - if (memcmp(hdr->addr1, data2->hw->wiphy->perm_addr, - ETH_ALEN) == 0) + if (mac80211_hwsim_addr_match(data2, hdr->addr1)) ack = true; memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status)); ieee80211_rx_irqsafe(data2->hw, nskb); @@ -553,24 +590,24 @@ static void mac80211_hwsim_stop(struct ieee80211_hw *hw) static int mac80211_hwsim_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { printk(KERN_DEBUG "%s:%s (type=%d mac_addr=%pM)\n", - wiphy_name(hw->wiphy), __func__, conf->type, - conf->mac_addr); - hwsim_set_magic(conf->vif); + wiphy_name(hw->wiphy), __func__, vif->type, + vif->addr); + hwsim_set_magic(vif); return 0; } static void mac80211_hwsim_remove_interface( - struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf) + struct ieee80211_hw *hw, struct ieee80211_vif *vif) { printk(KERN_DEBUG "%s:%s (type=%d mac_addr=%pM)\n", - wiphy_name(hw->wiphy), __func__, conf->type, - conf->mac_addr); - hwsim_check_magic(conf->vif); - hwsim_clear_magic(conf->vif); + wiphy_name(hw->wiphy), __func__, vif->type, + vif->addr); + hwsim_check_magic(vif); + hwsim_clear_magic(vif); } @@ -618,12 +655,26 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) { struct mac80211_hwsim_data *data = hw->priv; struct ieee80211_conf *conf = &hw->conf; - - printk(KERN_DEBUG "%s:%s (freq=%d idle=%d ps=%d)\n", + static const char *chantypes[4] = { + [NL80211_CHAN_NO_HT] = "noht", + [NL80211_CHAN_HT20] = "ht20", + [NL80211_CHAN_HT40MINUS] = "ht40-", + [NL80211_CHAN_HT40PLUS] = "ht40+", + }; + static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = { + [IEEE80211_SMPS_AUTOMATIC] = "auto", + [IEEE80211_SMPS_OFF] = "off", + [IEEE80211_SMPS_STATIC] = "static", + [IEEE80211_SMPS_DYNAMIC] = "dynamic", + }; + + printk(KERN_DEBUG "%s:%s (freq=%d/%s idle=%d ps=%d smps=%s)\n", wiphy_name(hw->wiphy), __func__, conf->channel->center_freq, + chantypes[conf->channel_type], !!(conf->flags & IEEE80211_CONF_IDLE), - !!(conf->flags & IEEE80211_CONF_PS)); + !!(conf->flags & IEEE80211_CONF_PS), + smps_modes[conf->smps_mode]); data->idle = !!(conf->flags & IEEE80211_CONF_IDLE); @@ -720,23 +771,41 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, } } +static int mac80211_hwsim_sta_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + hwsim_check_magic(vif); + hwsim_set_sta_magic(sta); + + return 0; +} + +static int mac80211_hwsim_sta_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + hwsim_check_magic(vif); + hwsim_clear_sta_magic(sta); + + return 0; +} + static void mac80211_hwsim_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd cmd, struct ieee80211_sta *sta) { hwsim_check_magic(vif); + switch (cmd) { - case STA_NOTIFY_ADD: - hwsim_set_sta_magic(sta); - break; - case STA_NOTIFY_REMOVE: - hwsim_clear_sta_magic(sta); - break; case STA_NOTIFY_SLEEP: case STA_NOTIFY_AWAKE: /* TODO: make good use of these flags */ break; + default: + WARN(1, "Invalid sta notify: %d\n", cmd); + break; } } @@ -827,7 +896,77 @@ static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw, } #endif -static const struct ieee80211_ops mac80211_hwsim_ops = +static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn) +{ + switch (action) { + case IEEE80211_AMPDU_TX_START: + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + case IEEE80211_AMPDU_TX_STOP: + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + break; + case IEEE80211_AMPDU_RX_START: + case IEEE80211_AMPDU_RX_STOP: + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static void mac80211_hwsim_flush(struct ieee80211_hw *hw, bool drop) +{ + /* + * In this special case, there's nothing we need to + * do because hwsim does transmission synchronously. + * In the future, when it does transmissions via + * userspace, we may need to do something. + */ +} + +struct hw_scan_done { + struct delayed_work w; + struct ieee80211_hw *hw; +}; + +static void hw_scan_done(struct work_struct *work) +{ + struct hw_scan_done *hsd = + container_of(work, struct hw_scan_done, w.work); + + ieee80211_scan_completed(hsd->hw, false); + kfree(hsd); +} + +static int mac80211_hwsim_hw_scan(struct ieee80211_hw *hw, + struct cfg80211_scan_request *req) +{ + struct hw_scan_done *hsd = kzalloc(sizeof(*hsd), GFP_KERNEL); + int i; + + if (!hsd) + return -ENOMEM; + + hsd->hw = hw; + INIT_DELAYED_WORK(&hsd->w, hw_scan_done); + + printk(KERN_DEBUG "hwsim scan request\n"); + for (i = 0; i < req->n_channels; i++) + printk(KERN_DEBUG "hwsim scan freq %d\n", + req->channels[i]->center_freq); + + ieee80211_queue_delayed_work(hw, &hsd->w, 2 * HZ); + + return 0; +} + +static struct ieee80211_ops mac80211_hwsim_ops = { .tx = mac80211_hwsim_tx, .start = mac80211_hwsim_start, @@ -837,10 +976,14 @@ static const struct ieee80211_ops mac80211_hwsim_ops = .config = mac80211_hwsim_config, .configure_filter = mac80211_hwsim_configure_filter, .bss_info_changed = mac80211_hwsim_bss_info_changed, + .sta_add = mac80211_hwsim_sta_add, + .sta_remove = mac80211_hwsim_sta_remove, .sta_notify = mac80211_hwsim_sta_notify, .set_tim = mac80211_hwsim_set_tim, .conf_tx = mac80211_hwsim_conf_tx, CFG80211_TESTMODE_CMD(mac80211_hwsim_testmode_cmd) + .ampdu_action = mac80211_hwsim_ampdu_action, + .flush = mac80211_hwsim_flush, }; @@ -1035,6 +1178,9 @@ static int __init init_mac80211_hwsim(void) if (radios < 1 || radios > 100) return -EINVAL; + if (fake_hw_scan) + mac80211_hwsim_ops.hw_scan = mac80211_hwsim_hw_scan; + spin_lock_init(&hwsim_radio_lock); INIT_LIST_HEAD(&hwsim_radios); @@ -1072,7 +1218,11 @@ static int __init init_mac80211_hwsim(void) SET_IEEE80211_DEV(hw, data->dev); addr[3] = i >> 8; addr[4] = i; - SET_IEEE80211_PERM_ADDR(hw, addr); + memcpy(data->addresses[0].addr, addr, ETH_ALEN); + memcpy(data->addresses[1].addr, addr, ETH_ALEN); + data->addresses[1].addr[0] |= 0x40; + hw->wiphy->n_addresses = 2; + hw->wiphy->addresses = data->addresses; hw->channel_change_time = 1; hw->queues = 4; @@ -1082,7 +1232,9 @@ static int __init init_mac80211_hwsim(void) BIT(NL80211_IFTYPE_MESH_POINT); hw->flags = IEEE80211_HW_MFP_CAPABLE | - IEEE80211_HW_SIGNAL_DBM; + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_STATIC_SMPS | + IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS; /* ask mac80211 to reserve space for magic */ hw->vif_data_size = sizeof(struct hwsim_vif_priv); diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 59f92105b0c2..ac65e13eb0de 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -2,7 +2,7 @@ * drivers/net/wireless/mwl8k.c * Driver for Marvell TOPDOG 802.11 Wireless cards * - * Copyright (C) 2008-2009 Marvell Semiconductor Inc. + * Copyright (C) 2008, 2009, 2010 Marvell Semiconductor Inc. * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any @@ -26,7 +26,7 @@ #define MWL8K_DESC "Marvell TOPDOG(R) 802.11 Wireless Network Driver" #define MWL8K_NAME KBUILD_MODNAME -#define MWL8K_VERSION "0.10" +#define MWL8K_VERSION "0.12" /* Register definitions */ #define MWL8K_HIU_GEN_PTR 0x00000c10 @@ -92,8 +92,7 @@ struct mwl8k_device_info { char *part_name; char *helper_image; char *fw_image; - struct rxd_ops *rxd_ops; - u16 modes; + struct rxd_ops *ap_rxd_ops; }; struct mwl8k_rx_queue { @@ -120,34 +119,36 @@ struct mwl8k_tx_queue { /* sw appends here */ int tail; - struct ieee80211_tx_queue_stats stats; + unsigned int len; struct mwl8k_tx_desc *txd; dma_addr_t txd_dma; struct sk_buff **skb; }; -/* Pointers to the firmware data and meta information about it. */ -struct mwl8k_firmware { - /* Boot helper code */ - struct firmware *helper; +struct mwl8k_priv { + struct ieee80211_hw *hw; + struct pci_dev *pdev; - /* Microcode */ - struct firmware *ucode; -}; + struct mwl8k_device_info *device_info; -struct mwl8k_priv { void __iomem *sram; void __iomem *regs; - struct ieee80211_hw *hw; - struct pci_dev *pdev; + /* firmware */ + struct firmware *fw_helper; + struct firmware *fw_ucode; - struct mwl8k_device_info *device_info; + /* hardware/firmware parameters */ bool ap_fw; struct rxd_ops *rxd_ops; - - /* firmware files and meta data */ - struct mwl8k_firmware fw; + struct ieee80211_supported_band band_24; + struct ieee80211_channel channels_24[14]; + struct ieee80211_rate rates_24[14]; + struct ieee80211_supported_band band_50; + struct ieee80211_channel channels_50[4]; + struct ieee80211_rate rates_50[9]; + u32 ap_macids_supported; + u32 sta_macids_supported; /* firmware access */ struct mutex fw_mutex; @@ -161,9 +162,9 @@ struct mwl8k_priv { /* TX quiesce completion, protected by fw_mutex and tx_lock */ struct completion *tx_wait; - struct ieee80211_vif *vif; - - struct ieee80211_channel *current_channel; + /* List of interfaces. */ + u32 macids_used; + struct list_head vif_list; /* power management status cookie from firmware */ u32 *cookie; @@ -182,11 +183,6 @@ struct mwl8k_priv { struct mwl8k_rx_queue rxq[MWL8K_RX_QUEUES]; struct mwl8k_tx_queue txq[MWL8K_TX_QUEUES]; - /* PHY parameters */ - struct ieee80211_supported_band band; - struct ieee80211_channel channels[14]; - struct ieee80211_rate rates[14]; - bool radio_on; bool radio_short_preamble; bool sniffer_enabled; @@ -205,32 +201,33 @@ struct mwl8k_priv { */ struct work_struct finalize_join_worker; - /* Tasklet to reclaim TX descriptors and buffers after tx */ - struct tasklet_struct tx_reclaim_task; + /* Tasklet to perform TX reclaim. */ + struct tasklet_struct poll_tx_task; + + /* Tasklet to perform RX. */ + struct tasklet_struct poll_rx_task; }; /* Per interface specific private data */ struct mwl8k_vif { - /* backpointer to parent config block */ - struct mwl8k_priv *priv; - - /* BSS config of AP or IBSS from mac80211*/ - struct ieee80211_bss_conf bss_info; - - /* BSSID of AP or IBSS */ - u8 bssid[ETH_ALEN]; - u8 mac_addr[ETH_ALEN]; + struct list_head list; + struct ieee80211_vif *vif; - /* Index into station database.Returned by update_sta_db call */ - u8 peer_id; + /* Firmware macid for this vif. */ + int macid; - /* Non AMPDU sequence number assigned by driver */ - u16 seqno; + /* Non AMPDU sequence number assigned by driver. */ + u16 seqno; }; - #define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv)) -static const struct ieee80211_channel mwl8k_channels[] = { +struct mwl8k_sta { + /* Index into station database. Returned by UPDATE_STADB. */ + u8 peer_id; +}; +#define MWL8K_STA(_sta) ((struct mwl8k_sta *)&((_sta)->drv_priv)) + +static const struct ieee80211_channel mwl8k_channels_24[] = { { .center_freq = 2412, .hw_value = 1, }, { .center_freq = 2417, .hw_value = 2, }, { .center_freq = 2422, .hw_value = 3, }, @@ -242,9 +239,12 @@ static const struct ieee80211_channel mwl8k_channels[] = { { .center_freq = 2452, .hw_value = 9, }, { .center_freq = 2457, .hw_value = 10, }, { .center_freq = 2462, .hw_value = 11, }, + { .center_freq = 2467, .hw_value = 12, }, + { .center_freq = 2472, .hw_value = 13, }, + { .center_freq = 2484, .hw_value = 14, }, }; -static const struct ieee80211_rate mwl8k_rates[] = { +static const struct ieee80211_rate mwl8k_rates_24[] = { { .bitrate = 10, .hw_value = 2, }, { .bitrate = 20, .hw_value = 4, }, { .bitrate = 55, .hw_value = 11, }, @@ -261,8 +261,23 @@ static const struct ieee80211_rate mwl8k_rates[] = { { .bitrate = 720, .hw_value = 144, }, }; -static const u8 mwl8k_rateids[12] = { - 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108, +static const struct ieee80211_channel mwl8k_channels_50[] = { + { .center_freq = 5180, .hw_value = 36, }, + { .center_freq = 5200, .hw_value = 40, }, + { .center_freq = 5220, .hw_value = 44, }, + { .center_freq = 5240, .hw_value = 48, }, +}; + +static const struct ieee80211_rate mwl8k_rates_50[] = { + { .bitrate = 60, .hw_value = 12, }, + { .bitrate = 90, .hw_value = 18, }, + { .bitrate = 120, .hw_value = 24, }, + { .bitrate = 180, .hw_value = 36, }, + { .bitrate = 240, .hw_value = 48, }, + { .bitrate = 360, .hw_value = 72, }, + { .bitrate = 480, .hw_value = 96, }, + { .bitrate = 540, .hw_value = 108, }, + { .bitrate = 720, .hw_value = 144, }, }; /* Set or get info from Firmware */ @@ -278,6 +293,7 @@ static const u8 mwl8k_rateids[12] = { #define MWL8K_CMD_RADIO_CONTROL 0x001c #define MWL8K_CMD_RF_TX_POWER 0x001e #define MWL8K_CMD_RF_ANTENNA 0x0020 +#define MWL8K_CMD_SET_BEACON 0x0100 /* per-vif */ #define MWL8K_CMD_SET_PRE_SCAN 0x0107 #define MWL8K_CMD_SET_POST_SCAN 0x0108 #define MWL8K_CMD_SET_RF_CHANNEL 0x010a @@ -291,8 +307,10 @@ static const u8 mwl8k_rateids[12] = { #define MWL8K_CMD_MIMO_CONFIG 0x0125 #define MWL8K_CMD_USE_FIXED_RATE 0x0126 #define MWL8K_CMD_ENABLE_SNIFFER 0x0150 -#define MWL8K_CMD_SET_MAC_ADDR 0x0202 +#define MWL8K_CMD_SET_MAC_ADDR 0x0202 /* per-vif */ #define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203 +#define MWL8K_CMD_BSS_START 0x1100 /* per-vif */ +#define MWL8K_CMD_SET_NEW_STN 0x1111 /* per-vif */ #define MWL8K_CMD_UPDATE_STADB 0x1123 static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize) @@ -310,6 +328,7 @@ static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize) MWL8K_CMDNAME(RADIO_CONTROL); MWL8K_CMDNAME(RF_TX_POWER); MWL8K_CMDNAME(RF_ANTENNA); + MWL8K_CMDNAME(SET_BEACON); MWL8K_CMDNAME(SET_PRE_SCAN); MWL8K_CMDNAME(SET_POST_SCAN); MWL8K_CMDNAME(SET_RF_CHANNEL); @@ -325,6 +344,8 @@ static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize) MWL8K_CMDNAME(ENABLE_SNIFFER); MWL8K_CMDNAME(SET_MAC_ADDR); MWL8K_CMDNAME(SET_RATEADAPT_MODE); + MWL8K_CMDNAME(BSS_START); + MWL8K_CMDNAME(SET_NEW_STN); MWL8K_CMDNAME(UPDATE_STADB); default: snprintf(buf, bufsize, "0x%x", cmd); @@ -355,8 +376,8 @@ static void mwl8k_release_fw(struct firmware **fw) static void mwl8k_release_firmware(struct mwl8k_priv *priv) { - mwl8k_release_fw(&priv->fw.ucode); - mwl8k_release_fw(&priv->fw.helper); + mwl8k_release_fw(&priv->fw_ucode); + mwl8k_release_fw(&priv->fw_helper); } /* Request fw image */ @@ -377,7 +398,7 @@ static int mwl8k_request_firmware(struct mwl8k_priv *priv) int rc; if (di->helper_image != NULL) { - rc = mwl8k_request_fw(priv, di->helper_image, &priv->fw.helper); + rc = mwl8k_request_fw(priv, di->helper_image, &priv->fw_helper); if (rc) { printk(KERN_ERR "%s: Error requesting helper " "firmware file %s\n", pci_name(priv->pdev), @@ -386,24 +407,22 @@ static int mwl8k_request_firmware(struct mwl8k_priv *priv) } } - rc = mwl8k_request_fw(priv, di->fw_image, &priv->fw.ucode); + rc = mwl8k_request_fw(priv, di->fw_image, &priv->fw_ucode); if (rc) { printk(KERN_ERR "%s: Error requesting firmware file %s\n", pci_name(priv->pdev), di->fw_image); - mwl8k_release_fw(&priv->fw.helper); + mwl8k_release_fw(&priv->fw_helper); return rc; } return 0; } -MODULE_FIRMWARE("mwl8k/helper_8687.fw"); -MODULE_FIRMWARE("mwl8k/fmimage_8687.fw"); - struct mwl8k_cmd_pkt { __le16 code; __le16 length; - __le16 seq_num; + __u8 seq_num; + __u8 macid; __le16 result; char payload[0]; } __attribute__((packed)); @@ -461,6 +480,7 @@ static int mwl8k_load_fw_image(struct mwl8k_priv *priv, cmd->code = cpu_to_le16(MWL8K_CMD_CODE_DNLD); cmd->seq_num = 0; + cmd->macid = 0; cmd->result = 0; done = 0; @@ -551,13 +571,12 @@ static int mwl8k_feed_fw_image(struct mwl8k_priv *priv, static int mwl8k_load_firmware(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; - struct firmware *fw = priv->fw.ucode; - struct mwl8k_device_info *di = priv->device_info; + struct firmware *fw = priv->fw_ucode; int rc; int loops; if (!memcmp(fw->data, "\x01\x00\x00\x00", 4)) { - struct firmware *helper = priv->fw.helper; + struct firmware *helper = priv->fw_helper; if (helper == NULL) { printk(KERN_ERR "%s: helper image needed but none " @@ -584,10 +603,7 @@ static int mwl8k_load_firmware(struct ieee80211_hw *hw) return rc; } - if (di->modes & BIT(NL80211_IFTYPE_AP)) - iowrite32(MWL8K_MODE_AP, priv->regs + MWL8K_HIU_GEN_PTR); - else - iowrite32(MWL8K_MODE_STA, priv->regs + MWL8K_HIU_GEN_PTR); + iowrite32(MWL8K_MODE_STA, priv->regs + MWL8K_HIU_GEN_PTR); loops = 500000; do { @@ -610,91 +626,6 @@ static int mwl8k_load_firmware(struct ieee80211_hw *hw) } -/* - * Defines shared between transmission and reception. - */ -/* HT control fields for firmware */ -struct ewc_ht_info { - __le16 control1; - __le16 control2; - __le16 control3; -} __attribute__((packed)); - -/* Firmware Station database operations */ -#define MWL8K_STA_DB_ADD_ENTRY 0 -#define MWL8K_STA_DB_MODIFY_ENTRY 1 -#define MWL8K_STA_DB_DEL_ENTRY 2 -#define MWL8K_STA_DB_FLUSH 3 - -/* Peer Entry flags - used to define the type of the peer node */ -#define MWL8K_PEER_TYPE_ACCESSPOINT 2 - -struct peer_capability_info { - /* Peer type - AP vs. STA. */ - __u8 peer_type; - - /* Basic 802.11 capabilities from assoc resp. */ - __le16 basic_caps; - - /* Set if peer supports 802.11n high throughput (HT). */ - __u8 ht_support; - - /* Valid if HT is supported. */ - __le16 ht_caps; - __u8 extended_ht_caps; - struct ewc_ht_info ewc_info; - - /* Legacy rate table. Intersection of our rates and peer rates. */ - __u8 legacy_rates[12]; - - /* HT rate table. Intersection of our rates and peer rates. */ - __u8 ht_rates[16]; - __u8 pad[16]; - - /* If set, interoperability mode, no proprietary extensions. */ - __u8 interop; - __u8 pad2; - __u8 station_id; - __le16 amsdu_enabled; -} __attribute__((packed)); - -/* Inline functions to manipulate QoS field in data descriptor. */ -static inline u16 mwl8k_qos_setbit_eosp(u16 qos) -{ - u16 val_mask = 1 << 4; - - /* End of Service Period Bit 4 */ - return qos | val_mask; -} - -static inline u16 mwl8k_qos_setbit_ack(u16 qos, u8 ack_policy) -{ - u16 val_mask = 0x3; - u8 shift = 5; - u16 qos_mask = ~(val_mask << shift); - - /* Ack Policy Bit 5-6 */ - return (qos & qos_mask) | ((ack_policy & val_mask) << shift); -} - -static inline u16 mwl8k_qos_setbit_amsdu(u16 qos) -{ - u16 val_mask = 1 << 7; - - /* AMSDU present Bit 7 */ - return qos | val_mask; -} - -static inline u16 mwl8k_qos_setbit_qlen(u16 qos, u8 len) -{ - u16 val_mask = 0xff; - u8 shift = 8; - u16 qos_mask = ~(val_mask << shift); - - /* Queue Length Bits 8-15 */ - return (qos & qos_mask) | ((len & val_mask) << shift); -} - /* DMA header used by firmware and hardware. */ struct mwl8k_dma_data { __le16 fwlen; @@ -761,9 +692,9 @@ static inline void mwl8k_add_dma_header(struct sk_buff *skb) /* - * Packet reception for 88w8366. + * Packet reception for 88w8366 AP firmware. */ -struct mwl8k_rxd_8366 { +struct mwl8k_rxd_8366_ap { __le16 pkt_len; __u8 sq2; __u8 rate; @@ -781,23 +712,23 @@ struct mwl8k_rxd_8366 { __u8 rx_ctrl; } __attribute__((packed)); -#define MWL8K_8366_RATE_INFO_MCS_FORMAT 0x80 -#define MWL8K_8366_RATE_INFO_40MHZ 0x40 -#define MWL8K_8366_RATE_INFO_RATEID(x) ((x) & 0x3f) +#define MWL8K_8366_AP_RATE_INFO_MCS_FORMAT 0x80 +#define MWL8K_8366_AP_RATE_INFO_40MHZ 0x40 +#define MWL8K_8366_AP_RATE_INFO_RATEID(x) ((x) & 0x3f) -#define MWL8K_8366_RX_CTRL_OWNED_BY_HOST 0x80 +#define MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST 0x80 -static void mwl8k_rxd_8366_init(void *_rxd, dma_addr_t next_dma_addr) +static void mwl8k_rxd_8366_ap_init(void *_rxd, dma_addr_t next_dma_addr) { - struct mwl8k_rxd_8366 *rxd = _rxd; + struct mwl8k_rxd_8366_ap *rxd = _rxd; rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr); - rxd->rx_ctrl = MWL8K_8366_RX_CTRL_OWNED_BY_HOST; + rxd->rx_ctrl = MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST; } -static void mwl8k_rxd_8366_refill(void *_rxd, dma_addr_t addr, int len) +static void mwl8k_rxd_8366_ap_refill(void *_rxd, dma_addr_t addr, int len) { - struct mwl8k_rxd_8366 *rxd = _rxd; + struct mwl8k_rxd_8366_ap *rxd = _rxd; rxd->pkt_len = cpu_to_le16(len); rxd->pkt_phys_addr = cpu_to_le32(addr); @@ -806,12 +737,12 @@ static void mwl8k_rxd_8366_refill(void *_rxd, dma_addr_t addr, int len) } static int -mwl8k_rxd_8366_process(void *_rxd, struct ieee80211_rx_status *status, - __le16 *qos) +mwl8k_rxd_8366_ap_process(void *_rxd, struct ieee80211_rx_status *status, + __le16 *qos) { - struct mwl8k_rxd_8366 *rxd = _rxd; + struct mwl8k_rxd_8366_ap *rxd = _rxd; - if (!(rxd->rx_ctrl & MWL8K_8366_RX_CTRL_OWNED_BY_HOST)) + if (!(rxd->rx_ctrl & MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST)) return -1; rmb(); @@ -820,23 +751,29 @@ mwl8k_rxd_8366_process(void *_rxd, struct ieee80211_rx_status *status, status->signal = -rxd->rssi; status->noise = -rxd->noise_floor; - if (rxd->rate & MWL8K_8366_RATE_INFO_MCS_FORMAT) { + if (rxd->rate & MWL8K_8366_AP_RATE_INFO_MCS_FORMAT) { status->flag |= RX_FLAG_HT; - if (rxd->rate & MWL8K_8366_RATE_INFO_40MHZ) + if (rxd->rate & MWL8K_8366_AP_RATE_INFO_40MHZ) status->flag |= RX_FLAG_40MHZ; - status->rate_idx = MWL8K_8366_RATE_INFO_RATEID(rxd->rate); + status->rate_idx = MWL8K_8366_AP_RATE_INFO_RATEID(rxd->rate); } else { int i; - for (i = 0; i < ARRAY_SIZE(mwl8k_rates); i++) { - if (mwl8k_rates[i].hw_value == rxd->rate) { + for (i = 0; i < ARRAY_SIZE(mwl8k_rates_24); i++) { + if (mwl8k_rates_24[i].hw_value == rxd->rate) { status->rate_idx = i; break; } } } - status->band = IEEE80211_BAND_2GHZ; + if (rxd->channel > 14) { + status->band = IEEE80211_BAND_5GHZ; + if (!(status->flag & RX_FLAG_HT)) + status->rate_idx -= 5; + } else { + status->band = IEEE80211_BAND_2GHZ; + } status->freq = ieee80211_channel_to_frequency(rxd->channel); *qos = rxd->qos_control; @@ -844,17 +781,17 @@ mwl8k_rxd_8366_process(void *_rxd, struct ieee80211_rx_status *status, return le16_to_cpu(rxd->pkt_len); } -static struct rxd_ops rxd_8366_ops = { - .rxd_size = sizeof(struct mwl8k_rxd_8366), - .rxd_init = mwl8k_rxd_8366_init, - .rxd_refill = mwl8k_rxd_8366_refill, - .rxd_process = mwl8k_rxd_8366_process, +static struct rxd_ops rxd_8366_ap_ops = { + .rxd_size = sizeof(struct mwl8k_rxd_8366_ap), + .rxd_init = mwl8k_rxd_8366_ap_init, + .rxd_refill = mwl8k_rxd_8366_ap_refill, + .rxd_process = mwl8k_rxd_8366_ap_process, }; /* - * Packet reception for 88w8687. + * Packet reception for STA firmware. */ -struct mwl8k_rxd_8687 { +struct mwl8k_rxd_sta { __le16 pkt_len; __u8 link_quality; __u8 noise_level; @@ -871,26 +808,26 @@ struct mwl8k_rxd_8687 { __u8 pad2[2]; } __attribute__((packed)); -#define MWL8K_8687_RATE_INFO_SHORTPRE 0x8000 -#define MWL8K_8687_RATE_INFO_ANTSELECT(x) (((x) >> 11) & 0x3) -#define MWL8K_8687_RATE_INFO_RATEID(x) (((x) >> 3) & 0x3f) -#define MWL8K_8687_RATE_INFO_40MHZ 0x0004 -#define MWL8K_8687_RATE_INFO_SHORTGI 0x0002 -#define MWL8K_8687_RATE_INFO_MCS_FORMAT 0x0001 +#define MWL8K_STA_RATE_INFO_SHORTPRE 0x8000 +#define MWL8K_STA_RATE_INFO_ANTSELECT(x) (((x) >> 11) & 0x3) +#define MWL8K_STA_RATE_INFO_RATEID(x) (((x) >> 3) & 0x3f) +#define MWL8K_STA_RATE_INFO_40MHZ 0x0004 +#define MWL8K_STA_RATE_INFO_SHORTGI 0x0002 +#define MWL8K_STA_RATE_INFO_MCS_FORMAT 0x0001 -#define MWL8K_8687_RX_CTRL_OWNED_BY_HOST 0x02 +#define MWL8K_STA_RX_CTRL_OWNED_BY_HOST 0x02 -static void mwl8k_rxd_8687_init(void *_rxd, dma_addr_t next_dma_addr) +static void mwl8k_rxd_sta_init(void *_rxd, dma_addr_t next_dma_addr) { - struct mwl8k_rxd_8687 *rxd = _rxd; + struct mwl8k_rxd_sta *rxd = _rxd; rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr); - rxd->rx_ctrl = MWL8K_8687_RX_CTRL_OWNED_BY_HOST; + rxd->rx_ctrl = MWL8K_STA_RX_CTRL_OWNED_BY_HOST; } -static void mwl8k_rxd_8687_refill(void *_rxd, dma_addr_t addr, int len) +static void mwl8k_rxd_sta_refill(void *_rxd, dma_addr_t addr, int len) { - struct mwl8k_rxd_8687 *rxd = _rxd; + struct mwl8k_rxd_sta *rxd = _rxd; rxd->pkt_len = cpu_to_le16(len); rxd->pkt_phys_addr = cpu_to_le32(addr); @@ -899,13 +836,13 @@ static void mwl8k_rxd_8687_refill(void *_rxd, dma_addr_t addr, int len) } static int -mwl8k_rxd_8687_process(void *_rxd, struct ieee80211_rx_status *status, +mwl8k_rxd_sta_process(void *_rxd, struct ieee80211_rx_status *status, __le16 *qos) { - struct mwl8k_rxd_8687 *rxd = _rxd; + struct mwl8k_rxd_sta *rxd = _rxd; u16 rate_info; - if (!(rxd->rx_ctrl & MWL8K_8687_RX_CTRL_OWNED_BY_HOST)) + if (!(rxd->rx_ctrl & MWL8K_STA_RX_CTRL_OWNED_BY_HOST)) return -1; rmb(); @@ -915,19 +852,25 @@ mwl8k_rxd_8687_process(void *_rxd, struct ieee80211_rx_status *status, status->signal = -rxd->rssi; status->noise = -rxd->noise_level; - status->antenna = MWL8K_8687_RATE_INFO_ANTSELECT(rate_info); - status->rate_idx = MWL8K_8687_RATE_INFO_RATEID(rate_info); + status->antenna = MWL8K_STA_RATE_INFO_ANTSELECT(rate_info); + status->rate_idx = MWL8K_STA_RATE_INFO_RATEID(rate_info); - if (rate_info & MWL8K_8687_RATE_INFO_SHORTPRE) + if (rate_info & MWL8K_STA_RATE_INFO_SHORTPRE) status->flag |= RX_FLAG_SHORTPRE; - if (rate_info & MWL8K_8687_RATE_INFO_40MHZ) + if (rate_info & MWL8K_STA_RATE_INFO_40MHZ) status->flag |= RX_FLAG_40MHZ; - if (rate_info & MWL8K_8687_RATE_INFO_SHORTGI) + if (rate_info & MWL8K_STA_RATE_INFO_SHORTGI) status->flag |= RX_FLAG_SHORT_GI; - if (rate_info & MWL8K_8687_RATE_INFO_MCS_FORMAT) + if (rate_info & MWL8K_STA_RATE_INFO_MCS_FORMAT) status->flag |= RX_FLAG_HT; - status->band = IEEE80211_BAND_2GHZ; + if (rxd->channel > 14) { + status->band = IEEE80211_BAND_5GHZ; + if (!(status->flag & RX_FLAG_HT)) + status->rate_idx -= 5; + } else { + status->band = IEEE80211_BAND_2GHZ; + } status->freq = ieee80211_channel_to_frequency(rxd->channel); *qos = rxd->qos_control; @@ -935,11 +878,11 @@ mwl8k_rxd_8687_process(void *_rxd, struct ieee80211_rx_status *status, return le16_to_cpu(rxd->pkt_len); } -static struct rxd_ops rxd_8687_ops = { - .rxd_size = sizeof(struct mwl8k_rxd_8687), - .rxd_init = mwl8k_rxd_8687_init, - .rxd_refill = mwl8k_rxd_8687_refill, - .rxd_process = mwl8k_rxd_8687_process, +static struct rxd_ops rxd_sta_ops = { + .rxd_size = sizeof(struct mwl8k_rxd_sta), + .rxd_init = mwl8k_rxd_sta_init, + .rxd_refill = mwl8k_rxd_sta_refill, + .rxd_process = mwl8k_rxd_sta_process, }; @@ -1153,16 +1096,18 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit) * Packet transmission. */ -/* Transmit packet ACK policy */ -#define MWL8K_TXD_ACK_POLICY_NORMAL 0 -#define MWL8K_TXD_ACK_POLICY_BLOCKACK 3 - #define MWL8K_TXD_STATUS_OK 0x00000001 #define MWL8K_TXD_STATUS_OK_RETRY 0x00000002 #define MWL8K_TXD_STATUS_OK_MORE_RETRY 0x00000004 #define MWL8K_TXD_STATUS_MULTICAST_TX 0x00000008 #define MWL8K_TXD_STATUS_FW_OWNED 0x80000000 +#define MWL8K_QOS_QLEN_UNSPEC 0xff00 +#define MWL8K_QOS_ACK_POLICY_MASK 0x0060 +#define MWL8K_QOS_ACK_POLICY_NORMAL 0x0000 +#define MWL8K_QOS_ACK_POLICY_BLOCKACK 0x0060 +#define MWL8K_QOS_EOSP 0x0010 + struct mwl8k_tx_desc { __le32 status; __u8 data_rate; @@ -1187,8 +1132,7 @@ static int mwl8k_txq_init(struct ieee80211_hw *hw, int index) int size; int i; - memset(&txq->stats, 0, sizeof(struct ieee80211_tx_queue_stats)); - txq->stats.limit = MWL8K_TX_DESCS; + txq->len = 0; txq->head = 0; txq->tail = 0; @@ -1264,7 +1208,7 @@ static void mwl8k_dump_tx_rings(struct ieee80211_hw *hw) printk(KERN_ERR "%s: txq[%d] len=%d head=%d tail=%d " "fw_owned=%d drv_owned=%d unused=%d\n", wiphy_name(hw->wiphy), i, - txq->stats.len, txq->head, txq->tail, + txq->len, txq->head, txq->tail, fw_owned, drv_owned, unused); } } @@ -1272,7 +1216,7 @@ static void mwl8k_dump_tx_rings(struct ieee80211_hw *hw) /* * Must be called with priv->fw_mutex held and tx queues stopped. */ -#define MWL8K_TX_WAIT_TIMEOUT_MS 1000 +#define MWL8K_TX_WAIT_TIMEOUT_MS 5000 static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) { @@ -1316,8 +1260,8 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) } if (priv->pending_tx_pkts < oldcount) { - printk(KERN_NOTICE "%s: timeout waiting for tx " - "rings to drain (%d -> %d pkts), retrying\n", + printk(KERN_NOTICE "%s: waiting for tx rings " + "to drain (%d -> %d pkts)\n", wiphy_name(hw->wiphy), oldcount, priv->pending_tx_pkts); retry = 1; @@ -1342,13 +1286,15 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) MWL8K_TXD_STATUS_OK_RETRY | \ MWL8K_TXD_STATUS_OK_MORE_RETRY)) -static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force) +static int +mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_tx_queue *txq = priv->txq + index; - int wake = 0; + int processed; - while (txq->stats.len > 0) { + processed = 0; + while (txq->len > 0 && limit--) { int tx; struct mwl8k_tx_desc *tx_desc; unsigned long addr; @@ -1370,8 +1316,8 @@ static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force) } txq->head = (tx + 1) % MWL8K_TX_DESCS; - BUG_ON(txq->stats.len == 0); - txq->stats.len--; + BUG_ON(txq->len == 0); + txq->len--; priv->pending_tx_pkts--; addr = le32_to_cpu(tx_desc->pkt_phys_addr); @@ -1395,11 +1341,13 @@ static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force) ieee80211_tx_status_irqsafe(hw, skb); - wake = 1; + processed++; } - if (wake && priv->radio_on && !mutex_is_locked(&priv->fw_mutex)) + if (processed && priv->radio_on && !mutex_is_locked(&priv->fw_mutex)) ieee80211_wake_queue(hw, index); + + return processed; } /* must be called only when the card's transmit is completely halted */ @@ -1408,7 +1356,7 @@ static void mwl8k_txq_deinit(struct ieee80211_hw *hw, int index) struct mwl8k_priv *priv = hw->priv; struct mwl8k_tx_queue *txq = priv->txq + index; - mwl8k_txq_reclaim(hw, index, 1); + mwl8k_txq_reclaim(hw, index, INT_MAX, 1); kfree(txq->skb); txq->skb = NULL; @@ -1446,11 +1394,9 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) mwl8k_vif = MWL8K_VIF(tx_info->control.vif); if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { - u16 seqno = mwl8k_vif->seqno; - wh->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); - wh->seq_ctrl |= cpu_to_le16(seqno << 4); - mwl8k_vif->seqno = seqno++ % 4096; + wh->seq_ctrl |= cpu_to_le16(mwl8k_vif->seqno); + mwl8k_vif->seqno += 0x10; } /* Setup firmware control bit fields for each frame type. */ @@ -1459,24 +1405,17 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) if (ieee80211_is_mgmt(wh->frame_control) || ieee80211_is_ctl(wh->frame_control)) { txdatarate = 0; - qos = mwl8k_qos_setbit_eosp(qos); - /* Set Queue size to unspecified */ - qos = mwl8k_qos_setbit_qlen(qos, 0xff); + qos |= MWL8K_QOS_QLEN_UNSPEC | MWL8K_QOS_EOSP; } else if (ieee80211_is_data(wh->frame_control)) { txdatarate = 1; if (is_multicast_ether_addr(wh->addr1)) txstatus |= MWL8K_TXD_STATUS_MULTICAST_TX; - /* Send pkt in an aggregate if AMPDU frame. */ + qos &= ~MWL8K_QOS_ACK_POLICY_MASK; if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) - qos = mwl8k_qos_setbit_ack(qos, - MWL8K_TXD_ACK_POLICY_BLOCKACK); + qos |= MWL8K_QOS_ACK_POLICY_BLOCKACK; else - qos = mwl8k_qos_setbit_ack(qos, - MWL8K_TXD_ACK_POLICY_NORMAL); - - if (qos & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT) - qos = mwl8k_qos_setbit_amsdu(qos); + qos |= MWL8K_QOS_ACK_POLICY_NORMAL; } dma = pci_map_single(priv->pdev, skb->data, @@ -1503,12 +1442,14 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) tx->pkt_phys_addr = cpu_to_le32(dma); tx->pkt_len = cpu_to_le16(skb->len); tx->rate_info = 0; - tx->peer_id = mwl8k_vif->peer_id; + if (!priv->ap_fw && tx_info->control.sta != NULL) + tx->peer_id = MWL8K_STA(tx_info->control.sta)->peer_id; + else + tx->peer_id = 0; wmb(); tx->status = cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED | txstatus); - txq->stats.count++; - txq->stats.len++; + txq->len++; priv->pending_tx_pkts++; txq->tail++; @@ -1656,6 +1597,56 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) return rc; } +static int mwl8k_post_pervif_cmd(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct mwl8k_cmd_pkt *cmd) +{ + if (vif != NULL) + cmd->macid = MWL8K_VIF(vif)->macid; + return mwl8k_post_cmd(hw, cmd); +} + +/* + * Setup code shared between STA and AP firmware images. + */ +static void mwl8k_setup_2ghz_band(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + + BUILD_BUG_ON(sizeof(priv->channels_24) != sizeof(mwl8k_channels_24)); + memcpy(priv->channels_24, mwl8k_channels_24, sizeof(mwl8k_channels_24)); + + BUILD_BUG_ON(sizeof(priv->rates_24) != sizeof(mwl8k_rates_24)); + memcpy(priv->rates_24, mwl8k_rates_24, sizeof(mwl8k_rates_24)); + + priv->band_24.band = IEEE80211_BAND_2GHZ; + priv->band_24.channels = priv->channels_24; + priv->band_24.n_channels = ARRAY_SIZE(mwl8k_channels_24); + priv->band_24.bitrates = priv->rates_24; + priv->band_24.n_bitrates = ARRAY_SIZE(mwl8k_rates_24); + + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band_24; +} + +static void mwl8k_setup_5ghz_band(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + + BUILD_BUG_ON(sizeof(priv->channels_50) != sizeof(mwl8k_channels_50)); + memcpy(priv->channels_50, mwl8k_channels_50, sizeof(mwl8k_channels_50)); + + BUILD_BUG_ON(sizeof(priv->rates_50) != sizeof(mwl8k_rates_50)); + memcpy(priv->rates_50, mwl8k_rates_50, sizeof(mwl8k_rates_50)); + + priv->band_50.band = IEEE80211_BAND_5GHZ; + priv->band_50.channels = priv->channels_50; + priv->band_50.n_channels = ARRAY_SIZE(mwl8k_channels_50); + priv->band_50.bitrates = priv->rates_50; + priv->band_50.n_bitrates = ARRAY_SIZE(mwl8k_rates_50); + + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &priv->band_50; +} + /* * CMD_GET_HW_SPEC (STA version). */ @@ -1678,6 +1669,89 @@ struct mwl8k_cmd_get_hw_spec_sta { __le32 total_rxd; } __attribute__((packed)); +#define MWL8K_CAP_MAX_AMSDU 0x20000000 +#define MWL8K_CAP_GREENFIELD 0x08000000 +#define MWL8K_CAP_AMPDU 0x04000000 +#define MWL8K_CAP_RX_STBC 0x01000000 +#define MWL8K_CAP_TX_STBC 0x00800000 +#define MWL8K_CAP_SHORTGI_40MHZ 0x00400000 +#define MWL8K_CAP_SHORTGI_20MHZ 0x00200000 +#define MWL8K_CAP_RX_ANTENNA_MASK 0x000e0000 +#define MWL8K_CAP_TX_ANTENNA_MASK 0x0001c000 +#define MWL8K_CAP_DELAY_BA 0x00003000 +#define MWL8K_CAP_MIMO 0x00000200 +#define MWL8K_CAP_40MHZ 0x00000100 +#define MWL8K_CAP_BAND_MASK 0x00000007 +#define MWL8K_CAP_5GHZ 0x00000004 +#define MWL8K_CAP_2GHZ4 0x00000001 + +static void +mwl8k_set_ht_caps(struct ieee80211_hw *hw, + struct ieee80211_supported_band *band, u32 cap) +{ + int rx_streams; + int tx_streams; + + band->ht_cap.ht_supported = 1; + + if (cap & MWL8K_CAP_MAX_AMSDU) + band->ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU; + if (cap & MWL8K_CAP_GREENFIELD) + band->ht_cap.cap |= IEEE80211_HT_CAP_GRN_FLD; + if (cap & MWL8K_CAP_AMPDU) { + hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; + band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; + } + if (cap & MWL8K_CAP_RX_STBC) + band->ht_cap.cap |= IEEE80211_HT_CAP_RX_STBC; + if (cap & MWL8K_CAP_TX_STBC) + band->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC; + if (cap & MWL8K_CAP_SHORTGI_40MHZ) + band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + if (cap & MWL8K_CAP_SHORTGI_20MHZ) + band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; + if (cap & MWL8K_CAP_DELAY_BA) + band->ht_cap.cap |= IEEE80211_HT_CAP_DELAY_BA; + if (cap & MWL8K_CAP_40MHZ) + band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + + rx_streams = hweight32(cap & MWL8K_CAP_RX_ANTENNA_MASK); + tx_streams = hweight32(cap & MWL8K_CAP_TX_ANTENNA_MASK); + + band->ht_cap.mcs.rx_mask[0] = 0xff; + if (rx_streams >= 2) + band->ht_cap.mcs.rx_mask[1] = 0xff; + if (rx_streams >= 3) + band->ht_cap.mcs.rx_mask[2] = 0xff; + band->ht_cap.mcs.rx_mask[4] = 0x01; + band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + + if (rx_streams != tx_streams) { + band->ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; + band->ht_cap.mcs.tx_params |= (tx_streams - 1) << + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; + } +} + +static void +mwl8k_set_caps(struct ieee80211_hw *hw, u32 caps) +{ + struct mwl8k_priv *priv = hw->priv; + + if ((caps & MWL8K_CAP_2GHZ4) || !(caps & MWL8K_CAP_BAND_MASK)) { + mwl8k_setup_2ghz_band(hw); + if (caps & MWL8K_CAP_MIMO) + mwl8k_set_ht_caps(hw, &priv->band_24, caps); + } + + if (caps & MWL8K_CAP_5GHZ) { + mwl8k_setup_5ghz_band(hw); + if (caps & MWL8K_CAP_MIMO) + mwl8k_set_ht_caps(hw, &priv->band_50, caps); + } +} + static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; @@ -1708,6 +1782,9 @@ static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw) priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs); priv->fw_rev = le32_to_cpu(cmd->fw_rev); priv->hw_rev = cmd->hw_rev; + mwl8k_set_caps(hw, le32_to_cpu(cmd->caps)); + priv->ap_macids_supported = 0x00000000; + priv->sta_macids_supported = 0x00000001; } kfree(cmd); @@ -1761,6 +1838,9 @@ static int mwl8k_cmd_get_hw_spec_ap(struct ieee80211_hw *hw) priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs); priv->fw_rev = le32_to_cpu(cmd->fw_rev); priv->hw_rev = cmd->hw_rev; + mwl8k_setup_2ghz_band(hw); + priv->ap_macids_supported = 0x000000ff; + priv->sta_macids_supported = 0x00000000; off = le32_to_cpu(cmd->wcbbase0) & 0xffff; iowrite32(cpu_to_le32(priv->txq[0].txd_dma), priv->sram + off); @@ -1806,7 +1886,9 @@ struct mwl8k_cmd_set_hw_spec { __le32 total_rxd; } __attribute__((packed)); -#define MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT 0x00000080 +#define MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT 0x00000080 +#define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP 0x00000020 +#define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON 0x00000010 static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw) { @@ -1827,7 +1909,9 @@ static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw) cmd->num_tx_queues = cpu_to_le32(MWL8K_TX_QUEUES); for (i = 0; i < MWL8K_TX_QUEUES; i++) cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma); - cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT); + cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT | + MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP | + MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON); cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS); cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS); @@ -1897,9 +1981,9 @@ __mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, int allmulti, } /* - * CMD_802_11_GET_STAT. + * CMD_GET_STAT. */ -struct mwl8k_cmd_802_11_get_stat { +struct mwl8k_cmd_get_stat { struct mwl8k_cmd_pkt header; __le32 stats[64]; } __attribute__((packed)); @@ -1909,10 +1993,10 @@ struct mwl8k_cmd_802_11_get_stat { #define MWL8K_STAT_FCS_ERROR 24 #define MWL8K_STAT_RTS_SUCCESS 11 -static int mwl8k_cmd_802_11_get_stat(struct ieee80211_hw *hw, - struct ieee80211_low_level_stats *stats) +static int mwl8k_cmd_get_stat(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) { - struct mwl8k_cmd_802_11_get_stat *cmd; + struct mwl8k_cmd_get_stat *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -1939,9 +2023,9 @@ static int mwl8k_cmd_802_11_get_stat(struct ieee80211_hw *hw, } /* - * CMD_802_11_RADIO_CONTROL. + * CMD_RADIO_CONTROL. */ -struct mwl8k_cmd_802_11_radio_control { +struct mwl8k_cmd_radio_control { struct mwl8k_cmd_pkt header; __le16 action; __le16 control; @@ -1949,10 +2033,10 @@ struct mwl8k_cmd_802_11_radio_control { } __attribute__((packed)); static int -mwl8k_cmd_802_11_radio_control(struct ieee80211_hw *hw, bool enable, bool force) +mwl8k_cmd_radio_control(struct ieee80211_hw *hw, bool enable, bool force) { struct mwl8k_priv *priv = hw->priv; - struct mwl8k_cmd_802_11_radio_control *cmd; + struct mwl8k_cmd_radio_control *cmd; int rc; if (enable == priv->radio_on && !force) @@ -1977,36 +2061,32 @@ mwl8k_cmd_802_11_radio_control(struct ieee80211_hw *hw, bool enable, bool force) return rc; } -static int mwl8k_cmd_802_11_radio_disable(struct ieee80211_hw *hw) +static int mwl8k_cmd_radio_disable(struct ieee80211_hw *hw) { - return mwl8k_cmd_802_11_radio_control(hw, 0, 0); + return mwl8k_cmd_radio_control(hw, 0, 0); } -static int mwl8k_cmd_802_11_radio_enable(struct ieee80211_hw *hw) +static int mwl8k_cmd_radio_enable(struct ieee80211_hw *hw) { - return mwl8k_cmd_802_11_radio_control(hw, 1, 0); + return mwl8k_cmd_radio_control(hw, 1, 0); } static int mwl8k_set_radio_preamble(struct ieee80211_hw *hw, bool short_preamble) { - struct mwl8k_priv *priv; - - if (hw == NULL || hw->priv == NULL) - return -EINVAL; - priv = hw->priv; + struct mwl8k_priv *priv = hw->priv; priv->radio_short_preamble = short_preamble; - return mwl8k_cmd_802_11_radio_control(hw, 1, 1); + return mwl8k_cmd_radio_control(hw, 1, 1); } /* - * CMD_802_11_RF_TX_POWER. + * CMD_RF_TX_POWER. */ #define MWL8K_TX_POWER_LEVEL_TOTAL 8 -struct mwl8k_cmd_802_11_rf_tx_power { +struct mwl8k_cmd_rf_tx_power { struct mwl8k_cmd_pkt header; __le16 action; __le16 support_level; @@ -2015,9 +2095,9 @@ struct mwl8k_cmd_802_11_rf_tx_power { __le16 power_level_list[MWL8K_TX_POWER_LEVEL_TOTAL]; } __attribute__((packed)); -static int mwl8k_cmd_802_11_rf_tx_power(struct ieee80211_hw *hw, int dBm) +static int mwl8k_cmd_rf_tx_power(struct ieee80211_hw *hw, int dBm) { - struct mwl8k_cmd_802_11_rf_tx_power *cmd; + struct mwl8k_cmd_rf_tx_power *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -2069,6 +2149,36 @@ mwl8k_cmd_rf_antenna(struct ieee80211_hw *hw, int antenna, int mask) } /* + * CMD_SET_BEACON. + */ +struct mwl8k_cmd_set_beacon { + struct mwl8k_cmd_pkt header; + __le16 beacon_len; + __u8 beacon[0]; +}; + +static int mwl8k_cmd_set_beacon(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u8 *beacon, int len) +{ + struct mwl8k_cmd_set_beacon *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd) + len, GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_BEACON); + cmd->header.length = cpu_to_le16(sizeof(*cmd) + len); + cmd->beacon_len = cpu_to_le16(len); + memcpy(cmd->beacon, beacon, len); + + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); + kfree(cmd); + + return rc; +} + +/* * CMD_SET_PRE_SCAN. */ struct mwl8k_cmd_set_pre_scan { @@ -2103,7 +2213,7 @@ struct mwl8k_cmd_set_post_scan { } __attribute__((packed)); static int -mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, __u8 *mac) +mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, const __u8 *mac) { struct mwl8k_cmd_set_post_scan *cmd; int rc; @@ -2134,8 +2244,9 @@ struct mwl8k_cmd_set_rf_channel { } __attribute__((packed)); static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw, - struct ieee80211_channel *channel) + struct ieee80211_conf *conf) { + struct ieee80211_channel *channel = conf->channel; struct mwl8k_cmd_set_rf_channel *cmd; int rc; @@ -2147,10 +2258,19 @@ static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw, cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le16(MWL8K_CMD_SET); cmd->current_channel = channel->hw_value; + if (channel->band == IEEE80211_BAND_2GHZ) - cmd->channel_flags = cpu_to_le32(0x00000081); - else - cmd->channel_flags = cpu_to_le32(0x00000000); + cmd->channel_flags |= cpu_to_le32(0x00000001); + else if (channel->band == IEEE80211_BAND_5GHZ) + cmd->channel_flags |= cpu_to_le32(0x00000004); + + if (conf->channel_type == NL80211_CHAN_NO_HT || + conf->channel_type == NL80211_CHAN_HT20) + cmd->channel_flags |= cpu_to_le32(0x00000080); + else if (conf->channel_type == NL80211_CHAN_HT40MINUS) + cmd->channel_flags |= cpu_to_le32(0x000001900); + else if (conf->channel_type == NL80211_CHAN_HT40PLUS) + cmd->channel_flags |= cpu_to_le32(0x000000900); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2159,85 +2279,75 @@ static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw, } /* - * CMD_SET_SLOT. + * CMD_SET_AID. */ -struct mwl8k_cmd_set_slot { - struct mwl8k_cmd_pkt header; - __le16 action; - __u8 short_slot; -} __attribute__((packed)); - -static int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, bool short_slot_time) -{ - struct mwl8k_cmd_set_slot *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_SLOT); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le16(MWL8K_CMD_SET); - cmd->short_slot = short_slot_time; - - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); +#define MWL8K_FRAME_PROT_DISABLED 0x00 +#define MWL8K_FRAME_PROT_11G 0x07 +#define MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY 0x02 +#define MWL8K_FRAME_PROT_11N_HT_ALL 0x06 - return rc; -} +struct mwl8k_cmd_update_set_aid { + struct mwl8k_cmd_pkt header; + __le16 aid; -/* - * CMD_MIMO_CONFIG. - */ -struct mwl8k_cmd_mimo_config { - struct mwl8k_cmd_pkt header; - __le32 action; - __u8 rx_antenna_map; - __u8 tx_antenna_map; + /* AP's MAC address (BSSID) */ + __u8 bssid[ETH_ALEN]; + __le16 protection_mode; + __u8 supp_rates[14]; } __attribute__((packed)); -static int mwl8k_cmd_mimo_config(struct ieee80211_hw *hw, __u8 rx, __u8 tx) +static void legacy_rate_mask_to_array(u8 *rates, u32 mask) { - struct mwl8k_cmd_mimo_config *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_MIMO_CONFIG); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le32((u32)MWL8K_CMD_SET); - cmd->rx_antenna_map = rx; - cmd->tx_antenna_map = tx; + int i; + int j; - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); + /* + * Clear nonstandard rates 4 and 13. + */ + mask &= 0x1fef; - return rc; + for (i = 0, j = 0; i < 14; i++) { + if (mask & (1 << i)) + rates[j++] = mwl8k_rates_24[i].hw_value; + } } -/* - * CMD_ENABLE_SNIFFER. - */ -struct mwl8k_cmd_enable_sniffer { - struct mwl8k_cmd_pkt header; - __le32 action; -} __attribute__((packed)); - -static int mwl8k_enable_sniffer(struct ieee80211_hw *hw, bool enable) +static int +mwl8k_cmd_set_aid(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u32 legacy_rate_mask) { - struct mwl8k_cmd_enable_sniffer *cmd; + struct mwl8k_cmd_update_set_aid *cmd; + u16 prot_mode; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; - cmd->header.code = cpu_to_le16(MWL8K_CMD_ENABLE_SNIFFER); + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_AID); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le32(!!enable); + cmd->aid = cpu_to_le16(vif->bss_conf.aid); + memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN); + + if (vif->bss_conf.use_cts_prot) { + prot_mode = MWL8K_FRAME_PROT_11G; + } else { + switch (vif->bss_conf.ht_operation_mode & + IEEE80211_HT_OP_MODE_PROTECTION) { + case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: + prot_mode = MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY; + break; + case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: + prot_mode = MWL8K_FRAME_PROT_11N_HT_ALL; + break; + default: + prot_mode = MWL8K_FRAME_PROT_DISABLED; + break; + } + } + cmd->protection_mode = cpu_to_le16(prot_mode); + + legacy_rate_mask_to_array(cmd->supp_rates, legacy_rate_mask); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2246,37 +2356,32 @@ static int mwl8k_enable_sniffer(struct ieee80211_hw *hw, bool enable) } /* - * CMD_SET_MAC_ADDR. + * CMD_SET_RATE. */ -struct mwl8k_cmd_set_mac_addr { - struct mwl8k_cmd_pkt header; - union { - struct { - __le16 mac_type; - __u8 mac_addr[ETH_ALEN]; - } mbss; - __u8 mac_addr[ETH_ALEN]; - }; +struct mwl8k_cmd_set_rate { + struct mwl8k_cmd_pkt header; + __u8 legacy_rates[14]; + + /* Bitmap for supported MCS codes. */ + __u8 mcs_set[16]; + __u8 reserved[16]; } __attribute__((packed)); -static int mwl8k_set_mac_addr(struct ieee80211_hw *hw, u8 *mac) +static int +mwl8k_cmd_set_rate(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 legacy_rate_mask, u8 *mcs_rates) { - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_cmd_set_mac_addr *cmd; + struct mwl8k_cmd_set_rate *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_MAC_ADDR); + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATE); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - if (priv->ap_fw) { - cmd->mbss.mac_type = 0; - memcpy(cmd->mbss.mac_addr, mac, ETH_ALEN); - } else { - memcpy(cmd->mac_addr, mac, ETH_ALEN); - } + legacy_rate_mask_to_array(cmd->legacy_rates, legacy_rate_mask); + memcpy(cmd->mcs_set, mcs_rates, 16); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2284,29 +2389,40 @@ static int mwl8k_set_mac_addr(struct ieee80211_hw *hw, u8 *mac) return rc; } - /* - * CMD_SET_RATEADAPT_MODE. + * CMD_FINALIZE_JOIN. */ -struct mwl8k_cmd_set_rate_adapt_mode { +#define MWL8K_FJ_BEACON_MAXLEN 128 + +struct mwl8k_cmd_finalize_join { struct mwl8k_cmd_pkt header; - __le16 action; - __le16 mode; + __le32 sleep_interval; /* Number of beacon periods to sleep */ + __u8 beacon_data[MWL8K_FJ_BEACON_MAXLEN]; } __attribute__((packed)); -static int mwl8k_cmd_setrateadaptmode(struct ieee80211_hw *hw, __u16 mode) +static int mwl8k_cmd_finalize_join(struct ieee80211_hw *hw, void *frame, + int framelen, int dtim) { - struct mwl8k_cmd_set_rate_adapt_mode *cmd; + struct mwl8k_cmd_finalize_join *cmd; + struct ieee80211_mgmt *payload = frame; + int payload_len; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATEADAPT_MODE); + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_FINALIZE_JOIN); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le16(MWL8K_CMD_SET); - cmd->mode = cpu_to_le16(mode); + cmd->sleep_interval = cpu_to_le32(dtim ? dtim : 1); + + payload_len = framelen - ieee80211_hdrlen(payload->frame_control); + if (payload_len < 0) + payload_len = 0; + else if (payload_len > MWL8K_FJ_BEACON_MAXLEN) + payload_len = MWL8K_FJ_BEACON_MAXLEN; + + memcpy(cmd->beacon_data, &payload->u.beacon, payload_len); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2315,59 +2431,57 @@ static int mwl8k_cmd_setrateadaptmode(struct ieee80211_hw *hw, __u16 mode) } /* - * CMD_SET_WMM_MODE. + * CMD_SET_RTS_THRESHOLD. */ -struct mwl8k_cmd_set_wmm { +struct mwl8k_cmd_set_rts_threshold { struct mwl8k_cmd_pkt header; __le16 action; + __le16 threshold; } __attribute__((packed)); -static int mwl8k_set_wmm(struct ieee80211_hw *hw, bool enable) +static int +mwl8k_cmd_set_rts_threshold(struct ieee80211_hw *hw, int rts_thresh) { - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_cmd_set_wmm *cmd; + struct mwl8k_cmd_set_rts_threshold *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_WMM_MODE); + cmd->header.code = cpu_to_le16(MWL8K_CMD_RTS_THRESHOLD); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le16(!!enable); + cmd->action = cpu_to_le16(MWL8K_CMD_SET); + cmd->threshold = cpu_to_le16(rts_thresh); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); - if (!rc) - priv->wmm_enabled = enable; - return rc; } /* - * CMD_SET_RTS_THRESHOLD. + * CMD_SET_SLOT. */ -struct mwl8k_cmd_rts_threshold { +struct mwl8k_cmd_set_slot { struct mwl8k_cmd_pkt header; __le16 action; - __le16 threshold; + __u8 short_slot; } __attribute__((packed)); -static int mwl8k_rts_threshold(struct ieee80211_hw *hw, - u16 action, u16 threshold) +static int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, bool short_slot_time) { - struct mwl8k_cmd_rts_threshold *cmd; + struct mwl8k_cmd_set_slot *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; - cmd->header.code = cpu_to_le16(MWL8K_CMD_RTS_THRESHOLD); + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_SLOT); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le16(action); - cmd->threshold = cpu_to_le16(threshold); + cmd->action = cpu_to_le16(MWL8K_CMD_SET); + cmd->short_slot = short_slot_time; rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2426,9 +2540,9 @@ struct mwl8k_cmd_set_edca_params { MWL8K_SET_EDCA_AIFS) static int -mwl8k_set_edca_params(struct ieee80211_hw *hw, __u8 qnum, - __u16 cw_min, __u16 cw_max, - __u8 aifs, __u16 txop) +mwl8k_cmd_set_edca_params(struct ieee80211_hw *hw, __u8 qnum, + __u16 cw_min, __u16 cw_max, + __u8 aifs, __u16 txop) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_cmd_set_edca_params *cmd; @@ -2438,12 +2552,6 @@ mwl8k_set_edca_params(struct ieee80211_hw *hw, __u8 qnum, if (cmd == NULL) return -ENOMEM; - /* - * Queues 0 (BE) and 1 (BK) are swapped in hardware for - * this call. - */ - qnum ^= !(qnum >> 1); - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_EDCA_PARAMS); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le16(MWL8K_SET_EDCA_ALL); @@ -2467,170 +2575,259 @@ mwl8k_set_edca_params(struct ieee80211_hw *hw, __u8 qnum, } /* - * CMD_FINALIZE_JOIN. + * CMD_SET_WMM_MODE. */ -#define MWL8K_FJ_BEACON_MAXLEN 128 - -struct mwl8k_cmd_finalize_join { +struct mwl8k_cmd_set_wmm_mode { struct mwl8k_cmd_pkt header; - __le32 sleep_interval; /* Number of beacon periods to sleep */ - __u8 beacon_data[MWL8K_FJ_BEACON_MAXLEN]; + __le16 action; } __attribute__((packed)); -static int mwl8k_finalize_join(struct ieee80211_hw *hw, void *frame, - int framelen, int dtim) +static int mwl8k_cmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable) { - struct mwl8k_cmd_finalize_join *cmd; - struct ieee80211_mgmt *payload = frame; - int payload_len; + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_cmd_set_wmm_mode *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_FINALIZE_JOIN); + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_WMM_MODE); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->sleep_interval = cpu_to_le32(dtim ? dtim : 1); - - payload_len = framelen - ieee80211_hdrlen(payload->frame_control); - if (payload_len < 0) - payload_len = 0; - else if (payload_len > MWL8K_FJ_BEACON_MAXLEN) - payload_len = MWL8K_FJ_BEACON_MAXLEN; - - memcpy(cmd->beacon_data, &payload->u.beacon, payload_len); + cmd->action = cpu_to_le16(!!enable); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); + if (!rc) + priv->wmm_enabled = enable; + return rc; } /* - * CMD_UPDATE_STADB. + * CMD_MIMO_CONFIG. */ -struct mwl8k_cmd_update_sta_db { +struct mwl8k_cmd_mimo_config { struct mwl8k_cmd_pkt header; + __le32 action; + __u8 rx_antenna_map; + __u8 tx_antenna_map; +} __attribute__((packed)); - /* See STADB_ACTION_TYPE */ - __le32 action; +static int mwl8k_cmd_mimo_config(struct ieee80211_hw *hw, __u8 rx, __u8 tx) +{ + struct mwl8k_cmd_mimo_config *cmd; + int rc; - /* Peer MAC address */ - __u8 peer_addr[ETH_ALEN]; + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; - __le32 reserved; + cmd->header.code = cpu_to_le16(MWL8K_CMD_MIMO_CONFIG); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le32((u32)MWL8K_CMD_SET); + cmd->rx_antenna_map = rx; + cmd->tx_antenna_map = tx; - /* Peer info - valid during add/update. */ - struct peer_capability_info peer_info; + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_USE_FIXED_RATE (STA version). + */ +struct mwl8k_cmd_use_fixed_rate_sta { + struct mwl8k_cmd_pkt header; + __le32 action; + __le32 allow_rate_drop; + __le32 num_rates; + struct { + __le32 is_ht_rate; + __le32 enable_retry; + __le32 rate; + __le32 retry_count; + } rate_entry[8]; + __le32 rate_type; + __le32 reserved1; + __le32 reserved2; } __attribute__((packed)); -static int mwl8k_cmd_update_sta_db(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, __u32 action) +#define MWL8K_USE_AUTO_RATE 0x0002 +#define MWL8K_UCAST_RATE 0 + +static int mwl8k_cmd_use_fixed_rate_sta(struct ieee80211_hw *hw) { - struct mwl8k_vif *mv_vif = MWL8K_VIF(vif); - struct ieee80211_bss_conf *info = &mv_vif->bss_info; - struct mwl8k_cmd_update_sta_db *cmd; - struct peer_capability_info *peer_info; + struct mwl8k_cmd_use_fixed_rate_sta *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; - cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB); + cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE); cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE); + cmd->rate_type = cpu_to_le32(MWL8K_UCAST_RATE); - cmd->action = cpu_to_le32(action); - peer_info = &cmd->peer_info; - memcpy(cmd->peer_addr, mv_vif->bssid, ETH_ALEN); + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); - switch (action) { - case MWL8K_STA_DB_ADD_ENTRY: - case MWL8K_STA_DB_MODIFY_ENTRY: - /* Build peer_info block */ - peer_info->peer_type = MWL8K_PEER_TYPE_ACCESSPOINT; - peer_info->basic_caps = cpu_to_le16(info->assoc_capability); - memcpy(peer_info->legacy_rates, mwl8k_rateids, - sizeof(mwl8k_rateids)); - peer_info->interop = 1; - peer_info->amsdu_enabled = 0; - - rc = mwl8k_post_cmd(hw, &cmd->header); - if (rc == 0) - mv_vif->peer_id = peer_info->station_id; + return rc; +} - break; +/* + * CMD_USE_FIXED_RATE (AP version). + */ +struct mwl8k_cmd_use_fixed_rate_ap { + struct mwl8k_cmd_pkt header; + __le32 action; + __le32 allow_rate_drop; + __le32 num_rates; + struct mwl8k_rate_entry_ap { + __le32 is_ht_rate; + __le32 enable_retry; + __le32 rate; + __le32 retry_count; + } rate_entry[4]; + u8 multicast_rate; + u8 multicast_rate_type; + u8 management_rate; +} __attribute__((packed)); - case MWL8K_STA_DB_DEL_ENTRY: - case MWL8K_STA_DB_FLUSH: - default: - rc = mwl8k_post_cmd(hw, &cmd->header); - if (rc == 0) - mv_vif->peer_id = 0; - break; - } +static int +mwl8k_cmd_use_fixed_rate_ap(struct ieee80211_hw *hw, int mcast, int mgmt) +{ + struct mwl8k_cmd_use_fixed_rate_ap *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE); + cmd->multicast_rate = mcast; + cmd->management_rate = mgmt; + + rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; } /* - * CMD_SET_AID. + * CMD_ENABLE_SNIFFER. */ -#define MWL8K_FRAME_PROT_DISABLED 0x00 -#define MWL8K_FRAME_PROT_11G 0x07 -#define MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY 0x02 -#define MWL8K_FRAME_PROT_11N_HT_ALL 0x06 - -struct mwl8k_cmd_update_set_aid { - struct mwl8k_cmd_pkt header; - __le16 aid; - - /* AP's MAC address (BSSID) */ - __u8 bssid[ETH_ALEN]; - __le16 protection_mode; - __u8 supp_rates[14]; +struct mwl8k_cmd_enable_sniffer { + struct mwl8k_cmd_pkt header; + __le32 action; } __attribute__((packed)); -static int mwl8k_cmd_set_aid(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) +static int mwl8k_cmd_enable_sniffer(struct ieee80211_hw *hw, bool enable) { - struct mwl8k_vif *mv_vif = MWL8K_VIF(vif); - struct ieee80211_bss_conf *info = &mv_vif->bss_info; - struct mwl8k_cmd_update_set_aid *cmd; - u16 prot_mode; + struct mwl8k_cmd_enable_sniffer *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_AID); + cmd->header.code = cpu_to_le16(MWL8K_CMD_ENABLE_SNIFFER); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->aid = cpu_to_le16(info->aid); + cmd->action = cpu_to_le32(!!enable); - memcpy(cmd->bssid, mv_vif->bssid, ETH_ALEN); + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); - if (info->use_cts_prot) { - prot_mode = MWL8K_FRAME_PROT_11G; + return rc; +} + +/* + * CMD_SET_MAC_ADDR. + */ +struct mwl8k_cmd_set_mac_addr { + struct mwl8k_cmd_pkt header; + union { + struct { + __le16 mac_type; + __u8 mac_addr[ETH_ALEN]; + } mbss; + __u8 mac_addr[ETH_ALEN]; + }; +} __attribute__((packed)); + +#define MWL8K_MAC_TYPE_PRIMARY_CLIENT 0 +#define MWL8K_MAC_TYPE_SECONDARY_CLIENT 1 +#define MWL8K_MAC_TYPE_PRIMARY_AP 2 +#define MWL8K_MAC_TYPE_SECONDARY_AP 3 + +static int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u8 *mac) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); + struct mwl8k_cmd_set_mac_addr *cmd; + int mac_type; + int rc; + + mac_type = MWL8K_MAC_TYPE_PRIMARY_AP; + if (vif != NULL && vif->type == NL80211_IFTYPE_STATION) { + if (mwl8k_vif->macid + 1 == ffs(priv->sta_macids_supported)) + mac_type = MWL8K_MAC_TYPE_PRIMARY_CLIENT; + else + mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT; + } else if (vif != NULL && vif->type == NL80211_IFTYPE_AP) { + if (mwl8k_vif->macid + 1 == ffs(priv->ap_macids_supported)) + mac_type = MWL8K_MAC_TYPE_PRIMARY_AP; + else + mac_type = MWL8K_MAC_TYPE_SECONDARY_AP; + } + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_MAC_ADDR); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + if (priv->ap_fw) { + cmd->mbss.mac_type = cpu_to_le16(mac_type); + memcpy(cmd->mbss.mac_addr, mac, ETH_ALEN); } else { - switch (info->ht_operation_mode & - IEEE80211_HT_OP_MODE_PROTECTION) { - case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: - prot_mode = MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY; - break; - case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: - prot_mode = MWL8K_FRAME_PROT_11N_HT_ALL; - break; - default: - prot_mode = MWL8K_FRAME_PROT_DISABLED; - break; - } + memcpy(cmd->mac_addr, mac, ETH_ALEN); } - cmd->protection_mode = cpu_to_le16(prot_mode); - memcpy(cmd->supp_rates, mwl8k_rateids, sizeof(mwl8k_rateids)); + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_SET_RATEADAPT_MODE. + */ +struct mwl8k_cmd_set_rate_adapt_mode { + struct mwl8k_cmd_pkt header; + __le16 action; + __le16 mode; +} __attribute__((packed)); + +static int mwl8k_cmd_set_rateadapt_mode(struct ieee80211_hw *hw, __u16 mode) +{ + struct mwl8k_cmd_set_rate_adapt_mode *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATEADAPT_MODE); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le16(MWL8K_CMD_SET); + cmd->mode = cpu_to_le16(mode); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2639,115 +2836,255 @@ static int mwl8k_cmd_set_aid(struct ieee80211_hw *hw, } /* - * CMD_SET_RATE. + * CMD_BSS_START. */ -struct mwl8k_cmd_update_rateset { - struct mwl8k_cmd_pkt header; - __u8 legacy_rates[14]; - - /* Bitmap for supported MCS codes. */ - __u8 mcs_set[16]; - __u8 reserved[16]; +struct mwl8k_cmd_bss_start { + struct mwl8k_cmd_pkt header; + __le32 enable; } __attribute__((packed)); -static int mwl8k_update_rateset(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) +static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int enable) { - struct mwl8k_cmd_update_rateset *cmd; + struct mwl8k_cmd_bss_start *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATE); + cmd->header.code = cpu_to_le16(MWL8K_CMD_BSS_START); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - memcpy(cmd->legacy_rates, mwl8k_rateids, sizeof(mwl8k_rateids)); + cmd->enable = cpu_to_le32(enable); - rc = mwl8k_post_cmd(hw, &cmd->header); + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); kfree(cmd); return rc; } /* - * CMD_USE_FIXED_RATE. + * CMD_SET_NEW_STN. */ -#define MWL8K_RATE_TABLE_SIZE 8 -#define MWL8K_UCAST_RATE 0 -#define MWL8K_USE_AUTO_RATE 0x0002 +struct mwl8k_cmd_set_new_stn { + struct mwl8k_cmd_pkt header; + __le16 aid; + __u8 mac_addr[6]; + __le16 stn_id; + __le16 action; + __le16 rsvd; + __le32 legacy_rates; + __u8 ht_rates[4]; + __le16 cap_info; + __le16 ht_capabilities_info; + __u8 mac_ht_param_info; + __u8 rev; + __u8 control_channel; + __u8 add_channel; + __le16 op_mode; + __le16 stbc; + __u8 add_qos_info; + __u8 is_qos_sta; + __le32 fw_sta_ptr; +} __attribute__((packed)); + +#define MWL8K_STA_ACTION_ADD 0 +#define MWL8K_STA_ACTION_REMOVE 2 + +static int mwl8k_cmd_set_new_stn_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mwl8k_cmd_set_new_stn *cmd; + u32 rates; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->aid = cpu_to_le16(sta->aid); + memcpy(cmd->mac_addr, sta->addr, ETH_ALEN); + cmd->stn_id = cpu_to_le16(sta->aid); + cmd->action = cpu_to_le16(MWL8K_STA_ACTION_ADD); + if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) + rates = sta->supp_rates[IEEE80211_BAND_2GHZ]; + else + rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5; + cmd->legacy_rates = cpu_to_le32(rates); + if (sta->ht_cap.ht_supported) { + cmd->ht_rates[0] = sta->ht_cap.mcs.rx_mask[0]; + cmd->ht_rates[1] = sta->ht_cap.mcs.rx_mask[1]; + cmd->ht_rates[2] = sta->ht_cap.mcs.rx_mask[2]; + cmd->ht_rates[3] = sta->ht_cap.mcs.rx_mask[3]; + cmd->ht_capabilities_info = cpu_to_le16(sta->ht_cap.cap); + cmd->mac_ht_param_info = (sta->ht_cap.ampdu_factor & 3) | + ((sta->ht_cap.ampdu_density & 7) << 2); + cmd->is_qos_sta = 1; + } + + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); + kfree(cmd); + + return rc; +} + +static int mwl8k_cmd_set_new_stn_add_self(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mwl8k_cmd_set_new_stn *cmd; + int rc; -struct mwl8k_rate_entry { - /* Set to 1 if HT rate, 0 if legacy. */ - __le32 is_ht_rate; + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; - /* Set to 1 to use retry_count field. */ - __le32 enable_retry; + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + memcpy(cmd->mac_addr, vif->addr, ETH_ALEN); - /* Specified legacy rate or MCS. */ - __le32 rate; + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); + kfree(cmd); - /* Number of allowed retries. */ - __le32 retry_count; + return rc; +} + +static int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u8 *addr) +{ + struct mwl8k_cmd_set_new_stn *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + memcpy(cmd->mac_addr, addr, ETH_ALEN); + cmd->action = cpu_to_le16(MWL8K_STA_ACTION_REMOVE); + + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_UPDATE_STADB. + */ +struct ewc_ht_info { + __le16 control1; + __le16 control2; + __le16 control3; } __attribute__((packed)); -struct mwl8k_rate_table { - /* 1 to allow specified rate and below */ - __le32 allow_rate_drop; - __le32 num_rates; - struct mwl8k_rate_entry rate_entry[MWL8K_RATE_TABLE_SIZE]; +struct peer_capability_info { + /* Peer type - AP vs. STA. */ + __u8 peer_type; + + /* Basic 802.11 capabilities from assoc resp. */ + __le16 basic_caps; + + /* Set if peer supports 802.11n high throughput (HT). */ + __u8 ht_support; + + /* Valid if HT is supported. */ + __le16 ht_caps; + __u8 extended_ht_caps; + struct ewc_ht_info ewc_info; + + /* Legacy rate table. Intersection of our rates and peer rates. */ + __u8 legacy_rates[12]; + + /* HT rate table. Intersection of our rates and peer rates. */ + __u8 ht_rates[16]; + __u8 pad[16]; + + /* If set, interoperability mode, no proprietary extensions. */ + __u8 interop; + __u8 pad2; + __u8 station_id; + __le16 amsdu_enabled; } __attribute__((packed)); -struct mwl8k_cmd_use_fixed_rate { - struct mwl8k_cmd_pkt header; +struct mwl8k_cmd_update_stadb { + struct mwl8k_cmd_pkt header; + + /* See STADB_ACTION_TYPE */ __le32 action; - struct mwl8k_rate_table rate_table; - /* Unicast, Broadcast or Multicast */ - __le32 rate_type; - __le32 reserved1; - __le32 reserved2; + /* Peer MAC address */ + __u8 peer_addr[ETH_ALEN]; + + __le32 reserved; + + /* Peer info - valid during add/update. */ + struct peer_capability_info peer_info; } __attribute__((packed)); -static int mwl8k_cmd_use_fixed_rate(struct ieee80211_hw *hw, - u32 action, u32 rate_type, struct mwl8k_rate_table *rate_table) +#define MWL8K_STA_DB_MODIFY_ENTRY 1 +#define MWL8K_STA_DB_DEL_ENTRY 2 + +/* Peer Entry flags - used to define the type of the peer node */ +#define MWL8K_PEER_TYPE_ACCESSPOINT 2 + +static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) { - struct mwl8k_cmd_use_fixed_rate *cmd; - int count; + struct mwl8k_cmd_update_stadb *cmd; + struct peer_capability_info *p; + u32 rates; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; - cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE); + cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB); cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le32(MWL8K_STA_DB_MODIFY_ENTRY); + memcpy(cmd->peer_addr, sta->addr, ETH_ALEN); + + p = &cmd->peer_info; + p->peer_type = MWL8K_PEER_TYPE_ACCESSPOINT; + p->basic_caps = cpu_to_le16(vif->bss_conf.assoc_capability); + p->ht_support = sta->ht_cap.ht_supported; + p->ht_caps = sta->ht_cap.cap; + p->extended_ht_caps = (sta->ht_cap.ampdu_factor & 3) | + ((sta->ht_cap.ampdu_density & 7) << 2); + if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) + rates = sta->supp_rates[IEEE80211_BAND_2GHZ]; + else + rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5; + legacy_rate_mask_to_array(p->legacy_rates, rates); + memcpy(p->ht_rates, sta->ht_cap.mcs.rx_mask, 16); + p->interop = 1; + p->amsdu_enabled = 0; - cmd->action = cpu_to_le32(action); - cmd->rate_type = cpu_to_le32(rate_type); + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); - if (rate_table != NULL) { - /* - * Copy over each field manually so that endian - * conversion can be done. - */ - cmd->rate_table.allow_rate_drop = - cpu_to_le32(rate_table->allow_rate_drop); - cmd->rate_table.num_rates = - cpu_to_le32(rate_table->num_rates); - - for (count = 0; count < rate_table->num_rates; count++) { - struct mwl8k_rate_entry *dst = - &cmd->rate_table.rate_entry[count]; - struct mwl8k_rate_entry *src = - &rate_table->rate_entry[count]; - - dst->is_ht_rate = cpu_to_le32(src->is_ht_rate); - dst->enable_retry = cpu_to_le32(src->enable_retry); - dst->rate = cpu_to_le32(src->rate); - dst->retry_count = cpu_to_le32(src->retry_count); - } - } + return rc ? rc : p->station_id; +} + +static int mwl8k_cmd_update_stadb_del(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u8 *addr) +{ + struct mwl8k_cmd_update_stadb *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le32(MWL8K_STA_DB_DEL_ENTRY); + memcpy(cmd->peer_addr, addr, ETH_ALEN); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2766,19 +3103,22 @@ static irqreturn_t mwl8k_interrupt(int irq, void *dev_id) u32 status; status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); - iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); - if (!status) return IRQ_NONE; - if (status & MWL8K_A2H_INT_TX_DONE) - tasklet_schedule(&priv->tx_reclaim_task); + if (status & MWL8K_A2H_INT_TX_DONE) { + status &= ~MWL8K_A2H_INT_TX_DONE; + tasklet_schedule(&priv->poll_tx_task); + } if (status & MWL8K_A2H_INT_RX_READY) { - while (rxq_process(hw, 0, 1)) - rxq_refill(hw, 0, 1); + status &= ~MWL8K_A2H_INT_RX_READY; + tasklet_schedule(&priv->poll_rx_task); } + if (status) + iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); + if (status & MWL8K_A2H_INT_OPC_DONE) { if (priv->hostcmd_wait != NULL) complete(priv->hostcmd_wait); @@ -2793,6 +3133,53 @@ static irqreturn_t mwl8k_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +static void mwl8k_tx_poll(unsigned long data) +{ + struct ieee80211_hw *hw = (struct ieee80211_hw *)data; + struct mwl8k_priv *priv = hw->priv; + int limit; + int i; + + limit = 32; + + spin_lock_bh(&priv->tx_lock); + + for (i = 0; i < MWL8K_TX_QUEUES; i++) + limit -= mwl8k_txq_reclaim(hw, i, limit, 0); + + if (!priv->pending_tx_pkts && priv->tx_wait != NULL) { + complete(priv->tx_wait); + priv->tx_wait = NULL; + } + + spin_unlock_bh(&priv->tx_lock); + + if (limit) { + writel(~MWL8K_A2H_INT_TX_DONE, + priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); + } else { + tasklet_schedule(&priv->poll_tx_task); + } +} + +static void mwl8k_rx_poll(unsigned long data) +{ + struct ieee80211_hw *hw = (struct ieee80211_hw *)data; + struct mwl8k_priv *priv = hw->priv; + int limit; + + limit = 32; + limit -= rxq_process(hw, 0, limit); + limit -= rxq_refill(hw, 0, limit); + + if (limit) { + writel(~MWL8K_A2H_INT_RX_READY, + priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); + } else { + tasklet_schedule(&priv->poll_rx_task); + } +} + /* * Core driver operations. @@ -2803,7 +3190,7 @@ static int mwl8k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) int index = skb_get_queue_mapping(skb); int rc; - if (priv->current_channel == NULL) { + if (!priv->radio_on) { printk(KERN_DEBUG "%s: dropped TX frame since radio " "disabled\n", wiphy_name(hw->wiphy)); dev_kfree_skb(skb); @@ -2828,19 +3215,20 @@ static int mwl8k_start(struct ieee80211_hw *hw) return -EIO; } - /* Enable tx reclaim tasklet */ - tasklet_enable(&priv->tx_reclaim_task); + /* Enable TX reclaim and RX tasklets. */ + tasklet_enable(&priv->poll_tx_task); + tasklet_enable(&priv->poll_rx_task); /* Enable interrupts */ iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); rc = mwl8k_fw_lock(hw); if (!rc) { - rc = mwl8k_cmd_802_11_radio_enable(hw); + rc = mwl8k_cmd_radio_enable(hw); if (!priv->ap_fw) { if (!rc) - rc = mwl8k_enable_sniffer(hw, 0); + rc = mwl8k_cmd_enable_sniffer(hw, 0); if (!rc) rc = mwl8k_cmd_set_pre_scan(hw); @@ -2851,10 +3239,10 @@ static int mwl8k_start(struct ieee80211_hw *hw) } if (!rc) - rc = mwl8k_cmd_setrateadaptmode(hw, 0); + rc = mwl8k_cmd_set_rateadapt_mode(hw, 0); if (!rc) - rc = mwl8k_set_wmm(hw, 0); + rc = mwl8k_cmd_set_wmm_mode(hw, 0); mwl8k_fw_unlock(hw); } @@ -2862,7 +3250,8 @@ static int mwl8k_start(struct ieee80211_hw *hw) if (rc) { iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); free_irq(priv->pdev->irq, hw); - tasklet_disable(&priv->tx_reclaim_task); + tasklet_disable(&priv->poll_tx_task); + tasklet_disable(&priv->poll_rx_task); } return rc; @@ -2873,7 +3262,7 @@ static void mwl8k_stop(struct ieee80211_hw *hw) struct mwl8k_priv *priv = hw->priv; int i; - mwl8k_cmd_802_11_radio_disable(hw); + mwl8k_cmd_radio_disable(hw); ieee80211_stop_queues(hw); @@ -2886,36 +3275,27 @@ static void mwl8k_stop(struct ieee80211_hw *hw) if (priv->beacon_skb != NULL) dev_kfree_skb(priv->beacon_skb); - /* Stop tx reclaim tasklet */ - tasklet_disable(&priv->tx_reclaim_task); + /* Stop TX reclaim and RX tasklets. */ + tasklet_disable(&priv->poll_tx_task); + tasklet_disable(&priv->poll_rx_task); /* Return all skbs to mac80211 */ for (i = 0; i < MWL8K_TX_QUEUES; i++) - mwl8k_txq_reclaim(hw, i, 1); + mwl8k_txq_reclaim(hw, i, INT_MAX, 1); } static int mwl8k_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_vif *mwl8k_vif; - - /* - * We only support one active interface at a time. - */ - if (priv->vif != NULL) - return -EBUSY; - - /* - * We only support managed interfaces for now. - */ - if (conf->type != NL80211_IFTYPE_STATION) - return -EINVAL; + u32 macids_supported; + int macid; /* * Reject interface creation if sniffer mode is active, as * STA operation is mutually exclusive with hardware sniffer - * mode. + * mode. (Sniffer mode is only used on STA firmware.) */ if (priv->sniffer_enabled) { printk(KERN_INFO "%s: unable to create STA " @@ -2924,37 +3304,54 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, return -EINVAL; } - /* Clean out driver private area */ - mwl8k_vif = MWL8K_VIF(conf->vif); - memset(mwl8k_vif, 0, sizeof(*mwl8k_vif)); - /* Set and save the mac address */ - mwl8k_set_mac_addr(hw, conf->mac_addr); - memcpy(mwl8k_vif->mac_addr, conf->mac_addr, ETH_ALEN); + switch (vif->type) { + case NL80211_IFTYPE_AP: + macids_supported = priv->ap_macids_supported; + break; + case NL80211_IFTYPE_STATION: + macids_supported = priv->sta_macids_supported; + break; + default: + return -EINVAL; + } - /* Back pointer to parent config block */ - mwl8k_vif->priv = priv; + macid = ffs(macids_supported & ~priv->macids_used); + if (!macid--) + return -EBUSY; - /* Set Initial sequence number to zero */ + /* Setup driver private area. */ + mwl8k_vif = MWL8K_VIF(vif); + memset(mwl8k_vif, 0, sizeof(*mwl8k_vif)); + mwl8k_vif->vif = vif; + mwl8k_vif->macid = macid; mwl8k_vif->seqno = 0; - priv->vif = conf->vif; - priv->current_channel = NULL; + /* Set the mac address. */ + mwl8k_cmd_set_mac_addr(hw, vif, vif->addr); + + if (priv->ap_fw) + mwl8k_cmd_set_new_stn_add_self(hw, vif); + + priv->macids_used |= 1 << mwl8k_vif->macid; + list_add_tail(&mwl8k_vif->list, &priv->vif_list); return 0; } static void mwl8k_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct mwl8k_priv *priv = hw->priv; + struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); - if (priv->vif == NULL) - return; + if (priv->ap_fw) + mwl8k_cmd_set_new_stn_del(hw, vif, vif->addr); - mwl8k_set_mac_addr(hw, "\x00\x00\x00\x00\x00\x00"); + mwl8k_cmd_set_mac_addr(hw, vif, "\x00\x00\x00\x00\x00\x00"); - priv->vif = NULL; + priv->macids_used &= ~(1 << mwl8k_vif->macid); + list_del(&mwl8k_vif->list); } static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) @@ -2964,8 +3361,7 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) int rc; if (conf->flags & IEEE80211_CONF_IDLE) { - mwl8k_cmd_802_11_radio_disable(hw); - priv->current_channel = NULL; + mwl8k_cmd_radio_disable(hw); return 0; } @@ -2973,19 +3369,17 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) if (rc) return rc; - rc = mwl8k_cmd_802_11_radio_enable(hw); + rc = mwl8k_cmd_radio_enable(hw); if (rc) goto out; - rc = mwl8k_cmd_set_rf_channel(hw, conf->channel); + rc = mwl8k_cmd_set_rf_channel(hw, conf); if (rc) goto out; - priv->current_channel = conf->channel; - if (conf->power_level > 18) conf->power_level = 18; - rc = mwl8k_cmd_802_11_rf_tx_power(hw, conf->power_level); + rc = mwl8k_cmd_rf_tx_power(hw, conf->power_level); if (rc) goto out; @@ -3003,79 +3397,160 @@ out: return rc; } -static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, - u32 changed) +static void +mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u32 changed) { struct mwl8k_priv *priv = hw->priv; - struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); + u32 ap_legacy_rates; + u8 ap_mcs_rates[16]; int rc; - if ((changed & BSS_CHANGED_ASSOC) == 0) + if (mwl8k_fw_lock(hw)) return; - priv->capture_beacon = false; - - rc = mwl8k_fw_lock(hw); - if (rc) - return; + /* + * No need to capture a beacon if we're no longer associated. + */ + if ((changed & BSS_CHANGED_ASSOC) && !vif->bss_conf.assoc) + priv->capture_beacon = false; - if (info->assoc) { - memcpy(&mwl8k_vif->bss_info, info, - sizeof(struct ieee80211_bss_conf)); + /* + * Get the AP's legacy and MCS rates. + */ + if (vif->bss_conf.assoc) { + struct ieee80211_sta *ap; - memcpy(mwl8k_vif->bssid, info->bssid, ETH_ALEN); + rcu_read_lock(); - /* Install rates */ - rc = mwl8k_update_rateset(hw, vif); - if (rc) + ap = ieee80211_find_sta(vif, vif->bss_conf.bssid); + if (ap == NULL) { + rcu_read_unlock(); goto out; + } + + if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) { + ap_legacy_rates = ap->supp_rates[IEEE80211_BAND_2GHZ]; + } else { + ap_legacy_rates = + ap->supp_rates[IEEE80211_BAND_5GHZ] << 5; + } + memcpy(ap_mcs_rates, ap->ht_cap.mcs.rx_mask, 16); - /* Turn on rate adaptation */ - rc = mwl8k_cmd_use_fixed_rate(hw, MWL8K_USE_AUTO_RATE, - MWL8K_UCAST_RATE, NULL); + rcu_read_unlock(); + } + + if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc) { + rc = mwl8k_cmd_set_rate(hw, vif, ap_legacy_rates, ap_mcs_rates); if (rc) goto out; - /* Set radio preamble */ - rc = mwl8k_set_radio_preamble(hw, info->use_short_preamble); + rc = mwl8k_cmd_use_fixed_rate_sta(hw); if (rc) goto out; + } - /* Set slot time */ - rc = mwl8k_cmd_set_slot(hw, info->use_short_slot); + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + rc = mwl8k_set_radio_preamble(hw, + vif->bss_conf.use_short_preamble); if (rc) goto out; + } - /* Update peer rate info */ - rc = mwl8k_cmd_update_sta_db(hw, vif, - MWL8K_STA_DB_MODIFY_ENTRY); + if (changed & BSS_CHANGED_ERP_SLOT) { + rc = mwl8k_cmd_set_slot(hw, vif->bss_conf.use_short_slot); if (rc) goto out; + } - /* Set AID */ - rc = mwl8k_cmd_set_aid(hw, vif); + if (vif->bss_conf.assoc && + (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT | + BSS_CHANGED_HT))) { + rc = mwl8k_cmd_set_aid(hw, vif, ap_legacy_rates); if (rc) goto out; + } + if (vif->bss_conf.assoc && + (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BEACON_INT))) { /* * Finalize the join. Tell rx handler to process * next beacon from our BSSID. */ - memcpy(priv->capture_bssid, mwl8k_vif->bssid, ETH_ALEN); + memcpy(priv->capture_bssid, vif->bss_conf.bssid, ETH_ALEN); priv->capture_beacon = true; - } else { - rc = mwl8k_cmd_update_sta_db(hw, vif, MWL8K_STA_DB_DEL_ENTRY); - memset(&mwl8k_vif->bss_info, 0, - sizeof(struct ieee80211_bss_conf)); - memset(mwl8k_vif->bssid, 0, ETH_ALEN); } out: mwl8k_fw_unlock(hw); } +static void +mwl8k_bss_info_changed_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u32 changed) +{ + int rc; + + if (mwl8k_fw_lock(hw)) + return; + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + rc = mwl8k_set_radio_preamble(hw, + vif->bss_conf.use_short_preamble); + if (rc) + goto out; + } + + if (changed & BSS_CHANGED_BASIC_RATES) { + int idx; + int rate; + + /* + * Use lowest supported basic rate for multicasts + * and management frames (such as probe responses -- + * beacons will always go out at 1 Mb/s). + */ + idx = ffs(vif->bss_conf.basic_rates); + if (idx) + idx--; + + if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) + rate = mwl8k_rates_24[idx].hw_value; + else + rate = mwl8k_rates_50[idx].hw_value; + + mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate); + } + + if (changed & (BSS_CHANGED_BEACON_INT | BSS_CHANGED_BEACON)) { + struct sk_buff *skb; + + skb = ieee80211_beacon_get(hw, vif); + if (skb != NULL) { + mwl8k_cmd_set_beacon(hw, vif, skb->data, skb->len); + kfree_skb(skb); + } + } + + if (changed & BSS_CHANGED_BEACON_ENABLED) + mwl8k_cmd_bss_start(hw, vif, info->enable_beacon); + +out: + mwl8k_fw_unlock(hw); +} + +static void +mwl8k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u32 changed) +{ + struct mwl8k_priv *priv = hw->priv; + + if (!priv->ap_fw) + mwl8k_bss_info_changed_sta(hw, vif, info, changed); + else + mwl8k_bss_info_changed_ap(hw, vif, info, changed); +} + static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw, int mc_count, struct dev_addr_list *mclist) { @@ -3105,7 +3580,7 @@ mwl8k_configure_filter_sniffer(struct ieee80211_hw *hw, * operation, so refuse to enable sniffer mode if a STA * interface is active. */ - if (priv->vif != NULL) { + if (!list_empty(&priv->vif_list)) { if (net_ratelimit()) printk(KERN_INFO "%s: not enabling sniffer " "mode because STA interface is active\n", @@ -3114,7 +3589,7 @@ mwl8k_configure_filter_sniffer(struct ieee80211_hw *hw, } if (!priv->sniffer_enabled) { - if (mwl8k_enable_sniffer(hw, 1)) + if (mwl8k_cmd_enable_sniffer(hw, 1)) return 0; priv->sniffer_enabled = true; } @@ -3126,6 +3601,14 @@ mwl8k_configure_filter_sniffer(struct ieee80211_hw *hw, return 1; } +static struct mwl8k_vif *mwl8k_first_vif(struct mwl8k_priv *priv) +{ + if (!list_empty(&priv->vif_list)) + return list_entry(priv->vif_list.next, struct mwl8k_vif, list); + + return NULL; +} + static void mwl8k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, @@ -3163,7 +3646,7 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw, } if (priv->sniffer_enabled) { - mwl8k_enable_sniffer(hw, 0); + mwl8k_cmd_enable_sniffer(hw, 0); priv->sniffer_enabled = false; } @@ -3174,7 +3657,8 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw, */ mwl8k_cmd_set_pre_scan(hw); } else { - u8 *bssid; + struct mwl8k_vif *mwl8k_vif; + const u8 *bssid; /* * Enable the BSS filter. @@ -3184,9 +3668,11 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw, * (where the OUI part needs to be nonzero for * the BSSID to be accepted by POST_SCAN). */ - bssid = "\x01\x00\x00\x00\x00\x00"; - if (priv->vif != NULL) - bssid = MWL8K_VIF(priv->vif)->bssid; + mwl8k_vif = mwl8k_first_vif(priv); + if (mwl8k_vif != NULL) + bssid = mwl8k_vif->vif->bss_conf.bssid; + else + bssid = "\x01\x00\x00\x00\x00\x00"; mwl8k_cmd_set_post_scan(hw, bssid); } @@ -3213,7 +3699,39 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw, static int mwl8k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) { - return mwl8k_rts_threshold(hw, MWL8K_CMD_SET, value); + return mwl8k_cmd_set_rts_threshold(hw, value); +} + +static int mwl8k_sta_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mwl8k_priv *priv = hw->priv; + + if (priv->ap_fw) + return mwl8k_cmd_set_new_stn_del(hw, vif, sta->addr); + else + return mwl8k_cmd_update_stadb_del(hw, vif, sta->addr); +} + +static int mwl8k_sta_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mwl8k_priv *priv = hw->priv; + int ret; + + if (!priv->ap_fw) { + ret = mwl8k_cmd_update_stadb_add(hw, vif, sta); + if (ret >= 0) { + MWL8K_STA(sta)->peer_id = ret; + return 0; + } + + return ret; + } + + return mwl8k_cmd_set_new_stn_add(hw, vif, sta); } static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue, @@ -3225,14 +3743,14 @@ static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue, rc = mwl8k_fw_lock(hw); if (!rc) { if (!priv->wmm_enabled) - rc = mwl8k_set_wmm(hw, 1); + rc = mwl8k_cmd_set_wmm_mode(hw, 1); if (!rc) - rc = mwl8k_set_edca_params(hw, queue, - params->cw_min, - params->cw_max, - params->aifs, - params->txop); + rc = mwl8k_cmd_set_edca_params(hw, queue, + params->cw_min, + params->cw_max, + params->aifs, + params->txop); mwl8k_fw_unlock(hw); } @@ -3240,28 +3758,26 @@ static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue, return rc; } -static int mwl8k_get_tx_stats(struct ieee80211_hw *hw, - struct ieee80211_tx_queue_stats *stats) +static int mwl8k_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) { - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_tx_queue *txq; - int index; - - spin_lock_bh(&priv->tx_lock); - for (index = 0; index < MWL8K_TX_QUEUES; index++) { - txq = priv->txq + index; - memcpy(&stats[index], &txq->stats, - sizeof(struct ieee80211_tx_queue_stats)); - } - spin_unlock_bh(&priv->tx_lock); - - return 0; + return mwl8k_cmd_get_stat(hw, stats); } -static int mwl8k_get_stats(struct ieee80211_hw *hw, - struct ieee80211_low_level_stats *stats) +static int +mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn) { - return mwl8k_cmd_802_11_get_stat(hw, stats); + switch (action) { + case IEEE80211_AMPDU_RX_START: + case IEEE80211_AMPDU_RX_STOP: + if (!(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION)) + return -ENOTSUPP; + return 0; + default: + return -ENOTSUPP; + } } static const struct ieee80211_ops mwl8k_ops = { @@ -3275,67 +3791,72 @@ static const struct ieee80211_ops mwl8k_ops = { .prepare_multicast = mwl8k_prepare_multicast, .configure_filter = mwl8k_configure_filter, .set_rts_threshold = mwl8k_set_rts_threshold, + .sta_add = mwl8k_sta_add, + .sta_remove = mwl8k_sta_remove, .conf_tx = mwl8k_conf_tx, - .get_tx_stats = mwl8k_get_tx_stats, .get_stats = mwl8k_get_stats, + .ampdu_action = mwl8k_ampdu_action, }; -static void mwl8k_tx_reclaim_handler(unsigned long data) -{ - int i; - struct ieee80211_hw *hw = (struct ieee80211_hw *) data; - struct mwl8k_priv *priv = hw->priv; - - spin_lock_bh(&priv->tx_lock); - for (i = 0; i < MWL8K_TX_QUEUES; i++) - mwl8k_txq_reclaim(hw, i, 0); - - if (priv->tx_wait != NULL && !priv->pending_tx_pkts) { - complete(priv->tx_wait); - priv->tx_wait = NULL; - } - spin_unlock_bh(&priv->tx_lock); -} - static void mwl8k_finalize_join_worker(struct work_struct *work) { struct mwl8k_priv *priv = container_of(work, struct mwl8k_priv, finalize_join_worker); struct sk_buff *skb = priv->beacon_skb; - u8 dtim = MWL8K_VIF(priv->vif)->bss_info.dtim_period; + struct ieee80211_mgmt *mgmt = (void *)skb->data; + int len = skb->len - offsetof(struct ieee80211_mgmt, u.beacon.variable); + const u8 *tim = cfg80211_find_ie(WLAN_EID_TIM, + mgmt->u.beacon.variable, len); + int dtim_period = 1; - mwl8k_finalize_join(priv->hw, skb->data, skb->len, dtim); - dev_kfree_skb(skb); + if (tim && tim[1] >= 2) + dtim_period = tim[3]; + + mwl8k_cmd_finalize_join(priv->hw, skb->data, skb->len, dtim_period); + dev_kfree_skb(skb); priv->beacon_skb = NULL; } enum { - MWL8687 = 0, + MWL8363 = 0, + MWL8687, MWL8366, }; static struct mwl8k_device_info mwl8k_info_tbl[] __devinitdata = { - { + [MWL8363] = { + .part_name = "88w8363", + .helper_image = "mwl8k/helper_8363.fw", + .fw_image = "mwl8k/fmimage_8363.fw", + }, + [MWL8687] = { .part_name = "88w8687", .helper_image = "mwl8k/helper_8687.fw", .fw_image = "mwl8k/fmimage_8687.fw", - .rxd_ops = &rxd_8687_ops, - .modes = BIT(NL80211_IFTYPE_STATION), }, - { + [MWL8366] = { .part_name = "88w8366", .helper_image = "mwl8k/helper_8366.fw", .fw_image = "mwl8k/fmimage_8366.fw", - .rxd_ops = &rxd_8366_ops, - .modes = 0, + .ap_rxd_ops = &rxd_8366_ap_ops, }, }; +MODULE_FIRMWARE("mwl8k/helper_8363.fw"); +MODULE_FIRMWARE("mwl8k/fmimage_8363.fw"); +MODULE_FIRMWARE("mwl8k/helper_8687.fw"); +MODULE_FIRMWARE("mwl8k/fmimage_8687.fw"); +MODULE_FIRMWARE("mwl8k/helper_8366.fw"); +MODULE_FIRMWARE("mwl8k/fmimage_8366.fw"); + static DEFINE_PCI_DEVICE_TABLE(mwl8k_pci_id_table) = { + { PCI_VDEVICE(MARVELL, 0x2a0c), .driver_data = MWL8363, }, + { PCI_VDEVICE(MARVELL, 0x2a24), .driver_data = MWL8363, }, { PCI_VDEVICE(MARVELL, 0x2a2b), .driver_data = MWL8687, }, { PCI_VDEVICE(MARVELL, 0x2a30), .driver_data = MWL8687, }, { PCI_VDEVICE(MARVELL, 0x2a40), .driver_data = MWL8366, }, + { PCI_VDEVICE(MARVELL, 0x2a43), .driver_data = MWL8366, }, { }, }; MODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table); @@ -3354,6 +3875,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, printed_version = 1; } + rc = pci_enable_device(pdev); if (rc) { printk(KERN_ERR "%s: Cannot enable new PCI device\n", @@ -3370,6 +3892,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, pci_set_master(pdev); + hw = ieee80211_alloc_hw(sizeof(*priv), &mwl8k_ops); if (hw == NULL) { printk(KERN_ERR "%s: ieee80211 alloc failed\n", MWL8K_NAME); @@ -3377,17 +3900,14 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, goto err_free_reg; } + SET_IEEE80211_DEV(hw, &pdev->dev); + pci_set_drvdata(pdev, hw); + priv = hw->priv; priv->hw = hw; priv->pdev = pdev; priv->device_info = &mwl8k_info_tbl[id->driver_data]; - priv->rxd_ops = priv->device_info->rxd_ops; - priv->sniffer_enabled = false; - priv->wmm_enabled = false; - priv->pending_tx_pkts = 0; - SET_IEEE80211_DEV(hw, &pdev->dev); - pci_set_drvdata(pdev, hw); priv->sram = pci_iomap(pdev, 0, 0x10000); if (priv->sram == NULL) { @@ -3410,16 +3930,46 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, } } - memcpy(priv->channels, mwl8k_channels, sizeof(mwl8k_channels)); - priv->band.band = IEEE80211_BAND_2GHZ; - priv->band.channels = priv->channels; - priv->band.n_channels = ARRAY_SIZE(mwl8k_channels); - priv->band.bitrates = priv->rates; - priv->band.n_bitrates = ARRAY_SIZE(mwl8k_rates); - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; - BUILD_BUG_ON(sizeof(priv->rates) != sizeof(mwl8k_rates)); - memcpy(priv->rates, mwl8k_rates, sizeof(mwl8k_rates)); + /* Reset firmware and hardware */ + mwl8k_hw_reset(priv); + + /* Ask userland hotplug daemon for the device firmware */ + rc = mwl8k_request_firmware(priv); + if (rc) { + printk(KERN_ERR "%s: Firmware files not found\n", + wiphy_name(hw->wiphy)); + goto err_stop_firmware; + } + + /* Load firmware into hardware */ + rc = mwl8k_load_firmware(hw); + if (rc) { + printk(KERN_ERR "%s: Cannot start firmware\n", + wiphy_name(hw->wiphy)); + goto err_stop_firmware; + } + + /* Reclaim memory once firmware is successfully loaded */ + mwl8k_release_firmware(priv); + + + if (priv->ap_fw) { + priv->rxd_ops = priv->device_info->ap_rxd_ops; + if (priv->rxd_ops == NULL) { + printk(KERN_ERR "%s: Driver does not have AP " + "firmware image support for this hardware\n", + wiphy_name(hw->wiphy)); + goto err_stop_firmware; + } + } else { + priv->rxd_ops = &rxd_sta_ops; + } + + priv->sniffer_enabled = false; + priv->wmm_enabled = false; + priv->pending_tx_pkts = 0; + /* * Extra headroom is the size of the required DMA header @@ -3432,12 +3982,13 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, hw->queues = MWL8K_TX_QUEUES; - hw->wiphy->interface_modes = priv->device_info->modes; - /* Set rssi and noise values to dBm */ hw->flags |= IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM; hw->vif_data_size = sizeof(struct mwl8k_vif); - priv->vif = NULL; + hw->sta_data_size = sizeof(struct mwl8k_sta); + + priv->macids_used = 0; + INIT_LIST_HEAD(&priv->vif_list); /* Set default radio state and preamble */ priv->radio_on = 0; @@ -3446,19 +3997,20 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, /* Finalize join worker */ INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker); - /* TX reclaim tasklet */ - tasklet_init(&priv->tx_reclaim_task, - mwl8k_tx_reclaim_handler, (unsigned long)hw); - tasklet_disable(&priv->tx_reclaim_task); + /* TX reclaim and RX tasklets. */ + tasklet_init(&priv->poll_tx_task, mwl8k_tx_poll, (unsigned long)hw); + tasklet_disable(&priv->poll_tx_task); + tasklet_init(&priv->poll_rx_task, mwl8k_rx_poll, (unsigned long)hw); + tasklet_disable(&priv->poll_rx_task); /* Power management cookie */ priv->cookie = pci_alloc_consistent(priv->pdev, 4, &priv->cookie_dma); if (priv->cookie == NULL) - goto err_iounmap; + goto err_stop_firmware; rc = mwl8k_rxq_init(hw, 0); if (rc) - goto err_iounmap; + goto err_free_cookie; rxq_refill(hw, 0, INT_MAX); mutex_init(&priv->fw_mutex); @@ -3478,7 +4030,8 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); - iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL); + iowrite32(MWL8K_A2H_INT_TX_DONE | MWL8K_A2H_INT_RX_READY, + priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL); iowrite32(0xffffffff, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); rc = request_irq(priv->pdev->irq, mwl8k_interrupt, @@ -3489,31 +4042,9 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, goto err_free_queues; } - /* Reset firmware and hardware */ - mwl8k_hw_reset(priv); - - /* Ask userland hotplug daemon for the device firmware */ - rc = mwl8k_request_firmware(priv); - if (rc) { - printk(KERN_ERR "%s: Firmware files not found\n", - wiphy_name(hw->wiphy)); - goto err_free_irq; - } - - /* Load firmware into hardware */ - rc = mwl8k_load_firmware(hw); - if (rc) { - printk(KERN_ERR "%s: Cannot start firmware\n", - wiphy_name(hw->wiphy)); - goto err_stop_firmware; - } - - /* Reclaim memory once firmware is successfully loaded */ - mwl8k_release_firmware(priv); - /* * Temporarily enable interrupts. Initial firmware host - * commands use interrupts and avoids polling. Disable + * commands use interrupts and avoid polling. Disable * interrupts when done. */ iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); @@ -3529,22 +4060,29 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, if (rc) { printk(KERN_ERR "%s: Cannot initialise firmware\n", wiphy_name(hw->wiphy)); - goto err_stop_firmware; + goto err_free_irq; } + hw->wiphy->interface_modes = 0; + if (priv->ap_macids_supported) + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP); + if (priv->sta_macids_supported) + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION); + + /* Turn radio off */ - rc = mwl8k_cmd_802_11_radio_disable(hw); + rc = mwl8k_cmd_radio_disable(hw); if (rc) { printk(KERN_ERR "%s: Cannot disable\n", wiphy_name(hw->wiphy)); - goto err_stop_firmware; + goto err_free_irq; } /* Clear MAC address */ - rc = mwl8k_set_mac_addr(hw, "\x00\x00\x00\x00\x00\x00"); + rc = mwl8k_cmd_set_mac_addr(hw, NULL, "\x00\x00\x00\x00\x00\x00"); if (rc) { printk(KERN_ERR "%s: Cannot clear MAC address\n", wiphy_name(hw->wiphy)); - goto err_stop_firmware; + goto err_free_irq; } /* Disable interrupts */ @@ -3555,7 +4093,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, if (rc) { printk(KERN_ERR "%s: Cannot register device\n", wiphy_name(hw->wiphy)); - goto err_stop_firmware; + goto err_free_queues; } printk(KERN_INFO "%s: %s v%d, %pM, %s firmware %u.%u.%u.%u\n", @@ -3567,10 +4105,6 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, return 0; -err_stop_firmware: - mwl8k_hw_reset(priv); - mwl8k_release_firmware(priv); - err_free_irq: iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); free_irq(priv->pdev->irq, hw); @@ -3580,11 +4114,16 @@ err_free_queues: mwl8k_txq_deinit(hw, i); mwl8k_rxq_deinit(hw, 0); -err_iounmap: +err_free_cookie: if (priv->cookie != NULL) pci_free_consistent(priv->pdev, 4, priv->cookie, priv->cookie_dma); +err_stop_firmware: + mwl8k_hw_reset(priv); + mwl8k_release_firmware(priv); + +err_iounmap: if (priv->regs != NULL) pci_iounmap(pdev, priv->regs); @@ -3622,15 +4161,16 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev) ieee80211_unregister_hw(hw); - /* Remove tx reclaim tasklet */ - tasklet_kill(&priv->tx_reclaim_task); + /* Remove TX reclaim and RX tasklets. */ + tasklet_kill(&priv->poll_tx_task); + tasklet_kill(&priv->poll_rx_task); /* Stop hardware */ mwl8k_hw_reset(priv); /* Return all skbs to mac80211 */ for (i = 0; i < MWL8K_TX_QUEUES; i++) - mwl8k_txq_reclaim(hw, i, 1); + mwl8k_txq_reclaim(hw, i, INT_MAX, 1); for (i = 0; i < MWL8K_TX_QUEUES; i++) mwl8k_txq_deinit(hw, i); diff --git a/drivers/net/wireless/orinoco/orinoco_cs.c b/drivers/net/wireless/orinoco/orinoco_cs.c index f27bb8367c98..1d4ada188eda 100644 --- a/drivers/net/wireless/orinoco/orinoco_cs.c +++ b/drivers/net/wireless/orinoco/orinoco_cs.c @@ -407,7 +407,6 @@ static struct pcmcia_device_id orinoco_cs_ids[] = { PCMCIA_DEVICE_PROD_ID12("3Com", "3CRWE737A AirConnect Wireless LAN PC Card", 0x41240e5b, 0x56010af3), PCMCIA_DEVICE_PROD_ID12("ACTIONTEC", "PRISM Wireless LAN PC Card", 0x393089da, 0xa71e69d5), PCMCIA_DEVICE_PROD_ID12("Addtron", "AWP-100 Wireless PCMCIA", 0xe6ec52ce, 0x08649af2), - PCMCIA_DEVICE_PROD_ID123("AIRVAST", "IEEE 802.11b Wireless PCMCIA Card", "HFA3863", 0xea569531, 0x4bcb9645, 0x355cb092), PCMCIA_DEVICE_PROD_ID12("Allied Telesyn", "AT-WCL452 Wireless PCMCIA Radio", 0x5cd01705, 0x4271660f), PCMCIA_DEVICE_PROD_ID12("ASUS", "802_11b_PC_CARD_25", 0x78fc06ee, 0xdb9aa842), PCMCIA_DEVICE_PROD_ID12("ASUS", "802_11B_CF_CARD_25", 0x78fc06ee, 0x45a50c1e), @@ -417,7 +416,6 @@ static struct pcmcia_device_id orinoco_cs_ids[] = { PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-CF-S11G", 0x2decece3, 0x82067c18), PCMCIA_DEVICE_PROD_ID12("Cabletron", "RoamAbout 802.11 DS", 0x32d445f5, 0xedeffd90), PCMCIA_DEVICE_PROD_ID12("Compaq", "WL200_11Mbps_Wireless_PCI_Card", 0x54f7c49c, 0x15a75e5b), - PCMCIA_DEVICE_PROD_ID123("corega", "WL PCCL-11", "ISL37300P", 0x0a21501a, 0x59868926, 0xc9049a39), PCMCIA_DEVICE_PROD_ID12("corega K.K.", "Wireless LAN PCC-11", 0x5261440f, 0xa6405584), PCMCIA_DEVICE_PROD_ID12("corega K.K.", "Wireless LAN PCCA-11", 0x5261440f, 0xdf6115f9), PCMCIA_DEVICE_PROD_ID12("corega_K.K.", "Wireless_LAN_PCCB-11", 0x29e33311, 0xee7a27ae), @@ -432,7 +430,6 @@ static struct pcmcia_device_id orinoco_cs_ids[] = { PCMCIA_DEVICE_PROD_ID12("INTERSIL", "HFA384x/IEEE", 0x74c5e40d, 0xdb472a18), PCMCIA_DEVICE_PROD_ID12("INTERSIL", "I-GATE 11M PC Card / PC Card plus", 0x74c5e40d, 0x8304ff77), PCMCIA_DEVICE_PROD_ID12("Intersil", "PRISM 2_5 PCMCIA ADAPTER", 0x4b801a17, 0x6345a0bf), - PCMCIA_DEVICE_PROD_ID123("Intersil", "PRISM Freedom PCMCIA Adapter", "ISL37100P", 0x4b801a17, 0xf222ec2d, 0x630d52b2), PCMCIA_DEVICE_PROD_ID12("LeArtery", "SYNCBYAIR 11Mbps Wireless LAN PC Card", 0x7e3b326a, 0x49893e92), PCMCIA_DEVICE_PROD_ID12("Linksys", "Wireless CompactFlash Card", 0x0733cc81, 0x0c52f395), PCMCIA_DEVICE_PROD_ID12("Lucent Technologies", "WaveLAN/IEEE", 0x23eb9949, 0xc562e72a), @@ -445,7 +442,6 @@ static struct pcmcia_device_id orinoco_cs_ids[] = { PCMCIA_DEVICE_PROD_ID12("Nortel Networks", "emobility 802.11 Wireless LAN PC Card", 0x2d617ea0, 0x88cd5767), PCMCIA_DEVICE_PROD_ID12("OEM", "PRISM2 IEEE 802.11 PC-Card", 0xfea54c90, 0x48f2bdd6), PCMCIA_DEVICE_PROD_ID12("OTC", "Wireless AirEZY 2411-PCC WLAN Card", 0x4ac44287, 0x235a6bed), - PCMCIA_DEVICE_PROD_ID123("PCMCIA", "11M WLAN Card v2.5", "ISL37300P", 0x281f1c5d, 0x6e440487, 0xc9049a39), PCMCIA_DEVICE_PROD_ID12("PLANEX", "GeoWave/GW-CF110", 0x209f40ab, 0xd9715264), PCMCIA_DEVICE_PROD_ID12("PLANEX", "GeoWave/GW-NS110", 0x209f40ab, 0x46263178), PCMCIA_DEVICE_PROD_ID12("PROXIM", "LAN PC CARD HARMONY 80211B", 0xc6536a5e, 0x090c3cd9), @@ -454,8 +450,11 @@ static struct pcmcia_device_id orinoco_cs_ids[] = { PCMCIA_DEVICE_PROD_ID12("SMC", "SMC2532W-B EliteConnect Wireless Adapter", 0xc4f8b18b, 0x196bd757), PCMCIA_DEVICE_PROD_ID12("SMC", "SMC2632W", 0xc4f8b18b, 0x474a1f2a), PCMCIA_DEVICE_PROD_ID12("Symbol Technologies", "LA4111 Spectrum24 Wireless LAN PC Card", 0x3f02b4d6, 0x3663cb0e), - PCMCIA_DEVICE_PROD_ID123("The Linksys Group, Inc.", "Instant Wireless Network PC Card", "ISL37300P", 0xa5f472c2, 0x590eb502, 0xc9049a39), PCMCIA_DEVICE_PROD_ID12("ZoomAir 11Mbps High", "Rate wireless Networking", 0x273fe3db, 0x32a1eaee), + PCMCIA_DEVICE_PROD_ID3("HFA3863", 0x355cb092), + PCMCIA_DEVICE_PROD_ID3("ISL37100P", 0x630d52b2), + PCMCIA_DEVICE_PROD_ID3("ISL37101P-10", 0xdd97a26b), + PCMCIA_DEVICE_PROD_ID3("ISL37300P", 0xc9049a39), PCMCIA_DEVICE_NULL, }; MODULE_DEVICE_TABLE(pcmcia, orinoco_cs_ids); diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index 18012dbfb45d..4f752a21495f 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -33,21 +33,29 @@ MODULE_DESCRIPTION("Softmac Prism54 common code"); MODULE_LICENSE("GPL"); MODULE_ALIAS("prism54common"); +static int p54_sta_add_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct p54_common *priv = hw->priv; + + /* + * Notify the firmware that we don't want or we don't + * need to buffer frames for this station anymore. + */ + + p54_sta_unlock(priv, sta->addr); + + return 0; +} + static void p54_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, enum sta_notify_cmd notify_cmd, struct ieee80211_sta *sta) { struct p54_common *priv = dev->priv; - switch (notify_cmd) { - case STA_NOTIFY_ADD: - case STA_NOTIFY_REMOVE: - /* - * Notify the firmware that we don't want or we don't - * need to buffer frames for this station anymore. - */ - p54_sta_unlock(priv, sta->addr); - break; + switch (notify_cmd) { case STA_NOTIFY_AWAKE: /* update the firmware's filter table */ p54_sta_unlock(priv, sta->addr); @@ -216,7 +224,7 @@ static void p54_stop(struct ieee80211_hw *dev) } static int p54_add_interface(struct ieee80211_hw *dev, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct p54_common *priv = dev->priv; @@ -226,28 +234,28 @@ static int p54_add_interface(struct ieee80211_hw *dev, return -EOPNOTSUPP; } - priv->vif = conf->vif; + priv->vif = vif; - switch (conf->type) { + switch (vif->type) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MESH_POINT: - priv->mode = conf->type; + priv->mode = vif->type; break; default: mutex_unlock(&priv->conf_mutex); return -EOPNOTSUPP; } - memcpy(priv->mac_addr, conf->mac_addr, ETH_ALEN); + memcpy(priv->mac_addr, vif->addr, ETH_ALEN); p54_setup_mac(priv); mutex_unlock(&priv->conf_mutex); return 0; } static void p54_remove_interface(struct ieee80211_hw *dev, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct p54_common *priv = dev->priv; @@ -358,16 +366,6 @@ static int p54_get_stats(struct ieee80211_hw *dev, return 0; } -static int p54_get_tx_stats(struct ieee80211_hw *dev, - struct ieee80211_tx_queue_stats *stats) -{ - struct p54_common *priv = dev->priv; - - memcpy(stats, &priv->tx_stats[P54_QUEUE_DATA], - sizeof(stats[0]) * dev->queues); - return 0; -} - static void p54_bss_info_changed(struct ieee80211_hw *dev, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, @@ -516,13 +514,14 @@ static const struct ieee80211_ops p54_ops = { .remove_interface = p54_remove_interface, .set_tim = p54_set_tim, .sta_notify = p54_sta_notify, + .sta_add = p54_sta_add_remove, + .sta_remove = p54_sta_add_remove, .set_key = p54_set_key, .config = p54_config, .bss_info_changed = p54_bss_info_changed, .configure_filter = p54_configure_filter, .conf_tx = p54_conf_tx, .get_stats = p54_get_stats, - .get_tx_stats = p54_get_tx_stats }; struct ieee80211_hw *p54_init_common(size_t priv_data_len) diff --git a/drivers/net/wireless/p54/p54.h b/drivers/net/wireless/p54/p54.h index 1afc39410e85..43a3b2ead81a 100644 --- a/drivers/net/wireless/p54/p54.h +++ b/drivers/net/wireless/p54/p54.h @@ -157,6 +157,12 @@ struct p54_led_dev { #endif /* CONFIG_P54_LEDS */ +struct p54_tx_queue_stats { + unsigned int len; + unsigned int limit; + unsigned int count; +}; + struct p54_common { struct ieee80211_hw *hw; struct ieee80211_vif *vif; @@ -183,7 +189,7 @@ struct p54_common { /* (e)DCF / QOS state */ bool use_short_slot; spinlock_t tx_stats_lock; - struct ieee80211_tx_queue_stats tx_stats[8]; + struct p54_tx_queue_stats tx_stats[8]; struct p54_edcf_queue_param qos_params[8]; /* Radio data */ diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c index a72f7c2577de..bda29c00f3ef 100644 --- a/drivers/net/wireless/p54/p54pci.c +++ b/drivers/net/wireless/p54/p54pci.c @@ -157,6 +157,14 @@ static void p54p_refill_rx_ring(struct ieee80211_hw *dev, skb_tail_pointer(skb), priv->common.rx_mtu + 32, PCI_DMA_FROMDEVICE); + + if (pci_dma_mapping_error(priv->pdev, mapping)) { + dev_kfree_skb_any(skb); + dev_err(&priv->pdev->dev, + "RX DMA Mapping error\n"); + break; + } + desc->host_addr = cpu_to_le32(mapping); desc->device_addr = 0; // FIXME: necessary? desc->len = cpu_to_le16(priv->common.rx_mtu + 32); @@ -226,14 +234,14 @@ static void p54p_check_rx_ring(struct ieee80211_hw *dev, u32 *index, p54p_refill_rx_ring(dev, ring_index, ring, ring_limit, rx_buf); } -/* caller must hold priv->lock */ static void p54p_check_tx_ring(struct ieee80211_hw *dev, u32 *index, int ring_index, struct p54p_desc *ring, u32 ring_limit, - void **tx_buf) + struct sk_buff **tx_buf) { struct p54p_priv *priv = dev->priv; struct p54p_ring_control *ring_control = priv->ring_control; struct p54p_desc *desc; + struct sk_buff *skb; u32 idx, i; i = (*index) % ring_limit; @@ -242,9 +250,8 @@ static void p54p_check_tx_ring(struct ieee80211_hw *dev, u32 *index, while (i != idx) { desc = &ring[i]; - if (tx_buf[i]) - if (FREE_AFTER_TX((struct sk_buff *) tx_buf[i])) - p54_free_skb(dev, tx_buf[i]); + + skb = tx_buf[i]; tx_buf[i] = NULL; pci_unmap_single(priv->pdev, le32_to_cpu(desc->host_addr), @@ -255,17 +262,28 @@ static void p54p_check_tx_ring(struct ieee80211_hw *dev, u32 *index, desc->len = 0; desc->flags = 0; + if (skb && FREE_AFTER_TX(skb)) + p54_free_skb(dev, skb); + i++; i %= ring_limit; } } -static void p54p_rx_tasklet(unsigned long dev_id) +static void p54p_tasklet(unsigned long dev_id) { struct ieee80211_hw *dev = (struct ieee80211_hw *)dev_id; struct p54p_priv *priv = dev->priv; struct p54p_ring_control *ring_control = priv->ring_control; + p54p_check_tx_ring(dev, &priv->tx_idx_mgmt, 3, ring_control->tx_mgmt, + ARRAY_SIZE(ring_control->tx_mgmt), + priv->tx_buf_mgmt); + + p54p_check_tx_ring(dev, &priv->tx_idx_data, 1, ring_control->tx_data, + ARRAY_SIZE(ring_control->tx_data), + priv->tx_buf_data); + p54p_check_rx_ring(dev, &priv->rx_idx_mgmt, 2, ring_control->rx_mgmt, ARRAY_SIZE(ring_control->rx_mgmt), priv->rx_buf_mgmt); @@ -280,59 +298,49 @@ static irqreturn_t p54p_interrupt(int irq, void *dev_id) { struct ieee80211_hw *dev = dev_id; struct p54p_priv *priv = dev->priv; - struct p54p_ring_control *ring_control = priv->ring_control; __le32 reg; - spin_lock(&priv->lock); reg = P54P_READ(int_ident); if (unlikely(reg == cpu_to_le32(0xFFFFFFFF))) { - spin_unlock(&priv->lock); - return IRQ_HANDLED; + goto out; } - P54P_WRITE(int_ack, reg); reg &= P54P_READ(int_enable); - if (reg & cpu_to_le32(ISL38XX_INT_IDENT_UPDATE)) { - p54p_check_tx_ring(dev, &priv->tx_idx_mgmt, - 3, ring_control->tx_mgmt, - ARRAY_SIZE(ring_control->tx_mgmt), - priv->tx_buf_mgmt); - - p54p_check_tx_ring(dev, &priv->tx_idx_data, - 1, ring_control->tx_data, - ARRAY_SIZE(ring_control->tx_data), - priv->tx_buf_data); - - tasklet_schedule(&priv->rx_tasklet); - - } else if (reg & cpu_to_le32(ISL38XX_INT_IDENT_INIT)) + if (reg & cpu_to_le32(ISL38XX_INT_IDENT_UPDATE)) + tasklet_schedule(&priv->tasklet); + else if (reg & cpu_to_le32(ISL38XX_INT_IDENT_INIT)) complete(&priv->boot_comp); - spin_unlock(&priv->lock); - +out: return reg ? IRQ_HANDLED : IRQ_NONE; } static void p54p_tx(struct ieee80211_hw *dev, struct sk_buff *skb) { + unsigned long flags; struct p54p_priv *priv = dev->priv; struct p54p_ring_control *ring_control = priv->ring_control; - unsigned long flags; struct p54p_desc *desc; dma_addr_t mapping; u32 device_idx, idx, i; spin_lock_irqsave(&priv->lock, flags); - device_idx = le32_to_cpu(ring_control->device_idx[1]); idx = le32_to_cpu(ring_control->host_idx[1]); i = idx % ARRAY_SIZE(ring_control->tx_data); - priv->tx_buf_data[i] = skb; mapping = pci_map_single(priv->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(priv->pdev, mapping)) { + spin_unlock_irqrestore(&priv->lock, flags); + p54_free_skb(dev, skb); + dev_err(&priv->pdev->dev, "TX DMA mapping error\n"); + return ; + } + priv->tx_buf_data[i] = skb; + desc = &ring_control->tx_data[i]; desc->host_addr = cpu_to_le32(mapping); desc->device_addr = ((struct p54_hdr *)skb->data)->req_id; @@ -354,14 +362,14 @@ static void p54p_stop(struct ieee80211_hw *dev) unsigned int i; struct p54p_desc *desc; - tasklet_kill(&priv->rx_tasklet); - P54P_WRITE(int_enable, cpu_to_le32(0)); P54P_READ(int_enable); udelay(10); free_irq(priv->pdev->irq, dev); + tasklet_kill(&priv->tasklet); + P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET)); for (i = 0; i < ARRAY_SIZE(priv->rx_buf_data); i++) { @@ -545,7 +553,7 @@ static int __devinit p54p_probe(struct pci_dev *pdev, priv->common.tx = p54p_tx; spin_lock_init(&priv->lock); - tasklet_init(&priv->rx_tasklet, p54p_rx_tasklet, (unsigned long)dev); + tasklet_init(&priv->tasklet, p54p_tasklet, (unsigned long)dev); err = request_firmware(&priv->firmware, "isl3886pci", &priv->pdev->dev); diff --git a/drivers/net/wireless/p54/p54pci.h b/drivers/net/wireless/p54/p54pci.h index fbb683953fb2..2feead617a3b 100644 --- a/drivers/net/wireless/p54/p54pci.h +++ b/drivers/net/wireless/p54/p54pci.h @@ -92,7 +92,7 @@ struct p54p_priv { struct p54_common common; struct pci_dev *pdev; struct p54p_csr __iomem *map; - struct tasklet_struct rx_tasklet; + struct tasklet_struct tasklet; const struct firmware *firmware; spinlock_t lock; struct p54p_ring_control *ring_control; @@ -101,8 +101,8 @@ struct p54p_priv { u32 rx_idx_mgmt, tx_idx_mgmt; struct sk_buff *rx_buf_data[8]; struct sk_buff *rx_buf_mgmt[4]; - void *tx_buf_data[32]; - void *tx_buf_mgmt[4]; + struct sk_buff *tx_buf_data[32]; + struct sk_buff *tx_buf_mgmt[4]; struct completion boot_comp; }; diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index 92af9b96bb7a..b3c4fbd80d8d 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -36,6 +36,7 @@ static struct usb_device_id p54u_table[] __devinitdata = { /* Version 1 devices (pci chip + net2280) */ {USB_DEVICE(0x0506, 0x0a11)}, /* 3COM 3CRWE254G72 */ {USB_DEVICE(0x0707, 0xee06)}, /* SMC 2862W-G */ + {USB_DEVICE(0x07aa, 0x001c)}, /* Corega CG-WLUSB2GT */ {USB_DEVICE(0x083a, 0x4501)}, /* Accton 802.11g WN4501 USB */ {USB_DEVICE(0x083a, 0x4502)}, /* Siemens Gigaset USB Adapter */ {USB_DEVICE(0x083a, 0x5501)}, /* Phillips CPWUA054 */ @@ -60,6 +61,7 @@ static struct usb_device_id p54u_table[] __devinitdata = { {USB_DEVICE(0x06b9, 0x0121)}, /* Thomson SpeedTouch 121g */ {USB_DEVICE(0x0707, 0xee13)}, /* SMC 2862W-G version 2 */ {USB_DEVICE(0x083a, 0x4521)}, /* Siemens Gigaset USB Adapter 54 version 2 */ + {USB_DEVICE(0x083a, 0xf503)}, /* Accton FD7050E ver 1010ec */ {USB_DEVICE(0x0846, 0x4240)}, /* Netgear WG111 (v2) */ {USB_DEVICE(0x0915, 0x2000)}, /* Cohiba Proto board */ {USB_DEVICE(0x0915, 0x2002)}, /* Cohiba Proto board */ diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index b6dda2b27fb5..66057999a93c 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c @@ -183,10 +183,10 @@ static int p54_tx_qos_accounting_alloc(struct p54_common *priv, struct sk_buff *skb, const u16 p54_queue) { - struct ieee80211_tx_queue_stats *queue; + struct p54_tx_queue_stats *queue; unsigned long flags; - if (WARN_ON(p54_queue > P54_QUEUE_NUM)) + if (WARN_ON(p54_queue >= P54_QUEUE_NUM)) return -EINVAL; queue = &priv->tx_stats[p54_queue]; diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 2ecbedb26e15..305c106fdc1c 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -2594,23 +2594,9 @@ end: /* * driver/device initialization */ -static int bcm4320a_early_init(struct usbnet *usbdev) -{ - /* bcm4320a doesn't handle configuration parameters well. Try - * set any and you get partially zeroed mac and broken device. - */ - - return 0; -} - -static int bcm4320b_early_init(struct usbnet *usbdev) +static void rndis_copy_module_params(struct usbnet *usbdev) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - char buf[8]; - - /* Early initialization settings, setting these won't have effect - * if called after generic_rndis_bind(). - */ priv->param_country[0] = modparam_country[0]; priv->param_country[1] = modparam_country[1]; @@ -2652,6 +2638,32 @@ static int bcm4320b_early_init(struct usbnet *usbdev) priv->param_workaround_interval = 500; else priv->param_workaround_interval = modparam_workaround_interval; +} + +static int bcm4320a_early_init(struct usbnet *usbdev) +{ + /* copy module parameters for bcm4320a so that iwconfig reports txpower + * and workaround parameter is copied to private structure correctly. + */ + rndis_copy_module_params(usbdev); + + /* bcm4320a doesn't handle configuration parameters well. Try + * set any and you get partially zeroed mac and broken device. + */ + + return 0; +} + +static int bcm4320b_early_init(struct usbnet *usbdev) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + char buf[8]; + + rndis_copy_module_params(usbdev); + + /* Early initialization settings, setting these won't have effect + * if called after generic_rndis_bind(). + */ rndis_set_config_parameter_str(usbdev, "Country", priv->param_country); rndis_set_config_parameter_str(usbdev, "FrameBursting", diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig index bf60689aaabb..5239e082cd0f 100644 --- a/drivers/net/wireless/rt2x00/Kconfig +++ b/drivers/net/wireless/rt2x00/Kconfig @@ -54,17 +54,17 @@ config RT61PCI When compiled as a module, this driver will be called rt61pci. config RT2800PCI_PCI - tristate + boolean depends on PCI default y config RT2800PCI_SOC - tristate + boolean depends on RALINK_RT288X || RALINK_RT305X default y config RT2800PCI - tristate "Ralink rt2800 (PCI/PCMCIA) support (VERY EXPERIMENTAL)" + tristate "Ralink rt28xx/rt30xx/rt35xx (PCI/PCIe/PCMCIA) support (EXPERIMENTAL)" depends on (RT2800PCI_PCI || RT2800PCI_SOC) && EXPERIMENTAL select RT2800_LIB select RT2X00_LIB_PCI if RT2800PCI_PCI @@ -75,7 +75,7 @@ config RT2800PCI select CRC_CCITT select EEPROM_93CX6 ---help--- - This adds support for rt2800 wireless chipset family. + This adds support for rt2800/rt3000/rt3500 wireless chipset family. Supported chips: RT2760, RT2790, RT2860, RT2880, RT2890 & RT3052 This driver is non-functional at the moment and is intended for @@ -83,6 +83,32 @@ config RT2800PCI When compiled as a module, this driver will be called "rt2800pci.ko". +if RT2800PCI + +config RT2800PCI_RT30XX + bool "rt2800pci - Include support for rt30xx (PCI/PCIe/PCMCIA) devices" + default n + ---help--- + This adds support for rt30xx wireless chipset family to the + rt2800pci driver. + Supported chips: RT3090, RT3091 & RT3092 + + Support for these devices is non-functional at the moment and is + intended for testers and developers. + +config RT2800PCI_RT35XX + bool "rt2800pci - Include support for rt35xx (PCI/PCIe/PCMCIA) devices" + default n + ---help--- + This adds support for rt35xx wireless chipset family to the + rt2800pci driver. + Supported chips: RT3060, RT3062, RT3562, RT3592 + + Support for these devices is non-functional at the moment and is + intended for testers and developers. + +endif + config RT2500USB tristate "Ralink rt2500 (USB) support" depends on USB @@ -126,6 +152,43 @@ config RT2800USB When compiled as a module, this driver will be called "rt2800usb.ko". +if RT2800USB + +config RT2800USB_RT30XX + bool "rt2800usb - Include support for rt30xx (USB) devices" + default n + ---help--- + This adds support for rt30xx wireless chipset family to the + rt2800usb driver. + Supported chips: RT3070, RT3071 & RT3072 + + Support for these devices is non-functional at the moment and is + intended for testers and developers. + +config RT2800USB_RT35XX + bool "rt2800usb - Include support for rt35xx (USB) devices" + default n + ---help--- + This adds support for rt35xx wireless chipset family to the + rt2800usb driver. + Supported chips: RT3572 + + Support for these devices is non-functional at the moment and is + intended for testers and developers. + +config RT2800USB_UNKNOWN + bool "rt2800usb - Include support for unknown (USB) devices" + default n + ---help--- + This adds support for rt2800 family devices that are known to + have a rt2800 family chipset, but for which the exact chipset + is unknown. + + Support status for these devices is unknown, and enabling these + devices may or may not work. + +endif + config RT2800_LIB tristate diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c index e7f46405a418..676814d3a81b 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/rt2x00/rt2400pci.c @@ -451,7 +451,7 @@ static void rt2400pci_config_channel(struct rt2x00_dev *rt2x00dev, /* * RF2420 chipset don't need any additional actions. */ - if (rt2x00_rf(&rt2x00dev->chip, RF2420)) + if (rt2x00_rf(rt2x00dev, RF2420)) return; /* @@ -1340,11 +1340,10 @@ static int rt2400pci_init_eeprom(struct rt2x00_dev *rt2x00dev) */ value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); rt2x00pci_register_read(rt2x00dev, CSR0, ®); - rt2x00_set_chip_rf(rt2x00dev, value, reg); - rt2x00_print_chip(rt2x00dev); + rt2x00_set_chip(rt2x00dev, RT2460, value, + rt2x00_get_field32(reg, CSR0_REVISION)); - if (!rt2x00_rf(&rt2x00dev->chip, RF2420) && - !rt2x00_rf(&rt2x00dev->chip, RF2421)) { + if (!rt2x00_rf(rt2x00dev, RF2420) && !rt2x00_rf(rt2x00dev, RF2421)) { ERROR(rt2x00dev, "Invalid RF chipset detected.\n"); return -ENODEV; } @@ -1562,7 +1561,6 @@ static const struct ieee80211_ops rt2400pci_mac80211_ops = { .get_stats = rt2x00mac_get_stats, .bss_info_changed = rt2x00mac_bss_info_changed, .conf_tx = rt2400pci_conf_tx, - .get_tx_stats = rt2x00mac_get_tx_stats, .get_tsf = rt2400pci_get_tsf, .tx_last_beacon = rt2400pci_tx_last_beacon, .rfkill_poll = rt2x00mac_rfkill_poll, diff --git a/drivers/net/wireless/rt2x00/rt2400pci.h b/drivers/net/wireless/rt2x00/rt2400pci.h index 6c21ef66dfe0..606137738848 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.h +++ b/drivers/net/wireless/rt2x00/rt2400pci.h @@ -65,6 +65,7 @@ * CSR0: ASIC revision number. */ #define CSR0 0x0000 +#define CSR0_REVISION FIELD32(0x0000ffff) /* * CSR1: System control register. diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c index 408fcfc120f5..c71266142ae9 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/rt2x00/rt2500pci.c @@ -440,8 +440,7 @@ static void rt2500pci_config_ant(struct rt2x00_dev *rt2x00dev, /* * RT2525E and RT5222 need to flip TX I/Q */ - if (rt2x00_rf(&rt2x00dev->chip, RF2525E) || - rt2x00_rf(&rt2x00dev->chip, RF5222)) { + if (rt2x00_rf(rt2x00dev, RF2525E) || rt2x00_rf(rt2x00dev, RF5222)) { rt2x00_set_field8(&r2, BBP_R2_TX_IQ_FLIP, 1); rt2x00_set_field32(®, BBPCSR1_CCK_FLIP, 1); rt2x00_set_field32(®, BBPCSR1_OFDM_FLIP, 1); @@ -449,7 +448,7 @@ static void rt2500pci_config_ant(struct rt2x00_dev *rt2x00dev, /* * RT2525E does not need RX I/Q Flip. */ - if (rt2x00_rf(&rt2x00dev->chip, RF2525E)) + if (rt2x00_rf(rt2x00dev, RF2525E)) rt2x00_set_field8(&r14, BBP_R14_RX_IQ_FLIP, 0); } else { rt2x00_set_field32(®, BBPCSR1_CCK_FLIP, 0); @@ -475,14 +474,14 @@ static void rt2500pci_config_channel(struct rt2x00_dev *rt2x00dev, * Switch on tuning bits. * For RT2523 devices we do not need to update the R1 register. */ - if (!rt2x00_rf(&rt2x00dev->chip, RF2523)) + if (!rt2x00_rf(rt2x00dev, RF2523)) rt2x00_set_field32(&rf->rf1, RF1_TUNER, 1); rt2x00_set_field32(&rf->rf3, RF3_TUNER, 1); /* * For RT2525 we should first set the channel to half band higher. */ - if (rt2x00_rf(&rt2x00dev->chip, RF2525)) { + if (rt2x00_rf(rt2x00dev, RF2525)) { static const u32 vals[] = { 0x00080cbe, 0x00080d02, 0x00080d06, 0x00080d0a, 0x00080d0e, 0x00080d12, 0x00080d16, 0x00080d1a, @@ -516,7 +515,7 @@ static void rt2500pci_config_channel(struct rt2x00_dev *rt2x00dev, * Switch off tuning bits. * For RT2523 devices we do not need to update the R1 register. */ - if (!rt2x00_rf(&rt2x00dev->chip, RF2523)) { + if (!rt2x00_rf(rt2x00dev, RF2523)) { rt2x00_set_field32(&rf->rf1, RF1_TUNER, 0); rt2500pci_rf_write(rt2x00dev, 1, rf->rf1); } @@ -640,7 +639,7 @@ static void rt2500pci_link_tuner(struct rt2x00_dev *rt2x00dev, * up to version C the link tuning should halt after 20 * seconds while being associated. */ - if (rt2x00_rev(&rt2x00dev->chip) < RT2560_VERSION_D && + if (rt2x00_rev(rt2x00dev) < RT2560_VERSION_D && rt2x00dev->intf_associated && count > 20) return; @@ -650,7 +649,7 @@ static void rt2500pci_link_tuner(struct rt2x00_dev *rt2x00dev, * should go straight to dynamic CCA tuning when they * are not associated. */ - if (rt2x00_rev(&rt2x00dev->chip) < RT2560_VERSION_D || + if (rt2x00_rev(rt2x00dev) < RT2560_VERSION_D || !rt2x00dev->intf_associated) goto dynamic_cca_tune; @@ -1504,15 +1503,15 @@ static int rt2500pci_init_eeprom(struct rt2x00_dev *rt2x00dev) */ value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); rt2x00pci_register_read(rt2x00dev, CSR0, ®); - rt2x00_set_chip_rf(rt2x00dev, value, reg); - rt2x00_print_chip(rt2x00dev); - - if (!rt2x00_rf(&rt2x00dev->chip, RF2522) && - !rt2x00_rf(&rt2x00dev->chip, RF2523) && - !rt2x00_rf(&rt2x00dev->chip, RF2524) && - !rt2x00_rf(&rt2x00dev->chip, RF2525) && - !rt2x00_rf(&rt2x00dev->chip, RF2525E) && - !rt2x00_rf(&rt2x00dev->chip, RF5222)) { + rt2x00_set_chip(rt2x00dev, RT2560, value, + rt2x00_get_field32(reg, CSR0_REVISION)); + + if (!rt2x00_rf(rt2x00dev, RF2522) && + !rt2x00_rf(rt2x00dev, RF2523) && + !rt2x00_rf(rt2x00dev, RF2524) && + !rt2x00_rf(rt2x00dev, RF2525) && + !rt2x00_rf(rt2x00dev, RF2525E) && + !rt2x00_rf(rt2x00dev, RF5222)) { ERROR(rt2x00dev, "Invalid RF chipset detected.\n"); return -ENODEV; } @@ -1744,22 +1743,22 @@ static int rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) spec->supported_bands = SUPPORT_BAND_2GHZ; spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; - if (rt2x00_rf(&rt2x00dev->chip, RF2522)) { + if (rt2x00_rf(rt2x00dev, RF2522)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2522); spec->channels = rf_vals_bg_2522; - } else if (rt2x00_rf(&rt2x00dev->chip, RF2523)) { + } else if (rt2x00_rf(rt2x00dev, RF2523)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2523); spec->channels = rf_vals_bg_2523; - } else if (rt2x00_rf(&rt2x00dev->chip, RF2524)) { + } else if (rt2x00_rf(rt2x00dev, RF2524)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2524); spec->channels = rf_vals_bg_2524; - } else if (rt2x00_rf(&rt2x00dev->chip, RF2525)) { + } else if (rt2x00_rf(rt2x00dev, RF2525)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525); spec->channels = rf_vals_bg_2525; - } else if (rt2x00_rf(&rt2x00dev->chip, RF2525E)) { + } else if (rt2x00_rf(rt2x00dev, RF2525E)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525e); spec->channels = rf_vals_bg_2525e; - } else if (rt2x00_rf(&rt2x00dev->chip, RF5222)) { + } else if (rt2x00_rf(rt2x00dev, RF5222)) { spec->supported_bands |= SUPPORT_BAND_5GHZ; spec->num_channels = ARRAY_SIZE(rf_vals_5222); spec->channels = rf_vals_5222; @@ -1860,7 +1859,6 @@ static const struct ieee80211_ops rt2500pci_mac80211_ops = { .get_stats = rt2x00mac_get_stats, .bss_info_changed = rt2x00mac_bss_info_changed, .conf_tx = rt2x00mac_conf_tx, - .get_tx_stats = rt2x00mac_get_tx_stats, .get_tsf = rt2500pci_get_tsf, .tx_last_beacon = rt2500pci_tx_last_beacon, .rfkill_poll = rt2x00mac_rfkill_poll, diff --git a/drivers/net/wireless/rt2x00/rt2500pci.h b/drivers/net/wireless/rt2x00/rt2500pci.h index b0075674c09b..6471f8e74b35 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.h +++ b/drivers/net/wireless/rt2x00/rt2500pci.h @@ -76,6 +76,7 @@ * CSR0: ASIC revision number. */ #define CSR0 0x0000 +#define CSR0_REVISION FIELD32(0x0000ffff) /* * CSR1: System control register. diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index 83f2592c59de..ee34c137e7cd 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -565,8 +565,7 @@ static void rt2500usb_config_ant(struct rt2x00_dev *rt2x00dev, /* * RT2525E and RT5222 need to flip TX I/Q */ - if (rt2x00_rf(&rt2x00dev->chip, RF2525E) || - rt2x00_rf(&rt2x00dev->chip, RF5222)) { + if (rt2x00_rf(rt2x00dev, RF2525E) || rt2x00_rf(rt2x00dev, RF5222)) { rt2x00_set_field8(&r2, BBP_R2_TX_IQ_FLIP, 1); rt2x00_set_field16(&csr5, PHY_CSR5_CCK_FLIP, 1); rt2x00_set_field16(&csr6, PHY_CSR6_OFDM_FLIP, 1); @@ -574,7 +573,7 @@ static void rt2500usb_config_ant(struct rt2x00_dev *rt2x00dev, /* * RT2525E does not need RX I/Q Flip. */ - if (rt2x00_rf(&rt2x00dev->chip, RF2525E)) + if (rt2x00_rf(rt2x00dev, RF2525E)) rt2x00_set_field8(&r14, BBP_R14_RX_IQ_FLIP, 0); } else { rt2x00_set_field16(&csr5, PHY_CSR5_CCK_FLIP, 0); @@ -598,7 +597,7 @@ static void rt2500usb_config_channel(struct rt2x00_dev *rt2x00dev, /* * For RT2525E we should first set the channel to half band higher. */ - if (rt2x00_rf(&rt2x00dev->chip, RF2525E)) { + if (rt2x00_rf(rt2x00dev, RF2525E)) { static const u32 vals[] = { 0x000008aa, 0x000008ae, 0x000008ae, 0x000008b2, 0x000008b2, 0x000008b6, 0x000008b6, 0x000008ba, @@ -793,7 +792,7 @@ static int rt2500usb_init_registers(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(®, MAC_CSR1_HOST_READY, 1); rt2500usb_register_write(rt2x00dev, MAC_CSR1, reg); - if (rt2x00_rev(&rt2x00dev->chip) >= RT2570_VERSION_C) { + if (rt2x00_rev(rt2x00dev) >= RT2570_VERSION_C) { rt2500usb_register_read(rt2x00dev, PHY_CSR2, ®); rt2x00_set_field16(®, PHY_CSR2_LNA, 0); } else { @@ -1409,21 +1408,18 @@ static int rt2500usb_init_eeprom(struct rt2x00_dev *rt2x00dev) value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); rt2500usb_register_read(rt2x00dev, MAC_CSR0, ®); rt2x00_set_chip(rt2x00dev, RT2570, value, reg); - rt2x00_print_chip(rt2x00dev); - - if (!rt2x00_check_rev(&rt2x00dev->chip, 0x000ffff0, 0) || - rt2x00_check_rev(&rt2x00dev->chip, 0x0000000f, 0)) { + if (((reg & 0xfff0) != 0) || ((reg & 0x0000000f) == 0)) { ERROR(rt2x00dev, "Invalid RT chipset detected.\n"); return -ENODEV; } - if (!rt2x00_rf(&rt2x00dev->chip, RF2522) && - !rt2x00_rf(&rt2x00dev->chip, RF2523) && - !rt2x00_rf(&rt2x00dev->chip, RF2524) && - !rt2x00_rf(&rt2x00dev->chip, RF2525) && - !rt2x00_rf(&rt2x00dev->chip, RF2525E) && - !rt2x00_rf(&rt2x00dev->chip, RF5222)) { + if (!rt2x00_rf(rt2x00dev, RF2522) && + !rt2x00_rf(rt2x00dev, RF2523) && + !rt2x00_rf(rt2x00dev, RF2524) && + !rt2x00_rf(rt2x00dev, RF2525) && + !rt2x00_rf(rt2x00dev, RF2525E) && + !rt2x00_rf(rt2x00dev, RF5222)) { ERROR(rt2x00dev, "Invalid RF chipset detected.\n"); return -ENODEV; } @@ -1667,22 +1663,22 @@ static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) spec->supported_bands = SUPPORT_BAND_2GHZ; spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; - if (rt2x00_rf(&rt2x00dev->chip, RF2522)) { + if (rt2x00_rf(rt2x00dev, RF2522)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2522); spec->channels = rf_vals_bg_2522; - } else if (rt2x00_rf(&rt2x00dev->chip, RF2523)) { + } else if (rt2x00_rf(rt2x00dev, RF2523)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2523); spec->channels = rf_vals_bg_2523; - } else if (rt2x00_rf(&rt2x00dev->chip, RF2524)) { + } else if (rt2x00_rf(rt2x00dev, RF2524)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2524); spec->channels = rf_vals_bg_2524; - } else if (rt2x00_rf(&rt2x00dev->chip, RF2525)) { + } else if (rt2x00_rf(rt2x00dev, RF2525)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525); spec->channels = rf_vals_bg_2525; - } else if (rt2x00_rf(&rt2x00dev->chip, RF2525E)) { + } else if (rt2x00_rf(rt2x00dev, RF2525E)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525e); spec->channels = rf_vals_bg_2525e; - } else if (rt2x00_rf(&rt2x00dev->chip, RF5222)) { + } else if (rt2x00_rf(rt2x00dev, RF5222)) { spec->supported_bands |= SUPPORT_BAND_5GHZ; spec->num_channels = ARRAY_SIZE(rf_vals_5222); spec->channels = rf_vals_5222; @@ -1763,7 +1759,6 @@ static const struct ieee80211_ops rt2500usb_mac80211_ops = { .get_stats = rt2x00mac_get_stats, .bss_info_changed = rt2x00mac_bss_info_changed, .conf_tx = rt2x00mac_conf_tx, - .get_tx_stats = rt2x00mac_get_tx_stats, .rfkill_poll = rt2x00mac_rfkill_poll, }; diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h index 1a7eae357fef..74c0433dba37 100644 --- a/drivers/net/wireless/rt2x00/rt2800.h +++ b/drivers/net/wireless/rt2x00/rt2800.h @@ -60,11 +60,11 @@ /* * Chipset version. */ -#define RT2860C_VERSION 0x28600100 -#define RT2860D_VERSION 0x28600101 -#define RT2880E_VERSION 0x28720200 -#define RT2883_VERSION 0x28830300 -#define RT3070_VERSION 0x30700200 +#define RT2860C_VERSION 0x0100 +#define RT2860D_VERSION 0x0101 +#define RT2880E_VERSION 0x0200 +#define RT2883_VERSION 0x0300 +#define RT3070_VERSION 0x0200 /* * Signal information. @@ -408,8 +408,8 @@ * ASIC_VER: 2860 or 2870 */ #define MAC_CSR0 0x1000 -#define MAC_CSR0_ASIC_REV FIELD32(0x0000ffff) -#define MAC_CSR0_ASIC_VER FIELD32(0xffff0000) +#define MAC_CSR0_REVISION FIELD32(0x0000ffff) +#define MAC_CSR0_CHIPSET FIELD32(0xffff0000) /* * MAC_SYS_CTRL: diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 9deae41cb784..18d4d8e4ae6b 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -37,9 +37,12 @@ #include <linux/module.h> #include "rt2x00.h" -#if defined(CONFIG_RT2800USB) || defined(CONFIG_RT2800USB_MODULE) +#if defined(CONFIG_RT2X00_LIB_USB) || defined(CONFIG_RT2X00_LIB_USB_MODULE) #include "rt2x00usb.h" #endif +#if defined(CONFIG_RT2X00_LIB_PCI) || defined(CONFIG_RT2X00_LIB_PCI_MODULE) +#include "rt2x00pci.h" +#endif #include "rt2800lib.h" #include "rt2800.h" #include "rt2800usb.h" @@ -89,7 +92,7 @@ static void rt2800_bbp_write(struct rt2x00_dev *rt2x00dev, rt2x00_set_field32(®, BBP_CSR_CFG_REGNUM, word); rt2x00_set_field32(®, BBP_CSR_CFG_BUSY, 1); rt2x00_set_field32(®, BBP_CSR_CFG_READ_CONTROL, 0); - if (rt2x00_intf_is_pci(rt2x00dev)) + if (rt2x00_is_pci(rt2x00dev) || rt2x00_is_soc(rt2x00dev)) rt2x00_set_field32(®, BBP_CSR_CFG_BBP_RW_MODE, 1); rt2800_register_write_lock(rt2x00dev, BBP_CSR_CFG, reg); @@ -118,7 +121,7 @@ static void rt2800_bbp_read(struct rt2x00_dev *rt2x00dev, rt2x00_set_field32(®, BBP_CSR_CFG_REGNUM, word); rt2x00_set_field32(®, BBP_CSR_CFG_BUSY, 1); rt2x00_set_field32(®, BBP_CSR_CFG_READ_CONTROL, 1); - if (rt2x00_intf_is_pci(rt2x00dev)) + if (rt2x00_is_pci(rt2x00dev) || rt2x00_is_soc(rt2x00dev)) rt2x00_set_field32(®, BBP_CSR_CFG_BBP_RW_MODE, 1); rt2800_register_write_lock(rt2x00dev, BBP_CSR_CFG, reg); @@ -218,10 +221,9 @@ void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev, u32 reg; /* - * RT2880 and RT3052 don't support MCU requests. + * SOC devices don't support MCU requests. */ - if (rt2x00_rt(&rt2x00dev->chip, RT2880) || - rt2x00_rt(&rt2x00dev->chip, RT3052)) + if (rt2x00_is_soc(rt2x00dev)) return; mutex_lock(&rt2x00dev->csr_mutex); @@ -246,6 +248,25 @@ void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev, } EXPORT_SYMBOL_GPL(rt2800_mcu_request); +int rt2800_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u32 reg; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); + if (!rt2x00_get_field32(reg, WPDMA_GLO_CFG_TX_DMA_BUSY) && + !rt2x00_get_field32(reg, WPDMA_GLO_CFG_RX_DMA_BUSY)) + return 0; + + msleep(1); + } + + ERROR(rt2x00dev, "WPDMA TX/RX busy, aborting.\n"); + return -EACCES; +} +EXPORT_SYMBOL_GPL(rt2800_wait_wpdma_ready); + #ifdef CONFIG_RT2X00_LIB_DEBUGFS const struct rt2x00debug rt2800_rt2x00debug = { .owner = THIS_MODULE, @@ -348,7 +369,7 @@ static int rt2800_blink_set(struct led_classdev *led_cdev, return 0; } -void rt2800_init_led(struct rt2x00_dev *rt2x00dev, +static void rt2800_init_led(struct rt2x00_dev *rt2x00dev, struct rt2x00_led *led, enum led_type type) { led->rt2x00dev = rt2x00dev; @@ -357,7 +378,6 @@ void rt2800_init_led(struct rt2x00_dev *rt2x00dev, led->led_dev.blink_set = rt2800_blink_set; led->flags = LED_INITIALIZED; } -EXPORT_SYMBOL_GPL(rt2800_init_led); #endif /* CONFIG_RT2X00_LIB_LEDS */ /* @@ -643,7 +663,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) switch ((int)ant->tx) { case 1: rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 0); - if (rt2x00_intf_is_pci(rt2x00dev)) + if (rt2x00_is_pci(rt2x00dev) || rt2x00_is_soc(rt2x00dev)) rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 0); break; case 2: @@ -806,12 +826,12 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, unsigned int tx_pin; u8 bbp; - if ((rt2x00_rt(&rt2x00dev->chip, RT3070) || - rt2x00_rt(&rt2x00dev->chip, RT3090)) && - (rt2x00_rf(&rt2x00dev->chip, RF2020) || - rt2x00_rf(&rt2x00dev->chip, RF3020) || - rt2x00_rf(&rt2x00dev->chip, RF3021) || - rt2x00_rf(&rt2x00dev->chip, RF3022))) + if ((rt2x00_rt(rt2x00dev, RT3070) || + rt2x00_rt(rt2x00dev, RT3090)) && + (rt2x00_rf(rt2x00dev, RF2020) || + rt2x00_rf(rt2x00dev, RF3020) || + rt2x00_rf(rt2x00dev, RF3021) || + rt2x00_rf(rt2x00dev, RF3022))) rt2800_config_channel_rt3x(rt2x00dev, conf, rf, info); else rt2800_config_channel_rt2x(rt2x00dev, conf, rf, info); @@ -878,7 +898,8 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, rt2x00_set_field8(&bbp, BBP3_HT40_PLUS, conf_is_ht40_plus(conf)); rt2800_bbp_write(rt2x00dev, 3, bbp); - if (rt2x00_rev(&rt2x00dev->chip) == RT2860C_VERSION) { + if (rt2x00_rt(rt2x00dev, RT2860) && + (rt2x00_rev(rt2x00dev) == RT2860C_VERSION)) { if (conf_is_ht40(conf)) { rt2800_bbp_write(rt2x00dev, 69, 0x1a); rt2800_bbp_write(rt2x00dev, 70, 0x0a); @@ -1040,8 +1061,9 @@ EXPORT_SYMBOL_GPL(rt2800_link_stats); static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev) { if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) { - if (rt2x00_intf_is_usb(rt2x00dev) && - rt2x00_rev(&rt2x00dev->chip) == RT3070_VERSION) + if (rt2x00_is_usb(rt2x00dev) && + rt2x00_rt(rt2x00dev, RT3070) && + (rt2x00_rev(rt2x00dev) == RT3070_VERSION)) return 0x1c + (2 * rt2x00dev->lna_gain); else return 0x2e + rt2x00dev->lna_gain; @@ -1072,7 +1094,8 @@ EXPORT_SYMBOL_GPL(rt2800_reset_tuner); void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, const u32 count) { - if (rt2x00_rev(&rt2x00dev->chip) == RT2860C_VERSION) + if (rt2x00_rt(rt2x00dev, RT2860) && + (rt2x00_rev(rt2x00dev) == RT2860C_VERSION)) return; /* @@ -1092,7 +1115,7 @@ int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) u32 reg; unsigned int i; - if (rt2x00_intf_is_usb(rt2x00dev)) { + if (rt2x00_is_usb(rt2x00dev)) { /* * Wait until BBP and RF are ready. */ @@ -1111,7 +1134,7 @@ int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) rt2800_register_read(rt2x00dev, PBF_SYS_CTRL, ®); rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, reg & ~0x00002000); - } else if (rt2x00_intf_is_pci(rt2x00dev)) + } else if (rt2x00_is_pci(rt2x00dev) || rt2x00_is_soc(rt2x00dev)) rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003); rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, ®); @@ -1119,9 +1142,9 @@ int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_BBP, 1); rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); - if (rt2x00_intf_is_usb(rt2x00dev)) { + if (rt2x00_is_usb(rt2x00dev)) { rt2800_register_write(rt2x00dev, USB_DMA_CFG, 0x00000000); -#if defined(CONFIG_RT2800USB) || defined(CONFIG_RT2800USB_MODULE) +#if defined(CONFIG_RT2X00_LIB_USB) || defined(CONFIG_RT2X00_LIB_USB_MODULE) rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0, USB_MODE_RESET, REGISTER_TIMEOUT); #endif @@ -1157,8 +1180,9 @@ int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) rt2x00_set_field32(®, BCN_TIME_CFG_TX_TIME_COMPENSATE, 0); rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); - if (rt2x00_intf_is_usb(rt2x00dev) && - rt2x00_rev(&rt2x00dev->chip) == RT3070_VERSION) { + if (rt2x00_is_usb(rt2x00dev) && + rt2x00_rt(rt2x00dev, RT3070) && + (rt2x00_rev(rt2x00dev) == RT3070_VERSION)) { rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400); rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000); rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); @@ -1185,8 +1209,14 @@ int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) rt2800_register_read(rt2x00dev, MAX_LEN_CFG, ®); rt2x00_set_field32(®, MAX_LEN_CFG_MAX_MPDU, AGGREGATION_SIZE); - if (rt2x00_rev(&rt2x00dev->chip) >= RT2880E_VERSION && - rt2x00_rev(&rt2x00dev->chip) < RT3070_VERSION) + if ((rt2x00_rt(rt2x00dev, RT2872) && + (rt2x00_rev(rt2x00dev) >= RT2880E_VERSION)) || + rt2x00_rt(rt2x00dev, RT2880) || + rt2x00_rt(rt2x00dev, RT2883) || + rt2x00_rt(rt2x00dev, RT2890) || + rt2x00_rt(rt2x00dev, RT3052) || + (rt2x00_rt(rt2x00dev, RT3070) && + (rt2x00_rev(rt2x00dev) < RT3070_VERSION))) rt2x00_set_field32(®, MAX_LEN_CFG_MAX_PSDU, 2); else rt2x00_set_field32(®, MAX_LEN_CFG_MAX_PSDU, 1); @@ -1276,7 +1306,7 @@ int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_GF40, 1); rt2800_register_write(rt2x00dev, GF40_PROT_CFG, reg); - if (rt2x00_intf_is_usb(rt2x00dev)) { + if (rt2x00_is_usb(rt2x00dev)) { rt2800_register_write(rt2x00dev, PBF_CFG, 0xf40006); rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); @@ -1336,7 +1366,7 @@ int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) rt2800_register_write(rt2x00dev, HW_BEACON_BASE6, 0); rt2800_register_write(rt2x00dev, HW_BEACON_BASE7, 0); - if (rt2x00_intf_is_usb(rt2x00dev)) { + if (rt2x00_is_usb(rt2x00dev)) { rt2800_register_read(rt2x00dev, USB_CYC_CFG, ®); rt2x00_set_field32(®, USB_CYC_CFG_CLOCK_CYCLE, 30); rt2800_register_write(rt2x00dev, USB_CYC_CFG, reg); @@ -1465,22 +1495,25 @@ int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 103, 0x00); rt2800_bbp_write(rt2x00dev, 105, 0x05); - if (rt2x00_rev(&rt2x00dev->chip) == RT2860C_VERSION) { + if (rt2x00_rt(rt2x00dev, RT2860) && + (rt2x00_rev(rt2x00dev) == RT2860C_VERSION)) { rt2800_bbp_write(rt2x00dev, 69, 0x16); rt2800_bbp_write(rt2x00dev, 73, 0x12); } - if (rt2x00_rev(&rt2x00dev->chip) > RT2860D_VERSION) + if (rt2x00_rt(rt2x00dev, RT2860) && + (rt2x00_rev(rt2x00dev) > RT2860D_VERSION)) rt2800_bbp_write(rt2x00dev, 84, 0x19); - if (rt2x00_intf_is_usb(rt2x00dev) && - rt2x00_rev(&rt2x00dev->chip) == RT3070_VERSION) { + if (rt2x00_is_usb(rt2x00dev) && + rt2x00_rt(rt2x00dev, RT3070) && + (rt2x00_rev(rt2x00dev) == RT3070_VERSION)) { rt2800_bbp_write(rt2x00dev, 70, 0x0a); rt2800_bbp_write(rt2x00dev, 84, 0x99); rt2800_bbp_write(rt2x00dev, 105, 0x05); } - if (rt2x00_rt(&rt2x00dev->chip, RT3052)) { + if (rt2x00_rt(rt2x00dev, RT3052)) { rt2800_bbp_write(rt2x00dev, 31, 0x08); rt2800_bbp_write(rt2x00dev, 78, 0x0e); rt2800_bbp_write(rt2x00dev, 80, 0x08); @@ -1565,14 +1598,15 @@ int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev) u8 rfcsr; u8 bbp; - if (rt2x00_intf_is_usb(rt2x00dev) && - rt2x00_rev(&rt2x00dev->chip) != RT3070_VERSION) + if (rt2x00_is_usb(rt2x00dev) && + rt2x00_rt(rt2x00dev, RT3070) && + (rt2x00_rev(rt2x00dev) != RT3070_VERSION)) return 0; - if (rt2x00_intf_is_pci(rt2x00dev)) { - if (!rt2x00_rf(&rt2x00dev->chip, RF3020) && - !rt2x00_rf(&rt2x00dev->chip, RF3021) && - !rt2x00_rf(&rt2x00dev->chip, RF3022)) + if (rt2x00_is_pci(rt2x00dev) || rt2x00_is_soc(rt2x00dev)) { + if (!rt2x00_rf(rt2x00dev, RF3020) && + !rt2x00_rf(rt2x00dev, RF3021) && + !rt2x00_rf(rt2x00dev, RF3022)) return 0; } @@ -1586,7 +1620,7 @@ int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev) rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 0); rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); - if (rt2x00_intf_is_usb(rt2x00dev)) { + if (rt2x00_is_usb(rt2x00dev)) { rt2800_rfcsr_write(rt2x00dev, 4, 0x40); rt2800_rfcsr_write(rt2x00dev, 5, 0x03); rt2800_rfcsr_write(rt2x00dev, 6, 0x02); @@ -1607,7 +1641,7 @@ int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev) rt2800_rfcsr_write(rt2x00dev, 25, 0x01); rt2800_rfcsr_write(rt2x00dev, 27, 0x03); rt2800_rfcsr_write(rt2x00dev, 29, 0x1f); - } else if (rt2x00_intf_is_pci(rt2x00dev)) { + } else if (rt2x00_is_pci(rt2x00dev) || rt2x00_is_soc(rt2x00dev)) { rt2800_rfcsr_write(rt2x00dev, 0, 0x50); rt2800_rfcsr_write(rt2x00dev, 1, 0x01); rt2800_rfcsr_write(rt2x00dev, 2, 0xf7); @@ -1737,7 +1771,12 @@ int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF2820); rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word); EEPROM(rt2x00dev, "Antenna: 0x%04x\n", word); - } else if (rt2x00_rev(&rt2x00dev->chip) < RT2883_VERSION) { + } else if (rt2x00_rt(rt2x00dev, RT2860) || + rt2x00_rt(rt2x00dev, RT2870) || + rt2x00_rt(rt2x00dev, RT2872) || + rt2x00_rt(rt2x00dev, RT2880) || + (rt2x00_rt(rt2x00dev, RT2883) && + (rt2x00_rev(rt2x00dev) < RT2883_VERSION))) { /* * There is a max of 2 RX streams for RT28x0 series */ @@ -1836,36 +1875,34 @@ int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); rt2800_register_read(rt2x00dev, MAC_CSR0, ®); - rt2x00_set_chip_rf(rt2x00dev, value, reg); - - if (rt2x00_intf_is_usb(rt2x00dev)) { - struct rt2x00_chip *chip = &rt2x00dev->chip; - - /* - * The check for rt2860 is not a typo, some rt2870 hardware - * identifies itself as rt2860 in the CSR register. - */ - if (rt2x00_check_rev(chip, 0xfff00000, 0x28600000) || - rt2x00_check_rev(chip, 0xfff00000, 0x28700000) || - rt2x00_check_rev(chip, 0xfff00000, 0x28800000)) { - rt2x00_set_chip_rt(rt2x00dev, RT2870); - } else if (rt2x00_check_rev(chip, 0xffff0000, 0x30700000)) { - rt2x00_set_chip_rt(rt2x00dev, RT3070); - } else { - ERROR(rt2x00dev, "Invalid RT chipset detected.\n"); - return -ENODEV; - } + rt2x00_set_chip(rt2x00dev, rt2x00_get_field32(reg, MAC_CSR0_CHIPSET), + value, rt2x00_get_field32(reg, MAC_CSR0_REVISION)); + + if (!rt2x00_rt(rt2x00dev, RT2860) && + !rt2x00_rt(rt2x00dev, RT2870) && + !rt2x00_rt(rt2x00dev, RT2872) && + !rt2x00_rt(rt2x00dev, RT2880) && + !rt2x00_rt(rt2x00dev, RT2883) && + !rt2x00_rt(rt2x00dev, RT2890) && + !rt2x00_rt(rt2x00dev, RT3052) && + !rt2x00_rt(rt2x00dev, RT3070) && + !rt2x00_rt(rt2x00dev, RT3071) && + !rt2x00_rt(rt2x00dev, RT3090) && + !rt2x00_rt(rt2x00dev, RT3390) && + !rt2x00_rt(rt2x00dev, RT3572)) { + ERROR(rt2x00dev, "Invalid RT chipset detected.\n"); + return -ENODEV; } - rt2x00_print_chip(rt2x00dev); - - if (!rt2x00_rf(&rt2x00dev->chip, RF2820) && - !rt2x00_rf(&rt2x00dev->chip, RF2850) && - !rt2x00_rf(&rt2x00dev->chip, RF2720) && - !rt2x00_rf(&rt2x00dev->chip, RF2750) && - !rt2x00_rf(&rt2x00dev->chip, RF3020) && - !rt2x00_rf(&rt2x00dev->chip, RF2020) && - !rt2x00_rf(&rt2x00dev->chip, RF3021) && - !rt2x00_rf(&rt2x00dev->chip, RF3022)) { + + if (!rt2x00_rf(rt2x00dev, RF2820) && + !rt2x00_rf(rt2x00dev, RF2850) && + !rt2x00_rf(rt2x00dev, RF2720) && + !rt2x00_rf(rt2x00dev, RF2750) && + !rt2x00_rf(rt2x00dev, RF3020) && + !rt2x00_rf(rt2x00dev, RF2020) && + !rt2x00_rf(rt2x00dev, RF3021) && + !rt2x00_rf(rt2x00dev, RF3022) && + !rt2x00_rf(rt2x00dev, RF3052)) { ERROR(rt2x00dev, "Invalid RF chipset detected.\n"); return -ENODEV; } @@ -2013,7 +2050,6 @@ static const struct rf_channel rf_vals_302x[] = { int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { - struct rt2x00_chip *chip = &rt2x00dev->chip; struct hw_mode_spec *spec = &rt2x00dev->spec; struct channel_info *info; char *tx_power1; @@ -2024,7 +2060,7 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) /* * Disable powersaving as default on PCI devices. */ - if (rt2x00_intf_is_pci(rt2x00dev)) + if (rt2x00_is_pci(rt2x00dev) || rt2x00_is_soc(rt2x00dev)) rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; /* @@ -2049,19 +2085,19 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) spec->supported_bands = SUPPORT_BAND_2GHZ; spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; - if (rt2x00_rf(chip, RF2820) || - rt2x00_rf(chip, RF2720) || - (rt2x00_intf_is_pci(rt2x00dev) && rt2x00_rf(chip, RF3052))) { + if (rt2x00_rf(rt2x00dev, RF2820) || + rt2x00_rf(rt2x00dev, RF2720) || + rt2x00_rf(rt2x00dev, RF3052)) { spec->num_channels = 14; spec->channels = rf_vals; - } else if (rt2x00_rf(chip, RF2850) || rt2x00_rf(chip, RF2750)) { + } else if (rt2x00_rf(rt2x00dev, RF2850) || rt2x00_rf(rt2x00dev, RF2750)) { spec->supported_bands |= SUPPORT_BAND_5GHZ; spec->num_channels = ARRAY_SIZE(rf_vals); spec->channels = rf_vals; - } else if (rt2x00_rf(chip, RF3020) || - rt2x00_rf(chip, RF2020) || - rt2x00_rf(chip, RF3021) || - rt2x00_rf(chip, RF3022)) { + } else if (rt2x00_rf(rt2x00dev, RF3020) || + rt2x00_rf(rt2x00dev, RF2020) || + rt2x00_rf(rt2x00dev, RF3021) || + rt2x00_rf(rt2x00dev, RF3022)) { spec->num_channels = ARRAY_SIZE(rf_vals_302x); spec->channels = rf_vals_302x; } @@ -2069,7 +2105,7 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) /* * Initialize HT information. */ - if (!rt2x00_rf(chip, RF2020)) + if (!rt2x00_rf(rt2x00dev, RF2020)) spec->ht.ht_supported = true; else spec->ht.ht_supported = false; @@ -2282,7 +2318,6 @@ const struct ieee80211_ops rt2800_mac80211_ops = { .set_rts_threshold = rt2800_set_rts_threshold, .bss_info_changed = rt2x00mac_bss_info_changed, .conf_tx = rt2800_conf_tx, - .get_tx_stats = rt2x00mac_get_tx_stats, .get_tsf = rt2800_get_tsf, .rfkill_poll = rt2x00mac_rfkill_poll, }; diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h index 535ce22f2ac8..ebabeae62d1b 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.h +++ b/drivers/net/wireless/rt2x00/rt2800lib.h @@ -114,8 +114,6 @@ void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev, extern const struct rt2x00debug rt2800_rt2x00debug; int rt2800_rfkill_poll(struct rt2x00_dev *rt2x00dev); -void rt2800_init_led(struct rt2x00_dev *rt2x00dev, - struct rt2x00_led *led, enum led_type type); int rt2800_config_shared_key(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_crypto *crypto, struct ieee80211_key_conf *key); @@ -139,6 +137,7 @@ void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, int rt2800_init_registers(struct rt2x00_dev *rt2x00dev); int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev); int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev); +int rt2800_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev); int rt2800_efuse_detect(struct rt2x00_dev *rt2x00dev); void rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev); diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index dfc886fcb44d..0e4c41766edc 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -48,14 +48,6 @@ #include "rt2800.h" #include "rt2800pci.h" -#ifdef CONFIG_RT2800PCI_PCI_MODULE -#define CONFIG_RT2800PCI_PCI -#endif - -#ifdef CONFIG_RT2800PCI_WISOC_MODULE -#define CONFIG_RT2800PCI_WISOC -#endif - /* * Allow hardware encryption to be disabled. */ @@ -87,7 +79,7 @@ static void rt2800pci_mcu_status(struct rt2x00_dev *rt2x00dev, const u8 token) rt2800_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0); } -#ifdef CONFIG_RT2800PCI_WISOC +#ifdef CONFIG_RT2800PCI_SOC static void rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev) { u32 *base_addr = (u32 *) KSEG1ADDR(0x1F040000); /* XXX for RT3052 */ @@ -98,7 +90,7 @@ static void rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev) static inline void rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev) { } -#endif /* CONFIG_RT2800PCI_WISOC */ +#endif /* CONFIG_RT2800PCI_SOC */ #ifdef CONFIG_RT2800PCI_PCI static void rt2800pci_eepromregister_read(struct eeprom_93cx6 *eeprom) @@ -461,24 +453,6 @@ static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev, rt2800_register_write(rt2x00dev, INT_MASK_CSR, reg); } -static int rt2800pci_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - u32 reg; - - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); - if (!rt2x00_get_field32(reg, WPDMA_GLO_CFG_TX_DMA_BUSY) && - !rt2x00_get_field32(reg, WPDMA_GLO_CFG_RX_DMA_BUSY)) - return 0; - - msleep(1); - } - - ERROR(rt2x00dev, "WPDMA TX/RX busy, aborting.\n"); - return -EACCES; -} - static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev) { u32 reg; @@ -487,10 +461,10 @@ static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev) /* * Initialize all registers. */ - if (unlikely(rt2800pci_wait_wpdma_ready(rt2x00dev) || + if (unlikely(rt2800_wait_wpdma_ready(rt2x00dev) || rt2800pci_init_queues(rt2x00dev) || rt2800_init_registers(rt2x00dev) || - rt2800pci_wait_wpdma_ready(rt2x00dev) || + rt2800_wait_wpdma_ready(rt2x00dev) || rt2800_init_bbp(rt2x00dev) || rt2800_init_rfcsr(rt2x00dev))) return -EIO; @@ -570,7 +544,7 @@ static void rt2800pci_disable_radio(struct rt2x00_dev *rt2x00dev) rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00); /* Wait for DMA, ignore error */ - rt2800pci_wait_wpdma_ready(rt2x00dev); + rt2800_wait_wpdma_ready(rt2x00dev); } static int rt2800pci_set_state(struct rt2x00_dev *rt2x00dev, @@ -835,7 +809,6 @@ static void rt2800pci_fill_rxdone(struct queue_entry *entry, struct rxdone_entry_desc *rxdesc) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); struct queue_entry_priv_pci *entry_priv = entry->priv_data; __le32 *rxd = entry_priv->desc; __le32 *rxwi = (__le32 *)entry->skb->data; @@ -883,10 +856,8 @@ static void rt2800pci_fill_rxdone(struct queue_entry *entry, if (rt2x00_get_field32(rxd3, RXD_W3_MY_BSS)) rxdesc->dev_flags |= RXDONE_MY_BSS; - if (rt2x00_get_field32(rxd3, RXD_W3_L2PAD)) { + if (rt2x00_get_field32(rxd3, RXD_W3_L2PAD)) rxdesc->dev_flags |= RXDONE_L2PAD; - skbdesc->flags |= SKBDESC_L2_PADDED; - } if (rt2x00_get_field32(rxwi1, RXWI_W1_SHORT_GI)) rxdesc->flags |= RX_FLAG_SHORT_GI; @@ -927,7 +898,6 @@ static void rt2800pci_fill_rxdone(struct queue_entry *entry, * Remove TXWI descriptor from start of buffer. */ skb_pull(entry->skb, RXWI_DESC_SIZE); - skb_trim(entry->skb, rxdesc->size); } /* @@ -1071,18 +1041,12 @@ static int rt2800pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) /* * Read EEPROM into buffer */ - switch (rt2x00dev->chip.rt) { - case RT2880: - case RT3052: + if (rt2x00_is_soc(rt2x00dev)) rt2800pci_read_eeprom_soc(rt2x00dev); - break; - default: - if (rt2800pci_efuse_detect(rt2x00dev)) - rt2800pci_read_eeprom_efuse(rt2x00dev); - else - rt2800pci_read_eeprom_pci(rt2x00dev); - break; - } + else if (rt2800pci_efuse_detect(rt2x00dev)) + rt2800pci_read_eeprom_efuse(rt2x00dev); + else + rt2800pci_read_eeprom_pci(rt2x00dev); return rt2800_validate_eeprom(rt2x00dev); } @@ -1133,8 +1097,7 @@ static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev) /* * This device requires firmware. */ - if (!rt2x00_rt(&rt2x00dev->chip, RT2880) && - !rt2x00_rt(&rt2x00dev->chip, RT3052)) + if (!rt2x00_is_soc(rt2x00dev)) __set_bit(DRIVER_REQUIRE_FIRMWARE, &rt2x00dev->flags); __set_bit(DRIVER_REQUIRE_DMA, &rt2x00dev->flags); __set_bit(DRIVER_REQUIRE_L2PAD, &rt2x00dev->flags); @@ -1222,7 +1185,10 @@ static const struct rt2x00_ops rt2800pci_ops = { * RT2800pci module information. */ static struct pci_device_id rt2800pci_device_table[] = { - { PCI_DEVICE(0x1462, 0x891a), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1814, 0x0601), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1814, 0x0681), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1814, 0x0701), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1814, 0x0781), PCI_DEVICE_DATA(&rt2800pci_ops) }, { PCI_DEVICE(0x1432, 0x7708), PCI_DEVICE_DATA(&rt2800pci_ops) }, { PCI_DEVICE(0x1432, 0x7727), PCI_DEVICE_DATA(&rt2800pci_ops) }, { PCI_DEVICE(0x1432, 0x7728), PCI_DEVICE_DATA(&rt2800pci_ops) }, @@ -1230,18 +1196,19 @@ static struct pci_device_id rt2800pci_device_table[] = { { PCI_DEVICE(0x1432, 0x7748), PCI_DEVICE_DATA(&rt2800pci_ops) }, { PCI_DEVICE(0x1432, 0x7758), PCI_DEVICE_DATA(&rt2800pci_ops) }, { PCI_DEVICE(0x1432, 0x7768), PCI_DEVICE_DATA(&rt2800pci_ops) }, - { PCI_DEVICE(0x1814, 0x0601), PCI_DEVICE_DATA(&rt2800pci_ops) }, - { PCI_DEVICE(0x1814, 0x0681), PCI_DEVICE_DATA(&rt2800pci_ops) }, - { PCI_DEVICE(0x1814, 0x0701), PCI_DEVICE_DATA(&rt2800pci_ops) }, - { PCI_DEVICE(0x1814, 0x0781), PCI_DEVICE_DATA(&rt2800pci_ops) }, - { PCI_DEVICE(0x1814, 0x3060), PCI_DEVICE_DATA(&rt2800pci_ops) }, - { PCI_DEVICE(0x1814, 0x3062), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1a3b, 0x1059), PCI_DEVICE_DATA(&rt2800pci_ops) }, +#ifdef CONFIG_RT2800PCI_RT30XX { PCI_DEVICE(0x1814, 0x3090), PCI_DEVICE_DATA(&rt2800pci_ops) }, { PCI_DEVICE(0x1814, 0x3091), PCI_DEVICE_DATA(&rt2800pci_ops) }, { PCI_DEVICE(0x1814, 0x3092), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1462, 0x891a), PCI_DEVICE_DATA(&rt2800pci_ops) }, +#endif +#ifdef CONFIG_RT2800PCI_RT35XX + { PCI_DEVICE(0x1814, 0x3060), PCI_DEVICE_DATA(&rt2800pci_ops) }, + { PCI_DEVICE(0x1814, 0x3062), PCI_DEVICE_DATA(&rt2800pci_ops) }, { PCI_DEVICE(0x1814, 0x3562), PCI_DEVICE_DATA(&rt2800pci_ops) }, { PCI_DEVICE(0x1814, 0x3592), PCI_DEVICE_DATA(&rt2800pci_ops) }, - { PCI_DEVICE(0x1a3b, 0x1059), PCI_DEVICE_DATA(&rt2800pci_ops) }, +#endif { 0, } }; @@ -1255,12 +1222,11 @@ MODULE_DEVICE_TABLE(pci, rt2800pci_device_table); #endif /* CONFIG_RT2800PCI_PCI */ MODULE_LICENSE("GPL"); -#ifdef CONFIG_RT2800PCI_WISOC -#if defined(CONFIG_RALINK_RT288X) -__rt2x00soc_probe(RT2880, &rt2800pci_ops); -#elif defined(CONFIG_RALINK_RT305X) -__rt2x00soc_probe(RT3052, &rt2800pci_ops); -#endif +#ifdef CONFIG_RT2800PCI_SOC +static int rt2800soc_probe(struct platform_device *pdev) +{ + return rt2x00soc_probe(pdev, rt2800pci_ops); +} static struct platform_driver rt2800soc_driver = { .driver = { @@ -1268,12 +1234,12 @@ static struct platform_driver rt2800soc_driver = { .owner = THIS_MODULE, .mod_name = KBUILD_MODNAME, }, - .probe = __rt2x00soc_probe, + .probe = rt2800soc_probe, .remove = __devexit_p(rt2x00soc_remove), .suspend = rt2x00soc_suspend, .resume = rt2x00soc_resume, }; -#endif /* CONFIG_RT2800PCI_WISOC */ +#endif /* CONFIG_RT2800PCI_SOC */ #ifdef CONFIG_RT2800PCI_PCI static struct pci_driver rt2800pci_driver = { @@ -1290,7 +1256,7 @@ static int __init rt2800pci_init(void) { int ret = 0; -#ifdef CONFIG_RT2800PCI_WISOC +#ifdef CONFIG_RT2800PCI_SOC ret = platform_driver_register(&rt2800soc_driver); if (ret) return ret; @@ -1298,7 +1264,7 @@ static int __init rt2800pci_init(void) #ifdef CONFIG_RT2800PCI_PCI ret = pci_register_driver(&rt2800pci_driver); if (ret) { -#ifdef CONFIG_RT2800PCI_WISOC +#ifdef CONFIG_RT2800PCI_SOC platform_driver_unregister(&rt2800soc_driver); #endif return ret; @@ -1313,7 +1279,7 @@ static void __exit rt2800pci_exit(void) #ifdef CONFIG_RT2800PCI_PCI pci_unregister_driver(&rt2800pci_driver); #endif -#ifdef CONFIG_RT2800PCI_WISOC +#ifdef CONFIG_RT2800PCI_SOC platform_driver_unregister(&rt2800soc_driver); #endif } diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index ab95346cf6a3..5e4ee2023fcf 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -92,7 +92,6 @@ static bool rt2800usb_check_crc(const u8 *data, const size_t len) static int rt2800usb_check_firmware(struct rt2x00_dev *rt2x00dev, const u8 *data, const size_t len) { - u16 chipset = (rt2x00_rev(&rt2x00dev->chip) >> 16) & 0xffff; size_t offset = 0; /* @@ -111,9 +110,9 @@ static int rt2800usb_check_firmware(struct rt2x00_dev *rt2x00dev, * Check if we need the upper 4kb firmware data or not. */ if ((len == 4096) && - (chipset != 0x2860) && - (chipset != 0x2872) && - (chipset != 0x3070)) + !rt2x00_rt(rt2x00dev, RT2860) && + !rt2x00_rt(rt2x00dev, RT2872) && + !rt2x00_rt(rt2x00dev, RT3070)) return FW_BAD_VERSION; /* @@ -138,14 +137,13 @@ static int rt2800usb_load_firmware(struct rt2x00_dev *rt2x00dev, u32 reg; u32 offset; u32 length; - u16 chipset = (rt2x00_rev(&rt2x00dev->chip) >> 16) & 0xffff; /* * Check which section of the firmware we need. */ - if ((chipset == 0x2860) || - (chipset == 0x2872) || - (chipset == 0x3070)) { + if (rt2x00_rt(rt2x00dev, RT2860) || + rt2x00_rt(rt2x00dev, RT2872) || + rt2x00_rt(rt2x00dev, RT3070)) { offset = 0; length = 4096; } else { @@ -200,9 +198,9 @@ static int rt2800usb_load_firmware(struct rt2x00_dev *rt2x00dev, */ rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0xff, 0, 0); - if ((chipset == 0x3070) || - (chipset == 0x3071) || - (chipset == 0x3572)) { + if (rt2x00_rt(rt2x00dev, RT3070) || + rt2x00_rt(rt2x00dev, RT3071) || + rt2x00_rt(rt2x00dev, RT3572)) { udelay(200); rt2800_mcu_request(rt2x00dev, MCU_CURRENT, 0, 0, 0); udelay(10); @@ -248,24 +246,6 @@ static void rt2800usb_toggle_rx(struct rt2x00_dev *rt2x00dev, rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); } -static int rt2800usb_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - u32 reg; - - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); - if (!rt2x00_get_field32(reg, WPDMA_GLO_CFG_TX_DMA_BUSY) && - !rt2x00_get_field32(reg, WPDMA_GLO_CFG_RX_DMA_BUSY)) - return 0; - - msleep(1); - } - - ERROR(rt2x00dev, "WPDMA TX/RX busy, aborting.\n"); - return -EACCES; -} - static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev) { u32 reg; @@ -274,7 +254,7 @@ static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev) /* * Initialize all registers. */ - if (unlikely(rt2800usb_wait_wpdma_ready(rt2x00dev) || + if (unlikely(rt2800_wait_wpdma_ready(rt2x00dev) || rt2800_init_registers(rt2x00dev) || rt2800_init_bbp(rt2x00dev) || rt2800_init_rfcsr(rt2x00dev))) @@ -295,9 +275,7 @@ static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev) rt2800_register_read(rt2x00dev, USB_DMA_CFG, ®); rt2x00_set_field32(®, USB_DMA_CFG_PHY_CLEAR, 0); - /* Don't use bulk in aggregation when working with USB 1.1 */ - rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_EN, - (rt2x00dev->rx->usb_maxpacket == 512)); + rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_EN, 0); rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_TIMEOUT, 128); /* * Total room for RX frames in kilobytes, PBF might still exceed @@ -346,7 +324,7 @@ static void rt2800usb_disable_radio(struct rt2x00_dev *rt2x00dev) rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0); /* Wait for DMA, ignore error */ - rt2800usb_wait_wpdma_ready(rt2x00dev); + rt2800_wait_wpdma_ready(rt2x00dev); rt2x00usb_disable_radio(rt2x00dev); } @@ -573,41 +551,57 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry, { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - __le32 *rxd = (__le32 *)entry->skb->data; + __le32 *rxi = (__le32 *)entry->skb->data; __le32 *rxwi; - u32 rxd0; + __le32 *rxd; + u32 rxi0; u32 rxwi0; u32 rxwi1; u32 rxwi2; u32 rxwi3; + u32 rxd0; + int rx_pkt_len; + + /* + * RX frame format is : + * | RXINFO | RXWI | header | L2 pad | payload | pad | RXD | USB pad | + * |<------------ rx_pkt_len -------------->| + */ + rt2x00_desc_read(rxi, 0, &rxi0); + rx_pkt_len = rt2x00_get_field32(rxi0, RXINFO_W0_USB_DMA_RX_PKT_LEN); + + rxwi = (__le32 *)(entry->skb->data + RXINFO_DESC_SIZE); + + /* + * FIXME : we need to check for rx_pkt_len validity + */ + rxd = (__le32 *)(entry->skb->data + RXINFO_DESC_SIZE + rx_pkt_len); /* * Copy descriptor to the skbdesc->desc buffer, making it safe from * moving of frame data in rt2x00usb. */ - memcpy(skbdesc->desc, rxd, skbdesc->desc_len); - rxd = (__le32 *)skbdesc->desc; - rxwi = &rxd[RXINFO_DESC_SIZE / sizeof(__le32)]; + memcpy(skbdesc->desc, rxi, skbdesc->desc_len); /* * It is now safe to read the descriptor on all architectures. */ - rt2x00_desc_read(rxd, 0, &rxd0); rt2x00_desc_read(rxwi, 0, &rxwi0); rt2x00_desc_read(rxwi, 1, &rxwi1); rt2x00_desc_read(rxwi, 2, &rxwi2); rt2x00_desc_read(rxwi, 3, &rxwi3); + rt2x00_desc_read(rxd, 0, &rxd0); - if (rt2x00_get_field32(rxd0, RXINFO_W0_CRC_ERROR)) + if (rt2x00_get_field32(rxd0, RXD_W0_CRC_ERROR)) rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags)) { rxdesc->cipher = rt2x00_get_field32(rxwi0, RXWI_W0_UDF); rxdesc->cipher_status = - rt2x00_get_field32(rxd0, RXINFO_W0_CIPHER_ERROR); + rt2x00_get_field32(rxd0, RXD_W0_CIPHER_ERROR); } - if (rt2x00_get_field32(rxd0, RXINFO_W0_DECRYPTED)) { + if (rt2x00_get_field32(rxd0, RXD_W0_DECRYPTED)) { /* * Hardware has stripped IV/EIV data from 802.11 frame during * decryption. Unfortunately the descriptor doesn't contain @@ -622,13 +616,11 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry, rxdesc->flags |= RX_FLAG_MMIC_ERROR; } - if (rt2x00_get_field32(rxd0, RXINFO_W0_MY_BSS)) + if (rt2x00_get_field32(rxd0, RXD_W0_MY_BSS)) rxdesc->dev_flags |= RXDONE_MY_BSS; - if (rt2x00_get_field32(rxd0, RXINFO_W0_L2PAD)) { + if (rt2x00_get_field32(rxd0, RXD_W0_L2PAD)) rxdesc->dev_flags |= RXDONE_L2PAD; - skbdesc->flags |= SKBDESC_L2_PADDED; - } if (rt2x00_get_field32(rxwi1, RXWI_W1_SHORT_GI)) rxdesc->flags |= RX_FLAG_SHORT_GI; @@ -663,7 +655,6 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry, * Remove RXWI descriptor from start of buffer. */ skb_pull(entry->skb, skbdesc->desc_len); - skb_trim(entry->skb, rxdesc->size); } /* @@ -814,51 +805,27 @@ static struct usb_device_id rt2800usb_device_table[] = { /* Abocom */ { USB_DEVICE(0x07b8, 0x2870), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x07b8, 0x2770), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x07b8, 0x3070), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x07b8, 0x3071), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x07b8, 0x3072), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x1482, 0x3c09), USB_DEVICE_DATA(&rt2800usb_ops) }, - /* AirTies */ - { USB_DEVICE(0x1eda, 0x2310), USB_DEVICE_DATA(&rt2800usb_ops) }, - /* Amigo */ - { USB_DEVICE(0x0e0b, 0x9031), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x0e0b, 0x9041), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Amit */ { USB_DEVICE(0x15c5, 0x0008), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Askey */ { USB_DEVICE(0x1690, 0x0740), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x1690, 0x0744), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x0930, 0x0a07), USB_DEVICE_DATA(&rt2800usb_ops) }, /* ASUS */ { USB_DEVICE(0x0b05, 0x1731), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0b05, 0x1732), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0b05, 0x1742), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x0b05, 0x1760), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x0b05, 0x1761), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x0b05, 0x1784), USB_DEVICE_DATA(&rt2800usb_ops) }, /* AzureWave */ { USB_DEVICE(0x13d3, 0x3247), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x13d3, 0x3262), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x13d3, 0x3273), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x13d3, 0x3284), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x13d3, 0x3305), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Belkin */ { USB_DEVICE(0x050d, 0x8053), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x050d, 0x805c), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x050d, 0x815c), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x050d, 0x825a), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Buffalo */ { USB_DEVICE(0x0411, 0x00e8), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x0411, 0x012e), USB_DEVICE_DATA(&rt2800usb_ops) }, - /* Cisco */ - { USB_DEVICE(0x167b, 0x4001), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Conceptronic */ { USB_DEVICE(0x14b2, 0x3c06), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x14b2, 0x3c07), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x14b2, 0x3c08), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x14b2, 0x3c09), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x14b2, 0x3c11), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x14b2, 0x3c12), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x14b2, 0x3c23), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x14b2, 0x3c25), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x14b2, 0x3c27), USB_DEVICE_DATA(&rt2800usb_ops) }, @@ -867,157 +834,257 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x07aa, 0x002f), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x07aa, 0x003c), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x07aa, 0x003f), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x07aa, 0x0041), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x07aa, 0x0042), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x18c5, 0x0008), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x18c5, 0x0012), USB_DEVICE_DATA(&rt2800usb_ops) }, /* D-Link */ { USB_DEVICE(0x07d1, 0x3c09), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x07d1, 0x3c11), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Edimax */ + { USB_DEVICE(0x7392, 0x7717), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x7392, 0x7718), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* EnGenius */ + { USB_DEVICE(0X1740, 0x9701), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x1740, 0x9702), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Gigabyte */ + { USB_DEVICE(0x1044, 0x800b), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Hawking */ + { USB_DEVICE(0x0e66, 0x0001), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0e66, 0x0003), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Linksys */ + { USB_DEVICE(0x1737, 0x0070), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x1737, 0x0071), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Logitec */ + { USB_DEVICE(0x0789, 0x0162), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0789, 0x0163), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0789, 0x0164), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Motorola */ + { USB_DEVICE(0x100d, 0x9031), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* MSI */ + { USB_DEVICE(0x0db0, 0x6899), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Philips */ + { USB_DEVICE(0x0471, 0x200f), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Planex */ + { USB_DEVICE(0x2019, 0xed06), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Ralink */ + { USB_DEVICE(0x148f, 0x2770), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x148f, 0x2870), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Samsung */ + { USB_DEVICE(0x04e8, 0x2018), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Siemens */ + { USB_DEVICE(0x129b, 0x1828), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Sitecom */ + { USB_DEVICE(0x0df6, 0x0017), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0df6, 0x002b), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0df6, 0x002c), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0df6, 0x002d), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0df6, 0x0039), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0df6, 0x003f), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* SMC */ + { USB_DEVICE(0x083a, 0x6618), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x083a, 0x7512), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x083a, 0x7522), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x083a, 0x8522), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x083a, 0xa618), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x083a, 0xb522), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Sparklan */ + { USB_DEVICE(0x15a9, 0x0006), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Sweex */ + { USB_DEVICE(0x177f, 0x0302), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* U-Media*/ + { USB_DEVICE(0x157e, 0x300e), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* ZCOM */ + { USB_DEVICE(0x0cde, 0x0022), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0cde, 0x0025), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Zinwell */ + { USB_DEVICE(0x5a57, 0x0280), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x5a57, 0x0282), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Zyxel */ + { USB_DEVICE(0x0586, 0x3416), USB_DEVICE_DATA(&rt2800usb_ops) }, +#ifdef CONFIG_RT2800USB_RT30XX + /* Abocom */ + { USB_DEVICE(0x07b8, 0x3070), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x07b8, 0x3071), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x07b8, 0x3072), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* AirTies */ + { USB_DEVICE(0x1eda, 0x2310), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* AzureWave */ + { USB_DEVICE(0x13d3, 0x3273), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Conceptronic */ + { USB_DEVICE(0x14b2, 0x3c12), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Corega */ + { USB_DEVICE(0x18c5, 0x0012), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* D-Link */ { USB_DEVICE(0x07d1, 0x3c0a), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x07d1, 0x3c0b), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x07d1, 0x3c0d), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x07d1, 0x3c0e), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x07d1, 0x3c0f), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x07d1, 0x3c11), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x07d1, 0x3c13), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x07d1, 0x3c15), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Edimax */ { USB_DEVICE(0x7392, 0x7711), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x7392, 0x7717), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x7392, 0x7718), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Encore */ { USB_DEVICE(0x203d, 0x1480), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x203d, 0x14a1), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x203d, 0x14a9), USB_DEVICE_DATA(&rt2800usb_ops) }, /* EnGenius */ - { USB_DEVICE(0X1740, 0x9701), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x1740, 0x9702), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x1740, 0x9703), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x1740, 0x9705), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x1740, 0x9706), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Gigabyte */ + { USB_DEVICE(0x1044, 0x800d), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* I-O DATA */ + { USB_DEVICE(0x04bb, 0x0945), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* MSI */ + { USB_DEVICE(0x0db0, 0x3820), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Pegatron */ + { USB_DEVICE(0x1d4d, 0x000c), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x1d4d, 0x000e), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Planex */ + { USB_DEVICE(0x2019, 0xab25), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Quanta */ + { USB_DEVICE(0x1a32, 0x0304), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Ralink */ + { USB_DEVICE(0x148f, 0x2070), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x148f, 0x3070), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x148f, 0x3071), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x148f, 0x3072), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Sitecom */ + { USB_DEVICE(0x0df6, 0x003e), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0df6, 0x0042), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* SMC */ + { USB_DEVICE(0x083a, 0x7511), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Zinwell */ + { USB_DEVICE(0x5a57, 0x0283), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x5a57, 0x5257), USB_DEVICE_DATA(&rt2800usb_ops) }, +#endif +#ifdef CONFIG_RT2800USB_RT35XX + /* Askey */ + { USB_DEVICE(0x1690, 0x0744), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Cisco */ + { USB_DEVICE(0x167b, 0x4001), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* EnGenius */ + { USB_DEVICE(0x1740, 0x9801), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* I-O DATA */ + { USB_DEVICE(0x04bb, 0x0944), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Ralink */ + { USB_DEVICE(0x148f, 0x3370), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x148f, 0x3572), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x148f, 0x8070), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Sitecom */ + { USB_DEVICE(0x0df6, 0x0041), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Zinwell */ + { USB_DEVICE(0x5a57, 0x0284), USB_DEVICE_DATA(&rt2800usb_ops) }, +#endif +#ifdef CONFIG_RT2800USB_UNKNOWN + /* + * Unclear what kind of devices these are (they aren't supported by the + * vendor driver). + */ + /* Allwin */ + { USB_DEVICE(0x8516, 0x2070), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x8516, 0x2770), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x8516, 0x2870), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x8516, 0x3070), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x8516, 0x3071), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x8516, 0x3072), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x8516, 0x3572), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Amigo */ + { USB_DEVICE(0x0e0b, 0x9031), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0e0b, 0x9041), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Askey */ + { USB_DEVICE(0x0930, 0x0a07), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* ASUS */ + { USB_DEVICE(0x0b05, 0x1760), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0b05, 0x1761), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0b05, 0x1784), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0b05, 0x1790), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x1761, 0x0b05), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* AzureWave */ + { USB_DEVICE(0x13d3, 0x3262), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x13d3, 0x3284), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x13d3, 0x3305), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Belkin */ + { USB_DEVICE(0x050d, 0x825a), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Buffalo */ + { USB_DEVICE(0x0411, 0x012e), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0411, 0x0148), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0411, 0x0150), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0411, 0x015d), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Conceptronic */ + { USB_DEVICE(0x14b2, 0x3c08), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x14b2, 0x3c11), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Corega */ + { USB_DEVICE(0x07aa, 0x0041), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x07aa, 0x0042), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x18c5, 0x0008), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* D-Link */ + { USB_DEVICE(0x07d1, 0x3c0b), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x07d1, 0x3c13), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x07d1, 0x3c15), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x07d1, 0x3c16), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Encore */ + { USB_DEVICE(0x203d, 0x14a1), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x203d, 0x14a9), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* EnGenius */ { USB_DEVICE(0x1740, 0x9707), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x1740, 0x9708), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x1740, 0x9709), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x1740, 0x9801), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Gemtek */ { USB_DEVICE(0x15a9, 0x0010), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Gigabyte */ - { USB_DEVICE(0x1044, 0x800b), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x1044, 0x800c), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x1044, 0x800d), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Hawking */ - { USB_DEVICE(0x0e66, 0x0001), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x0e66, 0x0003), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0e66, 0x0009), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0e66, 0x000b), USB_DEVICE_DATA(&rt2800usb_ops) }, /* I-O DATA */ - { USB_DEVICE(0x04bb, 0x0944), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x04bb, 0x0945), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x04bb, 0x0947), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x04bb, 0x0948), USB_DEVICE_DATA(&rt2800usb_ops) }, /* LevelOne */ { USB_DEVICE(0x1740, 0x0605), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x1740, 0x0615), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Linksys */ - { USB_DEVICE(0x1737, 0x0070), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x1737, 0x0071), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x1737, 0x0077), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x1737, 0x0078), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x1737, 0x0079), USB_DEVICE_DATA(&rt2800usb_ops) }, - /* Logitec */ - { USB_DEVICE(0x0789, 0x0162), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x0789, 0x0163), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x0789, 0x0164), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Motorola */ - { USB_DEVICE(0x100d, 0x9031), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x100d, 0x9032), USB_DEVICE_DATA(&rt2800usb_ops) }, /* MSI */ - { USB_DEVICE(0x0db0, 0x3820), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0db0, 0x3821), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0db0, 0x3822), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0db0, 0x3870), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x0db0, 0x6899), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0db0, 0x3871), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0db0, 0x821a), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0db0, 0x822a), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0db0, 0x870a), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x0db0, 0x871a), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0db0, 0x899a), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Ovislink */ { USB_DEVICE(0x1b75, 0x3072), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Para */ { USB_DEVICE(0x20b8, 0x8888), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Pegatron */ + { USB_DEVICE(0x05a6, 0x0101), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x1d4d, 0x0002), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x1d4d, 0x000c), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x1d4d, 0x000e), USB_DEVICE_DATA(&rt2800usb_ops) }, - /* Philips */ - { USB_DEVICE(0x0471, 0x200f), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x1d4d, 0x0010), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Planex */ - { USB_DEVICE(0x2019, 0xed06), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x2019, 0xab24), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x2019, 0xab25), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Qcom */ { USB_DEVICE(0x18e8, 0x6259), USB_DEVICE_DATA(&rt2800usb_ops) }, - /* Quanta */ - { USB_DEVICE(0x1a32, 0x0304), USB_DEVICE_DATA(&rt2800usb_ops) }, - /* Ralink */ - { USB_DEVICE(0x148f, 0x2070), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x148f, 0x2770), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x148f, 0x2870), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x148f, 0x3070), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x148f, 0x3071), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x148f, 0x3072), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x148f, 0x3572), USB_DEVICE_DATA(&rt2800usb_ops) }, - /* Samsung */ - { USB_DEVICE(0x04e8, 0x2018), USB_DEVICE_DATA(&rt2800usb_ops) }, - /* Siemens */ - { USB_DEVICE(0x129b, 0x1828), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Sitecom */ - { USB_DEVICE(0x0df6, 0x0017), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x0df6, 0x002b), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x0df6, 0x002c), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x0df6, 0x002d), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x0df6, 0x0039), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0df6, 0x003b), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0df6, 0x003c), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0df6, 0x003d), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x0df6, 0x003e), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x0df6, 0x003f), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0df6, 0x0040), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x0df6, 0x0041), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x0df6, 0x0042), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0df6, 0x0047), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0df6, 0x0048), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0df6, 0x004a), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0df6, 0x004d), USB_DEVICE_DATA(&rt2800usb_ops) }, /* SMC */ - { USB_DEVICE(0x083a, 0x6618), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x083a, 0x7511), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x083a, 0x7512), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x083a, 0x7522), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x083a, 0x8522), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x083a, 0xa512), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x083a, 0xa618), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x083a, 0xa701), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x083a, 0xa702), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x083a, 0xb522), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x083a, 0xc522), USB_DEVICE_DATA(&rt2800usb_ops) }, - /* Sparklan */ - { USB_DEVICE(0x15a9, 0x0006), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x083a, 0xd522), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Sweex */ { USB_DEVICE(0x177f, 0x0153), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x177f, 0x0302), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x177f, 0x0313), USB_DEVICE_DATA(&rt2800usb_ops) }, - /* U-Media*/ - { USB_DEVICE(0x157e, 0x300e), USB_DEVICE_DATA(&rt2800usb_ops) }, - /* ZCOM */ - { USB_DEVICE(0x0cde, 0x0022), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x0cde, 0x0025), USB_DEVICE_DATA(&rt2800usb_ops) }, - /* Zinwell */ - { USB_DEVICE(0x5a57, 0x0280), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x5a57, 0x0282), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x5a57, 0x0283), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x5a57, 0x0284), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x5a57, 0x5257), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Zyxel */ - { USB_DEVICE(0x0586, 0x3416), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0586, 0x341a), USB_DEVICE_DATA(&rt2800usb_ops) }, +#endif { 0, } }; diff --git a/drivers/net/wireless/rt2x00/rt2800usb.h b/drivers/net/wireless/rt2x00/rt2800usb.h index 1e4340a182ef..d1d8ae94b4d4 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.h +++ b/drivers/net/wireless/rt2x00/rt2800usb.h @@ -79,6 +79,8 @@ */ #define TXINFO_DESC_SIZE ( 1 * sizeof(__le32) ) #define RXINFO_DESC_SIZE ( 1 * sizeof(__le32) ) +#define RXWI_DESC_SIZE ( 4 * sizeof(__le32) ) +#define RXD_DESC_SIZE ( 1 * sizeof(__le32) ) /* * TX Info structure @@ -101,6 +103,54 @@ #define TXINFO_W0_USB_DMA_TX_BURST FIELD32(0x80000000) /* + * RX Info structure + */ + +/* + * Word 0 + */ + +#define RXINFO_W0_USB_DMA_RX_PKT_LEN FIELD32(0x0000ffff) + +/* + * RX WI structure + */ + +/* + * Word0 + */ +#define RXWI_W0_WIRELESS_CLI_ID FIELD32(0x000000ff) +#define RXWI_W0_KEY_INDEX FIELD32(0x00000300) +#define RXWI_W0_BSSID FIELD32(0x00001c00) +#define RXWI_W0_UDF FIELD32(0x0000e000) +#define RXWI_W0_MPDU_TOTAL_BYTE_COUNT FIELD32(0x0fff0000) +#define RXWI_W0_TID FIELD32(0xf0000000) + +/* + * Word1 + */ +#define RXWI_W1_FRAG FIELD32(0x0000000f) +#define RXWI_W1_SEQUENCE FIELD32(0x0000fff0) +#define RXWI_W1_MCS FIELD32(0x007f0000) +#define RXWI_W1_BW FIELD32(0x00800000) +#define RXWI_W1_SHORT_GI FIELD32(0x01000000) +#define RXWI_W1_STBC FIELD32(0x06000000) +#define RXWI_W1_PHYMODE FIELD32(0xc0000000) + +/* + * Word2 + */ +#define RXWI_W2_RSSI0 FIELD32(0x000000ff) +#define RXWI_W2_RSSI1 FIELD32(0x0000ff00) +#define RXWI_W2_RSSI2 FIELD32(0x00ff0000) + +/* + * Word3 + */ +#define RXWI_W3_SNR0 FIELD32(0x000000ff) +#define RXWI_W3_SNR1 FIELD32(0x0000ff00) + +/* * RX descriptor format for RX Ring. */ @@ -115,25 +165,25 @@ * AMSDU: rx with 802.3 header, not 802.11 header. */ -#define RXINFO_W0_BA FIELD32(0x00000001) -#define RXINFO_W0_DATA FIELD32(0x00000002) -#define RXINFO_W0_NULLDATA FIELD32(0x00000004) -#define RXINFO_W0_FRAG FIELD32(0x00000008) -#define RXINFO_W0_UNICAST_TO_ME FIELD32(0x00000010) -#define RXINFO_W0_MULTICAST FIELD32(0x00000020) -#define RXINFO_W0_BROADCAST FIELD32(0x00000040) -#define RXINFO_W0_MY_BSS FIELD32(0x00000080) -#define RXINFO_W0_CRC_ERROR FIELD32(0x00000100) -#define RXINFO_W0_CIPHER_ERROR FIELD32(0x00000600) -#define RXINFO_W0_AMSDU FIELD32(0x00000800) -#define RXINFO_W0_HTC FIELD32(0x00001000) -#define RXINFO_W0_RSSI FIELD32(0x00002000) -#define RXINFO_W0_L2PAD FIELD32(0x00004000) -#define RXINFO_W0_AMPDU FIELD32(0x00008000) -#define RXINFO_W0_DECRYPTED FIELD32(0x00010000) -#define RXINFO_W0_PLCP_RSSI FIELD32(0x00020000) -#define RXINFO_W0_CIPHER_ALG FIELD32(0x00040000) -#define RXINFO_W0_LAST_AMSDU FIELD32(0x00080000) -#define RXINFO_W0_PLCP_SIGNAL FIELD32(0xfff00000) +#define RXD_W0_BA FIELD32(0x00000001) +#define RXD_W0_DATA FIELD32(0x00000002) +#define RXD_W0_NULLDATA FIELD32(0x00000004) +#define RXD_W0_FRAG FIELD32(0x00000008) +#define RXD_W0_UNICAST_TO_ME FIELD32(0x00000010) +#define RXD_W0_MULTICAST FIELD32(0x00000020) +#define RXD_W0_BROADCAST FIELD32(0x00000040) +#define RXD_W0_MY_BSS FIELD32(0x00000080) +#define RXD_W0_CRC_ERROR FIELD32(0x00000100) +#define RXD_W0_CIPHER_ERROR FIELD32(0x00000600) +#define RXD_W0_AMSDU FIELD32(0x00000800) +#define RXD_W0_HTC FIELD32(0x00001000) +#define RXD_W0_RSSI FIELD32(0x00002000) +#define RXD_W0_L2PAD FIELD32(0x00004000) +#define RXD_W0_AMPDU FIELD32(0x00008000) +#define RXD_W0_DECRYPTED FIELD32(0x00010000) +#define RXD_W0_PLCP_RSSI FIELD32(0x00020000) +#define RXD_W0_CIPHER_ALG FIELD32(0x00040000) +#define RXD_W0_LAST_AMSDU FIELD32(0x00080000) +#define RXD_W0_PLCP_SIGNAL FIELD32(0xfff00000) #endif /* RT2800USB_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index dcfc8c25d1a7..d9daa9c406fa 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -104,6 +104,12 @@ #define GET_DURATION_RES(__size, __rate)(((__size) * 8 * 10) % (__rate)) /* + * Determine the number of L2 padding bytes required between the header and + * the payload. + */ +#define L2PAD_SIZE(__hdrlen) (-(__hdrlen) & 3) + +/* * Determine the alignment requirement, * to make sure the 802.11 payload is padded to a 4-byte boundrary * we must determine the address of the payload and calculate the @@ -154,6 +160,7 @@ struct avg_val { enum rt2x00_chip_intf { RT2X00_CHIP_INTF_PCI, RT2X00_CHIP_INTF_USB, + RT2X00_CHIP_INTF_SOC, }; /* @@ -163,25 +170,26 @@ enum rt2x00_chip_intf { */ struct rt2x00_chip { u16 rt; -#define RT2460 0x0101 -#define RT2560 0x0201 -#define RT2570 0x1201 -#define RT2561s 0x0301 /* Turbo */ -#define RT2561 0x0302 -#define RT2661 0x0401 -#define RT2571 0x1300 -#define RT2860 0x0601 /* 2.4GHz PCI/CB */ -#define RT2860D 0x0681 /* 2.4GHz, 5GHz PCI/CB */ -#define RT2890 0x0701 /* 2.4GHz PCIe */ -#define RT2890D 0x0781 /* 2.4GHz, 5GHz PCIe */ +#define RT2460 0x2460 +#define RT2560 0x2560 +#define RT2570 0x2570 +#define RT2661 0x2661 +#define RT2573 0x2573 +#define RT2860 0x2860 /* 2.4GHz PCI/CB */ +#define RT2870 0x2870 +#define RT2872 0x2872 #define RT2880 0x2880 /* WSOC */ +#define RT2883 0x2883 /* WSOC */ +#define RT2890 0x2890 /* 2.4GHz PCIe */ #define RT3052 0x3052 /* WSOC */ +#define RT3070 0x3070 +#define RT3071 0x3071 #define RT3090 0x3090 /* 2.4GHz PCIe */ -#define RT2870 0x1600 -#define RT3070 0x1800 +#define RT3390 0x3390 +#define RT3572 0x3572 u16 rf; - u32 rev; + u16 rev; enum rt2x00_chip_intf intf; }; @@ -911,51 +919,30 @@ static inline void rt2x00_eeprom_write(struct rt2x00_dev *rt2x00dev, * Chipset handlers */ static inline void rt2x00_set_chip(struct rt2x00_dev *rt2x00dev, - const u16 rt, const u16 rf, const u32 rev) + const u16 rt, const u16 rf, const u16 rev) { rt2x00dev->chip.rt = rt; rt2x00dev->chip.rf = rf; rt2x00dev->chip.rev = rev; -} - -static inline void rt2x00_set_chip_rt(struct rt2x00_dev *rt2x00dev, - const u16 rt) -{ - rt2x00dev->chip.rt = rt; -} - -static inline void rt2x00_set_chip_rf(struct rt2x00_dev *rt2x00dev, - const u16 rf, const u32 rev) -{ - rt2x00_set_chip(rt2x00dev, rt2x00dev->chip.rt, rf, rev); -} -static inline void rt2x00_print_chip(struct rt2x00_dev *rt2x00dev) -{ INFO(rt2x00dev, - "Chipset detected - rt: %04x, rf: %04x, rev: %08x.\n", + "Chipset detected - rt: %04x, rf: %04x, rev: %04x.\n", rt2x00dev->chip.rt, rt2x00dev->chip.rf, rt2x00dev->chip.rev); } -static inline char rt2x00_rt(const struct rt2x00_chip *chipset, const u16 chip) +static inline char rt2x00_rt(struct rt2x00_dev *rt2x00dev, const u16 rt) { - return (chipset->rt == chip); + return (rt2x00dev->chip.rt == rt); } -static inline char rt2x00_rf(const struct rt2x00_chip *chipset, const u16 chip) +static inline char rt2x00_rf(struct rt2x00_dev *rt2x00dev, const u16 rf) { - return (chipset->rf == chip); + return (rt2x00dev->chip.rf == rf); } -static inline u32 rt2x00_rev(const struct rt2x00_chip *chipset) +static inline u16 rt2x00_rev(struct rt2x00_dev *rt2x00dev) { - return chipset->rev; -} - -static inline bool rt2x00_check_rev(const struct rt2x00_chip *chipset, - const u32 mask, const u32 rev) -{ - return ((chipset->rev & mask) == rev); + return rt2x00dev->chip.rev; } static inline void rt2x00_set_chip_intf(struct rt2x00_dev *rt2x00dev, @@ -964,20 +951,25 @@ static inline void rt2x00_set_chip_intf(struct rt2x00_dev *rt2x00dev, rt2x00dev->chip.intf = intf; } -static inline bool rt2x00_intf(const struct rt2x00_chip *chipset, +static inline bool rt2x00_intf(struct rt2x00_dev *rt2x00dev, enum rt2x00_chip_intf intf) { - return (chipset->intf == intf); + return (rt2x00dev->chip.intf == intf); +} + +static inline bool rt2x00_is_pci(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI); } -static inline bool rt2x00_intf_is_pci(struct rt2x00_dev *rt2x00dev) +static inline bool rt2x00_is_usb(struct rt2x00_dev *rt2x00dev) { - return rt2x00_intf(&rt2x00dev->chip, RT2X00_CHIP_INTF_PCI); + return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_USB); } -static inline bool rt2x00_intf_is_usb(struct rt2x00_dev *rt2x00dev) +static inline bool rt2x00_is_soc(struct rt2x00_dev *rt2x00dev) { - return rt2x00_intf(&rt2x00dev->chip, RT2X00_CHIP_INTF_USB); + return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_SOC); } /** @@ -1019,9 +1011,9 @@ int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb); int rt2x00mac_start(struct ieee80211_hw *hw); void rt2x00mac_stop(struct ieee80211_hw *hw); int rt2x00mac_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf); + struct ieee80211_vif *vif); void rt2x00mac_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf); + struct ieee80211_vif *vif); int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed); void rt2x00mac_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, @@ -1038,8 +1030,6 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, #endif /* CONFIG_RT2X00_LIB_CRYPTO */ int rt2x00mac_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats); -int rt2x00mac_get_tx_stats(struct ieee80211_hw *hw, - struct ieee80211_tx_queue_stats *stats); void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/rt2x00/rt2x00debug.c index 7d323a763b54..70c04c282efc 100644 --- a/drivers/net/wireless/rt2x00/rt2x00debug.c +++ b/drivers/net/wireless/rt2x00/rt2x00debug.c @@ -184,7 +184,7 @@ void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev, dump_hdr->data_length = cpu_to_le32(skb->len); dump_hdr->chip_rt = cpu_to_le16(rt2x00dev->chip.rt); dump_hdr->chip_rf = cpu_to_le16(rt2x00dev->chip.rf); - dump_hdr->chip_rev = cpu_to_le32(rt2x00dev->chip.rev); + dump_hdr->chip_rev = cpu_to_le16(rt2x00dev->chip.rev); dump_hdr->type = cpu_to_le16(type); dump_hdr->queue_index = desc->entry->queue->qid; dump_hdr->entry_index = desc->entry->entry_idx; @@ -573,7 +573,7 @@ static struct dentry *rt2x00debug_create_file_chipset(const char *name, blob->data = data; data += sprintf(data, "rt chip:\t%04x\n", intf->rt2x00dev->chip.rt); data += sprintf(data, "rf chip:\t%04x\n", intf->rt2x00dev->chip.rf); - data += sprintf(data, "revision:\t%08x\n", intf->rt2x00dev->chip.rev); + data += sprintf(data, "revision:\t%04x\n", intf->rt2x00dev->chip.rev); data += sprintf(data, "\n"); data += sprintf(data, "register\tbase\twords\twordsize\n"); data += sprintf(data, "csr\t%d\t%d\t%d\n", diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 265e66dba552..b93731b79903 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -385,9 +385,6 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev, memset(&rxdesc, 0, sizeof(rxdesc)); rt2x00dev->ops->lib->fill_rxdone(entry, &rxdesc); - /* Trim buffer to correct size */ - skb_trim(entry->skb, rxdesc.size); - /* * The data behind the ieee80211 header must be * aligned on a 4 byte boundary. @@ -404,11 +401,16 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev, (rxdesc.flags & RX_FLAG_IV_STRIPPED)) rt2x00crypto_rx_insert_iv(entry->skb, header_length, &rxdesc); - else if (rxdesc.dev_flags & RXDONE_L2PAD) + else if (header_length && + (rxdesc.size > header_length) && + (rxdesc.dev_flags & RXDONE_L2PAD)) rt2x00queue_remove_l2pad(entry->skb, header_length); else rt2x00queue_align_payload(entry->skb, header_length); + /* Trim buffer to correct size */ + skb_trim(entry->skb, rxdesc.size); + /* * Check if the frame was received using HT. In that case, * the rate is the MCS index and should be passed to mac80211 diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index de549c244ed8..abbd857ec759 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -187,10 +187,10 @@ void rt2x00mac_stop(struct ieee80211_hw *hw) EXPORT_SYMBOL_GPL(rt2x00mac_stop); int rt2x00mac_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; - struct rt2x00_intf *intf = vif_to_intf(conf->vif); + struct rt2x00_intf *intf = vif_to_intf(vif); struct data_queue *queue = rt2x00queue_get_queue(rt2x00dev, QID_BEACON); struct queue_entry *entry = NULL; unsigned int i; @@ -203,7 +203,7 @@ int rt2x00mac_add_interface(struct ieee80211_hw *hw, !test_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags)) return -ENODEV; - switch (conf->type) { + switch (vif->type) { case NL80211_IFTYPE_AP: /* * We don't support mixed combinations of @@ -263,7 +263,7 @@ int rt2x00mac_add_interface(struct ieee80211_hw *hw, * increase interface count and start initialization. */ - if (conf->type == NL80211_IFTYPE_AP) + if (vif->type == NL80211_IFTYPE_AP) rt2x00dev->intf_ap_count++; else rt2x00dev->intf_sta_count++; @@ -273,16 +273,16 @@ int rt2x00mac_add_interface(struct ieee80211_hw *hw, mutex_init(&intf->beacon_skb_mutex); intf->beacon = entry; - if (conf->type == NL80211_IFTYPE_AP) - memcpy(&intf->bssid, conf->mac_addr, ETH_ALEN); - memcpy(&intf->mac, conf->mac_addr, ETH_ALEN); + if (vif->type == NL80211_IFTYPE_AP) + memcpy(&intf->bssid, vif->addr, ETH_ALEN); + memcpy(&intf->mac, vif->addr, ETH_ALEN); /* * The MAC adddress must be configured after the device * has been initialized. Otherwise the device can reset * the MAC registers. */ - rt2x00lib_config_intf(rt2x00dev, intf, conf->type, intf->mac, NULL); + rt2x00lib_config_intf(rt2x00dev, intf, vif->type, intf->mac, NULL); /* * Some filters depend on the current working mode. We can force @@ -296,10 +296,10 @@ int rt2x00mac_add_interface(struct ieee80211_hw *hw, EXPORT_SYMBOL_GPL(rt2x00mac_add_interface); void rt2x00mac_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; - struct rt2x00_intf *intf = vif_to_intf(conf->vif); + struct rt2x00_intf *intf = vif_to_intf(vif); /* * Don't allow interfaces to be remove while @@ -307,11 +307,11 @@ void rt2x00mac_remove_interface(struct ieee80211_hw *hw, * no interface is present. */ if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) || - (conf->type == NL80211_IFTYPE_AP && !rt2x00dev->intf_ap_count) || - (conf->type != NL80211_IFTYPE_AP && !rt2x00dev->intf_sta_count)) + (vif->type == NL80211_IFTYPE_AP && !rt2x00dev->intf_ap_count) || + (vif->type != NL80211_IFTYPE_AP && !rt2x00dev->intf_sta_count)) return; - if (conf->type == NL80211_IFTYPE_AP) + if (vif->type == NL80211_IFTYPE_AP) rt2x00dev->intf_ap_count--; else rt2x00dev->intf_sta_count--; @@ -555,22 +555,6 @@ int rt2x00mac_get_stats(struct ieee80211_hw *hw, } EXPORT_SYMBOL_GPL(rt2x00mac_get_stats); -int rt2x00mac_get_tx_stats(struct ieee80211_hw *hw, - struct ieee80211_tx_queue_stats *stats) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - unsigned int i; - - for (i = 0; i < rt2x00dev->ops->tx_queues; i++) { - stats[i].len = rt2x00dev->tx[i].length; - stats[i].limit = rt2x00dev->tx[i].limit; - stats[i].count = rt2x00dev->tx[i].count; - } - - return 0; -} -EXPORT_SYMBOL_GPL(rt2x00mac_get_tx_stats); - void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.c b/drivers/net/wireless/rt2x00/rt2x00pci.c index 0feb4d0e4668..047123b766fc 100644 --- a/drivers/net/wireless/rt2x00/rt2x00pci.c +++ b/drivers/net/wireless/rt2x00/rt2x00pci.c @@ -41,6 +41,9 @@ int rt2x00pci_regbusy_read(struct rt2x00_dev *rt2x00dev, { unsigned int i; + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return 0; + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt2x00pci_register_read(rt2x00dev, offset, reg); if (!rt2x00_get_field32(*reg, field)) @@ -269,7 +272,6 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) struct ieee80211_hw *hw; struct rt2x00_dev *rt2x00dev; int retval; - u16 chip; retval = pci_request_regions(pci_dev, pci_name(pci_dev)); if (retval) { @@ -312,12 +314,6 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI); - /* - * Determine RT chipset by reading PCI header. - */ - pci_read_config_word(pci_dev, PCI_DEVICE_ID, &chip); - rt2x00_set_chip_rt(rt2x00dev, chip); - retval = rt2x00pci_alloc_reg(rt2x00dev); if (retval) goto exit_free_device; diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.h b/drivers/net/wireless/rt2x00/rt2x00pci.h index d4f9449ab0a4..8149ff68410a 100644 --- a/drivers/net/wireless/rt2x00/rt2x00pci.h +++ b/drivers/net/wireless/rt2x00/rt2x00pci.h @@ -27,6 +27,7 @@ #define RT2X00PCI_H #include <linux/io.h> +#include <linux/pci.h> /* * This variable should be used with the diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 9915a09141ef..0b4801a14601 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -177,55 +177,45 @@ void rt2x00queue_align_payload(struct sk_buff *skb, unsigned int header_length) void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int header_length) { - struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); - unsigned int frame_length = skb->len; + unsigned int payload_length = skb->len - header_length; unsigned int header_align = ALIGN_SIZE(skb, 0); unsigned int payload_align = ALIGN_SIZE(skb, header_length); - unsigned int l2pad = 4 - (payload_align - header_align); + unsigned int l2pad = payload_length ? L2PAD_SIZE(header_length) : 0; - if (header_align == payload_align) { - /* - * Both header and payload must be moved the same - * amount of bytes to align them properly. This means - * we don't use the L2 padding but just move the entire - * frame. - */ - rt2x00queue_align_frame(skb); - } else if (!payload_align) { - /* - * Simple L2 padding, only the header needs to be moved, - * the payload is already properly aligned. - */ - skb_push(skb, header_align); - memmove(skb->data, skb->data + header_align, frame_length); - skbdesc->flags |= SKBDESC_L2_PADDED; - } else { - /* - * - * Complicated L2 padding, both header and payload need - * to be moved. By default we only move to the start - * of the buffer, so our header alignment needs to be - * increased if there is not enough room for the header - * to be moved. - */ - if (payload_align > header_align) - header_align += 4; + /* + * Adjust the header alignment if the payload needs to be moved more + * than the header. + */ + if (payload_align > header_align) + header_align += 4; + + /* There is nothing to do if no alignment is needed */ + if (!header_align) + return; + + /* Reserve the amount of space needed in front of the frame */ + skb_push(skb, header_align); + + /* + * Move the header. + */ + memmove(skb->data, skb->data + header_align, header_length); - skb_push(skb, header_align); - memmove(skb->data, skb->data + header_align, header_length); + /* Move the payload, if present and if required */ + if (payload_length && payload_align) memmove(skb->data + header_length + l2pad, skb->data + header_length + l2pad + payload_align, - frame_length - header_length); - skbdesc->flags |= SKBDESC_L2_PADDED; - } + payload_length); + + /* Trim the skb to the correct size */ + skb_trim(skb, header_length + l2pad + payload_length); } void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length) { - struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); - unsigned int l2pad = 4 - (header_length & 3); + unsigned int l2pad = L2PAD_SIZE(header_length); - if (!l2pad || (skbdesc->flags & SKBDESC_L2_PADDED)) + if (!l2pad) return; memmove(skb->data + l2pad, skb->data, header_length); @@ -346,7 +336,9 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry, * Header and alignment information. */ txdesc->header_length = ieee80211_get_hdrlen_from_skb(entry->skb); - txdesc->l2pad = ALIGN_SIZE(entry->skb, txdesc->header_length); + if (test_bit(DRIVER_REQUIRE_L2PAD, &rt2x00dev->flags) && + (entry->skb->len > txdesc->header_length)) + txdesc->l2pad = L2PAD_SIZE(txdesc->header_length); /* * Check whether this frame is to be acked. @@ -387,10 +379,13 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry, /* * Beacons and probe responses require the tsf timestamp - * to be inserted into the frame. + * to be inserted into the frame, except for a frame that has been injected + * through a monitor interface. This latter is needed for testing a + * monitor interface. */ - if (ieee80211_is_beacon(hdr->frame_control) || - ieee80211_is_probe_resp(hdr->frame_control)) + if ((ieee80211_is_beacon(hdr->frame_control) || + ieee80211_is_probe_resp(hdr->frame_control)) && + (!(tx_info->flags & IEEE80211_TX_CTL_INJECTED))) __set_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags); /* diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h index 70775e5ba1ac..c1e482bb37b3 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.h +++ b/drivers/net/wireless/rt2x00/rt2x00queue.h @@ -92,8 +92,6 @@ enum data_queue_qid { * @SKBDESC_DMA_MAPPED_TX: &skb_dma field has been mapped for TX * @SKBDESC_IV_STRIPPED: Frame contained a IV/EIV provided by * mac80211 but was stripped for processing by the driver. - * @SKBDESC_L2_PADDED: Payload has been padded for 4-byte alignment, - * the padded bytes are located between header and payload. * @SKBDESC_NOT_MAC80211: Frame didn't originate from mac80211, * don't try to pass it back. */ @@ -101,8 +99,7 @@ enum skb_frame_desc_flags { SKBDESC_DMA_MAPPED_RX = 1 << 0, SKBDESC_DMA_MAPPED_TX = 1 << 1, SKBDESC_IV_STRIPPED = 1 << 2, - SKBDESC_L2_PADDED = 1 << 3, - SKBDESC_NOT_MAC80211 = 1 << 4, + SKBDESC_NOT_MAC80211 = 1 << 3, }; /** diff --git a/drivers/net/wireless/rt2x00/rt2x00soc.c b/drivers/net/wireless/rt2x00/rt2x00soc.c index 19e684f8ffa1..4efdc96010f6 100644 --- a/drivers/net/wireless/rt2x00/rt2x00soc.c +++ b/drivers/net/wireless/rt2x00/rt2x00soc.c @@ -71,9 +71,7 @@ exit: return -ENOMEM; } -int rt2x00soc_probe(struct platform_device *pdev, - const unsigned short chipset, - const struct rt2x00_ops *ops) +int rt2x00soc_probe(struct platform_device *pdev, const struct rt2x00_ops *ops) { struct ieee80211_hw *hw; struct rt2x00_dev *rt2x00dev; @@ -94,12 +92,7 @@ int rt2x00soc_probe(struct platform_device *pdev, rt2x00dev->irq = platform_get_irq(pdev, 0); rt2x00dev->name = pdev->dev.driver->name; - /* - * SoC devices mimic PCI behavior. - */ - rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI); - - rt2x00_set_chip_rt(rt2x00dev, chipset); + rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_SOC); retval = rt2x00soc_alloc_reg(rt2x00dev); if (retval) diff --git a/drivers/net/wireless/rt2x00/rt2x00soc.h b/drivers/net/wireless/rt2x00/rt2x00soc.h index 8a3416624af5..4739edfe2f00 100644 --- a/drivers/net/wireless/rt2x00/rt2x00soc.h +++ b/drivers/net/wireless/rt2x00/rt2x00soc.h @@ -28,18 +28,10 @@ #define KSEG1ADDR(__ptr) __ptr -#define __rt2x00soc_probe(__chipset, __ops) \ -static int __rt2x00soc_probe(struct platform_device *pdev) \ -{ \ - return rt2x00soc_probe(pdev, (__chipset), (__ops)); \ -} - /* * SoC driver handlers. */ -int rt2x00soc_probe(struct platform_device *pdev, - const unsigned short chipset, - const struct rt2x00_ops *ops); +int rt2x00soc_probe(struct platform_device *pdev, const struct rt2x00_ops *ops); int rt2x00soc_remove(struct platform_device *pdev); #ifdef CONFIG_PM int rt2x00soc_suspend(struct platform_device *pdev, pm_message_t state); diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index 0ca589306d71..ee9c696fe1d4 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -637,8 +637,7 @@ static void rt61pci_config_antenna_5x(struct rt2x00_dev *rt2x00dev, rt61pci_bbp_read(rt2x00dev, 4, &r4); rt61pci_bbp_read(rt2x00dev, 77, &r77); - rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, - rt2x00_rf(&rt2x00dev->chip, RF5325)); + rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, rt2x00_rf(rt2x00dev, RF5325)); /* * Configure the RX antenna. @@ -684,8 +683,7 @@ static void rt61pci_config_antenna_2x(struct rt2x00_dev *rt2x00dev, rt61pci_bbp_read(rt2x00dev, 4, &r4); rt61pci_bbp_read(rt2x00dev, 77, &r77); - rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, - rt2x00_rf(&rt2x00dev->chip, RF2529)); + rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, rt2x00_rf(rt2x00dev, RF2529)); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, !test_bit(CONFIG_FRAME_TYPE, &rt2x00dev->flags)); @@ -833,12 +831,11 @@ static void rt61pci_config_ant(struct rt2x00_dev *rt2x00dev, rt2x00pci_register_write(rt2x00dev, PHY_CSR0, reg); - if (rt2x00_rf(&rt2x00dev->chip, RF5225) || - rt2x00_rf(&rt2x00dev->chip, RF5325)) + if (rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF5325)) rt61pci_config_antenna_5x(rt2x00dev, ant); - else if (rt2x00_rf(&rt2x00dev->chip, RF2527)) + else if (rt2x00_rf(rt2x00dev, RF2527)) rt61pci_config_antenna_2x(rt2x00dev, ant); - else if (rt2x00_rf(&rt2x00dev->chip, RF2529)) { + else if (rt2x00_rf(rt2x00dev, RF2529)) { if (test_bit(CONFIG_DOUBLE_ANTENNA, &rt2x00dev->flags)) rt61pci_config_antenna_2x(rt2x00dev, ant); else @@ -879,8 +876,7 @@ static void rt61pci_config_channel(struct rt2x00_dev *rt2x00dev, rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset); - smart = !(rt2x00_rf(&rt2x00dev->chip, RF5225) || - rt2x00_rf(&rt2x00dev->chip, RF2527)); + smart = !(rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF2527)); rt61pci_bbp_read(rt2x00dev, 3, &r3); rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, smart); @@ -1135,16 +1131,18 @@ dynamic_cca_tune: */ static char *rt61pci_get_firmware_name(struct rt2x00_dev *rt2x00dev) { + u16 chip; char *fw_name; - switch (rt2x00dev->chip.rt) { - case RT2561: + pci_read_config_word(to_pci_dev(rt2x00dev->dev), PCI_DEVICE_ID, &chip); + switch (chip) { + case RT2561_PCI_ID: fw_name = FIRMWARE_RT2561; break; - case RT2561s: + case RT2561s_PCI_ID: fw_name = FIRMWARE_RT2561s; break; - case RT2661: + case RT2661_PCI_ID: fw_name = FIRMWARE_RT2661; break; default: @@ -2299,13 +2297,13 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev) */ value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); rt2x00pci_register_read(rt2x00dev, MAC_CSR0, ®); - rt2x00_set_chip_rf(rt2x00dev, value, reg); - rt2x00_print_chip(rt2x00dev); + rt2x00_set_chip(rt2x00dev, rt2x00_get_field32(reg, MAC_CSR0_CHIPSET), + value, rt2x00_get_field32(reg, MAC_CSR0_REVISION)); - if (!rt2x00_rf(&rt2x00dev->chip, RF5225) && - !rt2x00_rf(&rt2x00dev->chip, RF5325) && - !rt2x00_rf(&rt2x00dev->chip, RF2527) && - !rt2x00_rf(&rt2x00dev->chip, RF2529)) { + if (!rt2x00_rf(rt2x00dev, RF5225) && + !rt2x00_rf(rt2x00dev, RF5325) && + !rt2x00_rf(rt2x00dev, RF2527) && + !rt2x00_rf(rt2x00dev, RF2529)) { ERROR(rt2x00dev, "Invalid RF chipset detected.\n"); return -ENODEV; } @@ -2360,7 +2358,7 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev) * the antenna settings should be gathered from the NIC * eeprom word. */ - if (rt2x00_rf(&rt2x00dev->chip, RF2529) && + if (rt2x00_rf(rt2x00dev, RF2529) && !test_bit(CONFIG_DOUBLE_ANTENNA, &rt2x00dev->flags)) { rt2x00dev->default_ant.rx = ANTENNA_A + rt2x00_get_field16(eeprom, EEPROM_NIC_RX_FIXED); @@ -2571,8 +2569,7 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) spec->channels = rf_vals_seq; } - if (rt2x00_rf(&rt2x00dev->chip, RF5225) || - rt2x00_rf(&rt2x00dev->chip, RF5325)) { + if (rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF5325)) { spec->supported_bands |= SUPPORT_BAND_5GHZ; spec->num_channels = ARRAY_SIZE(rf_vals_seq); } @@ -2735,7 +2732,6 @@ static const struct ieee80211_ops rt61pci_mac80211_ops = { .get_stats = rt2x00mac_get_stats, .bss_info_changed = rt2x00mac_bss_info_changed, .conf_tx = rt61pci_conf_tx, - .get_tx_stats = rt2x00mac_get_tx_stats, .get_tsf = rt61pci_get_tsf, .rfkill_poll = rt2x00mac_rfkill_poll, }; diff --git a/drivers/net/wireless/rt2x00/rt61pci.h b/drivers/net/wireless/rt2x00/rt61pci.h index 6f33f7f5668c..ab20c7758824 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.h +++ b/drivers/net/wireless/rt2x00/rt61pci.h @@ -28,6 +28,13 @@ #define RT61PCI_H /* + * RT chip PCI IDs. + */ +#define RT2561s_PCI_ID 0x0301 +#define RT2561_PCI_ID 0x0302 +#define RT2661_PCI_ID 0x0401 + +/* * RF chip defines. */ #define RF5225 0x0001 @@ -225,6 +232,8 @@ struct hw_pairwise_ta_entry { * MAC_CSR0: ASIC revision number. */ #define MAC_CSR0 0x3000 +#define MAC_CSR0_REVISION FIELD32(0x0000000f) +#define MAC_CSR0_CHIPSET FIELD32(0x000ffff0) /* * MAC_CSR1: System control register. diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index ced3b6ab5e16..f39a8ed17841 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -136,8 +136,8 @@ static void rt73usb_rf_write(struct rt2x00_dev *rt2x00dev, * all others contain 20 bits. */ rt2x00_set_field32(®, PHY_CSR4_NUMBER_OF_BITS, - 20 + (rt2x00_rf(&rt2x00dev->chip, RF5225) || - rt2x00_rf(&rt2x00dev->chip, RF2527))); + 20 + (rt2x00_rf(rt2x00dev, RF5225) || + rt2x00_rf(rt2x00dev, RF2527))); rt2x00_set_field32(®, PHY_CSR4_IF_SELECT, 0); rt2x00_set_field32(®, PHY_CSR4_BUSY, 1); @@ -741,11 +741,9 @@ static void rt73usb_config_ant(struct rt2x00_dev *rt2x00dev, rt2x00usb_register_write(rt2x00dev, PHY_CSR0, reg); - if (rt2x00_rf(&rt2x00dev->chip, RF5226) || - rt2x00_rf(&rt2x00dev->chip, RF5225)) + if (rt2x00_rf(rt2x00dev, RF5226) || rt2x00_rf(rt2x00dev, RF5225)) rt73usb_config_antenna_5x(rt2x00dev, ant); - else if (rt2x00_rf(&rt2x00dev->chip, RF2528) || - rt2x00_rf(&rt2x00dev->chip, RF2527)) + else if (rt2x00_rf(rt2x00dev, RF2528) || rt2x00_rf(rt2x00dev, RF2527)) rt73usb_config_antenna_2x(rt2x00dev, ant); } @@ -779,8 +777,7 @@ static void rt73usb_config_channel(struct rt2x00_dev *rt2x00dev, rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset); - smart = !(rt2x00_rf(&rt2x00dev->chip, RF5225) || - rt2x00_rf(&rt2x00dev->chip, RF2527)); + smart = !(rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF2527)); rt73usb_bbp_read(rt2x00dev, 3, &r3); rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, smart); @@ -1210,8 +1207,7 @@ static int rt73usb_init_registers(struct rt2x00_dev *rt2x00dev) rt2x00usb_register_write(rt2x00dev, SEC_CSR5, 0x00000000); reg = 0x000023b0; - if (rt2x00_rf(&rt2x00dev->chip, RF5225) || - rt2x00_rf(&rt2x00dev->chip, RF2527)) + if (rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF2527)) rt2x00_set_field32(®, PHY_CSR1_RF_RPI, 1); rt2x00usb_register_write(rt2x00dev, PHY_CSR1, reg); @@ -1824,19 +1820,18 @@ static int rt73usb_init_eeprom(struct rt2x00_dev *rt2x00dev) */ value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); rt2x00usb_register_read(rt2x00dev, MAC_CSR0, ®); - rt2x00_set_chip(rt2x00dev, RT2571, value, reg); - rt2x00_print_chip(rt2x00dev); + rt2x00_set_chip(rt2x00dev, rt2x00_get_field32(reg, MAC_CSR0_CHIPSET), + value, rt2x00_get_field32(reg, MAC_CSR0_REVISION)); - if (!rt2x00_check_rev(&rt2x00dev->chip, 0x000ffff0, 0x25730) || - rt2x00_check_rev(&rt2x00dev->chip, 0x0000000f, 0)) { + if (!rt2x00_rt(rt2x00dev, RT2573) || (rt2x00_rev(rt2x00dev) == 0)) { ERROR(rt2x00dev, "Invalid RT chipset detected.\n"); return -ENODEV; } - if (!rt2x00_rf(&rt2x00dev->chip, RF5226) && - !rt2x00_rf(&rt2x00dev->chip, RF2528) && - !rt2x00_rf(&rt2x00dev->chip, RF5225) && - !rt2x00_rf(&rt2x00dev->chip, RF2527)) { + if (!rt2x00_rf(rt2x00dev, RF5226) && + !rt2x00_rf(rt2x00dev, RF2528) && + !rt2x00_rf(rt2x00dev, RF5225) && + !rt2x00_rf(rt2x00dev, RF2527)) { ERROR(rt2x00dev, "Invalid RF chipset detected.\n"); return -ENODEV; } @@ -2081,17 +2076,17 @@ static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) spec->supported_bands = SUPPORT_BAND_2GHZ; spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; - if (rt2x00_rf(&rt2x00dev->chip, RF2528)) { + if (rt2x00_rf(rt2x00dev, RF2528)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2528); spec->channels = rf_vals_bg_2528; - } else if (rt2x00_rf(&rt2x00dev->chip, RF5226)) { + } else if (rt2x00_rf(rt2x00dev, RF5226)) { spec->supported_bands |= SUPPORT_BAND_5GHZ; spec->num_channels = ARRAY_SIZE(rf_vals_5226); spec->channels = rf_vals_5226; - } else if (rt2x00_rf(&rt2x00dev->chip, RF2527)) { + } else if (rt2x00_rf(rt2x00dev, RF2527)) { spec->num_channels = 14; spec->channels = rf_vals_5225_2527; - } else if (rt2x00_rf(&rt2x00dev->chip, RF5225)) { + } else if (rt2x00_rf(rt2x00dev, RF5225)) { spec->supported_bands |= SUPPORT_BAND_5GHZ; spec->num_channels = ARRAY_SIZE(rf_vals_5225_2527); spec->channels = rf_vals_5225_2527; @@ -2249,7 +2244,6 @@ static const struct ieee80211_ops rt73usb_mac80211_ops = { .get_stats = rt2x00mac_get_stats, .bss_info_changed = rt2x00mac_bss_info_changed, .conf_tx = rt73usb_conf_tx, - .get_tx_stats = rt2x00mac_get_tx_stats, .get_tsf = rt73usb_get_tsf, .rfkill_poll = rt2x00mac_rfkill_poll, }; @@ -2354,6 +2348,7 @@ static struct usb_device_id rt73usb_device_table[] = { { USB_DEVICE(0x08dd, 0x0120), USB_DEVICE_DATA(&rt73usb_ops) }, /* Buffalo */ { USB_DEVICE(0x0411, 0x00d8), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x0411, 0x00d9), USB_DEVICE_DATA(&rt73usb_ops) }, { USB_DEVICE(0x0411, 0x00f4), USB_DEVICE_DATA(&rt73usb_ops) }, { USB_DEVICE(0x0411, 0x0116), USB_DEVICE_DATA(&rt73usb_ops) }, { USB_DEVICE(0x0411, 0x0119), USB_DEVICE_DATA(&rt73usb_ops) }, diff --git a/drivers/net/wireless/rt2x00/rt73usb.h b/drivers/net/wireless/rt2x00/rt73usb.h index e783a099a8f1..b4e3ddda06cf 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.h +++ b/drivers/net/wireless/rt2x00/rt73usb.h @@ -142,6 +142,8 @@ struct hw_pairwise_ta_entry { * MAC_CSR0: ASIC revision number. */ #define MAC_CSR0 0x3000 +#define MAC_CSR0_REVISION FIELD32(0x0000000f) +#define MAC_CSR0_CHIPSET FIELD32(0x000ffff0) /* * MAC_CSR1: System control register. diff --git a/drivers/net/wireless/rtl818x/rtl8180.h b/drivers/net/wireless/rtl818x/rtl8180.h index 8721282a8185..de3844fe06d8 100644 --- a/drivers/net/wireless/rtl818x/rtl8180.h +++ b/drivers/net/wireless/rtl818x/rtl8180.h @@ -60,7 +60,6 @@ struct rtl8180_priv { struct rtl818x_csr __iomem *map; const struct rtl818x_rf_ops *rf; struct ieee80211_vif *vif; - int mode; /* rtl8180 driver specific */ spinlock_t lock; diff --git a/drivers/net/wireless/rtl818x/rtl8180_dev.c b/drivers/net/wireless/rtl818x/rtl8180_dev.c index 8a40a1439984..109ab1baf625 100644 --- a/drivers/net/wireless/rtl818x/rtl8180_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180_dev.c @@ -82,8 +82,6 @@ static const struct ieee80211_channel rtl818x_channels[] = { }; - - void rtl8180_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data) { struct rtl8180_priv *priv = dev->priv; @@ -615,7 +613,6 @@ static int rtl8180_start(struct ieee80211_hw *dev) reg |= RTL818X_CMD_TX_ENABLE; rtl818x_iowrite8(priv, &priv->map->CMD, reg); - priv->mode = NL80211_IFTYPE_MONITOR; return 0; err_free_rings: @@ -633,8 +630,6 @@ static void rtl8180_stop(struct ieee80211_hw *dev) u8 reg; int i; - priv->mode = NL80211_IFTYPE_UNSPECIFIED; - rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); reg = rtl818x_ioread8(priv, &priv->map->CMD); @@ -657,38 +652,39 @@ static void rtl8180_stop(struct ieee80211_hw *dev) } static int rtl8180_add_interface(struct ieee80211_hw *dev, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct rtl8180_priv *priv = dev->priv; - if (priv->mode != NL80211_IFTYPE_MONITOR) - return -EOPNOTSUPP; + /* + * We only support one active interface at a time. + */ + if (priv->vif) + return -EBUSY; - switch (conf->type) { + switch (vif->type) { case NL80211_IFTYPE_STATION: - priv->mode = conf->type; break; default: return -EOPNOTSUPP; } - priv->vif = conf->vif; + priv->vif = vif; rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->MAC[0], - le32_to_cpu(*(__le32 *)conf->mac_addr)); + le32_to_cpu(*(__le32 *)vif->addr)); rtl818x_iowrite16(priv, (__le16 __iomem *)&priv->map->MAC[4], - le16_to_cpu(*(__le16 *)(conf->mac_addr + 4))); + le16_to_cpu(*(__le16 *)(vif->addr + 4))); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); return 0; } static void rtl8180_remove_interface(struct ieee80211_hw *dev, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct rtl8180_priv *priv = dev->priv; - priv->mode = NL80211_IFTYPE_MONITOR; priv->vif = NULL; } @@ -765,6 +761,14 @@ static void rtl8180_configure_filter(struct ieee80211_hw *dev, rtl818x_iowrite32(priv, &priv->map->RX_CONF, priv->rx_conf); } +static u64 rtl8180_get_tsf(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + + return rtl818x_ioread32(priv, &priv->map->TSFT[0]) | + (u64)(rtl818x_ioread32(priv, &priv->map->TSFT[1])) << 32; +} + static const struct ieee80211_ops rtl8180_ops = { .tx = rtl8180_tx, .start = rtl8180_start, @@ -775,6 +779,7 @@ static const struct ieee80211_ops rtl8180_ops = { .bss_info_changed = rtl8180_bss_info_changed, .prepare_multicast = rtl8180_prepare_multicast, .configure_filter = rtl8180_configure_filter, + .get_tsf = rtl8180_get_tsf, }; static void rtl8180_eeprom_register_read(struct eeprom_93cx6 *eeprom) diff --git a/drivers/net/wireless/rtl818x/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187.h index 6af0f3f71f3a..6bb32112e65c 100644 --- a/drivers/net/wireless/rtl818x/rtl8187.h +++ b/drivers/net/wireless/rtl818x/rtl8187.h @@ -92,7 +92,7 @@ struct rtl8187_priv { struct rtl818x_csr *map; const struct rtl818x_rf_ops *rf; struct ieee80211_vif *vif; - int mode; + /* The mutex protects the TX loopback state. * Any attempt to set channels concurrently locks the device. */ diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c index 7ba3052b0708..0fb850e0c656 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c @@ -1019,31 +1019,30 @@ static void rtl8187_stop(struct ieee80211_hw *dev) } static int rtl8187_add_interface(struct ieee80211_hw *dev, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct rtl8187_priv *priv = dev->priv; int i; int ret = -EOPNOTSUPP; mutex_lock(&priv->conf_mutex); - if (priv->mode != NL80211_IFTYPE_MONITOR) + if (priv->vif) goto exit; - switch (conf->type) { + switch (vif->type) { case NL80211_IFTYPE_STATION: - priv->mode = conf->type; break; default: goto exit; } ret = 0; - priv->vif = conf->vif; + priv->vif = vif; rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); for (i = 0; i < ETH_ALEN; i++) rtl818x_iowrite8(priv, &priv->map->MAC[i], - ((u8 *)conf->mac_addr)[i]); + ((u8 *)vif->addr)[i]); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); exit: @@ -1052,11 +1051,10 @@ exit: } static void rtl8187_remove_interface(struct ieee80211_hw *dev, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct rtl8187_priv *priv = dev->priv; mutex_lock(&priv->conf_mutex); - priv->mode = NL80211_IFTYPE_MONITOR; priv->vif = NULL; mutex_unlock(&priv->conf_mutex); } @@ -1268,6 +1266,14 @@ static int rtl8187_conf_tx(struct ieee80211_hw *dev, u16 queue, return 0; } +static u64 rtl8187_get_tsf(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + + return rtl818x_ioread32(priv, &priv->map->TSFT[0]) | + (u64)(rtl818x_ioread32(priv, &priv->map->TSFT[1])) << 32; +} + static const struct ieee80211_ops rtl8187_ops = { .tx = rtl8187_tx, .start = rtl8187_start, @@ -1279,7 +1285,8 @@ static const struct ieee80211_ops rtl8187_ops = { .prepare_multicast = rtl8187_prepare_multicast, .configure_filter = rtl8187_configure_filter, .conf_tx = rtl8187_conf_tx, - .rfkill_poll = rtl8187_rfkill_poll + .rfkill_poll = rtl8187_rfkill_poll, + .get_tsf = rtl8187_get_tsf, }; static void rtl8187_eeprom_register_read(struct eeprom_93cx6 *eeprom) @@ -1366,7 +1373,6 @@ static int __devinit rtl8187_probe(struct usb_interface *intf, dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; - priv->mode = NL80211_IFTYPE_MONITOR; dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_RX_INCLUDES_FCS; diff --git a/drivers/net/wireless/rtl818x/rtl8187_leds.c b/drivers/net/wireless/rtl818x/rtl8187_leds.c index ded44c045eb2..4637337d5ce6 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_leds.c +++ b/drivers/net/wireless/rtl818x/rtl8187_leds.c @@ -33,7 +33,7 @@ static void led_turn_on(struct work_struct *work) struct rtl8187_led *led = &priv->led_tx; /* Don't change the LED, when the device is down. */ - if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) + if (!priv->vif || priv->vif->type == NL80211_IFTYPE_UNSPECIFIED) return ; /* Skip if the LED is not registered. */ @@ -71,7 +71,7 @@ static void led_turn_off(struct work_struct *work) struct rtl8187_led *led = &priv->led_tx; /* Don't change the LED, when the device is down. */ - if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) + if (!priv->vif || priv->vif->type == NL80211_IFTYPE_UNSPECIFIED) return ; /* Skip if the LED is not registered. */ @@ -241,5 +241,5 @@ void rtl8187_leds_exit(struct ieee80211_hw *dev) cancel_delayed_work_sync(&priv->led_off); cancel_delayed_work_sync(&priv->led_on); } -#endif /* def CONFIG_RTL8187_LED */ +#endif /* def CONFIG_RTL8187_LEDS */ diff --git a/drivers/net/wireless/rtl818x/rtl8187_leds.h b/drivers/net/wireless/rtl818x/rtl8187_leds.h index efe8041bdda4..d743c96d4a20 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_leds.h +++ b/drivers/net/wireless/rtl818x/rtl8187_leds.h @@ -54,6 +54,6 @@ struct rtl8187_led { void rtl8187_leds_init(struct ieee80211_hw *dev, u16 code); void rtl8187_leds_exit(struct ieee80211_hw *dev); -#endif /* def CONFIG_RTL8187_LED */ +#endif /* def CONFIG_RTL8187_LEDS */ #endif /* RTL8187_LED_H */ diff --git a/drivers/net/wireless/wl12xx/Makefile b/drivers/net/wireless/wl12xx/Makefile index 62e37ad01cc0..f47ec94c16dc 100644 --- a/drivers/net/wireless/wl12xx/Makefile +++ b/drivers/net/wireless/wl12xx/Makefile @@ -10,5 +10,7 @@ obj-$(CONFIG_WL1251_SDIO) += wl1251_sdio.o wl1271-objs = wl1271_main.o wl1271_spi.o wl1271_cmd.o \ wl1271_event.o wl1271_tx.o wl1271_rx.o \ wl1271_ps.o wl1271_acx.o wl1271_boot.o \ - wl1271_init.o wl1271_debugfs.o + wl1271_init.o wl1271_debugfs.o wl1271_io.o + +wl1271-$(CONFIG_NL80211_TESTMODE) += wl1271_testmode.o obj-$(CONFIG_WL1271) += wl1271.o diff --git a/drivers/net/wireless/wl12xx/wl1251.h b/drivers/net/wireless/wl12xx/wl1251.h index 054533f7a124..37c61c19cae5 100644 --- a/drivers/net/wireless/wl12xx/wl1251.h +++ b/drivers/net/wireless/wl12xx/wl1251.h @@ -247,6 +247,7 @@ struct wl1251_debugfs { struct dentry *rxpipe_tx_xfr_host_int_trig_rx_data; struct dentry *tx_queue_len; + struct dentry *tx_queue_status; struct dentry *retry_count; struct dentry *excessive_retries; @@ -340,9 +341,6 @@ struct wl1251 { /* Are we currently scanning */ bool scanning; - /* Our association ID */ - u16 aid; - /* Default key (for WEP) */ u32 default_key; diff --git a/drivers/net/wireless/wl12xx/wl1251_acx.c b/drivers/net/wireless/wl12xx/wl1251_acx.c index acfa086dbfc5..beff084040b5 100644 --- a/drivers/net/wireless/wl12xx/wl1251_acx.c +++ b/drivers/net/wireless/wl12xx/wl1251_acx.c @@ -976,3 +976,72 @@ out: kfree(acx); return ret; } + +int wl1251_acx_ac_cfg(struct wl1251 *wl, u8 ac, u8 cw_min, u16 cw_max, + u8 aifs, u16 txop) +{ + struct wl1251_acx_ac_cfg *acx; + int ret = 0; + + wl1251_debug(DEBUG_ACX, "acx ac cfg %d cw_ming %d cw_max %d " + "aifs %d txop %d", ac, cw_min, cw_max, aifs, txop); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->ac = ac; + acx->cw_min = cw_min; + acx->cw_max = cw_max; + acx->aifsn = aifs; + acx->txop_limit = txop; + + ret = wl1251_cmd_configure(wl, ACX_AC_CFG, acx, sizeof(*acx)); + if (ret < 0) { + wl1251_warning("acx ac cfg failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +int wl1251_acx_tid_cfg(struct wl1251 *wl, u8 queue, + enum wl1251_acx_channel_type type, + u8 tsid, enum wl1251_acx_ps_scheme ps_scheme, + enum wl1251_acx_ack_policy ack_policy) +{ + struct wl1251_acx_tid_cfg *acx; + int ret = 0; + + wl1251_debug(DEBUG_ACX, "acx tid cfg %d type %d tsid %d " + "ps_scheme %d ack_policy %d", queue, type, tsid, + ps_scheme, ack_policy); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->queue = queue; + acx->type = type; + acx->tsid = tsid; + acx->ps_scheme = ps_scheme; + acx->ack_policy = ack_policy; + + ret = wl1251_cmd_configure(wl, ACX_TID_CFG, acx, sizeof(*acx)); + if (ret < 0) { + wl1251_warning("acx tid cfg failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} diff --git a/drivers/net/wireless/wl12xx/wl1251_acx.h b/drivers/net/wireless/wl12xx/wl1251_acx.h index 652371432cd8..26160c45784c 100644 --- a/drivers/net/wireless/wl12xx/wl1251_acx.h +++ b/drivers/net/wireless/wl12xx/wl1251_acx.h @@ -1166,6 +1166,87 @@ struct wl1251_acx_wr_tbtt_and_dtim { u8 padding; } __attribute__ ((packed)); +struct wl1251_acx_ac_cfg { + struct acx_header header; + + /* + * Access Category - The TX queue's access category + * (refer to AccessCategory_enum) + */ + u8 ac; + + /* + * The contention window minimum size (in slots) for + * the access class. + */ + u8 cw_min; + + /* + * The contention window maximum size (in slots) for + * the access class. + */ + u16 cw_max; + + /* The AIF value (in slots) for the access class. */ + u8 aifsn; + + u8 reserved; + + /* The TX Op Limit (in microseconds) for the access class. */ + u16 txop_limit; +} __attribute__ ((packed)); + + +enum wl1251_acx_channel_type { + CHANNEL_TYPE_DCF = 0, + CHANNEL_TYPE_EDCF = 1, + CHANNEL_TYPE_HCCA = 2, +}; + +enum wl1251_acx_ps_scheme { + /* regular ps: simple sending of packets */ + WL1251_ACX_PS_SCHEME_LEGACY = 0, + + /* sending a packet triggers a unscheduled apsd downstream */ + WL1251_ACX_PS_SCHEME_UPSD_TRIGGER = 1, + + /* a pspoll packet will be sent before every data packet */ + WL1251_ACX_PS_SCHEME_LEGACY_PSPOLL = 2, + + /* scheduled apsd mode */ + WL1251_ACX_PS_SCHEME_SAPSD = 3, +}; + +enum wl1251_acx_ack_policy { + WL1251_ACX_ACK_POLICY_LEGACY = 0, + WL1251_ACX_ACK_POLICY_NO_ACK = 1, + WL1251_ACX_ACK_POLICY_BLOCK = 2, +}; + +struct wl1251_acx_tid_cfg { + struct acx_header header; + + /* tx queue id number (0-7) */ + u8 queue; + + /* channel access type for the queue, enum wl1251_acx_channel_type */ + u8 type; + + /* EDCA: ac index (0-3), HCCA: traffic stream id (8-15) */ + u8 tsid; + + /* ps scheme of the specified queue, enum wl1251_acx_ps_scheme */ + u8 ps_scheme; + + /* the tx queue ack policy, enum wl1251_acx_ack_policy */ + u8 ack_policy; + + u8 padding[3]; + + /* not supported */ + u32 apsdconf[2]; +} __attribute__ ((packed)); + /************************************************************************* Host Interrupt Register (WiLink -> Host) @@ -1322,5 +1403,11 @@ int wl1251_acx_tsf_info(struct wl1251 *wl, u64 *mactime); int wl1251_acx_rate_policies(struct wl1251 *wl); int wl1251_acx_mem_cfg(struct wl1251 *wl); int wl1251_acx_wr_tbtt_and_dtim(struct wl1251 *wl, u16 tbtt, u8 dtim); +int wl1251_acx_ac_cfg(struct wl1251 *wl, u8 ac, u8 cw_min, u16 cw_max, + u8 aifs, u16 txop); +int wl1251_acx_tid_cfg(struct wl1251 *wl, u8 queue, + enum wl1251_acx_channel_type type, + u8 tsid, enum wl1251_acx_ps_scheme ps_scheme, + enum wl1251_acx_ack_policy ack_policy); #endif /* __WL1251_ACX_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1251_cmd.c b/drivers/net/wireless/wl12xx/wl1251_cmd.c index 770f260726bd..0320b478bb3f 100644 --- a/drivers/net/wireless/wl12xx/wl1251_cmd.c +++ b/drivers/net/wireless/wl12xx/wl1251_cmd.c @@ -410,3 +410,86 @@ out: kfree(cmd); return ret; } + +int wl1251_cmd_scan(struct wl1251 *wl, u8 *ssid, size_t ssid_len, + struct ieee80211_channel *channels[], + unsigned int n_channels, unsigned int n_probes) +{ + struct wl1251_cmd_scan *cmd; + int i, ret = 0; + + wl1251_debug(DEBUG_CMD, "cmd scan"); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->params.rx_config_options = cpu_to_le32(CFG_RX_ALL_GOOD); + cmd->params.rx_filter_options = cpu_to_le32(CFG_RX_PRSP_EN | + CFG_RX_MGMT_EN | + CFG_RX_BCN_EN); + cmd->params.scan_options = 0; + cmd->params.num_channels = n_channels; + cmd->params.num_probe_requests = n_probes; + cmd->params.tx_rate = cpu_to_le16(1 << 1); /* 2 Mbps */ + cmd->params.tid_trigger = 0; + + for (i = 0; i < n_channels; i++) { + cmd->channels[i].min_duration = + cpu_to_le32(WL1251_SCAN_MIN_DURATION); + cmd->channels[i].max_duration = + cpu_to_le32(WL1251_SCAN_MAX_DURATION); + memset(&cmd->channels[i].bssid_lsb, 0xff, 4); + memset(&cmd->channels[i].bssid_msb, 0xff, 2); + cmd->channels[i].early_termination = 0; + cmd->channels[i].tx_power_att = 0; + cmd->channels[i].channel = channels[i]->hw_value; + } + + cmd->params.ssid_len = ssid_len; + if (ssid) + memcpy(cmd->params.ssid, ssid, ssid_len); + + ret = wl1251_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd)); + if (ret < 0) { + wl1251_error("cmd scan failed: %d", ret); + goto out; + } + + wl1251_mem_read(wl, wl->cmd_box_addr, cmd, sizeof(*cmd)); + + if (cmd->header.status != CMD_STATUS_SUCCESS) { + wl1251_error("cmd scan status wasn't success: %d", + cmd->header.status); + ret = -EIO; + goto out; + } + +out: + kfree(cmd); + return ret; +} + +int wl1251_cmd_trigger_scan_to(struct wl1251 *wl, u32 timeout) +{ + struct wl1251_cmd_trigger_scan_to *cmd; + int ret; + + wl1251_debug(DEBUG_CMD, "cmd trigger scan to"); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->timeout = timeout; + + ret = wl1251_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd)); + if (ret < 0) { + wl1251_error("cmd trigger scan to failed: %d", ret); + goto out; + } + +out: + kfree(cmd); + return ret; +} diff --git a/drivers/net/wireless/wl12xx/wl1251_cmd.h b/drivers/net/wireless/wl12xx/wl1251_cmd.h index dff798ad0ef5..4ad67cae94d2 100644 --- a/drivers/net/wireless/wl12xx/wl1251_cmd.h +++ b/drivers/net/wireless/wl12xx/wl1251_cmd.h @@ -27,6 +27,8 @@ #include "wl1251.h" +#include <net/cfg80211.h> + struct acx_header; int wl1251_cmd_send(struct wl1251 *wl, u16 type, void *buf, size_t buf_len); @@ -43,6 +45,10 @@ int wl1251_cmd_read_memory(struct wl1251 *wl, u32 addr, void *answer, size_t len); int wl1251_cmd_template_set(struct wl1251 *wl, u16 cmd_id, void *buf, size_t buf_len); +int wl1251_cmd_scan(struct wl1251 *wl, u8 *ssid, size_t ssid_len, + struct ieee80211_channel *channels[], + unsigned int n_channels, unsigned int n_probes); +int wl1251_cmd_trigger_scan_to(struct wl1251 *wl, u32 timeout); /* unit ms */ #define WL1251_COMMAND_TIMEOUT 2000 @@ -163,8 +169,12 @@ struct cmd_read_write_memory { #define CMDMBOX_HEADER_LEN 4 #define CMDMBOX_INFO_ELEM_HEADER_LEN 4 +#define WL1251_SCAN_MIN_DURATION 30000 +#define WL1251_SCAN_MAX_DURATION 60000 + +#define WL1251_SCAN_NUM_PROBES 3 -struct basic_scan_parameters { +struct wl1251_scan_parameters { u32 rx_config_options; u32 rx_filter_options; @@ -189,11 +199,11 @@ struct basic_scan_parameters { u8 tid_trigger; u8 ssid_len; - u32 ssid[8]; + u8 ssid[32]; } __attribute__ ((packed)); -struct basic_scan_channel_parameters { +struct wl1251_scan_ch_parameters { u32 min_duration; /* in TU */ u32 max_duration; /* in TU */ u32 bssid_lsb; @@ -213,11 +223,11 @@ struct basic_scan_channel_parameters { /* SCAN parameters */ #define SCAN_MAX_NUM_OF_CHANNELS 16 -struct cmd_scan { +struct wl1251_cmd_scan { struct wl1251_cmd_header header; - struct basic_scan_parameters params; - struct basic_scan_channel_parameters channels[SCAN_MAX_NUM_OF_CHANNELS]; + struct wl1251_scan_parameters params; + struct wl1251_scan_ch_parameters channels[SCAN_MAX_NUM_OF_CHANNELS]; } __attribute__ ((packed)); enum { diff --git a/drivers/net/wireless/wl12xx/wl1251_debugfs.c b/drivers/net/wireless/wl12xx/wl1251_debugfs.c index a00723059f83..0ccba57fb9fb 100644 --- a/drivers/net/wireless/wl12xx/wl1251_debugfs.c +++ b/drivers/net/wireless/wl12xx/wl1251_debugfs.c @@ -237,6 +237,27 @@ static const struct file_operations tx_queue_len_ops = { .open = wl1251_open_file_generic, }; +static ssize_t tx_queue_status_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct wl1251 *wl = file->private_data; + char buf[3], status; + int len; + + if (wl->tx_queue_stopped) + status = 's'; + else + status = 'r'; + + len = scnprintf(buf, sizeof(buf), "%c\n", status); + return simple_read_from_buffer(userbuf, count, ppos, buf, len); +} + +static const struct file_operations tx_queue_status_ops = { + .read = tx_queue_status_read, + .open = wl1251_open_file_generic, +}; + static void wl1251_debugfs_delete_files(struct wl1251 *wl) { DEBUGFS_FWSTATS_DEL(tx, internal_desc_overflow); @@ -331,6 +352,7 @@ static void wl1251_debugfs_delete_files(struct wl1251 *wl) DEBUGFS_FWSTATS_DEL(rxpipe, tx_xfr_host_int_trig_rx_data); DEBUGFS_DEL(tx_queue_len); + DEBUGFS_DEL(tx_queue_status); DEBUGFS_DEL(retry_count); DEBUGFS_DEL(excessive_retries); } @@ -431,6 +453,7 @@ static int wl1251_debugfs_add_files(struct wl1251 *wl) DEBUGFS_FWSTATS_ADD(rxpipe, tx_xfr_host_int_trig_rx_data); DEBUGFS_ADD(tx_queue_len, wl->debugfs.rootdir); + DEBUGFS_ADD(tx_queue_status, wl->debugfs.rootdir); DEBUGFS_ADD(retry_count, wl->debugfs.rootdir); DEBUGFS_ADD(excessive_retries, wl->debugfs.rootdir); diff --git a/drivers/net/wireless/wl12xx/wl1251_init.c b/drivers/net/wireless/wl12xx/wl1251_init.c index 5cb573383eeb..5aad56ea7153 100644 --- a/drivers/net/wireless/wl12xx/wl1251_init.c +++ b/drivers/net/wireless/wl12xx/wl1251_init.c @@ -294,6 +294,11 @@ static int wl1251_hw_init_tx_queue_config(struct wl1251 *wl) goto out; } + wl1251_acx_ac_cfg(wl, AC_BE, CWMIN_BE, CWMAX_BE, AIFS_DIFS, TXOP_BE); + wl1251_acx_ac_cfg(wl, AC_BK, CWMIN_BK, CWMAX_BK, AIFS_DIFS, TXOP_BK); + wl1251_acx_ac_cfg(wl, AC_VI, CWMIN_VI, CWMAX_VI, AIFS_DIFS, TXOP_VI); + wl1251_acx_ac_cfg(wl, AC_VO, CWMIN_VO, CWMAX_VO, AIFS_DIFS, TXOP_VO); + out: kfree(config); return ret; diff --git a/drivers/net/wireless/wl12xx/wl1251_init.h b/drivers/net/wireless/wl12xx/wl1251_init.h index b3b25ec885ea..269cefb3e7d4 100644 --- a/drivers/net/wireless/wl12xx/wl1251_init.h +++ b/drivers/net/wireless/wl12xx/wl1251_init.h @@ -26,6 +26,53 @@ #include "wl1251.h" +enum { + /* best effort/legacy */ + AC_BE = 0, + + /* background */ + AC_BK = 1, + + /* video */ + AC_VI = 2, + + /* voice */ + AC_VO = 3, + + /* broadcast dummy access category */ + AC_BCAST = 4, + + NUM_ACCESS_CATEGORIES = 4 +}; + +/* following are defult values for the IE fields*/ +#define CWMIN_BK 15 +#define CWMIN_BE 15 +#define CWMIN_VI 7 +#define CWMIN_VO 3 +#define CWMAX_BK 1023 +#define CWMAX_BE 63 +#define CWMAX_VI 15 +#define CWMAX_VO 7 + +/* slot number setting to start transmission at PIFS interval */ +#define AIFS_PIFS 1 + +/* + * slot number setting to start transmission at DIFS interval - normal DCF + * access + */ +#define AIFS_DIFS 2 + +#define AIFSN_BK 7 +#define AIFSN_BE 3 +#define AIFSN_VI AIFS_PIFS +#define AIFSN_VO AIFS_PIFS +#define TXOP_BK 0 +#define TXOP_BE 0 +#define TXOP_VI 3008 +#define TXOP_VO 1504 + int wl1251_hw_init_hwenc_config(struct wl1251 *wl); int wl1251_hw_init_templates_config(struct wl1251 *wl); int wl1251_hw_init_rx_config(struct wl1251 *wl, u32 config, u32 filter); diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c index 2f50a256efa5..24ae6a360ac8 100644 --- a/drivers/net/wireless/wl12xx/wl1251_main.c +++ b/drivers/net/wireless/wl12xx/wl1251_main.c @@ -395,6 +395,7 @@ static int wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) * the queue here, otherwise the queue will get too long. */ if (skb_queue_len(&wl->tx_queue) >= WL1251_TX_QUEUE_MAX_LENGTH) { + wl1251_debug(DEBUG_TX, "op_tx: tx_queue full, stop queues"); ieee80211_stop_queues(wl->hw); /* @@ -510,13 +511,13 @@ static void wl1251_op_stop(struct ieee80211_hw *hw) } static int wl1251_op_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct wl1251 *wl = hw->priv; int ret = 0; wl1251_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", - conf->type, conf->mac_addr); + vif->type, vif->addr); mutex_lock(&wl->mutex); if (wl->vif) { @@ -524,9 +525,9 @@ static int wl1251_op_add_interface(struct ieee80211_hw *hw, goto out; } - wl->vif = conf->vif; + wl->vif = vif; - switch (conf->type) { + switch (vif->type) { case NL80211_IFTYPE_STATION: wl->bss_type = BSS_TYPE_STA_BSS; break; @@ -538,8 +539,8 @@ static int wl1251_op_add_interface(struct ieee80211_hw *hw, goto out; } - if (memcmp(wl->mac_addr, conf->mac_addr, ETH_ALEN)) { - memcpy(wl->mac_addr, conf->mac_addr, ETH_ALEN); + if (memcmp(wl->mac_addr, vif->addr, ETH_ALEN)) { + memcpy(wl->mac_addr, vif->addr, ETH_ALEN); SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr); ret = wl1251_acx_station_id(wl); if (ret < 0) @@ -552,7 +553,7 @@ out: } static void wl1251_op_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct wl1251 *wl = hw->priv; @@ -562,43 +563,25 @@ static void wl1251_op_remove_interface(struct ieee80211_hw *hw, mutex_unlock(&wl->mutex); } -static int wl1251_build_null_data(struct wl1251 *wl) +static int wl1251_build_qos_null_data(struct wl1251 *wl) { - struct wl12xx_null_data_template template; + struct ieee80211_qos_hdr template; - if (!is_zero_ether_addr(wl->bssid)) { - memcpy(template.header.da, wl->bssid, ETH_ALEN); - memcpy(template.header.bssid, wl->bssid, ETH_ALEN); - } else { - memset(template.header.da, 0xff, ETH_ALEN); - memset(template.header.bssid, 0xff, ETH_ALEN); - } - - memcpy(template.header.sa, wl->mac_addr, ETH_ALEN); - template.header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA | - IEEE80211_STYPE_NULLFUNC | - IEEE80211_FCTL_TODS); - - return wl1251_cmd_template_set(wl, CMD_NULL_DATA, &template, - sizeof(template)); - -} - -static int wl1251_build_ps_poll(struct wl1251 *wl, u16 aid) -{ - struct wl12xx_ps_poll_template template; + memset(&template, 0, sizeof(template)); - memcpy(template.bssid, wl->bssid, ETH_ALEN); - memcpy(template.ta, wl->mac_addr, ETH_ALEN); + memcpy(template.addr1, wl->bssid, ETH_ALEN); + memcpy(template.addr2, wl->mac_addr, ETH_ALEN); + memcpy(template.addr3, wl->bssid, ETH_ALEN); - /* aid in PS-Poll has its two MSBs each set to 1 */ - template.aid = cpu_to_le16(1 << 15 | 1 << 14 | aid); + template.frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_NULLFUNC | + IEEE80211_FCTL_TODS); - template.fc = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); + /* FIXME: not sure what priority to use here */ + template.qos_ctrl = cpu_to_le16(0); - return wl1251_cmd_template_set(wl, CMD_PS_POLL, &template, + return wl1251_cmd_template_set(wl, CMD_QOS_NULL_DATA, &template, sizeof(template)); - } static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) @@ -634,26 +617,34 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) wl->psm_requested = true; + wl->dtim_period = conf->ps_dtim_period; + + ret = wl1251_acx_wr_tbtt_and_dtim(wl, wl->beacon_int, + wl->dtim_period); + /* - * We enter PSM only if we're already associated. - * If we're not, we'll enter it when joining an SSID, - * through the bss_info_changed() hook. + * mac80211 enables PSM only if we're already associated. */ ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE); + if (ret < 0) + goto out_sleep; } else if (!(conf->flags & IEEE80211_CONF_PS) && wl->psm_requested) { wl1251_debug(DEBUG_PSM, "psm disabled"); wl->psm_requested = false; - if (wl->psm) + if (wl->psm) { ret = wl1251_ps_set_mode(wl, STATION_ACTIVE_MODE); + if (ret < 0) + goto out_sleep; + } } if (conf->power_level != wl->power_level) { ret = wl1251_acx_tx_power(wl, conf->power_level); if (ret < 0) - goto out; + goto out_sleep; wl->power_level = conf->power_level; } @@ -864,199 +855,61 @@ out: return ret; } -static int wl1251_build_basic_rates(char *rates) -{ - u8 index = 0; - - rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB; - rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB; - rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_5MB; - rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_11MB; - - return index; -} - -static int wl1251_build_extended_rates(char *rates) +static int wl1251_op_hw_scan(struct ieee80211_hw *hw, + struct cfg80211_scan_request *req) { - u8 index = 0; - - rates[index++] = IEEE80211_OFDM_RATE_6MB; - rates[index++] = IEEE80211_OFDM_RATE_9MB; - rates[index++] = IEEE80211_OFDM_RATE_12MB; - rates[index++] = IEEE80211_OFDM_RATE_18MB; - rates[index++] = IEEE80211_OFDM_RATE_24MB; - rates[index++] = IEEE80211_OFDM_RATE_36MB; - rates[index++] = IEEE80211_OFDM_RATE_48MB; - rates[index++] = IEEE80211_OFDM_RATE_54MB; - - return index; -} - + struct wl1251 *wl = hw->priv; + struct sk_buff *skb; + size_t ssid_len = 0; + u8 *ssid = NULL; + int ret; -static int wl1251_build_probe_req(struct wl1251 *wl, u8 *ssid, size_t ssid_len) -{ - struct wl12xx_probe_req_template template; - struct wl12xx_ie_rates *rates; - char *ptr; - u16 size; - - ptr = (char *)&template; - size = sizeof(struct ieee80211_header); - - memset(template.header.da, 0xff, ETH_ALEN); - memset(template.header.bssid, 0xff, ETH_ALEN); - memcpy(template.header.sa, wl->mac_addr, ETH_ALEN); - template.header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); - - /* IEs */ - /* SSID */ - template.ssid.header.id = WLAN_EID_SSID; - template.ssid.header.len = ssid_len; - if (ssid_len && ssid) - memcpy(template.ssid.ssid, ssid, ssid_len); - size += sizeof(struct wl12xx_ie_header) + ssid_len; - ptr += size; - - /* Basic Rates */ - rates = (struct wl12xx_ie_rates *)ptr; - rates->header.id = WLAN_EID_SUPP_RATES; - rates->header.len = wl1251_build_basic_rates(rates->rates); - size += sizeof(struct wl12xx_ie_header) + rates->header.len; - ptr += sizeof(struct wl12xx_ie_header) + rates->header.len; - - /* Extended rates */ - rates = (struct wl12xx_ie_rates *)ptr; - rates->header.id = WLAN_EID_EXT_SUPP_RATES; - rates->header.len = wl1251_build_extended_rates(rates->rates); - size += sizeof(struct wl12xx_ie_header) + rates->header.len; - - wl1251_dump(DEBUG_SCAN, "PROBE REQ: ", &template, size); - - return wl1251_cmd_template_set(wl, CMD_PROBE_REQ, &template, - size); -} + wl1251_debug(DEBUG_MAC80211, "mac80211 hw scan"); -static int wl1251_hw_scan(struct wl1251 *wl, u8 *ssid, size_t len, - u8 active_scan, u8 high_prio, u8 num_channels, - u8 probe_requests) -{ - struct wl1251_cmd_trigger_scan_to *trigger = NULL; - struct cmd_scan *params = NULL; - int i, ret; - u16 scan_options = 0; - - if (wl->scanning) - return -EINVAL; - - params = kzalloc(sizeof(*params), GFP_KERNEL); - if (!params) - return -ENOMEM; - - params->params.rx_config_options = cpu_to_le32(CFG_RX_ALL_GOOD); - params->params.rx_filter_options = - cpu_to_le32(CFG_RX_PRSP_EN | CFG_RX_MGMT_EN | CFG_RX_BCN_EN); - - /* High priority scan */ - if (!active_scan) - scan_options |= SCAN_PASSIVE; - if (high_prio) - scan_options |= SCAN_PRIORITY_HIGH; - params->params.scan_options = scan_options; - - params->params.num_channels = num_channels; - params->params.num_probe_requests = probe_requests; - params->params.tx_rate = cpu_to_le16(1 << 1); /* 2 Mbps */ - params->params.tid_trigger = 0; - - for (i = 0; i < num_channels; i++) { - params->channels[i].min_duration = cpu_to_le32(30000); - params->channels[i].max_duration = cpu_to_le32(60000); - memset(¶ms->channels[i].bssid_lsb, 0xff, 4); - memset(¶ms->channels[i].bssid_msb, 0xff, 2); - params->channels[i].early_termination = 0; - params->channels[i].tx_power_att = 0; - params->channels[i].channel = i + 1; - memset(params->channels[i].pad, 0, 3); + if (req->n_ssids) { + ssid = req->ssids[0].ssid; + ssid_len = req->ssids[0].ssid_len; } - for (i = num_channels; i < SCAN_MAX_NUM_OF_CHANNELS; i++) - memset(¶ms->channels[i], 0, - sizeof(struct basic_scan_channel_parameters)); - - if (len && ssid) { - params->params.ssid_len = len; - memcpy(params->params.ssid, ssid, len); - } else { - params->params.ssid_len = 0; - memset(params->params.ssid, 0, 32); - } + mutex_lock(&wl->mutex); - ret = wl1251_build_probe_req(wl, ssid, len); - if (ret < 0) { - wl1251_error("PROBE request template failed"); + if (wl->scanning) { + wl1251_debug(DEBUG_SCAN, "scan already in progress"); + ret = -EINVAL; goto out; } - trigger = kzalloc(sizeof(*trigger), GFP_KERNEL); - if (!trigger) + ret = wl1251_ps_elp_wakeup(wl); + if (ret < 0) goto out; - trigger->timeout = 0; - - ret = wl1251_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger, - sizeof(*trigger)); - if (ret < 0) { - wl1251_error("trigger scan to failed for hw scan"); + skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len, + req->ie, req->ie_len); + if (!skb) { + ret = -ENOMEM; goto out; } - wl1251_dump(DEBUG_SCAN, "SCAN: ", params, sizeof(*params)); - - wl->scanning = true; + ret = wl1251_cmd_template_set(wl, CMD_PROBE_REQ, skb->data, + skb->len); + dev_kfree_skb(skb); + if (ret < 0) + goto out_sleep; - ret = wl1251_cmd_send(wl, CMD_SCAN, params, sizeof(*params)); + ret = wl1251_cmd_trigger_scan_to(wl, 0); if (ret < 0) - wl1251_error("SCAN failed"); + goto out_sleep; - wl1251_mem_read(wl, wl->cmd_box_addr, params, sizeof(*params)); + wl->scanning = true; - if (params->header.status != CMD_STATUS_SUCCESS) { - wl1251_error("TEST command answer error: %d", - params->header.status); + ret = wl1251_cmd_scan(wl, ssid, ssid_len, req->channels, + req->n_channels, WL1251_SCAN_NUM_PROBES); + if (ret < 0) { wl->scanning = false; - ret = -EIO; - goto out; - } - -out: - kfree(params); - return ret; - -} - -static int wl1251_op_hw_scan(struct ieee80211_hw *hw, - struct cfg80211_scan_request *req) -{ - struct wl1251 *wl = hw->priv; - int ret; - u8 *ssid = NULL; - size_t ssid_len = 0; - - wl1251_debug(DEBUG_MAC80211, "mac80211 hw scan"); - - if (req->n_ssids) { - ssid = req->ssids[0].ssid; - ssid_len = req->ssids[0].ssid_len; + goto out_sleep; } - mutex_lock(&wl->mutex); - - ret = wl1251_ps_elp_wakeup(wl); - if (ret < 0) - goto out; - - ret = wl1251_hw_scan(hw->priv, ssid, ssid_len, 1, 0, 13, 3); - +out_sleep: wl1251_ps_elp_sleep(wl); out: @@ -1093,9 +946,8 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_bss_conf *bss_conf, u32 changed) { - enum wl1251_cmd_ps_mode mode; struct wl1251 *wl = hw->priv; - struct sk_buff *beacon; + struct sk_buff *beacon, *skb; int ret; wl1251_debug(DEBUG_MAC80211, "mac80211 bss info changed"); @@ -1109,7 +961,17 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_BSSID) { memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); - ret = wl1251_build_null_data(wl); + skb = ieee80211_nullfunc_get(wl->hw, wl->vif); + if (!skb) + goto out_sleep; + + ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA, + skb->data, skb->len); + dev_kfree_skb(skb); + if (ret < 0) + goto out_sleep; + + ret = wl1251_build_qos_null_data(wl); if (ret < 0) goto out; @@ -1124,27 +986,21 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_ASSOC) { if (bss_conf->assoc) { wl->beacon_int = bss_conf->beacon_int; - wl->dtim_period = bss_conf->dtim_period; - ret = wl1251_acx_wr_tbtt_and_dtim(wl, wl->beacon_int, - wl->dtim_period); - wl->aid = bss_conf->aid; + skb = ieee80211_pspoll_get(wl->hw, wl->vif); + if (!skb) + goto out_sleep; - ret = wl1251_build_ps_poll(wl, wl->aid); + ret = wl1251_cmd_template_set(wl, CMD_PS_POLL, + skb->data, + skb->len); + dev_kfree_skb(skb); if (ret < 0) goto out_sleep; - ret = wl1251_acx_aid(wl, wl->aid); + ret = wl1251_acx_aid(wl, bss_conf->aid); if (ret < 0) goto out_sleep; - - /* If we want to go in PSM but we're not there yet */ - if (wl->psm_requested && !wl->psm) { - mode = STATION_POWER_SAVE_MODE; - ret = wl1251_ps_set_mode(wl, mode); - if (ret < 0) - goto out_sleep; - } } else { /* use defaults when not associated */ wl->beacon_int = WL1251_DEFAULT_BEACON_INT; @@ -1176,7 +1032,7 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, ret = wl1251_acx_cts_protect(wl, CTSPROTECT_DISABLE); if (ret < 0) { wl1251_warning("Set ctsprotect failed %d", ret); - goto out; + goto out_sleep; } } @@ -1187,7 +1043,7 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, if (ret < 0) { dev_kfree_skb(beacon); - goto out; + goto out_sleep; } ret = wl1251_cmd_template_set(wl, CMD_PROBE_RESP, beacon->data, @@ -1196,13 +1052,13 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, dev_kfree_skb(beacon); if (ret < 0) - goto out; + goto out_sleep; ret = wl1251_join(wl, wl->bss_type, wl->beacon_int, wl->channel, wl->dtim_period); if (ret < 0) - goto out; + goto out_sleep; } out_sleep: @@ -1273,6 +1129,49 @@ static struct ieee80211_channel wl1251_channels[] = { { .hw_value = 13, .center_freq = 2472}, }; +static int wl1251_op_conf_tx(struct ieee80211_hw *hw, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + enum wl1251_acx_ps_scheme ps_scheme; + struct wl1251 *wl = hw->priv; + int ret; + + mutex_lock(&wl->mutex); + + wl1251_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue); + + ret = wl1251_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + /* mac80211 uses units of 32 usec */ + ret = wl1251_acx_ac_cfg(wl, wl1251_tx_get_queue(queue), + params->cw_min, params->cw_max, + params->aifs, params->txop * 32); + if (ret < 0) + goto out_sleep; + + if (params->uapsd) + ps_scheme = WL1251_ACX_PS_SCHEME_UPSD_TRIGGER; + else + ps_scheme = WL1251_ACX_PS_SCHEME_LEGACY; + + ret = wl1251_acx_tid_cfg(wl, wl1251_tx_get_queue(queue), + CHANNEL_TYPE_EDCF, + wl1251_tx_get_queue(queue), ps_scheme, + WL1251_ACX_ACK_POLICY_LEGACY); + if (ret < 0) + goto out_sleep; + +out_sleep: + wl1251_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); + + return ret; +} + /* can't be const, mac80211 writes to this */ static struct ieee80211_supported_band wl1251_band_2ghz = { .channels = wl1251_channels, @@ -1293,6 +1192,7 @@ static const struct ieee80211_ops wl1251_ops = { .hw_scan = wl1251_op_hw_scan, .bss_info_changed = wl1251_op_bss_info_changed, .set_rts_threshold = wl1251_op_set_rts_threshold, + .conf_tx = wl1251_op_conf_tx, }; static int wl1251_register_hw(struct wl1251 *wl) @@ -1332,12 +1232,15 @@ int wl1251_init_ieee80211(struct wl1251 *wl) wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM | IEEE80211_HW_SUPPORTS_PS | - IEEE80211_HW_BEACON_FILTER; + IEEE80211_HW_BEACON_FILTER | + IEEE80211_HW_SUPPORTS_UAPSD; wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); wl->hw->wiphy->max_scan_ssids = 1; wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1251_band_2ghz; + wl->hw->queues = 4; + ret = wl1251_register_hw(wl); if (ret) goto out; diff --git a/drivers/net/wireless/wl12xx/wl1251_ps.c b/drivers/net/wireless/wl12xx/wl1251_ps.c index 9931b197ff77..851dfb65e474 100644 --- a/drivers/net/wireless/wl12xx/wl1251_ps.c +++ b/drivers/net/wireless/wl12xx/wl1251_ps.c @@ -26,7 +26,8 @@ #include "wl1251_cmd.h" #include "wl1251_io.h" -#define WL1251_WAKEUP_TIMEOUT 2000 +/* in ms */ +#define WL1251_WAKEUP_TIMEOUT 100 void wl1251_elp_work(struct work_struct *work) { @@ -67,7 +68,7 @@ void wl1251_ps_elp_sleep(struct wl1251 *wl) int wl1251_ps_elp_wakeup(struct wl1251 *wl) { - unsigned long timeout; + unsigned long timeout, start; u32 elp_reg; if (!wl->elp) @@ -75,6 +76,7 @@ int wl1251_ps_elp_wakeup(struct wl1251 *wl) wl1251_debug(DEBUG_PSM, "waking up chip from elp"); + start = jiffies; timeout = jiffies + msecs_to_jiffies(WL1251_WAKEUP_TIMEOUT); wl1251_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP); @@ -95,8 +97,7 @@ int wl1251_ps_elp_wakeup(struct wl1251 *wl) } wl1251_debug(DEBUG_PSM, "wakeup time: %u ms", - jiffies_to_msecs(jiffies) - - (jiffies_to_msecs(timeout) - WL1251_WAKEUP_TIMEOUT)); + jiffies_to_msecs(jiffies - start)); wl->elp = false; diff --git a/drivers/net/wireless/wl12xx/wl1251_rx.c b/drivers/net/wireless/wl12xx/wl1251_rx.c index f84cc89cbffc..b56732226cc0 100644 --- a/drivers/net/wireless/wl12xx/wl1251_rx.c +++ b/drivers/net/wireless/wl12xx/wl1251_rx.c @@ -126,7 +126,7 @@ static void wl1251_rx_body(struct wl1251 *wl, if (wl->rx_current_buffer) rx_packet_ring_addr += wl->data_path->rx_packet_ring_chunk_size; - skb = dev_alloc_skb(length); + skb = __dev_alloc_skb(length, GFP_KERNEL); if (!skb) { wl1251_error("Couldn't allocate RX frame"); return; diff --git a/drivers/net/wireless/wl12xx/wl1251_tx.c b/drivers/net/wireless/wl12xx/wl1251_tx.c index f85970615849..c8223185efd2 100644 --- a/drivers/net/wireless/wl12xx/wl1251_tx.c +++ b/drivers/net/wireless/wl12xx/wl1251_tx.c @@ -167,8 +167,7 @@ static int wl1251_tx_fill_hdr(struct wl1251 *wl, struct sk_buff *skb, tx_hdr->expiry_time = cpu_to_le32(1 << 16); tx_hdr->id = id; - /* FIXME: how to get the correct queue id? */ - tx_hdr->xmit_queue = 0; + tx_hdr->xmit_queue = wl1251_tx_get_queue(skb_get_queue_mapping(skb)); wl1251_tx_control(tx_hdr, control, fc); wl1251_tx_frag_block_num(tx_hdr); @@ -220,6 +219,7 @@ static int wl1251_tx_send_packet(struct wl1251 *wl, struct sk_buff *skb, /* align the buffer on a 4-byte boundary */ skb_reserve(skb, offset); memmove(skb->data, src, skb->len); + tx_hdr = (struct tx_double_buffer_desc *) skb->data; } else { wl1251_info("No handler, fixme!"); return -EINVAL; @@ -237,8 +237,9 @@ static int wl1251_tx_send_packet(struct wl1251 *wl, struct sk_buff *skb, wl1251_mem_write(wl, addr, skb->data, len); - wl1251_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u rate 0x%x", - tx_hdr->id, skb, tx_hdr->length, tx_hdr->rate); + wl1251_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u rate 0x%x " + "queue %d", tx_hdr->id, skb, tx_hdr->length, + tx_hdr->rate, tx_hdr->xmit_queue); return 0; } diff --git a/drivers/net/wireless/wl12xx/wl1251_tx.h b/drivers/net/wireless/wl12xx/wl1251_tx.h index 7c1c1665c810..55856c6bb97a 100644 --- a/drivers/net/wireless/wl12xx/wl1251_tx.h +++ b/drivers/net/wireless/wl12xx/wl1251_tx.h @@ -26,6 +26,7 @@ #define __WL1251_TX_H__ #include <linux/bitops.h> +#include "wl1251_acx.h" /* * @@ -209,6 +210,22 @@ struct tx_result { u8 done_2; } __attribute__ ((packed)); +static inline int wl1251_tx_get_queue(int queue) +{ + switch (queue) { + case 0: + return QOS_AC_VO; + case 1: + return QOS_AC_VI; + case 2: + return QOS_AC_BE; + case 3: + return QOS_AC_BK; + default: + return QOS_AC_BE; + } +} + void wl1251_tx_work(struct work_struct *work); void wl1251_tx_complete(struct wl1251 *wl); void wl1251_tx_flush(struct wl1251 *wl); diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index 94359b1a861f..97ea5096bc8c 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -43,7 +43,7 @@ enum { DEBUG_SPI = BIT(1), DEBUG_BOOT = BIT(2), DEBUG_MAILBOX = BIT(3), - DEBUG_NETLINK = BIT(4), + DEBUG_TESTMODE = BIT(4), DEBUG_EVENT = BIT(5), DEBUG_TX = BIT(6), DEBUG_RX = BIT(7), @@ -107,11 +107,36 @@ enum { CFG_RX_CTL_EN | CFG_RX_BCN_EN | \ CFG_RX_AUTH_EN | CFG_RX_ASSOC_EN) -#define WL1271_DEFAULT_BASIC_RATE_SET (CONF_TX_RATE_MASK_ALL) - #define WL1271_FW_NAME "wl1271-fw.bin" #define WL1271_NVS_NAME "wl1271-nvs.bin" +/* NVS data structure */ +#define WL1271_NVS_SECTION_SIZE 468 + +#define WL1271_NVS_GENERAL_PARAMS_SIZE 57 +#define WL1271_NVS_GENERAL_PARAMS_SIZE_PADDED \ + (WL1271_NVS_GENERAL_PARAMS_SIZE + 1) +#define WL1271_NVS_STAT_RADIO_PARAMS_SIZE 17 +#define WL1271_NVS_STAT_RADIO_PARAMS_SIZE_PADDED \ + (WL1271_NVS_STAT_RADIO_PARAMS_SIZE + 1) +#define WL1271_NVS_DYN_RADIO_PARAMS_SIZE 65 +#define WL1271_NVS_DYN_RADIO_PARAMS_SIZE_PADDED \ + (WL1271_NVS_DYN_RADIO_PARAMS_SIZE + 1) +#define WL1271_NVS_FEM_COUNT 2 +#define WL1271_NVS_INI_SPARE_SIZE 124 + +struct wl1271_nvs_file { + /* NVS section */ + u8 nvs[WL1271_NVS_SECTION_SIZE]; + + /* INI section */ + u8 general_params[WL1271_NVS_GENERAL_PARAMS_SIZE_PADDED]; + u8 stat_radio_params[WL1271_NVS_STAT_RADIO_PARAMS_SIZE_PADDED]; + u8 dyn_radio_params[WL1271_NVS_FEM_COUNT] + [WL1271_NVS_DYN_RADIO_PARAMS_SIZE_PADDED]; + u8 ini_spare[WL1271_NVS_INI_SPARE_SIZE]; +} __attribute__ ((packed)); + /* * Enable/disable 802.11a support for WL1273 */ @@ -276,6 +301,7 @@ struct wl1271_debugfs { struct dentry *retry_count; struct dentry *excessive_retries; + struct dentry *gpio_power; }; #define NUM_TX_QUEUES 4 @@ -322,6 +348,17 @@ 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_SCANNING (5) +#define WL1271_FLAG_IN_ELP (6) +#define WL1271_FLAG_PSM (7) +#define WL1271_FLAG_PSM_REQUESTED (8) + unsigned long flags; + struct wl1271_partition_set part; struct wl1271_chip chip; @@ -331,8 +368,7 @@ struct wl1271 { u8 *fw; size_t fw_len; - u8 *nvs; - size_t nvs_len; + struct wl1271_nvs_file *nvs; u8 bssid[ETH_ALEN]; u8 mac_addr[ETH_ALEN]; @@ -359,7 +395,6 @@ struct wl1271 { /* Frames scheduled for transmission, not handled yet */ struct sk_buff_head tx_queue; - bool tx_queue_stopped; struct work_struct tx_work; @@ -387,14 +422,15 @@ struct wl1271 { u32 mbox_ptr[2]; /* Are we currently scanning */ - bool scanning; struct wl1271_scan scan; /* Our association ID */ u16 aid; /* currently configured rate set */ + u32 sta_rate_set; u32 basic_rate_set; + u32 rate_set; /* The current band */ enum ieee80211_band band; @@ -405,18 +441,9 @@ struct wl1271 { unsigned int rx_config; unsigned int rx_filter; - /* is firmware in elp mode */ - bool elp; - struct completion *elp_compl; struct delayed_work elp_work; - /* we can be in psm, but not in elp, we have to differentiate */ - bool psm; - - /* PSM mode requested */ - bool psm_requested; - /* retry counter for PSM entries */ u8 psm_entry_retry; @@ -435,9 +462,6 @@ struct wl1271 { struct ieee80211_vif *vif; - /* Used for a workaround to send disconnect before rejoining */ - bool joined; - /* Current chipset configuration */ struct conf_drv_settings conf; @@ -455,11 +479,14 @@ int wl1271_plt_stop(struct wl1271 *wl); #define WL1271_TX_QUEUE_MAX_LENGTH 20 -/* WL1271 needs a 200ms sleep after power on */ +/* 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 miliseconds */ #define WL1271_POWER_ON_SLEEP 200 /* in miliseconds */ static inline bool wl1271_11a_enabled(void) { + /* FIXME: this could be determined based on the NVS-INI file */ #ifdef WL1271_80211A_ENABLED return true; #else diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c index 5cc89bbdac7a..60f10dce4800 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.c +++ b/drivers/net/wireless/wl12xx/wl1271_acx.c @@ -390,6 +390,35 @@ out: return ret; } +int wl1271_acx_dco_itrim_params(struct wl1271 *wl) +{ + struct acx_dco_itrim_params *dco; + struct conf_itrim_settings *c = &wl->conf.itrim; + int ret; + + wl1271_debug(DEBUG_ACX, "acx dco itrim parameters"); + + dco = kzalloc(sizeof(*dco), GFP_KERNEL); + if (!dco) { + ret = -ENOMEM; + goto out; + } + + dco->enable = c->enable; + dco->timeout = cpu_to_le32(c->timeout); + + ret = wl1271_cmd_configure(wl, ACX_SET_DCO_ITRIM_PARAMS, + dco, sizeof(*dco)); + if (ret < 0) { + wl1271_warning("failed to set dco itrim parameters: %d", ret); + goto out; + } + +out: + kfree(dco); + return ret; +} + int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter) { struct acx_beacon_filter_option *beacon_filter = NULL; @@ -758,10 +787,11 @@ int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats) return 0; } -int wl1271_acx_rate_policies(struct wl1271 *wl, u32 enabled_rates) +int wl1271_acx_rate_policies(struct wl1271 *wl) { struct acx_rate_policy *acx; struct conf_tx_rate_class *c = &wl->conf.tx.rc_conf; + int idx = 0; int ret = 0; wl1271_debug(DEBUG_ACX, "acx rate policies"); @@ -773,12 +803,21 @@ int wl1271_acx_rate_policies(struct wl1271 *wl, u32 enabled_rates) goto out; } - /* configure one default (one-size-fits-all) rate class */ - acx->rate_class_cnt = cpu_to_le32(1); - acx->rate_class[0].enabled_rates = cpu_to_le32(enabled_rates); - acx->rate_class[0].short_retry_limit = c->short_retry_limit; - acx->rate_class[0].long_retry_limit = c->long_retry_limit; - acx->rate_class[0].aflags = c->aflags; + /* configure one basic rate class */ + idx = ACX_TX_BASIC_RATE; + acx->rate_class[idx].enabled_rates = cpu_to_le32(wl->basic_rate_set); + acx->rate_class[idx].short_retry_limit = c->short_retry_limit; + acx->rate_class[idx].long_retry_limit = c->long_retry_limit; + acx->rate_class[idx].aflags = c->aflags; + + /* configure one AP supported rate class */ + idx = ACX_TX_AP_FULL_RATE; + acx->rate_class[idx].enabled_rates = cpu_to_le32(wl->rate_set); + acx->rate_class[idx].short_retry_limit = c->short_retry_limit; + acx->rate_class[idx].long_retry_limit = c->long_retry_limit; + acx->rate_class[idx].aflags = c->aflags; + + acx->rate_class_cnt = cpu_to_le32(ACX_TX_RATE_POLICY_CNT); ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx)); if (ret < 0) { @@ -791,12 +830,14 @@ out: return ret; } -int wl1271_acx_ac_cfg(struct wl1271 *wl) +int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max, + u8 aifsn, u16 txop) { struct acx_ac_cfg *acx; - int i, ret = 0; + int ret = 0; - wl1271_debug(DEBUG_ACX, "acx access category config"); + wl1271_debug(DEBUG_ACX, "acx ac cfg %d cw_ming %d cw_max %d " + "aifs %d txop %d", ac, cw_min, cw_max, aifsn, txop); acx = kzalloc(sizeof(*acx), GFP_KERNEL); @@ -805,21 +846,16 @@ int wl1271_acx_ac_cfg(struct wl1271 *wl) goto out; } - for (i = 0; i < wl->conf.tx.ac_conf_count; i++) { - struct conf_tx_ac_category *c = &(wl->conf.tx.ac_conf[i]); - acx->ac = c->ac; - acx->cw_min = c->cw_min; - acx->cw_max = cpu_to_le16(c->cw_max); - acx->aifsn = c->aifsn; - acx->reserved = 0; - acx->tx_op_limit = cpu_to_le16(c->tx_op_limit); + acx->ac = ac; + acx->cw_min = cw_min; + acx->cw_max = cpu_to_le16(cw_max); + acx->aifsn = aifsn; + acx->tx_op_limit = cpu_to_le16(txop); - ret = wl1271_cmd_configure(wl, ACX_AC_CFG, acx, sizeof(*acx)); - if (ret < 0) { - wl1271_warning("Setting of access category " - "config: %d", ret); - goto out; - } + ret = wl1271_cmd_configure(wl, ACX_AC_CFG, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx ac cfg failed: %d", ret); + goto out; } out: @@ -827,10 +863,12 @@ out: return ret; } -int wl1271_acx_tid_cfg(struct wl1271 *wl) +int wl1271_acx_tid_cfg(struct wl1271 *wl, u8 queue_id, u8 channel_type, + u8 tsid, u8 ps_scheme, u8 ack_policy, + u32 apsd_conf0, u32 apsd_conf1) { struct acx_tid_config *acx; - int i, ret = 0; + int ret = 0; wl1271_debug(DEBUG_ACX, "acx tid config"); @@ -841,21 +879,18 @@ int wl1271_acx_tid_cfg(struct wl1271 *wl) goto out; } - for (i = 0; i < wl->conf.tx.tid_conf_count; i++) { - struct conf_tx_tid *c = &(wl->conf.tx.tid_conf[i]); - acx->queue_id = c->queue_id; - acx->channel_type = c->channel_type; - acx->tsid = c->tsid; - acx->ps_scheme = c->ps_scheme; - acx->ack_policy = c->ack_policy; - acx->apsd_conf[0] = cpu_to_le32(c->apsd_conf[0]); - acx->apsd_conf[1] = cpu_to_le32(c->apsd_conf[1]); + acx->queue_id = queue_id; + acx->channel_type = channel_type; + acx->tsid = tsid; + acx->ps_scheme = ps_scheme; + acx->ack_policy = ack_policy; + acx->apsd_conf[0] = cpu_to_le32(apsd_conf0); + acx->apsd_conf[1] = cpu_to_le32(apsd_conf1); - ret = wl1271_cmd_configure(wl, ACX_TID_CFG, acx, sizeof(*acx)); - if (ret < 0) { - wl1271_warning("Setting of tid config failed: %d", ret); - goto out; - } + ret = wl1271_cmd_configure(wl, ACX_TID_CFG, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("Setting of tid config failed: %d", ret); + goto out; } out: @@ -1012,59 +1047,6 @@ out: return ret; } -int wl1271_acx_smart_reflex(struct wl1271 *wl) -{ - struct acx_smart_reflex_state *sr_state = NULL; - struct acx_smart_reflex_config_params *sr_param = NULL; - int i, ret; - - wl1271_debug(DEBUG_ACX, "acx smart reflex"); - - sr_param = kzalloc(sizeof(*sr_param), GFP_KERNEL); - if (!sr_param) { - ret = -ENOMEM; - goto out; - } - - for (i = 0; i < CONF_SR_ERR_TBL_COUNT; i++) { - struct conf_mart_reflex_err_table *e = - &(wl->conf.init.sr_err_tbl[i]); - - sr_param->error_table[i].len = e->len; - sr_param->error_table[i].upper_limit = e->upper_limit; - memcpy(sr_param->error_table[i].values, e->values, e->len); - } - - ret = wl1271_cmd_configure(wl, ACX_SET_SMART_REFLEX_PARAMS, - sr_param, sizeof(*sr_param)); - if (ret < 0) { - wl1271_warning("failed to set smart reflex params: %d", ret); - goto out; - } - - sr_state = kzalloc(sizeof(*sr_state), GFP_KERNEL); - if (!sr_state) { - ret = -ENOMEM; - goto out; - } - - /* enable smart reflex */ - sr_state->enable = wl->conf.init.sr_enable; - - ret = wl1271_cmd_configure(wl, ACX_SET_SMART_REFLEX_STATE, - sr_state, sizeof(*sr_state)); - if (ret < 0) { - wl1271_warning("failed to set smart reflex params: %d", ret); - goto out; - } - -out: - kfree(sr_state); - kfree(sr_param); - return ret; - -} - int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable) { struct wl1271_acx_bet_enable *acx = NULL; @@ -1132,3 +1114,31 @@ out: kfree(acx); return ret; } + +int wl1271_acx_pm_config(struct wl1271 *wl) +{ + struct wl1271_acx_pm_config *acx = NULL; + struct conf_pm_config_settings *c = &wl->conf.pm_config; + int ret = 0; + + wl1271_debug(DEBUG_ACX, "acx pm config"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->host_clk_settling_time = cpu_to_le32(c->host_clk_settling_time); + acx->host_fast_wakeup_support = c->host_fast_wakeup_support; + + ret = wl1271_cmd_configure(wl, ACX_PM_CONFIG, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx pm config failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h index 2ce0a8128542..aeccc98581eb 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.h +++ b/drivers/net/wireless/wl12xx/wl1271_acx.h @@ -2,7 +2,7 @@ * This file is part of wl1271 * * Copyright (C) 1998-2009 Texas Instruments. All rights reserved. - * Copyright (C) 2008-2009 Nokia Corporation + * Copyright (C) 2008-2010 Nokia Corporation * * Contact: Luciano Coelho <luciano.coelho@nokia.com> * @@ -348,7 +348,7 @@ struct acx_beacon_filter_option { * ACXBeaconFilterEntry (not 221) * Byte Offset Size (Bytes) Definition * =========== ============ ========== - * 0 1 IE identifier + * 0 1 IE identifier * 1 1 Treatment bit mask * * ACXBeaconFilterEntry (221) @@ -381,8 +381,8 @@ struct acx_beacon_filter_ie_table { struct acx_header header; u8 num_ie; - u8 table[BEACON_FILTER_TABLE_MAX_SIZE]; u8 pad[3]; + u8 table[BEACON_FILTER_TABLE_MAX_SIZE]; } __attribute__ ((packed)); struct acx_conn_monit_params { @@ -415,23 +415,12 @@ struct acx_bt_wlan_coex { u8 pad[3]; } __attribute__ ((packed)); -struct acx_smart_reflex_state { +struct acx_dco_itrim_params { struct acx_header header; u8 enable; u8 padding[3]; -} __attribute__ ((packed)); - -struct smart_reflex_err_table { - u8 len; - s8 upper_limit; - s8 values[14]; -} __attribute__ ((packed)); - -struct acx_smart_reflex_config_params { - struct acx_header header; - - struct smart_reflex_err_table error_table[3]; + __le32 timeout; } __attribute__ ((packed)); #define PTA_ANTENNA_TYPE_DEF (0) @@ -837,6 +826,9 @@ struct acx_rate_class { u8 reserved; }; +#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_header header; @@ -877,8 +869,8 @@ struct acx_tx_config_options { __le16 tx_compl_threshold; /* number of packets */ } __attribute__ ((packed)); -#define ACX_RX_MEM_BLOCKS 64 -#define ACX_TX_MIN_MEM_BLOCKS 64 +#define ACX_RX_MEM_BLOCKS 70 +#define ACX_TX_MIN_MEM_BLOCKS 40 #define ACX_TX_DESCRIPTORS 32 #define ACX_NUM_SSID_PROFILES 1 @@ -969,6 +961,13 @@ struct wl1271_acx_arp_filter { used. */ } __attribute__((packed)); +struct wl1271_acx_pm_config { + struct acx_header header; + + __le32 host_clk_settling_time; + u8 host_fast_wakeup_support; + u8 padding[3]; +} __attribute__ ((packed)); enum { ACX_WAKE_UP_CONDITIONS = 0x0002, @@ -1027,13 +1026,13 @@ enum { ACX_HT_BSS_OPERATION = 0x0058, ACX_COEX_ACTIVITY = 0x0059, ACX_SET_SMART_REFLEX_DEBUG = 0x005A, - ACX_SET_SMART_REFLEX_STATE = 0x005B, - ACX_SET_SMART_REFLEX_PARAMS = 0x005F, + ACX_SET_DCO_ITRIM_PARAMS = 0x0061, DOT11_RX_MSDU_LIFE_TIME = 0x1004, DOT11_CUR_TX_PWR = 0x100D, DOT11_RX_DOT11_MODE = 0x1012, DOT11_RTS_THRESHOLD = 0x1013, DOT11_GROUP_ADDRESS_TBL = 0x1014, + ACX_PM_CONFIG = 0x1016, MAX_DOT11_IE = DOT11_GROUP_ADDRESS_TBL, @@ -1056,6 +1055,7 @@ int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable, void *mc_list, u32 mc_list_len); int wl1271_acx_service_period_timeout(struct wl1271 *wl); int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold); +int wl1271_acx_dco_itrim_params(struct wl1271 *wl); int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter); int wl1271_acx_beacon_filter_table(struct wl1271 *wl); int wl1271_acx_conn_monit_params(struct wl1271 *wl); @@ -1069,9 +1069,12 @@ 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, u32 enabled_rates); -int wl1271_acx_ac_cfg(struct wl1271 *wl); -int wl1271_acx_tid_cfg(struct wl1271 *wl); +int wl1271_acx_rate_policies(struct wl1271 *wl); +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, + u8 tsid, u8 ps_scheme, u8 ack_policy, + u32 apsd_conf0, u32 apsd_conf1); int wl1271_acx_frag_threshold(struct wl1271 *wl); int wl1271_acx_tx_config_options(struct wl1271 *wl); int wl1271_acx_mem_cfg(struct wl1271 *wl); @@ -1081,5 +1084,6 @@ int wl1271_acx_smart_reflex(struct wl1271 *wl); int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable); int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, u8 *address, u8 version); +int wl1271_acx_pm_config(struct wl1271 *wl); #endif /* __WL1271_ACX_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.c b/drivers/net/wireless/wl12xx/wl1271_boot.c index b7c96454cca3..2be76ee42bb9 100644 --- a/drivers/net/wireless/wl12xx/wl1271_boot.c +++ b/drivers/net/wireless/wl12xx/wl1271_boot.c @@ -27,6 +27,7 @@ #include "wl1271_reg.h" #include "wl1271_boot.h" #include "wl1271_spi.h" +#include "wl1271_io.h" #include "wl1271_event.h" static struct wl1271_partition_set part_table[PART_TABLE_LEN] = { @@ -93,19 +94,19 @@ static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag) u32 cpu_ctrl; /* 10.5.0 run the firmware (I) */ - cpu_ctrl = wl1271_spi_read32(wl, ACX_REG_ECPU_CONTROL); + cpu_ctrl = wl1271_read32(wl, ACX_REG_ECPU_CONTROL); /* 10.5.1 run the firmware (II) */ cpu_ctrl |= flag; - wl1271_spi_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl); + wl1271_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl); } static void wl1271_boot_fw_version(struct wl1271 *wl) { struct wl1271_static_data static_data; - wl1271_spi_read(wl, wl->cmd_box_addr, - &static_data, sizeof(static_data), false); + 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)); @@ -164,7 +165,7 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, memcpy(chunk, p, CHUNK_SIZE); wl1271_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x", p, addr); - wl1271_spi_write(wl, addr, chunk, CHUNK_SIZE, false); + wl1271_write(wl, addr, chunk, CHUNK_SIZE, false); chunk_num++; } @@ -175,7 +176,7 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, memcpy(chunk, p, fw_data_len % CHUNK_SIZE); wl1271_debug(DEBUG_BOOT, "uploading fw last chunk (%zd B) 0x%p to 0x%x", fw_data_len % CHUNK_SIZE, p, addr); - wl1271_spi_write(wl, addr, chunk, fw_data_len % CHUNK_SIZE, false); + wl1271_write(wl, addr, chunk, fw_data_len % CHUNK_SIZE, false); kfree(chunk); return 0; @@ -219,23 +220,14 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) size_t nvs_len, burst_len; int i; u32 dest_addr, val; - u8 *nvs_ptr, *nvs, *nvs_aligned; + u8 *nvs_ptr, *nvs_aligned; - nvs = wl->nvs; - if (nvs == NULL) + if (wl->nvs == NULL) return -ENODEV; - nvs_ptr = nvs; - - nvs_len = wl->nvs_len; - - /* Update the device MAC address into the nvs */ - nvs[11] = wl->mac_addr[0]; - nvs[10] = wl->mac_addr[1]; - nvs[6] = wl->mac_addr[2]; - nvs[5] = wl->mac_addr[3]; - nvs[4] = wl->mac_addr[4]; - nvs[3] = wl->mac_addr[5]; + /* only the first part of the NVS needs to be uploaded */ + nvs_len = sizeof(wl->nvs->nvs); + nvs_ptr = (u8 *)wl->nvs->nvs; /* * Layout before the actual NVS tables: @@ -265,7 +257,7 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) wl1271_debug(DEBUG_BOOT, "nvs burst write 0x%x: 0x%x", dest_addr, val); - wl1271_spi_write32(wl, dest_addr, val); + wl1271_write32(wl, dest_addr, val); nvs_ptr += 4; dest_addr += 4; @@ -277,7 +269,7 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) * is 7 bytes further. */ nvs_ptr += 7; - nvs_len -= nvs_ptr - nvs; + nvs_len -= nvs_ptr - (u8 *)wl->nvs->nvs; nvs_len = ALIGN(nvs_len, 4); /* FIXME: The driver sets the partition here, but this is not needed, @@ -286,15 +278,20 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) wl1271_set_partition(wl, &part_table[PART_WORK]); /* Copy the NVS tables to a new block to ensure alignment */ - nvs_aligned = kmemdup(nvs_ptr, nvs_len, GFP_KERNEL); - if (!nvs_aligned) - return -ENOMEM; + /* FIXME: We jump 3 more bytes before uploading the NVS. It seems + that our NVS files have three extra zeros here. I'm not sure whether + the problem is in our NVS generation or we should really jumpt these + 3 bytes here */ + nvs_ptr += 3; + + nvs_aligned = kmemdup(nvs_ptr, nvs_len, GFP_KERNEL); if + (!nvs_aligned) return -ENOMEM; /* And finally we upload the NVS tables */ /* FIXME: In wl1271, we upload everything at once. No endianness handling needed here?! The ref driver doesn't do anything about it at this point */ - wl1271_spi_write(wl, CMD_MBOX_ADDRESS, nvs_aligned, nvs_len, false); + wl1271_write(wl, CMD_MBOX_ADDRESS, nvs_aligned, nvs_len, false); kfree(nvs_aligned); return 0; @@ -303,9 +300,9 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) static void wl1271_boot_enable_interrupts(struct wl1271 *wl) { enable_irq(wl->irq); - wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, - WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK)); - wl1271_spi_write32(wl, HI_CFG, HI_CFG_DEF_VAL); + wl1271_write32(wl, ACX_REG_INTERRUPT_MASK, + WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK)); + wl1271_write32(wl, HI_CFG, HI_CFG_DEF_VAL); } static int wl1271_boot_soft_reset(struct wl1271 *wl) @@ -314,13 +311,12 @@ static int wl1271_boot_soft_reset(struct wl1271 *wl) u32 boot_data; /* perform soft reset */ - wl1271_spi_write32(wl, ACX_REG_SLV_SOFT_RESET, - ACX_SLV_SOFT_RESET_BIT); + wl1271_write32(wl, ACX_REG_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT); /* SOFT_RESET is self clearing */ timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME); while (1) { - boot_data = wl1271_spi_read32(wl, ACX_REG_SLV_SOFT_RESET); + boot_data = wl1271_read32(wl, ACX_REG_SLV_SOFT_RESET); wl1271_debug(DEBUG_BOOT, "soft reset bootdata 0x%x", boot_data); if ((boot_data & ACX_SLV_SOFT_RESET_BIT) == 0) break; @@ -336,10 +332,10 @@ static int wl1271_boot_soft_reset(struct wl1271 *wl) } /* disable Rx/Tx */ - wl1271_spi_write32(wl, ENABLE, 0x0); + wl1271_write32(wl, ENABLE, 0x0); /* disable auto calibration on start*/ - wl1271_spi_write32(wl, SPARE_A2, 0xffff); + wl1271_write32(wl, SPARE_A2, 0xffff); return 0; } @@ -351,7 +347,7 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) wl1271_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT); - chip_id = wl1271_spi_read32(wl, CHIP_ID_B); + chip_id = wl1271_read32(wl, CHIP_ID_B); wl1271_debug(DEBUG_BOOT, "chip id after firmware boot: 0x%x", chip_id); @@ -364,8 +360,7 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) loop = 0; while (loop++ < INIT_LOOP) { udelay(INIT_LOOP_DELAY); - interrupt = wl1271_spi_read32(wl, - ACX_REG_INTERRUPT_NO_CLEAR); + interrupt = wl1271_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); if (interrupt == 0xffffffff) { wl1271_error("error reading hardware complete " @@ -374,8 +369,8 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) } /* check that ACX_INTR_INIT_COMPLETE is enabled */ else if (interrupt & WL1271_ACX_INTR_INIT_COMPLETE) { - wl1271_spi_write32(wl, ACX_REG_INTERRUPT_ACK, - WL1271_ACX_INTR_INIT_COMPLETE); + wl1271_write32(wl, ACX_REG_INTERRUPT_ACK, + WL1271_ACX_INTR_INIT_COMPLETE); break; } } @@ -387,10 +382,10 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) } /* get hardware config command mail box */ - wl->cmd_box_addr = wl1271_spi_read32(wl, REG_COMMAND_MAILBOX_PTR); + wl->cmd_box_addr = wl1271_read32(wl, REG_COMMAND_MAILBOX_PTR); /* get hardware config event mail box */ - wl->event_box_addr = wl1271_spi_read32(wl, REG_EVENT_MAILBOX_PTR); + wl->event_box_addr = wl1271_read32(wl, REG_EVENT_MAILBOX_PTR); /* set the working partition to its "running" mode offset */ wl1271_set_partition(wl, &part_table[PART_WORK]); @@ -463,9 +458,9 @@ int wl1271_boot(struct wl1271 *wl) wl1271_top_reg_write(wl, OCP_REG_CLK_POLARITY, val); } - wl1271_spi_write32(wl, PLL_PARAMETERS, clk); + wl1271_write32(wl, PLL_PARAMETERS, clk); - pause = wl1271_spi_read32(wl, PLL_PARAMETERS); + pause = wl1271_read32(wl, PLL_PARAMETERS); wl1271_debug(DEBUG_BOOT, "pause1 0x%x", pause); @@ -474,10 +469,10 @@ int wl1271_boot(struct wl1271 *wl) * 0x3ff (magic number ). How does * this work?! */ pause |= WU_COUNTER_PAUSE_VAL; - wl1271_spi_write32(wl, WU_COUNTER_PAUSE, pause); + wl1271_write32(wl, WU_COUNTER_PAUSE, pause); /* Continue the ELP wake up sequence */ - wl1271_spi_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); + wl1271_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); udelay(500); wl1271_set_partition(wl, &part_table[PART_DRPW]); @@ -487,18 +482,18 @@ int wl1271_boot(struct wl1271 *wl) before taking DRPw out of reset */ wl1271_debug(DEBUG_BOOT, "DRPW_SCRATCH_START %08x", DRPW_SCRATCH_START); - clk = wl1271_spi_read32(wl, DRPW_SCRATCH_START); + clk = wl1271_read32(wl, DRPW_SCRATCH_START); wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk); /* 2 */ clk |= (REF_CLOCK << 1) << 4; - wl1271_spi_write32(wl, DRPW_SCRATCH_START, clk); + wl1271_write32(wl, DRPW_SCRATCH_START, clk); wl1271_set_partition(wl, &part_table[PART_WORK]); /* Disable interrupts */ - wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); + wl1271_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); ret = wl1271_boot_soft_reset(wl); if (ret < 0) @@ -513,23 +508,22 @@ int wl1271_boot(struct wl1271 *wl) * ACX_EEPROMLESS_IND_REG */ wl1271_debug(DEBUG_BOOT, "ACX_EEPROMLESS_IND_REG"); - wl1271_spi_write32(wl, ACX_EEPROMLESS_IND_REG, - ACX_EEPROMLESS_IND_REG); + wl1271_write32(wl, ACX_EEPROMLESS_IND_REG, ACX_EEPROMLESS_IND_REG); - tmp = wl1271_spi_read32(wl, CHIP_ID_B); + tmp = wl1271_read32(wl, CHIP_ID_B); wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp); /* 6. read the EEPROM parameters */ - tmp = wl1271_spi_read32(wl, SCR_PAD2); + tmp = wl1271_read32(wl, SCR_PAD2); ret = wl1271_boot_write_irq_polarity(wl); if (ret < 0) goto out; /* FIXME: Need to check whether this is really what we want */ - wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, - WL1271_ACX_ALL_EVENTS_VECTOR); + wl1271_write32(wl, ACX_REG_INTERRUPT_MASK, + WL1271_ACX_ALL_EVENTS_VECTOR); /* WL1271: The reference driver skips steps 7 to 10 (jumps directly * to upload_fw) */ diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c index c3385b3d246c..36a64e06f290 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.c +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c @@ -30,6 +30,7 @@ #include "wl1271.h" #include "wl1271_reg.h" #include "wl1271_spi.h" +#include "wl1271_io.h" #include "wl1271_acx.h" #include "wl12xx_80211.h" #include "wl1271_cmd.h" @@ -57,13 +58,13 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, WARN_ON(len % 4 != 0); - wl1271_spi_write(wl, wl->cmd_box_addr, buf, len, false); + wl1271_write(wl, wl->cmd_box_addr, buf, len, false); - wl1271_spi_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD); + wl1271_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD); timeout = jiffies + msecs_to_jiffies(WL1271_COMMAND_TIMEOUT); - intr = wl1271_spi_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); + intr = wl1271_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); while (!(intr & WL1271_ACX_INTR_CMD_COMPLETE)) { if (time_after(jiffies, timeout)) { wl1271_error("command complete timeout"); @@ -73,13 +74,13 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, msleep(1); - intr = wl1271_spi_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); + intr = wl1271_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); } /* read back the status code of the command */ if (res_len == 0) res_len = sizeof(struct wl1271_cmd_header); - wl1271_spi_read(wl, wl->cmd_box_addr, cmd, res_len, false); + wl1271_read(wl, wl->cmd_box_addr, cmd, res_len, false); status = le16_to_cpu(cmd->status); if (status != CMD_STATUS_SUCCESS) { @@ -87,8 +88,8 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, ret = -EIO; } - wl1271_spi_write32(wl, ACX_REG_INTERRUPT_ACK, - WL1271_ACX_INTR_CMD_COMPLETE); + wl1271_write32(wl, ACX_REG_INTERRUPT_ACK, + WL1271_ACX_INTR_CMD_COMPLETE); out: return ret; @@ -191,23 +192,19 @@ static int wl1271_cmd_cal(struct wl1271 *wl) int wl1271_cmd_general_parms(struct wl1271 *wl) { struct wl1271_general_parms_cmd *gen_parms; - struct conf_general_parms *g = &wl->conf.init.genparam; int ret; + if (!wl->nvs) + return -ENODEV; + gen_parms = kzalloc(sizeof(*gen_parms), GFP_KERNEL); if (!gen_parms) return -ENOMEM; gen_parms->test.id = TEST_CMD_INI_FILE_GENERAL_PARAM; - gen_parms->ref_clk = g->ref_clk; - gen_parms->settling_time = g->settling_time; - gen_parms->clk_valid_on_wakeup = g->clk_valid_on_wakeup; - gen_parms->dc2dcmode = g->dc2dcmode; - gen_parms->single_dual_band = g->single_dual_band; - gen_parms->tx_bip_fem_autodetect = g->tx_bip_fem_autodetect; - gen_parms->tx_bip_fem_manufacturer = g->tx_bip_fem_manufacturer; - gen_parms->settings = g->settings; + memcpy(gen_parms->params, wl->nvs->general_params, + WL1271_NVS_GENERAL_PARAMS_SIZE); ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), 0); if (ret < 0) @@ -220,8 +217,11 @@ int wl1271_cmd_general_parms(struct wl1271 *wl) int wl1271_cmd_radio_parms(struct wl1271 *wl) { struct wl1271_radio_parms_cmd *radio_parms; - struct conf_radio_parms *r = &wl->conf.init.radioparam; - int i, ret; + struct conf_radio_parms *rparam = &wl->conf.init.radioparam; + int ret; + + if (!wl->nvs) + return -ENODEV; radio_parms = kzalloc(sizeof(*radio_parms), GFP_KERNEL); if (!radio_parms) @@ -229,60 +229,13 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl) radio_parms->test.id = TEST_CMD_INI_FILE_RADIO_PARAM; - /* Static radio parameters */ - radio_parms->rx_trace_loss = r->rx_trace_loss; - radio_parms->tx_trace_loss = r->tx_trace_loss; - memcpy(radio_parms->rx_rssi_and_proc_compens, - r->rx_rssi_and_proc_compens, - CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE); - - memcpy(radio_parms->rx_trace_loss_5, r->rx_trace_loss_5, - CONF_NUMBER_OF_SUB_BANDS_5); - memcpy(radio_parms->tx_trace_loss_5, r->tx_trace_loss_5, - CONF_NUMBER_OF_SUB_BANDS_5); - memcpy(radio_parms->rx_rssi_and_proc_compens_5, - r->rx_rssi_and_proc_compens_5, - CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE); - - /* Dynamic radio parameters */ - radio_parms->tx_ref_pd_voltage = cpu_to_le16(r->tx_ref_pd_voltage); - radio_parms->tx_ref_power = r->tx_ref_power; - radio_parms->tx_offset_db = r->tx_offset_db; - - memcpy(radio_parms->tx_rate_limits_normal, r->tx_rate_limits_normal, - CONF_NUMBER_OF_RATE_GROUPS); - memcpy(radio_parms->tx_rate_limits_degraded, r->tx_rate_limits_degraded, - CONF_NUMBER_OF_RATE_GROUPS); - - memcpy(radio_parms->tx_channel_limits_11b, r->tx_channel_limits_11b, - CONF_NUMBER_OF_CHANNELS_2_4); - memcpy(radio_parms->tx_channel_limits_ofdm, r->tx_channel_limits_ofdm, - CONF_NUMBER_OF_CHANNELS_2_4); - memcpy(radio_parms->tx_pdv_rate_offsets, r->tx_pdv_rate_offsets, - CONF_NUMBER_OF_RATE_GROUPS); - memcpy(radio_parms->tx_ibias, r->tx_ibias, CONF_NUMBER_OF_RATE_GROUPS); - - radio_parms->rx_fem_insertion_loss = r->rx_fem_insertion_loss; - - for (i = 0; i < CONF_NUMBER_OF_SUB_BANDS_5; i++) - radio_parms->tx_ref_pd_voltage_5[i] = - cpu_to_le16(r->tx_ref_pd_voltage_5[i]); - memcpy(radio_parms->tx_ref_power_5, r->tx_ref_power_5, - CONF_NUMBER_OF_SUB_BANDS_5); - memcpy(radio_parms->tx_offset_db_5, r->tx_offset_db_5, - CONF_NUMBER_OF_SUB_BANDS_5); - memcpy(radio_parms->tx_rate_limits_normal_5, - r->tx_rate_limits_normal_5, CONF_NUMBER_OF_RATE_GROUPS); - memcpy(radio_parms->tx_rate_limits_degraded_5, - r->tx_rate_limits_degraded_5, CONF_NUMBER_OF_RATE_GROUPS); - memcpy(radio_parms->tx_channel_limits_ofdm_5, - r->tx_channel_limits_ofdm_5, CONF_NUMBER_OF_CHANNELS_5); - memcpy(radio_parms->tx_pdv_rate_offsets_5, r->tx_pdv_rate_offsets_5, - CONF_NUMBER_OF_RATE_GROUPS); - memcpy(radio_parms->tx_ibias_5, r->tx_ibias_5, - CONF_NUMBER_OF_RATE_GROUPS); - memcpy(radio_parms->rx_fem_insertion_loss_5, - r->rx_fem_insertion_loss_5, CONF_NUMBER_OF_SUB_BANDS_5); + memcpy(radio_parms->stat_radio_params, wl->nvs->stat_radio_params, + WL1271_NVS_STAT_RADIO_PARAMS_SIZE); + memcpy(radio_parms->dyn_radio_params, + wl->nvs->dyn_radio_params[rparam->fem], + WL1271_NVS_DYN_RADIO_PARAMS_SIZE); + + /* FIXME: current NVS is missing 5GHz parameters */ wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ", radio_parms, sizeof(*radio_parms)); @@ -311,19 +264,6 @@ int wl1271_cmd_join(struct wl1271 *wl) do_cal = false; } - /* FIXME: This is a workaround, because with the current stack, we - * cannot know when we have disassociated. So, if we have already - * joined, we disconnect before joining again. */ - if (wl->joined) { - ret = wl1271_cmd_disconnect(wl); - if (ret < 0) { - wl1271_error("failed to disconnect before rejoining"); - goto out; - } - - wl->joined = false; - } - join = kzalloc(sizeof(*join), GFP_KERNEL); if (!join) { ret = -ENOMEM; @@ -388,8 +328,6 @@ int wl1271_cmd_join(struct wl1271 *wl) goto out_free; } - wl->joined = true; - /* * ugly hack: we should wait for JOIN_EVENT_COMPLETE_ID but to * simplify locking we just sleep instead, for now @@ -487,7 +425,7 @@ int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len) return 0; } -int wl1271_cmd_data_path(struct wl1271 *wl, u8 channel, bool enable) +int wl1271_cmd_data_path(struct wl1271 *wl, bool enable) { struct cmd_enabledisable_path *cmd; int ret; @@ -501,7 +439,8 @@ int wl1271_cmd_data_path(struct wl1271 *wl, u8 channel, bool enable) goto out; } - cmd->channel = channel; + /* the channel here is only used for calibration, so hardcoded to 1 */ + cmd->channel = 1; if (enable) { cmd_rx = CMD_ENABLE_RX; @@ -514,29 +453,29 @@ int wl1271_cmd_data_path(struct wl1271 *wl, u8 channel, bool enable) ret = wl1271_cmd_send(wl, cmd_rx, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("rx %s cmd for channel %d failed", - enable ? "start" : "stop", channel); + enable ? "start" : "stop", cmd->channel); goto out; } wl1271_debug(DEBUG_BOOT, "rx %s cmd channel %d", - enable ? "start" : "stop", channel); + enable ? "start" : "stop", cmd->channel); ret = wl1271_cmd_send(wl, cmd_tx, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("tx %s cmd for channel %d failed", - enable ? "start" : "stop", channel); + enable ? "start" : "stop", cmd->channel); return ret; } wl1271_debug(DEBUG_BOOT, "tx %s cmd channel %d", - enable ? "start" : "stop", channel); + enable ? "start" : "stop", cmd->channel); out: kfree(cmd); return ret; } -int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode) +int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode, bool send) { struct wl1271_cmd_ps_params *ps_params = NULL; int ret = 0; @@ -557,7 +496,7 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode) } ps_params->ps_mode = ps_mode; - ps_params->send_null_data = 1; + ps_params->send_null_data = send; ps_params->retries = 5; ps_params->hang_over_period = 128; ps_params->null_data_rate = cpu_to_le32(1); /* 1 Mbps */ @@ -636,7 +575,7 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, channels = wl->hw->wiphy->bands[ieee_band]->channels; n_ch = wl->hw->wiphy->bands[ieee_band]->n_channels; - if (wl->scanning) + if (test_bit(WL1271_FLAG_SCANNING, &wl->flags)) return -EINVAL; params = kzalloc(sizeof(*params), GFP_KERNEL); @@ -711,7 +650,7 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, wl1271_dump(DEBUG_SCAN, "SCAN: ", params, sizeof(*params)); - wl->scanning = true; + set_bit(WL1271_FLAG_SCANNING, &wl->flags); if (wl1271_11a_enabled()) { wl->scan.state = band; if (band == WL1271_SCAN_BAND_DUAL) { @@ -729,7 +668,7 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, ret = wl1271_cmd_send(wl, CMD_SCAN, params, sizeof(*params), 0); if (ret < 0) { wl1271_error("SCAN failed"); - wl->scanning = false; + clear_bit(WL1271_FLAG_SCANNING, &wl->flags); goto out; } @@ -1003,7 +942,7 @@ int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_warning("could not set keys"); - goto out; + goto out; } out: diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.h b/drivers/net/wireless/wl12xx/wl1271_cmd.h index b4fa4acb9229..2dc06c73532b 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.h +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.h @@ -37,8 +37,8 @@ int wl1271_cmd_join(struct wl1271 *wl); 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, u8 channel, bool enable); -int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode); +int wl1271_cmd_data_path(struct wl1271 *wl, bool enable); +int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode, bool send); int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer, size_t len); int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, @@ -428,67 +428,24 @@ struct wl1271_general_parms_cmd { struct wl1271_cmd_test_header test; - u8 ref_clk; - u8 settling_time; - u8 clk_valid_on_wakeup; - u8 dc2dcmode; - u8 single_dual_band; - - u8 tx_bip_fem_autodetect; - u8 tx_bip_fem_manufacturer; - u8 settings; + u8 params[WL1271_NVS_GENERAL_PARAMS_SIZE]; + s8 reserved[23]; } __attribute__ ((packed)); +#define WL1271_STAT_RADIO_PARAMS_5_SIZE 29 +#define WL1271_DYN_RADIO_PARAMS_5_SIZE 104 + struct wl1271_radio_parms_cmd { struct wl1271_cmd_header header; struct wl1271_cmd_test_header test; - /* Static radio parameters */ - /* 2.4GHz */ - u8 rx_trace_loss; - u8 tx_trace_loss; - s8 rx_rssi_and_proc_compens[CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE]; - - /* 5GHz */ - u8 rx_trace_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; - u8 tx_trace_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; - s8 rx_rssi_and_proc_compens_5[CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE]; - - /* Dynamic radio parameters */ - /* 2.4GHz */ - __le16 tx_ref_pd_voltage; - s8 tx_ref_power; - s8 tx_offset_db; - - s8 tx_rate_limits_normal[CONF_NUMBER_OF_RATE_GROUPS]; - s8 tx_rate_limits_degraded[CONF_NUMBER_OF_RATE_GROUPS]; - - s8 tx_channel_limits_11b[CONF_NUMBER_OF_CHANNELS_2_4]; - s8 tx_channel_limits_ofdm[CONF_NUMBER_OF_CHANNELS_2_4]; - s8 tx_pdv_rate_offsets[CONF_NUMBER_OF_RATE_GROUPS]; - - u8 tx_ibias[CONF_NUMBER_OF_RATE_GROUPS]; - u8 rx_fem_insertion_loss; + u8 stat_radio_params[WL1271_NVS_STAT_RADIO_PARAMS_SIZE]; + u8 stat_radio_params_5[WL1271_STAT_RADIO_PARAMS_5_SIZE]; - u8 padding2; - - /* 5GHz */ - __le16 tx_ref_pd_voltage_5[CONF_NUMBER_OF_SUB_BANDS_5]; - s8 tx_ref_power_5[CONF_NUMBER_OF_SUB_BANDS_5]; - s8 tx_offset_db_5[CONF_NUMBER_OF_SUB_BANDS_5]; - - s8 tx_rate_limits_normal_5[CONF_NUMBER_OF_RATE_GROUPS]; - s8 tx_rate_limits_degraded_5[CONF_NUMBER_OF_RATE_GROUPS]; - - s8 tx_channel_limits_ofdm_5[CONF_NUMBER_OF_CHANNELS_5]; - s8 tx_pdv_rate_offsets_5[CONF_NUMBER_OF_RATE_GROUPS]; - - /* FIXME: this is inconsistent with the types for 2.4GHz */ - s8 tx_ibias_5[CONF_NUMBER_OF_RATE_GROUPS]; - s8 rx_fem_insertion_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; - - u8 padding3[2]; + u8 dyn_radio_params[WL1271_NVS_DYN_RADIO_PARAMS_SIZE]; + u8 reserved; + u8 dyn_radio_params_5[WL1271_DYN_RADIO_PARAMS_5_SIZE]; } __attribute__ ((packed)); struct wl1271_cmd_cal_channel_tune { diff --git a/drivers/net/wireless/wl12xx/wl1271_conf.h b/drivers/net/wireless/wl12xx/wl1271_conf.h index 565373ede265..6f9e75cc5640 100644 --- a/drivers/net/wireless/wl12xx/wl1271_conf.h +++ b/drivers/net/wireless/wl12xx/wl1271_conf.h @@ -258,7 +258,8 @@ struct conf_rx_settings { #define CONF_TX_MAX_RATE_CLASSES 8 #define CONF_TX_RATE_MASK_UNSPECIFIED 0 -#define CONF_TX_RATE_MASK_ALL 0x1eff +#define CONF_TX_RATE_MASK_BASIC (CONF_HW_BIT_RATE_1MBPS | \ + CONF_HW_BIT_RATE_2MBPS) #define CONF_TX_RATE_RETRY_LIMIT 10 struct conf_tx_rate_class { @@ -722,31 +723,6 @@ struct conf_conn_settings { u8 psm_entry_retries; }; -#define CONF_SR_ERR_TBL_MAX_VALUES 14 - -struct conf_mart_reflex_err_table { - /* - * Length of the error table values table. - * - * Range: 0 - CONF_SR_ERR_TBL_MAX_VALUES - */ - u8 len; - - /* - * Smart Reflex error table upper limit. - * - * Range: s8 - */ - s8 upper_limit; - - /* - * Smart Reflex error table values. - * - * Range: s8 - */ - s8 values[CONF_SR_ERR_TBL_MAX_VALUES]; -}; - enum { CONF_REF_CLK_19_2_E, CONF_REF_CLK_26_E, @@ -759,64 +735,6 @@ enum single_dual_band_enum { CONF_DUAL_BAND }; -struct conf_general_parms { - /* - * RF Reference Clock type / speed - * - * Range: CONF_REF_CLK_* - */ - u8 ref_clk; - - /* - * Settling time of the reference clock after boot. - * - * Range: u8 - */ - u8 settling_time; - - /* - * Flag defining whether clock is valid on wakeup. - * - * Range: 0 - not valid on wakeup, 1 - valid on wakeup - */ - u8 clk_valid_on_wakeup; - - /* - * DC-to-DC mode. - * - * Range: Unknown - */ - u8 dc2dcmode; - - /* - * Flag defining whether used as single or dual-band. - * - * Range: CONF_SINGLE_BAND, CONF_DUAL_BAND - */ - u8 single_dual_band; - - /* - * TX bip fem autodetect flag. - * - * Range: Unknown - */ - u8 tx_bip_fem_autodetect; - - /* - * TX bip gem manufacturer. - * - * Range: Unknown - */ - u8 tx_bip_fem_manufacturer; - - /* - * Settings flags. - * - * Range: Unknown - */ - u8 settings; -}; - #define CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE 15 #define CONF_NUMBER_OF_SUB_BANDS_5 7 #define CONF_NUMBER_OF_RATE_GROUPS 6 @@ -825,87 +743,43 @@ struct conf_general_parms { struct conf_radio_parms { /* - * Static radio parameters for 2.4GHz - * - * Range: unknown - */ - u8 rx_trace_loss; - u8 tx_trace_loss; - s8 rx_rssi_and_proc_compens[CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE]; - - /* - * Static radio parameters for 5GHz - * - * Range: unknown - */ - u8 rx_trace_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; - u8 tx_trace_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; - s8 rx_rssi_and_proc_compens_5[CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE]; - - /* - * Dynamic radio parameters for 2.4GHz + * FEM parameter set to use * - * Range: unknown + * Range: 0 or 1 */ - s16 tx_ref_pd_voltage; - s8 tx_ref_power; - s8 tx_offset_db; - - s8 tx_rate_limits_normal[CONF_NUMBER_OF_RATE_GROUPS]; - s8 tx_rate_limits_degraded[CONF_NUMBER_OF_RATE_GROUPS]; - - s8 tx_channel_limits_11b[CONF_NUMBER_OF_CHANNELS_2_4]; - s8 tx_channel_limits_ofdm[CONF_NUMBER_OF_CHANNELS_2_4]; - s8 tx_pdv_rate_offsets[CONF_NUMBER_OF_RATE_GROUPS]; - - u8 tx_ibias[CONF_NUMBER_OF_RATE_GROUPS]; - u8 rx_fem_insertion_loss; + u8 fem; +}; +struct conf_init_settings { /* - * Dynamic radio parameters for 5GHz - * - * Range: unknown + * Configure radio parameters. */ - s16 tx_ref_pd_voltage_5[CONF_NUMBER_OF_SUB_BANDS_5]; - s8 tx_ref_power_5[CONF_NUMBER_OF_SUB_BANDS_5]; - s8 tx_offset_db_5[CONF_NUMBER_OF_SUB_BANDS_5]; - - s8 tx_rate_limits_normal_5[CONF_NUMBER_OF_RATE_GROUPS]; - s8 tx_rate_limits_degraded_5[CONF_NUMBER_OF_RATE_GROUPS]; - - s8 tx_channel_limits_ofdm_5[CONF_NUMBER_OF_CHANNELS_5]; - s8 tx_pdv_rate_offsets_5[CONF_NUMBER_OF_RATE_GROUPS]; + struct conf_radio_parms radioparam; - /* FIXME: this is inconsistent with the types for 2.4GHz */ - s8 tx_ibias_5[CONF_NUMBER_OF_RATE_GROUPS]; - s8 rx_fem_insertion_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; }; -#define CONF_SR_ERR_TBL_COUNT 3 +struct conf_itrim_settings { + /* enable dco itrim */ + u8 enable; -struct conf_init_settings { - /* - * Configure Smart Reflex error table values. - */ - struct conf_mart_reflex_err_table sr_err_tbl[CONF_SR_ERR_TBL_COUNT]; + /* moderation timeout in microsecs from the last TX */ + u32 timeout; +}; +struct conf_pm_config_settings { /* - * Smart Reflex enable flag. + * Host clock settling time * - * Range: 1 - Smart Reflex enabled, 0 - Smart Reflex disabled - */ - u8 sr_enable; - - /* - * Configure general parameters. + * Range: 0 - 30000 us */ - struct conf_general_parms genparam; + u32 host_clk_settling_time; /* - * Configure radio parameters. + * Host fast wakeup support + * + * Range: true, false */ - struct conf_radio_parms radioparam; - + bool host_fast_wakeup_support; }; struct conf_drv_settings { @@ -914,6 +788,8 @@ struct conf_drv_settings { struct conf_tx_settings tx; struct conf_conn_settings conn; struct conf_init_settings init; + struct conf_itrim_settings itrim; + struct conf_pm_config_settings pm_config; }; #endif diff --git a/drivers/net/wireless/wl12xx/wl1271_debugfs.c b/drivers/net/wireless/wl12xx/wl1271_debugfs.c index c1805e5f8964..8d7588ca68fd 100644 --- a/drivers/net/wireless/wl12xx/wl1271_debugfs.c +++ b/drivers/net/wireless/wl12xx/wl1271_debugfs.c @@ -237,6 +237,64 @@ static const struct file_operations tx_queue_len_ops = { .open = wl1271_open_file_generic, }; +static ssize_t gpio_power_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + bool state = test_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); + + int res; + char buf[10]; + + res = scnprintf(buf, sizeof(buf), "%d\n", state); + + return simple_read_from_buffer(user_buf, count, ppos, buf, res); +} + +static ssize_t gpio_power_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + char buf[10]; + size_t len; + 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; + } + buf[len] = '\0'; + + ret = strict_strtoul(buf, 0, &value); + if (ret < 0) { + wl1271_warning("illegal value in gpio_power"); + goto out; + } + + if (value) { + wl->set_power(true); + set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); + } else { + wl->set_power(false); + clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); + } + +out: + mutex_unlock(&wl->mutex); + return count; +} + +static const struct file_operations gpio_power_ops = { + .read = gpio_power_read, + .write = gpio_power_write, + .open = wl1271_open_file_generic +}; + static void wl1271_debugfs_delete_files(struct wl1271 *wl) { DEBUGFS_FWSTATS_DEL(tx, internal_desc_overflow); @@ -333,6 +391,8 @@ static void wl1271_debugfs_delete_files(struct wl1271 *wl) DEBUGFS_DEL(tx_queue_len); DEBUGFS_DEL(retry_count); DEBUGFS_DEL(excessive_retries); + + DEBUGFS_DEL(gpio_power); } static int wl1271_debugfs_add_files(struct wl1271 *wl) @@ -434,6 +494,8 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl) DEBUGFS_ADD(retry_count, wl->debugfs.rootdir); DEBUGFS_ADD(excessive_retries, wl->debugfs.rootdir); + DEBUGFS_ADD(gpio_power, wl->debugfs.rootdir); + out: if (ret < 0) wl1271_debugfs_delete_files(wl); diff --git a/drivers/net/wireless/wl12xx/wl1271_event.c b/drivers/net/wireless/wl12xx/wl1271_event.c index d13fdd99c85c..7468ef10194b 100644 --- a/drivers/net/wireless/wl12xx/wl1271_event.c +++ b/drivers/net/wireless/wl12xx/wl1271_event.c @@ -24,6 +24,7 @@ #include "wl1271.h" #include "wl1271_reg.h" #include "wl1271_spi.h" +#include "wl1271_io.h" #include "wl1271_event.h" #include "wl1271_ps.h" #include "wl12xx_80211.h" @@ -35,7 +36,7 @@ static int wl1271_event_scan_complete(struct wl1271 *wl, wl1271_debug(DEBUG_EVENT, "status: 0x%x", mbox->scheduled_scan_status); - if (wl->scanning) { + if (test_bit(WL1271_FLAG_SCANNING, &wl->flags)) { if (wl->scan.state == WL1271_SCAN_BAND_DUAL) { wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL, size); @@ -43,7 +44,7 @@ static int wl1271_event_scan_complete(struct wl1271 *wl, * to the wl1271_cmd_scan function that we are not * scanning as it checks that. */ - wl->scanning = false; + clear_bit(WL1271_FLAG_SCANNING, &wl->flags); wl1271_cmd_scan(wl, wl->scan.ssid, wl->scan.ssid_len, wl->scan.active, wl->scan.high_prio, @@ -62,7 +63,7 @@ static int wl1271_event_scan_complete(struct wl1271 *wl, mutex_unlock(&wl->mutex); ieee80211_scan_completed(wl->hw, false); mutex_lock(&wl->mutex); - wl->scanning = false; + clear_bit(WL1271_FLAG_SCANNING, &wl->flags); } } return 0; @@ -78,25 +79,61 @@ static int wl1271_event_ps_report(struct wl1271 *wl, switch (mbox->ps_status) { case EVENT_ENTER_POWER_SAVE_FAIL: - if (!wl->psm) { + wl1271_debug(DEBUG_PSM, "PSM entry failed"); + + if (!test_bit(WL1271_FLAG_PSM, &wl->flags)) { + /* remain in active mode */ wl->psm_entry_retry = 0; break; } if (wl->psm_entry_retry < wl->conf.conn.psm_entry_retries) { wl->psm_entry_retry++; - ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE); + ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE, + true); } else { wl1271_error("PSM entry failed, giving up.\n"); + /* FIXME: this may need to be reconsidered. for now it + is not possible to indicate to the mac80211 + afterwards that PSM entry failed. To maximize + functionality (receiving data and remaining + associated) make sure that we are in sync with the + AP in regard of PSM mode. */ + ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE, + false); wl->psm_entry_retry = 0; - *beacon_loss = true; } break; case EVENT_ENTER_POWER_SAVE_SUCCESS: wl->psm_entry_retry = 0; + + /* enable beacon filtering */ + ret = wl1271_acx_beacon_filter_opt(wl, true); + if (ret < 0) + break; + + /* enable beacon early termination */ + ret = wl1271_acx_bet_enable(wl, true); + if (ret < 0) + break; + + /* go to extremely low power mode */ + wl1271_ps_elp_sleep(wl); + if (ret < 0) + break; break; case EVENT_EXIT_POWER_SAVE_FAIL: - wl1271_info("PSM exit failed"); + 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, + false); break; case EVENT_EXIT_POWER_SAVE_SUCCESS: default: @@ -136,7 +173,8 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) * filtering) is enabled. Without PSM, the stack will receive all * beacons and can detect beacon loss by itself. */ - if (vector & BSS_LOSE_EVENT_ID && wl->psm) { + if (vector & BSS_LOSE_EVENT_ID && + test_bit(WL1271_FLAG_PSM, &wl->flags)) { wl1271_debug(DEBUG_EVENT, "BSS_LOSE_EVENT"); /* indicate to the stack, that beacons have been lost */ @@ -150,7 +188,7 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) return ret; } - if (beacon_loss) { + if (wl->vif && beacon_loss) { /* Obviously, it's dangerous to release the mutex while we are holding many of the variables in the wl struct. That's why it's done last in the function, and care must @@ -177,14 +215,14 @@ int wl1271_event_unmask(struct wl1271 *wl) void wl1271_event_mbox_config(struct wl1271 *wl) { - wl->mbox_ptr[0] = wl1271_spi_read32(wl, REG_EVENT_MAILBOX_PTR); + wl->mbox_ptr[0] = wl1271_read32(wl, REG_EVENT_MAILBOX_PTR); wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox); wl1271_debug(DEBUG_EVENT, "MBOX ptrs: 0x%x 0x%x", wl->mbox_ptr[0], wl->mbox_ptr[1]); } -int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num, bool do_ack) +int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num) { struct event_mailbox mbox; int ret; @@ -195,8 +233,8 @@ int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num, bool do_ack) return -EINVAL; /* first we read the mbox descriptor */ - wl1271_spi_read(wl, wl->mbox_ptr[mbox_num], &mbox, - sizeof(struct event_mailbox), false); + wl1271_read(wl, wl->mbox_ptr[mbox_num], &mbox, + sizeof(struct event_mailbox), false); /* process the descriptor */ ret = wl1271_event_process(wl, &mbox); @@ -204,9 +242,7 @@ int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num, bool do_ack) return ret; /* then we let the firmware know it can go on...*/ - if (do_ack) - wl1271_spi_write32(wl, ACX_REG_INTERRUPT_TRIG, - INTR_TRIG_EVENT_ACK); + wl1271_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_EVENT_ACK); return 0; } diff --git a/drivers/net/wireless/wl12xx/wl1271_event.h b/drivers/net/wireless/wl12xx/wl1271_event.h index 4e3f55ebb1a8..278f9206aa56 100644 --- a/drivers/net/wireless/wl12xx/wl1271_event.h +++ b/drivers/net/wireless/wl12xx/wl1271_event.h @@ -112,6 +112,6 @@ struct event_mailbox { int wl1271_event_unmask(struct wl1271 *wl); void wl1271_event_mbox_config(struct wl1271 *wl); -int wl1271_event_handle(struct wl1271 *wl, u8 mbox, bool do_ack); +int wl1271_event_handle(struct wl1271 *wl, u8 mbox); #endif diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c index 11249b436cf1..86c30a86a456 100644 --- a/drivers/net/wireless/wl12xx/wl1271_init.c +++ b/drivers/net/wireless/wl12xx/wl1271_init.c @@ -49,7 +49,7 @@ static int wl1271_init_hwenc_config(struct wl1271 *wl) return 0; } -static int wl1271_init_templates_config(struct wl1271 *wl) +int wl1271_init_templates_config(struct wl1271 *wl) { int ret; @@ -113,7 +113,7 @@ static int wl1271_init_rx_config(struct wl1271 *wl, u32 config, u32 filter) return 0; } -static int wl1271_init_phy_config(struct wl1271 *wl) +int wl1271_init_phy_config(struct wl1271 *wl) { int ret; @@ -156,7 +156,7 @@ static int wl1271_init_beacon_filter(struct wl1271 *wl) return 0; } -static int wl1271_init_pta(struct wl1271 *wl) +int wl1271_init_pta(struct wl1271 *wl) { int ret; @@ -171,7 +171,7 @@ static int wl1271_init_pta(struct wl1271 *wl) return 0; } -static int wl1271_init_energy_detection(struct wl1271 *wl) +int wl1271_init_energy_detection(struct wl1271 *wl) { int ret; @@ -195,7 +195,9 @@ static int wl1271_init_beacon_broadcast(struct wl1271 *wl) int wl1271_hw_init(struct wl1271 *wl) { - int ret; + struct conf_tx_ac_category *conf_ac; + struct conf_tx_tid *conf_tid; + int ret, i; ret = wl1271_cmd_general_parms(wl); if (ret < 0) @@ -229,6 +231,10 @@ int wl1271_hw_init(struct wl1271 *wl) if (ret < 0) goto out_free_memmap; + ret = wl1271_acx_dco_itrim_params(wl); + if (ret < 0) + goto out_free_memmap; + /* Initialize connection monitoring thresholds */ ret = wl1271_acx_conn_monit_params(wl); if (ret < 0) @@ -270,22 +276,36 @@ int wl1271_hw_init(struct wl1271 *wl) goto out_free_memmap; /* Default TID configuration */ - ret = wl1271_acx_tid_cfg(wl); - if (ret < 0) - goto out_free_memmap; + for (i = 0; i < wl->conf.tx.tid_conf_count; i++) { + conf_tid = &wl->conf.tx.tid_conf[i]; + ret = wl1271_acx_tid_cfg(wl, conf_tid->queue_id, + conf_tid->channel_type, + conf_tid->tsid, + conf_tid->ps_scheme, + conf_tid->ack_policy, + conf_tid->apsd_conf[0], + conf_tid->apsd_conf[1]); + if (ret < 0) + goto out_free_memmap; + } /* Default AC configuration */ - ret = wl1271_acx_ac_cfg(wl); - if (ret < 0) - goto out_free_memmap; + for (i = 0; i < wl->conf.tx.ac_conf_count; i++) { + conf_ac = &wl->conf.tx.ac_conf[i]; + ret = wl1271_acx_ac_cfg(wl, conf_ac->ac, conf_ac->cw_min, + conf_ac->cw_max, conf_ac->aifsn, + conf_ac->tx_op_limit); + if (ret < 0) + goto out_free_memmap; + } /* Configure TX rate classes */ - ret = wl1271_acx_rate_policies(wl, CONF_TX_RATE_MASK_ALL); + ret = wl1271_acx_rate_policies(wl); if (ret < 0) goto out_free_memmap; /* Enable data path */ - ret = wl1271_cmd_data_path(wl, wl->channel, 1); + ret = wl1271_cmd_data_path(wl, 1); if (ret < 0) goto out_free_memmap; @@ -299,8 +319,8 @@ int wl1271_hw_init(struct wl1271 *wl) if (ret < 0) goto out_free_memmap; - /* Configure smart reflex */ - ret = wl1271_acx_smart_reflex(wl); + /* configure PM */ + ret = wl1271_acx_pm_config(wl); if (ret < 0) goto out_free_memmap; diff --git a/drivers/net/wireless/wl12xx/wl1271_init.h b/drivers/net/wireless/wl12xx/wl1271_init.h index 930677fbe852..bc26f8c53b91 100644 --- a/drivers/net/wireless/wl12xx/wl1271_init.h +++ b/drivers/net/wireless/wl12xx/wl1271_init.h @@ -27,6 +27,10 @@ #include "wl1271.h" int wl1271_hw_init_power_auth(struct wl1271 *wl); +int wl1271_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); int wl1271_hw_init(struct wl1271 *wl); #endif diff --git a/drivers/net/wireless/wl12xx/wl1271_io.c b/drivers/net/wireless/wl12xx/wl1271_io.c new file mode 100644 index 000000000000..5cd94d5666c2 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_io.c @@ -0,0 +1,213 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2008-2010 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/crc7.h> +#include <linux/spi/spi.h> + +#include "wl1271.h" +#include "wl12xx_80211.h" +#include "wl1271_spi.h" +#include "wl1271_io.h" + +static int wl1271_translate_addr(struct wl1271 *wl, int addr) +{ + /* + * To translate, first check to which window of addresses the + * particular address belongs. Then subtract the starting address + * of that window from the address. Then, add offset of the + * translated region. + * + * The translated regions occur next to each other in physical device + * memory, so just add the sizes of the preceeding address regions to + * get the offset to the new region. + * + * Currently, only the two first regions are addressed, and the + * assumption is that all addresses will fall into either of those + * two. + */ + if ((addr >= wl->part.reg.start) && + (addr < wl->part.reg.start + wl->part.reg.size)) + return addr - wl->part.reg.start + wl->part.mem.size; + else + return addr - wl->part.mem.start; +} + +/* Set the SPI partitions to access the chip addresses + * + * To simplify driver code, a fixed (virtual) memory map is defined for + * register and memory addresses. Because in the chipset, in different stages + * of operation, those addresses will move around, an address translation + * mechanism is required. + * + * There are four partitions (three memory and one register partition), + * which are mapped to two different areas of the hardware memory. + * + * Virtual address + * space + * + * | | + * ...+----+--> mem.start + * Physical address ... | | + * space ... | | [PART_0] + * ... | | + * 00000000 <--+----+... ...+----+--> mem.start + mem.size + * | | ... | | + * |MEM | ... | | + * | | ... | | + * mem.size <--+----+... | | {unused area) + * | | ... | | + * |REG | ... | | + * mem.size | | ... | | + * + <--+----+... ...+----+--> reg.start + * reg.size | | ... | | + * |MEM2| ... | | [PART_1] + * | | ... | | + * ...+----+--> reg.start + reg.size + * | | + * + */ +int wl1271_set_partition(struct wl1271 *wl, + struct wl1271_partition_set *p) +{ + /* copy partition info */ + memcpy(&wl->part, p, sizeof(*p)); + + wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", + p->mem.start, p->mem.size); + wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", + p->reg.start, p->reg.size); + wl1271_debug(DEBUG_SPI, "mem2_start %08X mem2_size %08X", + p->mem2.start, p->mem2.size); + wl1271_debug(DEBUG_SPI, "mem3_start %08X mem3_size %08X", + p->mem3.start, p->mem3.size); + + /* write partition info to the chipset */ + wl1271_raw_write32(wl, HW_PART0_START_ADDR, p->mem.start); + wl1271_raw_write32(wl, HW_PART0_SIZE_ADDR, p->mem.size); + wl1271_raw_write32(wl, HW_PART1_START_ADDR, p->reg.start); + wl1271_raw_write32(wl, HW_PART1_SIZE_ADDR, p->reg.size); + wl1271_raw_write32(wl, HW_PART2_START_ADDR, p->mem2.start); + wl1271_raw_write32(wl, HW_PART2_SIZE_ADDR, p->mem2.size); + wl1271_raw_write32(wl, HW_PART3_START_ADDR, p->mem3.start); + + return 0; +} + +void wl1271_io_reset(struct wl1271 *wl) +{ + wl1271_spi_reset(wl); +} + +void wl1271_io_init(struct wl1271 *wl) +{ + wl1271_spi_init(wl); +} + +void wl1271_raw_write(struct wl1271 *wl, int addr, void *buf, + size_t len, bool fixed) +{ + wl1271_spi_raw_write(wl, addr, buf, len, fixed); +} + +void wl1271_raw_read(struct wl1271 *wl, int addr, void *buf, + size_t len, bool fixed) +{ + wl1271_spi_raw_read(wl, addr, buf, len, fixed); +} + +void wl1271_read(struct wl1271 *wl, int addr, void *buf, size_t len, + bool fixed) +{ + int physical; + + physical = wl1271_translate_addr(wl, addr); + + wl1271_spi_raw_read(wl, physical, buf, len, fixed); +} + +void wl1271_write(struct wl1271 *wl, int addr, void *buf, size_t len, + bool fixed) +{ + int physical; + + physical = wl1271_translate_addr(wl, addr); + + wl1271_spi_raw_write(wl, physical, buf, len, fixed); +} + +u32 wl1271_read32(struct wl1271 *wl, int addr) +{ + return wl1271_raw_read32(wl, wl1271_translate_addr(wl, addr)); +} + +void wl1271_write32(struct wl1271 *wl, int addr, u32 val) +{ + wl1271_raw_write32(wl, wl1271_translate_addr(wl, addr), val); +} + +void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val) +{ + /* write address >> 1 + 0x30000 to OCP_POR_CTR */ + addr = (addr >> 1) + 0x30000; + wl1271_write32(wl, OCP_POR_CTR, addr); + + /* write value to OCP_POR_WDATA */ + wl1271_write32(wl, OCP_DATA_WRITE, val); + + /* write 1 to OCP_CMD */ + wl1271_write32(wl, OCP_CMD, OCP_CMD_WRITE); +} + +u16 wl1271_top_reg_read(struct wl1271 *wl, int addr) +{ + u32 val; + int timeout = OCP_CMD_LOOP; + + /* write address >> 1 + 0x30000 to OCP_POR_CTR */ + addr = (addr >> 1) + 0x30000; + wl1271_write32(wl, OCP_POR_CTR, addr); + + /* write 2 to OCP_CMD */ + wl1271_write32(wl, OCP_CMD, OCP_CMD_READ); + + /* poll for data ready */ + do { + val = wl1271_read32(wl, OCP_DATA_READ); + } while (!(val & OCP_READY_MASK) && --timeout); + + if (!timeout) { + wl1271_warning("Top register access timed out."); + return 0xffff; + } + + /* check data status and return if OK */ + if ((val & OCP_STATUS_MASK) == OCP_STATUS_OK) + return val & 0xffff; + else { + wl1271_warning("Top register access returned error."); + return 0xffff; + } +} + diff --git a/drivers/net/wireless/wl12xx/wl1271_io.h b/drivers/net/wireless/wl12xx/wl1271_io.h new file mode 100644 index 000000000000..fa9a0b35788f --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_io.h @@ -0,0 +1,68 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 1998-2009 Texas Instruments. All rights reserved. + * Copyright (C) 2008-2010 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL1271_IO_H__ +#define __WL1271_IO_H__ + +struct wl1271; + +void wl1271_io_reset(struct wl1271 *wl); +void wl1271_io_init(struct wl1271 *wl); + +/* Raw target IO, address is not translated */ +void wl1271_raw_write(struct wl1271 *wl, int addr, void *buf, + size_t len, bool fixed); +void wl1271_raw_read(struct wl1271 *wl, int addr, void *buf, + size_t len, bool fixed); + +/* Translated target IO */ +void wl1271_read(struct wl1271 *wl, int addr, void *buf, size_t len, + bool fixed); +void wl1271_write(struct wl1271 *wl, int addr, void *buf, size_t len, + bool fixed); +u32 wl1271_read32(struct wl1271 *wl, int addr); +void wl1271_write32(struct wl1271 *wl, int addr, u32 val); + +/* Top Register IO */ +void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val); +u16 wl1271_top_reg_read(struct wl1271 *wl, int addr); + +int wl1271_set_partition(struct wl1271 *wl, + struct wl1271_partition_set *p); + +static inline u32 wl1271_raw_read32(struct wl1271 *wl, int addr) +{ + wl1271_raw_read(wl, addr, &wl->buffer_32, + sizeof(wl->buffer_32), false); + + return wl->buffer_32; +} + +static inline void wl1271_raw_write32(struct wl1271 *wl, int addr, u32 val) +{ + wl->buffer_32 = val; + wl1271_raw_write(wl, addr, &wl->buffer_32, + sizeof(wl->buffer_32), false); +} +#endif diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index b62c00ff42fe..2a864b24291d 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -1,7 +1,7 @@ /* * This file is part of wl1271 * - * Copyright (C) 2008-2009 Nokia Corporation + * Copyright (C) 2008-2010 Nokia Corporation * * Contact: Luciano Coelho <luciano.coelho@nokia.com> * @@ -38,6 +38,7 @@ #include "wl12xx_80211.h" #include "wl1271_reg.h" #include "wl1271_spi.h" +#include "wl1271_io.h" #include "wl1271_event.h" #include "wl1271_tx.h" #include "wl1271_rx.h" @@ -46,6 +47,9 @@ #include "wl1271_debugfs.h" #include "wl1271_cmd.h" #include "wl1271_boot.h" +#include "wl1271_testmode.h" + +#define WL1271_BOOT_RETRIES 3 static struct conf_drv_settings default_conf = { .sg = { @@ -67,16 +71,17 @@ static struct conf_drv_settings default_conf = { .ps_poll_timeout = 15, .upsd_timeout = 15, .rts_threshold = 2347, - .rx_cca_threshold = 0xFFEF, - .irq_blk_threshold = 0, - .irq_pkt_threshold = USHORT_MAX, - .irq_timeout = 5, + .rx_cca_threshold = 0, + .irq_blk_threshold = 0xFFFF, + .irq_pkt_threshold = 0, + .irq_timeout = 600, .queue_type = CONF_RX_QUEUE_TYPE_LOW_PRIORITY, }, .tx = { .tx_energy_detection = 0, .rc_conf = { - .enabled_rates = CONF_TX_RATE_MASK_UNSPECIFIED, + .enabled_rates = CONF_HW_BIT_RATE_1MBPS | + CONF_HW_BIT_RATE_2MBPS, .short_retry_limit = 10, .long_retry_limit = 10, .aflags = 0 @@ -172,8 +177,8 @@ static struct conf_drv_settings default_conf = { } }, .frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD, - .tx_compl_timeout = 5, - .tx_compl_threshold = 5 + .tx_compl_timeout = 700, + .tx_compl_threshold = 4 }, .conn = { .wake_up_event = CONF_WAKE_UP_EVENT_DTIM, @@ -186,12 +191,12 @@ static struct conf_drv_settings default_conf = { .rule = CONF_BCN_RULE_PASS_ON_APPEARANCE, } }, - .synch_fail_thold = 5, + .synch_fail_thold = 10, .bss_lose_timeout = 100, .beacon_rx_timeout = 10000, .broadcast_timeout = 20000, .rx_broadcast_in_ps = 1, - .ps_poll_threshold = 4, + .ps_poll_threshold = 20, .sig_trigger_count = 2, .sig_trigger = { [0] = { @@ -226,97 +231,17 @@ static struct conf_drv_settings default_conf = { .psm_entry_retries = 3 }, .init = { - .sr_err_tbl = { - [0] = { - .len = 7, - .upper_limit = 0x03, - .values = { - 0x18, 0x10, 0x05, 0xfb, 0xf0, 0xe8, - 0x00 } - }, - [1] = { - .len = 7, - .upper_limit = 0x03, - .values = { - 0x18, 0x10, 0x05, 0xf6, 0xf0, 0xe8, - 0x00 } - }, - [2] = { - .len = 7, - .upper_limit = 0x03, - .values = { - 0x18, 0x10, 0x05, 0xfb, 0xf0, 0xe8, - 0x00 } - } - }, - .sr_enable = 1, - .genparam = { - .ref_clk = CONF_REF_CLK_38_4_E, - .settling_time = 5, - .clk_valid_on_wakeup = 0, - .dc2dcmode = 0, - .single_dual_band = CONF_SINGLE_BAND, - .tx_bip_fem_autodetect = 0, - .tx_bip_fem_manufacturer = 1, - .settings = 1, - }, .radioparam = { - .rx_trace_loss = 10, - .tx_trace_loss = 10, - .rx_rssi_and_proc_compens = { - 0xec, 0xf6, 0x00, 0x0c, 0x18, 0xf8, - 0xfc, 0x00, 0x08, 0x10, 0xf0, 0xf8, - 0x00, 0x0a, 0x14 }, - .rx_trace_loss_5 = { 0, 0, 0, 0, 0, 0, 0 }, - .tx_trace_loss_5 = { 0, 0, 0, 0, 0, 0, 0 }, - .rx_rssi_and_proc_compens_5 = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00 }, - .tx_ref_pd_voltage = 0x24e, - .tx_ref_power = 0x78, - .tx_offset_db = 0x0, - .tx_rate_limits_normal = { - 0x1e, 0x1f, 0x22, 0x24, 0x28, 0x29 }, - .tx_rate_limits_degraded = { - 0x1b, 0x1c, 0x1e, 0x20, 0x24, 0x25 }, - .tx_channel_limits_11b = { - 0x22, 0x50, 0x50, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x50, 0x50, 0x22, 0x50, - 0x22, 0x50 }, - .tx_channel_limits_ofdm = { - 0x20, 0x50, 0x50, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x50, 0x50, 0x20, 0x50, - 0x20, 0x50 }, - .tx_pdv_rate_offsets = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - .tx_ibias = { - 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x27 }, - .rx_fem_insertion_loss = 0x14, - .tx_ref_pd_voltage_5 = { - 0x0190, 0x01a4, 0x01c3, 0x01d8, - 0x020a, 0x021c }, - .tx_ref_power_5 = { - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, - .tx_offset_db_5 = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - .tx_rate_limits_normal_5 = { - 0x1b, 0x1e, 0x21, 0x23, 0x27, 0x00 }, - .tx_rate_limits_degraded_5 = { - 0x1b, 0x1e, 0x21, 0x23, 0x27, 0x00 }, - .tx_channel_limits_ofdm_5 = { - 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x50 }, - .tx_pdv_rate_offsets_5 = { - 0x01, 0x02, 0x02, 0x02, 0x02, 0x00 }, - .tx_ibias_5 = { - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 }, - .rx_fem_insertion_loss_5 = { - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 } + .fem = 1, } + }, + .itrim = { + .enable = false, + .timeout = 50000, + }, + .pm_config = { + .host_clk_settling_time = 5000, + .host_fast_wakeup_support = false } }; @@ -337,15 +262,14 @@ static void wl1271_conf_init(struct wl1271 *wl) /* apply driver default configuration */ memcpy(&wl->conf, &default_conf, sizeof(default_conf)); - - if (wl1271_11a_enabled()) - wl->conf.init.genparam.single_dual_band = CONF_DUAL_BAND; } static int wl1271_plt_init(struct wl1271 *wl) { - int ret; + struct conf_tx_ac_category *conf_ac; + struct conf_tx_tid *conf_tid; + int ret, i; ret = wl1271_cmd_general_parms(wl); if (ret < 0) @@ -355,15 +279,89 @@ static int wl1271_plt_init(struct wl1271 *wl) if (ret < 0) return ret; - ret = wl1271_acx_init_mem_config(wl); + ret = wl1271_init_templates_config(wl); if (ret < 0) return ret; - ret = wl1271_cmd_data_path(wl, wl->channel, 1); + ret = wl1271_acx_init_mem_config(wl); if (ret < 0) return ret; + /* PHY layer config */ + ret = wl1271_init_phy_config(wl); + if (ret < 0) + goto out_free_memmap; + + ret = wl1271_acx_dco_itrim_params(wl); + if (ret < 0) + goto out_free_memmap; + + /* Initialize connection monitoring thresholds */ + ret = wl1271_acx_conn_monit_params(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; + + /* Default fragmentation threshold */ + ret = wl1271_acx_frag_threshold(wl); + if (ret < 0) + goto out_free_memmap; + + /* Default TID configuration */ + for (i = 0; i < wl->conf.tx.tid_conf_count; i++) { + conf_tid = &wl->conf.tx.tid_conf[i]; + ret = wl1271_acx_tid_cfg(wl, conf_tid->queue_id, + conf_tid->channel_type, + conf_tid->tsid, + conf_tid->ps_scheme, + conf_tid->ack_policy, + conf_tid->apsd_conf[0], + conf_tid->apsd_conf[1]); + if (ret < 0) + goto out_free_memmap; + } + + /* Default AC configuration */ + for (i = 0; i < wl->conf.tx.ac_conf_count; i++) { + conf_ac = &wl->conf.tx.ac_conf[i]; + ret = wl1271_acx_ac_cfg(wl, conf_ac->ac, conf_ac->cw_min, + conf_ac->cw_max, conf_ac->aifsn, + conf_ac->tx_op_limit); + 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 CAM power saving (ie. always active) */ + ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); + if (ret < 0) + goto out_free_memmap; + + /* configure PM */ + ret = wl1271_acx_pm_config(wl); + if (ret < 0) + goto out_free_memmap; + return 0; + + out_free_memmap: + kfree(wl->target_mem_map); + wl->target_mem_map = NULL; + + return ret; } static void wl1271_disable_interrupts(struct wl1271 *wl) @@ -374,11 +372,13 @@ static void wl1271_disable_interrupts(struct wl1271 *wl) static void wl1271_power_off(struct wl1271 *wl) { wl->set_power(false); + clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); } static void wl1271_power_on(struct wl1271 *wl) { wl->set_power(true); + set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); } static void wl1271_fw_status(struct wl1271 *wl, @@ -387,8 +387,7 @@ static void wl1271_fw_status(struct wl1271 *wl, u32 total = 0; int i; - wl1271_spi_read(wl, FW_STATUS_ADDR, status, - sizeof(*status), false); + wl1271_read(wl, FW_STATUS_ADDR, status, sizeof(*status), false); wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, " "drv_rx_counter = %d, tx_results_counter = %d)", @@ -435,7 +434,7 @@ static void wl1271_irq_work(struct work_struct *work) if (ret < 0) goto out; - wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); + wl1271_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); wl1271_fw_status(wl, wl->fw_status); intr = le32_to_cpu(wl->fw_status->intr); @@ -447,14 +446,13 @@ static void wl1271_irq_work(struct work_struct *work) intr &= WL1271_INTR_MASK; if (intr & WL1271_ACX_INTR_EVENT_A) { - bool do_ack = (intr & WL1271_ACX_INTR_EVENT_B) ? false : true; wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A"); - wl1271_event_handle(wl, 0, do_ack); + wl1271_event_handle(wl, 0); } if (intr & WL1271_ACX_INTR_EVENT_B) { wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B"); - wl1271_event_handle(wl, 1, true); + wl1271_event_handle(wl, 1); } if (intr & WL1271_ACX_INTR_INIT_COMPLETE) @@ -478,8 +476,8 @@ static void wl1271_irq_work(struct work_struct *work) } out_sleep: - wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, - WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK)); + wl1271_write32(wl, ACX_REG_INTERRUPT_MASK, + WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK)); wl1271_ps_elp_sleep(wl); out: @@ -546,6 +544,40 @@ out: return ret; } +static int wl1271_update_mac_addr(struct wl1271 *wl) +{ + int ret = 0; + u8 *nvs_ptr = (u8 *)wl->nvs->nvs; + + /* get mac address from the 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]; + + /* FIXME: if it is a zero-address, we should bail out. Now, instead, + we randomize an address */ + if (is_zero_ether_addr(wl->mac_addr)) { + static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf}; + memcpy(wl->mac_addr, nokia_oui, 3); + get_random_bytes(wl->mac_addr + 3, 3); + + /* update this address to the NVS */ + nvs_ptr[11] = wl->mac_addr[0]; + nvs_ptr[10] = wl->mac_addr[1]; + nvs_ptr[6] = wl->mac_addr[2]; + nvs_ptr[5] = wl->mac_addr[3]; + nvs_ptr[4] = wl->mac_addr[4]; + nvs_ptr[3] = wl->mac_addr[5]; + } + + SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr); + + return ret; +} + static int wl1271_fetch_nvs(struct wl1271 *wl) { const struct firmware *fw; @@ -558,15 +590,14 @@ static int wl1271_fetch_nvs(struct wl1271 *wl) return ret; } - if (fw->size % 4) { - wl1271_error("nvs size is not multiple of 32 bits: %zu", - fw->size); + if (fw->size != sizeof(struct wl1271_nvs_file)) { + wl1271_error("nvs size is not as expected: %zu != %zu", + fw->size, sizeof(struct wl1271_nvs_file)); ret = -EILSEQ; goto out; } - wl->nvs_len = fw->size; - wl->nvs = kmalloc(wl->nvs_len, GFP_KERNEL); + wl->nvs = kmalloc(sizeof(struct wl1271_nvs_file), GFP_KERNEL); if (!wl->nvs) { wl1271_error("could not allocate memory for the nvs file"); @@ -574,9 +605,9 @@ static int wl1271_fetch_nvs(struct wl1271 *wl) goto out; } - memcpy(wl->nvs, fw->data, wl->nvs_len); + memcpy(wl->nvs, fw->data, sizeof(struct wl1271_nvs_file)); - ret = 0; + ret = wl1271_update_mac_addr(wl); out: release_firmware(fw); @@ -614,10 +645,11 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) struct wl1271_partition_set partition; int ret = 0; + msleep(WL1271_PRE_POWER_ON_SLEEP); wl1271_power_on(wl); msleep(WL1271_POWER_ON_SLEEP); - wl1271_spi_reset(wl); - wl1271_spi_init(wl); + wl1271_io_reset(wl); + wl1271_io_init(wl); /* We don't need a real memory partition here, because we only want * to use the registers at this point. */ @@ -632,7 +664,7 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) /* whal_FwCtrl_BootSm() */ /* 0. read chip id from CHIP_ID */ - wl->chip.id = wl1271_spi_read32(wl, CHIP_ID_B); + wl->chip.id = wl1271_read32(wl, CHIP_ID_B); /* 1. check if chip id is valid */ @@ -643,7 +675,7 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) ret = wl1271_setup(wl); if (ret < 0) - goto out_power_off; + goto out; break; case CHIP_ID_1271_PG20: wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)", @@ -651,38 +683,34 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) ret = wl1271_setup(wl); if (ret < 0) - goto out_power_off; + goto out; break; default: - wl1271_error("unsupported chip id: 0x%x", wl->chip.id); + wl1271_warning("unsupported chip id: 0x%x", wl->chip.id); ret = -ENODEV; - goto out_power_off; + goto out; } if (wl->fw == NULL) { ret = wl1271_fetch_firmware(wl); if (ret < 0) - goto out_power_off; + goto out; } /* No NVS from netlink, try to get it from the filesystem */ if (wl->nvs == NULL) { ret = wl1271_fetch_nvs(wl); if (ret < 0) - goto out_power_off; + goto out; } - goto out; - -out_power_off: - wl1271_power_off(wl); - out: return ret; } int wl1271_plt_start(struct wl1271 *wl) { + int retries = WL1271_BOOT_RETRIES; int ret; mutex_lock(&wl->mutex); @@ -696,35 +724,43 @@ int wl1271_plt_start(struct wl1271 *wl) goto out; } - wl->state = WL1271_STATE_PLT; - - ret = wl1271_chip_wakeup(wl); - if (ret < 0) - goto out; - - ret = wl1271_boot(wl); - if (ret < 0) - goto out_power_off; - - wl1271_notice("firmware booted in PLT mode (%s)", wl->chip.fw_ver); - - ret = wl1271_plt_init(wl); - if (ret < 0) - goto out_irq_disable; + while (retries) { + retries--; + ret = wl1271_chip_wakeup(wl); + if (ret < 0) + goto power_off; - /* Make sure power saving is disabled */ - ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); - if (ret < 0) - goto out_irq_disable; + ret = wl1271_boot(wl); + if (ret < 0) + goto power_off; - goto out; + ret = wl1271_plt_init(wl); + if (ret < 0) + goto irq_disable; -out_irq_disable: - wl1271_disable_interrupts(wl); + wl->state = WL1271_STATE_PLT; + wl1271_notice("firmware booted in PLT mode (%s)", + wl->chip.fw_ver); + goto out; -out_power_off: - wl1271_power_off(wl); +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, + because we need to let any possibly pending IRQ out of + the system (and while we are WL1271_STATE_OFF the IRQ + 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); + mutex_lock(&wl->mutex); +power_off: + wl1271_power_off(wl); + } + wl1271_error("firmware boot in PLT mode failed despite %d retries", + WL1271_BOOT_RETRIES); out: mutex_unlock(&wl->mutex); @@ -762,7 +798,20 @@ out: 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; + + /* peek into the rates configured in the STA entry */ + spin_lock_irqsave(&wl->wl_lock, flags); + if (sta && sta->supp_rates[conf->channel->band] != wl->sta_rate_set) { + wl->sta_rate_set = sta->supp_rates[conf->channel->band]; + set_bit(WL1271_FLAG_STA_RATES_CHANGED, &wl->flags); + } + spin_unlock_irqrestore(&wl->wl_lock, flags); + /* queue the packet */ skb_queue_tail(&wl->tx_queue, skb); /* @@ -784,7 +833,7 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) * protected. Maybe fix this by removing the stupid * variable altogether and checking the real queue state? */ - wl->tx_queue_stopped = true; + set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags); } return NETDEV_TX_OK; @@ -880,6 +929,7 @@ static struct notifier_block wl1271_dev_notifier = { static int wl1271_op_start(struct ieee80211_hw *hw) { struct wl1271 *wl = hw->priv; + int retries = WL1271_BOOT_RETRIES; int ret = 0; wl1271_debug(DEBUG_MAC80211, "mac80211 start"); @@ -893,30 +943,42 @@ static int wl1271_op_start(struct ieee80211_hw *hw) goto out; } - ret = wl1271_chip_wakeup(wl); - if (ret < 0) - goto out; - - ret = wl1271_boot(wl); - if (ret < 0) - goto out_power_off; - - ret = wl1271_hw_init(wl); - if (ret < 0) - goto out_irq_disable; - - wl->state = WL1271_STATE_ON; + while (retries) { + retries--; + ret = wl1271_chip_wakeup(wl); + if (ret < 0) + goto power_off; - wl1271_info("firmware booted (%s)", wl->chip.fw_ver); + ret = wl1271_boot(wl); + if (ret < 0) + goto power_off; - goto out; + ret = wl1271_hw_init(wl); + if (ret < 0) + goto irq_disable; -out_irq_disable: - wl1271_disable_interrupts(wl); + wl->state = WL1271_STATE_ON; + wl1271_info("firmware booted (%s)", wl->chip.fw_ver); + goto out; -out_power_off: - wl1271_power_off(wl); +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, + because we need to let any possibly pending IRQ out of + the system (and while we are WL1271_STATE_OFF the IRQ + 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); + mutex_lock(&wl->mutex); +power_off: + wl1271_power_off(wl); + } + wl1271_error("firmware boot failed despite %d retries", + WL1271_BOOT_RETRIES); out: mutex_unlock(&wl->mutex); @@ -944,11 +1006,10 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) WARN_ON(wl->state != WL1271_STATE_ON); - if (wl->scanning) { + if (test_and_clear_bit(WL1271_FLAG_SCANNING, &wl->flags)) { mutex_unlock(&wl->mutex); ieee80211_scan_completed(wl->hw, true); mutex_lock(&wl->mutex); - wl->scanning = false; } wl->state = WL1271_STATE_OFF; @@ -973,10 +1034,7 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) wl->band = IEEE80211_BAND_2GHZ; wl->rx_counter = 0; - wl->elp = false; - wl->psm = 0; wl->psm_entry_retry = 0; - wl->tx_queue_stopped = false; wl->power_level = WL1271_DEFAULT_POWER_LEVEL; wl->tx_blocks_available = 0; wl->tx_results_count = 0; @@ -986,7 +1044,9 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) wl->tx_security_seq_32 = 0; wl->time_offset = 0; wl->session_counter = 0; - wl->joined = false; + wl->rate_set = CONF_TX_RATE_MASK_BASIC; + wl->sta_rate_set = 0; + wl->flags = 0; for (i = 0; i < NUM_TX_QUEUES; i++) wl->tx_blocks_freed[i] = 0; @@ -996,13 +1056,13 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) } static int wl1271_op_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct wl1271 *wl = hw->priv; int ret = 0; wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", - conf->type, conf->mac_addr); + vif->type, vif->addr); mutex_lock(&wl->mutex); if (wl->vif) { @@ -1010,9 +1070,9 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, goto out; } - wl->vif = conf->vif; + wl->vif = vif; - switch (conf->type) { + switch (vif->type) { case NL80211_IFTYPE_STATION: wl->bss_type = BSS_TYPE_STA_BSS; break; @@ -1032,7 +1092,7 @@ out: } static void wl1271_op_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct wl1271 *wl = hw->priv; @@ -1109,6 +1169,51 @@ out: } #endif +static int wl1271_join_channel(struct wl1271 *wl, int channel) +{ + int ret = 0; + /* we need to use a dummy BSSID for now */ + static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde, + 0xad, 0xbe, 0xef }; + + /* the dummy join is not required for ad-hoc */ + if (wl->bss_type == BSS_TYPE_IBSS) + goto out; + + /* disable mac filter, so we hear everything */ + wl->rx_config &= ~CFG_BSSID_FILTER_EN; + + wl->channel = channel; + memcpy(wl->bssid, dummy_bssid, ETH_ALEN); + + ret = wl1271_cmd_join(wl); + if (ret < 0) + goto out; + + set_bit(WL1271_FLAG_JOINED, &wl->flags); + +out: + return ret; +} + +static int wl1271_unjoin_channel(struct wl1271 *wl) +{ + int ret; + + /* to stop listening to a channel, we disconnect */ + ret = wl1271_cmd_disconnect(wl); + if (ret < 0) + goto out; + + clear_bit(WL1271_FLAG_JOINED, &wl->flags); + wl->channel = 0; + memset(wl->bssid, 0, ETH_ALEN); + wl->rx_config = WL1271_DEFAULT_RX_CONFIG; + +out: + return ret; +} + static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) { struct wl1271 *wl = hw->priv; @@ -1117,10 +1222,11 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) channel = ieee80211_frequency_to_channel(conf->channel->center_freq); - wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d", + wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s", channel, conf->flags & IEEE80211_CONF_PS ? "on" : "off", - conf->power_level); + conf->power_level, + conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use"); mutex_lock(&wl->mutex); @@ -1130,35 +1236,55 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) if (ret < 0) goto out; - if (channel != wl->channel) { - /* - * We assume that the stack will configure the right channel - * before associating, so we don't need to send a join - * command here. We will join the right channel when the - * BSSID changes - */ - wl->channel = channel; + if (changed & IEEE80211_CONF_CHANGE_IDLE) { + if (conf->flags & IEEE80211_CONF_IDLE && + test_bit(WL1271_FLAG_JOINED, &wl->flags)) + wl1271_unjoin_channel(wl); + else if (!(conf->flags & IEEE80211_CONF_IDLE)) + wl1271_join_channel(wl, channel); + + if (conf->flags & IEEE80211_CONF_IDLE) { + wl->rate_set = CONF_TX_RATE_MASK_BASIC; + wl->sta_rate_set = 0; + wl1271_acx_rate_policies(wl); + } } - if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) { - wl1271_info("psm enabled"); + /* if the channel changes while joined, join again */ + if (channel != wl->channel && + test_bit(WL1271_FLAG_JOINED, &wl->flags)) { + wl->channel = channel; + /* FIXME: maybe use CMD_CHANNEL_SWITCH for this? */ + ret = wl1271_cmd_join(wl); + if (ret < 0) + wl1271_warning("cmd join to update channel failed %d", + ret); + } else + wl->channel = channel; - wl->psm_requested = true; + if (conf->flags & IEEE80211_CONF_PS && + !test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) { + set_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags); /* * We enter PSM only if we're already associated. * If we're not, we'll enter it when joining an SSID, * through the bss_info_changed() hook. */ - ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE); + if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) { + wl1271_info("psm enabled"); + ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE, + true); + } } else if (!(conf->flags & IEEE80211_CONF_PS) && - wl->psm_requested) { + test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) { wl1271_info("psm disabled"); - wl->psm_requested = false; + clear_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags); - if (wl->psm) - ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE); + if (test_bit(WL1271_FLAG_PSM, &wl->flags)) + ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE, + true); } if (conf->power_level != wl->power_level) { @@ -1350,9 +1476,24 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, 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_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_key(wl, KEY_REMOVE, key_conf->keyidx, key_type, key_conf->keylen, key_conf->key, @@ -1440,20 +1581,21 @@ out: return ret; } -static u32 wl1271_enabled_rates_get(struct wl1271 *wl, u64 basic_rate_set) +static void wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *beacon) { - struct ieee80211_supported_band *band; - u32 enabled_rates = 0; - int bit; - - band = wl->hw->wiphy->bands[wl->band]; - for (bit = 0; bit < band->n_bitrates; bit++) { - if (basic_rate_set & 0x1) - enabled_rates |= band->bitrates[bit].hw_value; - basic_rate_set >>= 1; + u8 *ptr = beacon->data + + offsetof(struct ieee80211_mgmt, u.beacon.variable); + + /* find the location of the ssid in the beacon */ + while (ptr < beacon->data + beacon->len) { + if (ptr[0] == WLAN_EID_SSID) { + wl->ssid_len = ptr[1]; + memcpy(wl->ssid, ptr+2, wl->ssid_len); + return; + } + ptr += ptr[1]; } - - return enabled_rates; + wl1271_error("ad-hoc beacon template has no SSID!\n"); } static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, @@ -1463,6 +1605,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, { enum wl1271_cmd_ps_mode mode; struct wl1271 *wl = hw->priv; + bool do_join = false; int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed"); @@ -1473,9 +1616,67 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, if (ret < 0) goto out; + if (wl->bss_type == BSS_TYPE_IBSS) { + /* FIXME: This implements rudimentary ad-hoc support - + proper templates are on the wish list and notification + on when they change. This patch will update the templates + on every call to this function. */ + struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); + + if (beacon) { + struct ieee80211_hdr *hdr; + + wl1271_ssid_set(wl, beacon); + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, + beacon->data, + beacon->len); + + if (ret < 0) { + dev_kfree_skb(beacon); + goto out_sleep; + } + + hdr = (struct ieee80211_hdr *) beacon->data; + hdr->frame_control = cpu_to_le16( + IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_PROBE_RESP); + + ret = wl1271_cmd_template_set(wl, + CMD_TEMPL_PROBE_RESPONSE, + beacon->data, + beacon->len); + dev_kfree_skb(beacon); + if (ret < 0) + goto out_sleep; + + /* Need to update the SSID (for filtering etc) */ + do_join = true; + } + } + + if ((changed & BSS_CHANGED_BSSID) && + /* + * Now we know the correct bssid, so we send a new join command + * and enable the BSSID filter + */ + memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) { + wl->rx_config |= CFG_BSSID_FILTER_EN; + memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); + ret = wl1271_cmd_build_null_data(wl); + if (ret < 0) { + wl1271_warning("cmd buld null data failed %d", + ret); + goto out_sleep; + } + + /* Need to update the BSSID (for filtering etc) */ + do_join = true; + } + if (changed & BSS_CHANGED_ASSOC) { if (bss_conf->assoc) { wl->aid = bss_conf->aid; + set_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags); /* * with wl1271, we don't need to update the @@ -1492,15 +1693,16 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, goto out_sleep; /* If we want to go in PSM but we're not there yet */ - if (wl->psm_requested && !wl->psm) { + if (test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags) && + !test_bit(WL1271_FLAG_PSM, &wl->flags)) { mode = STATION_POWER_SAVE_MODE; - ret = wl1271_ps_set_mode(wl, mode); + ret = wl1271_ps_set_mode(wl, mode, true); if (ret < 0) goto out_sleep; } } else { /* use defaults when not associated */ - wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET; + clear_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags); wl->aid = 0; } @@ -1535,15 +1737,13 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, } } - if (changed & BSS_CHANGED_BASIC_RATES) { - wl->basic_rate_set = wl1271_enabled_rates_get( - wl, bss_conf->basic_rates); - - ret = wl1271_acx_rate_policies(wl, wl->basic_rate_set); + if (do_join) { + ret = wl1271_cmd_join(wl); if (ret < 0) { - wl1271_warning("Set rate policies failed %d", ret); + wl1271_warning("cmd join failed %d", ret); goto out_sleep; } + set_bit(WL1271_FLAG_JOINED, &wl->flags); } out_sleep: @@ -1553,6 +1753,43 @@ out: mutex_unlock(&wl->mutex); } +static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct wl1271 *wl = hw->priv; + int ret; + + mutex_lock(&wl->mutex); + + wl1271_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue); + + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + + ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue), + params->cw_min, params->cw_max, + params->aifs, params->txop); + 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), + CONF_PS_SCHEME_LEGACY_PSPOLL, + CONF_ACK_POLICY_LEGACY, 0, 0); + if (ret < 0) + goto out_sleep; + +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[] = { @@ -1599,19 +1836,19 @@ static struct ieee80211_rate wl1271_rates[] = { /* can't be const, mac80211 writes to this */ static struct ieee80211_channel wl1271_channels[] = { - { .hw_value = 1, .center_freq = 2412}, - { .hw_value = 2, .center_freq = 2417}, - { .hw_value = 3, .center_freq = 2422}, - { .hw_value = 4, .center_freq = 2427}, - { .hw_value = 5, .center_freq = 2432}, - { .hw_value = 6, .center_freq = 2437}, - { .hw_value = 7, .center_freq = 2442}, - { .hw_value = 8, .center_freq = 2447}, - { .hw_value = 9, .center_freq = 2452}, - { .hw_value = 10, .center_freq = 2457}, - { .hw_value = 11, .center_freq = 2462}, - { .hw_value = 12, .center_freq = 2467}, - { .hw_value = 13, .center_freq = 2472}, + { .hw_value = 1, .center_freq = 2412, .max_power = 25 }, + { .hw_value = 2, .center_freq = 2417, .max_power = 25 }, + { .hw_value = 3, .center_freq = 2422, .max_power = 25 }, + { .hw_value = 4, .center_freq = 2427, .max_power = 25 }, + { .hw_value = 5, .center_freq = 2432, .max_power = 25 }, + { .hw_value = 6, .center_freq = 2437, .max_power = 25 }, + { .hw_value = 7, .center_freq = 2442, .max_power = 25 }, + { .hw_value = 8, .center_freq = 2447, .max_power = 25 }, + { .hw_value = 9, .center_freq = 2452, .max_power = 25 }, + { .hw_value = 10, .center_freq = 2457, .max_power = 25 }, + { .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 }, }; /* can't be const, mac80211 writes to this */ @@ -1718,6 +1955,8 @@ static const struct ieee80211_ops wl1271_ops = { .hw_scan = wl1271_op_hw_scan, .bss_info_changed = wl1271_op_bss_info_changed, .set_rts_threshold = wl1271_op_set_rts_threshold, + .conf_tx = wl1271_op_conf_tx, + CFG80211_TESTMODE_CMD(wl1271_tm_cmd) }; static int wl1271_register_hw(struct wl1271 *wl) @@ -1757,7 +1996,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) IEEE80211_HW_BEACON_FILTER | IEEE80211_HW_SUPPORTS_PS; - wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC); wl->hw->wiphy->max_scan_ssids = 1; wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1271_band_2ghz; @@ -1785,24 +2025,17 @@ static struct platform_device wl1271_device = { }; #define WL1271_DEFAULT_CHANNEL 0 -static int __devinit wl1271_probe(struct spi_device *spi) + +static struct ieee80211_hw *wl1271_alloc_hw(void) { - struct wl12xx_platform_data *pdata; struct ieee80211_hw *hw; struct wl1271 *wl; - int ret, i; - static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf}; - - pdata = spi->dev.platform_data; - if (!pdata) { - wl1271_error("no platform data"); - return -ENODEV; - } + int i; hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops); if (!hw) { wl1271_error("could not alloc ieee80211_hw"); - return -ENOMEM; + return ERR_PTR(-ENOMEM); } wl = hw->priv; @@ -1811,44 +2044,80 @@ static int __devinit wl1271_probe(struct spi_device *spi) INIT_LIST_HEAD(&wl->list); wl->hw = hw; - dev_set_drvdata(&spi->dev, wl); - wl->spi = spi; skb_queue_head_init(&wl->tx_queue); INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work); wl->channel = WL1271_DEFAULT_CHANNEL; - wl->scanning = false; wl->default_key = 0; wl->rx_counter = 0; wl->rx_config = WL1271_DEFAULT_RX_CONFIG; wl->rx_filter = WL1271_DEFAULT_RX_FILTER; - wl->elp = false; - wl->psm = 0; - wl->psm_requested = false; wl->psm_entry_retry = 0; - wl->tx_queue_stopped = false; wl->power_level = WL1271_DEFAULT_POWER_LEVEL; - wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET; + wl->basic_rate_set = 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->joined = false; + wl->flags = 0; for (i = 0; i < ACX_TX_DESCRIPTORS; i++) wl->tx_frames[i] = NULL; spin_lock_init(&wl->wl_lock); - /* - * In case our MAC address is not correctly set, - * we use a random but Nokia MAC. - */ - memcpy(wl->mac_addr, nokia_oui, 3); - get_random_bytes(wl->mac_addr + 3, 3); - wl->state = WL1271_STATE_OFF; mutex_init(&wl->mutex); + /* Apply default driver configuration. */ + wl1271_conf_init(wl); + + return hw; +} + +int wl1271_free_hw(struct wl1271 *wl) +{ + ieee80211_unregister_hw(wl->hw); + + wl1271_debugfs_exit(wl); + + kfree(wl->target_mem_map); + vfree(wl->fw); + wl->fw = NULL; + kfree(wl->nvs); + wl->nvs = NULL; + + kfree(wl->fw_status); + kfree(wl->tx_res_if); + + ieee80211_free_hw(wl->hw); + + return 0; +} + +static int __devinit wl1271_probe(struct spi_device *spi) +{ + struct wl12xx_platform_data *pdata; + struct ieee80211_hw *hw; + struct wl1271 *wl; + int ret; + + pdata = spi->dev.platform_data; + if (!pdata) { + wl1271_error("no platform data"); + return -ENODEV; + } + + hw = wl1271_alloc_hw(); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + wl = hw->priv; + + dev_set_drvdata(&spi->dev, wl); + wl->spi = spi; + /* This is the only SPI value that we need to set here, the rest * comes from the board-peripherals file */ spi->bits_per_word = 32; @@ -1890,9 +2159,6 @@ static int __devinit wl1271_probe(struct spi_device *spi) } dev_set_drvdata(&wl1271_device.dev, wl); - /* Apply default driver configuration. */ - wl1271_conf_init(wl); - ret = wl1271_init_ieee80211(wl); if (ret) goto out_platform; @@ -1923,21 +2189,10 @@ static int __devexit wl1271_remove(struct spi_device *spi) { struct wl1271 *wl = dev_get_drvdata(&spi->dev); - ieee80211_unregister_hw(wl->hw); - - wl1271_debugfs_exit(wl); platform_device_unregister(&wl1271_device); free_irq(wl->irq, wl); - kfree(wl->target_mem_map); - vfree(wl->fw); - wl->fw = NULL; - kfree(wl->nvs); - wl->nvs = NULL; - kfree(wl->fw_status); - kfree(wl->tx_res_if); - - ieee80211_free_hw(wl->hw); + wl1271_free_hw(wl); return 0; } diff --git a/drivers/net/wireless/wl12xx/wl1271_ps.c b/drivers/net/wireless/wl12xx/wl1271_ps.c index 507cd91d7eed..e2b1ebf096e8 100644 --- a/drivers/net/wireless/wl12xx/wl1271_ps.c +++ b/drivers/net/wireless/wl12xx/wl1271_ps.c @@ -24,6 +24,7 @@ #include "wl1271_reg.h" #include "wl1271_ps.h" #include "wl1271_spi.h" +#include "wl1271_io.h" #define WL1271_WAKEUP_TIMEOUT 500 @@ -39,12 +40,13 @@ void wl1271_elp_work(struct work_struct *work) mutex_lock(&wl->mutex); - if (wl->elp || !wl->psm) + if (test_bit(WL1271_FLAG_IN_ELP, &wl->flags) || + !test_bit(WL1271_FLAG_PSM, &wl->flags)) goto out; wl1271_debug(DEBUG_PSM, "chip to elp"); wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP); - wl->elp = true; + set_bit(WL1271_FLAG_IN_ELP, &wl->flags); out: mutex_unlock(&wl->mutex); @@ -55,7 +57,7 @@ out: /* Routines to toggle sleep mode while in ELP */ void wl1271_ps_elp_sleep(struct wl1271 *wl) { - if (wl->psm) { + if (test_bit(WL1271_FLAG_PSM, &wl->flags)) { cancel_delayed_work(&wl->elp_work); ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, msecs_to_jiffies(ELP_ENTRY_DELAY)); @@ -70,7 +72,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake) u32 start_time = jiffies; bool pending = false; - if (!wl->elp) + if (!test_bit(WL1271_FLAG_IN_ELP, &wl->flags)) return 0; wl1271_debug(DEBUG_PSM, "waking up chip from elp"); @@ -101,7 +103,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake) } } - wl->elp = false; + clear_bit(WL1271_FLAG_IN_ELP, &wl->flags); wl1271_debug(DEBUG_PSM, "wakeup time: %u ms", jiffies_to_msecs(jiffies - start_time)); @@ -117,7 +119,8 @@ out: return 0; } -int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode) +int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, + bool send) { int ret; @@ -125,25 +128,11 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode) case STATION_POWER_SAVE_MODE: wl1271_debug(DEBUG_PSM, "entering psm"); - /* enable beacon filtering */ - ret = wl1271_acx_beacon_filter_opt(wl, true); + ret = wl1271_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE, send); if (ret < 0) return ret; - /* enable beacon early termination */ - ret = wl1271_acx_bet_enable(wl, true); - if (ret < 0) - return ret; - - ret = wl1271_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE); - if (ret < 0) - return ret; - - wl1271_ps_elp_sleep(wl); - if (ret < 0) - return ret; - - wl->psm = 1; + set_bit(WL1271_FLAG_PSM, &wl->flags); break; case STATION_ACTIVE_MODE: default: @@ -162,11 +151,11 @@ 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); + ret = wl1271_cmd_ps_mode(wl, STATION_ACTIVE_MODE, send); if (ret < 0) return ret; - wl->psm = 0; + clear_bit(WL1271_FLAG_PSM, &wl->flags); break; } diff --git a/drivers/net/wireless/wl12xx/wl1271_ps.h b/drivers/net/wireless/wl12xx/wl1271_ps.h index 779653d0ae85..940276f517a4 100644 --- a/drivers/net/wireless/wl12xx/wl1271_ps.h +++ b/drivers/net/wireless/wl12xx/wl1271_ps.h @@ -27,7 +27,8 @@ #include "wl1271.h" #include "wl1271_acx.h" -int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode); +int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, + bool send); 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); diff --git a/drivers/net/wireless/wl12xx/wl1271_reg.h b/drivers/net/wireless/wl12xx/wl1271_reg.h index 1f237389d1c7..990960771528 100644 --- a/drivers/net/wireless/wl12xx/wl1271_reg.h +++ b/drivers/net/wireless/wl12xx/wl1271_reg.h @@ -62,73 +62,10 @@ #define WL1271_SLV_REG_DATA (REGISTERS_BASE + 0x0008) #define WL1271_SLV_REG_ADATA (REGISTERS_BASE + 0x000c) #define WL1271_SLV_MEM_DATA (REGISTERS_BASE + 0x0018) -/* - * Interrupt registers. - * 64 bit interrupt sources registers ws ced. - * sme interupts were removed and new ones were added. - * Order was changed. - */ -#define FIQ_MASK (REGISTERS_BASE + 0x0400) -#define FIQ_MASK_L (REGISTERS_BASE + 0x0400) -#define FIQ_MASK_H (REGISTERS_BASE + 0x0404) -#define FIQ_MASK_SET (REGISTERS_BASE + 0x0408) -#define FIQ_MASK_SET_L (REGISTERS_BASE + 0x0408) -#define FIQ_MASK_SET_H (REGISTERS_BASE + 0x040C) -#define FIQ_MASK_CLR (REGISTERS_BASE + 0x0410) -#define FIQ_MASK_CLR_L (REGISTERS_BASE + 0x0410) -#define FIQ_MASK_CLR_H (REGISTERS_BASE + 0x0414) -#define IRQ_MASK (REGISTERS_BASE + 0x0418) -#define IRQ_MASK_L (REGISTERS_BASE + 0x0418) -#define IRQ_MASK_H (REGISTERS_BASE + 0x041C) -#define IRQ_MASK_SET (REGISTERS_BASE + 0x0420) -#define IRQ_MASK_SET_L (REGISTERS_BASE + 0x0420) -#define IRQ_MASK_SET_H (REGISTERS_BASE + 0x0424) -#define IRQ_MASK_CLR (REGISTERS_BASE + 0x0428) -#define IRQ_MASK_CLR_L (REGISTERS_BASE + 0x0428) -#define IRQ_MASK_CLR_H (REGISTERS_BASE + 0x042C) -#define ECPU_MASK (REGISTERS_BASE + 0x0448) -#define FIQ_STS_L (REGISTERS_BASE + 0x044C) -#define FIQ_STS_H (REGISTERS_BASE + 0x0450) -#define IRQ_STS_L (REGISTERS_BASE + 0x0454) -#define IRQ_STS_H (REGISTERS_BASE + 0x0458) -#define INT_STS_ND (REGISTERS_BASE + 0x0464) -#define INT_STS_RAW_L (REGISTERS_BASE + 0x0464) -#define INT_STS_RAW_H (REGISTERS_BASE + 0x0468) -#define INT_STS_CLR (REGISTERS_BASE + 0x04B4) -#define INT_STS_CLR_L (REGISTERS_BASE + 0x04B4) -#define INT_STS_CLR_H (REGISTERS_BASE + 0x04B8) -#define INT_ACK (REGISTERS_BASE + 0x046C) -#define INT_ACK_L (REGISTERS_BASE + 0x046C) -#define INT_ACK_H (REGISTERS_BASE + 0x0470) -#define INT_TRIG (REGISTERS_BASE + 0x0474) -#define INT_TRIG_L (REGISTERS_BASE + 0x0474) -#define INT_TRIG_H (REGISTERS_BASE + 0x0478) -#define HOST_STS_L (REGISTERS_BASE + 0x045C) -#define HOST_STS_H (REGISTERS_BASE + 0x0460) -#define HOST_MASK (REGISTERS_BASE + 0x0430) -#define HOST_MASK_L (REGISTERS_BASE + 0x0430) -#define HOST_MASK_H (REGISTERS_BASE + 0x0434) -#define HOST_MASK_SET (REGISTERS_BASE + 0x0438) -#define HOST_MASK_SET_L (REGISTERS_BASE + 0x0438) -#define HOST_MASK_SET_H (REGISTERS_BASE + 0x043C) -#define HOST_MASK_CLR (REGISTERS_BASE + 0x0440) -#define HOST_MASK_CLR_L (REGISTERS_BASE + 0x0440) -#define HOST_MASK_CLR_H (REGISTERS_BASE + 0x0444) #define ACX_REG_INTERRUPT_TRIG (REGISTERS_BASE + 0x0474) #define ACX_REG_INTERRUPT_TRIG_H (REGISTERS_BASE + 0x0478) -/* Host Interrupts*/ -#define HINT_MASK (REGISTERS_BASE + 0x0494) -#define HINT_MASK_SET (REGISTERS_BASE + 0x0498) -#define HINT_MASK_CLR (REGISTERS_BASE + 0x049C) -#define HINT_STS_ND_MASKED (REGISTERS_BASE + 0x04A0) -/*1150 spec calls this HINT_STS_RAW*/ -#define HINT_STS_ND (REGISTERS_BASE + 0x04B0) -#define HINT_STS_CLR (REGISTERS_BASE + 0x04A4) -#define HINT_ACK (REGISTERS_BASE + 0x04A8) -#define HINT_TRIG (REGISTERS_BASE + 0x04AC) - /*============================================= Host Interrupt Mask Register - 32bit (RW) ------------------------------------------ @@ -433,16 +370,6 @@ /*=============================================== - Phy regs - ===============================================*/ -#define ACX_PHY_ADDR_REG SBB_ADDR -#define ACX_PHY_DATA_REG SBB_DATA -#define ACX_PHY_CTRL_REG SBB_CTL -#define ACX_PHY_REG_WR_MASK 0x00000001ul -#define ACX_PHY_REG_RD_MASK 0x00000002ul - - -/*=============================================== EEPROM Read/Write Request 32bit RW ------------------------------------------ 1 EE_READ - EEPROM Read Request 1 - Setting this bit @@ -511,28 +438,6 @@ #define ACX_CONT_WIND_MIN_MASK 0x0000007f #define ACX_CONT_WIND_MAX 0x03ff0000 -/* - * Indirect slave register/memory registers - * ---------------------------------------- - */ -#define HW_SLAVE_REG_ADDR_REG 0x00000004 -#define HW_SLAVE_REG_DATA_REG 0x00000008 -#define HW_SLAVE_REG_CTRL_REG 0x0000000c - -#define SLAVE_AUTO_INC 0x00010000 -#define SLAVE_NO_AUTO_INC 0x00000000 -#define SLAVE_HOST_LITTLE_ENDIAN 0x00000000 - -#define HW_SLAVE_MEM_ADDR_REG SLV_MEM_ADDR -#define HW_SLAVE_MEM_DATA_REG SLV_MEM_DATA -#define HW_SLAVE_MEM_CTRL_REG SLV_MEM_CTL -#define HW_SLAVE_MEM_ENDIAN_REG SLV_END_CTL - -#define HW_FUNC_EVENT_INT_EN 0x8000 -#define HW_FUNC_EVENT_MASK_REG 0x00000034 - -#define ACX_MAC_TIMESTAMP_REG (MAC_TIMESTAMP) - /*=============================================== HI_CFG Interface Configuration Register Values ------------------------------------------ @@ -647,10 +552,6 @@ b12-b0 - Supported Rate indicator bits as defined below. ******************************************************************************/ -#define TNETW1251_CHIP_ID_PG1_0 0x07010101 -#define TNETW1251_CHIP_ID_PG1_1 0x07020101 -#define TNETW1251_CHIP_ID_PG1_2 0x07030101 - /************************************************************************* Interrupt Trigger Register (Host -> WiLink) diff --git a/drivers/net/wireless/wl12xx/wl1271_rx.c b/drivers/net/wireless/wl12xx/wl1271_rx.c index ca645f38109b..6730f5b96e76 100644 --- a/drivers/net/wireless/wl12xx/wl1271_rx.c +++ b/drivers/net/wireless/wl12xx/wl1271_rx.c @@ -26,6 +26,7 @@ #include "wl1271_reg.h" #include "wl1271_rx.h" #include "wl1271_spi.h" +#include "wl1271_io.h" static u8 wl1271_rx_get_mem_block(struct wl1271_fw_status *status, u32 drv_rx_counter) @@ -166,7 +167,7 @@ static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length) } buf = skb_put(skb, length); - wl1271_spi_read(wl, WL1271_SLV_MEM_DATA, buf, length, true); + wl1271_read(wl, WL1271_SLV_MEM_DATA, buf, length, true); /* the data read starts with the descriptor */ desc = (struct wl1271_rx_descriptor *) buf; @@ -210,15 +211,13 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status) wl->rx_mem_pool_addr.addr + 4; /* Choose the block we want to read */ - wl1271_spi_write(wl, WL1271_SLV_REG_DATA, - &wl->rx_mem_pool_addr, - sizeof(wl->rx_mem_pool_addr), false); + wl1271_write(wl, WL1271_SLV_REG_DATA, &wl->rx_mem_pool_addr, + sizeof(wl->rx_mem_pool_addr), false); wl1271_rx_handle_data(wl, buf_size); wl->rx_counter++; drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK; + wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter); } - - wl1271_spi_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter); } diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.c b/drivers/net/wireless/wl12xx/wl1271_spi.c index 02978a16e732..67a82934f36e 100644 --- a/drivers/net/wireless/wl12xx/wl1271_spi.c +++ b/drivers/net/wireless/wl12xx/wl1271_spi.c @@ -30,28 +30,6 @@ #include "wl12xx_80211.h" #include "wl1271_spi.h" -static int wl1271_translate_addr(struct wl1271 *wl, int addr) -{ - /* - * To translate, first check to which window of addresses the - * particular address belongs. Then subtract the starting address - * of that window from the address. Then, add offset of the - * translated region. - * - * The translated regions occur next to each other in physical device - * memory, so just add the sizes of the preceeding address regions to - * get the offset to the new region. - * - * Currently, only the two first regions are addressed, and the - * assumption is that all addresses will fall into either of those - * two. - */ - if ((addr >= wl->part.reg.start) && - (addr < wl->part.reg.start + wl->part.reg.size)) - return addr - wl->part.reg.start + wl->part.mem.size; - else - return addr - wl->part.mem.start; -} void wl1271_spi_reset(struct wl1271 *wl) { @@ -133,67 +111,6 @@ void wl1271_spi_init(struct wl1271 *wl) wl1271_dump(DEBUG_SPI, "spi init -> ", cmd, WSPI_INIT_CMD_LEN); } -/* Set the SPI partitions to access the chip addresses - * - * To simplify driver code, a fixed (virtual) memory map is defined for - * register and memory addresses. Because in the chipset, in different stages - * of operation, those addresses will move around, an address translation - * mechanism is required. - * - * There are four partitions (three memory and one register partition), - * which are mapped to two different areas of the hardware memory. - * - * Virtual address - * space - * - * | | - * ...+----+--> mem.start - * Physical address ... | | - * space ... | | [PART_0] - * ... | | - * 00000000 <--+----+... ...+----+--> mem.start + mem.size - * | | ... | | - * |MEM | ... | | - * | | ... | | - * mem.size <--+----+... | | {unused area) - * | | ... | | - * |REG | ... | | - * mem.size | | ... | | - * + <--+----+... ...+----+--> reg.start - * reg.size | | ... | | - * |MEM2| ... | | [PART_1] - * | | ... | | - * ...+----+--> reg.start + reg.size - * | | - * - */ -int wl1271_set_partition(struct wl1271 *wl, - struct wl1271_partition_set *p) -{ - /* copy partition info */ - memcpy(&wl->part, p, sizeof(*p)); - - wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", - p->mem.start, p->mem.size); - wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", - p->reg.start, p->reg.size); - wl1271_debug(DEBUG_SPI, "mem2_start %08X mem2_size %08X", - p->mem2.start, p->mem2.size); - wl1271_debug(DEBUG_SPI, "mem3_start %08X mem3_size %08X", - p->mem3.start, p->mem3.size); - - /* write partition info to the chipset */ - wl1271_raw_write32(wl, HW_PART0_START_ADDR, p->mem.start); - wl1271_raw_write32(wl, HW_PART0_SIZE_ADDR, p->mem.size); - wl1271_raw_write32(wl, HW_PART1_START_ADDR, p->reg.start); - wl1271_raw_write32(wl, HW_PART1_SIZE_ADDR, p->reg.size); - wl1271_raw_write32(wl, HW_PART2_START_ADDR, p->mem2.start); - wl1271_raw_write32(wl, HW_PART2_SIZE_ADDR, p->mem2.size); - wl1271_raw_write32(wl, HW_PART3_START_ADDR, p->mem3.start); - - return 0; -} - #define WL1271_BUSY_WORD_TIMEOUT 1000 /* FIXME: Check busy words, removed due to SPI bug */ @@ -338,78 +255,3 @@ void wl1271_spi_raw_write(struct wl1271 *wl, int addr, void *buf, wl1271_dump(DEBUG_SPI, "spi_write cmd -> ", cmd, sizeof(*cmd)); wl1271_dump(DEBUG_SPI, "spi_write buf -> ", buf, len); } - -void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf, size_t len, - bool fixed) -{ - int physical; - - physical = wl1271_translate_addr(wl, addr); - - wl1271_spi_raw_read(wl, physical, buf, len, fixed); -} - -void wl1271_spi_write(struct wl1271 *wl, int addr, void *buf, size_t len, - bool fixed) -{ - int physical; - - physical = wl1271_translate_addr(wl, addr); - - wl1271_spi_raw_write(wl, physical, buf, len, fixed); -} - -u32 wl1271_spi_read32(struct wl1271 *wl, int addr) -{ - return wl1271_raw_read32(wl, wl1271_translate_addr(wl, addr)); -} - -void wl1271_spi_write32(struct wl1271 *wl, int addr, u32 val) -{ - wl1271_raw_write32(wl, wl1271_translate_addr(wl, addr), val); -} - -void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val) -{ - /* write address >> 1 + 0x30000 to OCP_POR_CTR */ - addr = (addr >> 1) + 0x30000; - wl1271_spi_write32(wl, OCP_POR_CTR, addr); - - /* write value to OCP_POR_WDATA */ - wl1271_spi_write32(wl, OCP_DATA_WRITE, val); - - /* write 1 to OCP_CMD */ - wl1271_spi_write32(wl, OCP_CMD, OCP_CMD_WRITE); -} - -u16 wl1271_top_reg_read(struct wl1271 *wl, int addr) -{ - u32 val; - int timeout = OCP_CMD_LOOP; - - /* write address >> 1 + 0x30000 to OCP_POR_CTR */ - addr = (addr >> 1) + 0x30000; - wl1271_spi_write32(wl, OCP_POR_CTR, addr); - - /* write 2 to OCP_CMD */ - wl1271_spi_write32(wl, OCP_CMD, OCP_CMD_READ); - - /* poll for data ready */ - do { - val = wl1271_spi_read32(wl, OCP_DATA_READ); - timeout--; - } while (!(val & OCP_READY_MASK) && timeout); - - if (!timeout) { - wl1271_warning("Top register access timed out."); - return 0xffff; - } - - /* check data status and return if OK */ - if ((val & OCP_STATUS_MASK) == OCP_STATUS_OK) - return val & 0xffff; - else { - wl1271_warning("Top register access returned error."); - return 0xffff; - } -} diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.h b/drivers/net/wireless/wl12xx/wl1271_spi.h index cb7df1c56314..a803596dad4a 100644 --- a/drivers/net/wireless/wl12xx/wl1271_spi.h +++ b/drivers/net/wireless/wl12xx/wl1271_spi.h @@ -90,37 +90,7 @@ void wl1271_spi_raw_write(struct wl1271 *wl, int addr, void *buf, void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf, size_t len, bool fixed); -/* Translated target IO */ -void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf, size_t len, - bool fixed); -void wl1271_spi_write(struct wl1271 *wl, int addr, void *buf, size_t len, - bool fixed); -u32 wl1271_spi_read32(struct wl1271 *wl, int addr); -void wl1271_spi_write32(struct wl1271 *wl, int addr, u32 val); - -/* Top Register IO */ -void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val); -u16 wl1271_top_reg_read(struct wl1271 *wl, int addr); - /* INIT and RESET words */ void wl1271_spi_reset(struct wl1271 *wl); void wl1271_spi_init(struct wl1271 *wl); -int wl1271_set_partition(struct wl1271 *wl, - struct wl1271_partition_set *p); - -static inline u32 wl1271_raw_read32(struct wl1271 *wl, int addr) -{ - wl1271_spi_raw_read(wl, addr, &wl->buffer_32, - sizeof(wl->buffer_32), false); - - return wl->buffer_32; -} - -static inline void wl1271_raw_write32(struct wl1271 *wl, int addr, u32 val) -{ - wl->buffer_32 = val; - wl1271_spi_raw_write(wl, addr, &wl->buffer_32, - sizeof(wl->buffer_32), false); -} - #endif /* __WL1271_SPI_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1271_testmode.c b/drivers/net/wireless/wl12xx/wl1271_testmode.c new file mode 100644 index 000000000000..3919102e942e --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_testmode.c @@ -0,0 +1,283 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2010 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#include "wl1271_testmode.h" + +#include <net/genetlink.h> + +#include "wl1271.h" +#include "wl1271_spi.h" +#include "wl1271_acx.h" + +#define WL1271_TM_MAX_DATA_LENGTH 1024 + +enum wl1271_tm_commands { + WL1271_TM_CMD_UNSPEC, + WL1271_TM_CMD_TEST, + WL1271_TM_CMD_INTERROGATE, + WL1271_TM_CMD_CONFIGURE, + WL1271_TM_CMD_NVS_PUSH, + WL1271_TM_CMD_SET_PLT_MODE, + + __WL1271_TM_CMD_AFTER_LAST +}; +#define WL1271_TM_CMD_MAX (__WL1271_TM_CMD_AFTER_LAST - 1) + +enum wl1271_tm_attrs { + WL1271_TM_ATTR_UNSPEC, + WL1271_TM_ATTR_CMD_ID, + WL1271_TM_ATTR_ANSWER, + WL1271_TM_ATTR_DATA, + WL1271_TM_ATTR_IE_ID, + WL1271_TM_ATTR_PLT_MODE, + + __WL1271_TM_ATTR_AFTER_LAST +}; +#define WL1271_TM_ATTR_MAX (__WL1271_TM_ATTR_AFTER_LAST - 1) + +static struct nla_policy wl1271_tm_policy[WL1271_TM_ATTR_MAX + 1] = { + [WL1271_TM_ATTR_CMD_ID] = { .type = NLA_U32 }, + [WL1271_TM_ATTR_ANSWER] = { .type = NLA_U8 }, + [WL1271_TM_ATTR_DATA] = { .type = NLA_BINARY, + .len = WL1271_TM_MAX_DATA_LENGTH }, + [WL1271_TM_ATTR_IE_ID] = { .type = NLA_U32 }, + [WL1271_TM_ATTR_PLT_MODE] = { .type = NLA_U32 }, +}; + + +static int wl1271_tm_cmd_test(struct wl1271 *wl, struct nlattr *tb[]) +{ + int buf_len, ret, len; + struct sk_buff *skb; + void *buf; + u8 answer = 0; + + wl1271_debug(DEBUG_TESTMODE, "testmode cmd test"); + + if (!tb[WL1271_TM_ATTR_DATA]) + return -EINVAL; + + buf = nla_data(tb[WL1271_TM_ATTR_DATA]); + buf_len = nla_len(tb[WL1271_TM_ATTR_DATA]); + + if (tb[WL1271_TM_ATTR_ANSWER]) + answer = nla_get_u8(tb[WL1271_TM_ATTR_ANSWER]); + + if (buf_len > sizeof(struct wl1271_command)) + return -EMSGSIZE; + + mutex_lock(&wl->mutex); + ret = wl1271_cmd_test(wl, buf, buf_len, answer); + mutex_unlock(&wl->mutex); + + if (ret < 0) { + wl1271_warning("testmode cmd test failed: %d", ret); + return ret; + } + + if (answer) { + len = nla_total_size(buf_len); + skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, len); + if (!skb) + return -ENOMEM; + + NLA_PUT(skb, WL1271_TM_ATTR_DATA, buf_len, buf); + ret = cfg80211_testmode_reply(skb); + if (ret < 0) + return ret; + } + + return 0; + +nla_put_failure: + kfree_skb(skb); + return -EMSGSIZE; +} + +static int wl1271_tm_cmd_interrogate(struct wl1271 *wl, struct nlattr *tb[]) +{ + int ret; + struct wl1271_command *cmd; + struct sk_buff *skb; + u8 ie_id; + + wl1271_debug(DEBUG_TESTMODE, "testmode cmd interrogate"); + + if (!tb[WL1271_TM_ATTR_IE_ID]) + return -EINVAL; + + ie_id = nla_get_u8(tb[WL1271_TM_ATTR_IE_ID]); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + mutex_lock(&wl->mutex); + ret = wl1271_cmd_interrogate(wl, ie_id, cmd, sizeof(*cmd)); + mutex_unlock(&wl->mutex); + + if (ret < 0) { + wl1271_warning("testmode cmd interrogate failed: %d", ret); + return ret; + } + + skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + NLA_PUT(skb, WL1271_TM_ATTR_DATA, sizeof(*cmd), cmd); + + return 0; + +nla_put_failure: + kfree_skb(skb); + return -EMSGSIZE; +} + +static int wl1271_tm_cmd_configure(struct wl1271 *wl, struct nlattr *tb[]) +{ + int buf_len, ret; + void *buf; + u8 ie_id; + + wl1271_debug(DEBUG_TESTMODE, "testmode cmd configure"); + + if (!tb[WL1271_TM_ATTR_DATA]) + return -EINVAL; + if (!tb[WL1271_TM_ATTR_IE_ID]) + return -EINVAL; + + ie_id = nla_get_u8(tb[WL1271_TM_ATTR_IE_ID]); + buf = nla_data(tb[WL1271_TM_ATTR_DATA]); + buf_len = nla_len(tb[WL1271_TM_ATTR_DATA]); + + if (buf_len > sizeof(struct wl1271_command)) + return -EMSGSIZE; + + mutex_lock(&wl->mutex); + ret = wl1271_cmd_configure(wl, ie_id, buf, buf_len); + mutex_unlock(&wl->mutex); + + if (ret < 0) { + wl1271_warning("testmode cmd configure failed: %d", ret); + return ret; + } + + return 0; +} + +static int wl1271_tm_cmd_nvs_push(struct wl1271 *wl, struct nlattr *tb[]) +{ + int ret = 0; + size_t len; + void *buf; + + wl1271_debug(DEBUG_TESTMODE, "testmode cmd nvs push"); + + if (!tb[WL1271_TM_ATTR_DATA]) + return -EINVAL; + + buf = nla_data(tb[WL1271_TM_ATTR_DATA]); + len = nla_len(tb[WL1271_TM_ATTR_DATA]); + + if (len != sizeof(struct wl1271_nvs_file)) { + wl1271_error("nvs size is not as expected: %zu != %zu", + len, sizeof(struct wl1271_nvs_file)); + return -EMSGSIZE; + } + + mutex_lock(&wl->mutex); + + kfree(wl->nvs); + + wl->nvs = kmalloc(sizeof(struct wl1271_nvs_file), GFP_KERNEL); + if (!wl->nvs) { + wl1271_error("could not allocate memory for the nvs file"); + ret = -ENOMEM; + goto out; + } + + memcpy(wl->nvs, buf, len); + + wl1271_debug(DEBUG_TESTMODE, "testmode pushed nvs"); + +out: + mutex_unlock(&wl->mutex); + + return ret; +} + +static int wl1271_tm_cmd_set_plt_mode(struct wl1271 *wl, struct nlattr *tb[]) +{ + u32 val; + int ret; + + wl1271_debug(DEBUG_TESTMODE, "testmode cmd set plt mode"); + + if (!tb[WL1271_TM_ATTR_PLT_MODE]) + return -EINVAL; + + val = nla_get_u32(tb[WL1271_TM_ATTR_PLT_MODE]); + + switch (val) { + case 0: + ret = wl1271_plt_stop(wl); + break; + case 1: + ret = wl1271_plt_start(wl); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +int wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len) +{ + struct wl1271 *wl = hw->priv; + struct nlattr *tb[WL1271_TM_ATTR_MAX + 1]; + int err; + + err = nla_parse(tb, WL1271_TM_ATTR_MAX, data, len, wl1271_tm_policy); + if (err) + return err; + + if (!tb[WL1271_TM_ATTR_CMD_ID]) + return -EINVAL; + + switch (nla_get_u32(tb[WL1271_TM_ATTR_CMD_ID])) { + case WL1271_TM_CMD_TEST: + return wl1271_tm_cmd_test(wl, tb); + case WL1271_TM_CMD_INTERROGATE: + return wl1271_tm_cmd_interrogate(wl, tb); + case WL1271_TM_CMD_CONFIGURE: + return wl1271_tm_cmd_configure(wl, tb); + case WL1271_TM_CMD_NVS_PUSH: + return wl1271_tm_cmd_nvs_push(wl, tb); + case WL1271_TM_CMD_SET_PLT_MODE: + return wl1271_tm_cmd_set_plt_mode(wl, tb); + default: + return -EOPNOTSUPP; + } +} diff --git a/drivers/net/wireless/wl12xx/wl1271_testmode.h b/drivers/net/wireless/wl12xx/wl1271_testmode.h new file mode 100644 index 000000000000..c196d28f9d9d --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_testmode.h @@ -0,0 +1,31 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2010 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL1271_TESTMODE_H__ +#define __WL1271_TESTMODE_H__ + +#include <net/mac80211.h> + +int wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len); + +#endif /* __WL1271_TESTMODE_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.c b/drivers/net/wireless/wl12xx/wl1271_tx.c index 00af065c77c2..811e739d05bf 100644 --- a/drivers/net/wireless/wl12xx/wl1271_tx.c +++ b/drivers/net/wireless/wl12xx/wl1271_tx.c @@ -26,6 +26,7 @@ #include "wl1271.h" #include "wl1271_spi.h" +#include "wl1271_io.h" #include "wl1271_reg.h" #include "wl1271_ps.h" #include "wl1271_tx.h" @@ -87,7 +88,7 @@ static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, u32 extra, struct ieee80211_tx_info *control) { struct wl1271_tx_hw_descr *desc; - int pad; + int pad, ac; u16 tx_attr; desc = (struct wl1271_tx_hw_descr *) skb->data; @@ -107,9 +108,11 @@ static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, /* configure the tx attributes */ tx_attr = wl->session_counter << TX_HW_ATTR_OFST_SESSION_COUNTER; - /* FIXME: do we know the packet priority? can we identify mgmt - packets, and use max prio for them at least? */ - desc->tid = 0; + + /* queue */ + ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); + desc->tid = wl1271_tx_ac_to_tid(ac); + desc->aid = TX_HW_DEFAULT_AID; desc->reserved = 0; @@ -121,6 +124,11 @@ static int 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); @@ -158,11 +166,11 @@ static int wl1271_tx_send_packet(struct wl1271 *wl, struct sk_buff *skb, len = WL1271_TX_ALIGN(skb->len); /* perform a fixed address block write with the packet */ - wl1271_spi_write(wl, WL1271_SLV_MEM_DATA, skb->data, len, true); + wl1271_write(wl, WL1271_SLV_MEM_DATA, skb->data, len, true); /* write packet new counter into the write access register */ wl->tx_packets_count++; - wl1271_spi_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count); + wl1271_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count); desc = (struct wl1271_tx_hw_descr *) skb->data; wl1271_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u (%u words)", @@ -196,6 +204,7 @@ static int wl1271_tx_frame(struct wl1271 *wl, struct sk_buff *skb) ret = wl1271_cmd_set_default_wep_key(wl, idx); if (ret < 0) return ret; + wl->default_key = idx; } } @@ -214,18 +223,50 @@ static int wl1271_tx_frame(struct wl1271 *wl, struct sk_buff *skb) return ret; } +static u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set) +{ + struct ieee80211_supported_band *band; + u32 enabled_rates = 0; + int bit; + + band = wl->hw->wiphy->bands[wl->band]; + for (bit = 0; bit < band->n_bitrates; bit++) { + if (rate_set & 0x1) + enabled_rates |= band->bitrates[bit].hw_value; + rate_set >>= 1; + } + + return enabled_rates; +} + void wl1271_tx_work(struct work_struct *work) { struct wl1271 *wl = container_of(work, struct wl1271, tx_work); struct sk_buff *skb; bool woken_up = false; + u32 sta_rates = 0; 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); + } + mutex_lock(&wl->mutex); if (unlikely(wl->state == WL1271_STATE_OFF)) goto out; + /* if rates have changed, re-configure the rate policy */ + if (unlikely(sta_rates)) { + wl->rate_set = wl1271_tx_enabled_rates_get(wl, sta_rates); + wl1271_acx_rate_policies(wl); + } + while ((skb = skb_dequeue(&wl->tx_queue))) { if (!woken_up) { ret = wl1271_ps_elp_wakeup(wl, false); @@ -240,18 +281,18 @@ void wl1271_tx_work(struct work_struct *work) wl1271_debug(DEBUG_TX, "tx_work: fw buffer full, " "stop queues"); ieee80211_stop_queues(wl->hw); - wl->tx_queue_stopped = true; + set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags); skb_queue_head(&wl->tx_queue, skb); goto out; } else if (ret < 0) { dev_kfree_skb(skb); goto out; - } else if (wl->tx_queue_stopped) { + } else if (test_and_clear_bit(WL1271_FLAG_TX_QUEUE_STOPPED, + &wl->flags)) { /* firmware buffer has space, restart queues */ wl1271_debug(DEBUG_TX, "complete_packet: waking queues"); ieee80211_wake_queues(wl->hw); - wl->tx_queue_stopped = false; } } @@ -335,8 +376,8 @@ void wl1271_tx_complete(struct wl1271 *wl, u32 count) wl1271_debug(DEBUG_TX, "tx_complete received, packets: %d", count); /* read the tx results from the chipset */ - wl1271_spi_read(wl, le32_to_cpu(memmap->tx_result), - wl->tx_res_if, sizeof(*wl->tx_res_if), false); + wl1271_read(wl, le32_to_cpu(memmap->tx_result), + wl->tx_res_if, sizeof(*wl->tx_res_if), false); /* verify that the result buffer is not getting overrun */ if (count > TX_HW_RESULT_QUEUE_LEN) { @@ -357,10 +398,10 @@ void wl1271_tx_complete(struct wl1271 *wl, u32 count) } /* write host counter to chipset (to ack) */ - wl1271_spi_write32(wl, le32_to_cpu(memmap->tx_result) + - offsetof(struct wl1271_tx_hw_res_if, - tx_result_host_counter), - le32_to_cpu(wl->tx_res_if->tx_result_fw_counter)); + wl1271_write32(wl, le32_to_cpu(memmap->tx_result) + + offsetof(struct wl1271_tx_hw_res_if, + tx_result_host_counter), + le32_to_cpu(wl->tx_res_if->tx_result_fw_counter)); } /* caller must hold wl->mutex */ diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.h b/drivers/net/wireless/wl12xx/wl1271_tx.h index 416396caf0a0..17e405a09caa 100644 --- a/drivers/net/wireless/wl12xx/wl1271_tx.h +++ b/drivers/net/wireless/wl12xx/wl1271_tx.h @@ -123,6 +123,42 @@ struct wl1271_tx_hw_res_if { struct wl1271_tx_hw_res_descr tx_results_queue[TX_HW_RESULT_QUEUE_LEN]; } __attribute__ ((packed)); +static inline int wl1271_tx_get_queue(int queue) +{ + /* FIXME: use best effort until WMM is enabled */ + return CONF_TX_AC_BE; + + switch (queue) { + case 0: + return CONF_TX_AC_VO; + case 1: + return CONF_TX_AC_VI; + case 2: + return CONF_TX_AC_BE; + case 3: + return CONF_TX_AC_BK; + default: + return CONF_TX_AC_BE; + } +} + +/* wl1271 tx descriptor needs the tid and we need to convert it from ac */ +static inline int wl1271_tx_ac_to_tid(int ac) +{ + switch (ac) { + case 0: + return 0; + case 1: + return 2; + case 2: + return 4; + case 3: + return 6; + default: + return 0; + } +} + void wl1271_tx_work(struct work_struct *work); void wl1271_tx_complete(struct wl1271 *wl, u32 count); void wl1271_tx_flush(struct wl1271 *wl); diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index 9d9b263733e6..d90f0a25b9cf 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -869,7 +869,7 @@ int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length) } static int zd_op_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct zd_mac *mac = zd_hw_mac(hw); @@ -877,22 +877,22 @@ static int zd_op_add_interface(struct ieee80211_hw *hw, if (mac->type != NL80211_IFTYPE_UNSPECIFIED) return -EOPNOTSUPP; - switch (conf->type) { + switch (vif->type) { case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: - mac->type = conf->type; + mac->type = vif->type; break; default: return -EOPNOTSUPP; } - return zd_write_mac_addr(&mac->chip, conf->mac_addr); + return zd_write_mac_addr(&mac->chip, vif->addr); } static void zd_op_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct zd_mac *mac = zd_hw_mac(hw); mac->type = NL80211_IFTYPE_UNSPECIFIED; diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index 72d3e437e190..442fc1117326 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -1079,11 +1079,15 @@ static int eject_installer(struct usb_interface *intf) int r; /* Find bulk out endpoint */ - endpoint = &iface_desc->endpoint[1].desc; - if (usb_endpoint_dir_out(endpoint) && - usb_endpoint_xfer_bulk(endpoint)) { - bulk_out_ep = endpoint->bEndpointAddress; - } else { + for (r = 1; r >= 0; r--) { + endpoint = &iface_desc->endpoint[r].desc; + if (usb_endpoint_dir_out(endpoint) && + usb_endpoint_xfer_bulk(endpoint)) { + bulk_out_ep = endpoint->bEndpointAddress; + break; + } + } + if (r == -1) { dev_err(&udev->dev, "zd1211rw: Could not find bulk out endpoint\n"); return -ENODEV; |