From b292219fa5061e2657ecf518b48426913d0ddae6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 12 Oct 2012 10:55:53 +0200 Subject: wireless: use OR operation to set wiphy features The next patch will introduce a flag that is set by default in cfg80211 so drivers and mac80211 need to use |= to set features they have so that they don't clear the already-set feature. We could set the flag in wiphy_register() instead of wiphy_new() to avoid this patch, but then the drivers couldn't *unset* flags they don't want to use even though the implementation is generic. Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 7089f8160ad..99a75d92c6c 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3651,7 +3651,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT, ar->fw_capabilities)) - ar->wiphy->features = NL80211_FEATURE_INACTIVITY_TIMER; + ar->wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER; ar->wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | -- cgit v1.2.3-70-g09d2 From 04b2312a683537eec3dbac013920b0e3cfc06123 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Fri, 12 Oct 2012 12:28:14 +0200 Subject: wireless: drivers: make use of WLAN_EID_VENDOR_SPECIFIC The include file linux/ieee80211.h contains three definitions for the same thing in enum ieee80211_eid due to historic changes: /* Information Element IDs */ enum ieee80211_eid { : WLAN_EID_WPA = 221, WLAN_EID_GENERIC = 221, WLAN_EID_VENDOR_SPECIFIC = 221, : }; The standard refers to this as "vendor specific" element so the other two definitions are better not used. This patch changes the wireless drivers to use one definition, ie. WLAN_EID_VENDOR_SPECIFIC. Cc: Jouni Malinen Cc: Dan Williams Cc: Larry Finger Acked-by: Kalle Valo [ath6kl] Acked-by: Bing Zhao [mwifiex] Acked-by: Stanislav Yakovlev [ipw2x00] Signed-off-by: Arend van Spriel [change libipw as well] Signed-off-by: Johannes Berg --- drivers/net/wireless/airo.c | 2 +- drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 2 +- drivers/net/wireless/hostap/hostap_80211_rx.c | 2 +- drivers/net/wireless/ipw2x00/libipw_rx.c | 6 +++--- drivers/net/wireless/libertas/mesh.c | 2 +- drivers/net/wireless/mwifiex/scan.c | 13 ++++++++----- drivers/net/wireless/mwifiex/sta_ioctl.c | 4 ++-- drivers/net/wireless/orinoco/main.h | 2 +- 9 files changed, 19 insertions(+), 16 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index 3cd05a7173f..57f7db1ac31 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -7433,7 +7433,7 @@ static inline char *airo_translate_scan(struct net_device *dev, num_null_ies++; break; - case WLAN_EID_GENERIC: + case WLAN_EID_VENDOR_SPECIFIC: if (ie[1] >= 4 && ie[2] == 0x00 && ie[3] == 0x50 && diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 99a75d92c6c..277089963eb 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -301,7 +301,7 @@ static bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif) static bool ath6kl_is_wpa_ie(const u8 *pos) { - return pos[0] == WLAN_EID_WPA && pos[1] >= 4 && + return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 && pos[5] == 0x01; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index c1abaa6db59..0e952092ee8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -2679,7 +2679,7 @@ brcmf_find_wpaie(u8 *parse, u32 len) { struct brcmf_tlv *ie; - while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_WPA))) { + while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) { if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len, WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE)) return (struct brcmf_vs_tlv *)ie; diff --git a/drivers/net/wireless/hostap/hostap_80211_rx.c b/drivers/net/wireless/hostap/hostap_80211_rx.c index df7050abe71..d39e3e24077 100644 --- a/drivers/net/wireless/hostap/hostap_80211_rx.c +++ b/drivers/net/wireless/hostap/hostap_80211_rx.c @@ -415,7 +415,7 @@ static void hostap_rx_sta_beacon(local_info_t *local, struct sk_buff *skb, ssid = pos + 2; ssid_len = pos[1]; break; - case WLAN_EID_GENERIC: + case WLAN_EID_VENDOR_SPECIFIC: if (pos[1] >= 4 && pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 && pos[5] == 1) { diff --git a/drivers/net/wireless/ipw2x00/libipw_rx.c b/drivers/net/wireless/ipw2x00/libipw_rx.c index 02e05792323..95a1ca1e895 100644 --- a/drivers/net/wireless/ipw2x00/libipw_rx.c +++ b/drivers/net/wireless/ipw2x00/libipw_rx.c @@ -1108,7 +1108,7 @@ static const char *get_info_element_string(u16 id) MFIE_STRING(ERP_INFO); MFIE_STRING(RSN); MFIE_STRING(EXT_SUPP_RATES); - MFIE_STRING(GENERIC); + MFIE_STRING(VENDOR_SPECIFIC); MFIE_STRING(QOS_PARAMETER); default: return "UNKNOWN"; @@ -1248,8 +1248,8 @@ static int libipw_parse_info_param(struct libipw_info_element LIBIPW_DEBUG_MGMT("WLAN_EID_CHALLENGE: ignored\n"); break; - case WLAN_EID_GENERIC: - LIBIPW_DEBUG_MGMT("WLAN_EID_GENERIC: %d bytes\n", + case WLAN_EID_VENDOR_SPECIFIC: + LIBIPW_DEBUG_MGMT("WLAN_EID_VENDOR_SPECIFIC: %d bytes\n", info_element->len); if (!libipw_parse_qos_info_param_IE(info_element, network)) diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/libertas/mesh.c index 97807751ebc..3e81264db81 100644 --- a/drivers/net/wireless/libertas/mesh.c +++ b/drivers/net/wireless/libertas/mesh.c @@ -101,7 +101,7 @@ static int lbs_mesh_config(struct lbs_private *priv, uint16_t action, switch (action) { case CMD_ACT_MESH_CONFIG_START: - ie->id = WLAN_EID_GENERIC; + ie->id = WLAN_EID_VENDOR_SPECIFIC; ie->val.oui[0] = 0x00; ie->val.oui[1] = 0x50; ie->val.oui[2] = 0x43; diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 00b658d3b6e..5896b1fb4a2 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -153,7 +153,7 @@ mwifiex_is_wpa_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher) if (((bss_desc->bcn_wpa_ie) && ((*(bss_desc->bcn_wpa_ie)).vend_hdr.element_id == - WLAN_EID_WPA))) { + WLAN_EID_VENDOR_SPECIFIC))) { iebody = (struct ie_body *) bss_desc->bcn_wpa_ie->data; oui = &mwifiex_wpa_oui[cipher][0]; ret = mwifiex_search_oui_in_ie(iebody, oui); @@ -202,7 +202,7 @@ mwifiex_is_bss_no_sec(struct mwifiex_private *priv, if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && !priv->sec_info.wpa2_enabled && ((!bss_desc->bcn_wpa_ie) || ((*(bss_desc->bcn_wpa_ie)).vend_hdr.element_id != - WLAN_EID_WPA)) && + WLAN_EID_VENDOR_SPECIFIC)) && ((!bss_desc->bcn_rsn_ie) || ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id != WLAN_EID_RSN)) && @@ -237,7 +237,8 @@ mwifiex_is_bss_wpa(struct mwifiex_private *priv, { if (!priv->sec_info.wep_enabled && priv->sec_info.wpa_enabled && !priv->sec_info.wpa2_enabled && ((bss_desc->bcn_wpa_ie) && - ((*(bss_desc->bcn_wpa_ie)).vend_hdr.element_id == WLAN_EID_WPA)) + ((*(bss_desc->bcn_wpa_ie)). + vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC)) /* * Privacy bit may NOT be set in some APs like * LinkSys WRT54G && bss_desc->privacy @@ -309,7 +310,8 @@ mwifiex_is_bss_adhoc_aes(struct mwifiex_private *priv, if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && !priv->sec_info.wpa2_enabled && ((!bss_desc->bcn_wpa_ie) || - ((*(bss_desc->bcn_wpa_ie)).vend_hdr.element_id != WLAN_EID_WPA)) && + ((*(bss_desc->bcn_wpa_ie)). + vend_hdr.element_id != WLAN_EID_VENDOR_SPECIFIC)) && ((!bss_desc->bcn_rsn_ie) || ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id != WLAN_EID_RSN)) && !priv->sec_info.encryption_mode && bss_desc->privacy) { @@ -329,7 +331,8 @@ mwifiex_is_bss_dynamic_wep(struct mwifiex_private *priv, if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && !priv->sec_info.wpa2_enabled && ((!bss_desc->bcn_wpa_ie) || - ((*(bss_desc->bcn_wpa_ie)).vend_hdr.element_id != WLAN_EID_WPA)) && + ((*(bss_desc->bcn_wpa_ie)). + vend_hdr.element_id != WLAN_EID_VENDOR_SPECIFIC)) && ((!bss_desc->bcn_rsn_ie) || ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id != WLAN_EID_RSN)) && priv->sec_info.encryption_mode && bss_desc->privacy) { diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 0c9f70b2cbe..552d72ed055 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -713,7 +713,7 @@ static int mwifiex_set_wpa_ie_helper(struct mwifiex_private *priv, dev_dbg(priv->adapter->dev, "cmd: Set Wpa_ie_len=%d IE=%#x\n", priv->wpa_ie_len, priv->wpa_ie[0]); - if (priv->wpa_ie[0] == WLAN_EID_WPA) { + if (priv->wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC) { priv->sec_info.wpa_enabled = true; } else if (priv->wpa_ie[0] == WLAN_EID_RSN) { priv->sec_info.wpa2_enabled = true; @@ -1253,7 +1253,7 @@ mwifiex_set_gen_ie_helper(struct mwifiex_private *priv, u8 *ie_data_ptr, } pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr; /* Test to see if it is a WPA IE, if not, then it is a gen IE */ - if (((pvendor_ie->element_id == WLAN_EID_WPA) && + if (((pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) && (!memcmp(pvendor_ie->oui, wpa_oui, sizeof(wpa_oui)))) || (pvendor_ie->element_id == WLAN_EID_RSN)) { diff --git a/drivers/net/wireless/orinoco/main.h b/drivers/net/wireless/orinoco/main.h index 4dadf9880a9..5a8fec26136 100644 --- a/drivers/net/wireless/orinoco/main.h +++ b/drivers/net/wireless/orinoco/main.h @@ -39,7 +39,7 @@ static inline u8 *orinoco_get_wpa_ie(u8 *data, size_t len) { u8 *p = data; while ((p + 2 + WPA_SELECTOR_LEN) < (data + len)) { - if ((p[0] == WLAN_EID_GENERIC) && + if ((p[0] == WLAN_EID_VENDOR_SPECIFIC) && (memcmp(&p[2], WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0)) return p; p += p[1] + 2; -- cgit v1.2.3-70-g09d2 From 78b1775ba03c2edcc8d765dd53a7e171b18e79ac Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Sun, 30 Sep 2012 09:03:20 +0530 Subject: ath9k: Use a helper routine for MCI/FTP tuning Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/gpio.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index d9ed141a053..334c98d5f04 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -187,6 +187,25 @@ static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer) } } +static void ath_mci_ftp_adjust(struct ath_softc *sc) +{ + struct ath_btcoex *btcoex = &sc->btcoex; + struct ath_mci_profile *mci = &btcoex->mci; + struct ath_hw *ah = sc->sc_ah; + + btcoex->bt_wait_time += btcoex->btcoex_period; + if (btcoex->bt_wait_time > ATH_BTCOEX_RX_WAIT_TIME) { + if (ar9003_mci_state(ah, MCI_STATE_NEED_FTP_STOMP) && + (mci->num_pan || mci->num_other_acl)) + ah->btcoex_hw.mci.stomp_ftp = + (sc->rx.num_pkts < ATH_BTCOEX_STOMP_FTP_THRESH); + else + ah->btcoex_hw.mci.stomp_ftp = false; + btcoex->bt_wait_time = 0; + sc->rx.num_pkts = 0; + } +} + /* * This is the master bt coex timer which runs for every * 45ms, bt traffic will be given priority during 55% of this @@ -197,7 +216,6 @@ 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; - struct ath_mci_profile *mci = &btcoex->mci; u32 timer_period; bool is_btscan; unsigned long flags; @@ -214,17 +232,8 @@ static void ath_btcoex_period_timer(unsigned long data) ath_detect_bt_priority(sc); is_btscan = test_bit(BT_OP_SCAN, &btcoex->op_flags); - btcoex->bt_wait_time += btcoex->btcoex_period; - if (btcoex->bt_wait_time > ATH_BTCOEX_RX_WAIT_TIME) { - if (ar9003_mci_state(ah, MCI_STATE_NEED_FTP_STOMP) && - (mci->num_pan || mci->num_other_acl)) - ah->btcoex_hw.mci.stomp_ftp = - (sc->rx.num_pkts < ATH_BTCOEX_STOMP_FTP_THRESH); - else - ah->btcoex_hw.mci.stomp_ftp = false; - btcoex->bt_wait_time = 0; - sc->rx.num_pkts = 0; - } + if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI) + ath_mci_ftp_adjust(sc); spin_lock_bh(&btcoex->btcoex_lock); -- cgit v1.2.3-70-g09d2 From 750f32cf0a6e2a4d798e09da4079ede7d1721e54 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Sun, 30 Sep 2012 09:03:37 +0530 Subject: ath9k: Fix BT_OP_SCAN usage BT_OP_SCAN is applicable only for pre-MCI WLAN/BT combo chips and using it for MCI-based cards is incorrect. Fix this by cleaning up its usage. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/gpio.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index 334c98d5f04..bf4fb7db15e 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -216,8 +216,8 @@ 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; + enum ath_stomp_type stomp_type; u32 timer_period; - bool is_btscan; unsigned long flags; spin_lock_irqsave(&sc->sc_pm_lock, flags); @@ -228,19 +228,28 @@ static void ath_btcoex_period_timer(unsigned long data) spin_unlock_irqrestore(&sc->sc_pm_lock, flags); ath9k_ps_wakeup(sc); + if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI)) ath_detect_bt_priority(sc); - is_btscan = test_bit(BT_OP_SCAN, &btcoex->op_flags); if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI) ath_mci_ftp_adjust(sc); spin_lock_bh(&btcoex->btcoex_lock); - ath9k_hw_btcoex_bt_stomp(ah, is_btscan ? ATH_BTCOEX_STOMP_ALL : - btcoex->bt_stomp_type); + stomp_type = btcoex->bt_stomp_type; + timer_period = btcoex->btcoex_no_stomp; + + if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI)) { + if (test_bit(BT_OP_SCAN, &btcoex->op_flags)) { + stomp_type = ATH_BTCOEX_STOMP_ALL; + timer_period = btcoex->btscan_no_stomp; + } + } + ath9k_hw_btcoex_bt_stomp(ah, stomp_type); ath9k_hw_btcoex_enable(ah); + spin_unlock_bh(&btcoex->btcoex_lock); /* @@ -252,17 +261,16 @@ static void ath_btcoex_period_timer(unsigned long data) 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, timer_period, timer_period * 10); btcoex->hw_timer_enabled = true; } ath9k_ps_restore(sc); + skip_hw_wakeup: - timer_period = btcoex->btcoex_period; - mod_timer(&btcoex->period_timer, jiffies + msecs_to_jiffies(timer_period)); + mod_timer(&btcoex->period_timer, + jiffies + msecs_to_jiffies(btcoex->btcoex_period)); } /* @@ -282,7 +290,8 @@ static void ath_btcoex_no_stomp_timer(void *arg) spin_lock_bh(&btcoex->btcoex_lock); if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || - test_bit(BT_OP_SCAN, &btcoex->op_flags)) + (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI) && + test_bit(BT_OP_SCAN, &btcoex->op_flags))) ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE); else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_LOW); -- cgit v1.2.3-70-g09d2 From f255b9cccaeaa03ef6e58f7762bf7249b3dd7ea2 Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Wed, 10 Oct 2012 18:38:17 +0200 Subject: ath/ath9k/ar9003_eeprom.c: Remove semicolon after if This patch remove a semicolon after if(...) that is preventing the error check to work correctly. Removing this semicolon will change the code behavior, but this is intended. The semantic patch that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @r1@ position p; @@ if (...);@p @script:python@ p0 << r1.p; @@ // Emacs org-mode output cocci.print_main("", p0) cocci.print_secs("", p0) // Signed-off-by: Peter Senna Tschudin Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_eeprom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index 5bbe5057ba1..189aeb22f55 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -2989,7 +2989,7 @@ static u32 ath9k_hw_ar9300_get_eeprom(struct ath_hw *ah, case EEP_PAPRD: if (AR_SREV_9462(ah)) return false; - if (!ah->config.enable_paprd); + if (!ah->config.enable_paprd) return false; return !!(pBase->featureEnable & BIT(5)); case EEP_CHAIN_MASK_REDUCE: -- cgit v1.2.3-70-g09d2 From 037fd9b6473393c35a31f0c43e26eb7e874e901d Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 4 Oct 2012 19:43:15 +0200 Subject: ath_hw: Use common REG_WRITE parameter order All defines for REG_WRITE in Atheros wireless drivers use the order "ah", "register" and "value". hw.c is the only file using the order "ah", "value" and "register". drivers/net/wireless/ath/ath9k/hw.h:#define REG_WRITE(_ah, _reg, _val) \ drivers/net/wireless/ath/key.c:#define REG_WRITE(_ah, _reg, _val) (common->ops->write)(_ah, _val, _reg) This inconsistent definition can easily lead to implementation errors. The modification doesn't change the behavior of the driver or the generated code. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich Acked-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/hw.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/hw.c b/drivers/net/wireless/ath/hw.c index 19befb33107..39e8a590d7f 100644 --- a/drivers/net/wireless/ath/hw.c +++ b/drivers/net/wireless/ath/hw.c @@ -20,8 +20,8 @@ #include "ath.h" #include "reg.h" -#define REG_READ (common->ops->read) -#define REG_WRITE (common->ops->write) +#define REG_READ (common->ops->read) +#define REG_WRITE(_ah, _reg, _val) (common->ops->write)(_ah, _val, _reg) /** * ath_hw_set_bssid_mask - filter out bssids we listen @@ -119,8 +119,8 @@ void ath_hw_setbssidmask(struct ath_common *common) { void *ah = common->ah; - REG_WRITE(ah, get_unaligned_le32(common->bssidmask), AR_BSSMSKL); - REG_WRITE(ah, get_unaligned_le16(common->bssidmask + 4), AR_BSSMSKU); + REG_WRITE(ah, AR_BSSMSKL, get_unaligned_le32(common->bssidmask)); + REG_WRITE(ah, AR_BSSMSKU, get_unaligned_le16(common->bssidmask + 4)); } EXPORT_SYMBOL(ath_hw_setbssidmask); @@ -139,7 +139,7 @@ void ath_hw_cycle_counters_update(struct ath_common *common) void *ah = common->ah; /* freeze */ - REG_WRITE(ah, AR_MIBC_FMC, AR_MIBC); + REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC); /* read */ cycles = REG_READ(ah, AR_CCCNT); @@ -148,13 +148,13 @@ void ath_hw_cycle_counters_update(struct ath_common *common) tx = REG_READ(ah, AR_TFCNT); /* clear */ - REG_WRITE(ah, 0, AR_CCCNT); - REG_WRITE(ah, 0, AR_RFCNT); - REG_WRITE(ah, 0, AR_RCCNT); - REG_WRITE(ah, 0, AR_TFCNT); + REG_WRITE(ah, AR_CCCNT, 0); + REG_WRITE(ah, AR_RFCNT, 0); + REG_WRITE(ah, AR_RCCNT, 0); + REG_WRITE(ah, AR_TFCNT, 0); /* unfreeze */ - REG_WRITE(ah, 0, AR_MIBC); + REG_WRITE(ah, AR_MIBC, 0); /* update all cycle counters here */ common->cc_ani.cycles += cycles; -- cgit v1.2.3-70-g09d2 From b4764c809a306ea37b6409494896e919bbb5ec5f Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 6 Oct 2012 20:42:54 +0200 Subject: carl9170: handle traps from firmware loader This patch changes the way the driver deals with command responses and traps which are sent through the special interrupt input endpoint 3. While the carl9170 firmware does not use this endpoint for command responses or traps, the firmware loader on the device does. It uses it to notify the host about 'watchdog triggered' in case the firmware/hardware has crashed. Note: Even without this patch, the driver is still able to detect the mishap and reset the device. But previously it did that because the trap event caused an out-of-order message sequence number error, which also triggered a reset. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/rx.c | 6 +++--- drivers/net/wireless/ath/carl9170/usb.c | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index a0b72307854..9cd93f1d8be 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -164,9 +164,6 @@ void carl9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len) struct carl9170_rsp *cmd = buf; struct ieee80211_vif *vif; - if (carl9170_check_sequence(ar, cmd->hdr.seq)) - return; - if ((cmd->hdr.cmd & CARL9170_RSP_FLAG) != CARL9170_RSP_FLAG) { if (!(cmd->hdr.cmd & CARL9170_CMD_ASYNC_FLAG)) carl9170_cmd_callback(ar, len, buf); @@ -820,6 +817,9 @@ static void carl9170_rx_untie_cmds(struct ar9170 *ar, const u8 *respbuf, if (unlikely(i > resplen)) break; + if (carl9170_check_sequence(ar, cmd->hdr.seq)) + break; + carl9170_handle_command_response(ar, cmd, cmd->hdr.len + 4); } diff --git a/drivers/net/wireless/ath/carl9170/usb.c b/drivers/net/wireless/ath/carl9170/usb.c index 888152ce3ec..307bc0ddff9 100644 --- a/drivers/net/wireless/ath/carl9170/usb.c +++ b/drivers/net/wireless/ath/carl9170/usb.c @@ -295,6 +295,13 @@ static void carl9170_usb_rx_irq_complete(struct urb *urb) goto resubmit; } + /* + * While the carl9170 firmware does not use this EP, the + * firmware loader in the EEPROM unfortunately does. + * Therefore we need to be ready to handle out-of-band + * responses and traps in case the firmware crashed and + * the loader took over again. + */ carl9170_handle_command_response(ar, urb->transfer_buffer, urb->actual_length); -- cgit v1.2.3-70-g09d2 From 424749c75daf3611a68a49eca5940ac2b74e4406 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Wed, 10 Oct 2012 23:03:02 +0530 Subject: ath9k: perform ANI cycle in idle state As of now the ANI cycle is executed only when the chip is awake. On idle state case, the station wakes up from network sleep for beacon reception. Since most of the time, ANI cycle is not syncing with beacon wakeup, ANI cycle is ignored. Approx 5 mins once, the calibration is performed. This could affect the connection stability when the station is idle for long. Even though the OFDM and CCK phy error rates are too high, ANI is unable to tune its immunity level as quick enough due to rare execution. Here the experiment shows that OFDM and CCK levels are at default even on higher phy error rate. listenTime=44 OFDM:3 errs=121977/s CCK:2 errs=440818/s ofdm_turn=1 This change ensures that ANI calibration will be exectued atleast once for every 10 seconds. The below result shows improvements and immunity levels are adopted quick enough. listenTime=557 OFDM:4 errs=752/s CCK:4 errs=125/s ofdm_turn=0 Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 2 ++ drivers/net/wireless/ath/ath9k/hw.h | 1 + drivers/net/wireless/ath/ath9k/link.c | 12 +++++++++++- drivers/net/wireless/ath/ath9k/main.c | 3 ++- 4 files changed, 16 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index dfe6a4707fd..77c2c16b696 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -437,6 +437,7 @@ void ath9k_set_beacon(struct ath_softc *sc); #define ATH_LONG_CALINTERVAL_INT 1000 /* 1000 ms */ #define ATH_LONG_CALINTERVAL 30000 /* 30 seconds */ #define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */ +#define ATH_ANI_MAX_SKIP_COUNT 10 #define ATH_PAPRD_TIMEOUT 100 /* msecs */ #define ATH_PLL_WORK_INTERVAL 100 @@ -642,6 +643,7 @@ enum sc_op_flags { #define PS_WAIT_FOR_PSPOLL_DATA BIT(2) #define PS_WAIT_FOR_TX_ACK BIT(3) #define PS_BEACON_SYNC BIT(4) +#define PS_WAIT_FOR_ANI BIT(5) struct ath_rate_table; diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index dbc1b7a4cbf..1d4f5f1fdd8 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -834,6 +834,7 @@ struct ath_hw { int coarse_low[5]; int firpwr[5]; enum ath9k_ani_cmd ani_function; + u32 ani_skip_count; #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT struct ath_btcoex_hw btcoex_hw; diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index 7b88b9c39cc..223b9693527 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -350,8 +350,18 @@ void ath_ani_calibrate(unsigned long data) ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL; /* Only calibrate if awake */ - if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE) + if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE) { + if (++ah->ani_skip_count >= ATH_ANI_MAX_SKIP_COUNT) { + spin_lock_irqsave(&sc->sc_pm_lock, flags); + sc->ps_flags |= PS_WAIT_FOR_ANI; + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); + } goto set_timer; + } + ah->ani_skip_count = 0; + spin_lock_irqsave(&sc->sc_pm_lock, flags); + sc->ps_flags &= ~PS_WAIT_FOR_ANI; + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); ath9k_ps_wakeup(sc); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index dd45edfa6ba..2da62be081f 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -131,7 +131,8 @@ void ath9k_ps_restore(struct ath_softc *sc) !(sc->ps_flags & (PS_WAIT_FOR_BEACON | PS_WAIT_FOR_CAB | PS_WAIT_FOR_PSPOLL_DATA | - PS_WAIT_FOR_TX_ACK))) { + PS_WAIT_FOR_TX_ACK | + PS_WAIT_FOR_ANI))) { mode = ATH9K_PM_NETWORK_SLEEP; if (ath9k_hw_btcoex_is_enabled(sc->sc_ah)) ath9k_btcoex_stop_gen_timer(sc); -- cgit v1.2.3-70-g09d2 From 85b20fc2420c4d20729f3bbdbfe5962dcc58c3b0 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Thu, 21 Jun 2012 12:50:08 -0700 Subject: ath6kl: support rssi threshold for sched scan The ath6kl firmware can filter scan results based on rssi. This is useful to limit hosts wakeups on scheduled scans. Signed-off-by: Thomas Pedersen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 19 ++++++++++++++++++- drivers/net/wireless/ath/ath6kl/core.h | 3 +++ drivers/net/wireless/ath/ath6kl/wmi.c | 18 ++++++++++++++++++ drivers/net/wireless/ath/ath6kl/wmi.h | 10 ++++++++++ 4 files changed, 49 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 277089963eb..a5f3d6e5470 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3211,7 +3211,7 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); u16 interval; - int ret; + int ret, rssi_thold; if (ar->state != ATH6KL_STATE_ON) return -EIO; @@ -3244,6 +3244,23 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, return ret; } + if (test_bit(ATH6KL_FW_CAPABILITY_RSSI_SCAN_THOLD, + ar->fw_capabilities)) { + if (request->rssi_thold <= NL80211_SCAN_RSSI_THOLD_OFF) + rssi_thold = 0; + else if (request->rssi_thold < -127) + rssi_thold = -127; + else + rssi_thold = request->rssi_thold; + + ret = ath6kl_wmi_set_rssi_filter_cmd(ar->wmi, vif->fw_vif_idx, + rssi_thold); + if (ret) { + ath6kl_err("failed to set RSSI threshold for scan\n"); + return ret; + } + } + /* fw uses seconds, also make sure that it's >0 */ interval = max_t(u16, 1, request->interval / 1000); diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index cec49a31029..a6f0d2c40d2 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -115,6 +115,9 @@ enum ath6kl_fw_capability { */ ATH6KL_FW_CAPABILITY_SCHED_SCAN_MATCH_LIST, + /* Firmware supports filtering BSS results by RSSI */ + ATH6KL_FW_CAPABILITY_RSSI_SCAN_THOLD, + /* this needs to be last */ ATH6KL_FW_CAPABILITY_MAX, }; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index c30ab4b11d6..9673f277817 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -1531,6 +1531,24 @@ static int ath6kl_wmi_cac_event_rx(struct wmi *wmi, u8 *datap, int len, return 0; } +int ath6kl_wmi_set_rssi_filter_cmd(struct wmi *wmi, u8 if_idx, s8 rssi) +{ + struct sk_buff *skb; + struct wmi_set_rssi_filter_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_rssi_filter_cmd *) skb->data; + cmd->rssi = rssi; + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_RSSI_FILTER_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + static int ath6kl_wmi_send_snr_threshold_params(struct wmi *wmi, struct wmi_snr_threshold_params_cmd *snr_cmd) { diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 43339aca585..b5deaffb79e 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -628,6 +628,10 @@ enum wmi_cmd_id { WMI_SET_MCASTRATE, WMI_STA_BMISS_ENHANCE_CMDID, + + WMI_SET_REGDOMAIN_CMDID, + + WMI_SET_RSSI_FILTER_CMDID, }; enum wmi_mgmt_frame_type { @@ -1276,6 +1280,11 @@ struct wmi_snr_threshold_params_cmd { u8 reserved[3]; } __packed; +/* Don't report BSSs with signal (RSSI) below this threshold */ +struct wmi_set_rssi_filter_cmd { + s8 rssi; +} __packed; + enum wmi_preamble_policy { WMI_IGNORE_BARKER_IN_ERP = 0, WMI_FOLLOW_BARKER_IN_ERP, @@ -2592,6 +2601,7 @@ int ath6kl_wmi_add_wow_pattern_cmd(struct wmi *wmi, u8 if_idx, const u8 *mask); int ath6kl_wmi_del_wow_pattern_cmd(struct wmi *wmi, u8 if_idx, u16 list_id, u16 filter_id); +int ath6kl_wmi_set_rssi_filter_cmd(struct wmi *wmi, u8 if_idx, s8 rssi); int ath6kl_wmi_set_roam_lrssi_cmd(struct wmi *wmi, u8 lrssi); int ath6kl_wmi_ap_set_dtim_cmd(struct wmi *wmi, u8 if_idx, u32 dtim_period); int ath6kl_wmi_force_roam_cmd(struct wmi *wmi, const u8 *bssid); -- cgit v1.2.3-70-g09d2 From c95dcb595dde97510dd4bc98c3112fe4d5dbd71f Mon Sep 17 00:00:00 2001 From: Aarthi Thiruvengadam Date: Tue, 10 Jul 2012 13:20:40 -0700 Subject: ath6kl: use custom MAC address for newly created interfaces Firmware and driver generate MAC addresses for the second and third interfaces. In addition to the existing algorithm, flip bit 7 of 5th octet. Since both firmware and driver individually generate the MAC addresses, introduce a new firmware capability bit to keep them compatible. Signed-off-by: Aarthi Thiruvengadam Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 6 +++++- drivers/net/wireless/ath/ath6kl/core.h | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index a5f3d6e5470..a624a0c5e5f 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3523,9 +3523,13 @@ struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name, vif->htcap[IEEE80211_BAND_5GHZ].ht_enable = true; memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN); - if (fw_vif_idx != 0) + if (fw_vif_idx != 0) { ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) | 0x2; + if (test_bit(ATH6KL_FW_CAPABILITY_CUSTOM_MAC_ADDR, + ar->fw_capabilities)) + ndev->dev_addr[4] ^= 0x80; + } init_netdev(ndev); diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index a6f0d2c40d2..4e5edd9f16a 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -118,6 +118,9 @@ enum ath6kl_fw_capability { /* Firmware supports filtering BSS results by RSSI */ ATH6KL_FW_CAPABILITY_RSSI_SCAN_THOLD, + /* FW sets mac_addr[4] ^= 0x80 for newly created interfaces */ + ATH6KL_FW_CAPABILITY_CUSTOM_MAC_ADDR, + /* this needs to be last */ ATH6KL_FW_CAPABILITY_MAX, }; -- cgit v1.2.3-70-g09d2 From 4aca81bfb0b13b927320448c6e4820ffb95f095b Mon Sep 17 00:00:00 2001 From: Pandiyarajan Pitchaimuthu Date: Wed, 11 Jul 2012 12:51:43 +0530 Subject: ath6kl: Make use of return value from ath6kl_diag_read() In ath6kl_read_fwlogs(), return value from ath6kl_diag_read()is not used to bail out in case of any errors in reading fw log. No real issue is observed because of this, reported by source code analyzer. kvalo: fix a long line warning Signed-off-by: Pandiyarajan Pitchaimuthu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/main.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index c189e28e86a..6beffdee9a8 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -293,13 +293,17 @@ int ath6kl_read_fwlogs(struct ath6kl *ar) } address = TARG_VTOP(ar->target_type, debug_hdr_addr); - ath6kl_diag_read(ar, address, &debug_hdr, sizeof(debug_hdr)); + ret = ath6kl_diag_read(ar, address, &debug_hdr, sizeof(debug_hdr)); + if (ret) + goto out; address = TARG_VTOP(ar->target_type, le32_to_cpu(debug_hdr.dbuf_addr)); firstbuf = address; dropped = le32_to_cpu(debug_hdr.dropped); - ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf)); + ret = ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf)); + if (ret) + goto out; loop = 100; @@ -322,7 +326,8 @@ int ath6kl_read_fwlogs(struct ath6kl *ar) address = TARG_VTOP(ar->target_type, le32_to_cpu(debug_buf.next)); - ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf)); + ret = ath6kl_diag_read(ar, address, &debug_buf, + sizeof(debug_buf)); if (ret) goto out; -- cgit v1.2.3-70-g09d2 From bf744f11788280bcbd9bb8ec62974274b72a75bf Mon Sep 17 00:00:00 2001 From: Bala Shanmugam Date: Tue, 17 Jul 2012 12:01:55 +0530 Subject: ath6kl: Add support for AR6004 hardware version 1.3 Add support for AR6004 hardware with version 1.3 and has id 0x31c8088a. Signed-off-by: Bala Shanmugam Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 7 +++++++ drivers/net/wireless/ath/ath6kl/init.c | 20 ++++++++++++++++++++ drivers/net/wireless/ath/ath6kl/sdio.c | 3 +++ drivers/net/wireless/ath/ath6kl/usb.c | 3 +++ 4 files changed, 33 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 4e5edd9f16a..f16f6ba481d 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -192,6 +192,13 @@ enum ath6kl_hw_flags { #define AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE \ AR6004_HW_1_2_FW_DIR "/bdata.bin" +/* AR6004 1.3 definitions */ +#define AR6004_HW_1_3_VERSION 0x31c8088a +#define AR6004_HW_1_3_FW_DIR "ath6k/AR6004/hw1.3" +#define AR6004_HW_1_3_FIRMWARE_FILE "fw.ram.bin" +#define AR6004_HW_1_3_BOARD_DATA_FILE "ath6k/AR6004/hw1.3/bdata.bin" +#define AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE "ath6k/AR6004/hw1.3/bdata.bin" + /* Per STA data, used in AP mode */ #define STA_PS_AWAKE BIT(0) #define STA_PS_SLEEP BIT(1) diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index f90b5db741c..8bd429975e4 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -142,6 +142,26 @@ static const struct ath6kl_hw hw_list[] = { .fw_board = AR6004_HW_1_2_BOARD_DATA_FILE, .fw_default_board = AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE, }, + { + .id = AR6004_HW_1_3_VERSION, + .name = "ar6004 hw 1.3", + .dataset_patch_addr = 0x437860, + .app_load_addr = 0x1234, + .board_ext_data_addr = 0x437000, + .reserved_ram_size = 7168, + .board_addr = 0x436400, + .refclk_hz = 40000000, + .uarttx_pin = 11, + .flags = ATH6KL_HW_FLAG_64BIT_RATES, + + .fw = { + .dir = AR6004_HW_1_3_FW_DIR, + .fw = AR6004_HW_1_3_FIRMWARE_FILE, + }, + + .fw_board = AR6004_HW_1_3_BOARD_DATA_FILE, + .fw_default_board = AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE, + }, }; /* diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index 05b95405f7b..65e99833151 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -1462,3 +1462,6 @@ MODULE_FIRMWARE(AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_2_FW_DIR "/" AR6004_HW_1_2_FIRMWARE_FILE); MODULE_FIRMWARE(AR6004_HW_1_2_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_3_FW_DIR "/" AR6004_HW_1_3_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_1_3_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE); diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c index 3740c3d6ab8..4aa97aa6611 100644 --- a/drivers/net/wireless/ath/ath6kl/usb.c +++ b/drivers/net/wireless/ath/ath6kl/usb.c @@ -1220,3 +1220,6 @@ MODULE_FIRMWARE(AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_2_FIRMWARE_FILE); MODULE_FIRMWARE(AR6004_HW_1_2_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_3_FW_DIR "/" AR6004_HW_1_3_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_1_3_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE); -- cgit v1.2.3-70-g09d2 From 279b2862ee6ba9ee950c02044142f8ea137c302e Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Tue, 17 Jul 2012 19:39:55 -0700 Subject: ath6kl: support TX error rate notification The ath6kl firmware can monitor a connection and report when a certain TX failure threshold is crossed. Support this configuration and event reporting on compatible firmwares. Signed-off-by: Thomas Pedersen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 22 ++++++++++++++ drivers/net/wireless/ath/ath6kl/core.h | 4 +++ drivers/net/wireless/ath/ath6kl/wmi.c | 47 ++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath6kl/wmi.h | 35 ++++++++++++++++++++++ 4 files changed, 108 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index a624a0c5e5f..0194617ff30 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3326,6 +3326,27 @@ static int ath6kl_cfg80211_set_bitrate(struct wiphy *wiphy, mask); } +static int ath6kl_cfg80211_set_txe_config(struct wiphy *wiphy, + struct net_device *dev, + u32 rate, u32 pkts, u32 intvl) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + + if (vif->nw_type != INFRA_NETWORK || + !test_bit(ATH6KL_FW_CAPABILITY_TX_ERR_NOTIFY, ar->fw_capabilities)) + return -EOPNOTSUPP; + + if (vif->sme_state != SME_CONNECTED) + return -ENOTCONN; + + /* save this since the firmware won't report the interval */ + vif->txe_intvl = intvl; + + return ath6kl_wmi_set_txe_notify(ar->wmi, vif->fw_vif_idx, + rate, pkts, intvl); +} + static const struct ieee80211_txrx_stypes ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = { [NL80211_IFTYPE_STATION] = { @@ -3392,6 +3413,7 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = { .sched_scan_start = ath6kl_cfg80211_sscan_start, .sched_scan_stop = ath6kl_cfg80211_sscan_stop, .set_bitrate_mask = ath6kl_cfg80211_set_bitrate, + .set_cqm_txe_config = ath6kl_cfg80211_set_txe_config, }; void ath6kl_cfg80211_stop(struct ath6kl_vif *vif) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index f16f6ba481d..a754a153cc8 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -121,6 +121,9 @@ enum ath6kl_fw_capability { /* FW sets mac_addr[4] ^= 0x80 for newly created interfaces */ ATH6KL_FW_CAPABILITY_CUSTOM_MAC_ADDR, + /* Firmware supports TX error rate notification */ + ATH6KL_FW_CAPABILITY_TX_ERR_NOTIFY, + /* this needs to be last */ ATH6KL_FW_CAPABILITY_MAX, }; @@ -593,6 +596,7 @@ struct ath6kl_vif { u16 assoc_bss_beacon_int; u16 listen_intvl_t; u16 bmiss_time_t; + u32 txe_intvl; u16 bg_scan_period; u8 assoc_bss_dtim_period; struct net_device_stats net_stats; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 9673f277817..4762fa57063 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -1531,6 +1531,50 @@ static int ath6kl_wmi_cac_event_rx(struct wmi *wmi, u8 *datap, int len, return 0; } +static int ath6kl_wmi_txe_notify_event_rx(struct wmi *wmi, u8 *datap, int len, + struct ath6kl_vif *vif) +{ + struct wmi_txe_notify_event *ev; + u32 rate, pkts; + + if (len < sizeof(*ev)) + return -EINVAL; + + if (vif->sme_state != SME_CONNECTED) + return -ENOTCONN; + + ev = (struct wmi_txe_notify_event *) datap; + rate = le32_to_cpu(ev->rate); + pkts = le32_to_cpu(ev->pkts); + + ath6kl_dbg(ATH6KL_DBG_WMI, "TXE notify event: peer %pM rate %d% pkts %d intvl %ds\n", + vif->bssid, rate, pkts, vif->txe_intvl); + + cfg80211_cqm_txe_notify(vif->ndev, vif->bssid, pkts, + rate, vif->txe_intvl, GFP_KERNEL); + + return 0; +} + +int ath6kl_wmi_set_txe_notify(struct wmi *wmi, u8 idx, + u32 rate, u32 pkts, u32 intvl) +{ + struct sk_buff *skb; + struct wmi_txe_notify_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_txe_notify_cmd *) skb->data; + cmd->rate = cpu_to_le32(rate); + cmd->pkts = cpu_to_le32(pkts); + cmd->intvl = cpu_to_le32(intvl); + + return ath6kl_wmi_cmd_send(wmi, idx, skb, WMI_SET_TXE_NOTIFY_CMDID, + NO_SYNC_WMIFLAG); +} + int ath6kl_wmi_set_rssi_filter_cmd(struct wmi *wmi, u8 if_idx, s8 rssi) { struct sk_buff *skb; @@ -3768,6 +3812,9 @@ static int ath6kl_wmi_proc_events_vif(struct wmi *wmi, u16 if_idx, u16 cmd_id, case WMI_RX_ACTION_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_ACTION_EVENTID\n"); return ath6kl_wmi_rx_action_event_rx(wmi, datap, len, vif); + case WMI_TXE_NOTIFY_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TXE_NOTIFY_EVENTID\n"); + return ath6kl_wmi_txe_notify_event_rx(wmi, datap, len, vif); default: ath6kl_dbg(ATH6KL_DBG_WMI, "unknown cmd id 0x%x\n", cmd_id); return -EINVAL; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index b5deaffb79e..8e8846f1b1a 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -632,6 +632,12 @@ enum wmi_cmd_id { WMI_SET_REGDOMAIN_CMDID, WMI_SET_RSSI_FILTER_CMDID, + + WMI_SET_KEEP_ALIVE_EXT, + + WMI_VOICE_DETECTION_ENABLE_CMDID, + + WMI_SET_TXE_NOTIFY_CMDID, }; enum wmi_mgmt_frame_type { @@ -1464,6 +1470,20 @@ enum wmi_event_id { WMI_P2P_CAPABILITIES_EVENTID, WMI_RX_ACTION_EVENTID, WMI_P2P_INFO_EVENTID, + + /* WPS Events */ + WMI_WPS_GET_STATUS_EVENTID, + WMI_WPS_PROFILE_EVENTID, + + /* more P2P events */ + WMI_NOA_INFO_EVENTID, + WMI_OPPPS_INFO_EVENTID, + WMI_PORT_STATUS_EVENTID, + + /* 802.11w */ + WMI_GET_RSN_CAP_EVENTID, + + WMI_TXE_NOTIFY_EVENTID, }; struct wmi_ready_event_2 { @@ -2096,6 +2116,19 @@ struct wmi_del_wow_pattern_cmd { __le16 filter_id; } __packed; +/* WMI_SET_TXE_NOTIFY_CMDID */ +struct wmi_txe_notify_cmd { + __le32 rate; + __le32 pkts; + __le32 intvl; +} __packed; + +/* WMI_TXE_NOTIFY_EVENTID */ +struct wmi_txe_notify_event { + __le32 rate; + __le32 pkts; +} __packed; + /* WMI_SET_AKMP_PARAMS_CMD */ struct wmi_pmkid { @@ -2610,6 +2643,8 @@ int ath6kl_wmi_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, bool mc_all_on); int ath6kl_wmi_add_del_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, u8 *filter, bool add_filter); int ath6kl_wmi_sta_bmiss_enhance_cmd(struct wmi *wmi, u8 if_idx, bool enable); +int ath6kl_wmi_set_txe_notify(struct wmi *wmi, u8 idx, + u32 rate, u32 pkts, u32 intvl); /* AP mode uAPSD */ int ath6kl_wmi_ap_set_apsd(struct wmi *wmi, u8 if_idx, u8 enable); -- cgit v1.2.3-70-g09d2 From c8c72b74e289a3439e9c2438ca675c5a746bf929 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Thu, 19 Jul 2012 16:00:40 +0300 Subject: ath6kl: move ath6kl_wmi_startscan_cmd() To make it easier to refactor the scan commands move ath6kl_wmi_startscan_cmd() before the beginscan function. No functional changes. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/wmi.c | 88 +++++++++++++++++------------------ 1 file changed, 44 insertions(+), 44 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 4762fa57063..a9d7e000017 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -1895,22 +1895,22 @@ int ath6kl_wmi_disconnect_cmd(struct wmi *wmi, u8 if_idx) return ret; } -int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx, +/* ath6kl_wmi_start_scan_cmd is to be deprecated. Use + * ath6kl_wmi_begin_scan_cmd instead. The new function supports P2P + * mgmt operations using station interface. + */ +int ath6kl_wmi_startscan_cmd(struct wmi *wmi, u8 if_idx, enum wmi_scan_type scan_type, u32 force_fgscan, u32 is_legacy, u32 home_dwell_time, u32 force_scan_interval, - s8 num_chan, u16 *ch_list, u32 no_cck, u32 *rates) + s8 num_chan, u16 *ch_list) { - struct ieee80211_supported_band *sband; struct sk_buff *skb; - struct wmi_begin_scan_cmd *sc; - s8 size, *supp_rates; - int i, band, ret; - struct ath6kl *ar = wmi->parent_dev; - int num_rates; - u32 ratemask; + struct wmi_start_scan_cmd *sc; + s8 size; + int i, ret; - size = sizeof(struct wmi_begin_scan_cmd); + size = sizeof(struct wmi_start_scan_cmd); if ((scan_type != WMI_LONG_SCAN) && (scan_type != WMI_SHORT_SCAN)) return -EINVAL; @@ -1925,59 +1925,39 @@ int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx, if (!skb) return -ENOMEM; - sc = (struct wmi_begin_scan_cmd *) skb->data; + sc = (struct wmi_start_scan_cmd *) skb->data; sc->scan_type = scan_type; sc->force_fg_scan = cpu_to_le32(force_fgscan); sc->is_legacy = cpu_to_le32(is_legacy); sc->home_dwell_time = cpu_to_le32(home_dwell_time); sc->force_scan_intvl = cpu_to_le32(force_scan_interval); - sc->no_cck = cpu_to_le32(no_cck); sc->num_ch = num_chan; - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - sband = ar->wiphy->bands[band]; - - if (!sband) - continue; - - ratemask = rates[band]; - supp_rates = sc->supp_rates[band].rates; - num_rates = 0; - - for (i = 0; i < sband->n_bitrates; i++) { - if ((BIT(i) & ratemask) == 0) - continue; /* skip rate */ - supp_rates[num_rates++] = - (u8) (sband->bitrates[i].bitrate / 5); - } - sc->supp_rates[band].nrates = num_rates; - } - for (i = 0; i < num_chan; i++) sc->ch_list[i] = cpu_to_le16(ch_list[i]); - ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_BEGIN_SCAN_CMDID, + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_START_SCAN_CMDID, NO_SYNC_WMIFLAG); return ret; } -/* ath6kl_wmi_start_scan_cmd is to be deprecated. Use - * ath6kl_wmi_begin_scan_cmd instead. The new function supports P2P - * mgmt operations using station interface. - */ -int ath6kl_wmi_startscan_cmd(struct wmi *wmi, u8 if_idx, +int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx, enum wmi_scan_type scan_type, u32 force_fgscan, u32 is_legacy, u32 home_dwell_time, u32 force_scan_interval, - s8 num_chan, u16 *ch_list) + s8 num_chan, u16 *ch_list, u32 no_cck, u32 *rates) { + struct ieee80211_supported_band *sband; struct sk_buff *skb; - struct wmi_start_scan_cmd *sc; - s8 size; - int i, ret; + struct wmi_begin_scan_cmd *sc; + s8 size, *supp_rates; + int i, band, ret; + struct ath6kl *ar = wmi->parent_dev; + int num_rates; + u32 ratemask; - size = sizeof(struct wmi_start_scan_cmd); + size = sizeof(struct wmi_begin_scan_cmd); if ((scan_type != WMI_LONG_SCAN) && (scan_type != WMI_SHORT_SCAN)) return -EINVAL; @@ -1992,18 +1972,38 @@ int ath6kl_wmi_startscan_cmd(struct wmi *wmi, u8 if_idx, if (!skb) return -ENOMEM; - sc = (struct wmi_start_scan_cmd *) skb->data; + sc = (struct wmi_begin_scan_cmd *) skb->data; sc->scan_type = scan_type; sc->force_fg_scan = cpu_to_le32(force_fgscan); sc->is_legacy = cpu_to_le32(is_legacy); sc->home_dwell_time = cpu_to_le32(home_dwell_time); sc->force_scan_intvl = cpu_to_le32(force_scan_interval); + sc->no_cck = cpu_to_le32(no_cck); sc->num_ch = num_chan; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + sband = ar->wiphy->bands[band]; + + if (!sband) + continue; + + ratemask = rates[band]; + supp_rates = sc->supp_rates[band].rates; + num_rates = 0; + + for (i = 0; i < sband->n_bitrates; i++) { + if ((BIT(i) & ratemask) == 0) + continue; /* skip rate */ + supp_rates[num_rates++] = + (u8) (sband->bitrates[i].bitrate / 5); + } + sc->supp_rates[band].nrates = num_rates; + } + for (i = 0; i < num_chan; i++) sc->ch_list[i] = cpu_to_le16(ch_list[i]); - ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_START_SCAN_CMDID, + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_BEGIN_SCAN_CMDID, NO_SYNC_WMIFLAG); return ret; -- cgit v1.2.3-70-g09d2 From 11f0bfcf73f4a90c8c0e0b244a272379b376adb1 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Thu, 19 Jul 2012 16:00:48 +0300 Subject: ath6kl: refactor wmi scan command ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX was checked in cfg80211.c which is a bit awkward when adding more callsites to the scan functions. Refactor the code to wmi.c so that it's transparent to the callers. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 31 ++++++++---------------------- drivers/net/wireless/ath/ath6kl/wmi.c | 25 +++++++++++++++++++----- drivers/net/wireless/ath/ath6kl/wmi.h | 5 ----- 3 files changed, 28 insertions(+), 33 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 0194617ff30..4f538f0027f 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1031,30 +1031,15 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, vif->scan_req = request; - if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, - ar->fw_capabilities)) { - /* - * If capable of doing P2P mgmt operations using - * station interface, send additional information like - * supported rates to advertise and xmit rates for - * probe requests - */ - ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx, - WMI_LONG_SCAN, force_fg_scan, - false, 0, - ATH6KL_FG_SCAN_INTERVAL, - n_channels, channels, - request->no_cck, - request->rates); - } else { - ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx, - WMI_LONG_SCAN, force_fg_scan, - false, 0, - ATH6KL_FG_SCAN_INTERVAL, - n_channels, channels); - } + ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx, + WMI_LONG_SCAN, force_fg_scan, + false, 0, + ATH6KL_FG_SCAN_INTERVAL, + n_channels, channels, + request->no_cck, + request->rates); if (ret) { - ath6kl_err("wmi_startscan_cmd failed\n"); + ath6kl_err("failed to start scan: %d\n", ret); vif->scan_req = NULL; } diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index a9d7e000017..05cc871f824 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -1899,11 +1899,12 @@ int ath6kl_wmi_disconnect_cmd(struct wmi *wmi, u8 if_idx) * ath6kl_wmi_begin_scan_cmd instead. The new function supports P2P * mgmt operations using station interface. */ -int ath6kl_wmi_startscan_cmd(struct wmi *wmi, u8 if_idx, - enum wmi_scan_type scan_type, - u32 force_fgscan, u32 is_legacy, - u32 home_dwell_time, u32 force_scan_interval, - s8 num_chan, u16 *ch_list) +static int ath6kl_wmi_startscan_cmd(struct wmi *wmi, u8 if_idx, + enum wmi_scan_type scan_type, + u32 force_fgscan, u32 is_legacy, + u32 home_dwell_time, + u32 force_scan_interval, + s8 num_chan, u16 *ch_list) { struct sk_buff *skb; struct wmi_start_scan_cmd *sc; @@ -1942,6 +1943,11 @@ int ath6kl_wmi_startscan_cmd(struct wmi *wmi, u8 if_idx, return ret; } +/* + * beginscan supports (compared to old startscan) P2P mgmt operations using + * station interface, send additional information like supported rates to + * advertise and xmit rates for probe requests + */ int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx, enum wmi_scan_type scan_type, u32 force_fgscan, u32 is_legacy, @@ -1957,6 +1963,15 @@ int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx, int num_rates; u32 ratemask; + if (!test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, + ar->fw_capabilities)) { + return ath6kl_wmi_startscan_cmd(wmi, if_idx, + scan_type, force_fgscan, + is_legacy, home_dwell_time, + force_scan_interval, + num_chan, ch_list); + } + size = sizeof(struct wmi_begin_scan_cmd); if ((scan_type != WMI_LONG_SCAN) && (scan_type != WMI_SHORT_SCAN)) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 8e8846f1b1a..5166a8e6492 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -2547,11 +2547,6 @@ int ath6kl_wmi_connect_cmd(struct wmi *wmi, u8 if_idx, int ath6kl_wmi_reconnect_cmd(struct wmi *wmi, u8 if_idx, u8 *bssid, u16 channel); int ath6kl_wmi_disconnect_cmd(struct wmi *wmi, u8 if_idx); -int ath6kl_wmi_startscan_cmd(struct wmi *wmi, u8 if_idx, - enum wmi_scan_type scan_type, - u32 force_fgscan, u32 is_legacy, - u32 home_dwell_time, u32 force_scan_interval, - s8 num_chan, u16 *ch_list); int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx, enum wmi_scan_type scan_type, -- cgit v1.2.3-70-g09d2 From 84841ba29b1f55fb09703408477f097c7f8952f8 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Thu, 19 Jul 2012 16:00:56 +0300 Subject: ath6kl: add support for changing contry code To make it possible to change the country code from user space via nl80211 add handler for reg_notifier. The feature is only enabled when built time option CONFIG_ATH6KL_REGDOMAIN is enabled, which again depends on CFG80211_CERTIFICATION_ONUS for certication purposes. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/Kconfig | 9 ++++++ drivers/net/wireless/ath/ath6kl/cfg80211.c | 47 ++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath6kl/core.h | 3 ++ drivers/net/wireless/ath/ath6kl/wmi.c | 17 +++++++++++ drivers/net/wireless/ath/ath6kl/wmi.h | 6 ++++ 5 files changed, 82 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/Kconfig b/drivers/net/wireless/ath/ath6kl/Kconfig index d755a5e7ed2..26c4b722085 100644 --- a/drivers/net/wireless/ath/ath6kl/Kconfig +++ b/drivers/net/wireless/ath/ath6kl/Kconfig @@ -30,3 +30,12 @@ config ATH6KL_DEBUG depends on ATH6KL ---help--- Enables debug support + +config ATH6KL_REGDOMAIN + bool "Atheros ath6kl regdomain support" + depends on ATH6KL + depends on CFG80211_CERTIFICATION_ONUS + ---help--- + Enabling this makes it possible to change the regdomain in + the firmware. This can be only enabled if regulatory requirements + are taken into account. diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 4f538f0027f..f620af5e0db 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3458,6 +3458,49 @@ void ath6kl_cfg80211_stop_all(struct ath6kl *ar) ath6kl_cfg80211_stop(vif); } +static int ath6kl_cfg80211_reg_notify(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ath6kl *ar = wiphy_priv(wiphy); + u32 rates[IEEE80211_NUM_BANDS]; + int ret, i; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "cfg reg_notify %c%c%s%s initiator %d\n", + request->alpha2[0], request->alpha2[1], + request->intersect ? " intersect" : "", + request->processed ? " processed" : "", + request->initiator); + + ret = ath6kl_wmi_set_regdomain_cmd(ar->wmi, request->alpha2); + if (ret) { + ath6kl_err("failed to set regdomain: %d\n", ret); + return ret; + } + + /* + * Firmware will apply the regdomain change only after a scan is + * issued and it will send a WMI_REGDOMAIN_EVENTID when it has been + * changed. + */ + + for (i = 0; i < IEEE80211_NUM_BANDS; i++) + if (wiphy->bands[i]) + rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1; + + + ret = ath6kl_wmi_beginscan_cmd(ar->wmi, 0, WMI_LONG_SCAN, false, + false, 0, ATH6KL_FG_SCAN_INTERVAL, + 0, NULL, false, rates); + if (ret) { + ath6kl_err("failed to start scan for a regdomain change: %d\n", + ret); + return ret; + } + + return 0; +} + static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif) { vif->aggr_cntxt = aggr_init(vif); @@ -3590,6 +3633,10 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) BIT(NL80211_IFTYPE_P2P_CLIENT); } + if (config_enabled(CONFIG_ATH6KL_REGDOMAIN) && + test_bit(ATH6KL_FW_CAPABILITY_REGDOMAIN, ar->fw_capabilities)) + wiphy->reg_notifier = ath6kl_cfg80211_reg_notify; + /* max num of ssids that can be probed during scanning */ wiphy->max_scan_ssids = MAX_PROBED_SSIDS; diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index a754a153cc8..eb86b315adf 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -124,6 +124,9 @@ enum ath6kl_fw_capability { /* Firmware supports TX error rate notification */ ATH6KL_FW_CAPABILITY_TX_ERR_NOTIFY, + /* supports WMI_SET_REGDOMAIN_CMDID command */ + ATH6KL_FW_CAPABILITY_REGDOMAIN, + /* this needs to be last */ ATH6KL_FW_CAPABILITY_MAX, }; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 05cc871f824..cf91348fdbd 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -3216,6 +3216,23 @@ int ath6kl_wmi_sta_bmiss_enhance_cmd(struct wmi *wmi, u8 if_idx, bool enhance) return ret; } +int ath6kl_wmi_set_regdomain_cmd(struct wmi *wmi, const char *alpha2) +{ + struct sk_buff *skb; + struct wmi_set_regdomain_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_regdomain_cmd *) skb->data; + memcpy(cmd->iso_name, alpha2, 2); + + return ath6kl_wmi_cmd_send(wmi, 0, skb, + WMI_SET_REGDOMAIN_CMDID, + NO_SYNC_WMIFLAG); +} + s32 ath6kl_wmi_get_rate(s8 rate_index) { if (rate_index == RATE_AUTO) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 5166a8e6492..1d510bae155 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -1042,6 +1042,11 @@ struct wmi_sta_bmiss_enhance_cmd { u8 enable; } __packed; +struct wmi_set_regdomain_cmd { + u8 length; + u8 iso_name[2]; +} __packed; + /* WMI_SET_POWER_MODE_CMDID */ enum wmi_power_mode { REC_POWER = 0x01, @@ -2640,6 +2645,7 @@ int ath6kl_wmi_add_del_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, int ath6kl_wmi_sta_bmiss_enhance_cmd(struct wmi *wmi, u8 if_idx, bool enable); int ath6kl_wmi_set_txe_notify(struct wmi *wmi, u8 idx, u32 rate, u32 pkts, u32 intvl); +int ath6kl_wmi_set_regdomain_cmd(struct wmi *wmi, const char *alpha2); /* AP mode uAPSD */ int ath6kl_wmi_ap_set_apsd(struct wmi *wmi, u8 if_idx, u8 enable); -- cgit v1.2.3-70-g09d2 From f8c0305383121817c77d400c788d82ca1a74582c Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Thu, 12 Jul 2012 12:13:12 +0300 Subject: ath6kl: fix incorrect use of IEEE80211_NUM_BANDS ath6kl was incorrectly assuming that IEEE80211_NUM_BANDS will always be 2 and used that also in the firmware WMI interface definitions. But after the support for 60 GHz was added to cfg80211 IEEE80211_NUM_BANDS changed to 3 and this can cause all sort of problems, possibly even memory corruption. I only found this during code review and didn't notice any bugs, but I'm sure there are a few lurking somewhere. To fix this rename unused A_NUM_BANDS to ATH6KL_NUM_BANDS, which is always defined to be 2, and use that in WMI. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/wmi.c | 12 ++++++++---- drivers/net/wireless/ath/ath6kl/wmi.h | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index cf91348fdbd..d485a7c3428 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -2695,11 +2695,13 @@ static int ath6kl_set_bitrate_mask64(struct wmi *wmi, u8 if_idx, { struct sk_buff *skb; int ret, mode, band; - u64 mcsrate, ratemask[IEEE80211_NUM_BANDS]; + u64 mcsrate, ratemask[ATH6KL_NUM_BANDS]; struct wmi_set_tx_select_rates64_cmd *cmd; memset(&ratemask, 0, sizeof(ratemask)); - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + + /* only check 2.4 and 5 GHz bands, skip the rest */ + for (band = 0; band <= IEEE80211_BAND_5GHZ; band++) { /* copy legacy rate mask */ ratemask[band] = mask->control[band].legacy; if (band == IEEE80211_BAND_5GHZ) @@ -2745,11 +2747,13 @@ static int ath6kl_set_bitrate_mask32(struct wmi *wmi, u8 if_idx, { struct sk_buff *skb; int ret, mode, band; - u32 mcsrate, ratemask[IEEE80211_NUM_BANDS]; + u32 mcsrate, ratemask[ATH6KL_NUM_BANDS]; struct wmi_set_tx_select_rates32_cmd *cmd; memset(&ratemask, 0, sizeof(ratemask)); - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + + /* only check 2.4 and 5 GHz bands, skip the rest */ + for (band = 0; band <= IEEE80211_BAND_5GHZ; band++) { /* copy legacy rate mask */ ratemask[band] = mask->control[band].legacy; if (band == IEEE80211_BAND_5GHZ) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 1d510bae155..9c73ae73f26 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -48,7 +48,7 @@ #define A_BAND_24GHZ 0 #define A_BAND_5GHZ 1 -#define A_NUM_BANDS 2 +#define ATH6KL_NUM_BANDS 2 /* in ms */ #define WMI_IMPLICIT_PSTREAM_INACTIVITY_INT 5000 @@ -853,7 +853,7 @@ struct wmi_begin_scan_cmd { u8 scan_type; /* Supported rates to advertise in the probe request frames */ - struct wmi_supp_rates supp_rates[IEEE80211_NUM_BANDS]; + struct wmi_supp_rates supp_rates[ATH6KL_NUM_BANDS]; /* how many channels follow */ u8 num_ch; -- cgit v1.2.3-70-g09d2 From fd4377b6bacc0f04bb1898f3ccab9510172a7561 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Thu, 9 Aug 2012 17:32:33 -0700 Subject: ath6kl: configure wow filters per-vif Only WoW filters for the first vif were being set, causing failures to wake up on any concurrent connected vifs. Handle all per-vif suspend and resume tasks. Since cfg80211 issues user wow filters on a per-wiphy basis, set any custom filters on all connected vifs. Starting WoW in firmware and setting host sleep mode is still handled on a global per-phy level. The first vif is always used for bookkeeping regardless of whether it is connected or not. WoW is cancelled if no connected vifs are found. No firmware capability bits or API bump is needed for this patch, as setting filters for vifs with index > 0 will simply overwrite the index 0 filters in the current implementation. While not correct, this is identical to the existing behavior. kvalo: fix a checkpatch warning in ath6kl_wow_resume() Signed-off-by: Thomas Pedersen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 145 ++++++++++++++++++----------- 1 file changed, 93 insertions(+), 52 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index f620af5e0db..e8b9c1e2a04 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -2092,33 +2092,16 @@ static int ath6kl_cfg80211_host_sleep(struct ath6kl *ar, struct ath6kl_vif *vif) return ret; } -static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) +static int ath6kl_wow_suspend_vif(struct ath6kl_vif *vif, + struct cfg80211_wowlan *wow, u32 *filter) { + struct ath6kl *ar = vif->ar; struct in_device *in_dev; struct in_ifaddr *ifa; - struct ath6kl_vif *vif; int ret; - u32 filter = 0; u16 i, bmiss_time; - u8 index = 0; __be32 ips[MAX_IP_ADDRS]; - - /* The FW currently can't support multi-vif WoW properly. */ - if (ar->num_vif > 1) - return -EIO; - - vif = ath6kl_vif_first(ar); - if (!vif) - return -EIO; - - if (!ath6kl_cfg80211_ready(vif)) - return -EIO; - - if (!test_bit(CONNECTED, &vif->flags)) - return -ENOTCONN; - - if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST)) - return -EINVAL; + u8 index = 0; if (!test_bit(NETDEV_MCAST_ALL_ON, &vif->flags) && test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER, @@ -2140,7 +2123,7 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) * the user. */ if (wow) - ret = ath6kl_wow_usr(ar, vif, wow, &filter); + ret = ath6kl_wow_usr(ar, vif, wow, filter); else if (vif->nw_type == AP_NETWORK) ret = ath6kl_wow_ap(ar, vif); else @@ -2175,12 +2158,10 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) return ret; } - ar->state = ATH6KL_STATE_SUSPENDING; - /* Setup own IP addr for ARP agent. */ in_dev = __in_dev_get_rtnl(vif->ndev); if (!in_dev) - goto skip_arp; + return 0; ifa = in_dev->ifa_list; memset(&ips, 0, sizeof(ips)); @@ -2203,41 +2184,61 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) return ret; } -skip_arp: - ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx, + return ret; +} + +static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) +{ + struct ath6kl_vif *first_vif, *vif; + int ret = 0; + u32 filter = 0; + bool connected = false; + + /* enter / leave wow suspend on first vif always */ + first_vif = ath6kl_vif_first(ar); + if (WARN_ON(unlikely(!first_vif)) || + !ath6kl_cfg80211_ready(first_vif)) + return -EIO; + + if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST)) + return -EINVAL; + + /* install filters for each connected vif */ + spin_lock_bh(&ar->list_lock); + list_for_each_entry(vif, &ar->vif_list, list) { + if (!test_bit(CONNECTED, &vif->flags) || + !ath6kl_cfg80211_ready(vif)) + continue; + connected = true; + + ret = ath6kl_wow_suspend_vif(vif, wow, &filter); + if (ret) + break; + } + spin_unlock_bh(&ar->list_lock); + + if (!connected) + return -ENOTCONN; + else if (ret) + return ret; + + ar->state = ATH6KL_STATE_SUSPENDING; + + ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, first_vif->fw_vif_idx, ATH6KL_WOW_MODE_ENABLE, filter, WOW_HOST_REQ_DELAY); if (ret) return ret; - ret = ath6kl_cfg80211_host_sleep(ar, vif); - if (ret) - return ret; - - return 0; + return ath6kl_cfg80211_host_sleep(ar, first_vif); } -static int ath6kl_wow_resume(struct ath6kl *ar) +static int ath6kl_wow_resume_vif(struct ath6kl_vif *vif) { - struct ath6kl_vif *vif; + struct ath6kl *ar = vif->ar; int ret; - vif = ath6kl_vif_first(ar); - if (!vif) - return -EIO; - - ar->state = ATH6KL_STATE_RESUMING; - - ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, - ATH6KL_HOST_MODE_AWAKE); - if (ret) { - ath6kl_warn("Failed to configure host sleep mode for wow resume: %d\n", - ret); - ar->state = ATH6KL_STATE_WOW; - return ret; - } - if (vif->nw_type != AP_NETWORK) { ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0); @@ -2255,13 +2256,11 @@ static int ath6kl_wow_resume(struct ath6kl *ar) return ret; } - ar->state = ATH6KL_STATE_ON; - if (!test_bit(NETDEV_MCAST_ALL_OFF, &vif->flags) && test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER, ar->fw_capabilities)) { ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi, - vif->fw_vif_idx, true); + vif->fw_vif_idx, true); if (ret) return ret; } @@ -2271,6 +2270,48 @@ static int ath6kl_wow_resume(struct ath6kl *ar) return 0; } +static int ath6kl_wow_resume(struct ath6kl *ar) +{ + struct ath6kl_vif *vif; + int ret; + + vif = ath6kl_vif_first(ar); + if (WARN_ON(unlikely(!vif)) || + !ath6kl_cfg80211_ready(vif)) + return -EIO; + + ar->state = ATH6KL_STATE_RESUMING; + + ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_HOST_MODE_AWAKE); + if (ret) { + ath6kl_warn("Failed to configure host sleep mode for wow resume: %d\n", + ret); + goto cleanup; + } + + spin_lock_bh(&ar->list_lock); + list_for_each_entry(vif, &ar->vif_list, list) { + if (!test_bit(CONNECTED, &vif->flags) || + !ath6kl_cfg80211_ready(vif)) + continue; + ret = ath6kl_wow_resume_vif(vif); + if (ret) + break; + } + spin_unlock_bh(&ar->list_lock); + + if (ret) + goto cleanup; + + ar->state = ATH6KL_STATE_ON; + return 0; + +cleanup: + ar->state = ATH6KL_STATE_WOW; + return ret; +} + static int ath6kl_cfg80211_deepsleep_suspend(struct ath6kl *ar) { struct ath6kl_vif *vif; -- cgit v1.2.3-70-g09d2 From b5495e666d0b83553f6330e7ba33c0cee2271332 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Thu, 26 Jul 2012 18:11:11 -0700 Subject: ath6kl: restart concurrent vifs on failed connect When an ath6kl STA vif is issued a connect command, the firmware will disconnect all other beaconing vifs in preparation for a potential channel switch. The case where the connect fails is currently unhandled, so if a connection attempt on a STA vif fails and any vifs were waiting for a new channel, simply restart the concurrent vifs on their previous channel. Requires that we start tracking the last issued channel in ar->last_ch, which is valid since ath6kl only supports 1 channel at a time. Also clear the beaconing vif's want_ch_switch bit regardless of whether channel switch succeeds, to stop recommitting the same failed profile. Signed-off-by: Thomas Pedersen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 1 + drivers/net/wireless/ath/ath6kl/main.c | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index eb86b315adf..baf149ef346 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -699,6 +699,7 @@ struct ath6kl { struct ath6kl_req_key ap_mode_bkey; struct sk_buff_head mcastpsq; u32 want_ch_switch; + u16 last_ch; /* * FIXME: protects access to mcastpsq but is actually useless as diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 6beffdee9a8..eca4d4704ed 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -441,12 +441,9 @@ void ath6kl_connect_ap_mode_bss(struct ath6kl_vif *vif, u16 channel) break; } - if (ar->want_ch_switch & (1 << vif->fw_vif_idx)) { - ar->want_ch_switch &= ~(1 << vif->fw_vif_idx); + if (ar->last_ch != channel) /* we actually don't know the phymode, default to HT20 */ - ath6kl_cfg80211_ch_switch_notify(vif, channel, - WMI_11G_HT20); - } + ath6kl_cfg80211_ch_switch_notify(vif, channel, WMI_11G_HT20); ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, NONE_BSS_FILTER, 0); set_bit(CONNECTED, &vif->flags); @@ -633,6 +630,9 @@ static void ath6kl_check_ch_switch(struct ath6kl *ar, u16 channel) if (ar->want_ch_switch & (1 << vif->fw_vif_idx)) res = ath6kl_commit_ch_switch(vif, channel); + /* if channel switch failed, oh well we tried */ + ar->want_ch_switch &= ~(1 << vif->fw_vif_idx); + if (res) ath6kl_err("channel switch failed nw_type %d res %d\n", vif->nw_type, res); @@ -986,8 +986,11 @@ void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, if (vif->nw_type == AP_NETWORK) { /* disconnect due to other STA vif switching channels */ if (reason == BSS_DISCONNECTED && - prot_reason_status == WMI_AP_REASON_STA_ROAM) + prot_reason_status == WMI_AP_REASON_STA_ROAM) { ar->want_ch_switch |= 1 << vif->fw_vif_idx; + /* bail back to this channel if STA vif fails connect */ + ar->last_ch = le16_to_cpu(vif->profile.ch); + } if (!ath6kl_remove_sta(ar, bssid, prot_reason_status)) return; @@ -1046,6 +1049,9 @@ void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, } } + /* restart disconnected concurrent vifs waiting for new channel */ + ath6kl_check_ch_switch(ar, ar->last_ch); + /* update connect & link status atomically */ spin_lock_bh(&vif->if_lock); clear_bit(CONNECTED, &vif->flags); -- cgit v1.2.3-70-g09d2 From f21243a82253e34f64187aeb3d7f93fb7cb92536 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Fri, 27 Jul 2012 18:13:27 -0700 Subject: ath6kl: reconfigure RSN capabilities when restarting AP If the firmware decides to initiate a channel switch on an AP vif running an RSN BSS, reconfigure the saved RSN IE capabilities as well. Fixes a bug where the beacon and 4-way handshake would have a capability mismatch after a channel switch, since the firmware apparently clears these on an AP disconnect. Signed-off-by: Thomas Pedersen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 1 + drivers/net/wireless/ath/ath6kl/core.h | 1 + drivers/net/wireless/ath/ath6kl/main.c | 12 ++++++++++++ 3 files changed, 14 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index e8b9c1e2a04..75ddfafb11b 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -2924,6 +2924,7 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, WLAN_EID_RSN, WMI_RSN_IE_CAPB, (const u8 *) &rsn_capab, sizeof(rsn_capab)); + vif->rsn_capab = rsn_capab; if (res < 0) return res; } diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index baf149ef346..a95bf6ab7ec 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -605,6 +605,7 @@ struct ath6kl_vif { struct net_device_stats net_stats; struct target_stats target_stats; struct wmi_connect_cmd profile; + u16 rsn_capab; struct list_head mc_filter; }; diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index eca4d4704ed..9533558a623 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -608,6 +608,18 @@ static int ath6kl_commit_ch_switch(struct ath6kl_vif *vif, u16 channel) switch (vif->nw_type) { case AP_NETWORK: + /* + * reconfigure any saved RSN IE capabilites in the beacon / + * probe response to stay in sync with the supplicant. + */ + if (vif->rsn_capab && + test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE, + ar->fw_capabilities)) + ath6kl_wmi_set_ie_cmd(ar->wmi, vif->fw_vif_idx, + WLAN_EID_RSN, WMI_RSN_IE_CAPB, + (const u8 *) &vif->rsn_capab, + sizeof(vif->rsn_capab)); + return ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &vif->profile); default: -- cgit v1.2.3-70-g09d2 From 0616dc1f2bef563d7916c0dcedbb1bff7d9bd80b Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Tue, 14 Aug 2012 10:10:33 +0530 Subject: ath6kl: Fix potential skb double free in ath6kl_wmi_sync_point() skb given to ath6kl_control_tx() is owned by ath6kl_control_tx(). Calling function should not free the skb for error cases. This is found during code review. kvalo: fix a checkpatch warning in ath6kl_wmi_cmd_send() Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/txrx.c | 4 +++- drivers/net/wireless/ath/ath6kl/wmi.c | 35 ++++++++++++++++++---------------- 2 files changed, 22 insertions(+), 17 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index 7dfa0fd86d7..aab825152b1 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -288,8 +288,10 @@ int ath6kl_control_tx(void *devt, struct sk_buff *skb, int status = 0; struct ath6kl_cookie *cookie = NULL; - if (WARN_ON_ONCE(ar->state == ATH6KL_STATE_WOW)) + if (WARN_ON_ONCE(ar->state == ATH6KL_STATE_WOW)) { + dev_kfree_skb(skb); return -EACCES; + } spin_lock_bh(&ar->lock); diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index d485a7c3428..373751f9177 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -1739,8 +1739,11 @@ int ath6kl_wmi_cmd_send(struct wmi *wmi, u8 if_idx, struct sk_buff *skb, int ret; u16 info1; - if (WARN_ON(skb == NULL || (if_idx > (wmi->parent_dev->vif_max - 1)))) + if (WARN_ON(skb == NULL || + (if_idx > (wmi->parent_dev->vif_max - 1)))) { + dev_kfree_skb(skb); return -EINVAL; + } ath6kl_dbg(ATH6KL_DBG_WMI, "wmi tx id %d len %d flag %d\n", cmd_id, skb->len, sync_flag); @@ -2352,8 +2355,10 @@ static int ath6kl_wmi_data_sync_send(struct wmi *wmi, struct sk_buff *skb, struct wmi_data_hdr *data_hdr; int ret; - if (WARN_ON(skb == NULL || ep_id == wmi->ep_id)) + if (WARN_ON(skb == NULL || ep_id == wmi->ep_id)) { + dev_kfree_skb(skb); return -EINVAL; + } skb_push(skb, sizeof(struct wmi_data_hdr)); @@ -2390,10 +2395,8 @@ static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx) spin_unlock_bh(&wmi->lock); skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); - if (!skb) { - ret = -ENOMEM; - goto free_skb; - } + if (!skb) + return -ENOMEM; cmd = (struct wmi_sync_cmd *) skb->data; @@ -2416,7 +2419,7 @@ static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx) * then do not send the Synchronize cmd on the control ep */ if (ret) - goto free_skb; + goto free_cmd_skb; /* * Send sync cmd followed by sync data messages on all @@ -2426,15 +2429,12 @@ static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx) NO_SYNC_WMIFLAG); if (ret) - goto free_skb; - - /* cmd buffer sent, we no longer own it */ - skb = NULL; + goto free_data_skb; for (index = 0; index < num_pri_streams; index++) { if (WARN_ON(!data_sync_bufs[index].skb)) - break; + goto free_data_skb; ep_id = ath6kl_ac2_endpoint_id(wmi->parent_dev, data_sync_bufs[index]. @@ -2443,17 +2443,20 @@ static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx) ath6kl_wmi_data_sync_send(wmi, data_sync_bufs[index].skb, ep_id, if_idx); - if (ret) - break; - data_sync_bufs[index].skb = NULL; + + if (ret) + goto free_data_skb; } -free_skb: + return 0; + +free_cmd_skb: /* free up any resources left over (possibly due to an error) */ if (skb) dev_kfree_skb(skb); +free_data_skb: for (index = 0; index < num_pri_streams; index++) { if (data_sync_bufs[index].skb != NULL) { dev_kfree_skb((struct sk_buff *)data_sync_bufs[index]. -- cgit v1.2.3-70-g09d2 From 8114f9b6d28686de02c3f83f0543665728b1a15b Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Tue, 14 Aug 2012 10:10:34 +0530 Subject: ath6kl: Fix potential memory leak in ath6kl_tx_complete() We bail out from ath6kl_tx_complete() if any of the sanity checks on skb and ath6kl_cookie fails. By doing this we potentially leak few remaining buffers in packet_queue. Make sure to proceed processing the remaining buffers as well. This issue is found during code review. Reported-by: Wang yufeng Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/txrx.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index aab825152b1..740a488ef50 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -698,21 +698,26 @@ void ath6kl_tx_complete(struct htc_target *target, list_del(&packet->list); ath6kl_cookie = (struct ath6kl_cookie *)packet->pkt_cntxt; - if (!ath6kl_cookie) - goto fatal; + if (WARN_ON_ONCE(!ath6kl_cookie)) + continue; status = packet->status; skb = ath6kl_cookie->skb; eid = packet->endpoint; map_no = ath6kl_cookie->map_no; - if (!skb || !skb->data) - goto fatal; + if (WARN_ON_ONCE(!skb || !skb->data)) { + dev_kfree_skb(skb); + ath6kl_free_cookie(ar, ath6kl_cookie); + continue; + } __skb_queue_tail(&skb_queue, skb); - if (!status && (packet->act_len != skb->len)) - goto fatal; + if (WARN_ON_ONCE(!status && (packet->act_len != skb->len))) { + ath6kl_free_cookie(ar, ath6kl_cookie); + continue; + } ar->tx_pending[eid]--; @@ -794,11 +799,6 @@ void ath6kl_tx_complete(struct htc_target *target, wake_up(&ar->event_wq); return; - -fatal: - WARN_ON(1); - spin_unlock_bh(&ar->lock); - return; } void ath6kl_tx_data_cleanup(struct ath6kl *ar) -- cgit v1.2.3-70-g09d2 From a3b3842c2e27ba07f8f7944a76013425d182c47b Mon Sep 17 00:00:00 2001 From: Marina Makienko Date: Tue, 14 Aug 2012 12:11:30 +0400 Subject: ath6kl: check usb_register() return value ath6kl_usb_init() does not check usb_register() return value. As a result it may incorrectly report success of driver initialization. Found by Linux Driver Verification project (linuxtesting.org). kvalo: fix commit title and make cosmetic changes to the code to follow more the style used in the driver Signed-off-by: Marina Makienko Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/usb.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c index 4aa97aa6611..a6d13775467 100644 --- a/drivers/net/wireless/ath/ath6kl/usb.c +++ b/drivers/net/wireless/ath/ath6kl/usb.c @@ -1196,7 +1196,14 @@ static struct usb_driver ath6kl_usb_driver = { static int ath6kl_usb_init(void) { - usb_register(&ath6kl_usb_driver); + int ret; + + ret = usb_register(&ath6kl_usb_driver); + if (ret) { + ath6kl_err("usb registration failed: %d\n", ret); + return ret; + } + return 0; } -- cgit v1.2.3-70-g09d2 From b1f47e3a962b8b69612d1eecf4d50082b402fcc5 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Wed, 15 Aug 2012 16:51:24 -0700 Subject: ath6kl: rework scheduled scan This patch reflects changes in the firmware scheduled scan implementation to behave better in cases with multiple concurrent vifs. Major changes: - scheduled scan filters and state are now programmed per-vif. - decouple scheduled scan from host sleep. To maintain graceful failure with old firmwares, a new firmware capability bit is introduced: ATH6KL_FW_CAPABILITY_SCHED_SCAN_V2. ath6kl simply won't advertise scheduled scan to cfg80211 if the SCHED_SCAN_V2 is not supported. Since firmwares from here on out won't support the previous implicit API for scheduled scan (set WoW filters and host sleep), bump the firmware API to protect old drivers. Unfortunately, due to firmware RAM constraints ath6kl still cannot expect a scan complete event at the end of a scheduled scan results cycle, so the sched_scan_timer is retained. Signed-off-by: Thomas Pedersen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 45 +++++------------------------- drivers/net/wireless/ath/ath6kl/cfg80211.h | 1 - drivers/net/wireless/ath/ath6kl/core.h | 6 +++- drivers/net/wireless/ath/ath6kl/init.c | 6 ++++ drivers/net/wireless/ath/ath6kl/sdio.c | 19 ------------- drivers/net/wireless/ath/ath6kl/wmi.c | 23 ++++++++++++++- drivers/net/wireless/ath/ath6kl/wmi.h | 10 +++++++ 7 files changed, 50 insertions(+), 60 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 75ddfafb11b..850c94f6ebc 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -147,15 +147,11 @@ static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif) { struct ath6kl *ar = vif->ar; - if (ar->state != ATH6KL_STATE_SCHED_SCAN) + if (!test_and_clear_bit(SCHED_SCANNING, &vif->flags)) return false; del_timer_sync(&vif->sched_scan_timer); - - ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, - ATH6KL_HOST_MODE_AWAKE); - - ar->state = ATH6KL_STATE_ON; + ath6kl_wmi_enable_sched_scan_cmd(ar->wmi, vif->fw_vif_idx, false); return true; } @@ -2448,13 +2444,6 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar, break; - case ATH6KL_CFG_SUSPEND_SCHED_SCAN: - /* - * Nothing needed for schedule scan, firmware is already in - * wow mode and sleeping most of the time. - */ - break; - default: break; } @@ -2502,9 +2491,6 @@ int ath6kl_cfg80211_resume(struct ath6kl *ar) } break; - case ATH6KL_STATE_SCHED_SCAN: - break; - default: break; } @@ -3246,10 +3232,6 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, if (vif->sme_state != SME_DISCONNECTED) return -EBUSY; - /* The FW currently can't support multi-vif WoW properly. */ - if (ar->num_vif > 1) - return -EIO; - ath6kl_cfg80211_scan_complete_event(vif, true); ret = ath6kl_set_probed_ssids(ar, vif, request->ssids, @@ -3295,15 +3277,6 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, interval, interval, vif->bg_scan_period, 0, 0, 0, 3, 0, 0, 0); - ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx, - ATH6KL_WOW_MODE_ENABLE, - WOW_FILTER_SSID, - WOW_HOST_REQ_DELAY); - if (ret) { - ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret); - return ret; - } - /* this also clears IE in fw if it's not set */ ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, WMI_FRAME_PROBE_REQ, @@ -3314,17 +3287,13 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, return ret; } - ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, - ATH6KL_HOST_MODE_ASLEEP); - if (ret) { - ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n", - ret); + ret = ath6kl_wmi_enable_sched_scan_cmd(ar->wmi, vif->fw_vif_idx, true); + if (ret) return ret; - } - ar->state = ATH6KL_STATE_SCHED_SCAN; + set_bit(SCHED_SCANNING, &vif->flags); - return ret; + return 0; } static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy, @@ -3763,7 +3732,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; - if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities)) + if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN_V2, ar->fw_capabilities)) ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT, diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.h b/drivers/net/wireless/ath/ath6kl/cfg80211.h index 780f77775a9..e5e70f3a8ca 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.h +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.h @@ -22,7 +22,6 @@ enum ath6kl_cfg_suspend_mode { ATH6KL_CFG_SUSPEND_DEEPSLEEP, ATH6KL_CFG_SUSPEND_CUTPOWER, ATH6KL_CFG_SUSPEND_WOW, - ATH6KL_CFG_SUSPEND_SCHED_SCAN, }; struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name, diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index a95bf6ab7ec..8b31b9a0f38 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -127,6 +127,9 @@ enum ath6kl_fw_capability { /* supports WMI_SET_REGDOMAIN_CMDID command */ ATH6KL_FW_CAPABILITY_REGDOMAIN, + /* Firmware supports sched scan decoupled from host sleep */ + ATH6KL_FW_CAPABILITY_SCHED_SCAN_V2, + /* this needs to be last */ ATH6KL_FW_CAPABILITY_MAX, }; @@ -145,6 +148,7 @@ enum ath6kl_hw_flags { #define ATH6KL_FW_API2_FILE "fw-2.bin" #define ATH6KL_FW_API3_FILE "fw-3.bin" +#define ATH6KL_FW_API4_FILE "fw-4.bin" /* AR6003 1.0 definitions */ #define AR6003_HW_1_0_VERSION 0x300002ba @@ -555,6 +559,7 @@ enum ath6kl_vif_state { HOST_SLEEP_MODE_CMD_PROCESSED, NETDEV_MCAST_ALL_ON, NETDEV_MCAST_ALL_OFF, + SCHED_SCANNING, }; struct ath6kl_vif { @@ -640,7 +645,6 @@ enum ath6kl_state { ATH6KL_STATE_DEEPSLEEP, ATH6KL_STATE_CUTPOWER, ATH6KL_STATE_WOW, - ATH6KL_STATE_SCHED_SCAN, }; struct ath6kl { diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 8bd429975e4..eb3677bd6e8 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1108,6 +1108,12 @@ int ath6kl_init_fetch_firmwares(struct ath6kl *ar) if (ret) return ret; + ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API4_FILE); + if (ret == 0) { + ar->fw_api = 4; + goto out; + } + ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API3_FILE); if (ret == 0) { ar->fw_api = 3; diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index 65e99833151..cc17fe02bda 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -844,22 +844,6 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) bool try_deepsleep = false; int ret; - if (ar->state == ATH6KL_STATE_SCHED_SCAN) { - ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sched scan is in progress\n"); - - ret = ath6kl_set_sdio_pm_caps(ar); - if (ret) - goto cut_pwr; - - ret = ath6kl_cfg80211_suspend(ar, - ATH6KL_CFG_SUSPEND_SCHED_SCAN, - NULL); - if (ret) - goto cut_pwr; - - return 0; - } - if (ar->suspend_mode == WLAN_POWER_STATE_WOW || (!ar->suspend_mode && wow)) { @@ -942,9 +926,6 @@ static int ath6kl_sdio_resume(struct ath6kl *ar) case ATH6KL_STATE_WOW: break; - case ATH6KL_STATE_SCHED_SCAN: - break; - case ATH6KL_STATE_SUSPENDING: break; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 373751f9177..e95b035168a 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -1116,7 +1116,7 @@ static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len, * the timer would not ever fire if the scan interval is short * enough. */ - if (ar->state == ATH6KL_STATE_SCHED_SCAN && + if (test_bit(SCHED_SCANNING, &vif->flags) && !timer_pending(&vif->sched_scan_timer)) { mod_timer(&vif->sched_scan_timer, jiffies + msecs_to_jiffies(ATH6KL_SCHED_SCAN_RESULT_DELAY)); @@ -2027,6 +2027,27 @@ int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx, return ret; } +int ath6kl_wmi_enable_sched_scan_cmd(struct wmi *wmi, u8 if_idx, bool enable) +{ + struct sk_buff *skb; + struct wmi_enable_sched_scan_cmd *sc; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*sc)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "%s scheduled scan on vif %d\n", + enable ? "enabling" : "disabling", if_idx); + sc = (struct wmi_enable_sched_scan_cmd *) skb->data; + sc->enable = enable ? 1 : 0; + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_ENABLE_SCHED_SCAN_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + int ath6kl_wmi_scanparams_cmd(struct wmi *wmi, u8 if_idx, u16 fg_start_sec, u16 fg_end_sec, u16 bg_sec, diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 9c73ae73f26..a791b1bbe0c 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -638,6 +638,10 @@ enum wmi_cmd_id { WMI_VOICE_DETECTION_ENABLE_CMDID, WMI_SET_TXE_NOTIFY_CMDID, + + WMI_SET_RECOVERY_TEST_PARAMETER_CMDID, /*0xf094*/ + + WMI_ENABLE_SCHED_SCAN_CMDID, }; enum wmi_mgmt_frame_type { @@ -951,6 +955,11 @@ struct wmi_scan_params_cmd { __le32 max_dfsch_act_time; } __packed; +/* WMI_ENABLE_SCHED_SCAN_CMDID */ +struct wmi_enable_sched_scan_cmd { + u8 enable; +} __packed; + /* WMI_SET_BSS_FILTER_CMDID */ enum wmi_bss_filter { /* no beacons forwarded */ @@ -2559,6 +2568,7 @@ int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx, u32 home_dwell_time, u32 force_scan_interval, s8 num_chan, u16 *ch_list, u32 no_cck, u32 *rates); +int ath6kl_wmi_enable_sched_scan_cmd(struct wmi *wmi, u8 if_idx, bool enable); int ath6kl_wmi_scanparams_cmd(struct wmi *wmi, u8 if_idx, u16 fg_start_sec, u16 fg_end_sec, u16 bg_sec, -- cgit v1.2.3-70-g09d2 From 2c07cf4461c958e52efd9cfca1df67165426ba20 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Mon, 20 Aug 2012 14:26:50 -0700 Subject: ath6kl: consolidate WoW pattern length Since WOW_MASK_SIZE and WOW_PATTERN_SIZE have the same value, are logically equivalent, and part of the WMI API so therefore unlikely to change, consolidate these into WOW_PATTERN_SIZE. Reported-by Kalle Valo Signed-off-by: Thomas Pedersen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 +- drivers/net/wireless/ath/ath6kl/wmi.h | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 850c94f6ebc..a7c8ff084c4 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1870,7 +1870,7 @@ static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif, struct cfg80211_wowlan *wow, u32 *filter) { int ret, pos; - u8 mask[WOW_MASK_SIZE]; + u8 mask[WOW_PATTERN_SIZE]; u16 i; /* Configure the patterns that we received from the user. */ diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index a791b1bbe0c..a638151cf86 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -2062,7 +2062,6 @@ struct wmi_set_ie_cmd { #define WOW_MAX_FILTERS_PER_LIST 4 #define WOW_PATTERN_SIZE 64 -#define WOW_MASK_SIZE 64 #define MAC_MAX_FILTERS_PER_LIST 4 @@ -2071,7 +2070,7 @@ struct wow_filter { u8 wow_filter_id; u8 wow_filter_size; u8 wow_filter_offset; - u8 wow_filter_mask[WOW_MASK_SIZE]; + u8 wow_filter_mask[WOW_PATTERN_SIZE]; u8 wow_filter_pattern[WOW_PATTERN_SIZE]; } __packed; -- cgit v1.2.3-70-g09d2 From 3814264481fecba02ba60f2e6c6baea2d43b757b Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Fri, 24 Aug 2012 19:53:28 +0530 Subject: ath6kl: trivial cleanup on interface type selection a minor cleanup in assigning the driver specific network type based on interface type. Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index a7c8ff084c4..b58878648d2 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -365,17 +365,13 @@ static int ath6kl_nliftype_to_drv_iftype(enum nl80211_iftype type, u8 *nw_type) { switch (type) { case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: *nw_type = INFRA_NETWORK; break; case NL80211_IFTYPE_ADHOC: *nw_type = ADHOC_NETWORK; break; case NL80211_IFTYPE_AP: - *nw_type = AP_NETWORK; - break; - case NL80211_IFTYPE_P2P_CLIENT: - *nw_type = INFRA_NETWORK; - break; case NL80211_IFTYPE_P2P_GO: *nw_type = AP_NETWORK; break; -- cgit v1.2.3-70-g09d2 From 83685091acb878980711c3b28fe42e8959583e84 Mon Sep 17 00:00:00 2001 From: Dengke Qiu Date: Tue, 28 Aug 2012 15:33:42 +0800 Subject: ath6kl: fix link speed when using sgi The MSB of rate index from FW is used for sgi. But the ath6kl_wmi_get_rate doesn't handle it. The access to wmi_rate_tbl array may be out of range if sgi is 1. This may cause the return value of ath6kl_wmi_get_rate() function is incorrect link rate. We add sgi adjustment to avoid such case. kvalo: change patch title Signed-off-by: Dengke Qiu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/wmi.c | 13 ++++++++++++- drivers/net/wireless/ath/ath6kl/wmi.h | 3 +++ 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index e95b035168a..cd2db42c098 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -3263,10 +3263,21 @@ int ath6kl_wmi_set_regdomain_cmd(struct wmi *wmi, const char *alpha2) s32 ath6kl_wmi_get_rate(s8 rate_index) { + u8 sgi = 0; + if (rate_index == RATE_AUTO) return 0; - return wmi_rate_tbl[(u32) rate_index][0]; + /* SGI is stored as the MSB of the rate_index */ + if (rate_index & RATE_INDEX_MSB) { + rate_index &= RATE_INDEX_WITHOUT_SGI_MASK; + sgi = 1; + } + + if (WARN_ON(rate_index > RATE_MCS_7_40)) + rate_index = RATE_MCS_7_40; + + return wmi_rate_tbl[(u32) rate_index][sgi]; } static int ath6kl_wmi_get_pmkid_list_event_rx(struct wmi *wmi, u8 *datap, diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index a638151cf86..e916e57c9d9 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -1792,6 +1792,9 @@ struct rx_stats { a_sle32 ucast_rate; } __packed; +#define RATE_INDEX_WITHOUT_SGI_MASK 0x7f +#define RATE_INDEX_MSB 0x80 + struct tkip_ccmp_stats { __le32 tkip_local_mic_fail; __le32 tkip_cnter_measures_invoked; -- cgit v1.2.3-70-g09d2 From ede615d2f043539e23bc4022955dbe0c3ec70ca2 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Wed, 29 Aug 2012 19:40:25 +0530 Subject: ath6kl: Refactor ath6kl_init_hw_start() and ath6kl_init_hw_stop() So that these functions will be used to re-initialize the fw upon detecting fw error. This refactoring moves ar->state setting out of core stop/start functionality. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/init.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index eb3677bd6e8..be27ebec905 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1546,7 +1546,7 @@ static const char *ath6kl_init_get_hif_name(enum ath6kl_hif_type type) return NULL; } -int ath6kl_init_hw_start(struct ath6kl *ar) +static int __ath6kl_init_hw_start(struct ath6kl *ar) { long timeleft; int ret, i; @@ -1642,8 +1642,6 @@ int ath6kl_init_hw_start(struct ath6kl *ar) goto err_htc_stop; } - ar->state = ATH6KL_STATE_ON; - return 0; err_htc_stop: @@ -1656,7 +1654,18 @@ err_power_off: return ret; } -int ath6kl_init_hw_stop(struct ath6kl *ar) +int ath6kl_init_hw_start(struct ath6kl *ar) +{ + int err; + + err = __ath6kl_init_hw_start(ar); + if (err) + return err; + ar->state = ATH6KL_STATE_ON; + return 0; +} + +static int __ath6kl_init_hw_stop(struct ath6kl *ar) { int ret; @@ -1672,8 +1681,17 @@ int ath6kl_init_hw_stop(struct ath6kl *ar) if (ret) ath6kl_warn("failed to power off hif: %d\n", ret); - ar->state = ATH6KL_STATE_OFF; + return 0; +} +int ath6kl_init_hw_stop(struct ath6kl *ar) +{ + int err; + + err = __ath6kl_init_hw_stop(ar); + if (err) + return err; + ar->state = ATH6KL_STATE_OFF; return 0; } -- cgit v1.2.3-70-g09d2 From 84caf8005b09e0a4a57fce44119489d1b0bbbe94 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Wed, 29 Aug 2012 19:40:26 +0530 Subject: ath6kl: Recover from fw crash Re-initialize the target when fw crash is reported. This would make the device functional again after target crash. During the target re-initialization it is made sure that target is not bugged with data/cmd request, ar->state ATH6KL_STATE_RECOVERY is used for this purpose. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/Makefile | 1 + drivers/net/wireless/ath/ath6kl/cfg80211.c | 17 +++++++- drivers/net/wireless/ath/ath6kl/core.c | 4 ++ drivers/net/wireless/ath/ath6kl/core.h | 18 +++++++++ drivers/net/wireless/ath/ath6kl/debug.h | 1 + drivers/net/wireless/ath/ath6kl/hif.c | 1 + drivers/net/wireless/ath/ath6kl/init.c | 19 +++++++++ drivers/net/wireless/ath/ath6kl/recovery.c | 62 ++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath6kl/sdio.c | 3 ++ drivers/net/wireless/ath/ath6kl/txrx.c | 3 +- 10 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 drivers/net/wireless/ath/ath6kl/recovery.c (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/Makefile b/drivers/net/wireless/ath/ath6kl/Makefile index 8cae8886f17..cab0ec0d538 100644 --- a/drivers/net/wireless/ath/ath6kl/Makefile +++ b/drivers/net/wireless/ath/ath6kl/Makefile @@ -34,6 +34,7 @@ ath6kl_core-y += main.o ath6kl_core-y += txrx.o ath6kl_core-y += wmi.o ath6kl_core-y += core.o +ath6kl_core-y += recovery.o ath6kl_core-$(CONFIG_NL80211_TESTMODE) += testmode.o obj-$(CONFIG_ATH6KL_SDIO) += ath6kl_sdio.o diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index b58878648d2..8cf146b5c57 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -2503,14 +2503,23 @@ static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy, { struct ath6kl *ar = wiphy_priv(wiphy); + ath6kl_recovery_suspend(ar); + return ath6kl_hif_suspend(ar, wow); } static int __ath6kl_cfg80211_resume(struct wiphy *wiphy) { struct ath6kl *ar = wiphy_priv(wiphy); + int err; + + err = ath6kl_hif_resume(ar); + if (err) + return err; - return ath6kl_hif_resume(ar); + ar->fw_recovery.enable = true; + + return 0; } /* @@ -3434,6 +3443,10 @@ void ath6kl_cfg80211_stop(struct ath6kl_vif *vif) clear_bit(CONNECTED, &vif->flags); clear_bit(CONNECT_PEND, &vif->flags); + /* Stop netdev queues, needed during recovery */ + netif_stop_queue(vif->ndev); + netif_carrier_off(vif->ndev); + /* disable scanning */ if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF, 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0) @@ -3447,7 +3460,7 @@ void ath6kl_cfg80211_stop_all(struct ath6kl *ar) struct ath6kl_vif *vif; vif = ath6kl_vif_first(ar); - if (!vif) { + if (!vif && ar->state != ATH6KL_STATE_RECOVERY) { /* save the current power mode before enabling power save */ ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode; diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c index 82c4dd2a960..adcaa965486 100644 --- a/drivers/net/wireless/ath/ath6kl/core.c +++ b/drivers/net/wireless/ath/ath6kl/core.c @@ -202,6 +202,8 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n", __func__, wdev->netdev->name, wdev->netdev, ar); + ath6kl_recovery_init(ar); + return ret; err_rxbuf_cleanup: @@ -291,6 +293,8 @@ void ath6kl_core_cleanup(struct ath6kl *ar) { ath6kl_hif_power_off(ar); + ath6kl_recovery_cleanup(ar); + destroy_workqueue(ar->ath6kl_wq); if (ar->htc_target) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 8b31b9a0f38..c7dcdadd8b8 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -645,6 +645,12 @@ enum ath6kl_state { ATH6KL_STATE_DEEPSLEEP, ATH6KL_STATE_CUTPOWER, ATH6KL_STATE_WOW, + ATH6KL_STATE_RECOVERY, +}; + +/* Fw error recovery */ +enum ath6kl_fw_err { + ATH6KL_FW_ASSERT, }; struct ath6kl { @@ -790,6 +796,12 @@ struct ath6kl { bool wiphy_registered; + struct ath6kl_fw_recovery { + bool enable; + struct work_struct recovery_work; + unsigned long err_reason; + } fw_recovery; + #ifdef CONFIG_ATH6KL_DEBUG struct { struct sk_buff_head fwlog_queue; @@ -925,4 +937,10 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type); void ath6kl_core_cleanup(struct ath6kl *ar); void ath6kl_core_destroy(struct ath6kl *ar); +/* Fw error recovery */ +void ath6kl_init_hw_restart(struct ath6kl *ar); +void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason); +void ath6kl_recovery_init(struct ath6kl *ar); +void ath6kl_recovery_cleanup(struct ath6kl *ar); +void ath6kl_recovery_suspend(struct ath6kl *ar); #endif /* CORE_H */ diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h index 49639d8266c..f97cd4ead54 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.h +++ b/drivers/net/wireless/ath/ath6kl/debug.h @@ -44,6 +44,7 @@ enum ATH6K_DEBUG_MASK { ATH6KL_DBG_SUSPEND = BIT(20), ATH6KL_DBG_USB = BIT(21), ATH6KL_DBG_USB_BULK = BIT(22), + ATH6KL_DBG_RECOVERY = BIT(23), ATH6KL_DBG_ANY = 0xffffffff /* enable all logs */ }; diff --git a/drivers/net/wireless/ath/ath6kl/hif.c b/drivers/net/wireless/ath/ath6kl/hif.c index 68ed6c2665b..029914a22ea 100644 --- a/drivers/net/wireless/ath/ath6kl/hif.c +++ b/drivers/net/wireless/ath/ath6kl/hif.c @@ -136,6 +136,7 @@ static int ath6kl_hif_proc_dbg_intr(struct ath6kl_device *dev) ath6kl_hif_dump_fw_crash(dev->ar); ath6kl_read_fwlogs(dev->ar); + ath6kl_recovery_err_notify(dev->ar, ATH6KL_FW_ASSERT); return ret; } diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index be27ebec905..301443c9f9e 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1695,6 +1695,25 @@ int ath6kl_init_hw_stop(struct ath6kl *ar) return 0; } +void ath6kl_init_hw_restart(struct ath6kl *ar) +{ + + ar->state = ATH6KL_STATE_RECOVERY; + + ath6kl_cfg80211_stop_all(ar); + + if (__ath6kl_init_hw_stop(ar)) + return; + + if (__ath6kl_init_hw_start(ar)) { + ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Failed to restart during fw error recovery\n"); + return; + } + + ar->state = ATH6KL_STATE_ON; + ar->fw_recovery.err_reason = 0; +} + /* FIXME: move this to cfg80211.c and rename to ath6kl_cfg80211_vif_stop() */ void ath6kl_cleanup_vif(struct ath6kl_vif *vif, bool wmi_ready) { diff --git a/drivers/net/wireless/ath/ath6kl/recovery.c b/drivers/net/wireless/ath/ath6kl/recovery.c new file mode 100644 index 00000000000..c225fc4d3d5 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/recovery.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, 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 "core.h" +#include "cfg80211.h" +#include "debug.h" + +static void ath6kl_recovery_work(struct work_struct *work) +{ + struct ath6kl *ar = container_of(work, struct ath6kl, + fw_recovery.recovery_work); + + ath6kl_init_hw_restart(ar); +} + +void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason) +{ + ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Fw error detected, reason:%d\n", + reason); + + set_bit(reason, &ar->fw_recovery.err_reason); + + if (ar->fw_recovery.enable && ar->state != ATH6KL_STATE_RECOVERY) + queue_work(ar->ath6kl_wq, &ar->fw_recovery.recovery_work); +} + +void ath6kl_recovery_init(struct ath6kl *ar) +{ + struct ath6kl_fw_recovery *recovery = &ar->fw_recovery; + + recovery->enable = true; + INIT_WORK(&recovery->recovery_work, ath6kl_recovery_work); +} + +void ath6kl_recovery_cleanup(struct ath6kl *ar) +{ + ar->fw_recovery.enable = false; + + cancel_work_sync(&ar->fw_recovery.recovery_work); +} + +void ath6kl_recovery_suspend(struct ath6kl *ar) +{ + ath6kl_recovery_cleanup(ar); + + /* Process pending fw error detection */ + if (ar->fw_recovery.err_reason) + ath6kl_init_hw_restart(ar); +} diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index cc17fe02bda..a72a4d02a4c 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -931,6 +931,9 @@ static int ath6kl_sdio_resume(struct ath6kl *ar) case ATH6KL_STATE_RESUMING: break; + + case ATH6KL_STATE_RECOVERY: + break; } ath6kl_cfg80211_resume(ar); diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index 740a488ef50..cbe1a9d8911 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -288,7 +288,8 @@ int ath6kl_control_tx(void *devt, struct sk_buff *skb, int status = 0; struct ath6kl_cookie *cookie = NULL; - if (WARN_ON_ONCE(ar->state == ATH6KL_STATE_WOW)) { + if (WARN_ON_ONCE(ar->state == ATH6KL_STATE_WOW) || + ar->state == ATH6KL_STATE_RECOVERY) { dev_kfree_skb(skb); return -EACCES; } -- cgit v1.2.3-70-g09d2 From 9233299394de1c571e52ab2dbe1995c1fbdc8fda Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Wed, 29 Aug 2012 19:40:27 +0530 Subject: ath6kl: Add support to detect fw error through heart beat This patch adds support to detect fw error condition by sending periodic message (heart beat challenge) to firmware. Upon reception of the message, fw would send a response event to driver. When there are no reponses from fw for about 5 cmd driver would trigger the recovery logic assuming that fw has gone into an error state. Capable fw will advertise this capability through ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL bit. This feature is disabled by default, can be enabled through a modparam (heart_beat_poll). This modparam also confiures the polling interval in msecs. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 +- drivers/net/wireless/ath/ath6kl/core.c | 9 +++ drivers/net/wireless/ath/ath6kl/core.h | 16 ++++++ drivers/net/wireless/ath/ath6kl/init.c | 6 -- drivers/net/wireless/ath/ath6kl/recovery.c | 89 +++++++++++++++++++++++++++++- drivers/net/wireless/ath/ath6kl/wmi.c | 14 +++++ drivers/net/wireless/ath/ath6kl/wmi.h | 2 + 7 files changed, 129 insertions(+), 9 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 8cf146b5c57..c8b6be44fa2 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -2517,7 +2517,7 @@ static int __ath6kl_cfg80211_resume(struct wiphy *wiphy) if (err) return err; - ar->fw_recovery.enable = true; + ath6kl_recovery_resume(ar); return 0; } diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c index adcaa965486..fd5dd3aca77 100644 --- a/drivers/net/wireless/ath/ath6kl/core.c +++ b/drivers/net/wireless/ath/ath6kl/core.c @@ -33,6 +33,7 @@ static unsigned int wow_mode; static unsigned int uart_debug; static unsigned int ath6kl_p2p; static unsigned int testmode; +static unsigned int heart_beat_poll; module_param(debug_mask, uint, 0644); module_param(suspend_mode, uint, 0644); @@ -40,6 +41,9 @@ module_param(wow_mode, uint, 0644); module_param(uart_debug, uint, 0644); module_param(ath6kl_p2p, uint, 0644); module_param(testmode, uint, 0644); +module_param(heart_beat_poll, uint, 0644); +MODULE_PARM_DESC(heart_beat_poll, "Enable fw error detection periodic" \ + "polling. This also specifies the polling interval in msecs"); void ath6kl_core_tx_complete(struct ath6kl *ar, struct sk_buff *skb) { @@ -202,6 +206,11 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n", __func__, wdev->netdev->name, wdev->netdev, ar); + if (heart_beat_poll && + test_bit(ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL, + ar->fw_capabilities)) + ar->fw_recovery.hb_poll = heart_beat_poll; + ath6kl_recovery_init(ar); return ret; diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index c7dcdadd8b8..b2cbecf6cfe 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -130,6 +130,12 @@ enum ath6kl_fw_capability { /* Firmware supports sched scan decoupled from host sleep */ ATH6KL_FW_CAPABILITY_SCHED_SCAN_V2, + /* + * Firmware capability for hang detection through heart beat + * challenge messages. + */ + ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL, + /* this needs to be last */ ATH6KL_FW_CAPABILITY_MAX, }; @@ -649,8 +655,11 @@ enum ath6kl_state { }; /* Fw error recovery */ +#define ATH6KL_HB_RESP_MISS_THRES 5 + enum ath6kl_fw_err { ATH6KL_FW_ASSERT, + ATH6KL_FW_HB_RESP_FAILURE, }; struct ath6kl { @@ -800,6 +809,11 @@ struct ath6kl { bool enable; struct work_struct recovery_work; unsigned long err_reason; + unsigned long hb_poll; + struct timer_list hb_timer; + u32 seq_num; + bool hb_pending; + u8 hb_misscnt; } fw_recovery; #ifdef CONFIG_ATH6KL_DEBUG @@ -940,7 +954,9 @@ void ath6kl_core_destroy(struct ath6kl *ar); /* Fw error recovery */ void ath6kl_init_hw_restart(struct ath6kl *ar); void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason); +void ath6kl_recovery_hb_event(struct ath6kl *ar, u32 cookie); void ath6kl_recovery_init(struct ath6kl *ar); void ath6kl_recovery_cleanup(struct ath6kl *ar); void ath6kl_recovery_suspend(struct ath6kl *ar); +void ath6kl_recovery_resume(struct ath6kl *ar); #endif /* CORE_H */ diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 301443c9f9e..6e270fa6d63 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1697,9 +1697,6 @@ int ath6kl_init_hw_stop(struct ath6kl *ar) void ath6kl_init_hw_restart(struct ath6kl *ar) { - - ar->state = ATH6KL_STATE_RECOVERY; - ath6kl_cfg80211_stop_all(ar); if (__ath6kl_init_hw_stop(ar)) @@ -1709,9 +1706,6 @@ void ath6kl_init_hw_restart(struct ath6kl *ar) ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Failed to restart during fw error recovery\n"); return; } - - ar->state = ATH6KL_STATE_ON; - ar->fw_recovery.err_reason = 0; } /* FIXME: move this to cfg80211.c and rename to ath6kl_cfg80211_vif_stop() */ diff --git a/drivers/net/wireless/ath/ath6kl/recovery.c b/drivers/net/wireless/ath/ath6kl/recovery.c index c225fc4d3d5..4e3f205bb8a 100644 --- a/drivers/net/wireless/ath/ath6kl/recovery.c +++ b/drivers/net/wireless/ath/ath6kl/recovery.c @@ -23,7 +23,18 @@ static void ath6kl_recovery_work(struct work_struct *work) struct ath6kl *ar = container_of(work, struct ath6kl, fw_recovery.recovery_work); + ar->state = ATH6KL_STATE_RECOVERY; + + del_timer_sync(&ar->fw_recovery.hb_timer); + ath6kl_init_hw_restart(ar); + + ar->state = ATH6KL_STATE_ON; + ar->fw_recovery.err_reason = 0; + + if (ar->fw_recovery.enable) + mod_timer(&ar->fw_recovery.hb_timer, jiffies + + msecs_to_jiffies(ar->fw_recovery.hb_poll)); } void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason) @@ -37,18 +48,72 @@ void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason) queue_work(ar->ath6kl_wq, &ar->fw_recovery.recovery_work); } +void ath6kl_recovery_hb_event(struct ath6kl *ar, u32 cookie) +{ + if (cookie == ar->fw_recovery.seq_num) + ar->fw_recovery.hb_pending = false; +} + +static void ath6kl_recovery_hb_timer(unsigned long data) +{ + struct ath6kl *ar = (struct ath6kl *) data; + int err; + + if (!ar->fw_recovery.enable) + return; + + if (ar->fw_recovery.hb_pending) + ar->fw_recovery.hb_misscnt++; + else + ar->fw_recovery.hb_misscnt = 0; + + if (ar->fw_recovery.hb_misscnt > ATH6KL_HB_RESP_MISS_THRES) { + ar->fw_recovery.hb_misscnt = 0; + ar->fw_recovery.seq_num = 0; + ar->fw_recovery.hb_pending = false; + ath6kl_recovery_err_notify(ar, ATH6KL_FW_HB_RESP_FAILURE); + return; + } + + ar->fw_recovery.seq_num++; + ar->fw_recovery.hb_pending = true; + + err = ath6kl_wmi_get_challenge_resp_cmd(ar->wmi, + ar->fw_recovery.seq_num, 0); + if (err) + ath6kl_warn("Failed to send hb challenge request, err:%d\n", + err); + + if ((ar->state == ATH6KL_STATE_RECOVERY) || !ar->fw_recovery.enable) + return; + + mod_timer(&ar->fw_recovery.hb_timer, jiffies + + msecs_to_jiffies(ar->fw_recovery.hb_poll)); +} + void ath6kl_recovery_init(struct ath6kl *ar) { struct ath6kl_fw_recovery *recovery = &ar->fw_recovery; recovery->enable = true; INIT_WORK(&recovery->recovery_work, ath6kl_recovery_work); + recovery->seq_num = 0; + recovery->hb_misscnt = 0; + ar->fw_recovery.hb_pending = false; + ar->fw_recovery.hb_timer.function = ath6kl_recovery_hb_timer; + ar->fw_recovery.hb_timer.data = (unsigned long) ar; + init_timer_deferrable(&ar->fw_recovery.hb_timer); + + if (ar->fw_recovery.hb_poll) + mod_timer(&ar->fw_recovery.hb_timer, jiffies + + msecs_to_jiffies(ar->fw_recovery.hb_poll)); } void ath6kl_recovery_cleanup(struct ath6kl *ar) { ar->fw_recovery.enable = false; + del_timer_sync(&ar->fw_recovery.hb_timer); cancel_work_sync(&ar->fw_recovery.recovery_work); } @@ -56,7 +121,27 @@ void ath6kl_recovery_suspend(struct ath6kl *ar) { ath6kl_recovery_cleanup(ar); + if (!ar->fw_recovery.err_reason) + return; + /* Process pending fw error detection */ - if (ar->fw_recovery.err_reason) - ath6kl_init_hw_restart(ar); + ar->fw_recovery.err_reason = 0; + WARN_ON(ar->state != ATH6KL_STATE_ON); + ar->state = ATH6KL_STATE_RECOVERY; + ath6kl_init_hw_restart(ar); + ar->state = ATH6KL_STATE_ON; +} + +void ath6kl_recovery_resume(struct ath6kl *ar) +{ + ar->fw_recovery.enable = true; + + if (!ar->fw_recovery.hb_poll) + return; + + ar->fw_recovery.hb_pending = false; + ar->fw_recovery.seq_num = 0; + ar->fw_recovery.hb_misscnt = 0; + mod_timer(&ar->fw_recovery.hb_timer, + jiffies + msecs_to_jiffies(ar->fw_recovery.hb_poll)); } diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index cd2db42c098..68b46bda498 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -3767,6 +3767,19 @@ int ath6kl_wmi_set_inact_period(struct wmi *wmi, u8 if_idx, int inact_timeout) NO_SYNC_WMIFLAG); } +static void ath6kl_wmi_hb_challenge_resp_event(struct wmi *wmi, u8 *datap, + int len) +{ + struct wmix_hb_challenge_resp_cmd *cmd; + + if (len < sizeof(struct wmix_hb_challenge_resp_cmd)) + return; + + cmd = (struct wmix_hb_challenge_resp_cmd *) datap; + ath6kl_recovery_hb_event(wmi->parent_dev, + le32_to_cpu(cmd->cookie)); +} + static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb) { struct wmix_cmd_hdr *cmd; @@ -3791,6 +3804,7 @@ static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb) switch (id) { case WMIX_HB_CHALLENGE_RESP_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event hb challenge resp\n"); + ath6kl_wmi_hb_challenge_resp_event(wmi, datap, len); break; case WMIX_DBGLOG_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event dbglog len %d\n", len); diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index e916e57c9d9..98b1755e67f 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -2716,6 +2716,8 @@ int ath6kl_wmi_set_inact_period(struct wmi *wmi, u8 if_idx, int inact_timeout); void ath6kl_wmi_sscan_timer(unsigned long ptr); +int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source); + struct ath6kl_vif *ath6kl_get_vif_by_index(struct ath6kl *ar, u8 if_idx); void *ath6kl_wmi_init(struct ath6kl *devt); void ath6kl_wmi_shutdown(struct wmi *wmi); -- cgit v1.2.3-70-g09d2 From 77565794eb69cf73a5808c04b01bc2a97ebf32d3 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Wed, 29 Aug 2012 19:40:28 +0530 Subject: ath6kl: Recover from "wmi ctrl ep is full" condition In some error conditions, fw pauses HTC pipes which would result in control endpoint full condition. When we hit this case, most of the time the device will be unusable. Re-initialize the target to recover from this situation. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 1 + drivers/net/wireless/ath/ath6kl/recovery.c | 2 ++ drivers/net/wireless/ath/ath6kl/txrx.c | 1 + 3 files changed, 4 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index b2cbecf6cfe..ac90b31514b 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -660,6 +660,7 @@ enum ath6kl_state { enum ath6kl_fw_err { ATH6KL_FW_ASSERT, ATH6KL_FW_HB_RESP_FAILURE, + ATH6KL_FW_EP_FULL, }; struct ath6kl { diff --git a/drivers/net/wireless/ath/ath6kl/recovery.c b/drivers/net/wireless/ath/ath6kl/recovery.c index 4e3f205bb8a..03edeb8c2ce 100644 --- a/drivers/net/wireless/ath/ath6kl/recovery.c +++ b/drivers/net/wireless/ath/ath6kl/recovery.c @@ -30,6 +30,8 @@ static void ath6kl_recovery_work(struct work_struct *work) ath6kl_init_hw_restart(ar); ar->state = ATH6KL_STATE_ON; + clear_bit(WMI_CTRL_EP_FULL, &ar->flag); + ar->fw_recovery.err_reason = 0; if (ar->fw_recovery.enable) diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index cbe1a9d8911..e867193373f 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -594,6 +594,7 @@ enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, */ set_bit(WMI_CTRL_EP_FULL, &ar->flag); ath6kl_err("wmi ctrl ep is full\n"); + ath6kl_recovery_err_notify(ar, ATH6KL_FW_EP_FULL); return action; } -- cgit v1.2.3-70-g09d2 From 9d9188409aef2f3bebd1956d2f15bc970efcea7b Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Mon, 3 Sep 2012 12:49:34 +0530 Subject: ath6kl: Fix bug in scheduling hb_timer hb_timer should be scheduled only when hb_poll is non-zero. But in ath6kl_recovery_work() the timer is scheduled based on fw_recovery.enable instead which is wrong. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/recovery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/recovery.c b/drivers/net/wireless/ath/ath6kl/recovery.c index 03edeb8c2ce..c30df50b7ba 100644 --- a/drivers/net/wireless/ath/ath6kl/recovery.c +++ b/drivers/net/wireless/ath/ath6kl/recovery.c @@ -34,7 +34,7 @@ static void ath6kl_recovery_work(struct work_struct *work) ar->fw_recovery.err_reason = 0; - if (ar->fw_recovery.enable) + if (ar->fw_recovery.hb_poll) mod_timer(&ar->fw_recovery.hb_timer, jiffies + msecs_to_jiffies(ar->fw_recovery.hb_poll)); } -- cgit v1.2.3-70-g09d2 From e451f947c59d527088ebbd4505e776e968b9539a Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Mon, 3 Sep 2012 12:49:35 +0530 Subject: ath6kl: Remove unnecessary recovery state check in ath6kl_recovery_hb_timer() Checking for recovery state just before re-arming hb_timer is not necessary, this should be done at the begining of the timer instead. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/recovery.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/recovery.c b/drivers/net/wireless/ath/ath6kl/recovery.c index c30df50b7ba..1a82e8bf03c 100644 --- a/drivers/net/wireless/ath/ath6kl/recovery.c +++ b/drivers/net/wireless/ath/ath6kl/recovery.c @@ -61,7 +61,7 @@ static void ath6kl_recovery_hb_timer(unsigned long data) struct ath6kl *ar = (struct ath6kl *) data; int err; - if (!ar->fw_recovery.enable) + if (!ar->fw_recovery.enable || (ar->state == ATH6KL_STATE_RECOVERY)) return; if (ar->fw_recovery.hb_pending) @@ -86,9 +86,6 @@ static void ath6kl_recovery_hb_timer(unsigned long data) ath6kl_warn("Failed to send hb challenge request, err:%d\n", err); - if ((ar->state == ATH6KL_STATE_RECOVERY) || !ar->fw_recovery.enable) - return; - mod_timer(&ar->fw_recovery.hb_timer, jiffies + msecs_to_jiffies(ar->fw_recovery.hb_poll)); } -- cgit v1.2.3-70-g09d2 From a3561706320380027d4ac087e7b92ca19c0150df Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Mon, 3 Sep 2012 12:49:36 +0530 Subject: ath6kl: Add a bit to ath6kl_dev_state for recovery cleanup state Add a bit in ath6kl_dev_state to maintian the run time state of firmware recovery configuration. This would help to have user configuration in fw_recovery which will be added in a separate patch. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 2 +- drivers/net/wireless/ath/ath6kl/recovery.c | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index ac90b31514b..40a7b19925d 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -641,6 +641,7 @@ enum ath6kl_dev_state { SKIP_SCAN, ROAM_TBL_PEND, FIRST_BOOT, + RECOVERY_CLEANUP, }; enum ath6kl_state { @@ -807,7 +808,6 @@ struct ath6kl { bool wiphy_registered; struct ath6kl_fw_recovery { - bool enable; struct work_struct recovery_work; unsigned long err_reason; unsigned long hb_poll; diff --git a/drivers/net/wireless/ath/ath6kl/recovery.c b/drivers/net/wireless/ath/ath6kl/recovery.c index 1a82e8bf03c..98b6aa09e1b 100644 --- a/drivers/net/wireless/ath/ath6kl/recovery.c +++ b/drivers/net/wireless/ath/ath6kl/recovery.c @@ -46,7 +46,8 @@ void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason) set_bit(reason, &ar->fw_recovery.err_reason); - if (ar->fw_recovery.enable && ar->state != ATH6KL_STATE_RECOVERY) + if (!test_bit(RECOVERY_CLEANUP, &ar->flag) && + ar->state != ATH6KL_STATE_RECOVERY) queue_work(ar->ath6kl_wq, &ar->fw_recovery.recovery_work); } @@ -61,7 +62,8 @@ static void ath6kl_recovery_hb_timer(unsigned long data) struct ath6kl *ar = (struct ath6kl *) data; int err; - if (!ar->fw_recovery.enable || (ar->state == ATH6KL_STATE_RECOVERY)) + if (test_bit(RECOVERY_CLEANUP, &ar->flag) || + (ar->state == ATH6KL_STATE_RECOVERY)) return; if (ar->fw_recovery.hb_pending) @@ -94,7 +96,7 @@ void ath6kl_recovery_init(struct ath6kl *ar) { struct ath6kl_fw_recovery *recovery = &ar->fw_recovery; - recovery->enable = true; + clear_bit(RECOVERY_CLEANUP, &ar->flag); INIT_WORK(&recovery->recovery_work, ath6kl_recovery_work); recovery->seq_num = 0; recovery->hb_misscnt = 0; @@ -110,7 +112,7 @@ void ath6kl_recovery_init(struct ath6kl *ar) void ath6kl_recovery_cleanup(struct ath6kl *ar) { - ar->fw_recovery.enable = false; + set_bit(RECOVERY_CLEANUP, &ar->flag); del_timer_sync(&ar->fw_recovery.hb_timer); cancel_work_sync(&ar->fw_recovery.recovery_work); @@ -133,7 +135,7 @@ void ath6kl_recovery_suspend(struct ath6kl *ar) void ath6kl_recovery_resume(struct ath6kl *ar) { - ar->fw_recovery.enable = true; + clear_bit(RECOVERY_CLEANUP, &ar->flag); if (!ar->fw_recovery.hb_poll) return; -- cgit v1.2.3-70-g09d2 From 66ddcc39420f3c6d2356f7618fbed3dd61177cee Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Mon, 3 Sep 2012 12:49:37 +0530 Subject: ath6kl: Make fw error recovery configurable Add a modparam to configure recovery. Recovery from firmware error is disabled by default to debug the actual issue further. To recovery from error, modprobe ath6kl_core recovery_enable=1. Reported-by: Jin Navy Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.c | 12 ++++++++++-- drivers/net/wireless/ath/ath6kl/core.h | 1 + drivers/net/wireless/ath/ath6kl/recovery.c | 12 ++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c index fd5dd3aca77..4b46adbe8c9 100644 --- a/drivers/net/wireless/ath/ath6kl/core.c +++ b/drivers/net/wireless/ath/ath6kl/core.c @@ -33,6 +33,7 @@ static unsigned int wow_mode; static unsigned int uart_debug; static unsigned int ath6kl_p2p; static unsigned int testmode; +static unsigned int recovery_enable; static unsigned int heart_beat_poll; module_param(debug_mask, uint, 0644); @@ -41,9 +42,12 @@ module_param(wow_mode, uint, 0644); module_param(uart_debug, uint, 0644); module_param(ath6kl_p2p, uint, 0644); module_param(testmode, uint, 0644); +module_param(recovery_enable, uint, 0644); module_param(heart_beat_poll, uint, 0644); -MODULE_PARM_DESC(heart_beat_poll, "Enable fw error detection periodic" \ - "polling. This also specifies the polling interval in msecs"); +MODULE_PARM_DESC(recovery_enable, "Enable recovery from firmware error"); +MODULE_PARM_DESC(heart_beat_poll, "Enable fw error detection periodic" \ + "polling. This also specifies the polling interval in" \ + "msecs. Set reocvery_enable for this to be effective"); void ath6kl_core_tx_complete(struct ath6kl *ar, struct sk_buff *skb) { @@ -206,6 +210,10 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n", __func__, wdev->netdev->name, wdev->netdev, ar); + ar->fw_recovery.enable = !!recovery_enable; + if (!ar->fw_recovery.enable) + return ret; + if (heart_beat_poll && test_bit(ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL, ar->fw_capabilities)) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 40a7b19925d..3b2dfc18085 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -815,6 +815,7 @@ struct ath6kl { u32 seq_num; bool hb_pending; u8 hb_misscnt; + bool enable; } fw_recovery; #ifdef CONFIG_ATH6KL_DEBUG diff --git a/drivers/net/wireless/ath/ath6kl/recovery.c b/drivers/net/wireless/ath/ath6kl/recovery.c index 98b6aa09e1b..3a8d5e97dc8 100644 --- a/drivers/net/wireless/ath/ath6kl/recovery.c +++ b/drivers/net/wireless/ath/ath6kl/recovery.c @@ -41,6 +41,9 @@ static void ath6kl_recovery_work(struct work_struct *work) void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason) { + if (!ar->fw_recovery.enable) + return; + ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Fw error detected, reason:%d\n", reason); @@ -112,6 +115,9 @@ void ath6kl_recovery_init(struct ath6kl *ar) void ath6kl_recovery_cleanup(struct ath6kl *ar) { + if (!ar->fw_recovery.enable) + return; + set_bit(RECOVERY_CLEANUP, &ar->flag); del_timer_sync(&ar->fw_recovery.hb_timer); @@ -120,6 +126,9 @@ void ath6kl_recovery_cleanup(struct ath6kl *ar) void ath6kl_recovery_suspend(struct ath6kl *ar) { + if (!ar->fw_recovery.enable) + return; + ath6kl_recovery_cleanup(ar); if (!ar->fw_recovery.err_reason) @@ -135,6 +144,9 @@ void ath6kl_recovery_suspend(struct ath6kl *ar) void ath6kl_recovery_resume(struct ath6kl *ar) { + if (!ar->fw_recovery.enable) + return; + clear_bit(RECOVERY_CLEANUP, &ar->flag); if (!ar->fw_recovery.hb_poll) -- cgit v1.2.3-70-g09d2 From 527f6570300980251e818e80865b437eefb4e5d3 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Mon, 3 Sep 2012 22:15:36 +0200 Subject: ath6kl: fix uninitialized variable in ath6kl_sdio_enable_scatter() gcc 4.8 warns /backup/lsrc/git/linux-lto-2.6/drivers/net/wireless/ath/ath6kl/sdio.c: In function 'ath6kl_sdio_enable_scatter': /backup/lsrc/git/linux-lto-2.6/drivers/net/wireless/ath/ath6kl/sdio.c:748:16: warning: 'ret' may be used uninitialized in this function [-Wmaybe-uninitialized] if (virt_scat || ret) { ^ The variable can indeed be uninitialized when the previous if branch is skipped. I just set it to zero for now. I'm not fully sure the fix is correct, maybe the || should be an && ? Signed-off-by: Andi Kleen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/sdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index a72a4d02a4c..d111980d44c 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -709,7 +709,7 @@ static int ath6kl_sdio_enable_scatter(struct ath6kl *ar) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); struct htc_target *target = ar->htc_target; - int ret; + int ret = 0; bool virt_scat = false; if (ar_sdio->scatter_enabled) -- cgit v1.2.3-70-g09d2 From f08dbda25f14d6b9c1ba131f65d0c32917733f6c Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 5 Sep 2012 15:07:29 +0800 Subject: ath6kl: use list_move_tail instead of list_del/list_add_tail Using list_move_tail() instead of list_del() + list_add_tail(). spatch with a semantic match is used to found this problem. (http://coccinelle.lip6.fr/) Signed-off-by: Wei Yongjun Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/htc_pipe.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/htc_pipe.c b/drivers/net/wireless/ath/ath6kl/htc_pipe.c index f9626c72369..ba6bd497b78 100644 --- a/drivers/net/wireless/ath/ath6kl/htc_pipe.c +++ b/drivers/net/wireless/ath/ath6kl/htc_pipe.c @@ -374,9 +374,8 @@ static enum htc_send_queue_result htc_try_send(struct htc_target *target, packet = list_first_entry(txq, struct htc_packet, list); - list_del(&packet->list); - /* insert into local queue */ - list_add_tail(&packet->list, &send_queue); + /* move to local queue */ + list_move_tail(&packet->list, &send_queue); } /* @@ -399,11 +398,10 @@ static enum htc_send_queue_result htc_try_send(struct htc_target *target, * for cleanup */ } else { /* callback wants to keep this packet, - * remove from caller's queue */ - list_del(&packet->list); - /* put it in the send queue */ - list_add_tail(&packet->list, - &send_queue); + * move from caller's queue to the send + * queue */ + list_move_tail(&packet->list, + &send_queue); } } -- cgit v1.2.3-70-g09d2 From 58109df67aa073756eb5a2dc2ae068bc1bbcc125 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Tue, 11 Sep 2012 12:07:00 +0530 Subject: ath6kl: Fix reconnection issue after recovery Disallowing any wmi commands while re-initializing the firmware results in connection failures after recovery is done in open/WEP mode. To fix this, clear WMI_READY, to make sure no wmi command is tried while fw is down. Remove ATH6KL_STATE_RECOVERY state check in ath6kl_control_tx() so that any configuration during fw init time will go through using wmi commands. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 12 +++++++++--- drivers/net/wireless/ath/ath6kl/init.c | 6 +++++- drivers/net/wireless/ath/ath6kl/txrx.c | 3 +-- 3 files changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index c8b6be44fa2..530eede4f5e 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -151,6 +151,10 @@ static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif) return false; del_timer_sync(&vif->sched_scan_timer); + + if (ar->state == ATH6KL_STATE_RECOVERY) + return true; + ath6kl_wmi_enable_sched_scan_cmd(ar->wmi, vif->fw_vif_idx, false); return true; @@ -3435,8 +3439,9 @@ void ath6kl_cfg80211_stop(struct ath6kl_vif *vif) break; } - if (test_bit(CONNECTED, &vif->flags) || - test_bit(CONNECT_PEND, &vif->flags)) + if (vif->ar->state != ATH6KL_STATE_RECOVERY && + (test_bit(CONNECTED, &vif->flags) || + test_bit(CONNECT_PEND, &vif->flags))) ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx); vif->sme_state = SME_DISCONNECTED; @@ -3448,7 +3453,8 @@ void ath6kl_cfg80211_stop(struct ath6kl_vif *vif) netif_carrier_off(vif->ndev); /* disable scanning */ - if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF, + if (vif->ar->state != ATH6KL_STATE_RECOVERY && + ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF, 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0) ath6kl_warn("failed to disable scan during stop\n"); diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 6e270fa6d63..424676e06b3 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1697,10 +1697,14 @@ int ath6kl_init_hw_stop(struct ath6kl *ar) void ath6kl_init_hw_restart(struct ath6kl *ar) { + clear_bit(WMI_READY, &ar->flag); + ath6kl_cfg80211_stop_all(ar); - if (__ath6kl_init_hw_stop(ar)) + if (__ath6kl_init_hw_stop(ar)) { + ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Failed to stop during fw error recovery\n"); return; + } if (__ath6kl_init_hw_start(ar)) { ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Failed to restart during fw error recovery\n"); diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index e867193373f..efee590a234 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -288,8 +288,7 @@ int ath6kl_control_tx(void *devt, struct sk_buff *skb, int status = 0; struct ath6kl_cookie *cookie = NULL; - if (WARN_ON_ONCE(ar->state == ATH6KL_STATE_WOW) || - ar->state == ATH6KL_STATE_RECOVERY) { + if (WARN_ON_ONCE(ar->state == ATH6KL_STATE_WOW)) { dev_kfree_skb(skb); return -EACCES; } -- cgit v1.2.3-70-g09d2 From 43a06b346d1350009c8f7eaa1a2a137395874ca0 Mon Sep 17 00:00:00 2001 From: Raja Mani Date: Fri, 21 Sep 2012 15:08:53 +0530 Subject: ath6kl: Avoid null ptr dereference while printing reg domain pair Return value of ath6kl_get_regpair() is stored in 'regpair' in ath6kl_wmi_regdomain_event() func and it's directly accessed in the debug prints without checking for NULL value. There are situation to get NULL pointer as a return value from ath6kl_get_regpair() func. Fix this. Found this on code review. Signed-off-by: Raja Mani Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/wmi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 68b46bda498..64b81fd554b 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -936,8 +936,12 @@ static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len) regpair = ath6kl_get_regpair((u16) reg_code); country = ath6kl_regd_find_country_by_rd((u16) reg_code); - ath6kl_dbg(ATH6KL_DBG_WMI, "Regpair used: 0x%0x\n", - regpair->regDmnEnum); + if (regpair) + ath6kl_dbg(ATH6KL_DBG_WMI, "Regpair used: 0x%0x\n", + regpair->regDmnEnum); + else + ath6kl_warn("Regpair not found reg_code 0x%0x\n", + reg_code); } if (country && wmi->parent_dev->wiphy_registered) { -- cgit v1.2.3-70-g09d2 From d54601b92fbde2a7021a844e1373ba8c778cc0a3 Mon Sep 17 00:00:00 2001 From: Raja Mani Date: Fri, 21 Sep 2012 15:08:54 +0530 Subject: ath6kl: Check for valid rate table index There are 28 items defined in rate table array 'wmi_rate_tbl'. The rate table index (reply->rate_index) in ath6kl_wmi_bitrate_reply_rx() func is not checked for the valid max limit index before accessing rate table array. There may be some incidents to get memory crashes without safe max check. Fix this. Found this on code review. Signed-off-by: Raja Mani Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/wmi.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 64b81fd554b..f3aeebb2fd4 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -1174,6 +1174,9 @@ static int ath6kl_wmi_bitrate_reply_rx(struct wmi *wmi, u8 *datap, int len) rate = RATE_AUTO; } else { index = reply->rate_index & 0x7f; + if (WARN_ON_ONCE(index > (RATE_MCS_7_40 + 1))) + return -EINVAL; + sgi = (reply->rate_index & 0x80) ? 1 : 0; rate = wmi_rate_tbl[index][sgi]; } -- cgit v1.2.3-70-g09d2 From 363f149ce37bea91069177eab691111b242bfe73 Mon Sep 17 00:00:00 2001 From: Raja Mani Date: Fri, 21 Sep 2012 15:08:55 +0530 Subject: ath6kl: Check for valid endpoint ID values in ath6kl_control_tx() It's safe to check endpoint id values before it get really used. Found this on code review. Signed-off-by: Raja Mani Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/txrx.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index efee590a234..cf4380d573c 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -293,6 +293,12 @@ int ath6kl_control_tx(void *devt, struct sk_buff *skb, return -EACCES; } + if (WARN_ON_ONCE(eid == ENDPOINT_UNUSED || + eid >= ENDPOINT_MAX)) { + status = -EINVAL; + goto fail_ctrl_tx; + } + spin_lock_bh(&ar->lock); ath6kl_dbg(ATH6KL_DBG_WLAN_TX, -- cgit v1.2.3-70-g09d2 From baec5c6d0b191a44977a5fca131b5215e1868341 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Fri, 21 Sep 2012 12:45:24 +0530 Subject: ath6kl: Fix random rx data corruption The skb->tail pointer of rx buffers is not adjusted after skb->data pointer is aligned to 4-byte, this causes random rx data corruption. Signed-off-by: Jin Navy Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/txrx.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index cf4380d573c..c4501a9f051 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -894,8 +894,11 @@ void ath6kl_rx_refill(struct htc_target *target, enum htc_endpoint_id endpoint) break; packet = (struct htc_packet *) skb->head; - if (!IS_ALIGNED((unsigned long) skb->data, 4)) + if (!IS_ALIGNED((unsigned long) skb->data, 4)) { + size_t len = skb_headlen(skb); skb->data = PTR_ALIGN(skb->data - 4, 4); + skb_set_tail_pointer(skb, len); + } set_htc_rxpkt_info(packet, skb, skb->data, ATH6KL_BUFFER_SIZE, endpoint); packet->skb = skb; @@ -917,8 +920,11 @@ void ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count) return; packet = (struct htc_packet *) skb->head; - if (!IS_ALIGNED((unsigned long) skb->data, 4)) + if (!IS_ALIGNED((unsigned long) skb->data, 4)) { + size_t len = skb_headlen(skb); skb->data = PTR_ALIGN(skb->data - 4, 4); + skb_set_tail_pointer(skb, len); + } set_htc_rxpkt_info(packet, skb, skb->data, ATH6KL_AMSDU_BUFFER_SIZE, 0); packet->skb = skb; -- cgit v1.2.3-70-g09d2 From 07033ce2fbfb8d27663d1cd1e9ce36b9661007e0 Mon Sep 17 00:00:00 2001 From: Pandiyarajan Pitchaimuthu Date: Fri, 21 Sep 2012 15:06:14 +0530 Subject: ath6kl: Max clients reached notification When a station requests connection to an AP, that has already been connected to the maximum number of stations it can support, an event is sent to user space via NL80211_CMD_CONN_FAILED command and reason attribute NL80211_ATTR_CONN_FAILED_REASON with NL80211_CONN_FAIL_MAX_CLIENTS as reason. Signed-off-by: Pandiyarajan Pitchaimuthu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/main.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 9533558a623..cc2749a9e7f 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -1004,6 +1004,13 @@ void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, ar->last_ch = le16_to_cpu(vif->profile.ch); } + if (prot_reason_status == WMI_AP_REASON_MAX_STA) { + /* send max client reached notification to user space */ + cfg80211_conn_failed(vif->ndev, bssid, + NL80211_CONN_FAIL_MAX_CLIENTS, + GFP_KERNEL); + } + if (!ath6kl_remove_sta(ar, bssid, prot_reason_status)) return; -- cgit v1.2.3-70-g09d2 From 698bf867d0d3b5669c4e85b29d2a44043a2c5c99 Mon Sep 17 00:00:00 2001 From: Pandiyarajan Pitchaimuthu Date: Fri, 21 Sep 2012 15:08:53 +0530 Subject: ath6kl: Blocked client notification When a station tries to connect to an AP and if the MAC of the station is in the AP's block list, the station cannot connect to the AP. This is notified to the userspace with event NL80211_CMD_CONN_FAILED and attribute NL80211_ATTR_CONN_FAILED_REASON. The reason sent will be NL80211_CONN_FAIL_BLOCKED_CLIENT. Signed-off-by: Pandiyarajan Pitchaimuthu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/main.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index cc2749a9e7f..bd50b6b7b49 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -1011,6 +1011,13 @@ void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, GFP_KERNEL); } + if (prot_reason_status == WMI_AP_REASON_ACL) { + /* send blocked client notification to user space */ + cfg80211_conn_failed(vif->ndev, bssid, + NL80211_CONN_FAIL_BLOCKED_CLIENT, + GFP_KERNEL); + } + if (!ath6kl_remove_sta(ar, bssid, prot_reason_status)) return; -- cgit v1.2.3-70-g09d2 From 86aa7c1efc63e0969dee575ac9e021dbcbaa95c3 Mon Sep 17 00:00:00 2001 From: Pandiyarajan Pitchaimuthu Date: Fri, 21 Sep 2012 20:11:46 +0530 Subject: ath6kl: Array index out of bounds check The variable assigned_ep can be assigned value of -1 and is never checked if it equals -1. So the endpoint array can have -1 as the index value and can be out of bounds. The value of assigned_ep is checked for -1 and is ensured that the endpoint array doesn't go out of bounds. Signed-off-by: Pandiyarajan Pitchaimuthu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/htc_mbox.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/htc_mbox.c b/drivers/net/wireless/ath/ath6kl/htc_mbox.c index cd0e1ba410d..ceaf9219265 100644 --- a/drivers/net/wireless/ath/ath6kl/htc_mbox.c +++ b/drivers/net/wireless/ath/ath6kl/htc_mbox.c @@ -2492,7 +2492,8 @@ static int ath6kl_htc_mbox_conn_service(struct htc_target *target, max_msg_sz = le16_to_cpu(resp_msg->max_msg_sz); } - if (assigned_ep >= ENDPOINT_MAX || !max_msg_sz) { + if (WARN_ON_ONCE(assigned_ep == ENDPOINT_UNUSED || + assigned_ep >= ENDPOINT_MAX || !max_msg_sz)) { status = -ENOMEM; goto fail_tx; } -- cgit v1.2.3-70-g09d2 From 307749406d7daea452d55df76f734b4fffddf599 Mon Sep 17 00:00:00 2001 From: Pandiyarajan Pitchaimuthu Date: Fri, 21 Sep 2012 20:13:09 +0530 Subject: ath6kl: Check for valid endpoint ID in ath6kl_tx_complete() Endpoint ID is checked to make sure it is valid. Signed-off-by: Pandiyarajan Pitchaimuthu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/txrx.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index c4501a9f051..78b36928657 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -704,6 +704,10 @@ void ath6kl_tx_complete(struct htc_target *target, list); list_del(&packet->list); + if (WARN_ON_ONCE(packet->endpoint == ENDPOINT_UNUSED || + packet->endpoint >= ENDPOINT_MAX)) + continue; + ath6kl_cookie = (struct ath6kl_cookie *)packet->pkt_cntxt; if (WARN_ON_ONCE(!ath6kl_cookie)) continue; -- cgit v1.2.3-70-g09d2 From cf0dfa1330495ae2c3757788224cc043cfb17e77 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Thu, 27 Sep 2012 18:19:48 +0530 Subject: ath6kl: Remove obselete USB device related checks These checks are no longer needed as the necessary USB support is already present in the driver. Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/hif.c | 5 ----- drivers/net/wireless/ath/ath6kl/htc_mbox.c | 10 +--------- 2 files changed, 1 insertion(+), 14 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/hif.c b/drivers/net/wireless/ath/ath6kl/hif.c index 029914a22ea..a7a1ec40636 100644 --- a/drivers/net/wireless/ath/ath6kl/hif.c +++ b/drivers/net/wireless/ath/ath6kl/hif.c @@ -696,11 +696,6 @@ int ath6kl_hif_setup(struct ath6kl_device *dev) ath6kl_dbg(ATH6KL_DBG_HIF, "hif block size %d mbox addr 0x%x\n", dev->htc_cnxt->block_sz, dev->ar->mbox_info.htc_addr); - /* usb doesn't support enabling interrupts */ - /* FIXME: remove check once USB support is implemented */ - if (dev->ar->hif_type == ATH6KL_HIF_TYPE_USB) - return 0; - status = ath6kl_hif_disable_intrs(dev); fail_setup: diff --git a/drivers/net/wireless/ath/ath6kl/htc_mbox.c b/drivers/net/wireless/ath/ath6kl/htc_mbox.c index ceaf9219265..fbb78dfe078 100644 --- a/drivers/net/wireless/ath/ath6kl/htc_mbox.c +++ b/drivers/net/wireless/ath/ath6kl/htc_mbox.c @@ -2656,12 +2656,6 @@ static int ath6kl_htc_mbox_wait_target(struct htc_target *target) struct htc_service_connect_resp resp; int status; - /* FIXME: remove once USB support is implemented */ - if (target->dev->ar->hif_type == ATH6KL_HIF_TYPE_USB) { - ath6kl_err("HTC doesn't support USB yet. Patience!\n"); - return -EOPNOTSUPP; - } - /* we should be getting 1 control message that the target is ready */ packet = htc_wait_for_ctrl_msg(target); @@ -2891,9 +2885,7 @@ static void ath6kl_htc_mbox_cleanup(struct htc_target *target) { struct htc_packet *packet, *tmp_packet; - /* FIXME: remove check once USB support is implemented */ - if (target->dev->ar->hif_type != ATH6KL_HIF_TYPE_USB) - ath6kl_hif_cleanup_scatter(target->dev->ar); + ath6kl_hif_cleanup_scatter(target->dev->ar); list_for_each_entry_safe(packet, tmp_packet, &target->free_ctrl_txbuf, list) { -- cgit v1.2.3-70-g09d2 From 5dbdf6feecad54b739cb7a5cff521fba86cabba5 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Thu, 27 Sep 2012 18:19:49 +0530 Subject: ath6kl: Return error case when ath6kl_usb_alloc_pipe_resources fails Incase the resource allocation for the struct ath6kl_urb_context in the function ath6kl_usb_alloc_pipe_resources fails, return this error case so that ath6kl_usb_probe is aware of this error case. Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/usb.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c index a6d13775467..2014fac4274 100644 --- a/drivers/net/wireless/ath/ath6kl/usb.c +++ b/drivers/net/wireless/ath/ath6kl/usb.c @@ -185,9 +185,10 @@ static int ath6kl_usb_alloc_pipe_resources(struct ath6kl_usb_pipe *pipe, for (i = 0; i < urb_cnt; i++) { urb_context = kzalloc(sizeof(struct ath6kl_urb_context), GFP_KERNEL); - if (urb_context == NULL) - /* FIXME: set status to -ENOMEM */ - break; + if (urb_context == NULL) { + status = -ENOMEM; + goto fail_alloc_pipe_resources; + } urb_context->pipe = pipe; @@ -204,6 +205,7 @@ static int ath6kl_usb_alloc_pipe_resources(struct ath6kl_usb_pipe *pipe, pipe->logical_pipe_num, pipe->usb_pipe_handle, pipe->urb_alloc); +fail_alloc_pipe_resources: return status; } -- cgit v1.2.3-70-g09d2 From c0b34e2b41cc29c15b4cf247727efdab6a864c1b Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Thu, 27 Sep 2012 18:19:50 +0530 Subject: ath6kl: Rename ATH6KL_HW_FLAG_64BIT_RATES Rename ATH6KL_HW_FLAG_64BIT_RATES to ATH6KL_HW_64BIT_RATES. This seemed to be necessary to add/use new hardware flags without exceeding 80 lines. We shall be adding new hw flags dropping the FLAG term. Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 +- drivers/net/wireless/ath/ath6kl/core.h | 2 +- drivers/net/wireless/ath/ath6kl/init.c | 8 ++++---- drivers/net/wireless/ath/ath6kl/wmi.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 530eede4f5e..6d143e453f9 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3708,7 +3708,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) ath6kl_band_5ghz.ht_cap.ht_supported = false; } - if (ar->hw.flags & ATH6KL_HW_FLAG_64BIT_RATES) { + if (ar->hw.flags & ATH6KL_HW_64BIT_RATES) { ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff; ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff; ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff; diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 3b2dfc18085..fc5e4b25818 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -149,7 +149,7 @@ struct ath6kl_fw_ie { }; enum ath6kl_hw_flags { - ATH6KL_HW_FLAG_64BIT_RATES = BIT(0), + ATH6KL_HW_64BIT_RATES = BIT(0), }; #define ATH6KL_FW_API2_FILE "fw-2.bin" diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 424676e06b3..18550c51e12 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -93,7 +93,7 @@ static const struct ath6kl_hw hw_list[] = { .board_addr = 0x433900, .refclk_hz = 26000000, .uarttx_pin = 11, - .flags = ATH6KL_HW_FLAG_64BIT_RATES, + .flags = ATH6KL_HW_64BIT_RATES, .fw = { .dir = AR6004_HW_1_0_FW_DIR, @@ -113,7 +113,7 @@ static const struct ath6kl_hw hw_list[] = { .board_addr = 0x43d400, .refclk_hz = 40000000, .uarttx_pin = 11, - .flags = ATH6KL_HW_FLAG_64BIT_RATES, + .flags = ATH6KL_HW_64BIT_RATES, .fw = { .dir = AR6004_HW_1_1_FW_DIR, @@ -133,7 +133,7 @@ static const struct ath6kl_hw hw_list[] = { .board_addr = 0x435c00, .refclk_hz = 40000000, .uarttx_pin = 11, - .flags = ATH6KL_HW_FLAG_64BIT_RATES, + .flags = ATH6KL_HW_64BIT_RATES, .fw = { .dir = AR6004_HW_1_2_FW_DIR, @@ -152,7 +152,7 @@ static const struct ath6kl_hw hw_list[] = { .board_addr = 0x436400, .refclk_hz = 40000000, .uarttx_pin = 11, - .flags = ATH6KL_HW_FLAG_64BIT_RATES, + .flags = ATH6KL_HW_64BIT_RATES, .fw = { .dir = AR6004_HW_1_3_FW_DIR, diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index f3aeebb2fd4..55ccf977033 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -2828,7 +2828,7 @@ int ath6kl_wmi_set_bitrate_mask(struct wmi *wmi, u8 if_idx, { struct ath6kl *ar = wmi->parent_dev; - if (ar->hw.flags & ATH6KL_HW_FLAG_64BIT_RATES) + if (ar->hw.flags & ATH6KL_HW_64BIT_RATES) return ath6kl_set_bitrate_mask64(wmi, if_idx, mask); else return ath6kl_set_bitrate_mask32(wmi, if_idx, mask); -- cgit v1.2.3-70-g09d2 From 7ac25eacc6766617edaac69d928f431a9983ccf2 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Thu, 27 Sep 2012 18:19:51 +0530 Subject: ath6kl: Fix inactivity timeout for AR6004 Currently AR6004 handles the inactivity timeout resolution in minutes rather than seconds. So parse the inactivity timeout to the firmware in minutes. For now we will cleanup the inactive station entries to the nearest converted minutes (ex: an inactive time of 70 seconds would take atleast 2 - 3 minutes) Tested with surprise removal of client cards/host shutdown. Cc: Manikandan Radhakrishnan Reported-by: Leela Kella Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 10 +++++++++- drivers/net/wireless/ath/ath6kl/core.h | 3 ++- drivers/net/wireless/ath/ath6kl/init.c | 13 ++++++++----- 3 files changed, 19 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 6d143e453f9..bf5e7d51939 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -2760,6 +2760,7 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, int res; int i, ret; u16 rsn_capab = 0; + int inactivity_timeout = 0; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__); @@ -2896,8 +2897,15 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, } if (info->inactivity_timeout) { + + inactivity_timeout = info->inactivity_timeout; + + if (ar->hw.flags & ATH6KL_HW_AP_INACTIVITY_MINS) + inactivity_timeout = DIV_ROUND_UP(inactivity_timeout, + 60); + res = ath6kl_wmi_set_inact_period(ar->wmi, vif->fw_vif_idx, - info->inactivity_timeout); + inactivity_timeout); if (res < 0) return res; } diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index fc5e4b25818..95fc9b37e6a 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -149,7 +149,8 @@ struct ath6kl_fw_ie { }; enum ath6kl_hw_flags { - ATH6KL_HW_64BIT_RATES = BIT(0), + ATH6KL_HW_64BIT_RATES = BIT(0), + ATH6KL_HW_AP_INACTIVITY_MINS = BIT(1), }; #define ATH6KL_FW_API2_FILE "fw-2.bin" diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 18550c51e12..7ffb8533986 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -93,7 +93,8 @@ static const struct ath6kl_hw hw_list[] = { .board_addr = 0x433900, .refclk_hz = 26000000, .uarttx_pin = 11, - .flags = ATH6KL_HW_64BIT_RATES, + .flags = ATH6KL_HW_64BIT_RATES | + ATH6KL_HW_AP_INACTIVITY_MINS, .fw = { .dir = AR6004_HW_1_0_FW_DIR, @@ -113,8 +114,8 @@ static const struct ath6kl_hw hw_list[] = { .board_addr = 0x43d400, .refclk_hz = 40000000, .uarttx_pin = 11, - .flags = ATH6KL_HW_64BIT_RATES, - + .flags = ATH6KL_HW_64BIT_RATES | + ATH6KL_HW_AP_INACTIVITY_MINS, .fw = { .dir = AR6004_HW_1_1_FW_DIR, .fw = AR6004_HW_1_1_FIRMWARE_FILE, @@ -133,7 +134,8 @@ static const struct ath6kl_hw hw_list[] = { .board_addr = 0x435c00, .refclk_hz = 40000000, .uarttx_pin = 11, - .flags = ATH6KL_HW_64BIT_RATES, + .flags = ATH6KL_HW_64BIT_RATES | + ATH6KL_HW_AP_INACTIVITY_MINS, .fw = { .dir = AR6004_HW_1_2_FW_DIR, @@ -152,7 +154,8 @@ static const struct ath6kl_hw hw_list[] = { .board_addr = 0x436400, .refclk_hz = 40000000, .uarttx_pin = 11, - .flags = ATH6KL_HW_64BIT_RATES, + .flags = ATH6KL_HW_64BIT_RATES | + ATH6KL_HW_AP_INACTIVITY_MINS, .fw = { .dir = AR6004_HW_1_3_FW_DIR, -- cgit v1.2.3-70-g09d2 From 171fe76877d3d8071a901e64eb63eeee6c7760a2 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Thu, 27 Sep 2012 18:19:52 +0530 Subject: ath6kl: Fix mapping uplink endpoint for AR6004 AR6004(UB134) firmware supports only LP Endpoint, So map all Access Categories to Low Priority endpoints. This fixes a WPA2 connection issue as the uplink(tx) endpoint is appropriately mapped in sync with the firmware. Tested-by: Ben Gray Reported-by: Ben Gray Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 1 + drivers/net/wireless/ath/ath6kl/init.c | 5 +++-- drivers/net/wireless/ath/ath6kl/usb.c | 12 ++++++++++-- 3 files changed, 14 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 95fc9b37e6a..1778bd3c732 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -151,6 +151,7 @@ struct ath6kl_fw_ie { enum ath6kl_hw_flags { ATH6KL_HW_64BIT_RATES = BIT(0), ATH6KL_HW_AP_INACTIVITY_MINS = BIT(1), + ATH6KL_HW_MAP_LP_ENDPOINT = BIT(2), }; #define ATH6KL_FW_API2_FILE "fw-2.bin" diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 7ffb8533986..bb6aeea1c87 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -155,7 +155,8 @@ static const struct ath6kl_hw hw_list[] = { .refclk_hz = 40000000, .uarttx_pin = 11, .flags = ATH6KL_HW_64BIT_RATES | - ATH6KL_HW_AP_INACTIVITY_MINS, + ATH6KL_HW_AP_INACTIVITY_MINS | + ATH6KL_HW_MAP_LP_ENDPOINT, .fw = { .dir = AR6004_HW_1_3_FW_DIR, @@ -360,7 +361,7 @@ static int ath6kl_init_service_ep(struct ath6kl *ar) if (ath6kl_connectservice(ar, &connect, "WMI DATA BK")) return -EIO; - /* connect to Video service, map this to to HI PRI */ + /* connect to Video service, map this to HI PRI */ connect.svc_id = WMI_DATA_VI_SVC; if (ath6kl_connectservice(ar, &connect, "WMI DATA VI")) return -EIO; diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c index 2014fac4274..62bcc0d5bc2 100644 --- a/drivers/net/wireless/ath/ath6kl/usb.c +++ b/drivers/net/wireless/ath/ath6kl/usb.c @@ -805,7 +805,11 @@ static int ath6kl_usb_map_service_pipe(struct ath6kl *ar, u16 svc_id, *dl_pipe = ATH6KL_USB_PIPE_RX_DATA; break; case WMI_DATA_VI_SVC: - *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_MP; + + if (ar->hw.flags & ATH6KL_HW_MAP_LP_ENDPOINT) + *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_LP; + else + *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_MP; /* * Disable rxdata2 directly, it will be enabled * if FW enable rxdata2 @@ -813,7 +817,11 @@ static int ath6kl_usb_map_service_pipe(struct ath6kl *ar, u16 svc_id, *dl_pipe = ATH6KL_USB_PIPE_RX_DATA; break; case WMI_DATA_VO_SVC: - *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_HP; + + if (ar->hw.flags & ATH6KL_HW_MAP_LP_ENDPOINT) + *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_LP; + else + *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_MP; /* * Disable rxdata2 directly, it will be enabled * if FW enable rxdata2 -- cgit v1.2.3-70-g09d2 From a2e1be33a2a762a30e2960c11634a672cdf131cb Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Thu, 27 Sep 2012 18:19:53 +0530 Subject: ath6kl: Add a hardware flag for SDIO CRC error workaround Make use of SDIO CRC error workaround hardware flag and avoid target revision checks. Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 1 + drivers/net/wireless/ath/ath6kl/init.c | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 1778bd3c732..189d8faf8c8 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -152,6 +152,7 @@ enum ath6kl_hw_flags { ATH6KL_HW_64BIT_RATES = BIT(0), ATH6KL_HW_AP_INACTIVITY_MINS = BIT(1), ATH6KL_HW_MAP_LP_ENDPOINT = BIT(2), + ATH6KL_HW_SDIO_CRC_ERROR_WAR = BIT(3), }; #define ATH6KL_FW_API2_FILE "fw-2.bin" diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index bb6aeea1c87..f21fa322e5c 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -42,7 +42,7 @@ static const struct ath6kl_hw hw_list[] = { .reserved_ram_size = 6912, .refclk_hz = 26000000, .uarttx_pin = 8, - .flags = 0, + .flags = ATH6KL_HW_SDIO_CRC_ERROR_WAR, /* hw2.0 needs override address hardcoded */ .app_start_override_addr = 0x944C00, @@ -68,7 +68,7 @@ static const struct ath6kl_hw hw_list[] = { .refclk_hz = 26000000, .uarttx_pin = 8, .testscript_addr = 0x57ef74, - .flags = 0, + .flags = ATH6KL_HW_SDIO_CRC_ERROR_WAR, .fw = { .dir = AR6003_HW_2_1_1_FW_DIR, @@ -1431,8 +1431,7 @@ static int ath6kl_init_upload(struct ath6kl *ar) return status; /* WAR to avoid SDIO CRC err */ - if (ar->version.target_ver == AR6003_HW_2_0_VERSION || - ar->version.target_ver == AR6003_HW_2_1_1_VERSION) { + if (ar->hw.flags & ATH6KL_HW_SDIO_CRC_ERROR_WAR) { ath6kl_err("temporary war to avoid sdio crc error\n"); param = 0x28; -- cgit v1.2.3-70-g09d2 From e0509d3bdd7365d06c9bf570bf9f118cae6cbd58 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Tue, 11 Sep 2012 23:18:34 +0200 Subject: carl9170: fix spurious transmissions in sniffer mode Several people have complained about an unusual and undocumented feature of the AR9170 hardware: In siffer mode, the hardware generates spurious ACK frames for every received frame... even broadcasts. The reason for this malfunction is unknown: But there's a workaround: Instead of the special sniffer mode, the hardware will be put into station mode and all rx filters are disabled. Reported-by: Johannes Berg Reported-by: Marco Fonseca Reported-by: Janusz Dziedzic Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/mac.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/mac.c b/drivers/net/wireless/ath/carl9170/mac.c index e3b1b6e8776..24d75ab94f0 100644 --- a/drivers/net/wireless/ath/carl9170/mac.c +++ b/drivers/net/wireless/ath/carl9170/mac.c @@ -343,7 +343,24 @@ int carl9170_set_operating_mode(struct ar9170 *ar) break; } } else { - mac_addr = NULL; + /* + * Enable monitor mode + * + * rx_ctrl |= AR9170_MAC_RX_CTRL_ACK_IN_SNIFFER; + * sniffer |= AR9170_MAC_SNIFFER_ENABLE_PROMISC; + * + * When the hardware is in SNIFFER_PROMISC mode, + * it generates spurious ACKs for every incoming + * frame. This confuses every peer in the + * vicinity and the network throughput will suffer + * badly. + * + * Hence, the hardware will be put into station + * mode and just the rx filters are disabled. + */ + cam_mode |= AR9170_MAC_CAM_STA; + rx_ctrl |= AR9170_MAC_RX_CTRL_PASS_TO_HOST; + mac_addr = common->macaddr; bssid = NULL; } rcu_read_unlock(); @@ -355,8 +372,6 @@ int carl9170_set_operating_mode(struct ar9170 *ar) enc_mode |= AR9170_MAC_ENCRYPTION_RX_SOFTWARE; if (ar->sniffer_enabled) { - rx_ctrl |= AR9170_MAC_RX_CTRL_ACK_IN_SNIFFER; - sniffer |= AR9170_MAC_SNIFFER_ENABLE_PROMISC; enc_mode |= AR9170_MAC_ENCRYPTION_RX_SOFTWARE; } -- cgit v1.2.3-70-g09d2 From be41b052029f75a72df3c437a238bf9d574b6461 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Mon, 8 Oct 2012 21:30:51 +0530 Subject: ath9k: Ensure we set FTP_STOMP_LOW weight when WLAN is idle When WLAN is idle ensure we downgrade to FTP_STOMP_LOW weight (from STOMP_LOW) to provide more bandwidth for BT FTP profile. WLAN's idleness can be estimated by taking into account of the rx data packets and just ignore beacons, qos nullfunc etc. Also update bt_wait_time even if the chip is in NETWORK SLEEP mode. This should help BT throughput when WLAN is idle. Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/gpio.c | 2 +- drivers/net/wireless/ath/ath9k/recv.c | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index bf4fb7db15e..9e63a03330c 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -193,7 +193,6 @@ static void ath_mci_ftp_adjust(struct ath_softc *sc) struct ath_mci_profile *mci = &btcoex->mci; struct ath_hw *ah = sc->sc_ah; - btcoex->bt_wait_time += btcoex->btcoex_period; if (btcoex->bt_wait_time > ATH_BTCOEX_RX_WAIT_TIME) { if (ar9003_mci_state(ah, MCI_STATE_NEED_FTP_STOMP) && (mci->num_pan || mci->num_other_acl)) @@ -222,6 +221,7 @@ static void ath_btcoex_period_timer(unsigned long data) spin_lock_irqsave(&sc->sc_pm_lock, flags); if (sc->sc_ah->power_mode == ATH9K_PM_NETWORK_SLEEP) { + btcoex->bt_wait_time += btcoex->btcoex_period; spin_unlock_irqrestore(&sc->sc_pm_lock, flags); goto skip_hw_wakeup; } diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 83d16e7ed27..a04028bce28 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -1105,7 +1105,10 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) else rs.is_mybeacon = false; - sc->rx.num_pkts++; + if (ieee80211_is_data_present(hdr->frame_control) && + !ieee80211_is_qos_nullfunc(hdr->frame_control)) + sc->rx.num_pkts++; + ath_debug_stat_rx(sc, &rs); /* -- cgit v1.2.3-70-g09d2 From 8b0b6be5cb83e871adf8a134e843c01d75b1d128 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Mon, 8 Oct 2012 21:30:52 +0530 Subject: ath9k_htc: Advertise interface combinations supported This will allow us to create virtual interface the driver supports. Also this ensures multivif support and limitation advertised by the driver is taken care in cfg80211 itself. Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/htc_drv_init.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index d98255eb1b9..5ecf1287ddd 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -694,6 +694,20 @@ err_hw: return ret; } +static const struct ieee80211_iface_limit if_limits[] = { + { .max = 2, .types = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_P2P_CLIENT) }, + { .max = 2, .types = BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_GO) }, +}; + +static const struct ieee80211_iface_combination if_comb = { + .limits = if_limits, + .n_limits = ARRAY_SIZE(if_limits), + .max_interfaces = 2, + .num_different_channels = 1, +}; + static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, struct ieee80211_hw *hw) { @@ -716,6 +730,9 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT); + hw->wiphy->iface_combinations = &if_comb; + hw->wiphy->n_iface_combinations = 1; + hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN | -- cgit v1.2.3-70-g09d2 From 96f99d3db736ad0ac275bfec6f83240b1b3019ec Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Mon, 8 Oct 2012 21:30:53 +0530 Subject: ath9k_htc: Remove interface combination specific checks Once the driver advertizes interface combination logic based on its firmware/hardware limitation, cfg80211 takes care of all the necessary logic such as maximum beaconing vifs, standlone interface etc. Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 20 -------------------- 1 file changed, 20 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index ca78e33ca23..66f6a74c508 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1036,26 +1036,6 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw, mutex_lock(&priv->mutex); - if (priv->nvifs >= ATH9K_HTC_MAX_VIF) { - mutex_unlock(&priv->mutex); - return -ENOBUFS; - } - - if (priv->num_ibss_vif || - (priv->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) { - ath_err(common, "IBSS coexistence with other modes is not allowed\n"); - mutex_unlock(&priv->mutex); - return -ENOBUFS; - } - - if (((vif->type == NL80211_IFTYPE_AP) || - (vif->type == NL80211_IFTYPE_ADHOC)) && - ((priv->num_ap_vif + priv->num_ibss_vif) >= ATH9K_HTC_MAX_BCN_VIF)) { - ath_err(common, "Max. number of beaconing interfaces reached\n"); - mutex_unlock(&priv->mutex); - return -ENOBUFS; - } - ath9k_htc_ps_wakeup(priv); memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); memcpy(&hvif.myaddr, vif->addr, ETH_ALEN); -- cgit v1.2.3-70-g09d2 From aebc0d40f9f7310d4651df2772d208fd9b2d2fa0 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Mon, 8 Oct 2012 21:30:54 +0530 Subject: ath9k: Advertize beacon_int_infra_match Currently ath9k need to have beacon interval matched between STA mode and beaconing mode. Advertize this through interface combinations. Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/init.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index fad3ccd5cd9..546bae93647 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -687,6 +687,7 @@ static const struct ieee80211_iface_combination if_comb = { .n_limits = ARRAY_SIZE(if_limits), .max_interfaces = 2048, .num_different_channels = 1, + .beacon_int_infra_match = true, }; void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) -- cgit v1.2.3-70-g09d2 From 50072ebca3d0aec8c5b8543e767d45c6626523af Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Fri, 12 Oct 2012 14:07:22 +0530 Subject: ath9k: Send WLAN channel info to BT WLAN updates channel bitmap when associated and disassociated. Channel bitmap will reflect whare are the channels used or affected by WLAN and BT should avoid using those. Not doing so, could affect BT traffic as both WLAN and BT is operating on same channel. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 5 ++++ drivers/net/wireless/ath/ath9k/mci.c | 50 +++++++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/mci.h | 23 ++++++++++++++++ 3 files changed, 78 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 2da62be081f..515e184ab47 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1450,6 +1450,9 @@ static void ath9k_set_assoc_state(struct ath_softc *sc, sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON; spin_unlock_irqrestore(&sc->sc_pm_lock, flags); + if (ath9k_hw_mci_is_enabled(sc->sc_ah)) + ath9k_mci_update_wlan_channels(sc, false); + ath_dbg(common, CONFIG, "Primary Station interface: %pM, BSSID: %pM\n", vif->addr, common->curbssid); @@ -1506,6 +1509,8 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, memset(common->curbssid, 0, ETH_ALEN); common->curaid = 0; ath9k_hw_write_associd(sc->sc_ah); + if (ath9k_hw_mci_is_enabled(sc->sc_ah)) + ath9k_mci_update_wlan_channels(sc, true); } } diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index ec2d7c80756..1733a5ac327 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -600,3 +600,53 @@ void ath_mci_enable(struct ath_softc *sc) if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI) sc->sc_ah->imask |= ATH9K_INT_MCI; } + +void ath9k_mci_update_wlan_channels(struct ath_softc *sc, bool allow_all) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; + struct ath9k_channel *chan = ah->curchan; + u32 channelmap[] = {0x00000000, 0xffff0000, 0xffffffff, 0x7fffffff}; + int i; + s16 chan_start, chan_end; + u16 wlan_chan; + + if (!chan || !IS_CHAN_2GHZ(chan)) + return; + + if (allow_all) + goto send_wlan_chan; + + wlan_chan = chan->channel - 2402; + + chan_start = wlan_chan - 10; + chan_end = wlan_chan + 10; + + if (chan->chanmode == CHANNEL_G_HT40PLUS) + chan_end += 20; + else if (chan->chanmode == CHANNEL_G_HT40MINUS) + chan_start -= 20; + + /* adjust side band */ + chan_start -= 7; + chan_end += 7; + + if (chan_start <= 0) + chan_start = 0; + if (chan_end >= ATH_MCI_NUM_BT_CHANNELS) + chan_end = ATH_MCI_NUM_BT_CHANNELS - 1; + + ath_dbg(ath9k_hw_common(ah), MCI, + "WLAN current channel %d mask BT channel %d - %d\n", + wlan_chan, chan_start, chan_end); + + for (i = chan_start; i < chan_end; i++) + MCI_GPM_CLR_CHANNEL_BIT(&channelmap, i); + +send_wlan_chan: + /* update and send wlan channels info to BT */ + for (i = 0; i < 4; i++) + mci->wlan_channels[i] = channelmap[i]; + ar9003_mci_send_wlan_channels(ah); + ar9003_mci_state(ah, MCI_STATE_SEND_VERSION_QUERY); +} diff --git a/drivers/net/wireless/ath/ath9k/mci.h b/drivers/net/wireless/ath/ath9k/mci.h index fc14eea034e..a3df314c1e7 100644 --- a/drivers/net/wireless/ath/ath9k/mci.h +++ b/drivers/net/wireless/ath/ath9k/mci.h @@ -32,6 +32,24 @@ #define ATH_MCI_MAX_PROFILE (ATH_MCI_MAX_ACL_PROFILE +\ ATH_MCI_MAX_SCO_PROFILE) +#define ATH_MCI_NUM_BT_CHANNELS 79 + +#define MCI_GPM_SET_CHANNEL_BIT(_p_gpm, _bt_chan) \ + do { \ + if (_bt_chan < ATH_MCI_NUM_BT_CHANNELS) { \ + *(((u8 *)(_p_gpm)) + MCI_GPM_COEX_B_CHANNEL_MAP + \ + (_bt_chan / 8)) |= (1 << (_bt_chan & 7)); \ + } \ + } while (0) + +#define MCI_GPM_CLR_CHANNEL_BIT(_p_gpm, _bt_chan) \ + do { \ + if (_bt_chan < ATH_MCI_NUM_BT_CHANNELS) { \ + *(((u8 *)(_p_gpm)) + MCI_GPM_COEX_B_CHANNEL_MAP + \ + (_bt_chan / 8)) &= ~(1 << (_bt_chan & 7));\ + } \ + } while (0) + #define INC_PROF(_mci, _info) do { \ switch (_info->type) { \ case MCI_GPM_COEX_PROFILE_RFCOMM:\ @@ -133,10 +151,15 @@ void ath_mci_intr(struct ath_softc *sc); #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT void ath_mci_enable(struct ath_softc *sc); +void ath9k_mci_update_wlan_channels(struct ath_softc *sc, bool allow_all); #else static inline void ath_mci_enable(struct ath_softc *sc) { } +static inline void ath9k_mci_update_wlan_channels(struct ath_softc *sc, + bool allow_all) +{ +} #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ #endif /* MCI_H*/ -- cgit v1.2.3-70-g09d2 From db60428b1af11cf216bb0736b24b2cf0c7b54071 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Fri, 12 Oct 2012 14:07:23 +0530 Subject: ath9k: Add concurrent WLAN and BT tx support for MCI based chips This feature enables both WLAN and BT can transmit simultaneously by setting WLAN and BT to equal priorities. Whenever both are transmitting, it might violate regulatory power limits. To avoid regulatory violation, WLAN tx power will be adjusted according to BT power index based on avaliability of BT scheduling message. If the combined power exceeds threshold, BT transmission will be held off. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.h | 1 - drivers/net/wireless/ath/ath9k/btcoex.c | 60 +++++++++++++++++++++-------- drivers/net/wireless/ath/ath9k/btcoex.h | 3 ++ drivers/net/wireless/ath/ath9k/mci.c | 60 +++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/mci.h | 3 ++ 5 files changed, 110 insertions(+), 17 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.h b/drivers/net/wireless/ath/ath9k/ar9003_mci.h index 2a2d0188961..29282348eb4 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.h @@ -196,7 +196,6 @@ enum mci_state_type { MCI_STATE_SEND_WLAN_COEX_VERSION, MCI_STATE_SEND_VERSION_QUERY, MCI_STATE_SEND_STATUS_QUERY, - MCI_STATE_SET_CONCUR_TX_PRI, MCI_STATE_RECOVER_RX, MCI_STATE_NEED_FTP_STOMP, MCI_STATE_DEBUG, diff --git a/drivers/net/wireless/ath/ath9k/btcoex.c b/drivers/net/wireless/ath/ath9k/btcoex.c index 419e9a3f2fe..05d9be5be52 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.c +++ b/drivers/net/wireless/ath/ath9k/btcoex.c @@ -218,27 +218,45 @@ void ath9k_hw_btcoex_set_weight(struct ath_hw *ah, enum ath_stomp_type stomp_type) { struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; + struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; + u8 txprio_shift[] = { 24, 16, 16, 0 }; /* tx priority weight */ + bool concur_tx = (mci_hw->concur_tx && btcoex_hw->tx_prio[stomp_type]); + const u32 *weight = ar9003_wlan_weights[stomp_type]; + int i; - if (AR_SREV_9300_20_OR_LATER(ah)) { - const u32 *weight = ar9003_wlan_weights[stomp_type]; - int i; - - if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { - if ((stomp_type == ATH_BTCOEX_STOMP_LOW) && - btcoex_hw->mci.stomp_ftp) - stomp_type = ATH_BTCOEX_STOMP_LOW_FTP; - weight = mci_wlan_weights[stomp_type]; - } - - for (i = 0; i < AR9300_NUM_WLAN_WEIGHTS; i++) { - btcoex_hw->bt_weight[i] = AR9300_BT_WGHT; - btcoex_hw->wlan_weight[i] = weight[i]; - } - } else { + if (!AR_SREV_9300_20_OR_LATER(ah)) { btcoex_hw->bt_coex_weights = SM(bt_weight, AR_BTCOEX_BT_WGHT) | SM(wlan_weight, AR_BTCOEX_WL_WGHT); + return; + } + + if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { + enum ath_stomp_type stype = + ((stomp_type == ATH_BTCOEX_STOMP_LOW) && + btcoex_hw->mci.stomp_ftp) ? + ATH_BTCOEX_STOMP_LOW_FTP : stomp_type; + weight = mci_wlan_weights[stype]; } + + for (i = 0; i < AR9300_NUM_WLAN_WEIGHTS; i++) { + btcoex_hw->bt_weight[i] = AR9300_BT_WGHT; + btcoex_hw->wlan_weight[i] = weight[i]; + if (concur_tx && i) { + btcoex_hw->wlan_weight[i] &= + ~(0xff << txprio_shift[i-1]); + btcoex_hw->wlan_weight[i] |= + (btcoex_hw->tx_prio[stomp_type] << + txprio_shift[i-1]); + } + } + /* Last WLAN weight has to be adjusted wrt tx priority */ + if (concur_tx) { + btcoex_hw->wlan_weight[i-1] &= ~(0xff << txprio_shift[i-1]); + btcoex_hw->wlan_weight[i-1] |= (btcoex_hw->tx_prio[stomp_type] + << txprio_shift[i-1]); + } + } EXPORT_SYMBOL(ath9k_hw_btcoex_set_weight); @@ -385,3 +403,13 @@ void ath9k_hw_btcoex_bt_stomp(struct ath_hw *ah, } } EXPORT_SYMBOL(ath9k_hw_btcoex_bt_stomp); + +void ath9k_hw_btcoex_set_concur_txprio(struct ath_hw *ah, u8 *stomp_txprio) +{ + struct ath_btcoex_hw *btcoex = &ah->btcoex_hw; + int i; + + for (i = 0; i < ATH_BTCOEX_STOMP_MAX; i++) + btcoex->tx_prio[i] = stomp_txprio[i]; +} +EXPORT_SYMBOL(ath9k_hw_btcoex_set_concur_txprio); diff --git a/drivers/net/wireless/ath/ath9k/btcoex.h b/drivers/net/wireless/ath/ath9k/btcoex.h index 385197ad79b..a260fcb99d1 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.h +++ b/drivers/net/wireless/ath/ath9k/btcoex.h @@ -84,6 +84,7 @@ struct ath9k_hw_mci { u8 bt_ver_minor; u8 bt_state; u8 stomp_ftp; + bool concur_tx; }; struct ath_btcoex_hw { @@ -98,6 +99,7 @@ struct ath_btcoex_hw { u32 bt_coex_mode2; /* Register setting for AR_BT_COEX_MODE2 */ u32 bt_weight[AR9300_NUM_BT_WEIGHTS]; u32 wlan_weight[AR9300_NUM_WLAN_WEIGHTS]; + u8 tx_prio[ATH_BTCOEX_STOMP_MAX]; }; void ath9k_hw_btcoex_init_scheme(struct ath_hw *ah); @@ -112,5 +114,6 @@ void ath9k_hw_btcoex_set_weight(struct ath_hw *ah, void ath9k_hw_btcoex_disable(struct ath_hw *ah); void ath9k_hw_btcoex_bt_stomp(struct ath_hw *ah, enum ath_stomp_type stomp_type); +void ath9k_hw_btcoex_set_concur_txprio(struct ath_hw *ah, u8 *stomp_txprio); #endif diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 1733a5ac327..b37c8af6e02 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -43,6 +43,7 @@ static bool ath_mci_add_profile(struct ath_common *common, struct ath_mci_profile_info *info) { struct ath_mci_profile_info *entry; + u8 voice_priority[] = { 110, 110, 110, 112, 110, 110, 114, 116, 118 }; if ((mci->num_sco == ATH_MCI_MAX_SCO_PROFILE) && (info->type == MCI_GPM_COEX_PROFILE_VOICE)) @@ -59,6 +60,12 @@ static bool ath_mci_add_profile(struct ath_common *common, memcpy(entry, info, 10); INC_PROF(mci, info); list_add_tail(&entry->list, &mci->info); + if (info->type == MCI_GPM_COEX_PROFILE_VOICE) { + if (info->voice_type < sizeof(voice_priority)) + mci->voice_priority = voice_priority[info->voice_type]; + else + mci->voice_priority = 110; + } return true; } @@ -250,6 +257,57 @@ static void ath9k_mci_work(struct work_struct *work) ath_mci_update_scheme(sc); } +static void ath_mci_update_stomp_txprio(u8 cur_txprio, u8 *stomp_prio) +{ + if (cur_txprio < stomp_prio[ATH_BTCOEX_STOMP_NONE]) + stomp_prio[ATH_BTCOEX_STOMP_NONE] = cur_txprio; + + if (cur_txprio > stomp_prio[ATH_BTCOEX_STOMP_ALL]) + stomp_prio[ATH_BTCOEX_STOMP_ALL] = cur_txprio; + + if ((cur_txprio > ATH_MCI_HI_PRIO) && + (cur_txprio < stomp_prio[ATH_BTCOEX_STOMP_LOW])) + stomp_prio[ATH_BTCOEX_STOMP_LOW] = cur_txprio; +} + +static void ath_mci_set_concur_txprio(struct ath_softc *sc) +{ + struct ath_btcoex *btcoex = &sc->btcoex; + struct ath_mci_profile *mci = &btcoex->mci; + u8 stomp_txprio[] = { 0, 0, 0, 0 }; /* all, low, none, low_ftp */ + + if (mci->num_mgmt) { + stomp_txprio[ATH_BTCOEX_STOMP_ALL] = ATH_MCI_INQUIRY_PRIO; + if (!mci->num_pan && !mci->num_other_acl) + stomp_txprio[ATH_BTCOEX_STOMP_NONE] = + ATH_MCI_INQUIRY_PRIO; + } else { + u8 prof_prio[] = { 50, 90, 94, 52 };/* RFCOMM, A2DP, HID, PAN */ + + stomp_txprio[ATH_BTCOEX_STOMP_LOW] = + stomp_txprio[ATH_BTCOEX_STOMP_NONE] = 0xff; + + if (mci->num_sco) + ath_mci_update_stomp_txprio(mci->voice_priority, + stomp_txprio); + if (mci->num_other_acl) + ath_mci_update_stomp_txprio(prof_prio[0], stomp_txprio); + if (mci->num_a2dp) + ath_mci_update_stomp_txprio(prof_prio[1], stomp_txprio); + if (mci->num_hid) + ath_mci_update_stomp_txprio(prof_prio[2], stomp_txprio); + if (mci->num_pan) + ath_mci_update_stomp_txprio(prof_prio[3], stomp_txprio); + + if (stomp_txprio[ATH_BTCOEX_STOMP_NONE] == 0xff) + stomp_txprio[ATH_BTCOEX_STOMP_NONE] = 0; + + if (stomp_txprio[ATH_BTCOEX_STOMP_LOW] == 0xff) + stomp_txprio[ATH_BTCOEX_STOMP_LOW] = 0; + } + ath9k_hw_btcoex_set_concur_txprio(sc->sc_ah, stomp_txprio); +} + static u8 ath_mci_process_profile(struct ath_softc *sc, struct ath_mci_profile_info *info) { @@ -281,6 +339,7 @@ static u8 ath_mci_process_profile(struct ath_softc *sc, } else ath_mci_del_profile(common, mci, entry); + ath_mci_set_concur_txprio(sc); return 1; } @@ -314,6 +373,7 @@ static u8 ath_mci_process_status(struct ath_softc *sc, mci->num_mgmt++; } while (++i < ATH_MCI_MAX_PROFILE); + ath_mci_set_concur_txprio(sc); if (old_num_mgmt != mci->num_mgmt) return 1; diff --git a/drivers/net/wireless/ath/ath9k/mci.h b/drivers/net/wireless/ath/ath9k/mci.h index a3df314c1e7..e85a0e9506f 100644 --- a/drivers/net/wireless/ath/ath9k/mci.h +++ b/drivers/net/wireless/ath/ath9k/mci.h @@ -32,6 +32,8 @@ #define ATH_MCI_MAX_PROFILE (ATH_MCI_MAX_ACL_PROFILE +\ ATH_MCI_MAX_SCO_PROFILE) +#define ATH_MCI_INQUIRY_PRIO 62 +#define ATH_MCI_HI_PRIO 60 #define ATH_MCI_NUM_BT_CHANNELS 79 #define MCI_GPM_SET_CHANNEL_BIT(_p_gpm, _bt_chan) \ @@ -131,6 +133,7 @@ struct ath_mci_profile { u8 num_pan; u8 num_other_acl; u8 num_bdr; + u8 voice_priority; }; struct ath_mci_buf { -- cgit v1.2.3-70-g09d2 From 77d848372875d2e4cbdbf07030f0e08cab5e7f4d Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Fri, 12 Oct 2012 14:07:24 +0530 Subject: ath9k: fill channel mode in caldata It is useful to have channel mode in caldata to find out whether operaing channel is in HT40/20 when we are currently on offchannel. It will be used by BTCOEX to enable/disable concurrent tx mechanism later. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/calib.c | 1 + drivers/net/wireless/ath/ath9k/hw.h | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index e5cceb07757..f3448a032e6 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -410,6 +410,7 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, ah->caldata->channel = chan->channel; ah->caldata->channelFlags = chan->channelFlags & ~CHANNEL_CW_INT; + ah->caldata->chanmode = chan->chanmode; h = ah->caldata->nfCalHist; default_nf = ath9k_hw_get_default_nf(ah, chan); for (i = 0; i < NUM_NF_READINGS; i++) { diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 1d4f5f1fdd8..3e73bfe2315 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -401,6 +401,7 @@ enum ath9k_int { struct ath9k_hw_cal_data { u16 channel; u32 channelFlags; + u32 chanmode; int32_t CalValid; int8_t iCoff; int8_t qCoff; -- cgit v1.2.3-70-g09d2 From e82cb03f5a645533def34923d55404526bc22fae Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Fri, 12 Oct 2012 14:07:25 +0530 Subject: ath9k: adjust WLAN and BT concurrent transmission The simulataneous transmission of both WLAN and BT might cause increase in power levels. To avoid regulatory violation, WLAN tx power will be adjusted according to BT power index based on avaliability of BT scheduling messages. WLAN tx power reduction might affect its performance. So WLAN tx power is only be lowered when the signal strength is good enough. Otherwise concurrent tx will be disabled and WLAN uses it default power levels. Also concurrent tx is disabled whenever WLAN is moving to off-channel which might be used by BT. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_eeprom.c | 18 ++++++-- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 28 +++++++++++- drivers/net/wireless/ath/ath9k/ar9003_mci.h | 5 +++ drivers/net/wireless/ath/ath9k/ath9k.h | 1 + drivers/net/wireless/ath/ath9k/btcoex.h | 3 ++ drivers/net/wireless/ath/ath9k/gpio.c | 2 + drivers/net/wireless/ath/ath9k/main.c | 4 ++ drivers/net/wireless/ath/ath9k/mci.c | 59 ++++++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/mci.h | 8 ++++ 9 files changed, 124 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index 189aeb22f55..d31399871e8 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -18,6 +18,7 @@ #include "hw.h" #include "ar9003_phy.h" #include "ar9003_eeprom.h" +#include "ar9003_mci.h" #define COMP_HDR_LEN 4 #define COMP_CKSUM_LEN 2 @@ -41,7 +42,6 @@ static int ar9003_hw_power_interpolate(int32_t x, int32_t *px, int32_t *py, u_int16_t np); - static const struct ar9300_eeprom ar9300_default = { .eepromVersion = 2, .templateVersion = 2, @@ -5037,16 +5037,28 @@ static void ar9003_hw_set_power_per_rate_table(struct ath_hw *ah, case CTL_5GHT20: case CTL_2GHT20: for (i = ALL_TARGET_HT20_0_8_16; - i <= ALL_TARGET_HT20_23; i++) + i <= ALL_TARGET_HT20_23; i++) { pPwrArray[i] = (u8)min((u16)pPwrArray[i], minCtlPower); + if (ath9k_hw_mci_is_enabled(ah)) + pPwrArray[i] = + (u8)min((u16)pPwrArray[i], + ar9003_mci_get_max_txpower(ah, + pCtlMode[ctlMode])); + } break; case CTL_5GHT40: case CTL_2GHT40: for (i = ALL_TARGET_HT40_0_8_16; - i <= ALL_TARGET_HT40_23; i++) + i <= ALL_TARGET_HT40_23; i++) { pPwrArray[i] = (u8)min((u16)pPwrArray[i], minCtlPower); + if (ath9k_hw_mci_is_enabled(ah)) + pPwrArray[i] = + (u8)min((u16)pPwrArray[i], + ar9003_mci_get_max_txpower(ah, + pCtlMode[ctlMode])); + } break; default: break; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 44c202ce6c6..9aa8704eb3e 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -818,7 +818,7 @@ int ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g, { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; - u32 regval; + u32 regval, i; ath_dbg(common, MCI, "MCI Reset (full_sleep = %d, is_2g = %d)\n", is_full_sleep, is_2g); @@ -868,6 +868,18 @@ int ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g, REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, AR_BTCOEX_CTRL2_RX_DEWEIGHT, 1); REG_RMW_FIELD(ah, AR_PCU_MISC, AR_PCU_BT_ANT_PREVENT_RX, 0); + /* concurrent tx priority */ + if (mci->config & ATH_MCI_CONFIG_CONCUR_TX) { + REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, + AR_BTCOEX_CTRL2_DESC_BASED_TXPWR_ENABLE, 0); + REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, + AR_BTCOEX_CTRL2_TXPWR_THRESH, 0x7f); + REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, + AR_BTCOEX_CTRL_REDUCE_TXPWR, 0); + for (i = 0; i < 8; i++) + REG_WRITE(ah, AR_BTCOEX_MAX_TXPWR(i), 0x7f7f7f7f); + } + regval = MS(mci->config, ATH_MCI_CONFIG_CLK_DIV); REG_RMW_FIELD(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_CLK_DIV, regval); REG_SET_BIT(ah, AR_BTCOEX_CTRL, AR_BTCOEX_CTRL_MCI_MODE_EN); @@ -1426,3 +1438,17 @@ void ar9003_mci_send_wlan_channels(struct ath_hw *ah) ar9003_mci_send_coex_wlan_channels(ah, true); } EXPORT_SYMBOL(ar9003_mci_send_wlan_channels); + +u16 ar9003_mci_get_max_txpower(struct ath_hw *ah, u8 ctlmode) +{ + if (!ah->btcoex_hw.mci.concur_tx) + goto out; + + if (ctlmode == CTL_2GHT20) + return ATH_BTCOEX_HT20_MAX_TXPOWER; + else if (ctlmode == CTL_2GHT40) + return ATH_BTCOEX_HT40_MAX_TXPOWER; + +out: + return -1; +} diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.h b/drivers/net/wireless/ath/ath9k/ar9003_mci.h index 29282348eb4..0910310ae83 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.h @@ -277,6 +277,7 @@ void ar9003_mci_get_isr(struct ath_hw *ah, enum ath9k_int *masked); void ar9003_mci_bt_gain_ctrl(struct ath_hw *ah); void ar9003_mci_set_power_awake(struct ath_hw *ah); void ar9003_mci_check_gpm_offset(struct ath_hw *ah); +u16 ar9003_mci_get_max_txpower(struct ath_hw *ah, u8 ctlmode); #else @@ -323,6 +324,10 @@ static inline void ar9003_mci_set_power_awake(struct ath_hw *ah) static inline void ar9003_mci_check_gpm_offset(struct ath_hw *ah) { } +static inline u16 ar9003_mci_get_max_txpower(struct ath_hw *ah, u8 ctlmode) +{ + return -1; +} #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ #endif diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 77c2c16b696..18dfb764eed 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -479,6 +479,7 @@ struct ath_btcoex { u32 btscan_no_stomp; /* in usec */ u32 duty_cycle; u32 bt_wait_time; + int rssi_count; struct ath_gen_timer *no_stomp_timer; /* Timer for no BT stomping */ struct ath_mci_profile mci; }; diff --git a/drivers/net/wireless/ath/ath9k/btcoex.h b/drivers/net/wireless/ath/ath9k/btcoex.h index a260fcb99d1..94e921147e4 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.h +++ b/drivers/net/wireless/ath/ath9k/btcoex.h @@ -39,6 +39,9 @@ #define ATH_BTCOEX_RX_WAIT_TIME 100 #define ATH_BTCOEX_STOMP_FTP_THRESH 5 +#define ATH_BTCOEX_HT20_MAX_TXPOWER 0x14 +#define ATH_BTCOEX_HT40_MAX_TXPOWER 0x10 + #define AR9300_NUM_BT_WEIGHTS 4 #define AR9300_NUM_WLAN_WEIGHTS 4 /* Defines the BT AR_BT_COEX_WGHT used */ diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index 9e63a03330c..64dde1cd20e 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -227,6 +227,8 @@ static void ath_btcoex_period_timer(unsigned long data) } spin_unlock_irqrestore(&sc->sc_pm_lock, flags); + ath9k_mci_update_rssi(sc); + ath9k_ps_wakeup(sc); if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI)) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 515e184ab47..578a7234aa5 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -293,6 +293,10 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan, goto out; } + if (ath9k_hw_mci_is_enabled(sc->sc_ah) && + (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) + ath9k_mci_set_txpower(sc, true, false); + if (!ath_complete_reset(sc, true)) r = -EIO; diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index b37c8af6e02..19dbac42f3d 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -710,3 +710,62 @@ send_wlan_chan: ar9003_mci_send_wlan_channels(ah); ar9003_mci_state(ah, MCI_STATE_SEND_VERSION_QUERY); } + +void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel, + bool concur_tx) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath9k_hw_mci *mci_hw = &sc->sc_ah->btcoex_hw.mci; + bool old_concur_tx = mci_hw->concur_tx; + + if (!(mci_hw->config & ATH_MCI_CONFIG_CONCUR_TX)) { + mci_hw->concur_tx = false; + return; + } + + if (!IS_CHAN_2GHZ(ah->curchan)) + return; + + if (setchannel) { + struct ath9k_hw_cal_data *caldata = &sc->caldata; + if ((caldata->chanmode == CHANNEL_G_HT40PLUS) && + (ah->curchan->channel > caldata->channel) && + (ah->curchan->channel <= caldata->channel + 20)) + return; + if ((caldata->chanmode == CHANNEL_G_HT40MINUS) && + (ah->curchan->channel < caldata->channel) && + (ah->curchan->channel >= caldata->channel - 20)) + return; + mci_hw->concur_tx = false; + } else + mci_hw->concur_tx = concur_tx; + + if (old_concur_tx != mci_hw->concur_tx) + ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false); +} + +void ath9k_mci_update_rssi(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_btcoex *btcoex = &sc->btcoex; + struct ath9k_hw_mci *mci_hw = &sc->sc_ah->btcoex_hw.mci; + + if (!(mci_hw->config & ATH_MCI_CONFIG_CONCUR_TX)) + return; + + if (ah->stats.avgbrssi >= 40) { + if (btcoex->rssi_count < 0) + btcoex->rssi_count = 0; + if (++btcoex->rssi_count >= ATH_MCI_CONCUR_TX_SWITCH) { + btcoex->rssi_count = 0; + ath9k_mci_set_txpower(sc, false, true); + } + } else { + if (btcoex->rssi_count > 0) + btcoex->rssi_count = 0; + if (--btcoex->rssi_count <= -ATH_MCI_CONCUR_TX_SWITCH) { + btcoex->rssi_count = 0; + ath9k_mci_set_txpower(sc, false, false); + } + } +} diff --git a/drivers/net/wireless/ath/ath9k/mci.h b/drivers/net/wireless/ath/ath9k/mci.h index e85a0e9506f..e5f170a4d66 100644 --- a/drivers/net/wireless/ath/ath9k/mci.h +++ b/drivers/net/wireless/ath/ath9k/mci.h @@ -35,6 +35,7 @@ #define ATH_MCI_INQUIRY_PRIO 62 #define ATH_MCI_HI_PRIO 60 #define ATH_MCI_NUM_BT_CHANNELS 79 +#define ATH_MCI_CONCUR_TX_SWITCH 5 #define MCI_GPM_SET_CHANNEL_BIT(_p_gpm, _bt_chan) \ do { \ @@ -151,10 +152,13 @@ void ath_mci_flush_profile(struct ath_mci_profile *mci); int ath_mci_setup(struct ath_softc *sc); void ath_mci_cleanup(struct ath_softc *sc); void ath_mci_intr(struct ath_softc *sc); +void ath9k_mci_update_rssi(struct ath_softc *sc); #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT void ath_mci_enable(struct ath_softc *sc); void ath9k_mci_update_wlan_channels(struct ath_softc *sc, bool allow_all); +void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel, + bool concur_tx); #else static inline void ath_mci_enable(struct ath_softc *sc) { @@ -163,6 +167,10 @@ static inline void ath9k_mci_update_wlan_channels(struct ath_softc *sc, bool allow_all) { } +static inline void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel, + bool concur_tx) +{ +} #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ #endif /* MCI_H*/ -- cgit v1.2.3-70-g09d2 From 4c6231a408c1fbec714f82b4742d19f85130ba97 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 15 Oct 2012 15:29:45 +0530 Subject: ath9k_hw: Enable OSLA hw fix for AR9565 Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 3 +++ drivers/net/wireless/ath/ath9k/reg.h | 4 ++++ 2 files changed, 7 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 9aa8704eb3e..9fa6d22179b 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -799,6 +799,9 @@ static void ar9003_mci_osla_setup(struct ath_hw *ah, bool enable) REG_RMW_FIELD(ah, AR_MCI_SCHD_TABLE_2, AR_MCI_SCHD_TABLE_2_MEM_BASED, 1); + if (AR_SREV_9565(ah)) + REG_RMW_FIELD(ah, AR_MCI_MISC, AR_MCI_MISC_HW_FIX_EN, 1); + if (!(mci->config & ATH_MCI_CONFIG_DISABLE_AGGR_THRESH)) { thresh = MS(mci->config, ATH_MCI_CONFIG_AGGR_THRESH); REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index 4e6760f8596..c7a9ea750a4 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -2360,4 +2360,8 @@ enum { #define AR_GLB_SWREG_DISCONT_MODE 0x2002c #define AR_GLB_SWREG_DISCONT_EN_BT_WLAN 0x3 +#define AR_MCI_MISC 0x1a74 +#define AR_MCI_MISC_HW_FIX_EN 0x00000001 +#define AR_MCI_MISC_HW_FIX_EN_S 0 + #endif -- cgit v1.2.3-70-g09d2 From 7d47884f306afd1d0215133685f451aaafe3ca7a Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 15 Oct 2012 15:29:46 +0530 Subject: ath9k_hw: Fix selfgen chainmask for 9565 Self generated MCI messages is configured to use chain 1. As ar9565 is 1x1 solution, It can not use Chain 1. Hence fix Chain 1 for ar9462 alone. Not doing so, could affect WLAN connectivity in ar9565 as LNA sharing is not informed by BT. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 9fa6d22179b..6fbd376098d 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -1043,7 +1043,9 @@ void ar9003_mci_2g5g_switch(struct ath_hw *ah, bool force) if (!(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA)) ar9003_mci_osla_setup(ah, true); - REG_WRITE(ah, AR_SELFGEN_MASK, 0x02); + + if (AR_SREV_9462(ah)) + REG_WRITE(ah, AR_SELFGEN_MASK, 0x02); } else { ar9003_mci_send_lna_take(ah, true); udelay(5); -- cgit v1.2.3-70-g09d2 From d9575dad59de382dd1f1ddcaa6de38d9844691fe Mon Sep 17 00:00:00 2001 From: Bala Shanmugam Date: Mon, 15 Oct 2012 15:29:47 +0530 Subject: ath9k: Set appropriate bit for AR9565 in btc control register Signed-off-by: Bala Shanmugam Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 6fbd376098d..b2b994147ae 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -850,11 +850,18 @@ int ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g, SM(1, AR_BTCOEX_CTRL_WBTIMER_EN) | SM(1, AR_BTCOEX_CTRL_PA_SHARED) | SM(1, AR_BTCOEX_CTRL_LNA_SHARED) | - SM(2, AR_BTCOEX_CTRL_NUM_ANTENNAS) | - SM(3, AR_BTCOEX_CTRL_RX_CHAIN_MASK) | SM(0, AR_BTCOEX_CTRL_1_CHAIN_ACK) | SM(0, AR_BTCOEX_CTRL_1_CHAIN_BCN) | SM(0, AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN); + if (AR_SREV_9565(ah)) { + regval |= SM(1, AR_BTCOEX_CTRL_NUM_ANTENNAS) | + SM(1, AR_BTCOEX_CTRL_RX_CHAIN_MASK); + REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, + AR_BTCOEX_CTRL2_TX_CHAIN_MASK, 0x1); + } else { + regval |= SM(2, AR_BTCOEX_CTRL_NUM_ANTENNAS) | + SM(3, AR_BTCOEX_CTRL_RX_CHAIN_MASK); + } REG_WRITE(ah, AR_BTCOEX_CTRL, regval); -- cgit v1.2.3-70-g09d2 From b55f6bb7c3f890c3d537516efa8746a4784c058d Mon Sep 17 00:00:00 2001 From: Bala Shanmugam Date: Mon, 15 Oct 2012 15:29:48 +0530 Subject: ath9k: turn off RXIQ calibration while re-calibrating radio TXIQ and RXIQ share the same data path to upload the measurement result, we should turn off RXIQ calibration while re-calibrating radio Signed-off-by: Bala Shanmugam Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 6 ++++++ drivers/net/wireless/ath/ath9k/ar9003_phy.h | 1 + 2 files changed, 7 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index b2b994147ae..c46d8f18d81 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -750,6 +750,9 @@ int ar9003_mci_end_reset(struct ath_hw *ah, struct ath9k_channel *chan, mci_hw->bt_state = MCI_BT_AWAKE; + REG_CLR_BIT(ah, AR_PHY_TIMING4, + 1 << AR_PHY_TIMING_CONTROL4_DO_GAIN_DC_IQ_CAL_SHIFT); + if (caldata) { caldata->done_txiqcal_once = false; caldata->done_txclcal_once = false; @@ -759,6 +762,9 @@ int ar9003_mci_end_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (!ath9k_hw_init_cal(ah, chan)) return -EIO; + REG_SET_BIT(ah, AR_PHY_TIMING4, + 1 << AR_PHY_TIMING_CONTROL4_DO_GAIN_DC_IQ_CAL_SHIFT); + exit: ar9003_mci_enable_interrupt(ah); return 0; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h index 9a48e3d2f23..8f585233a78 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h @@ -32,6 +32,7 @@ #define AR_PHY_SPUR_REG (AR_CHAN_BASE + 0x1c) #define AR_PHY_RX_IQCAL_CORR_B0 (AR_CHAN_BASE + 0xdc) #define AR_PHY_TX_IQCAL_CONTROL_3 (AR_CHAN_BASE + 0xb0) +#define AR_PHY_TIMING_CONTROL4_DO_GAIN_DC_IQ_CAL_SHIFT 16 #define AR_PHY_TIMING11_SPUR_FREQ_SD 0x3FF00000 #define AR_PHY_TIMING11_SPUR_FREQ_SD_S 20 -- cgit v1.2.3-70-g09d2 From e9f9fd8cdc5fcb718e2ce778cb5e0eea27e2fdc8 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 15 Oct 2012 15:29:49 +0530 Subject: ath9k_hw: Disable MCI stat counter by default for AR9565 Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 3 +++ drivers/net/wireless/ath/ath9k/reg.h | 3 +++ 2 files changed, 6 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index c46d8f18d81..87d9c348e59 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -938,6 +938,9 @@ int ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g, mci->ready = true; ar9003_mci_prep_interface(ah); + if (AR_SREV_9565(ah)) + REG_RMW_FIELD(ah, AR_MCI_DBG_CNT_CTRL, + AR_MCI_DBG_CNT_CTRL_ENABLE, 0); if (en_int) ar9003_mci_enable_interrupt(ah); diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index c7a9ea750a4..8f40dba9142 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -2363,5 +2363,8 @@ enum { #define AR_MCI_MISC 0x1a74 #define AR_MCI_MISC_HW_FIX_EN 0x00000001 #define AR_MCI_MISC_HW_FIX_EN_S 0 +#define AR_MCI_DBG_CNT_CTRL 0x1a78 +#define AR_MCI_DBG_CNT_CTRL_ENABLE 0x00000001 +#define AR_MCI_DBG_CNT_CTRL_ENABLE_S 0 #endif -- cgit v1.2.3-70-g09d2 From 2097fdd7ebdb1674aaf7343b7a1d6cc2758c1dff Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 15 Oct 2012 15:29:50 +0530 Subject: ath9k_hw: Fix frequent BT rx recovery While resuming from S3, BT host issues HCI reset command and it causes BT firmware to busy with security key calculation. At this movement, WLAN detects MCI hardware error of MCI_CONT_INFO_TIMEOUT and then it starts the recovery sequence repeatedly. Too many recovery sequences would exhaust the BT kernel message pool. This patch imposes a duration between consecutive BT recovery procedure. Thus it solves BT firmware panic issue reported in AR9565. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 10 +++++++++- drivers/net/wireless/ath/ath9k/ar9003_mci.h | 1 + drivers/net/wireless/ath/ath9k/btcoex.h | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 87d9c348e59..b04fa462282 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -1203,7 +1203,7 @@ EXPORT_SYMBOL(ar9003_mci_cleanup); u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; - u32 value = 0; + u32 value = 0, tsf; u8 query_type; switch (state_type) { @@ -1261,6 +1261,14 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type) ar9003_mci_send_coex_bt_status_query(ah, true, query_type); break; case MCI_STATE_RECOVER_RX: + tsf = ath9k_hw_gettsf32(ah); + if ((tsf - mci->last_recovery) <= MCI_RECOVERY_DUR_TSF) { + ath_dbg(ath9k_hw_common(ah), MCI, + "(MCI) ignore Rx recovery\n"); + break; + } + ath_dbg(ath9k_hw_common(ah), MCI, "(MCI) RECOVER RX\n"); + mci->last_recovery = tsf; ar9003_mci_prep_interface(ah); mci->query_bt = true; mci->need_flush_btinfo = true; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.h b/drivers/net/wireless/ath/ath9k/ar9003_mci.h index 0910310ae83..3e51f54b012 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.h @@ -18,6 +18,7 @@ #define AR9003_MCI_H #define MCI_FLAG_DISABLE_TIMESTAMP 0x00000001 /* Disable time stamp */ +#define MCI_RECOVERY_DUR_TSF (100 * 1000) /* 100 ms */ /* Default remote BT device MCI COEX version */ #define MCI_GPM_COEX_MAJOR_VERSION_DEFAULT 3 diff --git a/drivers/net/wireless/ath/ath9k/btcoex.h b/drivers/net/wireless/ath/ath9k/btcoex.h index 94e921147e4..2f84ab273d0 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.h +++ b/drivers/net/wireless/ath/ath9k/btcoex.h @@ -88,6 +88,7 @@ struct ath9k_hw_mci { u8 bt_state; u8 stomp_ftp; bool concur_tx; + u32 last_recovery; }; struct ath_btcoex_hw { -- cgit v1.2.3-70-g09d2 From 6f37ff96d3bd2a53e68131a7c10ced933815b390 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 15 Oct 2012 15:29:51 +0530 Subject: ath9k_hw: Fix max rx rate drop for AR9565 Whenever i_coff of IQ calibration is too high, AR9565 drops max rx rate to MCS4. Skipping IQ update at this time can avoid this problem for AR9565. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_calib.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 84b558d126c..162401f22f8 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -276,6 +276,11 @@ static void ar9003_hw_iqcalibrate(struct ath_hw *ah, u8 numChains) offset_array[i], REG_READ(ah, offset_array[i])); + if (AR_SREV_9565(ah) && + (iCoff == 63 || qCoff == 63 || + iCoff == -63 || qCoff == -63)) + return; + REG_RMW_FIELD(ah, offset_array[i], AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF, iCoff); -- cgit v1.2.3-70-g09d2 From 506ed95c27b9e4db521df8433860da78b4747cd8 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 15 Oct 2012 15:29:52 +0530 Subject: ath9k_hw: Configure new switch table for AR9565 BTCOEX Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_eeprom.c | 2 +- drivers/net/wireless/ath/ath9k/reg.h | 4 ---- drivers/net/wireless/ath/ath9k/wow.c | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index d31399871e8..c86cb640004 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -3601,7 +3601,7 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz) * 7:4 R/W SWITCH_TABLE_COM_SPDT_WLAN_IDLE * SWITCH_TABLE_COM_SPDT_WLAN_IDLE */ - if (AR_SREV_9462_20_OR_LATER(ah)) { + if (AR_SREV_9462_20(ah) || AR_SREV_9565(ah)) { value = ar9003_switch_com_spdt_get(ah, is2ghz); REG_RMW_FIELD(ah, AR_PHY_GLB_CONTROL, AR_SWITCH_TABLE_COM_SPDT_ALL, value); diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index 8f40dba9142..e9dec138b91 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -907,10 +907,6 @@ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9462_20)) -#define AR_SREV_9462_20_OR_LATER(_ah) \ - (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \ - ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9462_20)) - #define AR_SREV_9565(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565)) diff --git a/drivers/net/wireless/ath/ath9k/wow.c b/drivers/net/wireless/ath/ath9k/wow.c index a483d518758..9f8563091be 100644 --- a/drivers/net/wireless/ath/ath9k/wow.c +++ b/drivers/net/wireless/ath/ath9k/wow.c @@ -118,7 +118,7 @@ static void ath9k_wow_create_keep_alive_pattern(struct ath_hw *ah) (ap_mac_addr[1] << 8) | (ap_mac_addr[0]); data_word[5] = (ap_mac_addr[5] << 8) | (ap_mac_addr[4]); - if (AR_SREV_9462_20_OR_LATER(ah)) { + if (AR_SREV_9462_20(ah)) { /* AR9462 2.0 has an extra descriptor word (time based * discard) compared to other chips */ REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + (12 * 4)), 0); -- cgit v1.2.3-70-g09d2 From 3c5c9d04f628135ff57eda5068c9d9513f0a94bf Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 15 Oct 2012 15:29:53 +0530 Subject: ath9k_hw: Set default MCI config for AR9565 Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/btcoex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/btcoex.c b/drivers/net/wireless/ath/ath9k/btcoex.c index 05d9be5be52..c90e9bc4b02 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.c +++ b/drivers/net/wireless/ath/ath9k/btcoex.c @@ -195,7 +195,7 @@ void ath9k_hw_btcoex_init_mci(struct ath_hw *ah) ah->btcoex_hw.mci.need_flush_btinfo = false; ah->btcoex_hw.mci.wlan_cal_seq = 0; ah->btcoex_hw.mci.wlan_cal_done = 0; - ah->btcoex_hw.mci.config = 0x2201; + ah->btcoex_hw.mci.config = (AR_SREV_9462(ah)) ? 0x2201 : 0xa4c1; } EXPORT_SYMBOL(ath9k_hw_btcoex_init_mci); -- cgit v1.2.3-70-g09d2 From f9401b1e74f33d40d97364be5244b715e8cee6ed Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 15 Oct 2012 15:29:54 +0530 Subject: ath9k: adjust duty cycle for FTP profile for AR9565 Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/mci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 19dbac42f3d..0dd2cbb52d6 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -157,7 +157,7 @@ static void ath_mci_update_scheme(struct ath_softc *sc) * For single PAN/FTP profile, allocate 35% for BT * to improve WLAN throughput. */ - btcoex->duty_cycle = 35; + btcoex->duty_cycle = AR_SREV_9565(sc->sc_ah) ? 40 : 35; btcoex->btcoex_period = 53; ath_dbg(common, MCI, "Single PAN/FTP bt period %d ms dutycycle %d\n", -- cgit v1.2.3-70-g09d2 From 7bf7a71e0f7ad69358d01f2a7ba7faa9428db90b Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 15 Oct 2012 15:29:55 +0530 Subject: ath9k: Add new BT profile info A2DP_Voice When the BT connection is initiated by headset, it's possible that headset requests to make one A2DP and one Voice connection over the same link. BT firmware will send a new profile A2DP_Voice in this case. So WLAN has to take care of this new profile for tuning BTCOEX parameters. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.h | 1 + drivers/net/wireless/ath/ath9k/mci.h | 2 ++ 2 files changed, 3 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.h b/drivers/net/wireless/ath/ath9k/ar9003_mci.h index 3e51f54b012..66d7ab9f920 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.h @@ -126,6 +126,7 @@ enum ath_mci_gpm_coex_profile_type { MCI_GPM_COEX_PROFILE_HID, MCI_GPM_COEX_PROFILE_BNEP, MCI_GPM_COEX_PROFILE_VOICE, + MCI_GPM_COEX_PROFILE_A2DPVO, MCI_GPM_COEX_PROFILE_MAX }; diff --git a/drivers/net/wireless/ath/ath9k/mci.h b/drivers/net/wireless/ath/ath9k/mci.h index e5f170a4d66..06958837620 100644 --- a/drivers/net/wireless/ath/ath9k/mci.h +++ b/drivers/net/wireless/ath/ath9k/mci.h @@ -70,6 +70,7 @@ _mci->num_pan++; \ break; \ case MCI_GPM_COEX_PROFILE_VOICE: \ + case MCI_GPM_COEX_PROFILE_A2DPVO:\ _mci->num_sco++; \ break; \ default: \ @@ -94,6 +95,7 @@ _mci->num_pan--; \ break; \ case MCI_GPM_COEX_PROFILE_VOICE: \ + case MCI_GPM_COEX_PROFILE_A2DPVO:\ _mci->num_sco--; \ break; \ default: \ -- cgit v1.2.3-70-g09d2 From 6c4a5f2413da948e7d1d189c9d94f05afde8702a Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Mon, 22 Oct 2012 15:01:29 +0200 Subject: carl9170: split up carl9170_handle_mpdu carl9170_handle_mpdu is the final part of the rx path of the driver. It splits the raw data streams from the device into skb packets and passes them on to mac80211. As a result of continuous updates, it grew over the years when new code was added by the following commits: - report A-MPDU status - fix HT peer BA session corruption - A-MPDU frame type filter - ... This patch splits the routine into two stages. The first stage only deals with the details about extracting and verifying the data from the incoming stream. Whereas the second stage packs it into skbs and passes it on to mac80211. Reported-by: Javier Lopez Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/rx.c | 45 ++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 13 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index 9cd93f1d8be..6d22382875b 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -660,6 +660,35 @@ static bool carl9170_ampdu_check(struct ar9170 *ar, u8 *buf, u8 ms, return false; } +static int carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len, + struct ieee80211_rx_status *status) +{ + struct sk_buff *skb; + + /* (driver) frame trap handler + * + * Because power-saving mode handing has to be implemented by + * the driver/firmware. We have to check each incoming beacon + * from the associated AP, if there's new data for us (either + * broadcast/multicast or unicast) we have to react quickly. + * + * So, if you have you want to add additional frame trap + * handlers, this would be the perfect place! + */ + + carl9170_ps_beacon(ar, buf, len); + + carl9170_ba_check(ar, buf, len); + + skb = carl9170_rx_copy_data(buf, len); + if (!skb) + return -ENOMEM; + + memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); + ieee80211_rx(ar->hw, skb); + return 0; +} + /* * If the frame alignment is right (or the kernel has * CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS), and there @@ -669,14 +698,12 @@ static bool carl9170_ampdu_check(struct ar9170 *ar, u8 *buf, u8 ms, * mode, and we need to observe the proper ordering, * this is non-trivial. */ - -static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len) +static void carl9170_rx_untie_data(struct ar9170 *ar, u8 *buf, int len) { struct ar9170_rx_head *head; struct ar9170_rx_macstatus *mac; struct ar9170_rx_phystatus *phy = NULL; struct ieee80211_rx_status status; - struct sk_buff *skb; int mpdu_len; u8 mac_status; @@ -788,18 +815,10 @@ static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len) if (phy) carl9170_rx_phy_status(ar, phy, &status); - carl9170_ps_beacon(ar, buf, mpdu_len); - - carl9170_ba_check(ar, buf, mpdu_len); - - skb = carl9170_rx_copy_data(buf, mpdu_len); - if (!skb) + if (carl9170_handle_mpdu(ar, buf, mpdu_len, &status)) goto drop; - memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); - ieee80211_rx(ar->hw, skb); return; - drop: ar->rx_dropped++; } @@ -851,7 +870,7 @@ static void __carl9170_rx(struct ar9170 *ar, u8 *buf, unsigned int len) if (i == 12) carl9170_rx_untie_cmds(ar, buf, len); else - carl9170_handle_mpdu(ar, buf, len); + carl9170_rx_untie_data(ar, buf, len); } static void carl9170_rx_stream(struct ar9170 *ar, void *buf, unsigned int len) -- cgit v1.2.3-70-g09d2 From b7d572e1871df06a96a1c9591c71c5494ff6b624 Mon Sep 17 00:00:00 2001 From: Pontus Fuchs Date: Tue, 23 Oct 2012 20:33:57 +0200 Subject: ar5523: Add new driver This driver is for the AR5523 chipset from Atheros. It was created in 2007 by Christoph Hellwig but it was never finished. I found it a couple of months ago and after some polishing it's working pretty fine. The driver was written with the FreeBSD driver (uath) as reference, which was written with the reverse-engineered windows driver as reference, hence the feature set is very limited. Station mode only, no HW crypto offload. Signed-off-by: Pontus Fuchs Signed-off-by: John W. Linville --- MAINTAINERS | 6 + drivers/net/wireless/ath/Kconfig | 1 + drivers/net/wireless/ath/Makefile | 1 + drivers/net/wireless/ath/ar5523/Kconfig | 7 + drivers/net/wireless/ath/ar5523/Makefile | 1 + drivers/net/wireless/ath/ar5523/ar5523.c | 1806 +++++++++++++++++++++++++++ drivers/net/wireless/ath/ar5523/ar5523.h | 152 +++ drivers/net/wireless/ath/ar5523/ar5523_hw.h | 431 +++++++ 8 files changed, 2405 insertions(+) create mode 100644 drivers/net/wireless/ath/ar5523/Kconfig create mode 100644 drivers/net/wireless/ath/ar5523/Makefile create mode 100644 drivers/net/wireless/ath/ar5523/ar5523.c create mode 100644 drivers/net/wireless/ath/ar5523/ar5523.h create mode 100644 drivers/net/wireless/ath/ar5523/ar5523_hw.h (limited to 'drivers/net/wireless/ath') diff --git a/MAINTAINERS b/MAINTAINERS index e73060fe078..568ea937309 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7477,6 +7477,12 @@ S: Maintained F: Documentation/usb/acm.txt F: drivers/usb/class/cdc-acm.* +USB AR5523 WIRELESS DRIVER +M: Pontus Fuchs +L: linux-wireless@vger.kernel.org +S: Maintained +F: drivers/net/wireless/ath/ar5523/ + USB ATTACHED SCSI M: Matthew Wilcox M: Sarah Sharp diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index 09602241901..c25dcf192fe 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -26,5 +26,6 @@ source "drivers/net/wireless/ath/ath5k/Kconfig" source "drivers/net/wireless/ath/ath9k/Kconfig" source "drivers/net/wireless/ath/carl9170/Kconfig" source "drivers/net/wireless/ath/ath6kl/Kconfig" +source "drivers/net/wireless/ath/ar5523/Kconfig" endif diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile index d716b748e57..1e18621326d 100644 --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_ATH5K) += ath5k/ obj-$(CONFIG_ATH9K_HW) += ath9k/ obj-$(CONFIG_CARL9170) += carl9170/ obj-$(CONFIG_ATH6KL) += ath6kl/ +obj-$(CONFIG_AR5523) += ar5523/ obj-$(CONFIG_ATH_COMMON) += ath.o diff --git a/drivers/net/wireless/ath/ar5523/Kconfig b/drivers/net/wireless/ath/ar5523/Kconfig new file mode 100644 index 00000000000..11d99ee8de5 --- /dev/null +++ b/drivers/net/wireless/ath/ar5523/Kconfig @@ -0,0 +1,7 @@ +config AR5523 + tristate "Atheros AR5523 wireless driver support" + depends on MAC80211 && USB + select FW_LOADER + ---help--- + This module add support for AR5523 based USB dongles such as D-Link + DWL-G132, Netgear WPN111 and many more. diff --git a/drivers/net/wireless/ath/ar5523/Makefile b/drivers/net/wireless/ath/ar5523/Makefile new file mode 100644 index 00000000000..ebf7f3bf0a3 --- /dev/null +++ b/drivers/net/wireless/ath/ar5523/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_AR5523) := ar5523.o diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c new file mode 100644 index 00000000000..f782b6e502b --- /dev/null +++ b/drivers/net/wireless/ath/ar5523/ar5523.c @@ -0,0 +1,1806 @@ +/* + * Copyright (c) 2006 Damien Bergamini + * Copyright (c) 2006 Sam Leffler, Errno Consulting + * Copyright (c) 2007 Christoph Hellwig + * Copyright (c) 2008-2009 Weongyo Jeong + * Copyright (c) 2012 Pontus Fuchs + * + * 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. + */ + +/* + * This driver is based on the uath driver written by Damien Bergamini for + * OpenBSD, who did black-box analysis of the Windows binary driver to find + * out how the hardware works. It contains a lot magic numbers because of + * that and only has minimal functionality. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ar5523.h" +#include "ar5523_hw.h" + +/* + * Various supported device vendors/products. + * UB51: AR5005UG 802.11b/g, UB52: AR5005UX 802.11a/b/g + */ + +static int ar5523_submit_rx_cmd(struct ar5523 *ar); +static void ar5523_data_tx_pkt_put(struct ar5523 *ar); + +static void ar5523_read_reply(struct ar5523 *ar, struct ar5523_cmd_hdr *hdr, + struct ar5523_tx_cmd *cmd) +{ + int dlen, olen; + u32 *rp; + + dlen = hdr->len - sizeof(*hdr); + + if (dlen < 0) { + WARN_ON(1); + goto out; + } + + ar5523_dbg(ar, "Code = %d len = %d\n", hdr->code & 0xff, dlen); + + rp = (u32 *)(hdr + 1); + if (dlen >= sizeof(u32)) { + olen = be32_to_cpu(rp[0]); + dlen -= sizeof(u32); + if (olen == 0) { + /* convention is 0 =>'s one word */ + olen = sizeof(u32); + } + } else + olen = 0; + + if (cmd->odata) { + if (cmd->olen < olen) { + ar5523_err(ar, "olen to small %d < %d\n", + cmd->olen, olen); + cmd->olen = 0; + cmd->res = -EOVERFLOW; + } else { + cmd->olen = olen; + memcpy(cmd->odata, &rp[1], olen); + cmd->res = 0; + } + } + +out: + complete(&cmd->done); +} + +static void ar5523_cmd_rx_cb(struct urb *urb) +{ + struct ar5523 *ar = urb->context; + struct ar5523_tx_cmd *cmd = &ar->tx_cmd; + struct ar5523_cmd_hdr *hdr = ar->rx_cmd_buf; + int dlen; + + if (urb->status) { + if (urb->status != -ESHUTDOWN) + ar5523_err(ar, "RX USB error %d.\n", urb->status); + goto skip; + } + + if (urb->actual_length < sizeof(struct ar5523_cmd_hdr)) { + ar5523_err(ar, "RX USB to short.\n"); + goto skip; + } + + ar5523_dbg(ar, "%s code %02x priv %d\n", __func__, + be32_to_cpu(hdr->code) & 0xff, hdr->priv); + + hdr->code = be32_to_cpu(hdr->code); + hdr->len = be32_to_cpu(hdr->len); + + switch (hdr->code & 0xff) { + default: + /* reply to a read command */ + if (hdr->priv != AR5523_CMD_ID) { + ar5523_err(ar, "Unexpected command id: %02x\n", + hdr->code & 0xff); + goto skip; + } + ar5523_read_reply(ar, hdr, cmd); + break; + + case WDCMSG_DEVICE_AVAIL: + ar5523_dbg(ar, "WDCMSG_DEVICE_AVAIL\n"); + cmd->res = 0; + cmd->olen = 0; + complete(&cmd->done); + break; + + case WDCMSG_SEND_COMPLETE: + ar5523_dbg(ar, "WDCMSG_SEND_COMPLETE: %d pending\n", + atomic_read(&ar->tx_nr_pending)); + if (!test_bit(AR5523_HW_UP, &ar->flags)) + ar5523_dbg(ar, "Unexpected WDCMSG_SEND_COMPLETE\n"); + else { + mod_timer(&ar->tx_wd_timer, + jiffies + AR5523_TX_WD_TIMEOUT); + ar5523_data_tx_pkt_put(ar); + + } + break; + + case WDCMSG_TARGET_START: + /* This command returns a bogus id so it needs special + handling */ + dlen = hdr->len - sizeof(*hdr); + if (dlen != (int)sizeof(u32)) { + ar5523_err(ar, "Invalid reply to WDCMSG_TARGET_START"); + return; + } + memcpy(cmd->odata, hdr + 1, sizeof(u32)); + cmd->olen = sizeof(u32); + cmd->res = 0; + complete(&cmd->done); + break; + + case WDCMSG_STATS_UPDATE: + ar5523_dbg(ar, "WDCMSG_STATS_UPDATE\n"); + break; + } + +skip: + ar5523_submit_rx_cmd(ar); +} + +static int ar5523_alloc_rx_cmd(struct ar5523 *ar) +{ + ar->rx_cmd_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!ar->rx_cmd_urb) + return -ENOMEM; + + ar->rx_cmd_buf = usb_alloc_coherent(ar->dev, AR5523_MAX_RXCMDSZ, + GFP_KERNEL, + &ar->rx_cmd_urb->transfer_dma); + if (!ar->rx_cmd_buf) { + usb_free_urb(ar->rx_cmd_urb); + return -ENOMEM; + } + return 0; +} + +static void ar5523_cancel_rx_cmd(struct ar5523 *ar) +{ + usb_kill_urb(ar->rx_cmd_urb); +} + +static void ar5523_free_rx_cmd(struct ar5523 *ar) +{ + usb_free_coherent(ar->dev, AR5523_MAX_RXCMDSZ, + ar->rx_cmd_buf, ar->rx_cmd_urb->transfer_dma); + usb_free_urb(ar->rx_cmd_urb); +} + +static int ar5523_submit_rx_cmd(struct ar5523 *ar) +{ + int error; + + usb_fill_bulk_urb(ar->rx_cmd_urb, ar->dev, + ar5523_cmd_rx_pipe(ar->dev), ar->rx_cmd_buf, + AR5523_MAX_RXCMDSZ, ar5523_cmd_rx_cb, ar); + ar->rx_cmd_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + error = usb_submit_urb(ar->rx_cmd_urb, GFP_ATOMIC); + if (error) { + if (error != -ENODEV) + ar5523_err(ar, "error %d when submitting rx urb\n", + error); + return error; + } + return 0; +} + +/* + * Command submitted cb + */ +static void ar5523_cmd_tx_cb(struct urb *urb) +{ + struct ar5523_tx_cmd *cmd = urb->context; + struct ar5523 *ar = cmd->ar; + + if (urb->status) { + ar5523_err(ar, "Failed to TX command. Status = %d\n", + urb->status); + cmd->res = urb->status; + complete(&cmd->done); + return; + } + + if (!(cmd->flags & AR5523_CMD_FLAG_READ)) { + cmd->res = 0; + complete(&cmd->done); + } +} + +static int ar5523_cmd(struct ar5523 *ar, u32 code, const void *idata, + int ilen, void *odata, int olen, int flags) +{ + struct ar5523_cmd_hdr *hdr; + struct ar5523_tx_cmd *cmd = &ar->tx_cmd; + int xferlen, error; + + /* always bulk-out a multiple of 4 bytes */ + xferlen = (sizeof(struct ar5523_cmd_hdr) + ilen + 3) & ~3; + + hdr = (struct ar5523_cmd_hdr *)cmd->buf_tx; + memset(hdr, 0, sizeof(struct ar5523_cmd_hdr)); + hdr->len = cpu_to_be32(xferlen); + hdr->code = cpu_to_be32(code); + hdr->priv = AR5523_CMD_ID; + + if (flags & AR5523_CMD_FLAG_MAGIC) + hdr->magic = cpu_to_be32(1 << 24); + memcpy(hdr + 1, idata, ilen); + + cmd->odata = odata; + cmd->olen = olen; + cmd->flags = flags; + + ar5523_dbg(ar, "do cmd %02x\n", code); + + usb_fill_bulk_urb(cmd->urb_tx, ar->dev, ar5523_cmd_tx_pipe(ar->dev), + cmd->buf_tx, xferlen, ar5523_cmd_tx_cb, cmd); + cmd->urb_tx->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + error = usb_submit_urb(cmd->urb_tx, GFP_KERNEL); + if (error) { + ar5523_err(ar, "could not send command 0x%x, error=%d\n", + code, error); + return error; + } + + if (!wait_for_completion_timeout(&cmd->done, 2 * HZ)) { + cmd->odata = NULL; + ar5523_err(ar, "timeout waiting for command %02x reply\n", + code); + cmd->res = -ETIMEDOUT; + } + return cmd->res; +} + +static int ar5523_cmd_write(struct ar5523 *ar, u32 code, const void *data, + int len, int flags) +{ + flags &= ~AR5523_CMD_FLAG_READ; + return ar5523_cmd(ar, code, data, len, NULL, 0, flags); +} + +static int ar5523_cmd_read(struct ar5523 *ar, u32 code, const void *idata, + int ilen, void *odata, int olen, int flags) +{ + flags |= AR5523_CMD_FLAG_READ; + return ar5523_cmd(ar, code, idata, ilen, odata, olen, flags); +} + +static int ar5523_config(struct ar5523 *ar, u32 reg, u32 val) +{ + struct ar5523_write_mac write; + int error; + + write.reg = cpu_to_be32(reg); + write.len = cpu_to_be32(0); /* 0 = single write */ + *(u32 *)write.data = cpu_to_be32(val); + + error = ar5523_cmd_write(ar, WDCMSG_TARGET_SET_CONFIG, &write, + 3 * sizeof(u32), 0); + if (error != 0) + ar5523_err(ar, "could not write register 0x%02x\n", reg); + return error; +} + +static int ar5523_config_multi(struct ar5523 *ar, u32 reg, const void *data, + int len) +{ + struct ar5523_write_mac write; + int error; + + write.reg = cpu_to_be32(reg); + write.len = cpu_to_be32(len); + memcpy(write.data, data, len); + + /* properly handle the case where len is zero (reset) */ + error = ar5523_cmd_write(ar, WDCMSG_TARGET_SET_CONFIG, &write, + (len == 0) ? sizeof(u32) : 2 * sizeof(u32) + len, 0); + if (error != 0) + ar5523_err(ar, "could not write %d bytes to register 0x%02x\n", + len, reg); + return error; +} + +static int ar5523_get_status(struct ar5523 *ar, u32 which, void *odata, + int olen) +{ + int error; + + which = cpu_to_be32(which); + error = ar5523_cmd_read(ar, WDCMSG_TARGET_GET_STATUS, + &which, sizeof(which), odata, olen, AR5523_CMD_FLAG_MAGIC); + if (error != 0) + ar5523_err(ar, "could not read EEPROM offset 0x%02x\n", + be32_to_cpu(which)); + return error; +} + +static int ar5523_get_capability(struct ar5523 *ar, u32 cap, u32 *val) +{ + int error; + + cap = cpu_to_be32(cap); + error = ar5523_cmd_read(ar, WDCMSG_TARGET_GET_CAPABILITY, + &cap, sizeof(cap), val, sizeof(u32), AR5523_CMD_FLAG_MAGIC); + if (error != 0) { + ar5523_err(ar, "could not read capability %u\n", + be32_to_cpu(cap)); + return error; + } + *val = be32_to_cpu(*val); + return error; +} + +static int ar5523_get_devcap(struct ar5523 *ar) +{ +#define GETCAP(x) do { \ + error = ar5523_get_capability(ar, x, &cap); \ + if (error != 0) \ + return error; \ + ar5523_info(ar, "Cap: " \ + "%s=0x%08x\n", #x, cap); \ +} while (0) + int error; + u32 cap; + + /* collect device capabilities */ + GETCAP(CAP_TARGET_VERSION); + GETCAP(CAP_TARGET_REVISION); + GETCAP(CAP_MAC_VERSION); + GETCAP(CAP_MAC_REVISION); + GETCAP(CAP_PHY_REVISION); + GETCAP(CAP_ANALOG_5GHz_REVISION); + GETCAP(CAP_ANALOG_2GHz_REVISION); + + GETCAP(CAP_REG_DOMAIN); + GETCAP(CAP_REG_CAP_BITS); + GETCAP(CAP_WIRELESS_MODES); + GETCAP(CAP_CHAN_SPREAD_SUPPORT); + GETCAP(CAP_COMPRESS_SUPPORT); + GETCAP(CAP_BURST_SUPPORT); + GETCAP(CAP_FAST_FRAMES_SUPPORT); + GETCAP(CAP_CHAP_TUNING_SUPPORT); + GETCAP(CAP_TURBOG_SUPPORT); + GETCAP(CAP_TURBO_PRIME_SUPPORT); + GETCAP(CAP_DEVICE_TYPE); + GETCAP(CAP_WME_SUPPORT); + GETCAP(CAP_TOTAL_QUEUES); + GETCAP(CAP_CONNECTION_ID_MAX); + + GETCAP(CAP_LOW_5GHZ_CHAN); + GETCAP(CAP_HIGH_5GHZ_CHAN); + GETCAP(CAP_LOW_2GHZ_CHAN); + GETCAP(CAP_HIGH_2GHZ_CHAN); + GETCAP(CAP_TWICE_ANTENNAGAIN_5G); + GETCAP(CAP_TWICE_ANTENNAGAIN_2G); + + GETCAP(CAP_CIPHER_AES_CCM); + GETCAP(CAP_CIPHER_TKIP); + GETCAP(CAP_MIC_TKIP); + return 0; +} + +static int ar5523_set_ledsteady(struct ar5523 *ar, int lednum, int ledmode) +{ + struct ar5523_cmd_ledsteady led; + + led.lednum = cpu_to_be32(lednum); + led.ledmode = cpu_to_be32(ledmode); + + ar5523_dbg(ar, "set %s led %s (steady)\n", + (lednum == UATH_LED_LINK) ? "link" : "activity", + ledmode ? "on" : "off"); + return ar5523_cmd_write(ar, WDCMSG_SET_LED_STEADY, &led, sizeof(led), + 0); +} + +static int ar5523_set_rxfilter(struct ar5523 *ar, u32 bits, u32 op) +{ + struct ar5523_cmd_rx_filter rxfilter; + + rxfilter.bits = cpu_to_be32(bits); + rxfilter.op = cpu_to_be32(op); + + ar5523_dbg(ar, "setting Rx filter=0x%x flags=0x%x\n", bits, op); + return ar5523_cmd_write(ar, WDCMSG_RX_FILTER, &rxfilter, + sizeof(rxfilter), 0); +} + +static int ar5523_reset_tx_queues(struct ar5523 *ar) +{ + __be32 qid = cpu_to_be32(0); + + ar5523_dbg(ar, "resetting Tx queue\n"); + return ar5523_cmd_write(ar, WDCMSG_RELEASE_TX_QUEUE, + &qid, sizeof(qid), 0); +} + +static int ar5523_set_chan(struct ar5523 *ar) +{ + struct ieee80211_conf *conf = &ar->hw->conf; + + struct ar5523_cmd_reset reset; + + memset(&reset, 0, sizeof(reset)); + reset.flags |= cpu_to_be32(UATH_CHAN_2GHZ); + reset.flags |= cpu_to_be32(UATH_CHAN_OFDM); + reset.freq = cpu_to_be32(conf->channel->center_freq); + reset.maxrdpower = cpu_to_be32(50); /* XXX */ + reset.channelchange = cpu_to_be32(1); + reset.keeprccontent = cpu_to_be32(0); + + ar5523_dbg(ar, "set chan flags 0x%x freq %d\n", + be32_to_cpu(reset.flags), + conf->channel->center_freq); + return ar5523_cmd_write(ar, WDCMSG_RESET, &reset, sizeof(reset), 0); +} + +static int ar5523_queue_init(struct ar5523 *ar) +{ + struct ar5523_cmd_txq_setup qinfo; + + ar5523_dbg(ar, "setting up Tx queue\n"); + qinfo.qid = cpu_to_be32(0); + qinfo.len = cpu_to_be32(sizeof(qinfo.attr)); + qinfo.attr.priority = cpu_to_be32(0); /* XXX */ + qinfo.attr.aifs = cpu_to_be32(3); + qinfo.attr.logcwmin = cpu_to_be32(4); + qinfo.attr.logcwmax = cpu_to_be32(10); + qinfo.attr.bursttime = cpu_to_be32(0); + qinfo.attr.mode = cpu_to_be32(0); + qinfo.attr.qflags = cpu_to_be32(1); /* XXX? */ + return ar5523_cmd_write(ar, WDCMSG_SETUP_TX_QUEUE, &qinfo, + sizeof(qinfo), 0); +} + +static int ar5523_switch_chan(struct ar5523 *ar) +{ + int error; + + error = ar5523_set_chan(ar); + if (error) { + ar5523_err(ar, "could not set chan, error %d\n", error); + goto out_err; + } + + /* reset Tx rings */ + error = ar5523_reset_tx_queues(ar); + if (error) { + ar5523_err(ar, "could not reset Tx queues, error %d\n", + error); + goto out_err; + } + /* set Tx rings WME properties */ + error = ar5523_queue_init(ar); + if (error) + ar5523_err(ar, "could not init wme, error %d\n", error); + +out_err: + return error; +} + +static void ar5523_rx_data_put(struct ar5523 *ar, + struct ar5523_rx_data *data) +{ + unsigned long flags; + spin_lock_irqsave(&ar->rx_data_list_lock, flags); + list_move(&data->list, &ar->rx_data_free); + spin_unlock_irqrestore(&ar->rx_data_list_lock, flags); +} + +static void ar5523_data_rx_cb(struct urb *urb) +{ + struct ar5523_rx_data *data = urb->context; + struct ar5523 *ar = data->ar; + struct ar5523_rx_desc *desc; + struct ar5523_chunk *chunk; + struct ieee80211_hw *hw = ar->hw; + struct ieee80211_rx_status *rx_status; + u32 rxlen; + int usblen = urb->actual_length; + int hdrlen, pad; + + ar5523_dbg(ar, "%s\n", __func__); + /* sync/async unlink faults aren't errors */ + if (urb->status) { + if (urb->status != -ESHUTDOWN) + ar5523_err(ar, "%s: USB err: %d\n", __func__, + urb->status); + goto skip; + } + + if (usblen < AR5523_MIN_RXBUFSZ) { + ar5523_err(ar, "RX: wrong xfer size (usblen=%d)\n", usblen); + goto skip; + } + + chunk = (struct ar5523_chunk *) data->skb->data; + + if (((chunk->flags & UATH_CFLAGS_FINAL) == 0) || + chunk->seqnum != 0) { + ar5523_dbg(ar, "RX: No final flag. s: %d f: %02x l: %d\n", + chunk->seqnum, chunk->flags, + be16_to_cpu(chunk->length)); + goto skip; + } + + /* Rx descriptor is located at the end, 32-bit aligned */ + desc = (struct ar5523_rx_desc *) + (data->skb->data + usblen - sizeof(struct ar5523_rx_desc)); + + rxlen = be32_to_cpu(desc->len); + if (rxlen > ar->rxbufsz) { + ar5523_dbg(ar, "RX: Bad descriptor (len=%d)\n", + be32_to_cpu(desc->len)); + goto skip; + } + + if (!rxlen) { + ar5523_dbg(ar, "RX: rxlen is 0\n"); + goto skip; + } + + if (be32_to_cpu(desc->status) != 0) { + ar5523_dbg(ar, "Bad RX status (0x%x len = %d). Skip\n", + be32_to_cpu(desc->status), be32_to_cpu(desc->len)); + goto skip; + } + + skb_reserve(data->skb, sizeof(*chunk)); + skb_put(data->skb, rxlen - sizeof(struct ar5523_rx_desc)); + + hdrlen = ieee80211_get_hdrlen_from_skb(data->skb); + if (!IS_ALIGNED(hdrlen, 4)) { + ar5523_dbg(ar, "eek, alignment workaround activated\n"); + pad = ALIGN(hdrlen, 4) - hdrlen; + memmove(data->skb->data + pad, data->skb->data, hdrlen); + skb_pull(data->skb, pad); + skb_put(data->skb, pad); + } + + rx_status = IEEE80211_SKB_RXCB(data->skb); + memset(rx_status, 0, sizeof(*rx_status)); + rx_status->freq = be32_to_cpu(desc->channel); + rx_status->band = hw->conf.channel->band; + rx_status->signal = -95 + be32_to_cpu(desc->rssi); + + ieee80211_rx_irqsafe(hw, data->skb); + data->skb = NULL; + +skip: + if (data->skb) { + dev_kfree_skb_irq(data->skb); + data->skb = NULL; + } + + ar5523_rx_data_put(ar, data); + if (atomic_inc_return(&ar->rx_data_free_cnt) >= + AR5523_RX_DATA_REFILL_COUNT && + test_bit(AR5523_HW_UP, &ar->flags)) + queue_work(ar->wq, &ar->rx_refill_work); +} + +static void ar5523_rx_refill_work(struct work_struct *work) +{ + struct ar5523 *ar = container_of(work, struct ar5523, rx_refill_work); + struct ar5523_rx_data *data; + unsigned long flags; + int error; + + ar5523_dbg(ar, "%s\n", __func__); + do { + spin_lock_irqsave(&ar->rx_data_list_lock, flags); + + if (!list_empty(&ar->rx_data_free)) + data = (struct ar5523_rx_data *) ar->rx_data_free.next; + else + data = NULL; + spin_unlock_irqrestore(&ar->rx_data_list_lock, flags); + + if (!data) + goto done; + + data->skb = alloc_skb(ar->rxbufsz, GFP_KERNEL); + if (!data->skb) { + ar5523_err(ar, "could not allocate rx skbuff\n"); + return; + } + + usb_fill_bulk_urb(data->urb, ar->dev, + ar5523_data_rx_pipe(ar->dev), data->skb->data, + ar->rxbufsz, ar5523_data_rx_cb, data); + + spin_lock_irqsave(&ar->rx_data_list_lock, flags); + list_move(&data->list, &ar->rx_data_used); + spin_unlock_irqrestore(&ar->rx_data_list_lock, flags); + atomic_dec(&ar->rx_data_free_cnt); + + error = usb_submit_urb(data->urb, GFP_KERNEL); + if (error) { + kfree_skb(data->skb); + if (error != -ENODEV) + ar5523_err(ar, "Err sending rx data urb %d\n", + error); + ar5523_rx_data_put(ar, data); + atomic_inc(&ar->rx_data_free_cnt); + return; + } + + } while (true); +done: + return; +} + +static void ar5523_cancel_rx_bufs(struct ar5523 *ar) +{ + struct ar5523_rx_data *data; + unsigned long flags; + + do { + spin_lock_irqsave(&ar->rx_data_list_lock, flags); + if (!list_empty(&ar->rx_data_used)) + data = (struct ar5523_rx_data *) ar->rx_data_used.next; + else + data = NULL; + spin_unlock_irqrestore(&ar->rx_data_list_lock, flags); + + if (!data) + break; + + usb_kill_urb(data->urb); + list_move(&data->list, &ar->rx_data_free); + atomic_inc(&ar->rx_data_free_cnt); + } while (data); +} + +static void ar5523_free_rx_bufs(struct ar5523 *ar) +{ + struct ar5523_rx_data *data; + + ar5523_cancel_rx_bufs(ar); + while (!list_empty(&ar->rx_data_free)) { + data = (struct ar5523_rx_data *) ar->rx_data_free.next; + list_del(&data->list); + usb_free_urb(data->urb); + } +} + +static int ar5523_alloc_rx_bufs(struct ar5523 *ar) +{ + int i; + + for (i = 0; i < AR5523_RX_DATA_COUNT; i++) { + struct ar5523_rx_data *data = &ar->rx_data[i]; + + data->ar = ar; + data->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!data->urb) { + ar5523_err(ar, "could not allocate rx data urb\n"); + goto err; + } + list_add_tail(&data->list, &ar->rx_data_free); + atomic_inc(&ar->rx_data_free_cnt); + } + return 0; + +err: + ar5523_free_rx_bufs(ar); + return -ENOMEM; +} + +static void ar5523_data_tx_pkt_put(struct ar5523 *ar) +{ + atomic_dec(&ar->tx_nr_total); + if (!atomic_dec_return(&ar->tx_nr_pending)) { + del_timer(&ar->tx_wd_timer); + wake_up(&ar->tx_flush_waitq); + } + + if (atomic_read(&ar->tx_nr_total) < AR5523_TX_DATA_RESTART_COUNT) { + ar5523_dbg(ar, "restart tx queue\n"); + ieee80211_wake_queues(ar->hw); + } +} + +static void ar5523_data_tx_cb(struct urb *urb) +{ + struct sk_buff *skb = urb->context; + struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb); + struct ar5523_tx_data *data = (struct ar5523_tx_data *) + txi->driver_data; + struct ar5523 *ar = data->ar; + unsigned long flags; + + ar5523_dbg(ar, "data tx urb completed: %d\n", urb->status); + + spin_lock_irqsave(&ar->tx_data_list_lock, flags); + list_del(&data->list); + spin_unlock_irqrestore(&ar->tx_data_list_lock, flags); + + if (urb->status) { + ar5523_dbg(ar, "%s: urb status: %d\n", __func__, urb->status); + ar5523_data_tx_pkt_put(ar); + ieee80211_free_txskb(ar->hw, skb); + } else { + skb_pull(skb, sizeof(struct ar5523_tx_desc) + sizeof(__be32)); + ieee80211_tx_status_irqsafe(ar->hw, skb); + } + usb_free_urb(urb); +} + +static void ar5523_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb); + struct ar5523_tx_data *data = (struct ar5523_tx_data *) + txi->driver_data; + struct ar5523 *ar = hw->priv; + unsigned long flags; + + ar5523_dbg(ar, "tx called\n"); + if (atomic_inc_return(&ar->tx_nr_total) >= AR5523_TX_DATA_COUNT) { + ar5523_dbg(ar, "tx queue full\n"); + ar5523_dbg(ar, "stop queues (tot %d pend %d)\n", + atomic_read(&ar->tx_nr_total), + atomic_read(&ar->tx_nr_pending)); + ieee80211_stop_queues(hw); + } + + data->skb = skb; + + spin_lock_irqsave(&ar->tx_data_list_lock, flags); + list_add_tail(&data->list, &ar->tx_queue_pending); + spin_unlock_irqrestore(&ar->tx_data_list_lock, flags); + + ieee80211_queue_work(ar->hw, &ar->tx_work); +} + +static void ar5523_tx_work_locked(struct ar5523 *ar) +{ + struct ar5523_tx_data *data; + struct ar5523_tx_desc *desc; + struct ar5523_chunk *chunk; + struct ieee80211_tx_info *txi; + struct urb *urb; + struct sk_buff *skb; + int error = 0, paylen; + u32 txqid; + unsigned long flags; + + BUILD_BUG_ON(sizeof(struct ar5523_tx_data) > + IEEE80211_TX_INFO_DRIVER_DATA_SIZE); + + ar5523_dbg(ar, "%s\n", __func__); + do { + spin_lock_irqsave(&ar->tx_data_list_lock, flags); + if (!list_empty(&ar->tx_queue_pending)) { + data = (struct ar5523_tx_data *) + ar->tx_queue_pending.next; + list_del(&data->list); + } else + data = NULL; + spin_unlock_irqrestore(&ar->tx_data_list_lock, flags); + + if (!data) + break; + + skb = data->skb; + txqid = 0; + txi = IEEE80211_SKB_CB(skb); + paylen = skb->len; + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + ar5523_err(ar, "Failed to allocate TX urb\n"); + ieee80211_free_txskb(ar->hw, skb); + continue; + } + + data->ar = ar; + data->urb = urb; + + desc = (struct ar5523_tx_desc *)skb_push(skb, sizeof(*desc)); + chunk = (struct ar5523_chunk *)skb_push(skb, sizeof(*chunk)); + + chunk->seqnum = 0; + chunk->flags = UATH_CFLAGS_FINAL; + chunk->length = cpu_to_be16(skb->len); + + desc->msglen = cpu_to_be32(skb->len); + desc->msgid = AR5523_DATA_ID; + desc->buflen = cpu_to_be32(paylen); + desc->type = cpu_to_be32(WDCMSG_SEND); + desc->flags = cpu_to_be32(UATH_TX_NOTIFY); + + if (test_bit(AR5523_CONNECTED, &ar->flags)) + desc->connid = cpu_to_be32(AR5523_ID_BSS); + else + desc->connid = cpu_to_be32(AR5523_ID_BROADCAST); + + if (txi->flags & IEEE80211_TX_CTL_USE_MINRATE) + txqid |= UATH_TXQID_MINRATE; + + desc->txqid = cpu_to_be32(txqid); + + urb->transfer_flags = URB_ZERO_PACKET; + usb_fill_bulk_urb(urb, ar->dev, ar5523_data_tx_pipe(ar->dev), + skb->data, skb->len, ar5523_data_tx_cb, skb); + + spin_lock_irqsave(&ar->tx_data_list_lock, flags); + list_add_tail(&data->list, &ar->tx_queue_submitted); + spin_unlock_irqrestore(&ar->tx_data_list_lock, flags); + mod_timer(&ar->tx_wd_timer, jiffies + AR5523_TX_WD_TIMEOUT); + atomic_inc(&ar->tx_nr_pending); + + ar5523_dbg(ar, "TX Frame (%d pending)\n", + atomic_read(&ar->tx_nr_pending)); + error = usb_submit_urb(urb, GFP_KERNEL); + if (error) { + ar5523_err(ar, "error %d when submitting tx urb\n", + error); + spin_lock_irqsave(&ar->tx_data_list_lock, flags); + list_del(&data->list); + spin_unlock_irqrestore(&ar->tx_data_list_lock, flags); + atomic_dec(&ar->tx_nr_pending); + ar5523_data_tx_pkt_put(ar); + usb_free_urb(urb); + ieee80211_free_txskb(ar->hw, skb); + } + } while (true); +} + +static void ar5523_tx_work(struct work_struct *work) +{ + struct ar5523 *ar = container_of(work, struct ar5523, tx_work); + + ar5523_dbg(ar, "%s\n", __func__); + mutex_lock(&ar->mutex); + ar5523_tx_work_locked(ar); + mutex_unlock(&ar->mutex); +} + +static void ar5523_tx_wd_timer(unsigned long arg) +{ + struct ar5523 *ar = (struct ar5523 *) arg; + + ar5523_dbg(ar, "TX watchdog timer triggered\n"); + ieee80211_queue_work(ar->hw, &ar->tx_wd_work); +} + +static void ar5523_tx_wd_work(struct work_struct *work) +{ + struct ar5523 *ar = container_of(work, struct ar5523, tx_wd_work); + + /* Occasionally the TX queues stop responding. The only way to + * recover seems to be to reset the dongle. + */ + + mutex_lock(&ar->mutex); + ar5523_err(ar, "TX queue stuck (tot %d pend %d)\n", + atomic_read(&ar->tx_nr_total), + atomic_read(&ar->tx_nr_pending)); + + ar5523_err(ar, "Will restart dongle.\n"); + ar5523_cmd_write(ar, WDCMSG_TARGET_RESET, NULL, 0, 0); + mutex_unlock(&ar->mutex); +} + +static void ar5523_flush_tx(struct ar5523 *ar) +{ + ar5523_tx_work_locked(ar); + + /* Don't waste time trying to flush if USB is disconnected */ + if (test_bit(AR5523_USB_DISCONNECTED, &ar->flags)) + return; + if (!wait_event_timeout(ar->tx_flush_waitq, + !atomic_read(&ar->tx_nr_pending), AR5523_FLUSH_TIMEOUT)) + ar5523_err(ar, "flush timeout (tot %d pend %d)\n", + atomic_read(&ar->tx_nr_total), + atomic_read(&ar->tx_nr_pending)); +} + +static void ar5523_free_tx_cmd(struct ar5523 *ar) +{ + struct ar5523_tx_cmd *cmd = &ar->tx_cmd; + + usb_free_coherent(ar->dev, AR5523_MAX_RXCMDSZ, cmd->buf_tx, + cmd->urb_tx->transfer_dma); + usb_free_urb(cmd->urb_tx); +} + +static int ar5523_alloc_tx_cmd(struct ar5523 *ar) +{ + struct ar5523_tx_cmd *cmd = &ar->tx_cmd; + + cmd->ar = ar; + init_completion(&cmd->done); + + cmd->urb_tx = usb_alloc_urb(0, GFP_KERNEL); + if (!cmd->urb_tx) { + ar5523_err(ar, "could not allocate urb\n"); + return -ENOMEM; + } + cmd->buf_tx = usb_alloc_coherent(ar->dev, AR5523_MAX_TXCMDSZ, + GFP_KERNEL, + &cmd->urb_tx->transfer_dma); + if (!cmd->buf_tx) { + usb_free_urb(cmd->urb_tx); + return -ENOMEM; + } + return 0; +} + +/* + * This function is called periodically (every second) when associated to + * query device statistics. + */ +static void ar5523_stat_work(struct work_struct *work) +{ + struct ar5523 *ar = container_of(work, struct ar5523, stat_work.work); + int error; + + ar5523_dbg(ar, "%s\n", __func__); + mutex_lock(&ar->mutex); + + /* + * Send request for statistics asynchronously once a second. This + * seems to be important. Throughput is a lot better if this is done. + */ + error = ar5523_cmd_write(ar, WDCMSG_TARGET_GET_STATS, NULL, 0, 0); + if (error) + ar5523_err(ar, "could not query stats, error %d\n", error); + mutex_unlock(&ar->mutex); + ieee80211_queue_delayed_work(ar->hw, &ar->stat_work, HZ); +} + +/* + * Interface routines to the mac80211 stack. + */ +static int ar5523_start(struct ieee80211_hw *hw) +{ + struct ar5523 *ar = hw->priv; + int error; + __be32 val; + + ar5523_dbg(ar, "start called\n"); + + mutex_lock(&ar->mutex); + val = cpu_to_be32(0); + ar5523_cmd_write(ar, WDCMSG_BIND, &val, sizeof(val), 0); + + /* set MAC address */ + ar5523_config_multi(ar, CFG_MAC_ADDR, &ar->hw->wiphy->perm_addr, + ETH_ALEN); + + /* XXX honor net80211 state */ + ar5523_config(ar, CFG_RATE_CONTROL_ENABLE, 0x00000001); + ar5523_config(ar, CFG_DIVERSITY_CTL, 0x00000001); + ar5523_config(ar, CFG_ABOLT, 0x0000003f); + ar5523_config(ar, CFG_WME_ENABLED, 0x00000000); + + ar5523_config(ar, CFG_SERVICE_TYPE, 1); + ar5523_config(ar, CFG_TP_SCALE, 0x00000000); + ar5523_config(ar, CFG_TPC_HALF_DBM5, 0x0000003c); + ar5523_config(ar, CFG_TPC_HALF_DBM2, 0x0000003c); + ar5523_config(ar, CFG_OVERRD_TX_POWER, 0x00000000); + ar5523_config(ar, CFG_GMODE_PROTECTION, 0x00000000); + ar5523_config(ar, CFG_GMODE_PROTECT_RATE_INDEX, 0x00000003); + ar5523_config(ar, CFG_PROTECTION_TYPE, 0x00000000); + ar5523_config(ar, CFG_MODE_CTS, 0x00000002); + + error = ar5523_cmd_read(ar, WDCMSG_TARGET_START, NULL, 0, + &val, sizeof(val), AR5523_CMD_FLAG_MAGIC); + if (error) { + ar5523_dbg(ar, "could not start target, error %d\n", error); + goto err; + } + ar5523_dbg(ar, "WDCMSG_TARGET_START returns handle: 0x%x\n", + be32_to_cpu(val)); + + ar5523_switch_chan(ar); + + val = cpu_to_be32(TARGET_DEVICE_AWAKE); + ar5523_cmd_write(ar, WDCMSG_SET_PWR_MODE, &val, sizeof(val), 0); + /* XXX? check */ + ar5523_cmd_write(ar, WDCMSG_RESET_KEY_CACHE, NULL, 0, 0); + + set_bit(AR5523_HW_UP, &ar->flags); + queue_work(ar->wq, &ar->rx_refill_work); + + /* enable Rx */ + ar5523_set_rxfilter(ar, 0, UATH_FILTER_OP_INIT); + ar5523_set_rxfilter(ar, + UATH_FILTER_RX_UCAST | UATH_FILTER_RX_MCAST | + UATH_FILTER_RX_BCAST | UATH_FILTER_RX_BEACON, + UATH_FILTER_OP_SET); + + ar5523_set_ledsteady(ar, UATH_LED_ACTIVITY, UATH_LED_ON); + ar5523_dbg(ar, "start OK\n"); + +err: + mutex_unlock(&ar->mutex); + return error; +} + +static void ar5523_stop(struct ieee80211_hw *hw) +{ + struct ar5523 *ar = hw->priv; + + ar5523_dbg(ar, "stop called\n"); + + cancel_delayed_work_sync(&ar->stat_work); + mutex_lock(&ar->mutex); + clear_bit(AR5523_HW_UP, &ar->flags); + + ar5523_set_ledsteady(ar, UATH_LED_LINK, UATH_LED_OFF); + ar5523_set_ledsteady(ar, UATH_LED_ACTIVITY, UATH_LED_OFF); + + ar5523_cmd_write(ar, WDCMSG_TARGET_STOP, NULL, 0, 0); + + del_timer_sync(&ar->tx_wd_timer); + cancel_work_sync(&ar->tx_wd_work); + cancel_work_sync(&ar->rx_refill_work); + ar5523_cancel_rx_bufs(ar); + mutex_unlock(&ar->mutex); +} + +static int ar5523_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + struct ar5523 *ar = hw->priv; + int ret; + + ar5523_dbg(ar, "set_rts_threshold called\n"); + mutex_lock(&ar->mutex); + + ret = ar5523_config(ar, CFG_USER_RTS_THRESHOLD, value); + + mutex_unlock(&ar->mutex); + return ret; +} + +static void ar5523_flush(struct ieee80211_hw *hw, bool drop) +{ + struct ar5523 *ar = hw->priv; + + ar5523_dbg(ar, "flush called\n"); + ar5523_flush_tx(ar); +} + +static int ar5523_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ar5523 *ar = hw->priv; + + ar5523_dbg(ar, "add interface called\n"); + + if (ar->vif) { + ar5523_dbg(ar, "invalid add_interface\n"); + return -EOPNOTSUPP; + } + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + ar->vif = vif; + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static void ar5523_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ar5523 *ar = hw->priv; + + ar5523_dbg(ar, "remove interface called\n"); + ar->vif = NULL; +} + +static int ar5523_hwconfig(struct ieee80211_hw *hw, u32 changed) +{ + struct ar5523 *ar = hw->priv; + + ar5523_dbg(ar, "config called\n"); + mutex_lock(&ar->mutex); + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + ar5523_dbg(ar, "Do channel switch\n"); + ar5523_flush_tx(ar); + ar5523_switch_chan(ar); + } + mutex_unlock(&ar->mutex); + return 0; +} + +static int ar5523_get_wlan_mode(struct ar5523 *ar, + struct ieee80211_bss_conf *bss_conf) +{ + struct ieee80211_supported_band *band; + int bit; + struct ieee80211_sta *sta; + u32 sta_rate_set; + + band = ar->hw->wiphy->bands[ar->hw->conf.channel->band]; + sta = ieee80211_find_sta(ar->vif, bss_conf->bssid); + if (!sta) { + ar5523_info(ar, "STA not found!\n"); + return WLAN_MODE_11b; + } + sta_rate_set = sta->supp_rates[ar->hw->conf.channel->band]; + + for (bit = 0; bit < band->n_bitrates; bit++) { + if (sta_rate_set & 1) { + int rate = band->bitrates[bit].bitrate; + switch (rate) { + case 60: + case 90: + case 120: + case 180: + case 240: + case 360: + case 480: + case 540: + return WLAN_MODE_11g; + } + } + sta_rate_set >>= 1; + } + return WLAN_MODE_11b; +} + +static void ar5523_create_rateset(struct ar5523 *ar, + struct ieee80211_bss_conf *bss_conf, + struct ar5523_cmd_rateset *rs, + bool basic) +{ + struct ieee80211_supported_band *band; + struct ieee80211_sta *sta; + int bit, i = 0; + u32 sta_rate_set, basic_rate_set; + + sta = ieee80211_find_sta(ar->vif, bss_conf->bssid); + basic_rate_set = bss_conf->basic_rates; + if (!sta) { + ar5523_info(ar, "STA not found. Cannot set rates\n"); + sta_rate_set = bss_conf->basic_rates; + } + sta_rate_set = sta->supp_rates[ar->hw->conf.channel->band]; + + ar5523_dbg(ar, "sta rate_set = %08x\n", sta_rate_set); + + band = ar->hw->wiphy->bands[ar->hw->conf.channel->band]; + for (bit = 0; bit < band->n_bitrates; bit++) { + BUG_ON(i >= AR5523_MAX_NRATES); + ar5523_dbg(ar, "Considering rate %d : %d\n", + band->bitrates[bit].hw_value, sta_rate_set & 1); + if (sta_rate_set & 1) { + rs->set[i] = band->bitrates[bit].hw_value; + if (basic_rate_set & 1 && basic) + rs->set[i] |= 0x80; + i++; + } + sta_rate_set >>= 1; + basic_rate_set >>= 1; + } + + rs->length = i; +} + +static int ar5523_set_basic_rates(struct ar5523 *ar, + struct ieee80211_bss_conf *bss) +{ + struct ar5523_cmd_rates rates; + + memset(&rates, 0, sizeof(rates)); + rates.connid = cpu_to_be32(2); /* XXX */ + rates.size = cpu_to_be32(sizeof(struct ar5523_cmd_rateset)); + ar5523_create_rateset(ar, bss, &rates.rateset, true); + + return ar5523_cmd_write(ar, WDCMSG_SET_BASIC_RATE, &rates, + sizeof(rates), 0); +} + +static int ar5523_create_connection(struct ar5523 *ar, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss) +{ + struct ar5523_cmd_create_connection create; + int wlan_mode; + + memset(&create, 0, sizeof(create)); + create.connid = cpu_to_be32(2); + create.bssid = cpu_to_be32(0); + /* XXX packed or not? */ + create.size = cpu_to_be32(sizeof(struct ar5523_cmd_rateset)); + + ar5523_create_rateset(ar, bss, &create.connattr.rateset, false); + + wlan_mode = ar5523_get_wlan_mode(ar, bss); + create.connattr.wlanmode = cpu_to_be32(wlan_mode); + + return ar5523_cmd_write(ar, WDCMSG_CREATE_CONNECTION, &create, + sizeof(create), 0); +} + +static int ar5523_write_associd(struct ar5523 *ar, + struct ieee80211_bss_conf *bss) +{ + struct ar5523_cmd_set_associd associd; + + memset(&associd, 0, sizeof(associd)); + associd.defaultrateix = cpu_to_be32(0); /* XXX */ + associd.associd = cpu_to_be32(bss->aid); + associd.timoffset = cpu_to_be32(0x3b); /* XXX */ + memcpy(associd.bssid, bss->bssid, ETH_ALEN); + return ar5523_cmd_write(ar, WDCMSG_WRITE_ASSOCID, &associd, + sizeof(associd), 0); +} + +static void ar5523_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss, + u32 changed) +{ + struct ar5523 *ar = hw->priv; + int error; + + ar5523_dbg(ar, "bss_info_changed called\n"); + mutex_lock(&ar->mutex); + + if (!(changed & BSS_CHANGED_ASSOC)) + goto out_unlock; + + if (bss->assoc) { + error = ar5523_create_connection(ar, vif, bss); + if (error) { + ar5523_err(ar, "could not create connection\n"); + goto out_unlock; + } + + error = ar5523_set_basic_rates(ar, bss); + if (error) { + ar5523_err(ar, "could not set negotiated rate set\n"); + goto out_unlock; + } + + error = ar5523_write_associd(ar, bss); + if (error) { + ar5523_err(ar, "could not set association\n"); + goto out_unlock; + } + + /* turn link LED on */ + ar5523_set_ledsteady(ar, UATH_LED_LINK, UATH_LED_ON); + set_bit(AR5523_CONNECTED, &ar->flags); + ieee80211_queue_delayed_work(hw, &ar->stat_work, HZ); + + } else { + cancel_delayed_work(&ar->stat_work); + clear_bit(AR5523_CONNECTED, &ar->flags); + ar5523_set_ledsteady(ar, UATH_LED_LINK, UATH_LED_OFF); + } + +out_unlock: + mutex_unlock(&ar->mutex); + +} + +#define AR5523_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \ + FIF_ALLMULTI | \ + FIF_FCSFAIL | \ + FIF_OTHER_BSS) + +static void ar5523_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct ar5523 *ar = hw->priv; + u32 filter = 0; + + ar5523_dbg(ar, "configure_filter called\n"); + mutex_lock(&ar->mutex); + ar5523_flush_tx(ar); + + *total_flags &= AR5523_SUPPORTED_FILTERS; + + /* The filters seems strange. UATH_FILTER_RX_BCAST and + * UATH_FILTER_RX_MCAST does not result in those frames being RXed. + * The only way I have found to get [mb]cast frames seems to be + * to set UATH_FILTER_RX_PROM. */ + filter |= UATH_FILTER_RX_UCAST | UATH_FILTER_RX_MCAST | + UATH_FILTER_RX_BCAST | UATH_FILTER_RX_BEACON | + UATH_FILTER_RX_PROM; + + ar5523_set_rxfilter(ar, 0, UATH_FILTER_OP_INIT); + ar5523_set_rxfilter(ar, filter, UATH_FILTER_OP_SET); + + mutex_unlock(&ar->mutex); +} + +static const struct ieee80211_ops ar5523_ops = { + .start = ar5523_start, + .stop = ar5523_stop, + .tx = ar5523_tx, + .set_rts_threshold = ar5523_set_rts_threshold, + .add_interface = ar5523_add_interface, + .remove_interface = ar5523_remove_interface, + .config = ar5523_hwconfig, + .bss_info_changed = ar5523_bss_info_changed, + .configure_filter = ar5523_configure_filter, + .flush = ar5523_flush, +}; + +static int ar5523_host_available(struct ar5523 *ar) +{ + struct ar5523_cmd_host_available setup; + + /* inform target the host is available */ + setup.sw_ver_major = cpu_to_be32(ATH_SW_VER_MAJOR); + setup.sw_ver_minor = cpu_to_be32(ATH_SW_VER_MINOR); + setup.sw_ver_patch = cpu_to_be32(ATH_SW_VER_PATCH); + setup.sw_ver_build = cpu_to_be32(ATH_SW_VER_BUILD); + return ar5523_cmd_read(ar, WDCMSG_HOST_AVAILABLE, + &setup, sizeof(setup), NULL, 0, 0); +} + +static int ar5523_get_devstatus(struct ar5523 *ar) +{ + u8 macaddr[ETH_ALEN]; + int error; + + /* retrieve MAC address */ + error = ar5523_get_status(ar, ST_MAC_ADDR, macaddr, ETH_ALEN); + if (error) { + ar5523_err(ar, "could not read MAC address\n"); + return error; + } + + SET_IEEE80211_PERM_ADDR(ar->hw, macaddr); + + error = ar5523_get_status(ar, ST_SERIAL_NUMBER, + &ar->serial[0], sizeof(ar->serial)); + if (error) { + ar5523_err(ar, "could not read device serial number\n"); + return error; + } + return 0; +} + +#define AR5523_SANE_RXBUFSZ 2000 + +static int ar5523_get_max_rxsz(struct ar5523 *ar) +{ + int error; + __be32 rxsize; + + /* Get max rx size */ + error = ar5523_get_status(ar, ST_WDC_TRANSPORT_CHUNK_SIZE, &rxsize, + sizeof(rxsize)); + if (error != 0) { + ar5523_err(ar, "could not read max RX size\n"); + return error; + } + + ar->rxbufsz = be32_to_cpu(rxsize); + + if (!ar->rxbufsz || ar->rxbufsz > AR5523_SANE_RXBUFSZ) { + ar5523_err(ar, "Bad rxbufsz from device. Using %d instead\n", + AR5523_SANE_RXBUFSZ); + ar->rxbufsz = AR5523_SANE_RXBUFSZ; + } + + ar5523_dbg(ar, "Max RX buf size: %d\n", ar->rxbufsz); + return 0; +} + +/* + * This is copied from rtl818x, but we should probably move this + * to common code as in OpenBSD. + */ +static const struct ieee80211_rate ar5523_rates[] = { + { .bitrate = 10, .hw_value = 2, }, + { .bitrate = 20, .hw_value = 4 }, + { .bitrate = 55, .hw_value = 11, }, + { .bitrate = 110, .hw_value = 22, }, + { .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, }, +}; + +static const struct ieee80211_channel ar5523_channels[] = { + { .center_freq = 2412 }, + { .center_freq = 2417 }, + { .center_freq = 2422 }, + { .center_freq = 2427 }, + { .center_freq = 2432 }, + { .center_freq = 2437 }, + { .center_freq = 2442 }, + { .center_freq = 2447 }, + { .center_freq = 2452 }, + { .center_freq = 2457 }, + { .center_freq = 2462 }, + { .center_freq = 2467 }, + { .center_freq = 2472 }, + { .center_freq = 2484 }, +}; + +static int ar5523_init_modes(struct ar5523 *ar) +{ + BUILD_BUG_ON(sizeof(ar->channels) != sizeof(ar5523_channels)); + BUILD_BUG_ON(sizeof(ar->rates) != sizeof(ar5523_rates)); + + memcpy(ar->channels, ar5523_channels, sizeof(ar5523_channels)); + memcpy(ar->rates, ar5523_rates, sizeof(ar5523_rates)); + + ar->band.band = IEEE80211_BAND_2GHZ; + ar->band.channels = ar->channels; + ar->band.n_channels = ARRAY_SIZE(ar5523_channels); + ar->band.bitrates = ar->rates; + ar->band.n_bitrates = ARRAY_SIZE(ar5523_rates); + ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &ar->band; + return 0; +} + +/* + * Load the MIPS R4000 microcode into the device. Once the image is loaded, + * the device will detach itself from the bus and reattach later with a new + * product Id (a la ezusb). + */ +static int ar5523_load_firmware(struct usb_device *dev) +{ + struct ar5523_fwblock *txblock, *rxblock; + const struct firmware *fw; + void *fwbuf; + int len, offset; + int foolen; /* XXX(hch): handle short transfers */ + int error = -ENXIO; + + if (request_firmware(&fw, AR5523_FIRMWARE_FILE, &dev->dev)) { + dev_err(&dev->dev, "no firmware found: %s\n", + AR5523_FIRMWARE_FILE); + return -ENOENT; + } + + txblock = kmalloc(sizeof(*txblock), GFP_KERNEL); + if (!txblock) + goto out; + + rxblock = kmalloc(sizeof(*rxblock), GFP_KERNEL); + if (!rxblock) + goto out_free_txblock; + + fwbuf = kmalloc(AR5523_MAX_FWBLOCK_SIZE, GFP_KERNEL); + if (!fwbuf) + goto out_free_rxblock; + + memset(txblock, 0, sizeof(struct ar5523_fwblock)); + txblock->flags = cpu_to_be32(AR5523_WRITE_BLOCK); + txblock->total = cpu_to_be32(fw->size); + + offset = 0; + len = fw->size; + while (len > 0) { + int mlen = min(len, AR5523_MAX_FWBLOCK_SIZE); + + txblock->remain = cpu_to_be32(len - mlen); + txblock->len = cpu_to_be32(mlen); + + /* send firmware block meta-data */ + error = usb_bulk_msg(dev, ar5523_cmd_tx_pipe(dev), + txblock, sizeof(*txblock), &foolen, + AR5523_CMD_TIMEOUT); + if (error) { + dev_err(&dev->dev, + "could not send firmware block info\n"); + goto out_free_fwbuf; + } + + /* send firmware block data */ + memcpy(fwbuf, fw->data + offset, mlen); + error = usb_bulk_msg(dev, ar5523_data_tx_pipe(dev), + fwbuf, mlen, &foolen, + AR5523_DATA_TIMEOUT); + if (error) { + dev_err(&dev->dev, + "could not send firmware block data\n"); + goto out_free_fwbuf; + } + + /* wait for ack from firmware */ + error = usb_bulk_msg(dev, ar5523_cmd_rx_pipe(dev), + rxblock, sizeof(*rxblock), &foolen, + AR5523_CMD_TIMEOUT); + if (error) { + dev_err(&dev->dev, + "could not read firmware answer\n"); + goto out_free_fwbuf; + } + + len -= mlen; + offset += mlen; + } + + /* + * Set the error to -ENXIO to make sure we continue probing for + * a driver. + */ + error = -ENXIO; + + out_free_fwbuf: + kfree(fwbuf); + out_free_rxblock: + kfree(rxblock); + out_free_txblock: + kfree(txblock); + out: + release_firmware(fw); + return error; +} + +static int ar5523_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct ieee80211_hw *hw; + struct ar5523 *ar; + int error = -ENOMEM; + + /* + * Load firmware if the device requires it. This will return + * -ENXIO on success and we'll get called back afer the usb + * id changes to indicate that the firmware is present. + */ + if (id->driver_info & AR5523_FLAG_PRE_FIRMWARE) + return ar5523_load_firmware(dev); + + + hw = ieee80211_alloc_hw(sizeof(*ar), &ar5523_ops); + if (!hw) + goto out; + SET_IEEE80211_DEV(hw, &intf->dev); + + ar = hw->priv; + ar->hw = hw; + ar->dev = dev; + mutex_init(&ar->mutex); + + INIT_DELAYED_WORK(&ar->stat_work, ar5523_stat_work); + init_timer(&ar->tx_wd_timer); + setup_timer(&ar->tx_wd_timer, ar5523_tx_wd_timer, (unsigned long) ar); + INIT_WORK(&ar->tx_wd_work, ar5523_tx_wd_work); + INIT_WORK(&ar->tx_work, ar5523_tx_work); + INIT_LIST_HEAD(&ar->tx_queue_pending); + INIT_LIST_HEAD(&ar->tx_queue_submitted); + spin_lock_init(&ar->tx_data_list_lock); + atomic_set(&ar->tx_nr_total, 0); + atomic_set(&ar->tx_nr_pending, 0); + init_waitqueue_head(&ar->tx_flush_waitq); + + atomic_set(&ar->rx_data_free_cnt, 0); + INIT_WORK(&ar->rx_refill_work, ar5523_rx_refill_work); + INIT_LIST_HEAD(&ar->rx_data_free); + INIT_LIST_HEAD(&ar->rx_data_used); + spin_lock_init(&ar->rx_data_list_lock); + + ar->wq = create_singlethread_workqueue("ar5523"); + if (!ar->wq) { + ar5523_err(ar, "Could not create wq\n"); + goto out_free_ar; + } + + error = ar5523_alloc_rx_bufs(ar); + if (error) { + ar5523_err(ar, "Could not allocate rx buffers\n"); + goto out_free_wq; + } + + error = ar5523_alloc_rx_cmd(ar); + if (error) { + ar5523_err(ar, "Could not allocate rx command buffers\n"); + goto out_free_rx_bufs; + } + + error = ar5523_alloc_tx_cmd(ar); + if (error) { + ar5523_err(ar, "Could not allocate tx command buffers\n"); + goto out_free_rx_cmd; + } + + error = ar5523_submit_rx_cmd(ar); + if (error) { + ar5523_err(ar, "Failed to submit rx cmd\n"); + goto out_free_tx_cmd; + } + + /* + * We're now ready to send/receive firmware commands. + */ + error = ar5523_host_available(ar); + if (error) { + ar5523_err(ar, "could not initialize adapter\n"); + goto out_cancel_rx_cmd; + } + + error = ar5523_get_max_rxsz(ar); + if (error) { + ar5523_err(ar, "could not get caps from adapter\n"); + goto out_cancel_rx_cmd; + } + + error = ar5523_get_devcap(ar); + if (error) { + ar5523_err(ar, "could not get caps from adapter\n"); + goto out_cancel_rx_cmd; + } + + error = ar5523_get_devstatus(ar); + if (error != 0) { + ar5523_err(ar, "could not get device status\n"); + goto out_cancel_rx_cmd; + } + + ar5523_info(ar, "MAC/BBP AR5523, RF AR%c112\n", + (id->driver_info & AR5523_FLAG_ABG) ? '5' : '2'); + + ar->vif = NULL; + hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_HAS_RATE_CONTROL; + hw->extra_tx_headroom = sizeof(struct ar5523_tx_desc) + + sizeof(struct ar5523_chunk); + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + hw->queues = 1; + + error = ar5523_init_modes(ar); + if (error) + goto out_cancel_rx_cmd; + + usb_set_intfdata(intf, hw); + + error = ieee80211_register_hw(hw); + if (error) { + ar5523_err(ar, "could not register device\n"); + goto out_cancel_rx_cmd; + } + + ar5523_info(ar, "Found and initialized AR5523 device\n"); + return 0; + +out_cancel_rx_cmd: + ar5523_cancel_rx_cmd(ar); +out_free_tx_cmd: + ar5523_free_tx_cmd(ar); +out_free_rx_cmd: + ar5523_free_rx_cmd(ar); +out_free_rx_bufs: + ar5523_free_rx_bufs(ar); +out_free_wq: + destroy_workqueue(ar->wq); +out_free_ar: + ieee80211_free_hw(hw); +out: + return error; +} + +static void ar5523_disconnect(struct usb_interface *intf) +{ + struct ieee80211_hw *hw = usb_get_intfdata(intf); + struct ar5523 *ar = hw->priv; + + ar5523_dbg(ar, "detaching\n"); + set_bit(AR5523_USB_DISCONNECTED, &ar->flags); + + ieee80211_unregister_hw(hw); + + ar5523_cancel_rx_cmd(ar); + ar5523_free_tx_cmd(ar); + ar5523_free_rx_cmd(ar); + ar5523_free_rx_bufs(ar); + + destroy_workqueue(ar->wq); + + ieee80211_free_hw(hw); + usb_set_intfdata(intf, NULL); +} + +#define AR5523_DEVICE_UG(vendor, device) \ + { USB_DEVICE((vendor), (device)) }, \ + { USB_DEVICE((vendor), (device) + 1), \ + .driver_info = AR5523_FLAG_PRE_FIRMWARE } +#define AR5523_DEVICE_UX(vendor, device) \ + { USB_DEVICE((vendor), (device)), \ + .driver_info = AR5523_FLAG_ABG }, \ + { USB_DEVICE((vendor), (device) + 1), \ + .driver_info = AR5523_FLAG_ABG|AR5523_FLAG_PRE_FIRMWARE } + +static struct usb_device_id ar5523_id_table[] = { + AR5523_DEVICE_UG(0x168c, 0x0001), /* Atheros / AR5523 */ + AR5523_DEVICE_UG(0x0cf3, 0x0001), /* Atheros2 / AR5523_1 */ + AR5523_DEVICE_UG(0x0cf3, 0x0003), /* Atheros2 / AR5523_2 */ + AR5523_DEVICE_UX(0x0cf3, 0x0005), /* Atheros2 / AR5523_3 */ + AR5523_DEVICE_UG(0x0d8e, 0x7801), /* Conceptronic / AR5523_1 */ + AR5523_DEVICE_UX(0x0d8e, 0x7811), /* Conceptronic / AR5523_2 */ + AR5523_DEVICE_UX(0x2001, 0x3a00), /* Dlink / DWLAG132 */ + AR5523_DEVICE_UG(0x2001, 0x3a02), /* Dlink / DWLG132 */ + AR5523_DEVICE_UX(0x2001, 0x3a04), /* Dlink / DWLAG122 */ + AR5523_DEVICE_UG(0x1690, 0x0712), /* Gigaset / AR5523 */ + AR5523_DEVICE_UG(0x1690, 0x0710), /* Gigaset / SMCWUSBTG */ + AR5523_DEVICE_UG(0x129b, 0x160c), /* Gigaset / USB stick 108 + (CyberTAN Technology) */ + AR5523_DEVICE_UG(0x16ab, 0x7801), /* Globalsun / AR5523_1 */ + AR5523_DEVICE_UX(0x16ab, 0x7811), /* Globalsun / AR5523_2 */ + AR5523_DEVICE_UG(0x0d8e, 0x7802), /* Globalsun / AR5523_3 */ + AR5523_DEVICE_UX(0x0846, 0x4300), /* Netgear / WG111U */ + AR5523_DEVICE_UG(0x0846, 0x4250), /* Netgear / WG111T */ + AR5523_DEVICE_UG(0x0846, 0x5f00), /* Netgear / WPN111 */ + AR5523_DEVICE_UG(0x157e, 0x3006), /* Umedia / AR5523_1 */ + AR5523_DEVICE_UX(0x157e, 0x3205), /* Umedia / AR5523_2 */ + AR5523_DEVICE_UG(0x157e, 0x3006), /* Umedia / TEW444UBEU */ + AR5523_DEVICE_UG(0x1435, 0x0826), /* Wistronneweb / AR5523_1 */ + AR5523_DEVICE_UX(0x1435, 0x0828), /* Wistronneweb / AR5523_2 */ + AR5523_DEVICE_UG(0x0cde, 0x0012), /* Zcom / AR5523 */ + AR5523_DEVICE_UG(0x1385, 0x4250), /* Netgear3 / WG111T (2) */ + AR5523_DEVICE_UG(0x1385, 0x5f00), /* Netgear / WPN111 */ + AR5523_DEVICE_UG(0x1385, 0x5f02), /* Netgear / WPN111 */ + { } +}; +MODULE_DEVICE_TABLE(usb, ar5523_id_table); + +static struct usb_driver ar5523_driver = { + .name = "ar5523", + .id_table = ar5523_id_table, + .probe = ar5523_probe, + .disconnect = ar5523_disconnect, +}; + +static int __init ar5523_init(void) +{ + return usb_register(&ar5523_driver); +} + +static void __exit ar5523_exit(void) +{ + usb_deregister(&ar5523_driver); +} + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_FIRMWARE(AR5523_FIRMWARE_FILE); + +module_init(ar5523_init); +module_exit(ar5523_exit); diff --git a/drivers/net/wireless/ath/ar5523/ar5523.h b/drivers/net/wireless/ath/ar5523/ar5523.h new file mode 100644 index 00000000000..6086ba3fb54 --- /dev/null +++ b/drivers/net/wireless/ath/ar5523/ar5523.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2006 Damien Bergamini + * Copyright (c) 2006 Sam Leffler, Errno Consulting + * Copyright (c) 2007 Christoph Hellwig + * Copyright (c) 2008-2009 Weongyo Jeong + * Copyright (c) 2012 Pontus Fuchs + * + * 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. + */ + +#define AR5523_FLAG_PRE_FIRMWARE (1 << 0) +#define AR5523_FLAG_ABG (1 << 1) + +#define AR5523_FIRMWARE_FILE "ar5523.bin" + +#define AR5523_CMD_TX_PIPE 0x01 +#define AR5523_DATA_TX_PIPE 0x02 +#define AR5523_CMD_RX_PIPE 0x81 +#define AR5523_DATA_RX_PIPE 0x82 + +#define ar5523_cmd_tx_pipe(dev) \ + usb_sndbulkpipe((dev), AR5523_CMD_TX_PIPE) +#define ar5523_data_tx_pipe(dev) \ + usb_sndbulkpipe((dev), AR5523_DATA_TX_PIPE) +#define ar5523_cmd_rx_pipe(dev) \ + usb_rcvbulkpipe((dev), AR5523_CMD_RX_PIPE) +#define ar5523_data_rx_pipe(dev) \ + usb_rcvbulkpipe((dev), AR5523_DATA_RX_PIPE) + +#define AR5523_DATA_TIMEOUT 10000 +#define AR5523_CMD_TIMEOUT 1000 + +#define AR5523_TX_DATA_COUNT 8 +#define AR5523_TX_DATA_RESTART_COUNT 2 +#define AR5523_RX_DATA_COUNT 16 +#define AR5523_RX_DATA_REFILL_COUNT 8 + +#define AR5523_CMD_ID 1 +#define AR5523_DATA_ID 2 + +#define AR5523_TX_WD_TIMEOUT (HZ * 2) +#define AR5523_FLUSH_TIMEOUT (HZ * 3) + +enum AR5523_flags { + AR5523_HW_UP, + AR5523_USB_DISCONNECTED, + AR5523_CONNECTED +}; + +struct ar5523_tx_cmd { + struct ar5523 *ar; + struct urb *urb_tx; + void *buf_tx; + void *odata; + int olen; + int flags; + int res; + struct completion done; +}; + +/* This struct is placed in tx_info->driver_data. It must not be larger + * than IEEE80211_TX_INFO_DRIVER_DATA_SIZE. + */ +struct ar5523_tx_data { + struct list_head list; + struct ar5523 *ar; + struct sk_buff *skb; + struct urb *urb; +}; + +struct ar5523_rx_data { + struct list_head list; + struct ar5523 *ar; + struct urb *urb; + struct sk_buff *skb; +}; + +struct ar5523 { + struct usb_device *dev; + struct ieee80211_hw *hw; + + unsigned long flags; + struct mutex mutex; + struct workqueue_struct *wq; + + struct ar5523_tx_cmd tx_cmd; + + struct delayed_work stat_work; + + struct timer_list tx_wd_timer; + struct work_struct tx_wd_work; + struct work_struct tx_work; + struct list_head tx_queue_pending; + struct list_head tx_queue_submitted; + spinlock_t tx_data_list_lock; + wait_queue_head_t tx_flush_waitq; + + /* Queued + Submitted TX frames */ + atomic_t tx_nr_total; + + /* Submitted TX frames */ + atomic_t tx_nr_pending; + + void *rx_cmd_buf; + struct urb *rx_cmd_urb; + + struct ar5523_rx_data rx_data[AR5523_RX_DATA_COUNT]; + spinlock_t rx_data_list_lock; + struct list_head rx_data_free; + struct list_head rx_data_used; + atomic_t rx_data_free_cnt; + + struct work_struct rx_refill_work; + + int rxbufsz; + u8 serial[16]; + + struct ieee80211_channel channels[14]; + struct ieee80211_rate rates[12]; + struct ieee80211_supported_band band; + struct ieee80211_vif *vif; +}; + +/* flags for sending firmware commands */ +#define AR5523_CMD_FLAG_READ (1 << 1) +#define AR5523_CMD_FLAG_MAGIC (1 << 2) + +#define ar5523_dbg(ar, format, arg...) \ + dev_dbg(&(ar)->dev->dev, format, ## arg) + +/* On USB hot-unplug there can be a lot of URBs in flight and they'll all + * fail. Instead of dealing with them in every possible place just surpress + * any messages on USB disconnect. + */ +#define ar5523_err(ar, format, arg...) \ +do { \ + if (!test_bit(AR5523_USB_DISCONNECTED, &ar->flags)) { \ + dev_err(&(ar)->dev->dev, format, ## arg); \ + } \ +} while (0) +#define ar5523_info(ar, format, arg...) \ + dev_info(&(ar)->dev->dev, format, ## arg) diff --git a/drivers/net/wireless/ath/ar5523/ar5523_hw.h b/drivers/net/wireless/ath/ar5523/ar5523_hw.h new file mode 100644 index 00000000000..a0e8bf46031 --- /dev/null +++ b/drivers/net/wireless/ath/ar5523/ar5523_hw.h @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2006 Damien Bergamini + * Copyright (c) 2006 Sam Leffler, Errno Consulting + * Copyright (c) 2007 Christoph Hellwig + * Copyright (c) 2008-2009 Weongyo Jeong + * Copyright (c) 2012 Pontus Fuchs + * + * 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. + */ + +/* all fields are big endian */ +struct ar5523_fwblock { + __be32 flags; +#define AR5523_WRITE_BLOCK (1 << 4) + + __be32 len; +#define AR5523_MAX_FWBLOCK_SIZE 2048 + + __be32 total; + __be32 remain; + __be32 rxtotal; + __be32 pad[123]; +} __packed; + +#define AR5523_MAX_RXCMDSZ 1024 +#define AR5523_MAX_TXCMDSZ 1024 + +struct ar5523_cmd_hdr { + __be32 len; + __be32 code; +/* NB: these are defined for rev 1.5 firmware; rev 1.6 is different */ +/* messages from Host -> Target */ +#define WDCMSG_HOST_AVAILABLE 0x01 +#define WDCMSG_BIND 0x02 +#define WDCMSG_TARGET_RESET 0x03 +#define WDCMSG_TARGET_GET_CAPABILITY 0x04 +#define WDCMSG_TARGET_SET_CONFIG 0x05 +#define WDCMSG_TARGET_GET_STATUS 0x06 +#define WDCMSG_TARGET_GET_STATS 0x07 +#define WDCMSG_TARGET_START 0x08 +#define WDCMSG_TARGET_STOP 0x09 +#define WDCMSG_TARGET_ENABLE 0x0a +#define WDCMSG_TARGET_DISABLE 0x0b +#define WDCMSG_CREATE_CONNECTION 0x0c +#define WDCMSG_UPDATE_CONNECT_ATTR 0x0d +#define WDCMSG_DELETE_CONNECT 0x0e +#define WDCMSG_SEND 0x0f +#define WDCMSG_FLUSH 0x10 +/* messages from Target -> Host */ +#define WDCMSG_STATS_UPDATE 0x11 +#define WDCMSG_BMISS 0x12 +#define WDCMSG_DEVICE_AVAIL 0x13 +#define WDCMSG_SEND_COMPLETE 0x14 +#define WDCMSG_DATA_AVAIL 0x15 +#define WDCMSG_SET_PWR_MODE 0x16 +#define WDCMSG_BMISS_ACK 0x17 +#define WDCMSG_SET_LED_STEADY 0x18 +#define WDCMSG_SET_LED_BLINK 0x19 +/* more messages */ +#define WDCMSG_SETUP_BEACON_DESC 0x1a +#define WDCMSG_BEACON_INIT 0x1b +#define WDCMSG_RESET_KEY_CACHE 0x1c +#define WDCMSG_RESET_KEY_CACHE_ENTRY 0x1d +#define WDCMSG_SET_KEY_CACHE_ENTRY 0x1e +#define WDCMSG_SET_DECOMP_MASK 0x1f +#define WDCMSG_SET_REGULATORY_DOMAIN 0x20 +#define WDCMSG_SET_LED_STATE 0x21 +#define WDCMSG_WRITE_ASSOCID 0x22 +#define WDCMSG_SET_STA_BEACON_TIMERS 0x23 +#define WDCMSG_GET_TSF 0x24 +#define WDCMSG_RESET_TSF 0x25 +#define WDCMSG_SET_ADHOC_MODE 0x26 +#define WDCMSG_SET_BASIC_RATE 0x27 +#define WDCMSG_MIB_CONTROL 0x28 +#define WDCMSG_GET_CHANNEL_DATA 0x29 +#define WDCMSG_GET_CUR_RSSI 0x2a +#define WDCMSG_SET_ANTENNA_SWITCH 0x2b +#define WDCMSG_USE_SHORT_SLOT_TIME 0x2f +#define WDCMSG_SET_POWER_MODE 0x30 +#define WDCMSG_SETUP_PSPOLL_DESC 0x31 +#define WDCMSG_SET_RX_MULTICAST_FILTER 0x32 +#define WDCMSG_RX_FILTER 0x33 +#define WDCMSG_PER_CALIBRATION 0x34 +#define WDCMSG_RESET 0x35 +#define WDCMSG_DISABLE 0x36 +#define WDCMSG_PHY_DISABLE 0x37 +#define WDCMSG_SET_TX_POWER_LIMIT 0x38 +#define WDCMSG_SET_TX_QUEUE_PARAMS 0x39 +#define WDCMSG_SETUP_TX_QUEUE 0x3a +#define WDCMSG_RELEASE_TX_QUEUE 0x3b +#define WDCMSG_SET_DEFAULT_KEY 0x43 + + __u32 priv; /* driver private data, + don't care about endianess */ + __be32 magic; + __be32 reserved2[4]; +}; + +struct ar5523_cmd_host_available { + __be32 sw_ver_major; + __be32 sw_ver_minor; + __be32 sw_ver_patch; + __be32 sw_ver_build; +} __packed; + +#define ATH_SW_VER_MAJOR 1 +#define ATH_SW_VER_MINOR 5 +#define ATH_SW_VER_PATCH 0 +#define ATH_SW_VER_BUILD 9999 + +struct ar5523_chunk { + u8 seqnum; /* sequence number for ordering */ + u8 flags; +#define UATH_CFLAGS_FINAL 0x01 /* final chunk of a msg */ +#define UATH_CFLAGS_RXMSG 0x02 /* chunk contains rx completion */ +#define UATH_CFLAGS_DEBUG 0x04 /* for debugging */ + __be16 length; /* chunk size in bytes */ + /* chunk data follows */ +} __packed; + +/* + * Message format for a WDCMSG_DATA_AVAIL message from Target to Host. + */ +struct ar5523_rx_desc { + __be32 len; /* msg length including header */ + __be32 code; /* WDCMSG_DATA_AVAIL */ + __be32 gennum; /* generation number */ + __be32 status; /* start of RECEIVE_INFO */ +#define UATH_STATUS_OK 0 +#define UATH_STATUS_STOP_IN_PROGRESS 1 +#define UATH_STATUS_CRC_ERR 2 +#define UATH_STATUS_PHY_ERR 3 +#define UATH_STATUS_DECRYPT_CRC_ERR 4 +#define UATH_STATUS_DECRYPT_MIC_ERR 5 +#define UATH_STATUS_DECOMP_ERR 6 +#define UATH_STATUS_KEY_ERR 7 +#define UATH_STATUS_ERR 8 + __be32 tstamp_low; /* low-order 32-bits of rx timestamp */ + __be32 tstamp_high; /* high-order 32-bits of rx timestamp */ + __be32 framelen; /* frame length */ + __be32 rate; /* rx rate code */ + __be32 antenna; + __be32 rssi; + __be32 channel; + __be32 phyerror; + __be32 connix; /* key table ix for bss traffic */ + __be32 decrypterror; + __be32 keycachemiss; + __be32 pad; /* XXX? */ +} __packed; + +struct ar5523_tx_desc { + __be32 msglen; + __be32 msgid; /* msg id (supplied by host) */ + __be32 type; /* opcode: WDMSG_SEND or WDCMSG_FLUSH */ + __be32 txqid; /* tx queue id and flags */ +#define UATH_TXQID_MASK 0x0f +#define UATH_TXQID_MINRATE 0x10 /* use min tx rate */ +#define UATH_TXQID_FF 0x20 /* content is fast frame */ + __be32 connid; /* tx connection id */ +#define UATH_ID_INVALID 0xffffffff /* for sending prior to connection */ + __be32 flags; /* non-zero if response desired */ +#define UATH_TX_NOTIFY (1 << 24) /* f/w will send a UATH_NOTIF_TX */ + __be32 buflen; /* payload length */ +} __packed; + + +#define AR5523_ID_BSS 2 +#define AR5523_ID_BROADCAST 0xffffffff + +/* structure for command UATH_CMD_WRITE_MAC */ +struct ar5523_write_mac { + __be32 reg; + __be32 len; + u8 data[32]; +} __packed; + +struct ar5523_cmd_rateset { + __u8 length; +#define AR5523_MAX_NRATES 32 + __u8 set[AR5523_MAX_NRATES]; +}; + +struct ar5523_cmd_set_associd { /* AR5523_WRITE_ASSOCID */ + __be32 defaultrateix; + __be32 associd; + __be32 timoffset; + __be32 turboprime; + __u8 bssid[6]; +} __packed; + +/* structure for command WDCMSG_RESET */ +struct ar5523_cmd_reset { + __be32 flags; /* channel flags */ +#define UATH_CHAN_TURBO 0x0100 +#define UATH_CHAN_CCK 0x0200 +#define UATH_CHAN_OFDM 0x0400 +#define UATH_CHAN_2GHZ 0x1000 +#define UATH_CHAN_5GHZ 0x2000 + __be32 freq; /* channel frequency */ + __be32 maxrdpower; + __be32 cfgctl; + __be32 twiceantennareduction; + __be32 channelchange; + __be32 keeprccontent; +} __packed; + +/* structure for command WDCMSG_SET_BASIC_RATE */ +struct ar5523_cmd_rates { + __be32 connid; + __be32 keeprccontent; + __be32 size; + struct ar5523_cmd_rateset rateset; +} __packed; + +enum { + WLAN_MODE_NONE = 0, + WLAN_MODE_11b, + WLAN_MODE_11a, + WLAN_MODE_11g, + WLAN_MODE_11a_TURBO, + WLAN_MODE_11g_TURBO, + WLAN_MODE_11a_TURBO_PRIME, + WLAN_MODE_11g_TURBO_PRIME, + WLAN_MODE_11a_XR, + WLAN_MODE_11g_XR, +}; + +struct ar5523_cmd_connection_attr { + __be32 longpreambleonly; + struct ar5523_cmd_rateset rateset; + __be32 wlanmode; +} __packed; + +/* structure for command AR5523_CREATE_CONNECTION */ +struct ar5523_cmd_create_connection { + __be32 connid; + __be32 bssid; + __be32 size; + struct ar5523_cmd_connection_attr connattr; +} __packed; + +struct ar5523_cmd_ledsteady { /* WDCMSG_SET_LED_STEADY */ + __be32 lednum; +#define UATH_LED_LINK 0 +#define UATH_LED_ACTIVITY 1 + __be32 ledmode; +#define UATH_LED_OFF 0 +#define UATH_LED_ON 1 +} __packed; + +struct ar5523_cmd_ledblink { /* WDCMSG_SET_LED_BLINK */ + __be32 lednum; + __be32 ledmode; + __be32 blinkrate; + __be32 slowmode; +} __packed; + +struct ar5523_cmd_ledstate { /* WDCMSG_SET_LED_STATE */ + __be32 connected; +} __packed; + +struct ar5523_cmd_txq_attr { + __be32 priority; + __be32 aifs; + __be32 logcwmin; + __be32 logcwmax; + __be32 bursttime; + __be32 mode; + __be32 qflags; +} __packed; + +struct ar5523_cmd_txq_setup { /* WDCMSG_SETUP_TX_QUEUE */ + __be32 qid; + __be32 len; + struct ar5523_cmd_txq_attr attr; +} __packed; + +struct ar5523_cmd_rx_filter { /* WDCMSG_RX_FILTER */ + __be32 bits; +#define UATH_FILTER_RX_UCAST 0x00000001 +#define UATH_FILTER_RX_MCAST 0x00000002 +#define UATH_FILTER_RX_BCAST 0x00000004 +#define UATH_FILTER_RX_CONTROL 0x00000008 +#define UATH_FILTER_RX_BEACON 0x00000010 /* beacon frames */ +#define UATH_FILTER_RX_PROM 0x00000020 /* promiscuous mode */ +#define UATH_FILTER_RX_PHY_ERR 0x00000040 /* phy errors */ +#define UATH_FILTER_RX_PHY_RADAR 0x00000080 /* radar phy errors */ +#define UATH_FILTER_RX_XR_POOL 0x00000400 /* XR group polls */ +#define UATH_FILTER_RX_PROBE_REQ 0x00000800 + __be32 op; +#define UATH_FILTER_OP_INIT 0x0 +#define UATH_FILTER_OP_SET 0x1 +#define UATH_FILTER_OP_CLEAR 0x2 +#define UATH_FILTER_OP_TEMP 0x3 +#define UATH_FILTER_OP_RESTORE 0x4 +} __packed; + +enum { + CFG_NONE, /* Sentinal to indicate "no config" */ + CFG_REG_DOMAIN, /* Regulatory Domain */ + CFG_RATE_CONTROL_ENABLE, + CFG_DEF_XMIT_DATA_RATE, /* NB: if rate control is not enabled */ + CFG_HW_TX_RETRIES, + CFG_SW_TX_RETRIES, + CFG_SLOW_CLOCK_ENABLE, + CFG_COMP_PROC, + CFG_USER_RTS_THRESHOLD, + CFG_XR2NORM_RATE_THRESHOLD, + CFG_XRMODE_SWITCH_COUNT, + CFG_PROTECTION_TYPE, + CFG_BURST_SEQ_THRESHOLD, + CFG_ABOLT, + CFG_IQ_LOG_COUNT_MAX, + CFG_MODE_CTS, + CFG_WME_ENABLED, + CFG_GPRS_CBR_PERIOD, + CFG_SERVICE_TYPE, + /* MAC Address to use. Overrides EEPROM */ + CFG_MAC_ADDR, + CFG_DEBUG_EAR, + CFG_INIT_REGS, + /* An ID for use in error & debug messages */ + CFG_DEBUG_ID, + CFG_COMP_WIN_SZ, + CFG_DIVERSITY_CTL, + CFG_TP_SCALE, + CFG_TPC_HALF_DBM5, + CFG_TPC_HALF_DBM2, + CFG_OVERRD_TX_POWER, + CFG_USE_32KHZ_CLOCK, + CFG_GMODE_PROTECTION, + CFG_GMODE_PROTECT_RATE_INDEX, + CFG_GMODE_NON_ERP_PREAMBLE, + CFG_WDC_TRANSPORT_CHUNK_SIZE, +}; + +enum { + /* Sentinal to indicate "no capability" */ + CAP_NONE, + CAP_ALL, /* ALL capabilities */ + CAP_TARGET_VERSION, + CAP_TARGET_REVISION, + CAP_MAC_VERSION, + CAP_MAC_REVISION, + CAP_PHY_REVISION, + CAP_ANALOG_5GHz_REVISION, + CAP_ANALOG_2GHz_REVISION, + /* Target supports WDC message debug features */ + CAP_DEBUG_WDCMSG_SUPPORT, + + CAP_REG_DOMAIN, + CAP_COUNTRY_CODE, + CAP_REG_CAP_BITS, + + CAP_WIRELESS_MODES, + CAP_CHAN_SPREAD_SUPPORT, + CAP_SLEEP_AFTER_BEACON_BROKEN, + CAP_COMPRESS_SUPPORT, + CAP_BURST_SUPPORT, + CAP_FAST_FRAMES_SUPPORT, + CAP_CHAP_TUNING_SUPPORT, + CAP_TURBOG_SUPPORT, + CAP_TURBO_PRIME_SUPPORT, + CAP_DEVICE_TYPE, + CAP_XR_SUPPORT, + CAP_WME_SUPPORT, + CAP_TOTAL_QUEUES, + CAP_CONNECTION_ID_MAX, /* Should absorb CAP_KEY_CACHE_SIZE */ + + CAP_LOW_5GHZ_CHAN, + CAP_HIGH_5GHZ_CHAN, + CAP_LOW_2GHZ_CHAN, + CAP_HIGH_2GHZ_CHAN, + + CAP_MIC_AES_CCM, + CAP_MIC_CKIP, + CAP_MIC_TKIP, + CAP_MIC_TKIP_WME, + CAP_CIPHER_AES_CCM, + CAP_CIPHER_CKIP, + CAP_CIPHER_TKIP, + + CAP_TWICE_ANTENNAGAIN_5G, + CAP_TWICE_ANTENNAGAIN_2G, +}; + +enum { + ST_NONE, /* Sentinal to indicate "no status" */ + ST_ALL, + ST_SERVICE_TYPE, + ST_WLAN_MODE, + ST_FREQ, + ST_BAND, + ST_LAST_RSSI, + ST_PS_FRAMES_DROPPED, + ST_CACHED_DEF_ANT, + ST_COUNT_OTHER_RX_ANT, + ST_USE_FAST_DIVERSITY, + ST_MAC_ADDR, + ST_RX_GENERATION_NUM, + ST_TX_QUEUE_DEPTH, + ST_SERIAL_NUMBER, + ST_WDC_TRANSPORT_CHUNK_SIZE, +}; + +enum { + TARGET_DEVICE_AWAKE, + TARGET_DEVICE_SLEEP, + TARGET_DEVICE_PWRDN, + TARGET_DEVICE_PWRSAVE, + TARGET_DEVICE_SUSPEND, + TARGET_DEVICE_RESUME, +}; + +/* this is in net/ieee80211.h, but that conflicts with the mac80211 headers */ +#define IEEE80211_2ADDR_LEN 16 + +#define AR5523_MIN_RXBUFSZ \ + (((sizeof(__be32) + IEEE80211_2ADDR_LEN + \ + sizeof(struct ar5523_rx_desc)) + 3) & ~3) -- cgit v1.2.3-70-g09d2 From 1680260226a8fd2aab590319da83ad8e610da9bd Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Thu, 25 Oct 2012 17:11:31 +0530 Subject: ath9k_hw: Enable hw PLL power save for AR9462 This reduced the power consumption to half in full and network sleep. Cc: stable@vger.kernel.org Cc: Paul Stewart Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_hw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index 1a36fa26263..0a6b7a3385d 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -219,10 +219,10 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) /* Awake -> Sleep Setting */ INIT_INI_ARRAY(&ah->iniPcieSerdes, - ar9462_pciephy_pll_on_clkreq_disable_L1_2p0); + ar9462_pciephy_clkreq_disable_L1_2p0); /* Sleep -> Awake Setting */ INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, - ar9462_pciephy_pll_on_clkreq_disable_L1_2p0); + ar9462_pciephy_clkreq_disable_L1_2p0); /* Fast clock modal settings */ INIT_INI_ARRAY(&ah->iniModesFastClock, -- cgit v1.2.3-70-g09d2 From 844648423dcf33fed96a4eb65f783f867efbe267 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Thu, 25 Oct 2012 17:16:51 +0530 Subject: ath9k_hw: Enable hw PLL power save for AR9565 This reduced the power consumption to half in full and network sleep. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_hw.c | 4 ++-- drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index 0a6b7a3385d..0693cd95b74 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -328,9 +328,9 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) ar9565_1p0_Modes_lowest_ob_db_tx_gain_table); INIT_INI_ARRAY(&ah->iniPcieSerdes, - ar9565_1p0_pciephy_pll_on_clkreq_disable_L1); + ar9565_1p0_pciephy_clkreq_disable_L1); INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, - ar9565_1p0_pciephy_pll_on_clkreq_disable_L1); + ar9565_1p0_pciephy_clkreq_disable_L1); INIT_INI_ARRAY(&ah->iniModesFastClock, ar9565_1p0_modes_fast_clock); diff --git a/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h index 843e79f67ff..0c2ac0c6dc8 100644 --- a/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h @@ -768,9 +768,9 @@ static const u32 ar9565_1p0_Modes_lowest_ob_db_tx_gain_table[][5] = { {0x00016054, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, }; -static const u32 ar9565_1p0_pciephy_pll_on_clkreq_disable_L1[][2] = { +static const u32 ar9565_1p0_pciephy_clkreq_disable_L1[][2] = { /* Addr allmodes */ - {0x00018c00, 0x18212ede}, + {0x00018c00, 0x18213ede}, {0x00018c04, 0x000801d8}, {0x00018c08, 0x0003780c}, }; -- cgit v1.2.3-70-g09d2 From e75d4ed6a9565fcccd579316b0fd933d2191f513 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Thu, 25 Oct 2012 17:16:52 +0530 Subject: ath9k_hw: Fix concurrent tx on lower tx power Whenever WLAN receives scheduling msg from BT, it reduces tx power based on RSSI level. And then BT starts simultaneous transmission along with WLAN. Sometimes HW MAC compares tx power that is used prior to power reduction which is causing BT transmission to defer. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 5 ++++- drivers/net/wireless/ath/ath9k/reg.h | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index b04fa462282..42b4412d679 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -881,9 +881,12 @@ int ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g, REG_RMW_FIELD(ah, AR_BTCOEX_CTRL3, AR_BTCOEX_CTRL3_CONT_INFO_TIMEOUT, 20); - REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, AR_BTCOEX_CTRL2_RX_DEWEIGHT, 1); + REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, AR_BTCOEX_CTRL2_RX_DEWEIGHT, 0); REG_RMW_FIELD(ah, AR_PCU_MISC, AR_PCU_BT_ANT_PREVENT_RX, 0); + /* Set the time out to 3.125ms (5 BT slots) */ + REG_RMW_FIELD(ah, AR_BTCOEX_WL_LNA, AR_BTCOEX_WL_LNA_TIMEOUT, 0x3D090); + /* concurrent tx priority */ if (mci->config & ATH_MCI_CONFIG_CONCUR_TX) { REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index e9dec138b91..ad3c82c0917 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -2311,6 +2311,8 @@ enum { #define AR_BTCOEX_MAX_TXPWR(_x) (0x18c0 + ((_x) << 2)) #define AR_BTCOEX_WL_LNA 0x1940 #define AR_BTCOEX_RFGAIN_CTRL 0x1944 +#define AR_BTCOEX_WL_LNA_TIMEOUT 0x003FFFFF +#define AR_BTCOEX_WL_LNA_TIMEOUT_S 0 #define AR_BTCOEX_CTRL2 0x1948 #define AR_BTCOEX_CTRL2_TXPWR_THRESH 0x0007F800 -- cgit v1.2.3-70-g09d2 From cdbe408da76d5cc294edb013850cc3a972d80968 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Thu, 25 Oct 2012 17:16:53 +0530 Subject: ath9k_hw: validate MCI stuck after RTC wakeup Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 8e1559aba49..71cd9f0c96a 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2153,9 +2153,6 @@ static bool ath9k_hw_set_power_awake(struct ath_hw *ah) AR_RTC_FORCE_WAKE_EN); udelay(50); - if (ath9k_hw_mci_is_enabled(ah)) - ar9003_mci_set_power_awake(ah); - for (i = POWER_UP_TIME / 50; i > 0; i--) { val = REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M; if (val == AR_RTC_STATUS_ON) @@ -2171,6 +2168,9 @@ static bool ath9k_hw_set_power_awake(struct ath_hw *ah) return false; } + if (ath9k_hw_mci_is_enabled(ah)) + ar9003_mci_set_power_awake(ah); + REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); return true; -- cgit v1.2.3-70-g09d2 From 4df50ca869890581020b95958251bd355c1dc6b1 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Thu, 25 Oct 2012 17:16:54 +0530 Subject: ath9k: Dump BTCOEX tuning parameters Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 6 +++++ drivers/net/wireless/ath/ath9k/debug.c | 34 +++++++++++++++++++++++- drivers/net/wireless/ath/ath9k/gpio.c | 48 ++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 18dfb764eed..4e125d8904a 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -494,6 +494,7 @@ void ath9k_btcoex_timer_pause(struct ath_softc *sc); void ath9k_btcoex_handle_interrupt(struct ath_softc *sc, u32 status); u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen); void ath9k_btcoex_stop_gen_timer(struct ath_softc *sc); +int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 len, u32 size); #else static inline int ath9k_init_btcoex(struct ath_softc *sc) { @@ -520,6 +521,11 @@ static inline u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, static inline void ath9k_btcoex_stop_gen_timer(struct ath_softc *sc) { } +static inline int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, + u32 len, u32 size) +{ + return 0; +} #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ struct ath9k_wow_pattern { diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 6727b566d29..a8be94b2a53 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -1586,6 +1586,35 @@ static const struct file_operations fops_samps = { #endif +#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT +static ssize_t read_file_btcoex(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + u32 len = 0, size = 1500; + char *buf; + size_t retval; + + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + len = ath9k_dump_btcoex(sc, buf, len, size); + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; +} + +static const struct file_operations fops_btcoex = { + .read = read_file_btcoex, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; +#endif + int ath9k_init_debug(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); @@ -1658,6 +1687,9 @@ int ath9k_init_debug(struct ath_hw *ah) sc->debug.debugfs_phy, &sc->sc_ah->gpio_val); debugfs_create_file("diversity", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_ant_diversity); - +#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT + debugfs_create_file("btcoex", S_IRUSR, sc->debug.debugfs_phy, sc, + &fops_btcoex); +#endif return 0; } diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index 64dde1cd20e..a8ea57b9f49 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -494,4 +494,52 @@ int ath9k_init_btcoex(struct ath_softc *sc) return 0; } +int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 len, u32 size) +{ +#define ATH_DUMP_BTCOEX(_s, _val) \ + do { \ + len += snprintf(buf + len, size - len, \ + "%20s : %10d\n", _s, (_val)); \ + } while (0) + + struct ath_btcoex *btcoex = &sc->btcoex; + struct ath_mci_profile *mci = &btcoex->mci; + struct ath_hw *ah = sc->sc_ah; + struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; + int i; + + ATH_DUMP_BTCOEX("Total BT profiles", NUM_PROF(mci)); + ATH_DUMP_BTCOEX("Number of MGMT", mci->num_mgmt); + ATH_DUMP_BTCOEX("Number of SCO", mci->num_sco); + ATH_DUMP_BTCOEX("Number of A2DP", mci->num_a2dp); + ATH_DUMP_BTCOEX("Number of HID", mci->num_hid); + ATH_DUMP_BTCOEX("Number of PAN", mci->num_pan); + ATH_DUMP_BTCOEX("Number of ACL", mci->num_other_acl); + ATH_DUMP_BTCOEX("Number of BDR", mci->num_bdr); + ATH_DUMP_BTCOEX("Aggr. Limit", mci->aggr_limit); + ATH_DUMP_BTCOEX("Stomp Type", btcoex->bt_stomp_type); + ATH_DUMP_BTCOEX("BTCoex Period (msec)", btcoex->btcoex_period); + ATH_DUMP_BTCOEX("Duty Cycle", btcoex->duty_cycle); + ATH_DUMP_BTCOEX("BT Wait time", btcoex->bt_wait_time); + ATH_DUMP_BTCOEX("Concurrent Tx", btcoex_hw->mci.concur_tx); + ATH_DUMP_BTCOEX("Concur RSSI count", btcoex->rssi_count); + len += snprintf(buf + len, size - len, "BT Weights: "); + for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++) + len += snprintf(buf + len, size - len, "%08x ", + btcoex_hw->bt_weight[i]); + len += snprintf(buf + len, size - len, "\n"); + len += snprintf(buf + len, size - len, "WLAN Weights: "); + for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++) + len += snprintf(buf + len, size - len, "%08x ", + btcoex_hw->wlan_weight[i]); + len += snprintf(buf + len, size - len, "\n"); + len += snprintf(buf + len, size - len, "Tx Priorities: "); + for (i = 0; i < ATH_BTCOEX_STOMP_MAX; i++) + len += snprintf(buf + len, size - len, "%08x ", + btcoex_hw->tx_prio[i]); + len += snprintf(buf + len, size - len, "\n"); +#undef ATH_DUMP_BTCOEX + + return len; +} #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ -- cgit v1.2.3-70-g09d2 From c8442118ad9cd05cfe3b993f058e70ab25b1009a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 24 Oct 2012 10:17:18 +0200 Subject: cfg80211: allow per interface TX power setting The TX power setting is currently per wiphy (hardware device) but with multi-channel capabilities that doesn't make much sense any more. Allow drivers (and mac80211) to advertise support for per-interface TX power configuration. When the TX power is configured for the wiphy, the wdev will be NULL and the driver can still handle that, but when a wdev is given the TX power can be set only for that wdev now. Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 9 ++++---- .../net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 6 ++++-- drivers/net/wireless/mwifiex/cfg80211.c | 1 + drivers/net/wireless/rndis_wlan.c | 10 +++++++-- include/net/cfg80211.h | 10 ++++++--- include/uapi/linux/nl80211.h | 2 ++ net/mac80211/cfg.c | 5 ++++- net/wireless/nl80211.c | 6 +++++- net/wireless/rdev-ops.h | 11 +++++----- net/wireless/trace.h | 24 ++++++++++++---------- net/wireless/wext-compat.c | 4 ++-- 11 files changed, 56 insertions(+), 32 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 277089963eb..d615f9f7506 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1384,11 +1384,8 @@ static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) return 0; } -/* - * The type nl80211_tx_power_setting replaces the following - * data type from 2.6.36 onwards -*/ static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy, + struct wireless_dev *wdev, enum nl80211_tx_power_setting type, int mbm) { @@ -1423,7 +1420,9 @@ static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy, return 0; } -static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm) +static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, + struct wireless_dev *wdev, + int *dbm) { struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); struct ath6kl_vif *vif; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index cb30feaa565..904c94121c1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -1721,7 +1721,7 @@ brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev, } static s32 -brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, +brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, enum nl80211_tx_power_setting type, s32 mbm) { @@ -1770,7 +1770,9 @@ done: return err; } -static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm) +static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, + s32 *dbm) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index fdb1eb86102..8e829b251d8 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -324,6 +324,7 @@ mwifiex_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, */ static int mwifiex_cfg80211_set_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, enum nl80211_tx_power_setting type, int mbm) { diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index bd1f0cb5608..5390af36c06 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -490,9 +490,12 @@ static int rndis_scan(struct wiphy *wiphy, static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed); static int rndis_set_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, enum nl80211_tx_power_setting type, int mbm); -static int rndis_get_tx_power(struct wiphy *wiphy, int *dbm); +static int rndis_get_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, + int *dbm); static int rndis_connect(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme); @@ -1903,6 +1906,7 @@ static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed) } static int rndis_set_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, enum nl80211_tx_power_setting type, int mbm) { @@ -1930,7 +1934,9 @@ static int rndis_set_tx_power(struct wiphy *wiphy, return -ENOTSUPP; } -static int rndis_get_tx_power(struct wiphy *wiphy, int *dbm) +static int rndis_get_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, + int *dbm) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c6964572890..8034a4268fc 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1551,7 +1551,10 @@ struct cfg80211_gtk_rekey_data { * struct wiphy. If returning an error, no value should be changed. * * @set_tx_power: set the transmit power according to the parameters, - * the power passed is in mBm, to get dBm use MBM_TO_DBM(). + * the power passed is in mBm, to get dBm use MBM_TO_DBM(). The + * wdev may be %NULL if power was set for the wiphy, and will + * always be %NULL unless the driver supports per-vif TX power + * (as advertised by the nl80211 feature flag.) * @get_tx_power: store the current TX power into the dbm variable; * return 0 if successful * @@ -1748,9 +1751,10 @@ struct cfg80211_ops { int (*set_wiphy_params)(struct wiphy *wiphy, u32 changed); - int (*set_tx_power)(struct wiphy *wiphy, + int (*set_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev, enum nl80211_tx_power_setting type, int mbm); - int (*get_tx_power)(struct wiphy *wiphy, int *dbm); + int (*get_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev, + int *dbm); int (*set_wds_peer)(struct wiphy *wiphy, struct net_device *dev, const u8 *addr); diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 617d0fbfc96..4c5f6748ed7 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3051,6 +3051,7 @@ enum nl80211_ap_sme_features { * @NL80211_FEATURE_LOW_PRIORITY_SCAN: This driver supports low priority scan * @NL80211_FEATURE_SCAN_FLUSH: Scan flush is supported * @NL80211_FEATURE_AP_SCAN: Support scanning using an AP vif + * @NL80211_FEATURE_VIF_TXPOWER: The driver supports per-vif TX power setting */ enum nl80211_feature_flags { NL80211_FEATURE_SK_TX_STATUS = 1 << 0, @@ -3062,6 +3063,7 @@ enum nl80211_feature_flags { NL80211_FEATURE_LOW_PRIORITY_SCAN = 1 << 6, NL80211_FEATURE_SCAN_FLUSH = 1 << 7, NL80211_FEATURE_AP_SCAN = 1 << 8, + NL80211_FEATURE_VIF_TXPOWER = 1 << 9, }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 34fd3eba309..a352e4d22dd 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1992,6 +1992,7 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) } static int ieee80211_set_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, enum nl80211_tx_power_setting type, int mbm) { struct ieee80211_local *local = wiphy_priv(wiphy); @@ -2026,7 +2027,9 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy, return 0; } -static int ieee80211_get_tx_power(struct wiphy *wiphy, int *dbm) +static int ieee80211_get_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, + int *dbm) { struct ieee80211_local *local = wiphy_priv(wiphy); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 879ca620fd6..87d4670ee53 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1585,9 +1585,13 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) } if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) { + struct wireless_dev *txp_wdev = wdev; enum nl80211_tx_power_setting type; int idx, mbm = 0; + if (!(rdev->wiphy.features & NL80211_FEATURE_VIF_TXPOWER)) + txp_wdev = NULL; + if (!rdev->ops->set_tx_power) { result = -EOPNOTSUPP; goto bad_res; @@ -1607,7 +1611,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) mbm = nla_get_u32(info->attrs[idx]); } - result = rdev_set_tx_power(rdev, type, mbm); + result = rdev_set_tx_power(rdev, txp_wdev, type, mbm); if (result) goto bad_res; } diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index eb5f8974e14..6e5fa659068 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -476,21 +476,22 @@ rdev_set_wiphy_params(struct cfg80211_registered_device *rdev, u32 changed) } static inline int rdev_set_tx_power(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, enum nl80211_tx_power_setting type, int mbm) { int ret; - trace_rdev_set_tx_power(&rdev->wiphy, type, mbm); - ret = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm); + trace_rdev_set_tx_power(&rdev->wiphy, wdev, type, mbm); + ret = rdev->ops->set_tx_power(&rdev->wiphy, wdev, type, mbm); trace_rdev_return_int(&rdev->wiphy, ret); return ret; } static inline int rdev_get_tx_power(struct cfg80211_registered_device *rdev, - int *dbm) + struct wireless_dev *wdev, int *dbm) { int ret; - trace_rdev_get_tx_power(&rdev->wiphy); - ret = rdev->ops->get_tx_power(&rdev->wiphy, dbm); + trace_rdev_get_tx_power(&rdev->wiphy, wdev); + ret = rdev->ops->get_tx_power(&rdev->wiphy, wdev, dbm); trace_rdev_return_int_int(&rdev->wiphy, ret, *dbm); return ret; } diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 0ca71caf85f..8e03c6382a8 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -26,7 +26,7 @@ #define WIPHY_PR_ARG MAC_PR_ARG(wiphy_mac) #define WDEV_ENTRY __field(u32, id) -#define WDEV_ASSIGN (__entry->id) = (wdev->identifier) +#define WDEV_ASSIGN (__entry->id) = (wdev ? wdev->identifier : 0) #define WDEV_PR_FMT ", wdev id: %u" #define WDEV_PR_ARG (__entry->id) @@ -260,11 +260,6 @@ DEFINE_EVENT(wiphy_only_evt, rdev_get_antenna, TP_ARGS(wiphy) ); -DEFINE_EVENT(wiphy_only_evt, rdev_get_tx_power, - TP_PROTO(struct wiphy *wiphy), - TP_ARGS(wiphy) -); - DEFINE_EVENT(wiphy_only_evt, rdev_rfkill_poll, TP_PROTO(struct wiphy *wiphy), TP_ARGS(wiphy) @@ -1230,22 +1225,29 @@ TRACE_EVENT(rdev_set_wiphy_params, WIPHY_PR_ARG, __entry->changed) ); +DEFINE_EVENT(wiphy_wdev_evt, rdev_get_tx_power, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), + TP_ARGS(wiphy, wdev) +); + TRACE_EVENT(rdev_set_tx_power, - TP_PROTO(struct wiphy *wiphy, enum nl80211_tx_power_setting type, - int mbm), - TP_ARGS(wiphy, type, mbm), + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, + enum nl80211_tx_power_setting type, int mbm), + TP_ARGS(wiphy, wdev, type, mbm), TP_STRUCT__entry( WIPHY_ENTRY + WDEV_ENTRY __field(enum nl80211_tx_power_setting, type) __field(int, mbm) ), TP_fast_assign( WIPHY_ASSIGN; + WDEV_ASSIGN; __entry->type = type; __entry->mbm = mbm; ), - TP_printk(WIPHY_PR_FMT ", type: %d, mbm: %d", - WIPHY_PR_ARG, __entry->type, __entry->mbm) + TP_printk(WIPHY_PR_FMT WDEV_PR_FMT ", type: %d, mbm: %d", + WIPHY_PR_ARG, WDEV_PR_ARG,__entry->type, __entry->mbm) ); TRACE_EVENT(rdev_return_int_int, diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 6488d2dbc1d..742ab6ec4c9 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -895,7 +895,7 @@ static int cfg80211_wext_siwtxpower(struct net_device *dev, return 0; } - return rdev_set_tx_power(rdev, type, DBM_TO_MBM(dbm)); + return rdev_set_tx_power(rdev, wdev, type, DBM_TO_MBM(dbm)); } static int cfg80211_wext_giwtxpower(struct net_device *dev, @@ -914,7 +914,7 @@ static int cfg80211_wext_giwtxpower(struct net_device *dev, if (!rdev->ops->get_tx_power) return -EOPNOTSUPP; - err = rdev_get_tx_power(rdev, &val); + err = rdev_get_tx_power(rdev, wdev, &val); if (err) return err; -- cgit v1.2.3-70-g09d2 From 38141fcfaad34a388e61a7a7e98d521330e049d6 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 30 Oct 2012 21:04:26 +0300 Subject: ar5523: make buffer size variable unsigned A negative buffer size doesn't make sense and it breaks this check in ar5523_get_max_rxsz(): if (!ar->rxbufsz || ar->rxbufsz > AR5523_SANE_RXBUFSZ) { ... Signed-off-by: Dan Carpenter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ar5523/ar5523.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ar5523/ar5523.h b/drivers/net/wireless/ath/ar5523/ar5523.h index 6086ba3fb54..00c6fd346d4 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523.h +++ b/drivers/net/wireless/ath/ar5523/ar5523.h @@ -122,7 +122,7 @@ struct ar5523 { struct work_struct rx_refill_work; - int rxbufsz; + unsigned int rxbufsz; u8 serial[16]; struct ieee80211_channel channels[14]; -- cgit v1.2.3-70-g09d2 From 8b2c98243e8d00f9c6b6059976d6de51491ee0c7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Nov 2012 20:23:30 +0100 Subject: mac80211: clarify interface iteration and make it configurable During hardware restart, all interfaces are iterated even though they haven't been re-added to the driver, document this behaviour. The same also happens during resume, which is even more confusing since all of the interfaces were previously removed from the driver. Make this optional so drivers relying on the current behaviour can still use it, but to let drivers that don't want this behaviour disable it. Also convert all API users, keeping the old semantics except in hwsim, where the new normal ones are desired. Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath5k/base.c | 10 ++++++---- drivers/net/wireless/ath/ath5k/mac80211-ops.c | 5 +++-- drivers/net/wireless/ath/ath9k/htc_drv_beacon.c | 6 +++--- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 20 ++++++++++++-------- drivers/net/wireless/ath/ath9k/main.c | 15 +++++++++------ drivers/net/wireless/mac80211_hwsim.c | 11 ++++++++--- drivers/net/wireless/rt2x00/rt2x00dev.c | 19 ++++++++++--------- drivers/net/wireless/rt2x00/rt2x00mac.c | 6 +++--- drivers/net/wireless/ti/wlcore/main.c | 2 +- include/net/mac80211.h | 23 +++++++++++++++++++++-- net/mac80211/util.c | 18 ++++++++++++++---- 11 files changed, 90 insertions(+), 45 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 9f31cfa56cc..cdd19232960 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -511,8 +511,9 @@ ath5k_update_bssid_mask_and_opmode(struct ath5k_hw *ah, ath5k_vif_iter(&iter_data, vif->addr, vif); /* Get list of all active MAC addresses */ - ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter, - &iter_data); + ieee80211_iterate_active_interfaces_atomic( + ah->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath5k_vif_iter, &iter_data); memcpy(ah->bssidmask, iter_data.mask, ETH_ALEN); ah->opmode = iter_data.opmode; @@ -3045,8 +3046,9 @@ ath5k_any_vif_assoc(struct ath5k_hw *ah) iter_data.need_set_hw_addr = false; iter_data.found_active = true; - ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter, - &iter_data); + ieee80211_iterate_active_interfaces_atomic( + ah->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath5k_vif_iter, &iter_data); return iter_data.any_assoc; } diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index 7a28538e6e0..1ea8c8795c8 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -452,8 +452,9 @@ ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, iter_data.hw_macaddr = NULL; iter_data.n_stas = 0; iter_data.need_set_hw_addr = false; - ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter, - &iter_data); + ieee80211_iterate_active_interfaces_atomic( + ah->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath5k_vif_iter, &iter_data); /* Set up RX Filter */ if (iter_data.n_stas > 1) { diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c index f42d2eb6af9..1318d79f5c4 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c @@ -587,9 +587,9 @@ static bool ath9k_htc_check_beacon_config(struct ath9k_htc_priv *priv, (priv->num_sta_vif > 1) && (vif->type == NL80211_IFTYPE_STATION)) { beacon_configured = false; - ieee80211_iterate_active_interfaces_atomic(priv->hw, - ath9k_htc_beacon_iter, - &beacon_configured); + ieee80211_iterate_active_interfaces_atomic( + priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath9k_htc_beacon_iter, &beacon_configured); if (beacon_configured) { ath_dbg(common, CONFIG, diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 66f6a74c508..02cce95331d 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -127,8 +127,9 @@ static void ath9k_htc_vif_reconfig(struct ath9k_htc_priv *priv) priv->rearm_ani = false; priv->reconfig_beacon = false; - ieee80211_iterate_active_interfaces_atomic(priv->hw, - ath9k_htc_vif_iter, priv); + ieee80211_iterate_active_interfaces_atomic( + priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath9k_htc_vif_iter, priv); if (priv->rearm_ani) ath9k_htc_start_ani(priv); @@ -165,8 +166,9 @@ static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv, ath9k_htc_bssid_iter(&iter_data, vif->addr, vif); /* Get list of all active MAC addresses */ - ieee80211_iterate_active_interfaces_atomic(priv->hw, ath9k_htc_bssid_iter, - &iter_data); + ieee80211_iterate_active_interfaces_atomic( + priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath9k_htc_bssid_iter, &iter_data); memcpy(common->bssidmask, iter_data.mask, ETH_ALEN); ath_hw_setbssidmask(common); @@ -1144,8 +1146,9 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw, */ if ((vif->type == NL80211_IFTYPE_AP) && (priv->num_ap_vif == 0)) { priv->rearm_ani = false; - ieee80211_iterate_active_interfaces_atomic(priv->hw, - ath9k_htc_vif_iter, priv); + ieee80211_iterate_active_interfaces_atomic( + priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath9k_htc_vif_iter, priv); if (!priv->rearm_ani) ath9k_htc_stop_ani(priv); } @@ -1466,8 +1469,9 @@ static void ath9k_htc_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif) static void ath9k_htc_choose_set_bssid(struct ath9k_htc_priv *priv) { if (priv->num_sta_assoc_vif == 1) { - ieee80211_iterate_active_interfaces_atomic(priv->hw, - ath9k_htc_bss_iter, priv); + ieee80211_iterate_active_interfaces_atomic( + priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath9k_htc_bss_iter, priv); ath9k_htc_set_bssid(priv); } } diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 578a7234aa5..c084532291a 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -924,8 +924,9 @@ void ath9k_calculate_iter_data(struct ieee80211_hw *hw, ath9k_vif_iter(iter_data, vif->addr, vif); /* Get list of all active MAC addresses */ - ieee80211_iterate_active_interfaces_atomic(sc->hw, ath9k_vif_iter, - iter_data); + ieee80211_iterate_active_interfaces_atomic( + sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath9k_vif_iter, iter_data); } /* Called with sc->mutex held. */ @@ -975,8 +976,9 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw, if (ah->opmode == NL80211_IFTYPE_STATION && old_opmode == NL80211_IFTYPE_AP && test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) { - ieee80211_iterate_active_interfaces_atomic(sc->hw, - ath9k_sta_vif_iter, sc); + ieee80211_iterate_active_interfaces_atomic( + sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath9k_sta_vif_iter, sc); } } @@ -1505,8 +1507,9 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, clear_bit(SC_OP_BEACONS, &sc->sc_flags); } - ieee80211_iterate_active_interfaces_atomic(sc->hw, - ath9k_bss_assoc_iter, sc); + ieee80211_iterate_active_interfaces_atomic( + sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath9k_bss_assoc_iter, sc); if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags) && ah->opmode == NL80211_IFTYPE_STATION) { diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 9b4f76718db..a8ec7086ad0 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -571,6 +571,7 @@ static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data, md.ret = false; md.addr = addr; ieee80211_iterate_active_interfaces_atomic(data->hw, + IEEE80211_IFACE_ITER_NORMAL, mac80211_hwsim_addr_iter, &md); @@ -744,8 +745,8 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, if (!hwsim_chans_compat(chan, data2->tmp_chan) && !hwsim_chans_compat(chan, data2->channel)) { ieee80211_iterate_active_interfaces_atomic( - data2->hw, mac80211_hwsim_tx_iter, - &tx_iter_data); + data2->hw, IEEE80211_IFACE_ITER_NORMAL, + mac80211_hwsim_tx_iter, &tx_iter_data); if (!tx_iter_data.receive) continue; } @@ -958,7 +959,8 @@ static void mac80211_hwsim_beacon(unsigned long arg) return; ieee80211_iterate_active_interfaces_atomic( - hw, mac80211_hwsim_beacon_tx, hw); + hw, IEEE80211_IFACE_ITER_NORMAL, + mac80211_hwsim_beacon_tx, hw); data->beacon_timer.expires = jiffies + data->beacon_int; add_timer(&data->beacon_timer); @@ -1680,14 +1682,17 @@ static int hwsim_fops_ps_write(void *dat, u64 val) if (val == PS_MANUAL_POLL) { ieee80211_iterate_active_interfaces(data->hw, + IEEE80211_IFACE_ITER_NORMAL, hwsim_send_ps_poll, data); data->ps_poll_pending = true; } else if (old_ps == PS_DISABLED && val != PS_DISABLED) { ieee80211_iterate_active_interfaces(data->hw, + IEEE80211_IFACE_ITER_NORMAL, hwsim_send_nullfunc_ps, data); } else if (old_ps != PS_DISABLED && val == PS_DISABLED) { ieee80211_iterate_active_interfaces(data->hw, + IEEE80211_IFACE_ITER_NORMAL, hwsim_send_nullfunc_no_ps, data); } diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 69097d1faeb..67d167993d4 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -157,6 +157,7 @@ static void rt2x00lib_intf_scheduled(struct work_struct *work) * requested configurations. */ ieee80211_iterate_active_interfaces(rt2x00dev->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, rt2x00lib_intf_scheduled_iter, rt2x00dev); } @@ -225,9 +226,9 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) return; /* send buffered bc/mc frames out for every bssid */ - ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw, - rt2x00lib_bc_buffer_iter, - rt2x00dev); + ieee80211_iterate_active_interfaces_atomic( + rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + rt2x00lib_bc_buffer_iter, rt2x00dev); /* * Devices with pre tbtt interrupt don't need to update the beacon * here as they will fetch the next beacon directly prior to @@ -237,9 +238,9 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) return; /* fetch next beacon */ - ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw, - rt2x00lib_beaconupdate_iter, - rt2x00dev); + ieee80211_iterate_active_interfaces_atomic( + rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + rt2x00lib_beaconupdate_iter, rt2x00dev); } EXPORT_SYMBOL_GPL(rt2x00lib_beacondone); @@ -249,9 +250,9 @@ void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev) return; /* fetch next beacon */ - ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw, - rt2x00lib_beaconupdate_iter, - rt2x00dev); + ieee80211_iterate_active_interfaces_atomic( + rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + rt2x00lib_beaconupdate_iter, rt2x00dev); } EXPORT_SYMBOL_GPL(rt2x00lib_pretbtt); diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index 98a9e48f8e4..ed7a1bb3f24 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -424,9 +424,9 @@ int rt2x00mac_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return 0; - ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw, - rt2x00mac_set_tim_iter, - rt2x00dev); + ieee80211_iterate_active_interfaces_atomic( + rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + rt2x00mac_set_tim_iter, rt2x00dev); /* queue work to upodate the beacon template */ ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->intf_work); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 25530c8760c..380cf1ff6cd 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -677,7 +677,7 @@ static void wl12xx_get_vif_count(struct ieee80211_hw *hw, memset(data, 0, sizeof(*data)); data->cur_vif = cur_vif; - ieee80211_iterate_active_interfaces(hw, + ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL, wl12xx_vif_count_iter, data); } diff --git a/include/net/mac80211.h b/include/net/mac80211.h index e1d83099231..a789dd1d4c1 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3412,6 +3412,21 @@ void ieee80211_sched_scan_results(struct ieee80211_hw *hw); */ void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw); +/** + * enum ieee80211_interface_iteration_flags - interface iteration flags + * @IEEE80211_IFACE_ITER_NORMAL: Iterate over all interfaces that have + * been added to the driver; However, note that during hardware + * reconfiguration (after restart_hw) it will iterate over a new + * interface and over all the existing interfaces even if they + * haven't been re-added to the driver yet. + * @IEEE80211_IFACE_ITER_RESUME_ALL: During resume, iterate over all + * interfaces, even if they haven't been re-added to the driver yet. + */ +enum ieee80211_interface_iteration_flags { + IEEE80211_IFACE_ITER_NORMAL = 0, + IEEE80211_IFACE_ITER_RESUME_ALL = BIT(0), +}; + /** * ieee80211_iterate_active_interfaces - iterate active interfaces * @@ -3420,13 +3435,15 @@ void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw); * This function allows the iterator function to sleep, when the iterator * function is atomic @ieee80211_iterate_active_interfaces_atomic can * be used. - * Does not iterate over a new interface during add_interface() + * Does not iterate over a new interface during add_interface(). * * @hw: the hardware struct of which the interfaces should be iterated over + * @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags * @iterator: the iterator function to call * @data: first argument of the iterator function */ void ieee80211_iterate_active_interfaces(struct ieee80211_hw *hw, + u32 iter_flags, void (*iterator)(void *data, u8 *mac, struct ieee80211_vif *vif), void *data); @@ -3438,13 +3455,15 @@ void ieee80211_iterate_active_interfaces(struct ieee80211_hw *hw, * hardware that are currently active and calls the callback for them. * This function requires the iterator callback function to be atomic, * if that is not desired, use @ieee80211_iterate_active_interfaces instead. - * Does not iterate over a new interface during add_interface() + * Does not iterate over a new interface during add_interface(). * * @hw: the hardware struct of which the interfaces should be iterated over + * @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags * @iterator: the iterator function to call, cannot sleep * @data: first argument of the iterator function */ void ieee80211_iterate_active_interfaces_atomic(struct ieee80211_hw *hw, + u32 iter_flags, void (*iterator)(void *data, u8 *mac, struct ieee80211_vif *vif), diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 84858a14c8b..01b9fa62f3e 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -512,7 +512,7 @@ void ieee80211_wake_queues(struct ieee80211_hw *hw) EXPORT_SYMBOL(ieee80211_wake_queues); void ieee80211_iterate_active_interfaces( - struct ieee80211_hw *hw, + struct ieee80211_hw *hw, u32 iter_flags, void (*iterator)(void *data, u8 *mac, struct ieee80211_vif *vif), void *data) @@ -530,6 +530,9 @@ void ieee80211_iterate_active_interfaces( default: break; } + if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) && + !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) + continue; if (ieee80211_sdata_running(sdata)) iterator(data, sdata->vif.addr, &sdata->vif); @@ -537,7 +540,9 @@ void ieee80211_iterate_active_interfaces( sdata = rcu_dereference_protected(local->monitor_sdata, lockdep_is_held(&local->iflist_mtx)); - if (sdata) + if (sdata && + (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || + sdata->flags & IEEE80211_SDATA_IN_DRIVER)) iterator(data, sdata->vif.addr, &sdata->vif); mutex_unlock(&local->iflist_mtx); @@ -545,7 +550,7 @@ void ieee80211_iterate_active_interfaces( EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces); void ieee80211_iterate_active_interfaces_atomic( - struct ieee80211_hw *hw, + struct ieee80211_hw *hw, u32 iter_flags, void (*iterator)(void *data, u8 *mac, struct ieee80211_vif *vif), void *data) @@ -563,13 +568,18 @@ void ieee80211_iterate_active_interfaces_atomic( default: break; } + if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) && + !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) + continue; if (ieee80211_sdata_running(sdata)) iterator(data, sdata->vif.addr, &sdata->vif); } sdata = rcu_dereference(local->monitor_sdata); - if (sdata) + if (sdata && + (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || + sdata->flags & IEEE80211_SDATA_IN_DRIVER)) iterator(data, sdata->vif.addr, &sdata->vif); rcu_read_unlock(); -- cgit v1.2.3-70-g09d2 From f4bda337bbb6e245e2a07f344990adeb6a70ff35 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Tue, 13 Nov 2012 10:46:27 -0800 Subject: mac80211: support RX_FLAG_MACTIME_END Allow drivers to indicate their mactime is at RX completion and adjust for this in mac80211. Also rename the existing RX_FLAG_MACTIME_MPDU to RX_FLAG_MACTIME_START to clarify its intent. Based on similar code by Johannes Berg. Signed-off-by: Thomas Pedersen [fix docs, atheros drivers] Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath5k/base.c | 2 +- drivers/net/wireless/ath/ath9k/htc_drv_txrx.c | 2 +- drivers/net/wireless/ath/ath9k/recv.c | 2 +- drivers/net/wireless/b43/xmit.c | 2 +- drivers/net/wireless/b43legacy/xmit.c | 2 +- drivers/net/wireless/brcm80211/brcmsmac/main.c | 2 +- drivers/net/wireless/iwlegacy/4965-mac.c | 2 +- drivers/net/wireless/iwlwifi/dvm/rx.c | 2 +- drivers/net/wireless/mac80211_hwsim.c | 2 +- drivers/net/wireless/p54/txrx.c | 2 +- drivers/net/wireless/rtl818x/rtl8180/dev.c | 2 +- drivers/net/wireless/rtl818x/rtl8187/dev.c | 2 +- drivers/net/wireless/rtlwifi/rtl8192ce/trx.c | 2 +- drivers/net/wireless/rtlwifi/rtl8192cu/trx.c | 2 +- drivers/net/wireless/rtlwifi/rtl8192de/trx.c | 2 +- drivers/net/wireless/rtlwifi/rtl8192se/trx.c | 2 +- drivers/net/wireless/ti/wl1251/rx.c | 2 +- include/net/mac80211.h | 8 +++- net/mac80211/ibss.c | 29 +++------------ net/mac80211/ieee80211_i.h | 11 ++++++ net/mac80211/mesh_sync.c | 44 ++++------------------ net/mac80211/rx.c | 14 +++++-- net/mac80211/util.c | 51 ++++++++++++++++++++++++++ 23 files changed, 108 insertions(+), 83 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index cdd19232960..2fd5bab2e22 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1349,7 +1349,7 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb, * right now, so it's not too bad... */ rxs->mactime = ath5k_extend_tsf(ah, rs->rs_tstamp); - rxs->flag |= RX_FLAG_MACTIME_MPDU; + rxs->flag |= RX_FLAG_MACTIME_START; rxs->freq = ah->curchan->center_freq; rxs->band = ah->curchan->band; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index 06cdcb772d7..e4ec9833298 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -1082,7 +1082,7 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv, rx_status->freq = hw->conf.channel->center_freq; rx_status->signal = rxbuf->rxstatus.rs_rssi + ATH_DEFAULT_NOISE_FLOOR; rx_status->antenna = rxbuf->rxstatus.rs_antenna; - rx_status->flag |= RX_FLAG_MACTIME_MPDU; + rx_status->flag |= RX_FLAG_MACTIME_START; return true; diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index a04028bce28..6aafbb77c49 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -976,7 +976,7 @@ static int ath9k_rx_skb_preprocess(struct ath_common *common, rx_status->freq = hw->conf.channel->center_freq; rx_status->signal = ah->noise + rx_stats->rs_rssi; rx_status->antenna = rx_stats->rs_antenna; - rx_status->flag |= RX_FLAG_MACTIME_MPDU; + rx_status->flag |= RX_FLAG_MACTIME_START; if (rx_stats->rs_moreaggr) rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c index 136510edf3c..8cb206a8908 100644 --- a/drivers/net/wireless/b43/xmit.c +++ b/drivers/net/wireless/b43/xmit.c @@ -796,7 +796,7 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr) status.mactime += mactime; if (low_mactime_now <= mactime) status.mactime -= 0x10000; - status.flag |= RX_FLAG_MACTIME_MPDU; + status.flag |= RX_FLAG_MACTIME_START; } chanid = (chanstat & B43_RX_CHAN_ID) >> B43_RX_CHAN_ID_SHIFT; diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/b43legacy/xmit.c index b8ffea6f5c6..849a28c8030 100644 --- a/drivers/net/wireless/b43legacy/xmit.c +++ b/drivers/net/wireless/b43legacy/xmit.c @@ -557,7 +557,7 @@ void b43legacy_rx(struct b43legacy_wldev *dev, status.mactime += mactime; if (low_mactime_now <= mactime) status.mactime -= 0x10000; - status.flag |= RX_FLAG_MACTIME_MPDU; + status.flag |= RX_FLAG_MACTIME_START; } chanid = (chanstat & B43legacy_RX_CHAN_ID) >> diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index 565c15abbed..02363f8afd7 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -7508,7 +7508,7 @@ prep_mac80211_status(struct brcms_c_info *wlc, struct d11rxhdr *rxh, /* fill in TSF and flag its presence */ rx_status->mactime = brcms_c_recover_tsf64(wlc, rxh); - rx_status->flag |= RX_FLAG_MACTIME_MPDU; + rx_status->flag |= RX_FLAG_MACTIME_START; channel = BRCMS_CHAN_CHANNEL(rxh->RxChan); diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index eac4dc8bc87..ef68b723995 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -686,7 +686,7 @@ il4965_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb) /* TSF isn't reliable. In order to allow smooth user experience, * this W/A doesn't propagate it to the mac80211 */ - /*rx_status.flag |= RX_FLAG_MACTIME_MPDU; */ + /*rx_status.flag |= RX_FLAG_MACTIME_START; */ il->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp); diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c index 5a9c325804f..ad50fb91583 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/iwlwifi/dvm/rx.c @@ -951,7 +951,7 @@ static int iwlagn_rx_reply_rx(struct iwl_priv *priv, /* TSF isn't reliable. In order to allow smooth user experience, * this W/A doesn't propagate it to the mac80211 */ - /*rx_status.flag |= RX_FLAG_MACTIME_MPDU;*/ + /*rx_status.flag |= RX_FLAG_MACTIME_START;*/ priv->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp); diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index a8ec7086ad0..ce522aa93af 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -699,7 +699,7 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, struct ieee80211_rate *txrate = ieee80211_get_tx_rate(hw, info); memset(&rx_status, 0, sizeof(rx_status)); - rx_status.flag |= RX_FLAG_MACTIME_MPDU; + rx_status.flag |= RX_FLAG_MACTIME_START; rx_status.freq = chan->center_freq; rx_status.band = chan->band; rx_status.rate_idx = info->control.rates[0].idx; diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index 5861e13a6fd..8ae982bd7cf 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c @@ -369,7 +369,7 @@ static int p54_rx_data(struct p54_common *priv, struct sk_buff *skb) rx_status->mactime = ((u64)priv->tsf_high32) << 32 | tsf32; priv->tsf_low32 = tsf32; - rx_status->flag |= RX_FLAG_MACTIME_MPDU; + rx_status->flag |= RX_FLAG_MACTIME_START; if (hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN)) header_len += hdr->align[0]; diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index 021d83e1b1d..b4218a5f206 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -150,7 +150,7 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev) rx_status.freq = dev->conf.channel->center_freq; rx_status.band = dev->conf.channel->band; rx_status.mactime = le64_to_cpu(entry->tsft); - rx_status.flag |= RX_FLAG_MACTIME_MPDU; + rx_status.flag |= RX_FLAG_MACTIME_START; if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c index 7811b631597..52e6bebcf08 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c @@ -381,7 +381,7 @@ static void rtl8187_rx_cb(struct urb *urb) rx_status.rate_idx = rate; rx_status.freq = dev->conf.channel->center_freq; rx_status.band = dev->conf.channel->band; - rx_status.flag |= RX_FLAG_MACTIME_MPDU; + rx_status.flag |= RX_FLAG_MACTIME_START; if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c index 390d6d4fcaa..a8fecd7638f 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c @@ -567,7 +567,7 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw, if (GET_RX_DESC_RXHT(pdesc)) rx_status->flag |= RX_FLAG_HT; - rx_status->flag |= RX_FLAG_MACTIME_MPDU; + rx_status->flag |= RX_FLAG_MACTIME_START; if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c index 6e66f04c363..b6222eedb83 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c @@ -334,7 +334,7 @@ bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw, rx_status->flag |= RX_FLAG_40MHZ; if (GET_RX_DESC_RX_HT(pdesc)) rx_status->flag |= RX_FLAG_HT; - rx_status->flag |= RX_FLAG_MACTIME_MPDU; + rx_status->flag |= RX_FLAG_MACTIME_START; if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; rx_status->rate_idx = rtlwifi_rate_mapping(hw, diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c index 4686f340b9d..3baaf21abed 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c @@ -514,7 +514,7 @@ bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, rx_status->flag |= RX_FLAG_40MHZ; if (GET_RX_DESC_RXHT(pdesc)) rx_status->flag |= RX_FLAG_HT; - rx_status->flag |= RX_FLAG_MACTIME_MPDU; + rx_status->flag |= RX_FLAG_MACTIME_START; if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; rx_status->rate_idx = rtlwifi_rate_mapping(hw, diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c index e3cf4c02122..26d027cb506 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c @@ -554,7 +554,7 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, if (stats->is_ht) rx_status->flag |= RX_FLAG_HT; - rx_status->flag |= RX_FLAG_MACTIME_MPDU; + rx_status->flag |= RX_FLAG_MACTIME_START; /* hw will set stats->decrypted true, if it finds the * frame is open data frame or mgmt frame, diff --git a/drivers/net/wireless/ti/wl1251/rx.c b/drivers/net/wireless/ti/wl1251/rx.c index 6af35265c90..23289d49dd3 100644 --- a/drivers/net/wireless/ti/wl1251/rx.c +++ b/drivers/net/wireless/ti/wl1251/rx.c @@ -81,7 +81,7 @@ static void wl1251_rx_status(struct wl1251 *wl, status->freq = ieee80211_channel_to_frequency(desc->channel, status->band); - status->flag |= RX_FLAG_MACTIME_MPDU; + status->flag |= RX_FLAG_MACTIME_START; if (desc->flags & RX_DESC_ENCRYPTION_MASK) { status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index a789dd1d4c1..b484a6569ea 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -711,10 +711,13 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) * the frame. * @RX_FLAG_FAILED_PLCP_CRC: Set this flag if the PCLP check failed on * the frame. - * @RX_FLAG_MACTIME_MPDU: The timestamp passed in the RX status (@mactime + * @RX_FLAG_MACTIME_START: The timestamp passed in the RX status (@mactime * field) is valid and contains the time the first symbol of the MPDU * was received. This is useful in monitor mode and for proper IBSS * merging. + * @RX_FLAG_MACTIME_END: The timestamp passed in the RX status (@mactime + * field) is valid and contains the time the last symbol of the MPDU + * (including FCS) was received. * @RX_FLAG_SHORTPRE: Short preamble was used for this frame * @RX_FLAG_HT: HT MCS was used and rate_idx is MCS index * @RX_FLAG_40MHZ: HT40 (40 MHz) was used @@ -745,7 +748,7 @@ enum mac80211_rx_flags { RX_FLAG_IV_STRIPPED = BIT(4), RX_FLAG_FAILED_FCS_CRC = BIT(5), RX_FLAG_FAILED_PLCP_CRC = BIT(6), - RX_FLAG_MACTIME_MPDU = BIT(7), + RX_FLAG_MACTIME_START = BIT(7), RX_FLAG_SHORTPRE = BIT(8), RX_FLAG_HT = BIT(9), RX_FLAG_40MHZ = BIT(10), @@ -759,6 +762,7 @@ enum mac80211_rx_flags { RX_FLAG_AMPDU_IS_LAST = BIT(18), RX_FLAG_AMPDU_DELIM_CRC_ERROR = BIT(19), RX_FLAG_AMPDU_DELIM_CRC_KNOWN = BIT(20), + RX_FLAG_MACTIME_END = BIT(21), }; /** diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index c7386b2b767..cc11558d8c1 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -543,30 +543,11 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, if (ether_addr_equal(cbss->bssid, sdata->u.ibss.bssid)) goto put_bss; - if (rx_status->flag & RX_FLAG_MACTIME_MPDU) { - /* - * For correct IBSS merging we need mactime; since mactime is - * defined as the time the first data symbol of the frame hits - * the PHY, and the timestamp of the beacon is defined as "the - * time that the data symbol containing the first bit of the - * timestamp is transmitted to the PHY plus the transmitting - * STA's delays through its local PHY from the MAC-PHY - * interface to its interface with the WM" (802.11 11.1.2) - * - equals the time this bit arrives at the receiver - we have - * to take into account the offset between the two. - * - * E.g. at 1 MBit that means mactime is 192 usec earlier - * (=24 bytes * 8 usecs/byte) than the beacon timestamp. - */ - int rate; - - if (rx_status->flag & RX_FLAG_HT) - rate = 65; /* TODO: HT rates */ - else - rate = local->hw.wiphy->bands[band]-> - bitrates[rx_status->rate_idx].bitrate; - - rx_timestamp = rx_status->mactime + (24 * 8 * 10 / rate); + if (ieee80211_have_rx_timestamp(rx_status)) { + /* time when timestamp field was received */ + rx_timestamp = + ieee80211_calculate_rx_timestamp(local, rx_status, + len + FCS_LEN, 24); } else { /* * second best option: get current TSF diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index e1fb97cc9a4..bff82a8e62f 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1259,7 +1259,18 @@ static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr) is_broadcast_ether_addr(raddr); } +static inline bool +ieee80211_have_rx_timestamp(struct ieee80211_rx_status *status) +{ + WARN_ON_ONCE(status->flag & RX_FLAG_MACTIME_START && + status->flag & RX_FLAG_MACTIME_END); + return status->flag & (RX_FLAG_MACTIME_START | RX_FLAG_MACTIME_END); +} +u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, + struct ieee80211_rx_status *status, + unsigned int mpdu_len, + unsigned int mpdu_offset); int ieee80211_hw_config(struct ieee80211_local *local, u32 changed); void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx); void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c index 407c8705e10..9c6ea9cfe1b 100644 --- a/net/mac80211/mesh_sync.c +++ b/net/mac80211/mesh_sync.c @@ -116,43 +116,13 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, goto no_sync; } - if (rx_status->flag & RX_FLAG_MACTIME_MPDU && rx_status->mactime) { - /* - * The mactime is defined as the time the first data symbol - * of the frame hits the PHY, and the timestamp of the beacon - * is defined as "the time that the data symbol containing the - * first bit of the timestamp is transmitted to the PHY plus - * the transmitting STA's delays through its local PHY from the - * MAC-PHY interface to its interface with the WM" (802.11 - * 11.1.2) - * - * T_r, in 13.13.2.2.2, is just defined as "the frame reception - * time" but we unless we interpret that time to be the same - * time of the beacon timestamp, the offset calculation will be - * off. Below we adjust t_r to be "the time at which the first - * symbol of the timestamp element in the beacon is received". - * This correction depends on the rate. - * - * Based on similar code in ibss.c - */ - int rate; - - if (rx_status->flag & RX_FLAG_HT) { - /* TODO: - * In principle there could be HT-beacons (Dual Beacon - * HT Operation options), but for now ignore them and - * just use the primary (i.e. non-HT) beacons for - * synchronization. - * */ - goto no_sync; - } else - rate = local->hw.wiphy->bands[rx_status->band]-> - bitrates[rx_status->rate_idx].bitrate; - - /* 24 bytes of header * 8 bits/byte * - * 10*(100 Kbps)/Mbps / rate (100 Kbps)*/ - t_r = rx_status->mactime + (24 * 8 * 10 / rate); - } + if (ieee80211_have_rx_timestamp(rx_status)) + /* time when timestamp field was received */ + t_r = ieee80211_calculate_rx_timestamp(local, rx_status, + 24 + 12 + + elems->total_len + + FCS_LEN, + 24); /* Timing offset calculation (see 13.13.2.2.2) */ t_t = le64_to_cpu(mgmt->u.beacon.timestamp); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 6ad330341b7..e3daee8fdf7 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -81,7 +81,7 @@ ieee80211_rx_radiotap_len(struct ieee80211_local *local, /* always present fields */ len = sizeof(struct ieee80211_radiotap_header) + 9; - if (status->flag & RX_FLAG_MACTIME_MPDU) + if (ieee80211_have_rx_timestamp(status)) len += 8; if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) len += 1; @@ -117,6 +117,11 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, struct ieee80211_radiotap_header *rthdr; unsigned char *pos; u16 rx_flags = 0; + int mpdulen; + + mpdulen = skb->len; + if (!(has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS))) + mpdulen += FCS_LEN; rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len); memset(rthdr, 0, rtap_len); @@ -134,8 +139,11 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, /* the order of the following fields is important */ /* IEEE80211_RADIOTAP_TSFT */ - if (status->flag & RX_FLAG_MACTIME_MPDU) { - put_unaligned_le64(status->mactime, pos); + if (ieee80211_have_rx_timestamp(status)) { + put_unaligned_le64( + ieee80211_calculate_rx_timestamp(local, status, + mpdulen, 0), + pos); rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT); pos += 8; } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 4e4f5851367..5bad758abfb 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2013,3 +2013,54 @@ u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs) return 2; return 1; } + +/** + * ieee80211_calculate_rx_timestamp - calculate timestamp in frame + * @local: mac80211 hw info struct + * @status: RX status + * @mpdu_len: total MPDU length (including FCS) + * @mpdu_offset: offset into MPDU to calculate timestamp at + * + * This function calculates the RX timestamp at the given MPDU offset, taking + * into account what the RX timestamp was. An offset of 0 will just normalize + * the timestamp to TSF at beginning of MPDU reception. + */ +u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, + struct ieee80211_rx_status *status, + unsigned int mpdu_len, + unsigned int mpdu_offset) +{ + u64 ts = status->mactime; + struct rate_info ri; + u16 rate; + + if (WARN_ON(!ieee80211_have_rx_timestamp(status))) + return 0; + + memset(&ri, 0, sizeof(ri)); + + /* Fill cfg80211 rate info */ + if (status->flag & RX_FLAG_HT) { + ri.mcs = status->rate_idx; + ri.flags |= RATE_INFO_FLAGS_MCS; + if (status->flag & RX_FLAG_40MHZ) + ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; + if (status->flag & RX_FLAG_SHORT_GI) + ri.flags |= RATE_INFO_FLAGS_SHORT_GI; + } else { + struct ieee80211_supported_band *sband; + + sband = local->hw.wiphy->bands[status->band]; + ri.legacy = sband->bitrates[status->rate_idx].bitrate; + } + + rate = cfg80211_calculate_bitrate(&ri); + + /* rewind from end of MPDU */ + if (status->flag & RX_FLAG_MACTIME_END) + ts -= mpdu_len * 8 * 10 / rate; + + ts += mpdu_offset * 8 * 10 / rate; + + return ts; +} -- cgit v1.2.3-70-g09d2 From 259bcf87fb51fb80185cf54fac7f00da56a80ac4 Mon Sep 17 00:00:00 2001 From: Zefir Kurtisi Date: Wed, 31 Oct 2012 12:21:56 +0100 Subject: ath9k: resolve name collision in DFS detector set_domain() is already defined in /arch/arm/asm/domain.h Signed-off-by: Zefir Kurtisi Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c | 4 ++-- drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c index ea2a6cf7ef2..f66da35d57a 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c @@ -274,7 +274,7 @@ static bool dpd_set_domain(struct dfs_pattern_detector *dpd, static struct dfs_pattern_detector default_dpd = { .exit = dpd_exit, - .set_domain = dpd_set_domain, + .set_dfs_domain = dpd_set_domain, .add_pulse = dpd_add_pulse, .region = NL80211_DFS_UNSET, }; @@ -291,7 +291,7 @@ dfs_pattern_detector_init(enum nl80211_dfs_regions region) *dpd = default_dpd; INIT_LIST_HEAD(&dpd->channel_detectors); - if (dpd->set_domain(dpd, region)) + if (dpd->set_dfs_domain(dpd, region)) return dpd; pr_err("Could not set DFS domain to %d. ", region); diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h index fd0328a3099..cda52f39f28 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h +++ b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h @@ -62,7 +62,7 @@ struct radar_detector_specs { /** * struct dfs_pattern_detector - DFS pattern detector * @exit(): destructor - * @set_domain(): set DFS domain, resets detector lines upon domain changes + * @set_dfs_domain(): set DFS domain, resets detector lines upon domain changes * @add_pulse(): add radar pulse to detector, returns true on detection * @region: active DFS region, NL80211_DFS_UNSET until set * @num_radar_types: number of different radar types @@ -72,7 +72,7 @@ struct radar_detector_specs { */ struct dfs_pattern_detector { void (*exit)(struct dfs_pattern_detector *dpd); - bool (*set_domain)(struct dfs_pattern_detector *dpd, + bool (*set_dfs_domain)(struct dfs_pattern_detector *dpd, enum nl80211_dfs_regions region); bool (*add_pulse)(struct dfs_pattern_detector *dpd, struct pulse_event *pe); -- cgit v1.2.3-70-g09d2 From 70bf870b54b96887d388286e7d8865406f1aa670 Mon Sep 17 00:00:00 2001 From: Zefir Kurtisi Date: Wed, 31 Oct 2012 12:22:34 +0100 Subject: ath9k: fix memory leak in DFS pattern detector Free instance of pattern detector if requested DFS domain is not supported. Signed-off-by: Zefir Kurtisi Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c index f66da35d57a..3b129143653 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c @@ -295,6 +295,7 @@ dfs_pattern_detector_init(enum nl80211_dfs_regions region) return dpd; pr_err("Could not set DFS domain to %d. ", region); + kfree(dpd); return NULL; } EXPORT_SYMBOL(dfs_pattern_detector_init); -- cgit v1.2.3-70-g09d2 From 158b42edabe82b6adfcd3310d116cc41fdc669ea Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 31 Oct 2012 11:52:51 -0700 Subject: carl9170: kill MODULE_VERSION This is pretty pointless. Lets kill this to stop people from thinking that its actually used. Maybe we should go on a crusade and kill this completely from the kernel. Cc: Vladimir Kondratiev Acked-by: Christian Lamparter Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/fw.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c index 24ac2876a73..aaebecd19e5 100644 --- a/drivers/net/wireless/ath/carl9170/fw.c +++ b/drivers/net/wireless/ath/carl9170/fw.c @@ -28,11 +28,6 @@ #include "fwcmd.h" #include "version.h" -#define MAKE_STR(symbol) #symbol -#define TO_STR(symbol) MAKE_STR(symbol) -#define CARL9170FW_API_VER_STR TO_STR(CARL9170FW_API_MAX_VER) -MODULE_VERSION(CARL9170FW_API_VER_STR ":" CARL9170FW_VERSION_GIT); - static const u8 otus_magic[4] = { OTUS_MAGIC }; static const void *carl9170_fw_find_desc(struct ar9170 *ar, const u8 descid[4], -- cgit v1.2.3-70-g09d2 From f0a5fd4e7c08554335e2938dccd32c9b9edee345 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 1 Nov 2012 09:59:13 +0800 Subject: ar5523: use module_usb_driver to simplify the code Use the module_usb_driver() macro to make the code simpler by eliminating module_init and module_exit calls. dpatch engine is used to auto generate this patch. (https://github.com/weiyj/dpatch) Signed-off-by: Wei Yongjun Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ar5523/ar5523.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c index f782b6e502b..402e6d378b2 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523.c +++ b/drivers/net/wireless/ath/ar5523/ar5523.c @@ -1789,18 +1789,7 @@ static struct usb_driver ar5523_driver = { .disconnect = ar5523_disconnect, }; -static int __init ar5523_init(void) -{ - return usb_register(&ar5523_driver); -} - -static void __exit ar5523_exit(void) -{ - usb_deregister(&ar5523_driver); -} +module_usb_driver(ar5523_driver); MODULE_LICENSE("Dual BSD/GPL"); MODULE_FIRMWARE(AR5523_FIRMWARE_FILE); - -module_init(ar5523_init); -module_exit(ar5523_exit); -- cgit v1.2.3-70-g09d2 From f3ec3bf527638b8efb576d1bdbaf8d66d1183f92 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 3 Nov 2012 21:30:25 +0100 Subject: drivers/net/wireless/ath/ath6kl/hif.c: drop if around WARN_ON Just use WARN_ON rather than an if containing only WARN_ON(1). A simplified version of the semantic patch that makes this transformation is as follows: (http://coccinelle.lip6.fr/) // @@ expression e; @@ - if (e) WARN_ON(1); + WARN_ON(e); // Signed-off-by: Julia Lawall Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath6kl/hif.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/hif.c b/drivers/net/wireless/ath/ath6kl/hif.c index 68ed6c2665b..9e47c4a138a 100644 --- a/drivers/net/wireless/ath/ath6kl/hif.c +++ b/drivers/net/wireless/ath/ath6kl/hif.c @@ -338,8 +338,7 @@ static int ath6kl_hif_proc_err_intr(struct ath6kl_device *dev) status = hif_read_write_sync(dev->ar, ERROR_INT_STATUS_ADDRESS, reg_buf, 4, HIF_WR_SYNC_BYTE_FIX); - if (status) - WARN_ON(1); + WARN_ON(status); return status; } @@ -383,8 +382,7 @@ static int ath6kl_hif_proc_cpu_intr(struct ath6kl_device *dev) status = hif_read_write_sync(dev->ar, CPU_INT_STATUS_ADDRESS, reg_buf, 4, HIF_WR_SYNC_BYTE_FIX); - if (status) - WARN_ON(1); + WARN_ON(status); return status; } -- cgit v1.2.3-70-g09d2 From d01a303e68310695b6d08327454f4ad76a48716f Mon Sep 17 00:00:00 2001 From: Pontus Fuchs Date: Mon, 5 Nov 2012 21:17:50 +0100 Subject: ar5523: Fix sparse endianness warnings __be32 variables where used a little careless leading to sparse warnings. Treat them a little more gentle. Reported-by: Fengguang Wu Signed-off-by: Pontus Fuchs Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ar5523/ar5523.c | 43 +++++++++++++++-------------- drivers/net/wireless/ath/ar5523/ar5523_hw.h | 2 +- 2 files changed, 24 insertions(+), 21 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c index 402e6d378b2..4bd7714cd66 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523.c +++ b/drivers/net/wireless/ath/ar5523/ar5523.c @@ -50,18 +50,19 @@ static void ar5523_read_reply(struct ar5523 *ar, struct ar5523_cmd_hdr *hdr, struct ar5523_tx_cmd *cmd) { int dlen, olen; - u32 *rp; + __be32 *rp; - dlen = hdr->len - sizeof(*hdr); + dlen = be32_to_cpu(hdr->len) - sizeof(*hdr); if (dlen < 0) { WARN_ON(1); goto out; } - ar5523_dbg(ar, "Code = %d len = %d\n", hdr->code & 0xff, dlen); + ar5523_dbg(ar, "Code = %d len = %d\n", be32_to_cpu(hdr->code) & 0xff, + dlen); - rp = (u32 *)(hdr + 1); + rp = (__be32 *)(hdr + 1); if (dlen >= sizeof(u32)) { olen = be32_to_cpu(rp[0]); dlen -= sizeof(u32); @@ -95,6 +96,7 @@ static void ar5523_cmd_rx_cb(struct urb *urb) struct ar5523_tx_cmd *cmd = &ar->tx_cmd; struct ar5523_cmd_hdr *hdr = ar->rx_cmd_buf; int dlen; + u32 code, hdrlen; if (urb->status) { if (urb->status != -ESHUTDOWN) @@ -110,15 +112,15 @@ static void ar5523_cmd_rx_cb(struct urb *urb) ar5523_dbg(ar, "%s code %02x priv %d\n", __func__, be32_to_cpu(hdr->code) & 0xff, hdr->priv); - hdr->code = be32_to_cpu(hdr->code); - hdr->len = be32_to_cpu(hdr->len); + code = be32_to_cpu(hdr->code); + hdrlen = be32_to_cpu(hdr->len); - switch (hdr->code & 0xff) { + switch (code & 0xff) { default: /* reply to a read command */ if (hdr->priv != AR5523_CMD_ID) { ar5523_err(ar, "Unexpected command id: %02x\n", - hdr->code & 0xff); + code & 0xff); goto skip; } ar5523_read_reply(ar, hdr, cmd); @@ -147,7 +149,7 @@ static void ar5523_cmd_rx_cb(struct urb *urb) case WDCMSG_TARGET_START: /* This command returns a bogus id so it needs special handling */ - dlen = hdr->len - sizeof(*hdr); + dlen = hdrlen - sizeof(*hdr); if (dlen != (int)sizeof(u32)) { ar5523_err(ar, "Invalid reply to WDCMSG_TARGET_START"); return; @@ -303,7 +305,7 @@ static int ar5523_config(struct ar5523 *ar, u32 reg, u32 val) write.reg = cpu_to_be32(reg); write.len = cpu_to_be32(0); /* 0 = single write */ - *(u32 *)write.data = cpu_to_be32(val); + *(__be32 *)write.data = cpu_to_be32(val); error = ar5523_cmd_write(ar, WDCMSG_TARGET_SET_CONFIG, &write, 3 * sizeof(u32), 0); @@ -335,29 +337,30 @@ static int ar5523_get_status(struct ar5523 *ar, u32 which, void *odata, int olen) { int error; + __be32 which_be; - which = cpu_to_be32(which); + which_be = cpu_to_be32(which); error = ar5523_cmd_read(ar, WDCMSG_TARGET_GET_STATUS, - &which, sizeof(which), odata, olen, AR5523_CMD_FLAG_MAGIC); + &which_be, sizeof(which_be), odata, olen, AR5523_CMD_FLAG_MAGIC); if (error != 0) - ar5523_err(ar, "could not read EEPROM offset 0x%02x\n", - be32_to_cpu(which)); + ar5523_err(ar, "could not read EEPROM offset 0x%02x\n", which); return error; } static int ar5523_get_capability(struct ar5523 *ar, u32 cap, u32 *val) { int error; + __be32 cap_be, val_be; - cap = cpu_to_be32(cap); - error = ar5523_cmd_read(ar, WDCMSG_TARGET_GET_CAPABILITY, - &cap, sizeof(cap), val, sizeof(u32), AR5523_CMD_FLAG_MAGIC); + cap_be = cpu_to_be32(cap); + error = ar5523_cmd_read(ar, WDCMSG_TARGET_GET_CAPABILITY, &cap_be, + sizeof(cap_be), &val_be, sizeof(__be32), + AR5523_CMD_FLAG_MAGIC); if (error != 0) { - ar5523_err(ar, "could not read capability %u\n", - be32_to_cpu(cap)); + ar5523_err(ar, "could not read capability %u\n", cap); return error; } - *val = be32_to_cpu(*val); + *val = be32_to_cpu(val_be); return error; } diff --git a/drivers/net/wireless/ath/ar5523/ar5523_hw.h b/drivers/net/wireless/ath/ar5523/ar5523_hw.h index a0e8bf46031..0fe2c803f48 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523_hw.h +++ b/drivers/net/wireless/ath/ar5523/ar5523_hw.h @@ -161,7 +161,7 @@ struct ar5523_rx_desc { struct ar5523_tx_desc { __be32 msglen; - __be32 msgid; /* msg id (supplied by host) */ + u32 msgid; /* msg id (supplied by host) */ __be32 type; /* opcode: WDMSG_SEND or WDCMSG_FLUSH */ __be32 txqid; /* tx queue id and flags */ #define UATH_TXQID_MASK 0x0f -- cgit v1.2.3-70-g09d2 From 9ee01b30fdc85a4ab5b0836ad98ac0ae5092fb4f Mon Sep 17 00:00:00 2001 From: Pontus Fuchs Date: Mon, 5 Nov 2012 21:17:51 +0100 Subject: ar5523: Don't dereference sta if NULL A missing else caused a potential NULL dereference. Reported-by: Yuanhan Liu Signed-off-by: Pontus Fuchs Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ar5523/ar5523.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c index 4bd7714cd66..7157f7d311c 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523.c +++ b/drivers/net/wireless/ath/ar5523/ar5523.c @@ -1196,8 +1196,8 @@ static void ar5523_create_rateset(struct ar5523 *ar, if (!sta) { ar5523_info(ar, "STA not found. Cannot set rates\n"); sta_rate_set = bss_conf->basic_rates; - } - sta_rate_set = sta->supp_rates[ar->hw->conf.channel->band]; + } else + sta_rate_set = sta->supp_rates[ar->hw->conf.channel->band]; ar5523_dbg(ar, "sta rate_set = %08x\n", sta_rate_set); -- cgit v1.2.3-70-g09d2 From ff7e68670c49b6404acc4fce6ca90d6c89ef0efe Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Thu, 15 Nov 2012 16:34:56 +0200 Subject: ath6kl: support NL80211_USER_REG_HINT_CELL_BASE events As ath6kl firmware can't do intersections the driver should only listen to regdom changes from cellular base stations, all other requests need to be refused. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index bf5e7d51939..41dea0d5fe0 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3500,11 +3500,18 @@ static int ath6kl_cfg80211_reg_notify(struct wiphy *wiphy, int ret, i; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, - "cfg reg_notify %c%c%s%s initiator %d\n", + "cfg reg_notify %c%c%s%s initiator %d hint_type %d\n", request->alpha2[0], request->alpha2[1], request->intersect ? " intersect" : "", request->processed ? " processed" : "", - request->initiator); + request->initiator, request->user_reg_hint_type); + + /* + * As firmware is not able intersect regdoms, we can only listen to + * cellular hints. + */ + if (request->user_reg_hint_type != NL80211_USER_REG_HINT_CELL_BASE) + return -EOPNOTSUPP; ret = ath6kl_wmi_set_regdomain_cmd(ar->wmi, request->alpha2); if (ret) { @@ -3668,8 +3675,10 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) } if (config_enabled(CONFIG_ATH6KL_REGDOMAIN) && - test_bit(ATH6KL_FW_CAPABILITY_REGDOMAIN, ar->fw_capabilities)) + test_bit(ATH6KL_FW_CAPABILITY_REGDOMAIN, ar->fw_capabilities)) { wiphy->reg_notifier = ath6kl_cfg80211_reg_notify; + ar->wiphy->features |= NL80211_FEATURE_CELL_BASE_REG_HINTS; + } /* max num of ssids that can be probed during scanning */ wiphy->max_scan_ssids = MAX_PROBED_SSIDS; -- cgit v1.2.3-70-g09d2 From a6952287942a13831502838d47e268d05c21c48f Mon Sep 17 00:00:00 2001 From: Zefir Kurtisi Date: Wed, 31 Oct 2012 12:23:01 +0100 Subject: ath9k: [DFS] add pulse width tolerance for ETSI Add 5% width tolerance for radar patterns defined by ETSI. Signed-off-by: Zefir Kurtisi Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c index 3b129143653..24877b00cbf 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c @@ -42,10 +42,15 @@ struct radar_types { #define MIN_PPB_THRESH 50 #define PPB_THRESH(PPB) ((PPB * MIN_PPB_THRESH + 50) / 100) #define PRF2PRI(PRF) ((1000000 + PRF / 2) / PRF) +/* percentage of pulse width tolerance */ +#define WIDTH_TOLERANCE 5 +#define WIDTH_LOWER(X) ((X*(100-WIDTH_TOLERANCE)+50)/100) +#define WIDTH_UPPER(X) ((X*(100+WIDTH_TOLERANCE)+50)/100) #define ETSI_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB) \ { \ - ID, WMIN, WMAX, (PRF2PRI(PMAX) - PRI_TOLERANCE), \ + ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX), \ + (PRF2PRI(PMAX) - PRI_TOLERANCE), \ (PRF2PRI(PMIN) * PRF + PRI_TOLERANCE), PRF, PPB * PRF, \ PPB_THRESH(PPB), PRI_TOLERANCE, \ } -- cgit v1.2.3-70-g09d2 From 0b6eb3662210cd06127980d2d5a06d6e129f373e Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Fri, 9 Nov 2012 14:51:30 +0530 Subject: ath9k_hw: Fix wrong peak detector DC offset An issue is reported in AR9462 & AR9565 that NF_cal_not_done is not observed when HW peak detector calibration is disabled. At that state, the HW is stuck at NF calibration which prevents tx output. The root cause is wrong peak detector offset calibrated by HW. To resolve this issue, peak detector calibration is done manually by SW for AR9462 and AR9565. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_calib.c | 76 ++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/ar9003_phy.h | 46 +++++++++++-- .../net/wireless/ath/ath9k/ar9462_2p0_initvals.h | 2 +- 3 files changed, 116 insertions(+), 8 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 162401f22f8..8b0d8dcd762 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -891,6 +891,74 @@ static void ar9003_hw_tx_iq_cal_reload(struct ath_hw *ah) AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1); } +static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g) +{ + int offset[8], total = 0, test; + int agc_out, i; + + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), + AR_PHY_65NM_RXRF_GAINSTAGES_RX_OVERRIDE, 0x1); + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), + AR_PHY_65NM_RXRF_GAINSTAGES_LNAON_CALDC, 0x0); + if (is_2g) + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), + AR_PHY_65NM_RXRF_GAINSTAGES_LNA2G_GAIN_OVR, 0x0); + else + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), + AR_PHY_65NM_RXRF_GAINSTAGES_LNA5G_GAIN_OVR, 0x0); + + REG_RMW_FIELD(ah, AR_PHY_65NM_RXTX2(chain), + AR_PHY_65NM_RXTX2_RXON_OVR, 0x1); + REG_RMW_FIELD(ah, AR_PHY_65NM_RXTX2(chain), + AR_PHY_65NM_RXTX2_RXON, 0x0); + + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE, 0x1); + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC_ON_OVR, 0x1); + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR, 0x1); + if (is_2g) + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR, 0x0); + else + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC5G_DBDAC_OVR, 0x0); + + for (i = 6; i > 0; i--) { + offset[i] = BIT(i - 1); + test = total + offset[i]; + + if (is_2g) + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, + test); + else + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR, + test); + udelay(100); + agc_out = REG_READ_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC_OUT); + offset[i] = (agc_out) ? 0 : 1; + total += (offset[i] << (i - 1)); + } + + if (is_2g) + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, total); + else + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR, total); + + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), + AR_PHY_65NM_RXRF_GAINSTAGES_RX_OVERRIDE, 0); + REG_RMW_FIELD(ah, AR_PHY_65NM_RXTX2(chain), + AR_PHY_65NM_RXTX2_RXON_OVR, 0); + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR, 0); +} + static bool ar9003_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) { @@ -989,6 +1057,14 @@ skip_tx_iqcal: status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0, AH_WAIT_TIMEOUT); + if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { + for (i = 0; i < AR9300_MAX_CHAINS; i++) { + if (!(ah->rxchainmask & (1 << i))) + continue; + ar9003_hw_manual_peak_cal(ah, i, + IS_CHAN_2GHZ(chan)); + } + } } if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h index 8f585233a78..4c3d06de711 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h @@ -698,13 +698,6 @@ #define AR_PHY_65NM_CH0_THERM_SAR_ADC_OUT 0x0000ff00 #define AR_PHY_65NM_CH0_THERM_SAR_ADC_OUT_S 8 -#define AR_PHY_65NM_CH0_RXTX1 0x16100 -#define AR_PHY_65NM_CH0_RXTX2 0x16104 -#define AR_PHY_65NM_CH1_RXTX1 0x16500 -#define AR_PHY_65NM_CH1_RXTX2 0x16504 -#define AR_PHY_65NM_CH2_RXTX1 0x16900 -#define AR_PHY_65NM_CH2_RXTX2 0x16904 - #define AR_CH0_TOP2 (AR_SREV_9300(ah) ? 0x1628c : \ (AR_SREV_9462(ah) ? 0x16290 : 0x16284)) #define AR_CH0_TOP2_XPABIASLVL 0xf000 @@ -1286,4 +1279,43 @@ #define AR_BTCOEX_WL_LNADIV_BT_INACTIVE_THRESHOLD 0xFC000000 #define AR_BTCOEX_WL_LNADIV_BT_INACTIVE_THRESHOLD_S 26 +/* Manual Peak detector calibration */ +#define AR_PHY_65NM_BASE 0x16000 +#define AR_PHY_65NM_RXRF_GAINSTAGES(i) (AR_PHY_65NM_BASE + \ + (i * 0x400) + 0x8) +#define AR_PHY_65NM_RXRF_GAINSTAGES_RX_OVERRIDE 0x80000000 +#define AR_PHY_65NM_RXRF_GAINSTAGES_RX_OVERRIDE_S 31 +#define AR_PHY_65NM_RXRF_GAINSTAGES_LNAON_CALDC 0x00000002 +#define AR_PHY_65NM_RXRF_GAINSTAGES_LNAON_CALDC_S 1 +#define AR_PHY_65NM_RXRF_GAINSTAGES_LNA2G_GAIN_OVR 0x70000000 +#define AR_PHY_65NM_RXRF_GAINSTAGES_LNA2G_GAIN_OVR_S 28 +#define AR_PHY_65NM_RXRF_GAINSTAGES_LNA5G_GAIN_OVR 0x03800000 +#define AR_PHY_65NM_RXRF_GAINSTAGES_LNA5G_GAIN_OVR_S 23 + +#define AR_PHY_65NM_RXTX2(i) (AR_PHY_65NM_BASE + \ + (i * 0x400) + 0x104) +#define AR_PHY_65NM_RXTX2_RXON_OVR 0x00001000 +#define AR_PHY_65NM_RXTX2_RXON_OVR_S 12 +#define AR_PHY_65NM_RXTX2_RXON 0x00000800 +#define AR_PHY_65NM_RXTX2_RXON_S 11 + +#define AR_PHY_65NM_RXRF_AGC(i) (AR_PHY_65NM_BASE + \ + (i * 0x400) + 0xc) +#define AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE 0x80000000 +#define AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE_S 31 +#define AR_PHY_65NM_RXRF_AGC_AGC_ON_OVR 0x40000000 +#define AR_PHY_65NM_RXRF_AGC_AGC_ON_OVR_S 30 +#define AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR 0x20000000 +#define AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR_S 29 +#define AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR 0x1E000000 +#define AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR_S 25 +#define AR_PHY_65NM_RXRF_AGC_AGC5G_DBDAC_OVR 0x00078000 +#define AR_PHY_65NM_RXRF_AGC_AGC5G_DBDAC_OVR_S 15 +#define AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR 0x01F80000 +#define AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR_S 19 +#define AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR 0x00007e00 +#define AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR_S 9 +#define AR_PHY_65NM_RXRF_AGC_AGC_OUT 0x00000004 +#define AR_PHY_65NM_RXRF_AGC_AGC_OUT_S 2 + #endif /* AR9003_PHY_H */ diff --git a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h index 58f30f65c6b..ccc42a71b43 100644 --- a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h @@ -78,7 +78,7 @@ static const u32 ar9462_2p0_baseband_postamble[][5] = { {0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110}, {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222}, - {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, + {0x0000a2c4, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18}, {0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982}, {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b}, {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, -- cgit v1.2.3-70-g09d2 From 413c0303cfbf269cdc7a90653ec15f3cb73cd080 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 12 Nov 2012 10:56:43 +0530 Subject: ath9k_hw: Update AR9485 initvals Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9485_initvals.h | 336 ++++++++++++++++++----- 1 file changed, 273 insertions(+), 63 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9485_initvals.h b/drivers/net/wireless/ath/ath9k/ar9485_initvals.h index fb4497fc7a3..02e4d977bd4 100644 --- a/drivers/net/wireless/ath/ath9k/ar9485_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9485_initvals.h @@ -18,7 +18,7 @@ #ifndef INITVALS_9485_H #define INITVALS_9485_H -/* AR9485 1.0 */ +/* AR9485 1.1 */ #define ar9485_1_1_mac_postamble ar9300_2p2_mac_postamble @@ -31,6 +31,11 @@ static const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_disable_L1[][2] = { static const u32 ar9485Common_wo_xlna_rx_gain_1_1[][2] = { /* Addr allmodes */ + {0x00009e00, 0x037216a0}, + {0x00009e04, 0x00182020}, + {0x00009e18, 0x00000000}, + {0x00009e2c, 0x00004121}, + {0x00009e44, 0x02282324}, {0x0000a000, 0x00060005}, {0x0000a004, 0x00810080}, {0x0000a008, 0x00830082}, @@ -164,6 +169,11 @@ static const u32 ar9485Common_wo_xlna_rx_gain_1_1[][2] = { static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, + {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0x7999a83a, 0x7999a83a}, + {0x0000a2dc, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e0, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e4, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e8, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, @@ -198,6 +208,22 @@ static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = { {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a580, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a584, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a588, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a58c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a590, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a594, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a598, 0x00000000, 0x00000000, 0x01404501, 0x01404501}, + {0x0000a59c, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02}, + {0x0000a5a0, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02}, + {0x0000a5a4, 0x00000000, 0x00000000, 0x02808803, 0x02808803}, + {0x0000a5a8, 0x00000000, 0x00000000, 0x04c14b04, 0x04c14b04}, + {0x0000a5ac, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b0, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b4, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b8, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5bc, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, {0x0000b500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b504, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b508, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -234,9 +260,193 @@ static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = { {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, }; -#define ar9485Modes_high_ob_db_tx_gain_1_1 ar9485Modes_high_power_tx_gain_1_1 +static const u32 ar9485Modes_high_ob_db_tx_gain_1_1[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, + {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0x7999a83a, 0x7999a83a}, + {0x0000a2dc, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e0, 0x00000000, 0x00000000, 0xffc63a84, 0xffc63a84}, + {0x0000a2e4, 0x00000000, 0x00000000, 0xfe0fc000, 0xfe0fc000}, + {0x0000a2e8, 0x00000000, 0x00000000, 0xfff00000, 0xfff00000}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, + {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, + {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, + {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, + {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200}, + {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202}, + {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400}, + {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402}, + {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404}, + {0x0000a520, 0x2f001f04, 0x2f001f04, 0x21000603, 0x21000603}, + {0x0000a524, 0x35001fc4, 0x35001fc4, 0x25000605, 0x25000605}, + {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2a000a03, 0x2a000a03}, + {0x0000a52c, 0x41023e85, 0x41023e85, 0x2c000a04, 0x2c000a04}, + {0x0000a530, 0x48023ec6, 0x48023ec6, 0x34000e20, 0x34000e20}, + {0x0000a534, 0x4d023f01, 0x4d023f01, 0x35000e21, 0x35000e21}, + {0x0000a538, 0x53023f4b, 0x53023f4b, 0x43000e62, 0x43000e62}, + {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x45000e63, 0x45000e63}, + {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x49000e65, 0x49000e65}, + {0x0000a544, 0x6502feca, 0x6502feca, 0x4b000e66, 0x4b000e66}, + {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x4d001645, 0x4d001645}, + {0x0000a54c, 0x7203feca, 0x7203feca, 0x51001865, 0x51001865}, + {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x55001a86, 0x55001a86}, + {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x57001ce9, 0x57001ce9}, + {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5a001ceb, 0x5a001ceb}, + {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x5e001eeb, 0x5e001eeb}, + {0x0000a560, 0x900fff0b, 0x900fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a564, 0x960fffcb, 0x960fffcb, 0x5e001eeb, 0x5e001eeb}, + {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a580, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a584, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a588, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a58c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a590, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a594, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a598, 0x00000000, 0x00000000, 0x01404501, 0x01404501}, + {0x0000a59c, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02}, + {0x0000a5a0, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02}, + {0x0000a5a4, 0x00000000, 0x00000000, 0x02808803, 0x02808803}, + {0x0000a5a8, 0x00000000, 0x00000000, 0x04c14b04, 0x04c14b04}, + {0x0000a5ac, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b0, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b4, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b8, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5bc, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000b500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b504, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b508, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b50c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b510, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b514, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b518, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b51c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b520, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b524, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b528, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b52c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b530, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b534, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b538, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b53c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b540, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b544, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b548, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b54c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b550, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b554, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b558, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b55c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b560, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b564, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b568, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b56c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b570, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b574, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b578, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b57c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00016044, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db}, + {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, +}; -#define ar9485Modes_low_ob_db_tx_gain_1_1 ar9485Modes_high_ob_db_tx_gain_1_1 +static const u32 ar9485Modes_low_ob_db_tx_gain_1_1[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, + {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0x7999a83a, 0x7999a83a}, + {0x0000a2dc, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e0, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e4, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e8, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, + {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, + {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, + {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, + {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200}, + {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202}, + {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400}, + {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402}, + {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404}, + {0x0000a520, 0x2f001f04, 0x2f001f04, 0x21000603, 0x21000603}, + {0x0000a524, 0x35001fc4, 0x35001fc4, 0x25000605, 0x25000605}, + {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2a000a03, 0x2a000a03}, + {0x0000a52c, 0x41023e85, 0x41023e85, 0x2c000a04, 0x2c000a04}, + {0x0000a530, 0x48023ec6, 0x48023ec6, 0x34000e20, 0x34000e20}, + {0x0000a534, 0x4d023f01, 0x4d023f01, 0x35000e21, 0x35000e21}, + {0x0000a538, 0x53023f4b, 0x53023f4b, 0x43000e62, 0x43000e62}, + {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x45000e63, 0x45000e63}, + {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x49000e65, 0x49000e65}, + {0x0000a544, 0x6502feca, 0x6502feca, 0x4b000e66, 0x4b000e66}, + {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x4d001645, 0x4d001645}, + {0x0000a54c, 0x7203feca, 0x7203feca, 0x51001865, 0x51001865}, + {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x55001a86, 0x55001a86}, + {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x57001ce9, 0x57001ce9}, + {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5a001ceb, 0x5a001ceb}, + {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x5e001eeb, 0x5e001eeb}, + {0x0000a560, 0x900fff0b, 0x900fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a564, 0x960fffcb, 0x960fffcb, 0x5e001eeb, 0x5e001eeb}, + {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a580, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a584, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a588, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a58c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a590, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a594, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a598, 0x00000000, 0x00000000, 0x01404501, 0x01404501}, + {0x0000a59c, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02}, + {0x0000a5a0, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02}, + {0x0000a5a4, 0x00000000, 0x00000000, 0x02808803, 0x02808803}, + {0x0000a5a8, 0x00000000, 0x00000000, 0x04c14b04, 0x04c14b04}, + {0x0000a5ac, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b0, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b4, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b8, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5bc, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000b500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b504, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b508, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b50c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b510, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b514, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b518, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b51c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b520, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b524, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b528, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b52c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b530, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b534, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b538, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b53c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b540, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b544, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b548, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b54c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b550, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b554, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b558, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b55c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b560, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b564, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b568, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b56c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b570, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b574, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b578, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b57c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00016044, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db}, + {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, +}; #define ar9485_modes_lowest_ob_db_tx_gain_1_1 ar9485Modes_low_ob_db_tx_gain_1_1 @@ -245,19 +455,19 @@ static const u32 ar9485_1_1[][2] = { {0x0000a580, 0x00000000}, {0x0000a584, 0x00000000}, {0x0000a588, 0x00000000}, - {0x0000a58c, 0x00000000}, - {0x0000a590, 0x00000000}, - {0x0000a594, 0x00000000}, - {0x0000a598, 0x00000000}, - {0x0000a59c, 0x00000000}, - {0x0000a5a0, 0x00000000}, - {0x0000a5a4, 0x00000000}, - {0x0000a5a8, 0x00000000}, - {0x0000a5ac, 0x00000000}, - {0x0000a5b0, 0x00000000}, - {0x0000a5b4, 0x00000000}, - {0x0000a5b8, 0x00000000}, - {0x0000a5bc, 0x00000000}, + {0x0000a58c, 0x01804000}, + {0x0000a590, 0x02808a02}, + {0x0000a594, 0x0340ca02}, + {0x0000a598, 0x0340cd03}, + {0x0000a59c, 0x0340cd03}, + {0x0000a5a0, 0x06415304}, + {0x0000a5a4, 0x04c11905}, + {0x0000a5a8, 0x06415905}, + {0x0000a5ac, 0x06415905}, + {0x0000a5b0, 0x06415905}, + {0x0000a5b4, 0x06415905}, + {0x0000a5b8, 0x06415905}, + {0x0000a5bc, 0x06415905}, }; static const u32 ar9485_1_1_radio_core[][2] = { @@ -340,7 +550,7 @@ static const u32 ar9485_1_1_baseband_core[][2] = { {0x00009880, 0x201fff00}, {0x00009884, 0x00001042}, {0x000098a4, 0x00200400}, - {0x000098b0, 0x52440bbe}, + {0x000098b0, 0x32840bbe}, {0x000098d0, 0x004b6a8e}, {0x000098d4, 0x00000820}, {0x000098dc, 0x00000000}, @@ -362,7 +572,7 @@ static const u32 ar9485_1_1_baseband_core[][2] = { {0x00009d18, 0x00000000}, {0x00009d1c, 0x00000000}, {0x00009e08, 0x0038233c}, - {0x00009e24, 0x9927b515}, + {0x00009e24, 0x992bb515}, {0x00009e28, 0x12ef0200}, {0x00009e30, 0x06336f77}, {0x00009e34, 0x6af6532f}, @@ -427,7 +637,7 @@ static const u32 ar9485_1_1_baseband_core[][2] = { {0x0000a408, 0x0e79e5c6}, {0x0000a40c, 0x00820820}, {0x0000a414, 0x1ce739cf}, - {0x0000a418, 0x2d0019ce}, + {0x0000a418, 0x2d0021ce}, {0x0000a41c, 0x1ce739ce}, {0x0000a420, 0x000001ce}, {0x0000a424, 0x1ce739ce}, @@ -443,8 +653,8 @@ static const u32 ar9485_1_1_baseband_core[][2] = { {0x0000a44c, 0x00000001}, {0x0000a450, 0x00010000}, {0x0000a5c4, 0xbfad9d74}, - {0x0000a5c8, 0x0048060a}, - {0x0000a5cc, 0x00000637}, + {0x0000a5c8, 0x00480605}, + {0x0000a5cc, 0x00002e37}, {0x0000a760, 0x03020100}, {0x0000a764, 0x09080504}, {0x0000a768, 0x0d0c0b0a}, @@ -464,17 +674,22 @@ static const u32 ar9485_1_1_baseband_core[][2] = { static const u32 ar9485_common_rx_gain_1_1[][2] = { /* Addr allmodes */ - {0x0000a000, 0x00010000}, - {0x0000a004, 0x00030002}, - {0x0000a008, 0x00050004}, - {0x0000a00c, 0x00810080}, - {0x0000a010, 0x01800082}, - {0x0000a014, 0x01820181}, - {0x0000a018, 0x01840183}, - {0x0000a01c, 0x01880185}, - {0x0000a020, 0x018a0189}, - {0x0000a024, 0x02850284}, - {0x0000a028, 0x02890288}, + {0x00009e00, 0x03721b20}, + {0x00009e04, 0x00082020}, + {0x00009e18, 0x0300501e}, + {0x00009e2c, 0x00002e21}, + {0x00009e44, 0x02182324}, + {0x0000a000, 0x00060005}, + {0x0000a004, 0x00810080}, + {0x0000a008, 0x00830082}, + {0x0000a00c, 0x00850084}, + {0x0000a010, 0x01820181}, + {0x0000a014, 0x01840183}, + {0x0000a018, 0x01880185}, + {0x0000a01c, 0x018a0189}, + {0x0000a020, 0x02850284}, + {0x0000a024, 0x02890288}, + {0x0000a028, 0x028b028a}, {0x0000a02c, 0x03850384}, {0x0000a030, 0x03890388}, {0x0000a034, 0x038b038a}, @@ -496,15 +711,15 @@ static const u32 ar9485_common_rx_gain_1_1[][2] = { {0x0000a074, 0x00000000}, {0x0000a078, 0x00000000}, {0x0000a07c, 0x00000000}, - {0x0000a080, 0x28282828}, - {0x0000a084, 0x28282828}, - {0x0000a088, 0x28282828}, - {0x0000a08c, 0x28282828}, - {0x0000a090, 0x28282828}, - {0x0000a094, 0x21212128}, - {0x0000a098, 0x171c1c1c}, - {0x0000a09c, 0x02020212}, - {0x0000a0a0, 0x00000202}, + {0x0000a080, 0x18181818}, + {0x0000a084, 0x18181818}, + {0x0000a088, 0x18181818}, + {0x0000a08c, 0x18181818}, + {0x0000a090, 0x18181818}, + {0x0000a094, 0x18181818}, + {0x0000a098, 0x17181818}, + {0x0000a09c, 0x02020b0b}, + {0x0000a0a0, 0x02020202}, {0x0000a0a4, 0x00000000}, {0x0000a0a8, 0x00000000}, {0x0000a0ac, 0x00000000}, @@ -512,22 +727,22 @@ static const u32 ar9485_common_rx_gain_1_1[][2] = { {0x0000a0b4, 0x00000000}, {0x0000a0b8, 0x00000000}, {0x0000a0bc, 0x00000000}, - {0x0000a0c0, 0x001f0000}, - {0x0000a0c4, 0x111f1100}, - {0x0000a0c8, 0x111d111e}, - {0x0000a0cc, 0x111b111c}, - {0x0000a0d0, 0x22032204}, - {0x0000a0d4, 0x22012202}, - {0x0000a0d8, 0x221f2200}, - {0x0000a0dc, 0x221d221e}, - {0x0000a0e0, 0x33013302}, - {0x0000a0e4, 0x331f3300}, - {0x0000a0e8, 0x4402331e}, - {0x0000a0ec, 0x44004401}, - {0x0000a0f0, 0x441e441f}, - {0x0000a0f4, 0x55015502}, - {0x0000a0f8, 0x551f5500}, - {0x0000a0fc, 0x6602551e}, + {0x0000a0c0, 0x22072208}, + {0x0000a0c4, 0x22052206}, + {0x0000a0c8, 0x22032204}, + {0x0000a0cc, 0x22012202}, + {0x0000a0d0, 0x221f2200}, + {0x0000a0d4, 0x221d221e}, + {0x0000a0d8, 0x33023303}, + {0x0000a0dc, 0x33003301}, + {0x0000a0e0, 0x331e331f}, + {0x0000a0e4, 0x4402331d}, + {0x0000a0e8, 0x44004401}, + {0x0000a0ec, 0x441e441f}, + {0x0000a0f0, 0x55025503}, + {0x0000a0f4, 0x55005501}, + {0x0000a0f8, 0x551e551f}, + {0x0000a0fc, 0x6602551d}, {0x0000a100, 0x66006601}, {0x0000a104, 0x661e661f}, {0x0000a108, 0x7703661d}, @@ -636,17 +851,12 @@ static const u32 ar9485_1_1_baseband_postamble[][5] = { {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, {0x00009830, 0x0000059c, 0x0000059c, 0x0000059c, 0x0000059c}, {0x00009c00, 0x00000044, 0x00000044, 0x00000044, 0x00000044}, - {0x00009e00, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0}, - {0x00009e04, 0x00182020, 0x00182020, 0x00182020, 0x00182020}, {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec80d2e, 0x7ec80d2e}, - {0x00009e14, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e}, - {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00009e14, 0x31395d53, 0x31396053, 0x312e6053, 0x312e5d53}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, - {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, {0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946222, 0xcf946222}, - {0x00009e44, 0x02321e27, 0x02321e27, 0x02282324, 0x02282324}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302010, 0x50302010}, {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, {0x0000a204, 0x01303fc0, 0x01303fc4, 0x01303fc4, 0x01303fc0}, -- cgit v1.2.3-70-g09d2 From b126b02796eaac8534b699571bd4209e05b64146 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 12 Nov 2012 10:56:44 +0530 Subject: ath9k: Remove unused workaround The workaround for ASPM/L0s is needed only for AR9485 1.0, which was never sold and is not supported by ath9k. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 5 ----- drivers/net/wireless/ath/ath9k/hw.h | 3 --- drivers/net/wireless/ath/ath9k/main.c | 3 --- drivers/net/wireless/ath/ath9k/pci.c | 12 ------------ 4 files changed, 23 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 71cd9f0c96a..756191b9eed 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2561,11 +2561,6 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) pCap->hw_caps |= ATH9K_HW_CAP_ANT_DIV_COMB; } - if (AR_SREV_9485_10(ah)) { - pCap->pcie_lcr_extsync_en = true; - pCap->pcie_lcr_offset = 0x80; - } - if (ath9k_hw_dfs_tested(ah)) pCap->hw_caps |= ATH9K_HW_CAP_DFS; diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 3e73bfe2315..bdabbda5a83 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -273,8 +273,6 @@ struct ath9k_hw_capabilities { u8 rx_status_len; u8 tx_desc_len; u8 txs_len; - u16 pcie_lcr_offset; - bool pcie_lcr_extsync_en; }; struct ath9k_ops_config { @@ -930,7 +928,6 @@ struct ath_bus_ops { void (*read_cachesize)(struct ath_common *common, int *csz); bool (*eeprom_read)(struct ath_common *common, u32 off, u16 *data); void (*bt_coex_prep)(struct ath_common *common); - void (*extn_synch_en)(struct ath_common *common); void (*aspm_init)(struct ath_common *common); }; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index c084532291a..9594b6fcdf0 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -686,9 +686,6 @@ static int ath9k_start(struct ieee80211_hw *hw) spin_unlock_bh(&sc->sc_pcu_lock); - if (ah->caps.pcie_lcr_extsync_en && common->bus_ops->extn_synch_en) - common->bus_ops->extn_synch_en(common); - mutex_unlock(&sc->mutex); ath9k_ps_restore(sc); diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index f088f4bf9a2..9553203ee62 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -96,17 +96,6 @@ static bool ath_pci_eeprom_read(struct ath_common *common, u32 off, u16 *data) return true; } -static void ath_pci_extn_synch_enable(struct ath_common *common) -{ - struct ath_softc *sc = (struct ath_softc *) common->priv; - struct pci_dev *pdev = to_pci_dev(sc->dev); - u8 lnkctl; - - pci_read_config_byte(pdev, sc->sc_ah->caps.pcie_lcr_offset, &lnkctl); - lnkctl |= PCI_EXP_LNKCTL_ES; - pci_write_config_byte(pdev, sc->sc_ah->caps.pcie_lcr_offset, lnkctl); -} - /* Need to be called after we discover btcoex capabilities */ static void ath_pci_aspm_init(struct ath_common *common) { @@ -153,7 +142,6 @@ static const struct ath_bus_ops ath_pci_bus_ops = { .ath_bus_type = ATH_PCI, .read_cachesize = ath_pci_read_cachesize, .eeprom_read = ath_pci_eeprom_read, - .extn_synch_en = ath_pci_extn_synch_enable, .aspm_init = ath_pci_aspm_init, }; -- cgit v1.2.3-70-g09d2 From c32f5bbb00cf268f76e977d8cf9c75fa3a89c038 Mon Sep 17 00:00:00 2001 From: Syam Sidhardhan Date: Tue, 13 Nov 2012 21:09:50 +0530 Subject: ath5k: Use module_platform_driver macro for ahb.c Simplify the code by make use of module_platform_driver macro. Signed-off-by: Syam Sidhardhan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/ahb.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/ahb.c b/drivers/net/wireless/ath/ath5k/ahb.c index aec33cc207f..8e8bcc7a480 100644 --- a/drivers/net/wireless/ath/ath5k/ahb.c +++ b/drivers/net/wireless/ath/ath5k/ahb.c @@ -236,17 +236,4 @@ static struct platform_driver ath_ahb_driver = { }, }; -static int __init -ath5k_ahb_init(void) -{ - return platform_driver_register(&ath_ahb_driver); -} - -static void __exit -ath5k_ahb_exit(void) -{ - platform_driver_unregister(&ath_ahb_driver); -} - -module_init(ath5k_ahb_init); -module_exit(ath5k_ahb_exit); +module_platform_driver(ath_ahb_driver); -- cgit v1.2.3-70-g09d2 From 957708f1a2d3fd41021ea0282e1cd856d23df9ca Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 14 Nov 2012 11:23:07 +0300 Subject: ath5k: precedence error in ath5k_hw_nic_wakeup() '|' has higher precedence than ?:. Since AR5K_PHY_TURBO_MODE is 0x1 and "AR5K_PHY_TURBO_MODE | (ah->ah_radio == AR5K_RF2425)" is true then we always set turbo to zero. Signed-off-by: Dan Carpenter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/reset.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c index 0c2dd4771c3..4084b107628 100644 --- a/drivers/net/wireless/ath/ath5k/reset.c +++ b/drivers/net/wireless/ath/ath5k/reset.c @@ -789,9 +789,9 @@ ath5k_hw_nic_wakeup(struct ath5k_hw *ah, struct ieee80211_channel *channel) * (I don't think it supports 44MHz) */ /* On 2425 initvals TURBO_SHORT is not present */ if (ah->ah_bwmode == AR5K_BWMODE_40MHZ) { - turbo = AR5K_PHY_TURBO_MODE | - (ah->ah_radio == AR5K_RF2425) ? 0 : - AR5K_PHY_TURBO_SHORT; + turbo = AR5K_PHY_TURBO_MODE; + if (ah->ah_radio != AR5K_RF2425) + turbo |= AR5K_PHY_TURBO_SHORT; } else if (ah->ah_bwmode != AR5K_BWMODE_DEFAULT) { if (ah->ah_radio == AR5K_RF5413) { mode |= (ah->ah_bwmode == AR5K_BWMODE_10MHZ) ? -- cgit v1.2.3-70-g09d2 From 57527f8d4d71b5167a02fb6713857f55d3974748 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Tue, 13 Nov 2012 11:33:53 +0530 Subject: ath9k_hw: Program filter coefficients correctly 2484 Mhz (Japan) usage requires filter coefficients to be programmed in the CCK TX FIR registers. This is required for AR9331, AR9485 and AR9462. Fix this and also remove a few useless macros and a duplicate variable. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_hw.c | 22 +++++++++++++--------- drivers/net/wireless/ath/ath9k/ar9003_phy.c | 2 +- drivers/net/wireless/ath/ath9k/ar9485_initvals.h | 2 ++ drivers/net/wireless/ath/ath9k/hw.h | 1 - 4 files changed, 16 insertions(+), 11 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index 0693cd95b74..74fd3977fee 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -35,12 +35,6 @@ */ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) { -#define AR9462_BB_CTX_COEFJ(x) \ - ar9462_##x##_baseband_core_txfir_coeff_japan_2484 - -#define AR9462_BBC_TXIFR_COEFFJ \ - ar9462_2p0_baseband_core_txfir_coeff_japan_2484 - if (AR_SREV_9330_11(ah)) { /* mac */ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], @@ -70,6 +64,10 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9331_modes_lowest_ob_db_tx_gain_1p1); + /* Japan 2484 Mhz CCK */ + INIT_INI_ARRAY(&ah->iniCckfirJapan2484, + ar9331_1p1_baseband_core_txfir_coeff_japan_2484); + /* additional clock settings */ if (ah->is_clk_25mhz) INIT_INI_ARRAY(&ah->iniAdditional, @@ -106,6 +104,10 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9331_modes_lowest_ob_db_tx_gain_1p2); + /* Japan 2484 Mhz CCK */ + INIT_INI_ARRAY(&ah->iniCckfirJapan2484, + ar9331_1p2_baseband_core_txfir_coeff_japan_2484); + /* additional clock settings */ if (ah->is_clk_25mhz) INIT_INI_ARRAY(&ah->iniAdditional, @@ -180,6 +182,10 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9485_modes_lowest_ob_db_tx_gain_1_1); + /* Japan 2484 Mhz CCK */ + INIT_INI_ARRAY(&ah->iniCckfirJapan2484, + ar9485_1_1_baseband_core_txfir_coeff_japan_2484); + /* Load PCIE SERDES settings from INI */ /* Awake Setting */ @@ -229,9 +235,7 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) ar9462_modes_fast_clock_2p0); INIT_INI_ARRAY(&ah->iniCckfirJapan2484, - AR9462_BB_CTX_COEFJ(2p0)); - - INIT_INI_ARRAY(&ah->ini_japan2484, AR9462_BBC_TXIFR_COEFFJ); + ar9462_2p0_baseband_core_txfir_coeff_japan_2484); } else if (AR_SREV_9550(ah)) { /* mac */ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 759f5f5a715..ce19c09fa8e 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -784,7 +784,7 @@ static int ar9003_hw_process_ini(struct ath_hw *ah, REG_WRITE_ARRAY(&ah->iniAdditional, 1, regWrites); if (chan->channel == 2484) - ar9003_hw_prog_ini(ah, &ah->ini_japan2484, 1); + ar9003_hw_prog_ini(ah, &ah->iniCckfirJapan2484, 1); if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) REG_WRITE(ah, AR_GLB_SWREG_DISCONT_MODE, diff --git a/drivers/net/wireless/ath/ath9k/ar9485_initvals.h b/drivers/net/wireless/ath/ath9k/ar9485_initvals.h index 02e4d977bd4..a3710f3bb90 100644 --- a/drivers/net/wireless/ath/ath9k/ar9485_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9485_initvals.h @@ -1060,4 +1060,6 @@ static const u32 ar9485_1_1_mac_core[][2] = { {0x000083d0, 0x000301ff}, }; +#define ar9485_1_1_baseband_core_txfir_coeff_japan_2484 ar9462_2p0_baseband_core_txfir_coeff_japan_2484 + #endif /* INITVALS_9485_H */ diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index bdabbda5a83..3636dabf03e 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -875,7 +875,6 @@ struct ath_hw { struct ar5416IniArray iniModesTxGain; struct ar5416IniArray iniCckfirNormal; struct ar5416IniArray iniCckfirJapan2484; - struct ar5416IniArray ini_japan2484; struct ar5416IniArray iniModes_9271_ANI_reg; struct ar5416IniArray ini_radio_post_sys2ant; -- cgit v1.2.3-70-g09d2 From ac46ba4384d2c44c56702d0da591696045fba682 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 19 Nov 2012 14:24:46 +0530 Subject: ath9k: Fix BTCOEX debugfs file usage The debugfs file for dumping btcoex parameters unconditionally assumes a MCI-based device. This will not work for older btcoex chips. Fix this by branching out the routine into separate functions. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 11 ++++++-- drivers/net/wireless/ath/ath9k/debug.c | 8 +++++- drivers/net/wireless/ath/ath9k/gpio.c | 51 +++++++++++++++++++++++----------- 3 files changed, 50 insertions(+), 20 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 4e125d8904a..9a93d2bb8f6 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -461,6 +461,12 @@ void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type); /* BTCOEX */ /**********/ +#define ATH_DUMP_BTCOEX(_s, _val) \ + do { \ + len += snprintf(buf + len, size - len, \ + "%20s : %10d\n", _s, (_val)); \ + } while (0) + enum bt_op_flags { BT_OP_PRIORITY_DETECTED, BT_OP_SCAN, @@ -494,7 +500,7 @@ void ath9k_btcoex_timer_pause(struct ath_softc *sc); void ath9k_btcoex_handle_interrupt(struct ath_softc *sc, u32 status); u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen); void ath9k_btcoex_stop_gen_timer(struct ath_softc *sc); -int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 len, u32 size); +int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 size); #else static inline int ath9k_init_btcoex(struct ath_softc *sc) { @@ -521,8 +527,7 @@ static inline u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, static inline void ath9k_btcoex_stop_gen_timer(struct ath_softc *sc) { } -static inline int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, - u32 len, u32 size) +static inline int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 size) { return 0; } diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index a8be94b2a53..585aee47860 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -1599,8 +1599,14 @@ static ssize_t read_file_btcoex(struct file *file, char __user *user_buf, if (buf == NULL) return -ENOMEM; - len = ath9k_dump_btcoex(sc, buf, len, size); + if (!sc->sc_ah->common.btcoex_enabled) { + len = snprintf(buf, size, "%s\n", + "BTCOEX is disabled"); + goto exit; + } + len = ath9k_dump_btcoex(sc, buf, size); +exit: retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index a8ea57b9f49..4236df8ffe6 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -494,35 +494,31 @@ int ath9k_init_btcoex(struct ath_softc *sc) return 0; } -int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 len, u32 size) +static int ath9k_dump_mci_btcoex(struct ath_softc *sc, u8 *buf, u32 size) { -#define ATH_DUMP_BTCOEX(_s, _val) \ - do { \ - len += snprintf(buf + len, size - len, \ - "%20s : %10d\n", _s, (_val)); \ - } while (0) - struct ath_btcoex *btcoex = &sc->btcoex; struct ath_mci_profile *mci = &btcoex->mci; struct ath_hw *ah = sc->sc_ah; struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; + u32 len = 0; int i; ATH_DUMP_BTCOEX("Total BT profiles", NUM_PROF(mci)); - ATH_DUMP_BTCOEX("Number of MGMT", mci->num_mgmt); - ATH_DUMP_BTCOEX("Number of SCO", mci->num_sco); - ATH_DUMP_BTCOEX("Number of A2DP", mci->num_a2dp); - ATH_DUMP_BTCOEX("Number of HID", mci->num_hid); - ATH_DUMP_BTCOEX("Number of PAN", mci->num_pan); - ATH_DUMP_BTCOEX("Number of ACL", mci->num_other_acl); - ATH_DUMP_BTCOEX("Number of BDR", mci->num_bdr); + ATH_DUMP_BTCOEX("MGMT", mci->num_mgmt); + ATH_DUMP_BTCOEX("SCO", mci->num_sco); + ATH_DUMP_BTCOEX("A2DP", mci->num_a2dp); + ATH_DUMP_BTCOEX("HID", mci->num_hid); + ATH_DUMP_BTCOEX("PAN", mci->num_pan); + ATH_DUMP_BTCOEX("ACL", mci->num_other_acl); + ATH_DUMP_BTCOEX("BDR", mci->num_bdr); ATH_DUMP_BTCOEX("Aggr. Limit", mci->aggr_limit); ATH_DUMP_BTCOEX("Stomp Type", btcoex->bt_stomp_type); ATH_DUMP_BTCOEX("BTCoex Period (msec)", btcoex->btcoex_period); ATH_DUMP_BTCOEX("Duty Cycle", btcoex->duty_cycle); ATH_DUMP_BTCOEX("BT Wait time", btcoex->bt_wait_time); ATH_DUMP_BTCOEX("Concurrent Tx", btcoex_hw->mci.concur_tx); - ATH_DUMP_BTCOEX("Concur RSSI count", btcoex->rssi_count); + ATH_DUMP_BTCOEX("Concurrent RSSI cnt", btcoex->rssi_count); + len += snprintf(buf + len, size - len, "BT Weights: "); for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++) len += snprintf(buf + len, size - len, "%08x ", @@ -537,9 +533,32 @@ int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 len, u32 size) for (i = 0; i < ATH_BTCOEX_STOMP_MAX; i++) len += snprintf(buf + len, size - len, "%08x ", btcoex_hw->tx_prio[i]); + len += snprintf(buf + len, size - len, "\n"); -#undef ATH_DUMP_BTCOEX return len; } + +static int ath9k_dump_legacy_btcoex(struct ath_softc *sc, u8 *buf, u32 size) +{ + + struct ath_btcoex *btcoex = &sc->btcoex; + u32 len = 0; + + ATH_DUMP_BTCOEX("Stomp Type", btcoex->bt_stomp_type); + ATH_DUMP_BTCOEX("BTCoex Period (msec)", btcoex->btcoex_period); + ATH_DUMP_BTCOEX("Duty Cycle", btcoex->duty_cycle); + ATH_DUMP_BTCOEX("BT Wait time", btcoex->bt_wait_time); + + return len; +} + +int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 size) +{ + if (ath9k_hw_mci_is_enabled(sc->sc_ah)) + return ath9k_dump_mci_btcoex(sc, buf, size); + else + return ath9k_dump_legacy_btcoex(sc, buf, size); +} + #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ -- cgit v1.2.3-70-g09d2 From ca90ef443cab31a5481d8a7f85514d28368fef44 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 20 Nov 2012 18:29:59 +0530 Subject: ath9k: Process FATAL interrupts at first FATAL and WATCHDOG interrupts should be processed first followed by others. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 9594b6fcdf0..e59d8e3fcbc 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -494,17 +494,6 @@ irqreturn_t ath_isr(int irq, void *dev) if (status & SCHED_INTR) sched = true; -#ifdef CONFIG_PM_SLEEP - if (status & ATH9K_INT_BMISS) { - if (atomic_read(&sc->wow_sleep_proc_intr) == 0) { - ath_dbg(common, ANY, "during WoW we got a BMISS\n"); - atomic_inc(&sc->wow_got_bmiss_intr); - atomic_dec(&sc->wow_sleep_proc_intr); - } - ath_dbg(common, INTERRUPT, "beacon miss interrupt\n"); - } -#endif - /* * If a FATAL or RXORN interrupt is received, we have to reset the * chip immediately. @@ -523,7 +512,15 @@ irqreturn_t ath_isr(int irq, void *dev) goto chip_reset; } - +#ifdef CONFIG_PM_SLEEP + if (status & ATH9K_INT_BMISS) { + if (atomic_read(&sc->wow_sleep_proc_intr) == 0) { + ath_dbg(common, ANY, "during WoW we got a BMISS\n"); + atomic_inc(&sc->wow_got_bmiss_intr); + atomic_dec(&sc->wow_sleep_proc_intr); + } + } +#endif if (status & ATH9K_INT_SWBA) tasklet_schedule(&sc->bcon_tasklet); -- cgit v1.2.3-70-g09d2 From b88083bfb37297330240a478bef76316ee3f1b9b Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 20 Nov 2012 18:30:00 +0530 Subject: ath9k: Fix MCI reset in BT cal_req This patch reverts the commit "ath9k_hw: Wait BT calibration to complete" and bail out from MCI interrupt routine for chip reset. The above commit stalls the WLAN TCP traffic while bringing up and down the BT interface iteratively. Fixing this properly by queueing up chip reset and bailing out properly from tasklet routine. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 1 - drivers/net/wireless/ath/ath9k/debug.c | 3 +++ drivers/net/wireless/ath/ath9k/debug.h | 1 + drivers/net/wireless/ath/ath9k/mci.c | 21 +++------------------ 4 files changed, 7 insertions(+), 19 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 42b4412d679..8dd069259e7 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -714,7 +714,6 @@ bool ar9003_mci_start_reset(struct ath_hw *ah, struct ath9k_channel *chan) return true; } -EXPORT_SYMBOL(ar9003_mci_start_reset); int ar9003_mci_end_reset(struct ath_hw *ah, struct ath9k_channel *chan, struct ath9k_hw_cal_data *caldata) diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 585aee47860..eed0b9ab8fe 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -837,6 +837,9 @@ static ssize_t read_file_reset(struct file *file, char __user *user_buf, len += snprintf(buf + len, sizeof(buf) - len, "%17s: %2d\n", "PLL RX Hang", sc->debug.stats.reset[RESET_TYPE_PLL_HANG]); + len += snprintf(buf + len, sizeof(buf) - len, + "%17s: %2d\n", "MCI Reset", + sc->debug.stats.reset[RESET_TYPE_MCI]); if (len > sizeof(buf)) len = sizeof(buf); diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 2ed9785a38f..61341cda131 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -41,6 +41,7 @@ enum ath_reset_type { RESET_TYPE_PLL_HANG, RESET_TYPE_MAC_HANG, RESET_TYPE_BEACON_STUCK, + RESET_TYPE_MCI, __RESET_TYPE_MAX }; diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 0dd2cbb52d6..ece192d7735 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -207,23 +207,6 @@ skip_tuning: ath9k_btcoex_timer_resume(sc); } -static void ath_mci_wait_btcal_done(struct ath_softc *sc) -{ - struct ath_hw *ah = sc->sc_ah; - - /* Stop tx & rx */ - ieee80211_stop_queues(sc->hw); - ath_stoprecv(sc); - ath_drain_all_txq(sc, false); - - /* Wait for cal done */ - ar9003_mci_start_reset(ah, ah->curchan); - - /* Resume tx & rx */ - ath_startrecv(sc); - ieee80211_wake_queues(sc->hw); -} - static void ath_mci_cal_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) { struct ath_hw *ah = sc->sc_ah; @@ -235,7 +218,7 @@ static void ath_mci_cal_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) case MCI_GPM_BT_CAL_REQ: if (mci_hw->bt_state == MCI_BT_AWAKE) { mci_hw->bt_state = MCI_BT_CAL_START; - ath_mci_wait_btcal_done(sc); + ath9k_queue_reset(sc, RESET_TYPE_MCI); } ath_dbg(common, MCI, "MCI State : %d\n", mci_hw->bt_state); break; @@ -578,6 +561,8 @@ void ath_mci_intr(struct ath_softc *sc) mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_GPM; while (more_data == MCI_GPM_MORE) { + if (test_bit(SC_OP_HW_RESET, &sc->sc_flags)) + return; pgpm = mci->gpm_buf.bf_addr; offset = ar9003_mci_get_next_gpm_offset(ah, false, -- cgit v1.2.3-70-g09d2 From 2884561a6472d6f9c960ccf2250a72ca058167a1 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 20 Nov 2012 18:30:01 +0530 Subject: ath9k: stomp audio profiles on weak signal strength On lower WLAN signal strength, WLAN downlink traffic might suffer from retransmissions. At the mean time, playing SCO/A2DP profiles is affecting WLAN stability. In such scenario, by stomping SCO/A2DP BT traffic completely for a BTCOEX period, gives WLAN traffic an oppertunity to recover PHY rate. It also improves WLAN stability at lower RSSI without sacificing BT traffic. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 + drivers/net/wireless/ath/ath9k/btcoex.c | 1 + drivers/net/wireless/ath/ath9k/btcoex.h | 1 + drivers/net/wireless/ath/ath9k/gpio.c | 5 ++++- drivers/net/wireless/ath/ath9k/mci.c | 18 ++++++++++++++++++ 5 files changed, 25 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 9a93d2bb8f6..04fa5f350e8 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -488,6 +488,7 @@ struct ath_btcoex { int rssi_count; struct ath_gen_timer *no_stomp_timer; /* Timer for no BT stomping */ struct ath_mci_profile mci; + u8 stomp_audio; }; #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT diff --git a/drivers/net/wireless/ath/ath9k/btcoex.c b/drivers/net/wireless/ath/ath9k/btcoex.c index c90e9bc4b02..9963b0bf9f7 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.c +++ b/drivers/net/wireless/ath/ath9k/btcoex.c @@ -49,6 +49,7 @@ static const u32 mci_wlan_weights[ATH_BTCOEX_STOMP_MAX] { 0x01017d01, 0x3b3b3b01, 0x3b3b3b01, 0x3b3b3b3b }, /* STOMP_LOW */ { 0x01017d01, 0x01010101, 0x01010101, 0x01010101 }, /* STOMP_NONE */ { 0x01017d01, 0x013b0101, 0x3b3b0101, 0x3b3b013b }, /* STOMP_LOW_FTP */ + { 0xffffff01, 0xffffffff, 0xffffff01, 0xffffffff }, /* STOMP_AUDIO */ }; void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum) diff --git a/drivers/net/wireless/ath/ath9k/btcoex.h b/drivers/net/wireless/ath/ath9k/btcoex.h index 2f84ab273d0..6de26ea5d5f 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.h +++ b/drivers/net/wireless/ath/ath9k/btcoex.h @@ -50,6 +50,7 @@ enum ath_stomp_type { ATH_BTCOEX_STOMP_LOW, ATH_BTCOEX_STOMP_NONE, ATH_BTCOEX_STOMP_LOW_FTP, + ATH_BTCOEX_STOMP_AUDIO, ATH_BTCOEX_STOMP_MAX }; diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index 4236df8ffe6..7b39cc14662 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -247,6 +247,9 @@ static void ath_btcoex_period_timer(unsigned long data) stomp_type = ATH_BTCOEX_STOMP_ALL; timer_period = btcoex->btscan_no_stomp; } + } else if (btcoex->stomp_audio >= 5) { + stomp_type = ATH_BTCOEX_STOMP_AUDIO; + btcoex->stomp_audio = 0; } ath9k_hw_btcoex_bt_stomp(ah, stomp_type); @@ -295,7 +298,7 @@ static void ath_btcoex_no_stomp_timer(void *arg) (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI) && test_bit(BT_OP_SCAN, &btcoex->op_flags))) ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE); - else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) + else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_LOW); ath9k_hw_btcoex_enable(ah); diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index ece192d7735..706378ea3ba 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -729,12 +729,30 @@ void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel, ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false); } +static void ath9k_mci_stomp_audio(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_btcoex *btcoex = &sc->btcoex; + struct ath_mci_profile *mci = &btcoex->mci; + + if (!mci->num_sco && !mci->num_a2dp) + return; + + if (ah->stats.avgbrssi > 25) { + btcoex->stomp_audio = 0; + return; + } + + btcoex->stomp_audio++; +} void ath9k_mci_update_rssi(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_btcoex *btcoex = &sc->btcoex; struct ath9k_hw_mci *mci_hw = &sc->sc_ah->btcoex_hw.mci; + ath9k_mci_stomp_audio(sc); + if (!(mci_hw->config & ATH_MCI_CONFIG_CONCUR_TX)) return; -- cgit v1.2.3-70-g09d2 From bea843c73854becf998047a83af22a90de3fd19b Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Wed, 21 Nov 2012 18:13:10 +0530 Subject: ath9k/ath9k_htc: Remove WME macros Use the macros provided by mac80211 and remove redundant declarations inside the drivers. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 16 ++++----- drivers/net/wireless/ath/ath9k/beacon.c | 2 +- drivers/net/wireless/ath/ath9k/common.h | 7 ---- drivers/net/wireless/ath/ath9k/debug.c | 44 ++++++++++++------------- drivers/net/wireless/ath/ath9k/gpio.c | 2 +- drivers/net/wireless/ath/ath9k/htc.h | 4 +-- drivers/net/wireless/ath/ath9k/htc_drv_beacon.c | 2 +- drivers/net/wireless/ath/ath9k/htc_drv_debug.c | 8 ++--- drivers/net/wireless/ath/ath9k/htc_drv_gpio.c | 2 +- drivers/net/wireless/ath/ath9k/htc_drv_init.c | 8 ++--- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 4 +-- drivers/net/wireless/ath/ath9k/htc_drv_txrx.c | 26 +++++++-------- drivers/net/wireless/ath/ath9k/init.c | 2 +- drivers/net/wireless/ath/ath9k/link.c | 2 +- drivers/net/wireless/ath/ath9k/main.c | 26 +++++++-------- drivers/net/wireless/ath/ath9k/xmit.c | 10 +++--- 16 files changed, 79 insertions(+), 86 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 04fa5f350e8..3c1cebe55f6 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -129,10 +129,10 @@ void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd, #define ATH_TXMAXTRY 13 #define TID_TO_WME_AC(_tid) \ - ((((_tid) == 0) || ((_tid) == 3)) ? WME_AC_BE : \ - (((_tid) == 1) || ((_tid) == 2)) ? WME_AC_BK : \ - (((_tid) == 4) || ((_tid) == 5)) ? WME_AC_VI : \ - WME_AC_VO) + ((((_tid) == 0) || ((_tid) == 3)) ? IEEE80211_AC_BE : \ + (((_tid) == 1) || ((_tid) == 2)) ? IEEE80211_AC_BK : \ + (((_tid) == 4) || ((_tid) == 5)) ? IEEE80211_AC_VI : \ + IEEE80211_AC_VO) #define ATH_AGGR_DELIM_SZ 4 #define ATH_AGGR_MINPLEN 256 /* in bytes, minimum packet length */ @@ -265,7 +265,7 @@ struct ath_node { struct ieee80211_sta *sta; /* station struct we're part of */ struct ieee80211_vif *vif; /* interface with which we're associated */ struct ath_atx_tid tid[WME_NUM_TID]; - struct ath_atx_ac ac[WME_NUM_AC]; + struct ath_atx_ac ac[IEEE80211_NUM_ACS]; int ps_key; u16 maxampdu; @@ -299,9 +299,9 @@ struct ath_tx { struct list_head txbuf; struct ath_txq txq[ATH9K_NUM_TX_QUEUES]; struct ath_descdma txdma; - struct ath_txq *txq_map[WME_NUM_AC]; - u32 txq_max_pending[WME_NUM_AC]; - u16 max_aggr_framelen[WME_NUM_AC][4][32]; + struct ath_txq *txq_map[IEEE80211_NUM_ACS]; + u32 txq_max_pending[IEEE80211_NUM_ACS]; + u16 max_aggr_framelen[IEEE80211_NUM_ACS][4][32]; }; struct ath_rx_edma { diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 1b48414dca9..531fffd801a 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -46,7 +46,7 @@ static void ath9k_beaconq_config(struct ath_softc *sc) qi.tqi_cwmax = 0; } else { /* Adhoc mode; important thing is to use 2x cwmin. */ - txq = sc->tx.txq_map[WME_AC_BE]; + txq = sc->tx.txq_map[IEEE80211_AC_BE]; ath9k_hw_get_txq_props(ah, txq->axq_qnum, &qi_be); qi.tqi_aifs = qi_be.tqi_aifs; if (ah->slottime == ATH9K_SLOT_TIME_20) diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h index ad14fecc76c..76b54390031 100644 --- a/drivers/net/wireless/ath/ath9k/common.h +++ b/drivers/net/wireless/ath/ath9k/common.h @@ -28,13 +28,6 @@ #define WME_MAX_BA WME_BA_BMP_SIZE #define ATH_TID_MAX_BUFS (2 * WME_MAX_BA) -/* These must match mac80211 skb queue mapping numbers */ -#define WME_AC_VO 0 -#define WME_AC_VI 1 -#define WME_AC_BE 2 -#define WME_AC_BK 3 -#define WME_NUM_AC 4 - #define ATH_RSSI_DUMMY_MARKER 0x127 #define ATH_RSSI_LPF_LEN 10 #define RSSI_LPF_THRESHOLD -20 diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index eed0b9ab8fe..957e38646fd 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -517,10 +517,10 @@ static const struct file_operations fops_interrupt = { do { \ len += snprintf(buf + len, size - len, \ "%s%13u%11u%10u%10u\n", str, \ - sc->debug.stats.txstats[PR_QNUM(WME_AC_BE)].elem, \ - sc->debug.stats.txstats[PR_QNUM(WME_AC_BK)].elem, \ - sc->debug.stats.txstats[PR_QNUM(WME_AC_VI)].elem, \ - sc->debug.stats.txstats[PR_QNUM(WME_AC_VO)].elem); \ + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BE)].elem, \ + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BK)].elem, \ + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VI)].elem, \ + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VO)].elem); \ if (len >= size) \ goto done; \ } while(0) @@ -529,10 +529,10 @@ static const struct file_operations fops_interrupt = { do { \ len += snprintf(buf + len, size - len, \ "%s%13u%11u%10u%10u\n", str, \ - (unsigned int)(sc->tx.txq_map[WME_AC_BE]->elem), \ - (unsigned int)(sc->tx.txq_map[WME_AC_BK]->elem), \ - (unsigned int)(sc->tx.txq_map[WME_AC_VI]->elem), \ - (unsigned int)(sc->tx.txq_map[WME_AC_VO]->elem)); \ + (unsigned int)(sc->tx.txq_map[IEEE80211_AC_BE]->elem), \ + (unsigned int)(sc->tx.txq_map[IEEE80211_AC_BK]->elem), \ + (unsigned int)(sc->tx.txq_map[IEEE80211_AC_VI]->elem), \ + (unsigned int)(sc->tx.txq_map[IEEE80211_AC_VO]->elem)); \ if (len >= size) \ goto done; \ } while(0) @@ -541,10 +541,10 @@ do { \ do { \ len += snprintf(buf + len, size - len, \ "%s%13i%11i%10i%10i\n", str, \ - list_empty(&sc->tx.txq_map[WME_AC_BE]->elem), \ - list_empty(&sc->tx.txq_map[WME_AC_BK]->elem), \ - list_empty(&sc->tx.txq_map[WME_AC_VI]->elem), \ - list_empty(&sc->tx.txq_map[WME_AC_VO]->elem)); \ + list_empty(&sc->tx.txq_map[IEEE80211_AC_BE]->elem), \ + list_empty(&sc->tx.txq_map[IEEE80211_AC_BK]->elem), \ + list_empty(&sc->tx.txq_map[IEEE80211_AC_VI]->elem), \ + list_empty(&sc->tx.txq_map[IEEE80211_AC_VO]->elem)); \ if (len >= size) \ goto done; \ } while (0) @@ -593,10 +593,10 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf, PR("TX-Failed: ", txfailed); len += snprintf(buf + len, size - len, "%s%11p%11p%10p%10p\n", "txq-memory-address:", - sc->tx.txq_map[WME_AC_BE], - sc->tx.txq_map[WME_AC_BK], - sc->tx.txq_map[WME_AC_VI], - sc->tx.txq_map[WME_AC_VO]); + sc->tx.txq_map[IEEE80211_AC_BE], + sc->tx.txq_map[IEEE80211_AC_BK], + sc->tx.txq_map[IEEE80211_AC_VI], + sc->tx.txq_map[IEEE80211_AC_VO]); if (len >= size) goto done; @@ -617,7 +617,7 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf, } /* Print out more detailed queue-info */ - for (i = 0; i <= WME_AC_BK; i++) { + for (i = 0; i <= IEEE80211_AC_BK; i++) { struct ath_txq *txq = &(sc->tx.txq[i]); struct ath_atx_ac *ac; struct ath_atx_tid *tid; @@ -695,7 +695,7 @@ static ssize_t read_file_stations(struct file *file, char __user *user_buf, goto done; } - for (q = 0; q < WME_NUM_AC; q++) { + for (q = 0; q < IEEE80211_NUM_ACS; q++) { struct ath_atx_ac *ac = &(an->ac[q]); len += snprintf(buf + len, size - len, " ac: %p %s %i %p\n", @@ -1648,13 +1648,13 @@ int ath9k_init_debug(struct ath_hw *ah) debugfs_create_file("xmit", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_xmit); debugfs_create_u32("qlen_bk", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, - &sc->tx.txq_max_pending[WME_AC_BK]); + &sc->tx.txq_max_pending[IEEE80211_AC_BK]); debugfs_create_u32("qlen_be", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, - &sc->tx.txq_max_pending[WME_AC_BE]); + &sc->tx.txq_max_pending[IEEE80211_AC_BE]); debugfs_create_u32("qlen_vi", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, - &sc->tx.txq_max_pending[WME_AC_VI]); + &sc->tx.txq_max_pending[IEEE80211_AC_VI]); debugfs_create_u32("qlen_vo", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, - &sc->tx.txq_max_pending[WME_AC_VO]); + &sc->tx.txq_max_pending[IEEE80211_AC_VO]); debugfs_create_file("stations", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_stations); debugfs_create_file("misc", S_IRUSR, sc->debug.debugfs_phy, sc, diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index 7b39cc14662..4b412aaf4f3 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -474,7 +474,7 @@ int ath9k_init_btcoex(struct ath_softc *sc) r = ath_init_btcoex_timer(sc); if (r) return -1; - txq = sc->tx.txq_map[WME_AC_BE]; + txq = sc->tx.txq_map[IEEE80211_AC_BE]; ath9k_hw_init_btcoex_hw(sc->sc_ah, txq->axq_qnum); sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; if (ath9k_hw_mci_is_enabled(ah)) { diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index b30596fcf73..96bfb18078f 100644 --- a/drivers/net/wireless/ath/ath9k/htc.h +++ b/drivers/net/wireless/ath/ath9k/htc.h @@ -331,7 +331,7 @@ struct ath_tx_stats { u32 skb_success; u32 skb_failed; u32 cab_queued; - u32 queue_stats[WME_NUM_AC]; + u32 queue_stats[IEEE80211_NUM_ACS]; }; struct ath_rx_stats { @@ -493,7 +493,7 @@ struct ath9k_htc_priv { int beaconq; int cabq; - int hwq_map[WME_NUM_AC]; + int hwq_map[IEEE80211_NUM_ACS]; #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT struct ath_btcoex btcoex; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c index 1318d79f5c4..d0ce1f5bba1 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c @@ -33,7 +33,7 @@ void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv) qi.tqi_cwmin = 0; qi.tqi_cwmax = 0; } else if (priv->ah->opmode == NL80211_IFTYPE_ADHOC) { - int qnum = priv->hwq_map[WME_AC_BE]; + int qnum = priv->hwq_map[IEEE80211_AC_BE]; ath9k_hw_get_txq_props(ah, qnum, &qi_be); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c index 3035deb7a0c..87110de577e 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c @@ -218,16 +218,16 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf, len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "BE queued", - priv->debug.tx_stats.queue_stats[WME_AC_BE]); + priv->debug.tx_stats.queue_stats[IEEE80211_AC_BE]); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "BK queued", - priv->debug.tx_stats.queue_stats[WME_AC_BK]); + priv->debug.tx_stats.queue_stats[IEEE80211_AC_BK]); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "VI queued", - priv->debug.tx_stats.queue_stats[WME_AC_VI]); + priv->debug.tx_stats.queue_stats[IEEE80211_AC_VI]); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "VO queued", - priv->debug.tx_stats.queue_stats[WME_AC_VO]); + priv->debug.tx_stats.queue_stats[IEEE80211_AC_VO]); if (len > sizeof(buf)) len = sizeof(buf); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c index 0eacfc13c91..105582d6b71 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c @@ -207,7 +207,7 @@ void ath9k_htc_init_btcoex(struct ath9k_htc_priv *priv, char *product) priv->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; ath9k_hw_btcoex_init_3wire(priv->ah); ath_htc_init_btcoex_work(priv); - qnum = priv->hwq_map[WME_AC_BE]; + qnum = priv->hwq_map[IEEE80211_AC_BE]; ath9k_hw_init_btcoex_hw(priv->ah, qnum); break; default: diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index 5ecf1287ddd..05d5ba66cac 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -549,20 +549,20 @@ static int ath9k_init_queues(struct ath9k_htc_priv *priv) goto err; } - if (!ath9k_htc_txq_setup(priv, WME_AC_BE)) { + if (!ath9k_htc_txq_setup(priv, IEEE80211_AC_BE)) { ath_err(common, "Unable to setup xmit queue for BE traffic\n"); goto err; } - if (!ath9k_htc_txq_setup(priv, WME_AC_BK)) { + if (!ath9k_htc_txq_setup(priv, IEEE80211_AC_BK)) { ath_err(common, "Unable to setup xmit queue for BK traffic\n"); goto err; } - if (!ath9k_htc_txq_setup(priv, WME_AC_VI)) { + if (!ath9k_htc_txq_setup(priv, IEEE80211_AC_VI)) { ath_err(common, "Unable to setup xmit queue for VI traffic\n"); goto err; } - if (!ath9k_htc_txq_setup(priv, WME_AC_VO)) { + if (!ath9k_htc_txq_setup(priv, IEEE80211_AC_VO)) { ath_err(common, "Unable to setup xmit queue for VO traffic\n"); goto err; } diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 02cce95331d..9c07a8fa513 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1349,7 +1349,7 @@ static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, struct ath9k_tx_queue_info qi; int ret = 0, qnum; - if (queue >= WME_NUM_AC) + if (queue >= IEEE80211_NUM_ACS) return 0; mutex_lock(&priv->mutex); @@ -1376,7 +1376,7 @@ static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, } if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) && - (qnum == priv->hwq_map[WME_AC_BE])) + (qnum == priv->hwq_map[IEEE80211_AC_BE])) ath9k_htc_beaconq_config(priv); out: ath9k_htc_ps_restore(priv); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index 06cdcb772d7..3a22d17a261 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -21,10 +21,10 @@ /******/ static const int subtype_txq_to_hwq[] = { - [WME_AC_BE] = ATH_TXQ_AC_BE, - [WME_AC_BK] = ATH_TXQ_AC_BK, - [WME_AC_VI] = ATH_TXQ_AC_VI, - [WME_AC_VO] = ATH_TXQ_AC_VO, + [IEEE80211_AC_BE] = ATH_TXQ_AC_BE, + [IEEE80211_AC_BK] = ATH_TXQ_AC_BK, + [IEEE80211_AC_VI] = ATH_TXQ_AC_VI, + [IEEE80211_AC_VO] = ATH_TXQ_AC_VO, }; #define ATH9K_HTC_INIT_TXQ(subtype) do { \ @@ -41,15 +41,15 @@ int get_hw_qnum(u16 queue, int *hwq_map) { switch (queue) { case 0: - return hwq_map[WME_AC_VO]; + return hwq_map[IEEE80211_AC_VO]; case 1: - return hwq_map[WME_AC_VI]; + return hwq_map[IEEE80211_AC_VI]; case 2: - return hwq_map[WME_AC_BE]; + return hwq_map[IEEE80211_AC_BE]; case 3: - return hwq_map[WME_AC_BK]; + return hwq_map[IEEE80211_AC_BK]; default: - return hwq_map[WME_AC_BE]; + return hwq_map[IEEE80211_AC_BE]; } } @@ -106,20 +106,20 @@ static inline enum htc_endpoint_id get_htc_epid(struct ath9k_htc_priv *priv, switch (qnum) { case 0: - TX_QSTAT_INC(WME_AC_VO); + TX_QSTAT_INC(IEEE80211_AC_VO); epid = priv->data_vo_ep; break; case 1: - TX_QSTAT_INC(WME_AC_VI); + TX_QSTAT_INC(IEEE80211_AC_VI); epid = priv->data_vi_ep; break; case 2: - TX_QSTAT_INC(WME_AC_BE); + TX_QSTAT_INC(IEEE80211_AC_BE); epid = priv->data_be_ep; break; case 3: default: - TX_QSTAT_INC(WME_AC_BK); + TX_QSTAT_INC(IEEE80211_AC_BK); epid = priv->data_bk_ep; break; } diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 546bae93647..345a01af542 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -435,7 +435,7 @@ static int ath9k_init_queues(struct ath_softc *sc) sc->config.cabqReadytime = ATH_CABQ_READY_TIME; ath_cabq_update(sc); - for (i = 0; i < WME_NUM_AC; i++) { + for (i = 0; i < IEEE80211_NUM_ACS; i++) { sc->tx.txq_map[i] = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, i); sc->tx.txq_map[i]->mac80211_qnum = i; sc->tx.txq_max_pending[i] = ATH_MAX_QDEPTH; diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index 223b9693527..227fdb81481 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -211,7 +211,7 @@ static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int int time_left; memset(&txctl, 0, sizeof(txctl)); - txctl.txq = sc->tx.txq_map[WME_AC_BE]; + txctl.txq = sc->tx.txq_map[IEEE80211_AC_BE]; memset(tx_info, 0, sizeof(*tx_info)); tx_info->band = hw->conf.channel->band; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index e59d8e3fcbc..f8b3cacaeac 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1325,7 +1325,7 @@ static int ath9k_conf_tx(struct ieee80211_hw *hw, struct ath9k_tx_queue_info qi; int ret = 0; - if (queue >= WME_NUM_AC) + if (queue >= IEEE80211_NUM_ACS) return 0; txq = sc->tx.txq_map[queue]; @@ -1956,10 +1956,10 @@ static int ath9k_get_et_sset_count(struct ieee80211_hw *hw, #define PR_QNUM(_n) (sc->tx.txq_map[_n]->axq_qnum) #define AWDATA(elem) \ do { \ - data[i++] = sc->debug.stats.txstats[PR_QNUM(WME_AC_BE)].elem; \ - data[i++] = sc->debug.stats.txstats[PR_QNUM(WME_AC_BK)].elem; \ - data[i++] = sc->debug.stats.txstats[PR_QNUM(WME_AC_VI)].elem; \ - data[i++] = sc->debug.stats.txstats[PR_QNUM(WME_AC_VO)].elem; \ + data[i++] = sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BE)].elem; \ + data[i++] = sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BK)].elem; \ + data[i++] = sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VI)].elem; \ + data[i++] = sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VO)].elem; \ } while (0) #define AWDATA_RX(elem) \ @@ -1974,14 +1974,14 @@ static void ath9k_get_et_stats(struct ieee80211_hw *hw, struct ath_softc *sc = hw->priv; int i = 0; - data[i++] = (sc->debug.stats.txstats[PR_QNUM(WME_AC_BE)].tx_pkts_all + - sc->debug.stats.txstats[PR_QNUM(WME_AC_BK)].tx_pkts_all + - sc->debug.stats.txstats[PR_QNUM(WME_AC_VI)].tx_pkts_all + - sc->debug.stats.txstats[PR_QNUM(WME_AC_VO)].tx_pkts_all); - data[i++] = (sc->debug.stats.txstats[PR_QNUM(WME_AC_BE)].tx_bytes_all + - sc->debug.stats.txstats[PR_QNUM(WME_AC_BK)].tx_bytes_all + - sc->debug.stats.txstats[PR_QNUM(WME_AC_VI)].tx_bytes_all + - sc->debug.stats.txstats[PR_QNUM(WME_AC_VO)].tx_bytes_all); + data[i++] = (sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BE)].tx_pkts_all + + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BK)].tx_pkts_all + + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VI)].tx_pkts_all + + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VO)].tx_pkts_all); + data[i++] = (sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BE)].tx_bytes_all + + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BK)].tx_bytes_all + + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VI)].tx_bytes_all + + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VO)].tx_bytes_all); AWDATA_RX(rx_pkts_all); AWDATA_RX(rx_bytes_all); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 741918a2027..ae7f2897c1a 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1354,10 +1354,10 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) struct ath_hw *ah = sc->sc_ah; struct ath9k_tx_queue_info qi; static const int subtype_txq_to_hwq[] = { - [WME_AC_BE] = ATH_TXQ_AC_BE, - [WME_AC_BK] = ATH_TXQ_AC_BK, - [WME_AC_VI] = ATH_TXQ_AC_VI, - [WME_AC_VO] = ATH_TXQ_AC_VO, + [IEEE80211_AC_BE] = ATH_TXQ_AC_BE, + [IEEE80211_AC_BK] = ATH_TXQ_AC_BK, + [IEEE80211_AC_VI] = ATH_TXQ_AC_VI, + [IEEE80211_AC_VO] = ATH_TXQ_AC_VO, }; int axq_qnum, i; @@ -2464,7 +2464,7 @@ void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an) } for (acno = 0, ac = &an->ac[acno]; - acno < WME_NUM_AC; acno++, ac++) { + acno < IEEE80211_NUM_ACS; acno++, ac++) { ac->sched = false; ac->txq = sc->tx.txq_map[acno]; INIT_LIST_HEAD(&ac->tid_q); -- cgit v1.2.3-70-g09d2 From 78ef731ce549dd9baf6eba8cf52f61727613690a Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Wed, 21 Nov 2012 18:13:11 +0530 Subject: ath9k: Fix the 'xmit' debugfs file The 'xmit' debugfs file has become big and unwieldy, fix multiple issues with its usage: * Store TX counters/statistics only for the 4 Access Categories. Use IEEE80211_NUM_ACS instead of ATH9K_NUM_TX_QUEUES. * Move various utility macros to debug.h, they can be reused elsewhere. * Remove tx_complete_poll_work_seen. * Remove code that accesses various internal queue-specific variables without any locking whatsoever. HW/SW queue details will be handled in a subsequent patch. * Do not print internal values like txq_headidx and txq_headidx. They were mostly unused anyway, considering code like: PRX("txq_tailidx: ", txq_headidx); * Handle 'txprocdesc' for EDMA too. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 - drivers/net/wireless/ath/ath9k/debug.c | 104 ++------------------------------- drivers/net/wireless/ath/ath9k/debug.h | 17 +++++- drivers/net/wireless/ath/ath9k/link.c | 3 - drivers/net/wireless/ath/ath9k/main.c | 1 - drivers/net/wireless/ath/ath9k/xmit.c | 2 + 6 files changed, 23 insertions(+), 105 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 3c1cebe55f6..e739b56db53 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -725,7 +725,6 @@ struct ath_softc { struct ath9k_debug debug; spinlock_t nodes_lock; struct list_head nodes; /* basically, stations */ - unsigned int tx_complete_poll_work_seen; #endif struct ath_beacon_config cur_beacon_conf; struct delayed_work tx_complete_work; diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 957e38646fd..f9df82bdd21 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -512,62 +512,19 @@ static const struct file_operations fops_interrupt = { .llseek = default_llseek, }; -#define PR_QNUM(_n) sc->tx.txq_map[_n]->axq_qnum -#define PR(str, elem) \ - do { \ - len += snprintf(buf + len, size - len, \ - "%s%13u%11u%10u%10u\n", str, \ - sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BE)].elem, \ - sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BK)].elem, \ - sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VI)].elem, \ - sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VO)].elem); \ - if (len >= size) \ - goto done; \ -} while(0) - -#define PRX(str, elem) \ -do { \ - len += snprintf(buf + len, size - len, \ - "%s%13u%11u%10u%10u\n", str, \ - (unsigned int)(sc->tx.txq_map[IEEE80211_AC_BE]->elem), \ - (unsigned int)(sc->tx.txq_map[IEEE80211_AC_BK]->elem), \ - (unsigned int)(sc->tx.txq_map[IEEE80211_AC_VI]->elem), \ - (unsigned int)(sc->tx.txq_map[IEEE80211_AC_VO]->elem)); \ - if (len >= size) \ - goto done; \ -} while(0) - -#define PRQLE(str, elem) \ -do { \ - len += snprintf(buf + len, size - len, \ - "%s%13i%11i%10i%10i\n", str, \ - list_empty(&sc->tx.txq_map[IEEE80211_AC_BE]->elem), \ - list_empty(&sc->tx.txq_map[IEEE80211_AC_BK]->elem), \ - list_empty(&sc->tx.txq_map[IEEE80211_AC_VI]->elem), \ - list_empty(&sc->tx.txq_map[IEEE80211_AC_VO]->elem)); \ - if (len >= size) \ - goto done; \ -} while (0) - static ssize_t read_file_xmit(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; char *buf; - unsigned int len = 0, size = 8000; - int i; + unsigned int len = 0, size = 2048; ssize_t retval = 0; - char tmp[32]; buf = kzalloc(size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; - len += sprintf(buf, "Num-Tx-Queues: %i tx-queues-setup: 0x%x" - " poll-work-seen: %u\n" - "%30s %10s%10s%10s\n\n", - ATH9K_NUM_TX_QUEUES, sc->tx.txqsetup, - sc->tx_complete_poll_work_seen, + len += sprintf(buf, "%30s %10s%10s%10s\n\n", "BE", "BK", "VI", "VO"); PR("MPDUs Queued: ", queued); @@ -587,62 +544,11 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf, PR("DELIM Underrun: ", delim_underrun); PR("TX-Pkts-All: ", tx_pkts_all); PR("TX-Bytes-All: ", tx_bytes_all); - PR("hw-put-tx-buf: ", puttxbuf); - PR("hw-tx-start: ", txstart); - PR("hw-tx-proc-desc: ", txprocdesc); + PR("HW-put-tx-buf: ", puttxbuf); + PR("HW-tx-start: ", txstart); + PR("HW-tx-proc-desc: ", txprocdesc); PR("TX-Failed: ", txfailed); - len += snprintf(buf + len, size - len, - "%s%11p%11p%10p%10p\n", "txq-memory-address:", - sc->tx.txq_map[IEEE80211_AC_BE], - sc->tx.txq_map[IEEE80211_AC_BK], - sc->tx.txq_map[IEEE80211_AC_VI], - sc->tx.txq_map[IEEE80211_AC_VO]); - if (len >= size) - goto done; - - PRX("axq-qnum: ", axq_qnum); - PRX("axq-depth: ", axq_depth); - PRX("axq-ampdu_depth: ", axq_ampdu_depth); - PRX("axq-stopped ", stopped); - PRX("tx-in-progress ", axq_tx_inprogress); - PRX("pending-frames ", pending_frames); - PRX("txq_headidx: ", txq_headidx); - PRX("txq_tailidx: ", txq_headidx); - - PRQLE("axq_q empty: ", axq_q); - PRQLE("axq_acq empty: ", axq_acq); - for (i = 0; i < ATH_TXFIFO_DEPTH; i++) { - snprintf(tmp, sizeof(tmp) - 1, "txq_fifo[%i] empty: ", i); - PRQLE(tmp, txq_fifo[i]); - } - - /* Print out more detailed queue-info */ - for (i = 0; i <= IEEE80211_AC_BK; i++) { - struct ath_txq *txq = &(sc->tx.txq[i]); - struct ath_atx_ac *ac; - struct ath_atx_tid *tid; - if (len >= size) - goto done; - spin_lock_bh(&txq->axq_lock); - if (!list_empty(&txq->axq_acq)) { - ac = list_first_entry(&txq->axq_acq, struct ath_atx_ac, - list); - len += snprintf(buf + len, size - len, - "txq[%i] first-ac: %p sched: %i\n", - i, ac, ac->sched); - if (list_empty(&ac->tid_q) || (len >= size)) - goto done_for; - tid = list_first_entry(&ac->tid_q, struct ath_atx_tid, - list); - len += snprintf(buf + len, size - len, - " first-tid: %p sched: %i paused: %i\n", - tid, tid->sched, tid->paused); - } - done_for: - spin_unlock_bh(&txq->axq_lock); - } -done: if (len > size) len = size; diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 61341cda131..f9bee18de5a 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -179,6 +179,21 @@ struct ath_tx_stats { u32 txfailed; }; +/* + * Various utility macros to print TX/Queue counters. + */ +#define PR_QNUM(_n) sc->tx.txq_map[_n]->axq_qnum +#define TXSTATS sc->debug.stats.txstats +#define PR(str, elem) \ + do { \ + len += snprintf(buf + len, size - len, \ + "%s%13u%11u%10u%10u\n", str, \ + TXSTATS[PR_QNUM(IEEE80211_AC_BE)].elem, \ + TXSTATS[PR_QNUM(IEEE80211_AC_BK)].elem, \ + TXSTATS[PR_QNUM(IEEE80211_AC_VI)].elem, \ + TXSTATS[PR_QNUM(IEEE80211_AC_VO)].elem); \ + } while(0) + #define RX_STAT_INC(c) (sc->debug.stats.rxstats.c++) /** @@ -227,7 +242,7 @@ struct ath_rx_stats { struct ath_stats { struct ath_interrupt_stats istats; - struct ath_tx_stats txstats[ATH9K_NUM_TX_QUEUES]; + struct ath_tx_stats txstats[IEEE80211_NUM_ACS]; struct ath_rx_stats rxstats; struct ath_dfs_stats dfs_stats; u32 reset[__RESET_TYPE_MAX]; diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index 227fdb81481..fc6b075ad63 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -27,9 +27,6 @@ void ath_tx_complete_poll_work(struct work_struct *work) struct ath_txq *txq; int i; bool needreset = false; -#ifdef CONFIG_ATH9K_DEBUGFS - sc->tx_complete_poll_work_seen++; -#endif for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) if (ATH_TXQ_SETUP(sc, i)) { diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index f8b3cacaeac..6a809bfd5aa 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1953,7 +1953,6 @@ static int ath9k_get_et_sset_count(struct ieee80211_hw *hw, return 0; } -#define PR_QNUM(_n) (sc->tx.txq_map[_n]->axq_qnum) #define AWDATA(elem) \ do { \ data[i++] = sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BE)].elem; \ diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index ae7f2897c1a..34130943f9d 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2319,6 +2319,8 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) ath_txq_lock(sc, txq); + TX_STAT_INC(txq->axq_qnum, txprocdesc); + if (list_empty(&txq->txq_fifo[txq->txq_tailidx])) { ath_txq_unlock(sc, txq); return; -- cgit v1.2.3-70-g09d2 From c0b74876ee4873dc89d80b59f5e890a1d0a87be1 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Wed, 21 Nov 2012 18:13:12 +0530 Subject: ath9k: Add a debugfs file to dump queue statistics Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/debug.c | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index f9df82bdd21..086dee84162 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -558,6 +558,50 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf, return retval; } +static ssize_t read_file_queues(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ath_txq *txq; + char *buf; + unsigned int len = 0, size = 1024; + ssize_t retval = 0; + int i; + char *qname[4] = {"VO", "VI", "BE", "BK"}; + + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + txq = sc->tx.txq_map[i]; + len += snprintf(buf + len, size - len, "(%s): ", qname[i]); + + ath_txq_lock(sc, txq); + + len += snprintf(buf + len, size - len, "%s: %d ", + "qnum", txq->axq_qnum); + len += snprintf(buf + len, size - len, "%s: %2d ", + "qdepth", txq->axq_depth); + len += snprintf(buf + len, size - len, "%s: %2d ", + "ampdu-depth", txq->axq_ampdu_depth); + len += snprintf(buf + len, size - len, "%s: %3d ", + "pending", txq->pending_frames); + len += snprintf(buf + len, size - len, "%s: %d\n", + "stopped", txq->stopped); + + ath_txq_unlock(sc, txq); + } + + if (len > size) + len = size; + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; +} + static ssize_t read_file_stations(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -828,6 +872,13 @@ static const struct file_operations fops_xmit = { .llseek = default_llseek, }; +static const struct file_operations fops_queues = { + .read = read_file_queues, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + static const struct file_operations fops_stations = { .read = read_file_stations, .open = simple_open, @@ -1553,6 +1604,8 @@ int ath9k_init_debug(struct ath_hw *ah) &fops_interrupt); debugfs_create_file("xmit", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_xmit); + debugfs_create_file("queues", S_IRUSR, sc->debug.debugfs_phy, sc, + &fops_queues); debugfs_create_u32("qlen_bk", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &sc->tx.txq_max_pending[IEEE80211_AC_BK]); debugfs_create_u32("qlen_be", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, -- cgit v1.2.3-70-g09d2 From 1c11e10b6a64f14427be518ccc2a242b73fc3d9f Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Wed, 21 Nov 2012 18:13:13 +0530 Subject: ath9k: Fill remove_sta_debugfs() callback Remove the rate control statistics debugfs file properly via remove_sta_debugfs(). Also, check for both MAC80211_DEBUGFS and ATH9K_DEBUGFS config options. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/rc.c | 17 +++++++++++++---- drivers/net/wireless/ath/ath9k/rc.h | 2 ++ 2 files changed, 15 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index 27ed80b5488..004f016290d 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -1350,7 +1350,7 @@ static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband, } } -#ifdef CONFIG_ATH9K_DEBUGFS +#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS) static ssize_t read_file_rcstat(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -1428,10 +1428,17 @@ static void ath_rate_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir) { struct ath_rate_priv *rc = priv_sta; - debugfs_create_file("rc_stats", S_IRUGO, dir, rc, &fops_rcstat); + rc->debugfs_rcstats = debugfs_create_file("rc_stats", S_IRUGO, + dir, rc, &fops_rcstat); } -#endif /* CONFIG_ATH9K_DEBUGFS */ +static void ath_rate_remove_sta_debugfs(void *priv, void *priv_sta) +{ + struct ath_rate_priv *rc = priv_sta; + debugfs_remove(rc->debugfs_rcstats); +} + +#endif /* CONFIG_MAC80211_DEBUGFS && CONFIG_ATH9K_DEBUGFS */ static void *ath_rate_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) { @@ -1476,8 +1483,10 @@ static struct rate_control_ops ath_rate_ops = { .free = ath_rate_free, .alloc_sta = ath_rate_alloc_sta, .free_sta = ath_rate_free_sta, -#ifdef CONFIG_ATH9K_DEBUGFS + +#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS) .add_sta_debugfs = ath_rate_add_sta_debugfs, + .remove_sta_debugfs = ath_rate_remove_sta_debugfs, #endif }; diff --git a/drivers/net/wireless/ath/ath9k/rc.h b/drivers/net/wireless/ath/ath9k/rc.h index 268e67dc5fb..528c83cf701 100644 --- a/drivers/net/wireless/ath/ath9k/rc.h +++ b/drivers/net/wireless/ath/ath9k/rc.h @@ -211,7 +211,9 @@ struct ath_rate_priv { struct ath_rateset neg_ht_rates; const struct ath_rate_table *rate_table; +#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS) struct dentry *debugfs_rcstats; +#endif struct ath_rc_stats rcstats[RATE_TABLE_SIZE]; }; -- cgit v1.2.3-70-g09d2 From 4d28f771ff2ca0701999b037df38ad06e949edf6 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Wed, 21 Nov 2012 18:13:14 +0530 Subject: ath9k: Fix rate control debugging Update the rate statistics only when debugfs has been enabled in ath9k and mac80211 and move the stat() functions under proper conditionals. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/rc.c | 36 ++++++++++++++++++------------------ drivers/net/wireless/ath/ath9k/rc.h | 16 +++++++++++++++- 2 files changed, 33 insertions(+), 19 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index 004f016290d..714558d1ba7 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -982,16 +982,6 @@ static void ath_rc_update_per(struct ath_softc *sc, } } -static void ath_debug_stat_retries(struct ath_rate_priv *rc, int rix, - int xretries, int retries, u8 per) -{ - struct ath_rc_stats *stats = &rc->rcstats[rix]; - - stats->xretries += xretries; - stats->retries += retries; - stats->per = per; -} - static void ath_rc_update_ht(struct ath_softc *sc, struct ath_rate_priv *ath_rc_priv, struct ieee80211_tx_info *tx_info, @@ -1065,14 +1055,6 @@ static void ath_rc_update_ht(struct ath_softc *sc, } -static void ath_debug_stat_rc(struct ath_rate_priv *rc, int final_rate) -{ - struct ath_rc_stats *stats; - - stats = &rc->rcstats[final_rate]; - stats->success++; -} - static void ath_rc_tx_status(struct ath_softc *sc, struct ath_rate_priv *ath_rc_priv, struct sk_buff *skb) @@ -1352,6 +1334,24 @@ static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband, #if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS) +void ath_debug_stat_rc(struct ath_rate_priv *rc, int final_rate) +{ + struct ath_rc_stats *stats; + + stats = &rc->rcstats[final_rate]; + stats->success++; +} + +void ath_debug_stat_retries(struct ath_rate_priv *rc, int rix, + int xretries, int retries, u8 per) +{ + struct ath_rc_stats *stats = &rc->rcstats[rix]; + + stats->xretries += xretries; + stats->retries += retries; + stats->per = per; +} + static ssize_t read_file_rcstat(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { diff --git a/drivers/net/wireless/ath/ath9k/rc.h b/drivers/net/wireless/ath/ath9k/rc.h index 528c83cf701..267dbfcfaa9 100644 --- a/drivers/net/wireless/ath/ath9k/rc.h +++ b/drivers/net/wireless/ath/ath9k/rc.h @@ -213,10 +213,24 @@ struct ath_rate_priv { #if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS) struct dentry *debugfs_rcstats; -#endif struct ath_rc_stats rcstats[RATE_TABLE_SIZE]; +#endif }; +#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS) +void ath_debug_stat_rc(struct ath_rate_priv *rc, int final_rate); +void ath_debug_stat_retries(struct ath_rate_priv *rc, int rix, + int xretries, int retries, u8 per); +#else +static inline void ath_debug_stat_rc(struct ath_rate_priv *rc, int final_rate) +{ +} +static inline void ath_debug_stat_retries(struct ath_rate_priv *rc, int rix, + int xretries, int retries, u8 per) +{ +} +#endif + #ifdef CONFIG_ATH9K_RATE_CONTROL int ath_rate_control_register(void); void ath_rate_control_unregister(void); -- cgit v1.2.3-70-g09d2 From 580bdac386daa25e0432cbb4078d1cc3919127e3 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Wed, 21 Nov 2012 18:13:15 +0530 Subject: ath9k: Remove 'stations' debugfs file The 'stations' debugfs file has multiple issues. It doesn't scale to an arbitrary number of associated stations and allocating 64K is not elegant either. Now that changes have been made in mac80211 to support dynamic creation/deletion of driver-specific debugfs files on station addition/removal, remove this file and make use of the mac80211 hooks (which will be done in a sebsequent patch). Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 5 --- drivers/net/wireless/ath/ath9k/debug.c | 74 ---------------------------------- drivers/net/wireless/ath/ath9k/init.c | 4 -- drivers/net/wireless/ath/ath9k/main.c | 12 ------ 4 files changed, 95 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index e739b56db53..80bab1b8447 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -259,9 +259,6 @@ struct ath_atx_tid { }; struct ath_node { -#ifdef CONFIG_ATH9K_DEBUGFS - struct list_head list; /* for sc->nodes */ -#endif struct ieee80211_sta *sta; /* station struct we're part of */ struct ieee80211_vif *vif; /* interface with which we're associated */ struct ath_atx_tid tid[WME_NUM_TID]; @@ -723,8 +720,6 @@ struct ath_softc { #ifdef CONFIG_ATH9K_DEBUGFS struct ath9k_debug debug; - spinlock_t nodes_lock; - struct list_head nodes; /* basically, stations */ #endif struct ath_beacon_config cur_beacon_conf; struct delayed_work tx_complete_work; diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 086dee84162..939308c2571 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -602,71 +602,6 @@ static ssize_t read_file_queues(struct file *file, char __user *user_buf, return retval; } -static ssize_t read_file_stations(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - char *buf; - unsigned int len = 0, size = 64000; - struct ath_node *an = NULL; - ssize_t retval = 0; - int q; - - buf = kzalloc(size, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - len += snprintf(buf + len, size - len, - "Stations:\n" - " tid: addr sched paused buf_q-empty an ac baw\n" - " ac: addr sched tid_q-empty txq\n"); - - spin_lock(&sc->nodes_lock); - list_for_each_entry(an, &sc->nodes, list) { - unsigned short ma = an->maxampdu; - if (ma == 0) - ma = 65535; /* see ath_lookup_rate */ - len += snprintf(buf + len, size - len, - "iface: %pM sta: %pM max-ampdu: %hu mpdu-density: %uus\n", - an->vif->addr, an->sta->addr, ma, - (unsigned int)(an->mpdudensity)); - if (len >= size) - goto done; - - for (q = 0; q < WME_NUM_TID; q++) { - struct ath_atx_tid *tid = &(an->tid[q]); - len += snprintf(buf + len, size - len, - " tid: %p %s %s %i %p %p %hu\n", - tid, tid->sched ? "sched" : "idle", - tid->paused ? "paused" : "running", - skb_queue_empty(&tid->buf_q), - tid->an, tid->ac, tid->baw_size); - if (len >= size) - goto done; - } - - for (q = 0; q < IEEE80211_NUM_ACS; q++) { - struct ath_atx_ac *ac = &(an->ac[q]); - len += snprintf(buf + len, size - len, - " ac: %p %s %i %p\n", - ac, ac->sched ? "sched" : "idle", - list_empty(&ac->tid_q), ac->txq); - if (len >= size) - goto done; - } - } - -done: - spin_unlock(&sc->nodes_lock); - if (len > size) - len = size; - - retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - - return retval; -} - static ssize_t read_file_misc(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -879,13 +814,6 @@ static const struct file_operations fops_queues = { .llseek = default_llseek, }; -static const struct file_operations fops_stations = { - .read = read_file_stations, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - static const struct file_operations fops_misc = { .read = read_file_misc, .open = simple_open, @@ -1614,8 +1542,6 @@ int ath9k_init_debug(struct ath_hw *ah) &sc->tx.txq_max_pending[IEEE80211_AC_VI]); debugfs_create_u32("qlen_vo", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &sc->tx.txq_max_pending[IEEE80211_AC_VO]); - debugfs_create_file("stations", S_IRUSR, sc->debug.debugfs_phy, sc, - &fops_stations); debugfs_create_file("misc", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_misc); debugfs_create_file("reset", S_IRUSR, sc->debug.debugfs_phy, sc, diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 345a01af542..80cae53a33e 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -563,10 +563,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, spin_lock_init(&sc->sc_serial_rw); spin_lock_init(&sc->sc_pm_lock); mutex_init(&sc->mutex); -#ifdef CONFIG_ATH9K_DEBUGFS - spin_lock_init(&sc->nodes_lock); - INIT_LIST_HEAD(&sc->nodes); -#endif #ifdef CONFIG_ATH9K_MAC_DEBUG spin_lock_init(&sc->debug.samp_lock); #endif diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 6a809bfd5aa..0653dbc99e3 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -331,11 +331,6 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta, u8 density; an = (struct ath_node *)sta->drv_priv; -#ifdef CONFIG_ATH9K_DEBUGFS - spin_lock(&sc->nodes_lock); - list_add(&an->list, &sc->nodes); - spin_unlock(&sc->nodes_lock); -#endif an->sta = sta; an->vif = vif; @@ -352,13 +347,6 @@ static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta) { struct ath_node *an = (struct ath_node *)sta->drv_priv; -#ifdef CONFIG_ATH9K_DEBUGFS - spin_lock(&sc->nodes_lock); - list_del(&an->list); - spin_unlock(&sc->nodes_lock); - an->sta = NULL; -#endif - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) ath_tx_node_cleanup(sc, an); } -- cgit v1.2.3-70-g09d2 From 42d97a599eb6b2aab3a401b3e5799a399d6c7652 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 8 Nov 2012 18:31:02 +0100 Subject: cfg80211: remove remain-on-channel channel type As mwifiex (and mac80211 in the software case) are the only drivers actually implementing remain-on-channel with channel type, userspace can't be relying on it. This is the case, as it's used only for P2P operations right now. Rather than adding a flag to tell userspace whether or not it can actually rely on it, simplify all the code by removing the ability to use different channel types. Leave only the validation of the attribute, so that if we extend it again later (with the needed capability flag), it can't break userspace sending invalid data. Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 7 ++-- drivers/net/wireless/ath/ath6kl/wmi.c | 5 ++- drivers/net/wireless/iwlwifi/dvm/dev.h | 1 - drivers/net/wireless/iwlwifi/dvm/mac80211.c | 2 -- drivers/net/wireless/mac80211_hwsim.c | 1 - drivers/net/wireless/mwifiex/cfg80211.c | 16 +++------ drivers/net/wireless/mwifiex/main.h | 2 -- drivers/net/wireless/mwifiex/sta_event.c | 1 - drivers/net/wireless/mwifiex/sta_ioctl.c | 3 +- include/net/cfg80211.h | 11 ++---- include/net/mac80211.h | 1 - include/uapi/linux/nl80211.h | 14 ++++---- net/mac80211/cfg.c | 27 +++++---------- net/mac80211/driver-ops.h | 5 ++- net/mac80211/ieee80211_i.h | 2 -- net/mac80211/main.c | 2 +- net/mac80211/offchannel.c | 8 ++--- net/mac80211/trace.h | 6 ++-- net/wireless/core.h | 6 ++-- net/wireless/mlme.c | 21 ++++-------- net/wireless/nl80211.c | 36 ++++++++------------ net/wireless/nl80211.h | 4 +-- net/wireless/rdev-ops.h | 20 +++++------ net/wireless/trace.h | 52 ++++++++++------------------- 24 files changed, 81 insertions(+), 172 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index d615f9f7506..74091d33ed6 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -2976,7 +2976,6 @@ static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev, static int ath6kl_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration, u64 *cookie) { @@ -3135,10 +3134,8 @@ static bool ath6kl_is_p2p_go_ssid(const u8 *buf, size_t len) static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, - enum nl80211_channel_type channel_type, - bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, bool no_cck, - bool dont_wait_for_ack, u64 *cookie) + unsigned int wait, const u8 *buf, size_t len, + bool no_cck, bool dont_wait_for_ack, u64 *cookie) { struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev); struct ath6kl *ar = ath6kl_priv(vif->ndev); diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index c30ab4b11d6..0e05c41cdcf 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -474,7 +474,7 @@ static int ath6kl_wmi_remain_on_chnl_event_rx(struct wmi *wmi, u8 *datap, return -EINVAL; } id = vif->last_roc_id; - cfg80211_ready_on_channel(&vif->wdev, id, chan, NL80211_CHAN_NO_HT, + cfg80211_ready_on_channel(&vif->wdev, id, chan, dur, GFP_ATOMIC); return 0; @@ -513,8 +513,7 @@ static int ath6kl_wmi_cancel_remain_on_chnl_event_rx(struct wmi *wmi, else id = vif->last_roc_id; /* timeout on uncanceled r-o-c */ vif->last_cancel_roc_id = 0; - cfg80211_remain_on_channel_expired(&vif->wdev, id, chan, - NL80211_CHAN_NO_HT, GFP_ATOMIC); + cfg80211_remain_on_channel_expired(&vif->wdev, id, chan, GFP_ATOMIC); return 0; } diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h index 8141f91c372..29c571a5625 100644 --- a/drivers/net/wireless/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/iwlwifi/dvm/dev.h @@ -789,7 +789,6 @@ struct iwl_priv { /* remain-on-channel offload support */ struct ieee80211_channel *hw_roc_channel; struct delayed_work hw_roc_disable_work; - enum nl80211_channel_type hw_roc_chantype; int hw_roc_duration; bool hw_roc_setup, hw_roc_start_notified; diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index e75d80341f2..852edb02e5f 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -1034,7 +1034,6 @@ done: static int iwlagn_mac_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type, int duration) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); @@ -1066,7 +1065,6 @@ static int iwlagn_mac_remain_on_channel(struct ieee80211_hw *hw, } priv->hw_roc_channel = channel; - priv->hw_roc_chantype = channel_type; /* convert from ms to TU */ priv->hw_roc_duration = DIV_ROUND_UP(1000 * duration, 1024); priv->hw_roc_start_notified = false; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 3baa51f1bb8..b0338543547 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1455,7 +1455,6 @@ static void hw_roc_done(struct work_struct *work) static int mac80211_hwsim_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, int duration) { struct mac80211_hwsim_data *hwsim = hw->priv; diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 8e829b251d8..f69190b492a 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -180,10 +180,8 @@ mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len) static int mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, - enum nl80211_channel_type channel_type, - bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, bool no_cck, - bool dont_wait_for_ack, u64 *cookie) + unsigned int wait, const u8 *buf, size_t len, + bool no_cck, bool dont_wait_for_ack, u64 *cookie) { struct sk_buff *skb; u16 pkt_len; @@ -253,7 +251,6 @@ static int mwifiex_cfg80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration, u64 *cookie) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); @@ -271,15 +268,14 @@ mwifiex_cfg80211_remain_on_channel(struct wiphy *wiphy, } ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_SET, chan, - &channel_type, duration); + duration); if (!ret) { *cookie = random32() | 1; priv->roc_cfg.cookie = *cookie; priv->roc_cfg.chan = *chan; - priv->roc_cfg.chan_type = channel_type; - cfg80211_ready_on_channel(wdev, *cookie, chan, channel_type, + cfg80211_ready_on_channel(wdev, *cookie, chan, duration, GFP_ATOMIC); wiphy_dbg(wiphy, "info: ROC, cookie = 0x%llx\n", *cookie); @@ -302,13 +298,11 @@ mwifiex_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, return -ENOENT; ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_REMOVE, - &priv->roc_cfg.chan, - &priv->roc_cfg.chan_type, 0); + &priv->roc_cfg.chan, 0); if (!ret) { cfg80211_remain_on_channel_expired(wdev, cookie, &priv->roc_cfg.chan, - priv->roc_cfg.chan_type, GFP_ATOMIC); memset(&priv->roc_cfg, 0, sizeof(struct mwifiex_roc_cfg)); diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 81f8772dcb0..771717df1c5 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -371,7 +371,6 @@ struct wps { struct mwifiex_roc_cfg { u64 cookie; struct ieee80211_channel chan; - enum nl80211_channel_type chan_type; }; struct mwifiex_adapter; @@ -1016,7 +1015,6 @@ int mwifiex_get_ver_ext(struct mwifiex_private *priv); int mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action, struct ieee80211_channel *chan, - enum nl80211_channel_type *channel_type, unsigned int duration); int mwifiex_set_bss_role(struct mwifiex_private *priv, u8 bss_role); diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c index 8132119e1a2..78dfa31c908 100644 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ b/drivers/net/wireless/mwifiex/sta_event.c @@ -424,7 +424,6 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) cfg80211_remain_on_channel_expired(priv->wdev, priv->roc_cfg.cookie, &priv->roc_cfg.chan, - priv->roc_cfg.chan_type, GFP_ATOMIC); memset(&priv->roc_cfg, 0x00, sizeof(struct mwifiex_roc_cfg)); diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 552d72ed055..24af6ba7d8a 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -1046,7 +1046,6 @@ mwifiex_get_ver_ext(struct mwifiex_private *priv) int mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action, struct ieee80211_channel *chan, - enum nl80211_channel_type *ct, unsigned int duration) { struct host_cmd_ds_remain_on_chan roc_cfg; @@ -1056,7 +1055,7 @@ mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action, roc_cfg.action = cpu_to_le16(action); if (action == HostCmd_ACT_GEN_SET) { roc_cfg.band_cfg = chan->band; - sc = mwifiex_chan_type_to_sec_chan_offset(*ct); + sc = mwifiex_chan_type_to_sec_chan_offset(NL80211_CHAN_NO_HT); roc_cfg.band_cfg |= (sc << 2); roc_cfg.channel = diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c2c185febb8..1effe0682d2 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1791,7 +1791,6 @@ struct cfg80211_ops { int (*remain_on_channel)(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration, u64 *cookie); int (*cancel_remain_on_channel)(struct wiphy *wiphy, @@ -1800,10 +1799,8 @@ struct cfg80211_ops { int (*mgmt_tx)(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, - enum nl80211_channel_type channel_type, - bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, bool no_cck, - bool dont_wait_for_ack, u64 *cookie); + unsigned int wait, const u8 *buf, size_t len, + bool no_cck, bool dont_wait_for_ack, u64 *cookie); int (*mgmt_tx_cancel_wait)(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie); @@ -3350,14 +3347,12 @@ void cfg80211_disconnected(struct net_device *dev, u16 reason, * @wdev: wireless device * @cookie: the request cookie * @chan: The current channel (from remain_on_channel request) - * @channel_type: Channel type * @duration: Duration in milliseconds that the driver intents to remain on the * channel * @gfp: allocation flags */ void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration, gfp_t gfp); /** @@ -3365,12 +3360,10 @@ void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie, * @wdev: wireless device * @cookie: the request cookie * @chan: The current channel (from remain_on_channel request) - * @channel_type: Channel type * @gfp: allocation flags */ void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, gfp_t gfp); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index e1293c7e4d2..12093778b05 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2550,7 +2550,6 @@ struct ieee80211_ops { int (*remain_on_channel)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, int duration); int (*cancel_remain_on_channel)(struct ieee80211_hw *hw); int (*set_ringparam)(struct ieee80211_hw *hw, u32 tx, u32 rx); diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 1a9a819cfab..43cd6fa084c 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -401,8 +401,7 @@ * a response while being associated to an AP on another channel. * %NL80211_ATTR_IFINDEX is used to specify which interface (and thus * radio) is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the - * frequency for the operation and %NL80211_ATTR_WIPHY_CHANNEL_TYPE may be - * optionally used to specify additional channel parameters. + * frequency for the operation. * %NL80211_ATTR_DURATION is used to specify the duration in milliseconds * to remain on the channel. This command is also used as an event to * notify when the requested duration starts (it may take a while for the @@ -440,12 +439,11 @@ * as an event indicating reception of a frame that was not processed in * kernel code, but is for us (i.e., which may need to be processed in a * user space application). %NL80211_ATTR_FRAME is used to specify the - * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ (and - * optionally %NL80211_ATTR_WIPHY_CHANNEL_TYPE) is used to indicate on - * which channel the frame is to be transmitted or was received. If this - * channel is not the current channel (remain-on-channel or the - * operational channel) the device will switch to the given channel and - * transmit the frame, optionally waiting for a response for the time + * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ is used + * to indicate on which channel the frame is to be transmitted or was + * received. If this channel is not the current channel (remain-on-channel + * or the operational channel) the device will switch to the given channel + * and transmit the frame, optionally waiting for a response for the time * specified using %NL80211_ATTR_DURATION. When called, this operation * returns a cookie (%NL80211_ATTR_COOKIE) that will be included with the * TX status event pertaining to the TX request. diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 18926aea480..ac0241e3539 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2236,7 +2236,6 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, static int ieee80211_start_roc_work(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type, unsigned int duration, u64 *cookie, struct sk_buff *txskb) { @@ -2254,7 +2253,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, return -ENOMEM; roc->chan = channel; - roc->chan_type = channel_type; roc->duration = duration; roc->req_duration = duration; roc->frame = txskb; @@ -2287,8 +2285,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, if (!duration) duration = 10; - ret = drv_remain_on_channel(local, sdata, channel, channel_type, - duration); + ret = drv_remain_on_channel(local, sdata, channel, duration); if (ret) { kfree(roc); return ret; @@ -2299,8 +2296,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, out_check_combine: list_for_each_entry(tmp, &local->roc_list, list) { - if (tmp->chan != channel || tmp->chan_type != channel_type || - tmp->sdata != sdata) + if (tmp->chan != channel || tmp->sdata != sdata) continue; /* @@ -2417,7 +2413,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, static int ieee80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration, u64 *cookie) { @@ -2426,7 +2421,7 @@ static int ieee80211_remain_on_channel(struct wiphy *wiphy, int ret; mutex_lock(&local->mtx); - ret = ieee80211_start_roc_work(local, sdata, chan, channel_type, + ret = ieee80211_start_roc_work(local, sdata, chan, duration, cookie, NULL); mutex_unlock(&local->mtx); @@ -2519,10 +2514,8 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, - enum nl80211_channel_type channel_type, - bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, bool no_cck, - bool dont_wait_for_ack, u64 *cookie) + unsigned int wait, const u8 *buf, size_t len, + bool no_cck, bool dont_wait_for_ack, u64 *cookie) { struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); struct ieee80211_local *local = sdata->local; @@ -2591,14 +2584,10 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, rcu_read_lock(); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); - if (chanctx_conf) { + if (chanctx_conf) need_offchan = chan != chanctx_conf->channel; - if (channel_type_valid && - channel_type != chanctx_conf->channel_type) - need_offchan = true; - } else { + else need_offchan = true; - } rcu_read_unlock(); } @@ -2633,7 +2622,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, local->hw.offchannel_tx_hw_queue; /* This will handle all kinds of coalescing and immediate TX */ - ret = ieee80211_start_roc_work(local, sdata, chan, channel_type, + ret = ieee80211_start_roc_work(local, sdata, chan, wait, cookie, skb); if (ret) kfree_skb(skb); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 68c27aaf5c9..c6560cc7a9d 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -738,16 +738,15 @@ static inline int drv_get_antenna(struct ieee80211_local *local, static inline int drv_remain_on_channel(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_channel *chan, - enum nl80211_channel_type chantype, unsigned int duration) { int ret; might_sleep(); - trace_drv_remain_on_channel(local, sdata, chan, chantype, duration); + trace_drv_remain_on_channel(local, sdata, chan, duration); ret = local->ops->remain_on_channel(&local->hw, &sdata->vif, - chan, chantype, duration); + chan, duration); trace_drv_return_int(local, ret); return ret; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index d5da0fe1431..fba4b1f425c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -348,7 +348,6 @@ struct ieee80211_roc_work { struct ieee80211_sub_if_data *sdata; struct ieee80211_channel *chan; - enum nl80211_channel_type chan_type; bool started, abort, hw_begun, notified; @@ -1048,7 +1047,6 @@ struct ieee80211_local { /* Temporary remain-on-channel for off-channel operations */ struct ieee80211_channel *tmp_channel; - enum nl80211_channel_type tmp_channel_type; /* channel contexts */ struct list_head chanctx_list; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 70e87600cac..b229cded456 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -115,7 +115,7 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local) channel_type = NL80211_CHAN_NO_HT; } else if (local->tmp_channel) { chan = local->tmp_channel; - channel_type = local->tmp_channel_type; + channel_type = NL80211_CHAN_NO_HT; } else { chan = local->_oper_channel; channel_type = local->_oper_channel_type; diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index 7f8a3651081..5abddfe3e10 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -205,8 +205,8 @@ void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc) } } else { cfg80211_ready_on_channel(&roc->sdata->wdev, roc->cookie, - roc->chan, roc->chan_type, - roc->req_duration, GFP_KERNEL); + roc->chan, roc->req_duration, + GFP_KERNEL); } roc->notified = true; @@ -284,7 +284,6 @@ void ieee80211_start_next_roc(struct ieee80211_local *local) duration = 10; ret = drv_remain_on_channel(local, roc->sdata, roc->chan, - roc->chan_type, duration); roc->started = true; @@ -321,7 +320,7 @@ void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc) if (!roc->mgmt_tx_cookie) cfg80211_remain_on_channel_expired(&roc->sdata->wdev, roc->cookie, roc->chan, - roc->chan_type, GFP_KERNEL); + GFP_KERNEL); list_for_each_entry_safe(dep, tmp, &roc->dependents, list) ieee80211_roc_notify_destroy(dep); @@ -359,7 +358,6 @@ void ieee80211_sw_roc_work(struct work_struct *work) ieee80211_recalc_idle(local); local->tmp_channel = roc->chan; - local->tmp_channel_type = roc->chan_type; ieee80211_hw_config(local, 0); /* tell userspace or send frame */ diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index e9579b7a2cd..bc28346ba20 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -1022,15 +1022,14 @@ TRACE_EVENT(drv_remain_on_channel, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_channel *chan, - enum nl80211_channel_type chantype, unsigned int duration), + unsigned int duration), - TP_ARGS(local, sdata, chan, chantype, duration), + TP_ARGS(local, sdata, chan, duration), TP_STRUCT__entry( LOCAL_ENTRY VIF_ENTRY __field(int, center_freq) - __field(int, channel_type) __field(unsigned int, duration) ), @@ -1038,7 +1037,6 @@ TRACE_EVENT(drv_remain_on_channel, LOCAL_ASSIGN; VIF_ASSIGN; __entry->center_freq = chan->center_freq; - __entry->channel_type = chantype; __entry->duration = duration; ), diff --git a/net/wireless/core.h b/net/wireless/core.h index e53831c876b..b0a09cf56e0 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -378,10 +378,8 @@ void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev); int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, - enum nl80211_channel_type channel_type, - bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, bool no_cck, - bool dont_wait_for_ack, u64 *cookie); + unsigned int wait, const u8 *buf, size_t len, + bool no_cck, bool dont_wait_for_ack, u64 *cookie); void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa, const struct ieee80211_ht_cap *ht_capa_mask); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 4bfd14f7c59..a9646b53a09 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -579,31 +579,25 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration, gfp_t gfp) { struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - trace_cfg80211_ready_on_channel(wdev, cookie, chan, channel_type, - duration); - nl80211_send_remain_on_channel(rdev, wdev, cookie, chan, channel_type, - duration, gfp); + trace_cfg80211_ready_on_channel(wdev, cookie, chan, duration); + nl80211_send_remain_on_channel(rdev, wdev, cookie, chan, duration, gfp); } EXPORT_SYMBOL(cfg80211_ready_on_channel); void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, gfp_t gfp) { struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan, - channel_type); - nl80211_send_remain_on_channel_cancel(rdev, wdev, cookie, chan, - channel_type, gfp); + trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan); + nl80211_send_remain_on_channel_cancel(rdev, wdev, cookie, chan, gfp); } EXPORT_SYMBOL(cfg80211_remain_on_channel_expired); @@ -758,10 +752,8 @@ void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, - enum nl80211_channel_type channel_type, - bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, bool no_cck, - bool dont_wait_for_ack, u64 *cookie) + unsigned int wait, const u8 *buf, size_t len, + bool no_cck, bool dont_wait_for_ack, u64 *cookie) { const struct ieee80211_mgmt *mgmt; u16 stype; @@ -855,7 +847,6 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, /* Transmit the Action frame as requested by user space */ return rdev_mgmt_tx(rdev, wdev, chan, offchan, - channel_type, channel_type_valid, wait, buf, len, no_cck, dont_wait_for_ack, cookie); } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 4c427fa5c45..e880f449495 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5952,7 +5952,6 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, struct sk_buff *msg; void *hdr; u64 cookie; - enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; u32 freq, duration; int err; @@ -5975,11 +5974,11 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, return -EINVAL; if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && - !nl80211_valid_channel_type(info, &channel_type)) + !nl80211_valid_channel_type(info, NULL)) return -EINVAL; freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); - chan = rdev_freq_to_chan(rdev, freq, channel_type); + chan = rdev_freq_to_chan(rdev, freq, NL80211_CHAN_NO_HT); if (chan == NULL) return -EINVAL; @@ -5995,8 +5994,7 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, goto free_msg; } - err = rdev_remain_on_channel(rdev, wdev, chan, channel_type, duration, - &cookie); + err = rdev_remain_on_channel(rdev, wdev, chan, duration, &cookie); if (err) goto free_msg; @@ -6216,8 +6214,6 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct wireless_dev *wdev = info->user_ptr[1]; struct ieee80211_channel *chan; - enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; - bool channel_type_valid = false; u32 freq; int err; void *hdr = NULL; @@ -6264,11 +6260,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) } - if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { - if (!nl80211_valid_channel_type(info, &channel_type)) - return -EINVAL; - channel_type_valid = true; - } + if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && + !nl80211_valid_channel_type(info, NULL)) + return -EINVAL; offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK]; @@ -6278,7 +6272,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); - chan = rdev_freq_to_chan(rdev, freq, channel_type); + chan = rdev_freq_to_chan(rdev, freq, NL80211_CHAN_NO_HT); if (chan == NULL) return -EINVAL; @@ -6296,8 +6290,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) } } - err = cfg80211_mlme_mgmt_tx(rdev, wdev, chan, offchan, channel_type, - channel_type_valid, wait, + err = cfg80211_mlme_mgmt_tx(rdev, wdev, chan, offchan, wait, nla_data(info->attrs[NL80211_ATTR_FRAME]), nla_len(info->attrs[NL80211_ATTR_FRAME]), no_cck, dont_wait_for_ack, &cookie); @@ -8395,7 +8388,6 @@ static void nl80211_send_remain_on_chan_event( int cmd, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration, gfp_t gfp) { struct sk_buff *msg; @@ -8416,7 +8408,8 @@ static void nl80211_send_remain_on_chan_event( wdev->netdev->ifindex)) || nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) || nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq) || - nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type) || + nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_NO_HT) || nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie)) goto nla_put_failure; @@ -8438,23 +8431,20 @@ static void nl80211_send_remain_on_chan_event( void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration, gfp_t gfp) { nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL, rdev, wdev, cookie, chan, - channel_type, duration, gfp); + duration, gfp); } void nl80211_send_remain_on_channel_cancel( struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, - u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, gfp_t gfp) + u64 cookie, struct ieee80211_channel *chan, gfp_t gfp) { nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, - rdev, wdev, cookie, chan, - channel_type, 0, gfp); + rdev, wdev, cookie, chan, 0, gfp); } void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index f6153516068..7adbd767dbf 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -76,13 +76,11 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration, gfp_t gfp); void nl80211_send_remain_on_channel_cancel( struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, - u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, gfp_t gfp); + u64 cookie, struct ieee80211_channel *chan, gfp_t gfp); void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *mac_addr, diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 6e5fa659068..ee54a5aa438 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -600,14 +600,12 @@ static inline int rdev_remain_on_channel(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration, u64 *cookie) { int ret; - trace_rdev_remain_on_channel(&rdev->wiphy, wdev, chan, channel_type, - duration); + trace_rdev_remain_on_channel(&rdev->wiphy, wdev, chan, duration); ret = rdev->ops->remain_on_channel(&rdev->wiphy, wdev, chan, - channel_type, duration, cookie); + duration, cookie); trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie); return ret; } @@ -626,17 +624,15 @@ rdev_cancel_remain_on_channel(struct cfg80211_registered_device *rdev, static inline int rdev_mgmt_tx(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, - enum nl80211_channel_type channel_type, - bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, bool no_cck, - bool dont_wait_for_ack, u64 *cookie) + unsigned int wait, const u8 *buf, size_t len, + bool no_cck, bool dont_wait_for_ack, u64 *cookie) { int ret; - trace_rdev_mgmt_tx(&rdev->wiphy, wdev, chan, offchan, channel_type, - channel_type_valid, wait, no_cck, dont_wait_for_ack); + trace_rdev_mgmt_tx(&rdev->wiphy, wdev, chan, offchan, + wait, no_cck, dont_wait_for_ack); ret = rdev->ops->mgmt_tx(&rdev->wiphy, wdev, chan, offchan, - channel_type, channel_type_valid, wait, buf, - len, no_cck, dont_wait_for_ack, cookie); + wait, buf, len, no_cck, + dont_wait_for_ack, cookie); trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie); return ret; } diff --git a/net/wireless/trace.h b/net/wireless/trace.h index f264c20a709..ed10833f9a3 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1573,25 +1573,22 @@ DEFINE_EVENT(rdev_pmksa, rdev_del_pmksa, TRACE_EVENT(rdev_remain_on_channel, TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration), - TP_ARGS(wiphy, wdev, chan, channel_type, duration), + unsigned int duration), + TP_ARGS(wiphy, wdev, chan, duration), TP_STRUCT__entry( WIPHY_ENTRY WDEV_ENTRY CHAN_ENTRY - __field(enum nl80211_channel_type, channel_type) __field(unsigned int, duration) ), TP_fast_assign( WIPHY_ASSIGN; WDEV_ASSIGN; CHAN_ASSIGN(chan); - __entry->channel_type = channel_type; __entry->duration = duration; ), - TP_printk(WIPHY_PR_FMT WDEV_PR_FMT CHAN_PR_FMT ", channel type: %d, duration: %u", - WIPHY_PR_ARG, WDEV_PR_ARG, CHAN_PR_ARG, __entry->channel_type, - __entry->duration) + TP_printk(WIPHY_PR_FMT WDEV_PR_FMT CHAN_PR_FMT ", duration: %u", + WIPHY_PR_ARG, WDEV_PR_ARG, CHAN_PR_ARG, __entry->duration) ); TRACE_EVENT(rdev_return_int_cookie, @@ -1631,18 +1628,13 @@ TRACE_EVENT(rdev_cancel_remain_on_channel, TRACE_EVENT(rdev_mgmt_tx, TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, - enum nl80211_channel_type channel_type, - bool channel_type_valid, unsigned int wait, bool no_cck, - bool dont_wait_for_ack), - TP_ARGS(wiphy, wdev, chan, offchan, channel_type, channel_type_valid, - wait, no_cck, dont_wait_for_ack), + unsigned int wait, bool no_cck, bool dont_wait_for_ack), + TP_ARGS(wiphy, wdev, chan, offchan, wait, no_cck, dont_wait_for_ack), TP_STRUCT__entry( WIPHY_ENTRY WDEV_ENTRY CHAN_ENTRY __field(bool, offchan) - __field(enum nl80211_channel_type, channel_type) - __field(bool, channel_type_valid) __field(unsigned int, wait) __field(bool, no_cck) __field(bool, dont_wait_for_ack) @@ -1652,18 +1644,14 @@ TRACE_EVENT(rdev_mgmt_tx, WDEV_ASSIGN; CHAN_ASSIGN(chan); __entry->offchan = offchan; - __entry->channel_type = channel_type; - __entry->channel_type_valid = channel_type_valid; __entry->wait = wait; __entry->no_cck = no_cck; __entry->dont_wait_for_ack = dont_wait_for_ack; ), - TP_printk(WIPHY_PR_FMT WDEV_PR_FMT CHAN_PR_FMT ", offchan: %s, " - "channel type: %d, channel type valid: %s, wait: %u, " - "no cck: %s, dont wait for ack: %s", + TP_printk(WIPHY_PR_FMT WDEV_PR_FMT CHAN_PR_FMT ", offchan: %s," + " wait: %u, no cck: %s, dont wait for ack: %s", WIPHY_PR_ARG, WDEV_PR_ARG, CHAN_PR_ARG, - BOOL_TO_STR(__entry->offchan), __entry->channel_type, - BOOL_TO_STR(__entry->channel_type_valid), __entry->wait, + BOOL_TO_STR(__entry->offchan), __entry->wait, BOOL_TO_STR(__entry->no_cck), BOOL_TO_STR(__entry->dont_wait_for_ack)) ); @@ -1894,47 +1882,41 @@ TRACE_EVENT(cfg80211_michael_mic_failure, TRACE_EVENT(cfg80211_ready_on_channel, TP_PROTO(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration), - TP_ARGS(wdev, cookie, chan, channel_type, duration), + unsigned int duration), + TP_ARGS(wdev, cookie, chan, duration), TP_STRUCT__entry( WDEV_ENTRY __field(u64, cookie) CHAN_ENTRY - __field(enum nl80211_channel_type, channel_type) __field(unsigned int, duration) ), TP_fast_assign( WDEV_ASSIGN; __entry->cookie = cookie; CHAN_ASSIGN(chan); - __entry->channel_type = channel_type; __entry->duration = duration; ), - TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT ", channel type: %d, duration: %u", + TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT ", duration: %u", WDEV_PR_ARG, __entry->cookie, CHAN_PR_ARG, - __entry->channel_type, __entry->duration) + __entry->duration) ); TRACE_EVENT(cfg80211_ready_on_channel_expired, TP_PROTO(struct wireless_dev *wdev, u64 cookie, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type), - TP_ARGS(wdev, cookie, chan, channel_type), + struct ieee80211_channel *chan), + TP_ARGS(wdev, cookie, chan), TP_STRUCT__entry( WDEV_ENTRY __field(u64, cookie) CHAN_ENTRY - __field(enum nl80211_channel_type, channel_type) ), TP_fast_assign( WDEV_ASSIGN; __entry->cookie = cookie; CHAN_ASSIGN(chan); - __entry->channel_type = channel_type; ), - TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT ", channel type: %d", - WDEV_PR_ARG, __entry->cookie, CHAN_PR_ARG, - __entry->channel_type) + TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT, + WDEV_PR_ARG, __entry->cookie, CHAN_PR_ARG) ); TRACE_EVENT(cfg80211_new_sta, -- cgit v1.2.3-70-g09d2 From 683b6d3b31a51956ea540df00abb0b78894924c1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 8 Nov 2012 21:25:48 +0100 Subject: cfg80211: pass a channel definition struct Instead of passing a channel pointer and channel type to all functions and driver methods, pass a new channel definition struct. Right now, this struct contains just the control channel and channel type, but for VHT this will change. Also, add a small inline cfg80211_get_chandef_type() so that drivers don't need to use the _type field of the new structure all the time, which will change. Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 22 +- .../net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 11 +- drivers/net/wireless/libertas/cfg.c | 24 +- drivers/net/wireless/mwifiex/cfg80211.c | 30 ++- drivers/net/wireless/orinoco/cfg.c | 11 +- drivers/net/wireless/rndis_wlan.c | 2 +- include/net/cfg80211.h | 65 +++--- net/mac80211/cfg.c | 43 ++-- net/mac80211/ibss.c | 21 +- net/wireless/chan.c | 59 +---- net/wireless/core.h | 11 +- net/wireless/ibss.c | 27 ++- net/wireless/mesh.c | 49 ++-- net/wireless/mlme.c | 15 +- net/wireless/nl80211.c | 253 +++++++++++---------- net/wireless/nl80211.h | 4 +- net/wireless/rdev-ops.h | 22 +- net/wireless/trace.h | 100 ++++---- net/wireless/wext-compat.c | 26 ++- net/wireless/wext-sme.c | 10 +- 20 files changed, 403 insertions(+), 402 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 74091d33ed6..c0cc2e59fe6 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1093,15 +1093,20 @@ out: void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq, enum wmi_phy_mode mode) { - enum nl80211_channel_type type; + struct cfg80211_chan_def chandef; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "channel switch notify nw_type %d freq %d mode %d\n", vif->nw_type, freq, mode); - type = (mode == WMI_11G_HT20) ? NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT; + chandef.chan = ieee80211_get_channel(vif->ar->wiphy, freq); + if (WARN_ON(!chandef.chan)) + return; + + chandef._type = (mode == WMI_11G_HT20) ? + NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT; - cfg80211_ch_switch_notify(vif->ndev, freq, type); + cfg80211_ch_switch_notify(vif->ndev, &chandef); } static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, @@ -1613,8 +1618,8 @@ static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy, vif->ssid_len = ibss_param->ssid_len; memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len); - if (ibss_param->channel) - vif->ch_hint = ibss_param->channel->center_freq; + if (ibss_param->chandef.chan) + vif->ch_hint = ibss_param->chandef.chan->center_freq; if (ibss_param->channel_fixed) { /* @@ -2856,7 +2861,7 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, p.ssid_len = vif->ssid_len; memcpy(p.ssid, vif->ssid, vif->ssid_len); p.dot11_auth_mode = vif->dot11_auth_mode; - p.ch = cpu_to_le16(info->channel->center_freq); + p.ch = cpu_to_le16(info->chandef.chan->center_freq); /* Enable uAPSD support by default */ res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true); @@ -2880,8 +2885,9 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, return res; } - if (ath6kl_set_htcap(vif, info->channel->band, - info->channel_type != NL80211_CHAN_NO_HT)) + if (ath6kl_set_htcap(vif, info->chandef.chan->band, + cfg80211_get_chandef_type(&info->chandef) + != NL80211_CHAN_NO_HT)) return -EIO; /* diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 904c94121c1..b596ca4f22b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -1212,8 +1212,8 @@ brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev, else WL_CONN("No BSSID specified\n"); - if (params->channel) - WL_CONN("channel: %d\n", params->channel->center_freq); + if (params->chandef.chan) + WL_CONN("channel: %d\n", params->chandef.chan->center_freq); else WL_CONN("no channel specified\n"); @@ -1286,12 +1286,12 @@ brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev, } /* Channel */ - if (params->channel) { + if (params->chandef.chan) { u32 target_channel; cfg->channel = ieee80211_frequency_to_channel( - params->channel->center_freq); + params->chandef.chan->center_freq); if (params->channel_fixed) { /* adding chanspec */ brcmf_ch_to_chanspec(cfg->channel, @@ -3938,7 +3938,8 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, s32 bssidx = 0; WL_TRACE("channel_type=%d, beacon_interval=%d, dtim_period=%d,\n", - settings->channel_type, settings->beacon_interval, + cfg80211_get_chandef_type(&settings->chandef), + settings->beacon_interval, settings->dtim_period); WL_TRACE("ssid=%s(%d), auth_type=%d, inactivity_timeout=%d\n", settings->ssid, settings->ssid_len, settings->auth_type, diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 1c10b542ab2..ec36868f6fc 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -436,19 +436,19 @@ static int lbs_add_wpa_tlv(u8 *tlv, const u8 *ie, u8 ie_len) */ static int lbs_cfg_set_monitor_channel(struct wiphy *wiphy, - struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type) + struct cfg80211_chan_def *chandef) { struct lbs_private *priv = wiphy_priv(wiphy); int ret = -ENOTSUPP; lbs_deb_enter_args(LBS_DEB_CFG80211, "freq %d, type %d", - channel->center_freq, channel_type); + chandef->chan->center_freq, + cfg80211_get_chandef_type(chandef)); - if (channel_type != NL80211_CHAN_NO_HT) + if (cfg80211_get_chandef_type(chandef) != NL80211_CHAN_NO_HT) goto out; - ret = lbs_set_channel(priv, channel->hw_value); + ret = lbs_set_channel(priv, chandef->chan->hw_value); out: lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); @@ -1734,7 +1734,7 @@ static void lbs_join_post(struct lbs_private *priv, /* Fake DS channel IE */ *fake++ = WLAN_EID_DS_PARAMS; *fake++ = 1; - *fake++ = params->channel->hw_value; + *fake++ = params->chandef.chan->hw_value; /* Fake IBSS params IE */ *fake++ = WLAN_EID_IBSS_PARAMS; *fake++ = 2; @@ -1755,7 +1755,7 @@ static void lbs_join_post(struct lbs_private *priv, lbs_deb_hex(LBS_DEB_CFG80211, "IE", fake_ie, fake - fake_ie); bss = cfg80211_inform_bss(priv->wdev->wiphy, - params->channel, + params->chandef.chan, bssid, 0, capability, @@ -1833,7 +1833,7 @@ static int lbs_ibss_join_existing(struct lbs_private *priv, cmd.bss.beaconperiod = cpu_to_le16(params->beacon_interval); cmd.bss.ds.header.id = WLAN_EID_DS_PARAMS; cmd.bss.ds.header.len = 1; - cmd.bss.ds.channel = params->channel->hw_value; + cmd.bss.ds.channel = params->chandef.chan->hw_value; cmd.bss.ibss.header.id = WLAN_EID_IBSS_PARAMS; cmd.bss.ibss.header.len = 2; cmd.bss.ibss.atimwindow = 0; @@ -1942,7 +1942,7 @@ static int lbs_ibss_start_new(struct lbs_private *priv, cmd.ibss.atimwindow = 0; cmd.ds.header.id = WLAN_EID_DS_PARAMS; cmd.ds.header.len = 1; - cmd.ds.channel = params->channel->hw_value; + cmd.ds.channel = params->chandef.chan->hw_value; /* Only v8 and below support setting probe delay */ if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME); @@ -1987,18 +1987,18 @@ static int lbs_join_ibss(struct wiphy *wiphy, struct net_device *dev, lbs_deb_enter(LBS_DEB_CFG80211); - if (!params->channel) { + if (!params->chandef.chan) { ret = -ENOTSUPP; goto out; } - ret = lbs_set_channel(priv, params->channel->hw_value); + ret = lbs_set_channel(priv, params->chandef.chan->hw_value); if (ret) goto out; /* Search if someone is beaconing. This assumes that the * bss list is populated already */ - bss = cfg80211_get_bss(wiphy, params->channel, params->bssid, + bss = cfg80211_get_bss(wiphy, params->chandef.chan, params->bssid, params->ssid, params->ssid_len, WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index f69190b492a..42be612faf0 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1291,21 +1291,23 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, return -EINVAL; } - bss_cfg->channel = - (u8)ieee80211_frequency_to_channel(params->channel->center_freq); + bss_cfg->channel = ieee80211_frequency_to_channel( + params->chandef.chan->center_freq); /* Set appropriate bands */ - if (params->channel->band == IEEE80211_BAND_2GHZ) { + if (params->chandef.chan->band == IEEE80211_BAND_2GHZ) { bss_cfg->band_cfg = BAND_CONFIG_BG; - if (params->channel_type == NL80211_CHAN_NO_HT) + if (cfg80211_get_chandef_type(¶ms->chandef) == + NL80211_CHAN_NO_HT) config_bands = BAND_B | BAND_G; else config_bands = BAND_B | BAND_G | BAND_GN; } else { bss_cfg->band_cfg = BAND_CONFIG_A; - if (params->channel_type == NL80211_CHAN_NO_HT) + if (cfg80211_get_chandef_type(¶ms->chandef) == + NL80211_CHAN_NO_HT) config_bands = BAND_A; else config_bands = BAND_AN | BAND_A; @@ -1678,7 +1680,7 @@ static int mwifiex_set_ibss_params(struct mwifiex_private *priv, int index = 0, i; u8 config_bands = 0; - if (params->channel->band == IEEE80211_BAND_2GHZ) { + if (params->chandef.chan->band == IEEE80211_BAND_2GHZ) { if (!params->basic_rates) { config_bands = BAND_B | BAND_G; } else { @@ -1703,10 +1705,12 @@ static int mwifiex_set_ibss_params(struct mwifiex_private *priv, } } - if (params->channel_type != NL80211_CHAN_NO_HT) + if (cfg80211_get_chandef_type(¶ms->chandef) != + NL80211_CHAN_NO_HT) config_bands |= BAND_GN; } else { - if (params->channel_type == NL80211_CHAN_NO_HT) + if (cfg80211_get_chandef_type(¶ms->chandef) != + NL80211_CHAN_NO_HT) config_bands = BAND_A; else config_bands = BAND_AN | BAND_A; @@ -1723,9 +1727,10 @@ static int mwifiex_set_ibss_params(struct mwifiex_private *priv, } adapter->sec_chan_offset = - mwifiex_chan_type_to_sec_chan_offset(params->channel_type); - priv->adhoc_channel = - ieee80211_frequency_to_channel(params->channel->center_freq); + mwifiex_chan_type_to_sec_chan_offset( + cfg80211_get_chandef_type(¶ms->chandef)); + priv->adhoc_channel = ieee80211_frequency_to_channel( + params->chandef.chan->center_freq); wiphy_dbg(wiphy, "info: set ibss band %d, chan %d, chan offset %d\n", config_bands, priv->adhoc_channel, adapter->sec_chan_offset); @@ -1759,7 +1764,8 @@ mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, ret = mwifiex_cfg80211_assoc(priv, params->ssid_len, params->ssid, params->bssid, priv->bss_mode, - params->channel, NULL, params->privacy); + params->chandef.chan, NULL, + params->privacy); done: if (!ret) { cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, GFP_KERNEL); diff --git a/drivers/net/wireless/orinoco/cfg.c b/drivers/net/wireless/orinoco/cfg.c index 7b751fba7e1..d01edd2c50c 100644 --- a/drivers/net/wireless/orinoco/cfg.c +++ b/drivers/net/wireless/orinoco/cfg.c @@ -161,24 +161,23 @@ static int orinoco_scan(struct wiphy *wiphy, } static int orinoco_set_monitor_channel(struct wiphy *wiphy, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type) + struct cfg80211_chan_def *chandef) { struct orinoco_private *priv = wiphy_priv(wiphy); int err = 0; unsigned long flags; int channel; - if (!chan) + if (!chandef->chan) return -EINVAL; - if (channel_type != NL80211_CHAN_NO_HT) + if (cfg80211_get_chandef_type(chandef) != NL80211_CHAN_NO_HT) return -EINVAL; - if (chan->band != IEEE80211_BAND_2GHZ) + if (chandef->chan->band != IEEE80211_BAND_2GHZ) return -EINVAL; - channel = ieee80211_freq_to_dsss_chan(chan->center_freq); + channel = ieee80211_freq_to_dsss_chan(chandef->chan->center_freq); if ((channel < 1) || (channel > NUM_CHANNELS) || !(priv->channel_mask & (1 << (channel - 1)))) diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 5390af36c06..abe1d039be8 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -2293,7 +2293,7 @@ static int rndis_join_ibss(struct wiphy *wiphy, struct net_device *dev, { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; - struct ieee80211_channel *channel = params->channel; + struct ieee80211_channel *channel = params->chandef.chan; struct ndis_80211_ssid ssid; enum nl80211_auth_type auth_type; int ret, alg, length, chan = -1; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 1effe0682d2..86f777af79e 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -305,6 +305,23 @@ struct key_params { u32 cipher; }; +/** + * struct cfg80211_chan_def - channel definition + * @chan: the (control) channel + * @_type: the channel type, don't use this field, + * use cfg80211_get_chandef_type() if needed. + */ +struct cfg80211_chan_def { + struct ieee80211_channel *chan; + enum nl80211_channel_type _type; +}; + +static inline enum nl80211_channel_type +cfg80211_get_chandef_type(const struct cfg80211_chan_def *chandef) +{ + return chandef->_type; +} + /** * enum survey_info_flags - survey information flags * @@ -426,8 +443,7 @@ struct cfg80211_beacon_data { * * Used to configure an AP interface. * - * @channel: the channel to start the AP on - * @channel_type: the channel type to use + * @chandef: defines the channel to use * @beacon: beacon data * @beacon_interval: beacon interval * @dtim_period: DTIM period @@ -441,8 +457,7 @@ struct cfg80211_beacon_data { * @inactivity_timeout: time in seconds to determine station's inactivity. */ struct cfg80211_ap_settings { - struct ieee80211_channel *channel; - enum nl80211_channel_type channel_type; + struct cfg80211_chan_def chandef; struct cfg80211_beacon_data beacon; @@ -909,8 +924,7 @@ struct mesh_config { /** * struct mesh_setup - 802.11s mesh setup configuration - * @channel: the channel to start the mesh network on - * @channel_type: the channel type to use + * @chandef: defines the channel to use * @mesh_id: the mesh ID * @mesh_id_len: length of the mesh ID, at least 1 and at most 32 bytes * @sync_method: which synchronization method to use @@ -925,8 +939,7 @@ struct mesh_config { * These parameters are fixed when the mesh is created. */ struct mesh_setup { - struct ieee80211_channel *channel; - enum nl80211_channel_type channel_type; + struct cfg80211_chan_def chandef; const u8 *mesh_id; u8 mesh_id_len; u8 sync_method; @@ -1266,8 +1279,7 @@ struct cfg80211_disassoc_request { * @ssid_len: The length of the SSID, will always be non-zero. * @bssid: Fixed BSSID requested, maybe be %NULL, if set do not * search for IBSSs with a different BSSID. - * @channel: The channel to use if no IBSS can be found to join. - * @channel_type: channel type (HT mode) + * @chandef: defines the channel to use if no other IBSS to join can be found * @channel_fixed: The channel should be fixed -- do not search for * IBSSs to join on other channels. * @ie: information element(s) to include in the beacon @@ -1285,8 +1297,7 @@ struct cfg80211_disassoc_request { struct cfg80211_ibss_params { u8 *ssid; u8 *bssid; - struct ieee80211_channel *channel; - enum nl80211_channel_type channel_type; + struct cfg80211_chan_def chandef; u8 *ie; u8 ssid_len, ie_len; u16 beacon_interval; @@ -1728,8 +1739,7 @@ struct cfg80211_ops { struct ieee80211_channel *chan); int (*set_monitor_channel)(struct wiphy *wiphy, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type); + struct cfg80211_chan_def *chandef); int (*scan)(struct wiphy *wiphy, struct cfg80211_scan_request *request); @@ -1855,10 +1865,9 @@ struct cfg80211_ops { void (*get_et_strings)(struct wiphy *wiphy, struct net_device *dev, u32 sset, u8 *data); - struct ieee80211_channel * - (*get_channel)(struct wiphy *wiphy, + int (*get_channel)(struct wiphy *wiphy, struct wireless_dev *wdev, - enum nl80211_channel_type *type); + struct cfg80211_chan_def *chandef); int (*start_p2p_device)(struct wiphy *wiphy, struct wireless_dev *wdev); @@ -2466,8 +2475,7 @@ struct wireless_dev { spinlock_t event_lock; struct cfg80211_internal_bss *current_bss; /* associated / joined */ - struct ieee80211_channel *preset_chan; - enum nl80211_channel_type preset_chantype; + struct cfg80211_chan_def preset_chandef; /* for AP and mesh channel tracking */ struct ieee80211_channel *channel; @@ -3563,28 +3571,25 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy, int freq, int sig_dbm); /** - * cfg80211_can_beacon_sec_chan - test if ht40 on extension channel can be used + * cfg80211_reg_can_beacon - check if beaconing is allowed * @wiphy: the wiphy - * @chan: main channel - * @channel_type: HT mode + * @chandef: the channel definition * * This function returns true if there is no secondary channel or the secondary - * channel can be used for beaconing (i.e. is not a radar channel etc.) + * channel(s) can be used for beaconing (i.e. is not a radar channel etc.) */ -bool cfg80211_can_beacon_sec_chan(struct wiphy *wiphy, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type); +bool cfg80211_reg_can_beacon(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef); /* * cfg80211_ch_switch_notify - update wdev channel and notify userspace * @dev: the device which switched channels - * @freq: new channel frequency (in MHz) - * @type: channel type + * @chandef: the new channel definition * * Acquires wdev_lock, so must only be called from sleepable driver context! */ -void cfg80211_ch_switch_notify(struct net_device *dev, int freq, - enum nl80211_channel_type type); +void cfg80211_ch_switch_notify(struct net_device *dev, + struct cfg80211_chan_def *chandef); /* * cfg80211_tdls_oper_request - request userspace to perform TDLS operation diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index ac0241e3539..fbb2d072cb9 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -735,14 +735,16 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, } static int ieee80211_set_monitor_channel(struct wiphy *wiphy, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type) + struct cfg80211_chan_def *chandef) { struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_sub_if_data *sdata; + enum nl80211_channel_type channel_type; int ret = 0; - if (local->monitor_channel == chan && + channel_type = cfg80211_get_chandef_type(chandef); + + if (local->monitor_channel == chandef->chan && local->monitor_channel_type == channel_type) return 0; @@ -754,17 +756,17 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy, if (sdata) { ieee80211_vif_release_channel(sdata); ret = ieee80211_vif_use_channel( - sdata, chan, channel_type, + sdata, chandef->chan, channel_type, IEEE80211_CHANCTX_EXCLUSIVE); } } else if (local->open_count == local->monitors) { - local->_oper_channel = chan; + local->_oper_channel = chandef->chan; local->_oper_channel_type = channel_type; ieee80211_hw_config(local, 0); } if (ret == 0) { - local->monitor_channel = chan; + local->monitor_channel = chandef->chan; local->monitor_channel_type = channel_type; } mutex_unlock(&local->iflist_mtx); @@ -888,9 +890,10 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, sdata->smps_mode = IEEE80211_SMPS_OFF; sdata->needed_rx_chains = sdata->local->rx_chains; - err = ieee80211_vif_use_channel(sdata, params->channel, - params->channel_type, - IEEE80211_CHANCTX_SHARED); + err = ieee80211_vif_use_channel( + sdata, params->chandef.chan, + cfg80211_get_chandef_type(¶ms->chandef), + IEEE80211_CHANCTX_SHARED); if (err) return err; @@ -1707,9 +1710,10 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev, sdata->smps_mode = IEEE80211_SMPS_OFF; sdata->needed_rx_chains = sdata->local->rx_chains; - err = ieee80211_vif_use_channel(sdata, setup->channel, - setup->channel_type, - IEEE80211_CHANCTX_SHARED); + err = ieee80211_vif_use_channel( + sdata, setup->chandef.chan, + cfg80211_get_chandef_type(&setup->chandef), + IEEE80211_CHANCTX_SHARED); if (err) return err; @@ -3110,23 +3114,24 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, return 0; } -static struct ieee80211_channel * -ieee80211_cfg_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev, - enum nl80211_channel_type *type) +static int ieee80211_cfg_get_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_chan_def *chandef) { struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); struct ieee80211_chanctx_conf *chanctx_conf; - struct ieee80211_channel *chan = NULL; + int ret = -ENODATA; rcu_read_lock(); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); if (chanctx_conf) { - *type = chanctx_conf->channel_type; - chan = chanctx_conf->channel; + chandef->chan = chanctx_conf->channel; + chandef->_type = chanctx_conf->channel_type; + ret = 0; } rcu_read_unlock(); - return chan; + return ret; } #ifdef CONFIG_PM diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 845973b67a7..bed616fd97e 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -51,7 +51,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, struct cfg80211_bss *bss; u32 bss_change; u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; - enum nl80211_channel_type channel_type; + struct cfg80211_chan_def chandef; lockdep_assert_held(&ifibss->mtx); @@ -79,12 +79,13 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0; - channel_type = ifibss->channel_type; - if (!cfg80211_can_beacon_sec_chan(local->hw.wiphy, chan, channel_type)) - channel_type = NL80211_CHAN_HT20; + chandef.chan = chan; + chandef._type = ifibss->channel_type; + if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) + chandef._type = NL80211_CHAN_HT20; ieee80211_vif_release_channel(sdata); - if (ieee80211_vif_use_channel(sdata, chan, channel_type, + if (ieee80211_vif_use_channel(sdata, chan, chandef._type, ifibss->fixed_channel ? IEEE80211_CHANCTX_SHARED : IEEE80211_CHANCTX_EXCLUSIVE)) { @@ -158,7 +159,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, ifibss->ie, ifibss->ie_len); /* add HT capability and information IEs */ - if (channel_type && sband->ht_cap.ht_supported) { + if (chandef._type != NL80211_CHAN_NO_HT && + sband->ht_cap.ht_supported) { pos = skb_put(skb, 4 + sizeof(struct ieee80211_ht_cap) + sizeof(struct ieee80211_ht_operation)); @@ -170,7 +172,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, * keep them at 0 */ pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap, - chan, channel_type, 0); + chan, chandef._type, 0); } if (local->hw.queues >= IEEE80211_NUM_ACS) { @@ -1078,8 +1080,9 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, sdata->vif.bss_conf.beacon_int = params->beacon_interval; - sdata->u.ibss.channel = params->channel; - sdata->u.ibss.channel_type = params->channel_type; + sdata->u.ibss.channel = params->chandef.chan; + sdata->u.ibss.channel_type = + cfg80211_get_chandef_type(¶ms->chandef); sdata->u.ibss.fixed_channel = params->channel_fixed; if (params->ie) { diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 48febd2160b..e834422de40 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -11,51 +11,15 @@ #include "core.h" #include "rdev-ops.h" -struct ieee80211_channel * -rdev_freq_to_chan(struct cfg80211_registered_device *rdev, - int freq, enum nl80211_channel_type channel_type) -{ - struct ieee80211_channel *chan; - struct ieee80211_sta_ht_cap *ht_cap; - - chan = ieee80211_get_channel(&rdev->wiphy, freq); - - /* Primary channel not allowed */ - if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) - return NULL; - - if (channel_type == NL80211_CHAN_HT40MINUS && - chan->flags & IEEE80211_CHAN_NO_HT40MINUS) - return NULL; - else if (channel_type == NL80211_CHAN_HT40PLUS && - chan->flags & IEEE80211_CHAN_NO_HT40PLUS) - return NULL; - - ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; - - if (channel_type != NL80211_CHAN_NO_HT) { - if (!ht_cap->ht_supported) - return NULL; - - if (channel_type != NL80211_CHAN_HT20 && - (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || - ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)) - return NULL; - } - - return chan; -} - -bool cfg80211_can_beacon_sec_chan(struct wiphy *wiphy, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type) +bool cfg80211_reg_can_beacon(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef) { struct ieee80211_channel *sec_chan; int diff; - trace_cfg80211_can_beacon_sec_chan(wiphy, chan, channel_type); + trace_cfg80211_reg_can_beacon(wiphy, chandef); - switch (channel_type) { + switch (chandef->_type) { case NL80211_CHAN_HT40PLUS: diff = 20; break; @@ -67,7 +31,8 @@ bool cfg80211_can_beacon_sec_chan(struct wiphy *wiphy, return true; } - sec_chan = ieee80211_get_channel(wiphy, chan->center_freq + diff); + sec_chan = ieee80211_get_channel(wiphy, + chandef->chan->center_freq + diff); if (!sec_chan) { trace_cfg80211_return_bool(false); return false; @@ -84,23 +49,17 @@ bool cfg80211_can_beacon_sec_chan(struct wiphy *wiphy, trace_cfg80211_return_bool(true); return true; } -EXPORT_SYMBOL(cfg80211_can_beacon_sec_chan); +EXPORT_SYMBOL(cfg80211_reg_can_beacon); int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, - int freq, enum nl80211_channel_type chantype) + struct cfg80211_chan_def *chandef) { - struct ieee80211_channel *chan; - if (!rdev->ops->set_monitor_channel) return -EOPNOTSUPP; if (!cfg80211_has_monitors_only(rdev)) return -EBUSY; - chan = rdev_freq_to_chan(rdev, freq, chantype); - if (!chan) - return -EINVAL; - - return rdev_set_monitor_channel(rdev, chan, chantype); + return rdev_set_monitor_channel(rdev, chandef); } void diff --git a/net/wireless/core.h b/net/wireless/core.h index b0a09cf56e0..6183a0d25b8 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -309,9 +309,9 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, const struct mesh_config *conf); int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev); -int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, int freq, - enum nl80211_channel_type channel_type); +int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_chan_def *chandef); /* AP */ int cfg80211_stop_ap(struct cfg80211_registered_device *rdev, @@ -470,11 +470,8 @@ cfg80211_get_chan_state(struct wireless_dev *wdev, struct ieee80211_channel **chan, enum cfg80211_chan_mode *chanmode); -struct ieee80211_channel * -rdev_freq_to_chan(struct cfg80211_registered_device *rdev, - int freq, enum nl80211_channel_type channel_type); int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, - int freq, enum nl80211_channel_type chantype); + struct cfg80211_chan_def *chandef); int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, const u8 *rates, unsigned int n_rates, diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 27941d5db72..ccc8865dfad 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -100,9 +100,9 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, * 11a for maximum compatibility. */ struct ieee80211_supported_band *sband = - rdev->wiphy.bands[params->channel->band]; + rdev->wiphy.bands[params->chandef.chan->band]; int j; - u32 flag = params->channel->band == IEEE80211_BAND_5GHZ ? + u32 flag = params->chandef.chan->band == IEEE80211_BAND_5GHZ ? IEEE80211_RATE_MANDATORY_A : IEEE80211_RATE_MANDATORY_B; @@ -118,11 +118,11 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, wdev->ibss_fixed = params->channel_fixed; #ifdef CONFIG_CFG80211_WEXT - wdev->wext.ibss.channel = params->channel; + wdev->wext.ibss.chandef = params->chandef; #endif wdev->sme_state = CFG80211_SME_CONNECTING; - err = cfg80211_can_use_chan(rdev, wdev, params->channel, + err = cfg80211_can_use_chan(rdev, wdev, params->chandef.chan, params->channel_fixed ? CHAN_MODE_SHARED : CHAN_MODE_EXCLUSIVE); @@ -251,7 +251,9 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, wdev->wext.ibss.beacon_interval = 100; /* try to find an IBSS channel if none requested ... */ - if (!wdev->wext.ibss.channel) { + if (!wdev->wext.ibss.chandef.chan) { + wdev->wext.ibss.chandef._type = NL80211_CHAN_NO_HT; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; @@ -266,15 +268,15 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, continue; if (chan->flags & IEEE80211_CHAN_DISABLED) continue; - wdev->wext.ibss.channel = chan; + wdev->wext.ibss.chandef.chan = chan; break; } - if (wdev->wext.ibss.channel) + if (wdev->wext.ibss.chandef.chan) break; } - if (!wdev->wext.ibss.channel) + if (!wdev->wext.ibss.chandef.chan) return -EINVAL; } @@ -336,7 +338,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, return -EINVAL; } - if (wdev->wext.ibss.channel == chan) + if (wdev->wext.ibss.chandef.chan == chan) return 0; wdev_lock(wdev); @@ -349,7 +351,8 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, return err; if (chan) { - wdev->wext.ibss.channel = chan; + wdev->wext.ibss.chandef.chan = chan; + wdev->wext.ibss.chandef._type = NL80211_CHAN_NO_HT; wdev->wext.ibss.channel_fixed = true; } else { /* cfg80211_ibss_wext_join will pick one if needed */ @@ -379,8 +382,8 @@ int cfg80211_ibss_wext_giwfreq(struct net_device *dev, wdev_lock(wdev); if (wdev->current_bss) chan = wdev->current_bss->pub.channel; - else if (wdev->wext.ibss.channel) - chan = wdev->wext.ibss.channel; + else if (wdev->wext.ibss.chandef.chan) + chan = wdev->wext.ibss.chandef.chan; wdev_unlock(wdev); if (chan) { diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 966cfc4cd79..12b5a570a30 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -73,8 +73,6 @@ const struct mesh_config default_mesh_config = { const struct mesh_setup default_mesh_setup = { /* cfg80211_join_mesh() will pick a channel if needed */ - .channel = NULL, - .channel_type = NL80211_CHAN_NO_HT, .sync_method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET, .path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP, .path_metric = IEEE80211_PATH_METRIC_AIRTIME, @@ -111,13 +109,12 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, if (!rdev->ops->join_mesh) return -EOPNOTSUPP; - if (!setup->channel) { + if (!setup->chandef.chan) { /* if no channel explicitly given, use preset channel */ - setup->channel = wdev->preset_chan; - setup->channel_type = wdev->preset_chantype; + setup->chandef = wdev->preset_chandef; } - if (!setup->channel) { + if (!setup->chandef.chan) { /* if we don't have that either, use the first usable channel */ enum ieee80211_band band; @@ -137,26 +134,25 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, IEEE80211_CHAN_DISABLED | IEEE80211_CHAN_RADAR)) continue; - setup->channel = chan; + setup->chandef.chan = chan; break; } - if (setup->channel) + if (setup->chandef.chan) break; } /* no usable channel ... */ - if (!setup->channel) + if (!setup->chandef.chan) return -EINVAL; - setup->channel_type = NL80211_CHAN_NO_HT; + setup->chandef._type = NL80211_CHAN_NO_HT; } - if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, setup->channel, - setup->channel_type)) + if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef)) return -EINVAL; - err = cfg80211_can_use_chan(rdev, wdev, setup->channel, + err = cfg80211_can_use_chan(rdev, wdev, setup->chandef.chan, CHAN_MODE_SHARED); if (err) return err; @@ -165,7 +161,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, if (!err) { memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len); wdev->mesh_id_len = setup->mesh_id_len; - wdev->channel = setup->channel; + wdev->channel = setup->chandef.chan; } return err; @@ -188,20 +184,12 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, return err; } -int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, int freq, - enum nl80211_channel_type channel_type) +int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_chan_def *chandef) { - struct ieee80211_channel *channel; int err; - channel = rdev_freq_to_chan(rdev, freq, channel_type); - if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy, - channel, - channel_type)) { - return -EINVAL; - } - /* * Workaround for libertas (only!), it puts the interface * into mesh mode but doesn't implement join_mesh. Instead, @@ -210,21 +198,21 @@ int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, * compatible with 802.11 mesh. */ if (rdev->ops->libertas_set_mesh_channel) { - if (channel_type != NL80211_CHAN_NO_HT) + if (chandef->_type != NL80211_CHAN_NO_HT) return -EINVAL; if (!netif_running(wdev->netdev)) return -ENETDOWN; - err = cfg80211_can_use_chan(rdev, wdev, channel, + err = cfg80211_can_use_chan(rdev, wdev, chandef->chan, CHAN_MODE_SHARED); if (err) return err; err = rdev_libertas_set_mesh_channel(rdev, wdev->netdev, - channel); + chandef->chan); if (!err) - wdev->channel = channel; + wdev->channel = chandef->chan; return err; } @@ -232,8 +220,7 @@ int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, if (wdev->mesh_id_len) return -EBUSY; - wdev->preset_chan = channel; - wdev->preset_chantype = channel_type; + wdev->preset_chandef = *chandef; return 0; } diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index a9646b53a09..5e8123ee63f 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -988,15 +988,14 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, } EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); -void cfg80211_ch_switch_notify(struct net_device *dev, int freq, - enum nl80211_channel_type type) +void cfg80211_ch_switch_notify(struct net_device *dev, + struct cfg80211_chan_def *chandef) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - struct ieee80211_channel *chan; - trace_cfg80211_ch_switch_notify(dev, freq, type); + trace_cfg80211_ch_switch_notify(dev, chandef); wdev_lock(wdev); @@ -1004,12 +1003,8 @@ void cfg80211_ch_switch_notify(struct net_device *dev, int freq, wdev->iftype != NL80211_IFTYPE_P2P_GO)) goto out; - chan = rdev_freq_to_chan(rdev, freq, type); - if (WARN_ON(!chan)) - goto out; - - wdev->channel = chan; - nl80211_ch_switch_notify(rdev, dev, freq, type, GFP_KERNEL); + wdev->channel = chandef->chan; + nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL); out: wdev_unlock(wdev); return; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e880f449495..999108cd947 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1381,30 +1381,82 @@ static bool nl80211_valid_channel_type(struct genl_info *info, return true; } +static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, + struct genl_info *info, + struct cfg80211_chan_def *chandef) +{ + struct ieee80211_sta_ht_cap *ht_cap; + struct ieee80211_channel *sc; + u32 control_freq; + int offs; + + if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) + return -EINVAL; + + control_freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); + + chandef->chan = ieee80211_get_channel(&rdev->wiphy, control_freq); + chandef->_type = NL80211_CHAN_NO_HT; + + if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && + !nl80211_valid_channel_type(info, &chandef->_type)) + return -EINVAL; + + /* Primary channel not allowed */ + if (!chandef->chan || chandef->chan->flags & IEEE80211_CHAN_DISABLED) + return -EINVAL; + + ht_cap = &rdev->wiphy.bands[chandef->chan->band]->ht_cap; + + switch (chandef->_type) { + case NL80211_CHAN_NO_HT: + break; + case NL80211_CHAN_HT40MINUS: + if (chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS) + return -EINVAL; + offs = -20; + /* fall through */ + case NL80211_CHAN_HT40PLUS: + if (chandef->_type == NL80211_CHAN_HT40PLUS) { + if (chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS) + return -EINVAL; + offs = 20; + } + if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || + ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT) + return -EINVAL; + + sc = ieee80211_get_channel(&rdev->wiphy, + chandef->chan->center_freq + offs); + if (!sc || sc->flags & IEEE80211_CHAN_DISABLED) + return -EINVAL; + /* fall through */ + case NL80211_CHAN_HT20: + if (!ht_cap->ht_supported) + return -EINVAL; + break; + } + + return 0; +} + static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct genl_info *info) { - struct ieee80211_channel *channel; - enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; - u32 freq; + struct cfg80211_chan_def chandef; int result; enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR; if (wdev) iftype = wdev->iftype; - if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) - return -EINVAL; - if (!nl80211_can_set_dev_channel(wdev)) return -EOPNOTSUPP; - if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && - !nl80211_valid_channel_type(info, &channel_type)) - return -EINVAL; - - freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); + result = nl80211_parse_chandef(rdev, info, &chandef); + if (result) + return result; mutex_lock(&rdev->devlist_mtx); switch (iftype) { @@ -1414,22 +1466,18 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, result = -EBUSY; break; } - channel = rdev_freq_to_chan(rdev, freq, channel_type); - if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy, - channel, - channel_type)) { + if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef)) { result = -EINVAL; break; } - wdev->preset_chan = channel; - wdev->preset_chantype = channel_type; + wdev->preset_chandef = chandef; result = 0; break; case NL80211_IFTYPE_MESH_POINT: - result = cfg80211_set_mesh_freq(rdev, wdev, freq, channel_type); + result = cfg80211_set_mesh_channel(rdev, wdev, &chandef); break; case NL80211_IFTYPE_MONITOR: - result = cfg80211_set_monitor_channel(rdev, freq, channel_type); + result = cfg80211_set_monitor_channel(rdev, &chandef); break; default: result = -EINVAL; @@ -1749,6 +1797,17 @@ static inline u64 wdev_id(struct wireless_dev *wdev) ((u64)wiphy_to_dev(wdev->wiphy)->wiphy_idx << 32); } +static int nl80211_send_chandef(struct sk_buff *msg, + struct cfg80211_chan_def *chandef) +{ + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, + chandef->chan->center_freq)) + return -ENOBUFS; + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, chandef->_type)) + return -ENOBUFS; + return 0; +} + static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) @@ -1775,16 +1834,14 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag goto nla_put_failure; if (rdev->ops->get_channel) { - struct ieee80211_channel *chan; - enum nl80211_channel_type channel_type; - - chan = rdev_get_channel(rdev, wdev, &channel_type); - if (chan && - (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, - chan->center_freq) || - nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, - channel_type))) - goto nla_put_failure; + int ret; + struct cfg80211_chan_def chandef; + + ret = rdev_get_channel(rdev, wdev, &chandef); + if (ret == 0) { + if (nl80211_send_chandef(msg, &chandef)) + goto nla_put_failure; + } } if (wdev->ssid_len) { @@ -2492,11 +2549,10 @@ static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev, wdev->iftype != NL80211_IFTYPE_P2P_GO) continue; - if (!wdev->preset_chan) + if (!wdev->preset_chandef.chan) continue; - params->channel = wdev->preset_chan; - params->channel_type = wdev->preset_chantype; + params->chandef = wdev->preset_chandef; ret = true; break; } @@ -2618,30 +2674,19 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) } if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { - enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; - - if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && - !nl80211_valid_channel_type(info, &channel_type)) - return -EINVAL; - - params.channel = rdev_freq_to_chan(rdev, - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]), - channel_type); - if (!params.channel) - return -EINVAL; - params.channel_type = channel_type; - } else if (wdev->preset_chan) { - params.channel = wdev->preset_chan; - params.channel_type = wdev->preset_chantype; + err = nl80211_parse_chandef(rdev, info, ¶ms.chandef); + if (err) + return err; + } else if (wdev->preset_chandef.chan) { + params.chandef = wdev->preset_chandef; } else if (!nl80211_get_ap_channel(rdev, ¶ms)) return -EINVAL; - if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, params.channel, - params.channel_type)) + if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef)) return -EINVAL; mutex_lock(&rdev->devlist_mtx); - err = cfg80211_can_use_chan(rdev, wdev, params.channel, + err = cfg80211_can_use_chan(rdev, wdev, params.chandef.chan, CHAN_MODE_SHARED); mutex_unlock(&rdev->devlist_mtx); @@ -2650,10 +2695,9 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) err = rdev_start_ap(rdev, dev, ¶ms); if (!err) { - wdev->preset_chan = params.channel; - wdev->preset_chantype = params.channel_type; + wdev->preset_chandef = params.chandef; wdev->beacon_interval = params.beacon_interval; - wdev->channel = params.channel; + wdev->channel = params.chandef.chan; wdev->ssid_len = params.ssid_len; memcpy(wdev->ssid, params.ssid, wdev->ssid_len); } @@ -5330,8 +5374,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; - if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || - !info->attrs[NL80211_ATTR_SSID] || + if (!info->attrs[NL80211_ATTR_SSID] || !nla_len(info->attrs[NL80211_ATTR_SSID])) return -EINVAL; @@ -5366,34 +5409,11 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } - if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { - enum nl80211_channel_type channel_type; - - if (!nl80211_valid_channel_type(info, &channel_type)) - return -EINVAL; - - if (channel_type != NL80211_CHAN_NO_HT && - !(wiphy->features & NL80211_FEATURE_HT_IBSS)) - return -EINVAL; - - ibss.channel_type = channel_type; - } else { - ibss.channel_type = NL80211_CHAN_NO_HT; - } - - ibss.channel = rdev_freq_to_chan(rdev, - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]), - ibss.channel_type); - if (!ibss.channel || - ibss.channel->flags & IEEE80211_CHAN_NO_IBSS || - ibss.channel->flags & IEEE80211_CHAN_DISABLED) - return -EINVAL; + err = nl80211_parse_chandef(rdev, info, &ibss.chandef); + if (err) + return err; - /* Both channels should be able to initiate communication */ - if ((ibss.channel_type == NL80211_CHAN_HT40PLUS || - ibss.channel_type == NL80211_CHAN_HT40MINUS) && - !cfg80211_can_beacon_sec_chan(&rdev->wiphy, ibss.channel, - ibss.channel_type)) + if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef)) return -EINVAL; ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; @@ -5405,7 +5425,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) int n_rates = nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); struct ieee80211_supported_band *sband = - wiphy->bands[ibss.channel->band]; + wiphy->bands[ibss.chandef.chan->band]; err = ieee80211_get_ratemask(sband, rates, n_rates, &ibss.basic_rates); @@ -5427,7 +5447,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) if (IS_ERR(connkeys)) return PTR_ERR(connkeys); - if ((ibss.channel_type != NL80211_CHAN_NO_HT) && no_ht) { + if ((ibss.chandef._type != NL80211_CHAN_NO_HT) && no_ht) { kfree(connkeys); return -EINVAL; } @@ -5948,11 +5968,11 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct wireless_dev *wdev = info->user_ptr[1]; - struct ieee80211_channel *chan; + struct cfg80211_chan_def chandef; struct sk_buff *msg; void *hdr; u64 cookie; - u32 freq, duration; + u32 duration; int err; if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || @@ -5973,14 +5993,9 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, duration > rdev->wiphy.max_remain_on_channel_duration) return -EINVAL; - if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && - !nl80211_valid_channel_type(info, NULL)) - return -EINVAL; - - freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); - chan = rdev_freq_to_chan(rdev, freq, NL80211_CHAN_NO_HT); - if (chan == NULL) - return -EINVAL; + err = nl80211_parse_chandef(rdev, info, &chandef); + if (err) + return err; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) @@ -5994,7 +6009,8 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, goto free_msg; } - err = rdev_remain_on_channel(rdev, wdev, chan, duration, &cookie); + err = rdev_remain_on_channel(rdev, wdev, chandef.chan, + duration, &cookie); if (err) goto free_msg; @@ -6213,8 +6229,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct wireless_dev *wdev = info->user_ptr[1]; - struct ieee80211_channel *chan; - u32 freq; + struct cfg80211_chan_def chandef; int err; void *hdr = NULL; u64 cookie; @@ -6224,8 +6239,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK]; - if (!info->attrs[NL80211_ATTR_FRAME] || - !info->attrs[NL80211_ATTR_WIPHY_FREQ]) + if (!info->attrs[NL80211_ATTR_FRAME]) return -EINVAL; if (!rdev->ops->mgmt_tx) @@ -6260,10 +6274,6 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) } - if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && - !nl80211_valid_channel_type(info, NULL)) - return -EINVAL; - offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK]; if (offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)) @@ -6271,10 +6281,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); - freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); - chan = rdev_freq_to_chan(rdev, freq, NL80211_CHAN_NO_HT); - if (chan == NULL) - return -EINVAL; + err = nl80211_parse_chandef(rdev, info, &chandef); + if (err) + return err; if (!dont_wait_for_ack) { msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); @@ -6290,7 +6299,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) } } - err = cfg80211_mlme_mgmt_tx(rdev, wdev, chan, offchan, wait, + err = cfg80211_mlme_mgmt_tx(rdev, wdev, chandef.chan, offchan, wait, nla_data(info->attrs[NL80211_ATTR_FRAME]), nla_len(info->attrs[NL80211_ATTR_FRAME]), no_cck, dont_wait_for_ack, &cookie); @@ -6554,21 +6563,12 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) } if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { - enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; - - if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && - !nl80211_valid_channel_type(info, &channel_type)) - return -EINVAL; - - setup.channel = rdev_freq_to_chan(rdev, - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]), - channel_type); - if (!setup.channel) - return -EINVAL; - setup.channel_type = channel_type; + err = nl80211_parse_chandef(rdev, info, &setup.chandef); + if (err) + return err; } else { /* cfg80211_join_mesh() will sort it out */ - setup.channel = NULL; + setup.chandef.chan = NULL; } return cfg80211_join_mesh(rdev, dev, &setup, &cfg); @@ -8800,8 +8800,8 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, } void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, - struct net_device *netdev, int freq, - enum nl80211_channel_type type, gfp_t gfp) + struct net_device *netdev, + struct cfg80211_chan_def *chandef, gfp_t gfp) { struct sk_buff *msg; void *hdr; @@ -8816,9 +8816,10 @@ void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, return; } - if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || - nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) || - nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, type)) + if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex)) + goto nla_put_failure; + + if (nl80211_send_chandef(msg, chandef)) goto nla_put_failure; genlmsg_end(msg, hdr); diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 7adbd767dbf..2acba8477e9 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -127,8 +127,8 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, const u8 *bssid, bool preauth, gfp_t gfp); void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, - struct net_device *dev, int freq, - enum nl80211_channel_type type, gfp_t gfp); + struct net_device *dev, + struct cfg80211_chan_def *chandef, gfp_t gfp); bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp); diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index ee54a5aa438..6c0c8191f83 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -359,12 +359,11 @@ rdev_libertas_set_mesh_channel(struct cfg80211_registered_device *rdev, static inline int rdev_set_monitor_channel(struct cfg80211_registered_device *rdev, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type) + struct cfg80211_chan_def *chandef) { int ret; - trace_rdev_set_monitor_channel(&rdev->wiphy, chan, channel_type); - ret = rdev->ops->set_monitor_channel(&rdev->wiphy, chan, channel_type); + trace_rdev_set_monitor_channel(&rdev->wiphy, chandef); + ret = rdev->ops->set_monitor_channel(&rdev->wiphy, chandef); trace_rdev_return_int(&rdev->wiphy, ret); return ret; } @@ -844,14 +843,17 @@ static inline void rdev_get_et_strings(struct cfg80211_registered_device *rdev, trace_rdev_return_void(&rdev->wiphy); } -static inline struct ieee80211_channel -*rdev_get_channel(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, enum nl80211_channel_type *type) +static inline int +rdev_get_channel(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_chan_def *chandef) { - struct ieee80211_channel *ret; + int ret; + trace_rdev_get_channel(&rdev->wiphy, wdev); - ret = rdev->ops->get_channel(&rdev->wiphy, wdev, type); - trace_rdev_return_channel(&rdev->wiphy, ret, *type); + ret = rdev->ops->get_channel(&rdev->wiphy, wdev, chandef); + trace_rdev_return_chandef(&rdev->wiphy, ret, chandef); + return ret; } diff --git a/net/wireless/trace.h b/net/wireless/trace.h index ed10833f9a3..1370d52b139 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -126,6 +126,26 @@ #define CHAN_PR_FMT ", band: %d, freq: %u" #define CHAN_PR_ARG __entry->band, __entry->center_freq +#define CHAN_DEF_ENTRY __field(enum ieee80211_band, band) \ + __field(u16, center_freq) \ + __field(u32, channel_type) +#define CHAN_DEF_ASSIGN(chandef) \ + do { \ + if ((chandef) && (chandef)->chan) { \ + __entry->band = (chandef)->chan->band; \ + __entry->center_freq = \ + (chandef)->chan->center_freq; \ + __entry->channel_type = (chandef)->_type; \ + } else { \ + __entry->band = 0; \ + __entry->center_freq = 0; \ + __entry->channel_type = 0; \ + } \ + } while (0) +#define CHAN_DEF_PR_FMT ", band: %d, freq: %u, chantype: %d" +#define CHAN_DEF_PR_ARG __entry->band, __entry->center_freq, \ + __entry->channel_type + #define SINFO_ENTRY __field(int, generation) \ __field(u32, connected_time) \ __field(u32, inactive_time) \ @@ -433,7 +453,7 @@ TRACE_EVENT(rdev_start_ap, TP_STRUCT__entry( WIPHY_ENTRY NETDEV_ENTRY - CHAN_ENTRY + CHAN_DEF_ENTRY __field(int, beacon_interval) __field(int, dtim_period) __array(char, ssid, IEEE80211_MAX_SSID_LEN + 1) @@ -446,7 +466,7 @@ TRACE_EVENT(rdev_start_ap, TP_fast_assign( WIPHY_ASSIGN; NETDEV_ASSIGN; - CHAN_ASSIGN(settings->channel); + CHAN_DEF_ASSIGN(&settings->chandef); __entry->beacon_interval = settings->beacon_interval; __entry->dtim_period = settings->dtim_period; __entry->hidden_ssid = settings->hidden_ssid; @@ -458,10 +478,10 @@ TRACE_EVENT(rdev_start_ap, memcpy(__entry->ssid, settings->ssid, settings->ssid_len); ), TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", AP settings - ssid: %s, " - CHAN_PR_FMT ", beacon interval: %d, dtim period: %d, " + CHAN_DEF_PR_FMT ", beacon interval: %d, dtim period: %d, " "hidden ssid: %d, wpa versions: %u, privacy: %s, " "auth type: %d, inactivity timeout: %d", - WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->ssid, CHAN_PR_ARG, + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->ssid, CHAN_DEF_PR_ARG, __entry->beacon_interval, __entry->dtim_period, __entry->hidden_ssid, __entry->wpa_ver, BOOL_TO_STR(__entry->privacy), __entry->auth_type, @@ -933,21 +953,19 @@ TRACE_EVENT(rdev_libertas_set_mesh_channel, ); TRACE_EVENT(rdev_set_monitor_channel, - TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *chan, - enum nl80211_channel_type chan_type), - TP_ARGS(wiphy, chan, chan_type), + TP_PROTO(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef), + TP_ARGS(wiphy, chandef), TP_STRUCT__entry( WIPHY_ENTRY - CHAN_ENTRY - __field(enum nl80211_channel_type, chan_type) + CHAN_DEF_ENTRY ), TP_fast_assign( WIPHY_ASSIGN; - CHAN_ASSIGN(chan); - __entry->chan_type = chan_type; + CHAN_DEF_ASSIGN(chandef); ), - TP_printk(WIPHY_PR_FMT CHAN_PR_FMT ", channel type : %d", - WIPHY_PR_ARG, CHAN_PR_ARG, __entry->chan_type) + TP_printk(WIPHY_PR_FMT CHAN_DEF_PR_FMT, + WIPHY_PR_ARG, CHAN_DEF_PR_ARG) ); TRACE_EVENT(rdev_auth, @@ -1713,22 +1731,25 @@ DEFINE_EVENT(wiphy_wdev_evt, rdev_get_channel, TP_ARGS(wiphy, wdev) ); -TRACE_EVENT(rdev_return_channel, - TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *chan, - enum nl80211_channel_type type), - TP_ARGS(wiphy, chan, type), +TRACE_EVENT(rdev_return_chandef, + TP_PROTO(struct wiphy *wiphy, int ret, + struct cfg80211_chan_def *chandef), + TP_ARGS(wiphy, ret, chandef), TP_STRUCT__entry( WIPHY_ENTRY - CHAN_ENTRY - __field(enum nl80211_channel_type, type) + __field(int, ret) + CHAN_DEF_ENTRY ), TP_fast_assign( WIPHY_ASSIGN; - CHAN_ASSIGN(chan); - __entry->type = type; + if (ret == 0) + CHAN_DEF_ASSIGN(chandef); + else + CHAN_DEF_ASSIGN((struct cfg80211_chan_def *)NULL); + __entry->ret = ret; ), - TP_printk(WIPHY_PR_FMT CHAN_PR_FMT ", channel type: %d", - WIPHY_PR_ARG, CHAN_PR_ARG, __entry->type) + TP_printk(WIPHY_PR_FMT CHAN_DEF_PR_FMT ", ret: %d", + WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->ret) ); DEFINE_EVENT(wiphy_wdev_evt, rdev_start_p2p_device, @@ -1992,40 +2013,35 @@ TRACE_EVENT(cfg80211_cqm_rssi_notify, NETDEV_PR_ARG, __entry->rssi_event) ); -TRACE_EVENT(cfg80211_can_beacon_sec_chan, - TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type), - TP_ARGS(wiphy, channel, channel_type), +TRACE_EVENT(cfg80211_reg_can_beacon, + TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef), + TP_ARGS(wiphy, chandef), TP_STRUCT__entry( WIPHY_ENTRY - CHAN_ENTRY - __field(enum nl80211_channel_type, channel_type) + CHAN_DEF_ENTRY ), TP_fast_assign( WIPHY_ASSIGN; - CHAN_ASSIGN(channel); - __entry->channel_type = channel_type; + CHAN_DEF_ASSIGN(chandef); ), - TP_printk(WIPHY_PR_FMT CHAN_PR_FMT ", channel_type: %d", - WIPHY_PR_ARG, CHAN_PR_ARG, __entry->channel_type) + TP_printk(WIPHY_PR_FMT CHAN_DEF_PR_FMT, + WIPHY_PR_ARG, CHAN_DEF_PR_ARG) ); TRACE_EVENT(cfg80211_ch_switch_notify, - TP_PROTO(struct net_device *netdev, int freq, - enum nl80211_channel_type type), - TP_ARGS(netdev, freq, type), + TP_PROTO(struct net_device *netdev, + struct cfg80211_chan_def *chandef), + TP_ARGS(netdev, chandef), TP_STRUCT__entry( NETDEV_ENTRY - __field(int, freq) - __field(enum nl80211_channel_type, type) + CHAN_DEF_ENTRY ), TP_fast_assign( NETDEV_ASSIGN; - __entry->freq = freq; - __entry->type = type; + CHAN_DEF_ASSIGN(chandef); ), - TP_printk(NETDEV_PR_FMT ", freq: %d, type: %d", NETDEV_PR_ARG, - __entry->freq, __entry->type) + TP_printk(NETDEV_PR_FMT CHAN_DEF_PR_FMT, + NETDEV_PR_ARG, CHAN_DEF_PR_ARG) ); DECLARE_EVENT_CLASS(cfg80211_rx_evt, diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 742ab6ec4c9..da3307f3236 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -784,6 +784,9 @@ static int cfg80211_wext_siwfreq(struct net_device *dev, { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_chan_def chandef = { + ._type = NL80211_CHAN_NO_HT, + }; int freq, err; switch (wdev->iftype) { @@ -797,8 +800,11 @@ static int cfg80211_wext_siwfreq(struct net_device *dev, return freq; if (freq == 0) return -EINVAL; + chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq); + if (!chandef.chan) + return -EINVAL; mutex_lock(&rdev->devlist_mtx); - err = cfg80211_set_monitor_channel(rdev, freq, NL80211_CHAN_NO_HT); + err = cfg80211_set_monitor_channel(rdev, &chandef); mutex_unlock(&rdev->devlist_mtx); return err; case NL80211_IFTYPE_MESH_POINT: @@ -807,9 +813,11 @@ static int cfg80211_wext_siwfreq(struct net_device *dev, return freq; if (freq == 0) return -EINVAL; + chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq); + if (!chandef.chan) + return -EINVAL; mutex_lock(&rdev->devlist_mtx); - err = cfg80211_set_mesh_freq(rdev, wdev, freq, - NL80211_CHAN_NO_HT); + err = cfg80211_set_mesh_channel(rdev, wdev, &chandef); mutex_unlock(&rdev->devlist_mtx); return err; default: @@ -823,8 +831,8 @@ static int cfg80211_wext_giwfreq(struct net_device *dev, { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct ieee80211_channel *chan; - enum nl80211_channel_type channel_type; + struct cfg80211_chan_def chandef; + int ret; switch (wdev->iftype) { case NL80211_IFTYPE_STATION: @@ -835,10 +843,10 @@ static int cfg80211_wext_giwfreq(struct net_device *dev, if (!rdev->ops->get_channel) return -EINVAL; - chan = rdev_get_channel(rdev, wdev, &channel_type); - if (!chan) - return -EINVAL; - freq->m = chan->center_freq; + ret = rdev_get_channel(rdev, wdev, &chandef); + if (ret) + return ret; + freq->m = chandef.chan->center_freq; freq->e = 6; return 0; default: diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 1f773f668d1..e6e5dbf2f61 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -119,7 +119,15 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, * channel we disconnected above and reconnect below. */ if (chan && !wdev->wext.connect.ssid_len) { - err = cfg80211_set_monitor_channel(rdev, freq, NL80211_CHAN_NO_HT); + struct cfg80211_chan_def chandef = { + ._type = NL80211_CHAN_NO_HT, + }; + + chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq); + if (chandef.chan) + err = cfg80211_set_monitor_channel(rdev, &chandef); + else + err = -EINVAL; goto out; } -- cgit v1.2.3-70-g09d2 From 3d9d1d6656a73ea8407734cfb00b81d14ef62d4b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 8 Nov 2012 23:14:50 +0100 Subject: nl80211/cfg80211: support VHT channel configuration Change nl80211 to support specifying a VHT (or HT) using the control channel frequency (as before) and new attributes for the channel width and first and second center frequency. The old channel type is of course still supported for HT. Also change the cfg80211 channel definition struct to support these by adding the relevant fields to it (and removing the _type field.) This also adds new helper functions: - cfg80211_chandef_create to create a channel def struct given the control channel and channel type, - cfg80211_chandef_identical to check if two channel definitions are identical - cfg80211_chandef_compatible to check if the given channel definitions are compatible, and return the wider of the two This isn't entirely complete, but that doesn't matter until we have a driver using it. In particular, it's missing - regulatory checks on the usable bandwidth (if that even makes sense) - regulatory TX power (database can't deal with it) - a proper channel compatibility calculation for the new channel types Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 10 +- include/net/cfg80211.h | 73 ++++++++- include/uapi/linux/nl80211.h | 61 +++++-- net/mac80211/cfg.c | 5 +- net/mac80211/ibss.c | 13 +- net/wireless/chan.c | 249 ++++++++++++++++++++++++++--- net/wireless/core.h | 6 + net/wireless/ibss.c | 4 +- net/wireless/mesh.c | 4 +- net/wireless/nl80211.c | 160 ++++++++++++------ net/wireless/trace.h | 28 ++-- net/wireless/wext-compat.c | 4 +- net/wireless/wext-sme.c | 3 +- 13 files changed, 507 insertions(+), 113 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index c0cc2e59fe6..51bbe85c574 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1099,12 +1099,10 @@ void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq, "channel switch notify nw_type %d freq %d mode %d\n", vif->nw_type, freq, mode); - chandef.chan = ieee80211_get_channel(vif->ar->wiphy, freq); - if (WARN_ON(!chandef.chan)) - return; - - chandef._type = (mode == WMI_11G_HT20) ? - NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT; + cfg80211_chandef_create(&chandef, + ieee80211_get_channel(vif->ar->wiphy, freq), + (mode == WMI_11G_HT20) ? + NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT); cfg80211_ch_switch_notify(vif->ndev, &chandef); } diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 86f777af79e..977da58fb7e 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -308,20 +308,85 @@ struct key_params { /** * struct cfg80211_chan_def - channel definition * @chan: the (control) channel - * @_type: the channel type, don't use this field, - * use cfg80211_get_chandef_type() if needed. + * @width: channel width + * @center_freq1: center frequency of first segment + * @center_freq2: center frequency of second segment + * (only with 80+80 MHz) */ struct cfg80211_chan_def { struct ieee80211_channel *chan; - enum nl80211_channel_type _type; + enum nl80211_chan_width width; + u32 center_freq1; + u32 center_freq2; }; +/** + * cfg80211_get_chandef_type - return old channel type from chandef + * @chandef: the channel definition + * + * Returns the old channel type (NOHT, HT20, HT40+/-) from a given + * chandef, which must have a bandwidth allowing this conversion. + */ static inline enum nl80211_channel_type cfg80211_get_chandef_type(const struct cfg80211_chan_def *chandef) { - return chandef->_type; + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + return NL80211_CHAN_NO_HT; + case NL80211_CHAN_WIDTH_20: + return NL80211_CHAN_HT20; + case NL80211_CHAN_WIDTH_40: + if (chandef->center_freq1 > chandef->chan->center_freq) + return NL80211_CHAN_HT40PLUS; + return NL80211_CHAN_HT40MINUS; + default: + WARN_ON(1); + return NL80211_CHAN_NO_HT; + } +} + +/** + * cfg80211_chandef_create - create channel definition using channel type + * @chandef: the channel definition struct to fill + * @channel: the control channel + * @chantype: the channel type + * + * Given a channel type, create a channel definition. + */ +void cfg80211_chandef_create(struct cfg80211_chan_def *chandef, + struct ieee80211_channel *channel, + enum nl80211_channel_type chantype); + +/** + * cfg80211_chandef_identical - check if two channel definitions are identical + * @chandef1: first channel definition + * @chandef2: second channel definition + * + * Returns %true if the channels defined by the channel definitions are + * identical, %false otherwise. + */ +static inline bool +cfg80211_chandef_identical(const struct cfg80211_chan_def *chandef1, + const struct cfg80211_chan_def *chandef2) +{ + return (chandef1->chan == chandef2->chan && + chandef1->width == chandef2->width && + chandef1->center_freq1 == chandef2->center_freq1 && + chandef1->center_freq2 == chandef2->center_freq2); } +/** + * cfg80211_chandef_compatible - check if two channel definitions are compatible + * @chandef1: first channel definition + * @chandef2: second channel definition + * + * Returns %NULL if the given channel definitions are incompatible, + * chandef1 or chandef2 otherwise. + */ +const struct cfg80211_chan_def * +cfg80211_chandef_compatible(const struct cfg80211_chan_def *chandef1, + const struct cfg80211_chan_def *chandef2); + /** * enum survey_info_flags - survey information flags * diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 82b5ad38435..84f9c7d84c6 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -118,8 +118,9 @@ * to get a list of all present wiphys. * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or * %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME, - * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ, - * %NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT, + * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ (and the + * attributes determining the channel width; this is used for setting + * monitor mode channel), %NL80211_ATTR_WIPHY_RETRY_SHORT, * %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD, * and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD. * However, for setting the channel, see %NL80211_CMD_SET_CHANNEL @@ -171,7 +172,7 @@ * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY, * %NL80211_ATTR_AUTH_TYPE and %NL80211_ATTR_INACTIVITY_TIMEOUT. * The channel to use can be set on the interface or be given using the - * %NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_WIPHY_CHANNEL_TYPE attrs. + * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel width. * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP @@ -471,8 +472,8 @@ * command is used as an event to indicate the that a trigger level was * reached. * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ - * and %NL80211_ATTR_WIPHY_CHANNEL_TYPE) the given interface (identifed - * by %NL80211_ATTR_IFINDEX) shall operate on. + * and the attributes determining channel width) the given interface + * (identifed by %NL80211_ATTR_IFINDEX) shall operate on. * In case multiple channels are supported by the device, the mechanism * with which it switches channels is implementation-defined. * When a monitor interface is given, it can only switch channel while @@ -566,8 +567,8 @@ * * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels * independently of the userspace SME, send this event indicating - * %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ with - * %NL80211_ATTR_WIPHY_CHANNEL_TYPE. + * %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ and the + * attributes determining channel width. * * @NL80211_CMD_START_P2P_DEVICE: Start the given P2P Device, identified by * its %NL80211_ATTR_WDEV identifier. It must have been created with @@ -771,14 +772,26 @@ enum nl80211_commands { * /sys/class/ieee80211//index * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming) * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters - * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz + * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz, + * defines the channel together with the (deprecated) + * %NL80211_ATTR_WIPHY_CHANNEL_TYPE attribute or the attributes + * %NL80211_ATTR_CHANNEL_WIDTH and if needed %NL80211_ATTR_CENTER_FREQ1 + * and %NL80211_ATTR_CENTER_FREQ2 + * @NL80211_ATTR_CHANNEL_WIDTH: u32 attribute containing one of the values + * of &enum nl80211_chan_width, describing the channel width. See the + * documentation of the enum for more information. + * @NL80211_ATTR_CENTER_FREQ1: Center frequency of the first part of the + * channel, used for anything but 20 MHz bandwidth + * @NL80211_ATTR_CENTER_FREQ2: Center frequency of the second part of the + * channel, used only for 80+80 MHz bandwidth * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ - * if HT20 or HT40 are allowed (i.e., 802.11n disabled if not included): + * if HT20 or HT40 are to be used (i.e., HT disabled if not included): * NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including * this attribute) * NL80211_CHAN_HT20 = HT20 only * NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel * NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel + * This attribute is now deprecated. * @NL80211_ATTR_WIPHY_RETRY_SHORT: TX retry limit for frames whose length is * less than or equal to the RTS threshold; allowed range: 1..255; * dot11ShortRetryLimit; u8 @@ -1553,6 +1566,10 @@ enum nl80211_attrs { NL80211_ATTR_SCAN_FLAGS, + NL80211_ATTR_CHANNEL_WIDTH, + NL80211_ATTR_CENTER_FREQ1, + NL80211_ATTR_CENTER_FREQ2, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -2454,6 +2471,32 @@ enum nl80211_channel_type { NL80211_CHAN_HT40PLUS }; +/** + * enum nl80211_chan_width - channel width definitions + * + * These values are used with the %NL80211_ATTR_CHANNEL_WIDTH + * attribute. + * + * @NL80211_CHAN_WIDTH_20_NOHT: 20 MHz, non-HT channel + * @NL80211_CHAN_WIDTH_20: 20 MHz HT channel + * @NL80211_CHAN_WIDTH_40: 40 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * attribute must be provided as well + * @NL80211_CHAN_WIDTH_80: 80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * attribute must be provided as well + * @NL80211_CHAN_WIDTH_80P80: 80+80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * and %NL80211_ATTR_CENTER_FREQ2 attributes must be provided as well + * @NL80211_CHAN_WIDTH_160: 160 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * attribute must be provided as well + */ +enum nl80211_chan_width { + NL80211_CHAN_WIDTH_20_NOHT, + NL80211_CHAN_WIDTH_20, + NL80211_CHAN_WIDTH_40, + NL80211_CHAN_WIDTH_80, + NL80211_CHAN_WIDTH_80P80, + NL80211_CHAN_WIDTH_160, +}; + /** * enum nl80211_bss - netlink attributes for a BSS * diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index fbb2d072cb9..7136b945798 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3125,8 +3125,9 @@ static int ieee80211_cfg_get_channel(struct wiphy *wiphy, rcu_read_lock(); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); if (chanctx_conf) { - chandef->chan = chanctx_conf->channel; - chandef->_type = chanctx_conf->channel_type; + cfg80211_chandef_create(chandef, + chanctx_conf->channel, + chanctx_conf->channel_type); ret = 0; } rcu_read_unlock(); diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index bed616fd97e..5648bbed240 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -52,6 +52,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, u32 bss_change; u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; struct cfg80211_chan_def chandef; + enum nl80211_channel_type chan_type; lockdep_assert_held(&ifibss->mtx); @@ -79,13 +80,13 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0; - chandef.chan = chan; - chandef._type = ifibss->channel_type; + chan_type = ifibss->channel_type; + cfg80211_chandef_create(&chandef, chan, chan_type); if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) - chandef._type = NL80211_CHAN_HT20; + chan_type = NL80211_CHAN_HT20; ieee80211_vif_release_channel(sdata); - if (ieee80211_vif_use_channel(sdata, chan, chandef._type, + if (ieee80211_vif_use_channel(sdata, chan, chan_type, ifibss->fixed_channel ? IEEE80211_CHANCTX_SHARED : IEEE80211_CHANCTX_EXCLUSIVE)) { @@ -159,7 +160,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, ifibss->ie, ifibss->ie_len); /* add HT capability and information IEs */ - if (chandef._type != NL80211_CHAN_NO_HT && + if (chan_type != NL80211_CHAN_NO_HT && sband->ht_cap.ht_supported) { pos = skb_put(skb, 4 + sizeof(struct ieee80211_ht_cap) + @@ -172,7 +173,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, * keep them at 0 */ pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap, - chan, chandef._type, 0); + chan, chan_type, 0); } if (local->hw.queues >= IEEE80211_NUM_ACS) { diff --git a/net/wireless/chan.c b/net/wireless/chan.c index e834422de40..bf2dfd54ff3 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -11,43 +11,252 @@ #include "core.h" #include "rdev-ops.h" -bool cfg80211_reg_can_beacon(struct wiphy *wiphy, - struct cfg80211_chan_def *chandef) +void cfg80211_chandef_create(struct cfg80211_chan_def *chandef, + struct ieee80211_channel *chan, + enum nl80211_channel_type chan_type) { - struct ieee80211_channel *sec_chan; - int diff; + if (WARN_ON(!chan)) + return; - trace_cfg80211_reg_can_beacon(wiphy, chandef); + chandef->chan = chan; + chandef->center_freq2 = 0; - switch (chandef->_type) { + switch (chan_type) { + case NL80211_CHAN_NO_HT: + chandef->width = NL80211_CHAN_WIDTH_20_NOHT; + chandef->center_freq1 = chan->center_freq; + break; + case NL80211_CHAN_HT20: + chandef->width = NL80211_CHAN_WIDTH_20; + chandef->center_freq1 = chan->center_freq; + break; case NL80211_CHAN_HT40PLUS: - diff = 20; + chandef->width = NL80211_CHAN_WIDTH_40; + chandef->center_freq1 = chan->center_freq + 10; break; case NL80211_CHAN_HT40MINUS: - diff = -20; + chandef->width = NL80211_CHAN_WIDTH_40; + chandef->center_freq1 = chan->center_freq - 10; + break; + default: + WARN_ON(1); + } +} +EXPORT_SYMBOL(cfg80211_chandef_create); + +bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef) +{ + u32 control_freq; + + if (!chandef->chan) + return false; + + control_freq = chandef->chan->center_freq; + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20: + case NL80211_CHAN_WIDTH_20_NOHT: + if (chandef->center_freq1 != control_freq) + return false; + if (chandef->center_freq2) + return false; + break; + case NL80211_CHAN_WIDTH_40: + if (chandef->center_freq1 != control_freq + 10 && + chandef->center_freq1 != control_freq - 10) + return false; + if (chandef->center_freq2) + return false; + break; + case NL80211_CHAN_WIDTH_80P80: + if (chandef->center_freq1 != control_freq + 30 && + chandef->center_freq1 != control_freq + 10 && + chandef->center_freq1 != control_freq - 10 && + chandef->center_freq1 != control_freq - 30) + return false; + if (!chandef->center_freq2) + return false; + break; + case NL80211_CHAN_WIDTH_80: + if (chandef->center_freq1 != control_freq + 30 && + chandef->center_freq1 != control_freq + 10 && + chandef->center_freq1 != control_freq - 10 && + chandef->center_freq1 != control_freq - 30) + return false; + if (chandef->center_freq2) + return false; + break; + case NL80211_CHAN_WIDTH_160: + if (chandef->center_freq1 != control_freq + 70 && + chandef->center_freq1 != control_freq + 50 && + chandef->center_freq1 != control_freq + 30 && + chandef->center_freq1 != control_freq + 10 && + chandef->center_freq1 != control_freq - 10 && + chandef->center_freq1 != control_freq - 30 && + chandef->center_freq1 != control_freq - 50 && + chandef->center_freq1 != control_freq - 70) + return false; + if (chandef->center_freq2) + return false; + break; + default: + return false; + } + + return true; +} + +static void chandef_primary_freqs(const struct cfg80211_chan_def *c, + int *pri40, int *pri80) +{ + int tmp; + + switch (c->width) { + case NL80211_CHAN_WIDTH_40: + *pri40 = c->center_freq1; + *pri80 = 0; + break; + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + *pri80 = c->center_freq1; + /* n_P20 */ + tmp = (30 + c->chan->center_freq - c->center_freq1)/20; + /* n_P40 */ + tmp /= 2; + /* freq_P40 */ + *pri40 = c->center_freq1 - 20 + 40 * tmp; + break; + case NL80211_CHAN_WIDTH_160: + /* n_P20 */ + tmp = (70 + c->chan->center_freq - c->center_freq1)/20; + /* n_P40 */ + tmp /= 2; + /* freq_P40 */ + *pri40 = c->center_freq1 - 60 + 40 * tmp; + /* n_P80 */ + tmp /= 2; + *pri80 = c->center_freq1 - 40 + 80 * tmp; break; default: - trace_cfg80211_return_bool(true); - return true; + WARN_ON_ONCE(1); } +} + +const struct cfg80211_chan_def * +cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, + const struct cfg80211_chan_def *c2) +{ + u32 c1_pri40, c1_pri80, c2_pri40, c2_pri80; - sec_chan = ieee80211_get_channel(wiphy, - chandef->chan->center_freq + diff); - if (!sec_chan) { + /* If they are identical, return */ + if (cfg80211_chandef_identical(c1, c2)) + return c1; + + /* otherwise, must have same control channel */ + if (c1->chan != c2->chan) + return NULL; + + /* + * If they have the same width, but aren't identical, + * then they can't be compatible. + */ + if (c1->width == c2->width) + return NULL; + + if (c1->width == NL80211_CHAN_WIDTH_20_NOHT || + c1->width == NL80211_CHAN_WIDTH_20) + return c2; + + if (c2->width == NL80211_CHAN_WIDTH_20_NOHT || + c2->width == NL80211_CHAN_WIDTH_20) + return c1; + + chandef_primary_freqs(c1, &c1_pri40, &c1_pri80); + chandef_primary_freqs(c2, &c2_pri40, &c2_pri80); + + if (c1_pri40 != c2_pri40) + return NULL; + + WARN_ON(!c1_pri80 && !c2_pri80); + if (c1_pri80 && c2_pri80 && c1_pri80 != c2_pri80) + return NULL; + + if (c1->width > c2->width) + return c1; + return c2; +} +EXPORT_SYMBOL(cfg80211_chandef_compatible); + +bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, + u32 center_freq, u32 bandwidth, + u32 prohibited_flags) +{ + struct ieee80211_channel *c; + u32 freq; + + for (freq = center_freq - bandwidth/2 + 10; + freq <= center_freq + bandwidth/2 - 10; + freq += 20) { + c = ieee80211_get_channel(wiphy, freq); + if (!c || c->flags & prohibited_flags) + return false; + } + + return true; +} + +static bool cfg80211_check_beacon_chans(struct wiphy *wiphy, + u32 center_freq, u32 bw) +{ + return cfg80211_secondary_chans_ok(wiphy, center_freq, bw, + IEEE80211_CHAN_DISABLED | + IEEE80211_CHAN_PASSIVE_SCAN | + IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_RADAR); +} + +bool cfg80211_reg_can_beacon(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef) +{ + u32 width; + bool res; + + trace_cfg80211_reg_can_beacon(wiphy, chandef); + + if (WARN_ON(!cfg80211_chan_def_valid(chandef))) { trace_cfg80211_return_bool(false); return false; } - /* we'll need a DFS capability later */ - if (sec_chan->flags & (IEEE80211_CHAN_DISABLED | - IEEE80211_CHAN_PASSIVE_SCAN | - IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_RADAR)) { + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + width = 20; + break; + case NL80211_CHAN_WIDTH_40: + width = 40; + break; + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + width = 80; + break; + case NL80211_CHAN_WIDTH_160: + width = 160; + break; + default: + WARN_ON_ONCE(1); trace_cfg80211_return_bool(false); return false; } - trace_cfg80211_return_bool(true); - return true; + + res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq1, width); + + if (res && chandef->center_freq2) + res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq2, + width); + + trace_cfg80211_return_bool(res); + return res; } EXPORT_SYMBOL(cfg80211_reg_can_beacon); diff --git a/net/wireless/core.h b/net/wireless/core.h index 6183a0d25b8..a0c8decf6a4 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -483,6 +483,12 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype, int num); +bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef); + +bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, + u32 center_freq, u32 bandwidth, + u32 prohibited_flags); + #define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10 #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index ccc8865dfad..9b9551e4a6f 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -252,7 +252,7 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, /* try to find an IBSS channel if none requested ... */ if (!wdev->wext.ibss.chandef.chan) { - wdev->wext.ibss.chandef._type = NL80211_CHAN_NO_HT; + wdev->wext.ibss.chandef.width = NL80211_CHAN_WIDTH_20_NOHT; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { struct ieee80211_supported_band *sband; @@ -352,7 +352,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, if (chan) { wdev->wext.ibss.chandef.chan = chan; - wdev->wext.ibss.chandef._type = NL80211_CHAN_NO_HT; + wdev->wext.ibss.chandef.width = NL80211_CHAN_WIDTH_20_NOHT; wdev->wext.ibss.channel_fixed = true; } else { /* cfg80211_ibss_wext_join will pick one if needed */ diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 12b5a570a30..3ee5a728228 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -146,7 +146,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, if (!setup->chandef.chan) return -EINVAL; - setup->chandef._type = NL80211_CHAN_NO_HT; + setup->chandef.width = NL80211_CHAN_WIDTH_20_NOHT;; } if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef)) @@ -198,7 +198,7 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, * compatible with 802.11 mesh. */ if (rdev->ops->libertas_set_mesh_channel) { - if (chandef->_type != NL80211_CHAN_NO_HT) + if (chandef->width != NL80211_CHAN_WIDTH_20_NOHT) return -EINVAL; if (!netif_running(wdev->netdev)) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 999108cd947..15158a3d64a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -223,8 +223,13 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, .len = 20-1 }, [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED }, + [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 }, [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 }, + [NL80211_ATTR_CHANNEL_WIDTH] = { .type = NLA_U32 }, + [NL80211_ATTR_CENTER_FREQ1] = { .type = NLA_U32 }, + [NL80211_ATTR_CENTER_FREQ2] = { .type = NLA_U32 }, + [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 }, [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 }, [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 }, @@ -1360,35 +1365,13 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) wdev->iftype == NL80211_IFTYPE_P2P_GO; } -static bool nl80211_valid_channel_type(struct genl_info *info, - enum nl80211_channel_type *channel_type) -{ - enum nl80211_channel_type tmp; - - if (!info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) - return false; - - tmp = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); - if (tmp != NL80211_CHAN_NO_HT && - tmp != NL80211_CHAN_HT20 && - tmp != NL80211_CHAN_HT40PLUS && - tmp != NL80211_CHAN_HT40MINUS) - return false; - - if (channel_type) - *channel_type = tmp; - - return true; -} - static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, struct genl_info *info, struct cfg80211_chan_def *chandef) { struct ieee80211_sta_ht_cap *ht_cap; - struct ieee80211_channel *sc; - u32 control_freq; - int offs; + struct ieee80211_sta_vht_cap *vht_cap; + u32 control_freq, width; if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) return -EINVAL; @@ -1396,47 +1379,105 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, control_freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); chandef->chan = ieee80211_get_channel(&rdev->wiphy, control_freq); - chandef->_type = NL80211_CHAN_NO_HT; - - if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && - !nl80211_valid_channel_type(info, &chandef->_type)) - return -EINVAL; + chandef->width = NL80211_CHAN_WIDTH_20_NOHT; + chandef->center_freq1 = control_freq; + chandef->center_freq2 = 0; /* Primary channel not allowed */ if (!chandef->chan || chandef->chan->flags & IEEE80211_CHAN_DISABLED) return -EINVAL; + if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { + enum nl80211_channel_type chantype; + + chantype = nla_get_u32( + info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); + + switch (chantype) { + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: + case NL80211_CHAN_HT40PLUS: + case NL80211_CHAN_HT40MINUS: + cfg80211_chandef_create(chandef, chandef->chan, + chantype); + break; + default: + return -EINVAL; + } + } else if (info->attrs[NL80211_ATTR_CHANNEL_WIDTH]) { + chandef->width = + nla_get_u32(info->attrs[NL80211_ATTR_CHANNEL_WIDTH]); + if (info->attrs[NL80211_ATTR_CENTER_FREQ1]) + chandef->center_freq1 = + nla_get_u32( + info->attrs[NL80211_ATTR_CENTER_FREQ1]); + if (info->attrs[NL80211_ATTR_CENTER_FREQ2]) + chandef->center_freq2 = + nla_get_u32( + info->attrs[NL80211_ATTR_CENTER_FREQ2]); + } + ht_cap = &rdev->wiphy.bands[chandef->chan->band]->ht_cap; + vht_cap = &rdev->wiphy.bands[chandef->chan->band]->vht_cap; - switch (chandef->_type) { - case NL80211_CHAN_NO_HT: + if (!cfg80211_chan_def_valid(chandef)) + return -EINVAL; + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20: + if (!ht_cap->ht_supported) + return -EINVAL; + case NL80211_CHAN_WIDTH_20_NOHT: + width = 20; break; - case NL80211_CHAN_HT40MINUS: - if (chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS) + case NL80211_CHAN_WIDTH_40: + width = 40; + /* quick early regulatory check */ + if (chandef->center_freq1 < control_freq && + chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS) + return -EINVAL; + if (chandef->center_freq1 > control_freq && + chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS) + return -EINVAL; + if (!ht_cap->ht_supported) return -EINVAL; - offs = -20; - /* fall through */ - case NL80211_CHAN_HT40PLUS: - if (chandef->_type == NL80211_CHAN_HT40PLUS) { - if (chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS) - return -EINVAL; - offs = 20; - } if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT) return -EINVAL; - - sc = ieee80211_get_channel(&rdev->wiphy, - chandef->chan->center_freq + offs); - if (!sc || sc->flags & IEEE80211_CHAN_DISABLED) + break; + case NL80211_CHAN_WIDTH_80: + width = 80; + if (!vht_cap->vht_supported) return -EINVAL; - /* fall through */ - case NL80211_CHAN_HT20: - if (!ht_cap->ht_supported) + break; + case NL80211_CHAN_WIDTH_80P80: + width = 80; + if (!vht_cap->vht_supported) + return -EINVAL; + if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) + return -EINVAL; + break; + case NL80211_CHAN_WIDTH_160: + width = 160; + if (!vht_cap->vht_supported) + return -EINVAL; + if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ)) return -EINVAL; break; + default: + return -EINVAL; } + if (!cfg80211_secondary_chans_ok(&rdev->wiphy, chandef->center_freq1, + width, IEEE80211_CHAN_DISABLED)) + return -EINVAL; + if (chandef->center_freq2 && + !cfg80211_secondary_chans_ok(&rdev->wiphy, chandef->center_freq2, + width, IEEE80211_CHAN_DISABLED)) + return -EINVAL; + + /* TODO: missing regulatory check on bandwidth */ + return 0; } @@ -1800,10 +1841,28 @@ static inline u64 wdev_id(struct wireless_dev *wdev) static int nl80211_send_chandef(struct sk_buff *msg, struct cfg80211_chan_def *chandef) { + WARN_ON(!cfg80211_chan_def_valid(chandef)); + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chandef->chan->center_freq)) return -ENOBUFS; - if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, chandef->_type)) + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + case NL80211_CHAN_WIDTH_40: + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + cfg80211_get_chandef_type(chandef))) + return -ENOBUFS; + break; + default: + break; + } + if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, chandef->width)) + return -ENOBUFS; + if (nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, chandef->center_freq1)) + return -ENOBUFS; + if (chandef->center_freq2 && + nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, chandef->center_freq2)) return -ENOBUFS; return 0; } @@ -5447,7 +5506,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) if (IS_ERR(connkeys)) return PTR_ERR(connkeys); - if ((ibss.chandef._type != NL80211_CHAN_NO_HT) && no_ht) { + if ((ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) && + no_ht) { kfree(connkeys); return -EINVAL; } diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 1370d52b139..3c7aa122156 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -126,25 +126,33 @@ #define CHAN_PR_FMT ", band: %d, freq: %u" #define CHAN_PR_ARG __entry->band, __entry->center_freq -#define CHAN_DEF_ENTRY __field(enum ieee80211_band, band) \ - __field(u16, center_freq) \ - __field(u32, channel_type) +#define CHAN_DEF_ENTRY __field(enum ieee80211_band, band) \ + __field(u32, control_freq) \ + __field(u32, width) \ + __field(u32, center_freq1) \ + __field(u32, center_freq2) #define CHAN_DEF_ASSIGN(chandef) \ do { \ if ((chandef) && (chandef)->chan) { \ __entry->band = (chandef)->chan->band; \ - __entry->center_freq = \ + __entry->control_freq = \ (chandef)->chan->center_freq; \ - __entry->channel_type = (chandef)->_type; \ + __entry->width = (chandef)->width; \ + __entry->center_freq1 = (chandef)->center_freq1;\ + __entry->center_freq2 = (chandef)->center_freq2;\ } else { \ __entry->band = 0; \ - __entry->center_freq = 0; \ - __entry->channel_type = 0; \ + __entry->control_freq = 0; \ + __entry->width = 0; \ + __entry->center_freq1 = 0; \ + __entry->center_freq2 = 0; \ } \ } while (0) -#define CHAN_DEF_PR_FMT ", band: %d, freq: %u, chantype: %d" -#define CHAN_DEF_PR_ARG __entry->band, __entry->center_freq, \ - __entry->channel_type +#define CHAN_DEF_PR_FMT \ + ", band: %d, control freq: %u, width: %d, cf1: %u, cf2: %u" +#define CHAN_DEF_PR_ARG __entry->band, __entry->control_freq, \ + __entry->width, __entry->center_freq1, \ + __entry->center_freq2 #define SINFO_ENTRY __field(int, generation) \ __field(u32, connected_time) \ diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index da3307f3236..f9680c9cf9b 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -785,7 +785,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev, struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_chan_def chandef = { - ._type = NL80211_CHAN_NO_HT, + .width = NL80211_CHAN_WIDTH_20_NOHT, }; int freq, err; @@ -800,6 +800,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev, return freq; if (freq == 0) return -EINVAL; + chandef.center_freq1 = freq; chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq); if (!chandef.chan) return -EINVAL; @@ -813,6 +814,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev, return freq; if (freq == 0) return -EINVAL; + chandef.center_freq1 = freq; chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq); if (!chandef.chan) return -EINVAL; diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index e6e5dbf2f61..873af63187c 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -120,7 +120,8 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, */ if (chan && !wdev->wext.connect.ssid_len) { struct cfg80211_chan_def chandef = { - ._type = NL80211_CHAN_NO_HT, + .width = NL80211_CHAN_WIDTH_20_NOHT, + .center_freq1 = freq, }; chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq); -- cgit v1.2.3-70-g09d2 From d6144d85f830848ab31eb5d073d9bb4742b64e03 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 27 Nov 2012 13:51:32 +0530 Subject: ath9k: Fix buffer overflow error The commit "ath9k: stomp audio profiles on weak signal strength" failed to take care of new stomp type while programming concurrent tx priority. That leads to array index out of bounds access. drivers/net/wireless/ath/ath9k/btcoex.c:414 ath9k_hw_btcoex_set_concur_txprio() error: buffer overflow 'stomp_txprio' 4 <= 4 Reported-by: Dan Carpenter Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/mci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 706378ea3ba..5c02702f21e 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -257,8 +257,9 @@ static void ath_mci_set_concur_txprio(struct ath_softc *sc) { struct ath_btcoex *btcoex = &sc->btcoex; struct ath_mci_profile *mci = &btcoex->mci; - u8 stomp_txprio[] = { 0, 0, 0, 0 }; /* all, low, none, low_ftp */ + u8 stomp_txprio[ATH_BTCOEX_STOMP_MAX]; + memset(stomp_txprio, 0, sizeof(stomp_txprio)); if (mci->num_mgmt) { stomp_txprio[ATH_BTCOEX_STOMP_ALL] = ATH_MCI_INQUIRY_PRIO; if (!mci->num_pan && !mci->num_other_acl) -- cgit v1.2.3-70-g09d2 From c175db87411f0e79924a48563d5f595b9a352907 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Wed, 28 Nov 2012 15:08:52 +0530 Subject: ath9k: Move ethtool functions to debug.c The ethtool statistics are available only when CONFIG_ATH9K_DEBUGFS is enabled, move these functions to debug.c Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/debug.c | 119 ++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/debug.h | 8 +++ drivers/net/wireless/ath/ath9k/main.c | 127 --------------------------------- 3 files changed, 127 insertions(+), 127 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 939308c2571..cb5e45798ac 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -1509,6 +1509,125 @@ static const struct file_operations fops_btcoex = { }; #endif +/* Ethtool support for get-stats */ + +#define AMKSTR(nm) #nm "_BE", #nm "_BK", #nm "_VI", #nm "_VO" +static const char ath9k_gstrings_stats[][ETH_GSTRING_LEN] = { + "tx_pkts_nic", + "tx_bytes_nic", + "rx_pkts_nic", + "rx_bytes_nic", + AMKSTR(d_tx_pkts), + AMKSTR(d_tx_bytes), + AMKSTR(d_tx_mpdus_queued), + AMKSTR(d_tx_mpdus_completed), + AMKSTR(d_tx_mpdu_xretries), + AMKSTR(d_tx_aggregates), + AMKSTR(d_tx_ampdus_queued_hw), + AMKSTR(d_tx_ampdus_queued_sw), + AMKSTR(d_tx_ampdus_completed), + AMKSTR(d_tx_ampdu_retries), + AMKSTR(d_tx_ampdu_xretries), + AMKSTR(d_tx_fifo_underrun), + AMKSTR(d_tx_op_exceeded), + AMKSTR(d_tx_timer_expiry), + AMKSTR(d_tx_desc_cfg_err), + AMKSTR(d_tx_data_underrun), + AMKSTR(d_tx_delim_underrun), + "d_rx_decrypt_crc_err", + "d_rx_phy_err", + "d_rx_mic_err", + "d_rx_pre_delim_crc_err", + "d_rx_post_delim_crc_err", + "d_rx_decrypt_busy_err", + + "d_rx_phyerr_radar", + "d_rx_phyerr_ofdm_timing", + "d_rx_phyerr_cck_timing", + +}; +#define ATH9K_SSTATS_LEN ARRAY_SIZE(ath9k_gstrings_stats) + +void ath9k_get_et_strings(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u32 sset, u8 *data) +{ + if (sset == ETH_SS_STATS) + memcpy(data, *ath9k_gstrings_stats, + sizeof(ath9k_gstrings_stats)); +} + +int ath9k_get_et_sset_count(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int sset) +{ + if (sset == ETH_SS_STATS) + return ATH9K_SSTATS_LEN; + return 0; +} + +#define AWDATA(elem) \ + do { \ + data[i++] = sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BE)].elem; \ + data[i++] = sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BK)].elem; \ + data[i++] = sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VI)].elem; \ + data[i++] = sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VO)].elem; \ + } while (0) + +#define AWDATA_RX(elem) \ + do { \ + data[i++] = sc->debug.stats.rxstats.elem; \ + } while (0) + +void ath9k_get_et_stats(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ethtool_stats *stats, u64 *data) +{ + struct ath_softc *sc = hw->priv; + int i = 0; + + data[i++] = (sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BE)].tx_pkts_all + + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BK)].tx_pkts_all + + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VI)].tx_pkts_all + + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VO)].tx_pkts_all); + data[i++] = (sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BE)].tx_bytes_all + + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BK)].tx_bytes_all + + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VI)].tx_bytes_all + + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VO)].tx_bytes_all); + AWDATA_RX(rx_pkts_all); + AWDATA_RX(rx_bytes_all); + + AWDATA(tx_pkts_all); + AWDATA(tx_bytes_all); + AWDATA(queued); + AWDATA(completed); + AWDATA(xretries); + AWDATA(a_aggr); + AWDATA(a_queued_hw); + AWDATA(a_queued_sw); + AWDATA(a_completed); + AWDATA(a_retries); + AWDATA(a_xretries); + AWDATA(fifo_underrun); + AWDATA(xtxop); + AWDATA(timer_exp); + AWDATA(desc_cfg_err); + AWDATA(data_underrun); + AWDATA(delim_underrun); + + AWDATA_RX(decrypt_crc_err); + AWDATA_RX(phy_err); + AWDATA_RX(mic_err); + AWDATA_RX(pre_delim_crc_err); + AWDATA_RX(post_delim_crc_err); + AWDATA_RX(decrypt_busy_err); + + AWDATA_RX(phy_err_stats[ATH9K_PHYERR_RADAR]); + AWDATA_RX(phy_err_stats[ATH9K_PHYERR_OFDM_TIMING]); + AWDATA_RX(phy_err_stats[ATH9K_PHYERR_CCK_TIMING]); + + WARN_ON(i != ATH9K_SSTATS_LEN); +} + int ath9k_init_debug(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index f9bee18de5a..bfc21b37cb1 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -307,6 +307,14 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf, struct ath_tx_status *ts, struct ath_txq *txq, unsigned int flags); void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs); +int ath9k_get_et_sset_count(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int sset); +void ath9k_get_et_stats(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ethtool_stats *stats, u64 *data); +void ath9k_get_et_strings(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u32 sset, u8 *data); #else diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 0653dbc99e3..ffae384276a 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1882,133 +1882,6 @@ static int ath9k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) return 0; } -#ifdef CONFIG_ATH9K_DEBUGFS - -/* Ethtool support for get-stats */ - -#define AMKSTR(nm) #nm "_BE", #nm "_BK", #nm "_VI", #nm "_VO" -static const char ath9k_gstrings_stats[][ETH_GSTRING_LEN] = { - "tx_pkts_nic", - "tx_bytes_nic", - "rx_pkts_nic", - "rx_bytes_nic", - AMKSTR(d_tx_pkts), - AMKSTR(d_tx_bytes), - AMKSTR(d_tx_mpdus_queued), - AMKSTR(d_tx_mpdus_completed), - AMKSTR(d_tx_mpdu_xretries), - AMKSTR(d_tx_aggregates), - AMKSTR(d_tx_ampdus_queued_hw), - AMKSTR(d_tx_ampdus_queued_sw), - AMKSTR(d_tx_ampdus_completed), - AMKSTR(d_tx_ampdu_retries), - AMKSTR(d_tx_ampdu_xretries), - AMKSTR(d_tx_fifo_underrun), - AMKSTR(d_tx_op_exceeded), - AMKSTR(d_tx_timer_expiry), - AMKSTR(d_tx_desc_cfg_err), - AMKSTR(d_tx_data_underrun), - AMKSTR(d_tx_delim_underrun), - - "d_rx_decrypt_crc_err", - "d_rx_phy_err", - "d_rx_mic_err", - "d_rx_pre_delim_crc_err", - "d_rx_post_delim_crc_err", - "d_rx_decrypt_busy_err", - - "d_rx_phyerr_radar", - "d_rx_phyerr_ofdm_timing", - "d_rx_phyerr_cck_timing", - -}; -#define ATH9K_SSTATS_LEN ARRAY_SIZE(ath9k_gstrings_stats) - -static void ath9k_get_et_strings(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - u32 sset, u8 *data) -{ - if (sset == ETH_SS_STATS) - memcpy(data, *ath9k_gstrings_stats, - sizeof(ath9k_gstrings_stats)); -} - -static int ath9k_get_et_sset_count(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, int sset) -{ - if (sset == ETH_SS_STATS) - return ATH9K_SSTATS_LEN; - return 0; -} - -#define AWDATA(elem) \ - do { \ - data[i++] = sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BE)].elem; \ - data[i++] = sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BK)].elem; \ - data[i++] = sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VI)].elem; \ - data[i++] = sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VO)].elem; \ - } while (0) - -#define AWDATA_RX(elem) \ - do { \ - data[i++] = sc->debug.stats.rxstats.elem; \ - } while (0) - -static void ath9k_get_et_stats(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ethtool_stats *stats, u64 *data) -{ - struct ath_softc *sc = hw->priv; - int i = 0; - - data[i++] = (sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BE)].tx_pkts_all + - sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BK)].tx_pkts_all + - sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VI)].tx_pkts_all + - sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VO)].tx_pkts_all); - data[i++] = (sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BE)].tx_bytes_all + - sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BK)].tx_bytes_all + - sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VI)].tx_bytes_all + - sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VO)].tx_bytes_all); - AWDATA_RX(rx_pkts_all); - AWDATA_RX(rx_bytes_all); - - AWDATA(tx_pkts_all); - AWDATA(tx_bytes_all); - AWDATA(queued); - AWDATA(completed); - AWDATA(xretries); - AWDATA(a_aggr); - AWDATA(a_queued_hw); - AWDATA(a_queued_sw); - AWDATA(a_completed); - AWDATA(a_retries); - AWDATA(a_xretries); - AWDATA(fifo_underrun); - AWDATA(xtxop); - AWDATA(timer_exp); - AWDATA(desc_cfg_err); - AWDATA(data_underrun); - AWDATA(delim_underrun); - - AWDATA_RX(decrypt_crc_err); - AWDATA_RX(phy_err); - AWDATA_RX(mic_err); - AWDATA_RX(pre_delim_crc_err); - AWDATA_RX(post_delim_crc_err); - AWDATA_RX(decrypt_busy_err); - - AWDATA_RX(phy_err_stats[ATH9K_PHYERR_RADAR]); - AWDATA_RX(phy_err_stats[ATH9K_PHYERR_OFDM_TIMING]); - AWDATA_RX(phy_err_stats[ATH9K_PHYERR_CCK_TIMING]); - - WARN_ON(i != ATH9K_SSTATS_LEN); -} - -/* End of ethtool get-stats functions */ - -#endif - - #ifdef CONFIG_PM_SLEEP static void ath9k_wow_map_triggers(struct ath_softc *sc, -- cgit v1.2.3-70-g09d2 From de7b7604eae91f00cd587f8541eec11a7fc505aa Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Wed, 28 Nov 2012 15:08:53 +0530 Subject: ath9k: Replace WME_NUM_TID with IEEE80211_NUM_TIDS Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 2 +- drivers/net/wireless/ath/ath9k/common.h | 1 - drivers/net/wireless/ath/ath9k/xmit.c | 8 ++++---- 3 files changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 80bab1b8447..60bf784e557 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -261,7 +261,7 @@ struct ath_atx_tid { struct ath_node { struct ieee80211_sta *sta; /* station struct we're part of */ struct ieee80211_vif *vif; /* interface with which we're associated */ - struct ath_atx_tid tid[WME_NUM_TID]; + struct ath_atx_tid tid[IEEE80211_NUM_TIDS]; struct ath_atx_ac ac[IEEE80211_NUM_ACS]; int ps_key; diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h index 76b54390031..5f845beeb18 100644 --- a/drivers/net/wireless/ath/ath9k/common.h +++ b/drivers/net/wireless/ath/ath9k/common.h @@ -23,7 +23,6 @@ /* Common header for Atheros 802.11n base driver cores */ -#define WME_NUM_TID 16 #define WME_BA_BMP_SIZE 64 #define WME_MAX_BA WME_BA_BMP_SIZE #define ATH_TID_MAX_BUFS (2 * WME_MAX_BA) diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 34130943f9d..a0a3063dae7 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1263,7 +1263,7 @@ void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, int tidno; for (tidno = 0, tid = &an->tid[tidno]; - tidno < WME_NUM_TID; tidno++, tid++) { + tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { if (!tid->sched) continue; @@ -1297,7 +1297,7 @@ void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an) int tidno; for (tidno = 0, tid = &an->tid[tidno]; - tidno < WME_NUM_TID; tidno++, tid++) { + tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { ac = tid->ac; txq = ac->txq; @@ -2448,7 +2448,7 @@ void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an) int tidno, acno; for (tidno = 0, tid = &an->tid[tidno]; - tidno < WME_NUM_TID; + tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { tid->an = an; tid->tidno = tidno; @@ -2481,7 +2481,7 @@ void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an) int tidno; for (tidno = 0, tid = &an->tid[tidno]; - tidno < WME_NUM_TID; tidno++, tid++) { + tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { ac = tid->ac; txq = ac->txq; -- cgit v1.2.3-70-g09d2 From a145daf7f04696d0cec98d5328760fe65fe418c5 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Wed, 28 Nov 2012 15:08:54 +0530 Subject: ath9k: Implement sta_add_debugfs/sta_remove_debugfs Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 5 ++ drivers/net/wireless/ath/ath9k/debug.c | 90 ++++++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/debug.h | 9 +++- drivers/net/wireless/ath/ath9k/main.c | 10 +++- 4 files changed, 111 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 60bf784e557..86e26a19efd 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -259,6 +259,7 @@ struct ath_atx_tid { }; struct ath_node { + struct ath_softc *sc; struct ieee80211_sta *sta; /* station struct we're part of */ struct ieee80211_vif *vif; /* interface with which we're associated */ struct ath_atx_tid tid[IEEE80211_NUM_TIDS]; @@ -269,6 +270,10 @@ struct ath_node { u8 mpdudensity; bool sleeping; + +#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS) + struct dentry *node_stat; +#endif }; #define AGGR_CLEANUP BIT(1) diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index cb5e45798ac..13ff9edc240 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -1509,6 +1509,96 @@ static const struct file_operations fops_btcoex = { }; #endif +static ssize_t read_file_node_stat(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_node *an = file->private_data; + struct ath_softc *sc = an->sc; + struct ath_atx_tid *tid; + struct ath_atx_ac *ac; + struct ath_txq *txq; + u32 len = 0, size = 4096; + char *buf; + size_t retval; + int tidno, acno; + + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + if (!an->sta->ht_cap.ht_supported) { + len = snprintf(buf, size, "%s\n", + "HT not supported"); + goto exit; + } + + len = snprintf(buf, size, "Max-AMPDU: %d\n", + an->maxampdu); + len += snprintf(buf + len, size - len, "MPDU Density: %d\n\n", + an->mpdudensity); + + len += snprintf(buf + len, size - len, + "%2s%7s\n", "AC", "SCHED"); + + for (acno = 0, ac = &an->ac[acno]; + acno < IEEE80211_NUM_ACS; acno++, ac++) { + txq = ac->txq; + ath_txq_lock(sc, txq); + len += snprintf(buf + len, size - len, + "%2d%7d\n", + acno, ac->sched); + ath_txq_unlock(sc, txq); + } + + len += snprintf(buf + len, size - len, + "\n%3s%11s%10s%10s%10s%10s%9s%6s%8s\n", + "TID", "SEQ_START", "SEQ_NEXT", "BAW_SIZE", + "BAW_HEAD", "BAW_TAIL", "BAR_IDX", "SCHED", "PAUSED"); + + for (tidno = 0, tid = &an->tid[tidno]; + tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { + txq = tid->ac->txq; + ath_txq_lock(sc, txq); + len += snprintf(buf + len, size - len, + "%3d%11d%10d%10d%10d%10d%9d%6d%8d\n", + tid->tidno, tid->seq_start, tid->seq_next, + tid->baw_size, tid->baw_head, tid->baw_tail, + tid->bar_index, tid->sched, tid->paused); + ath_txq_unlock(sc, txq); + } +exit: + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; +} + +static const struct file_operations fops_node_stat = { + .read = read_file_node_stat, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath9k_sta_add_debugfs(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct dentry *dir) +{ + struct ath_node *an = (struct ath_node *)sta->drv_priv; + an->node_stat = debugfs_create_file("node_stat", S_IRUGO, + dir, an, &fops_node_stat); +} + +void ath9k_sta_remove_debugfs(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct dentry *dir) +{ + struct ath_node *an = (struct ath_node *)sta->drv_priv; + debugfs_remove(an->node_stat); +} + /* Ethtool support for get-stats */ #define AMKSTR(nm) #nm "_BE", #nm "_BK", #nm "_VI", #nm "_VO" diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index bfc21b37cb1..72d4893311b 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -315,7 +315,14 @@ void ath9k_get_et_stats(struct ieee80211_hw *hw, void ath9k_get_et_strings(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 sset, u8 *data); - +void ath9k_sta_add_debugfs(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct dentry *dir); +void ath9k_sta_remove_debugfs(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct dentry *dir); #else #define RX_STAT_INC(c) /* NOP */ diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index ffae384276a..be30a9af152 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -331,6 +331,7 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta, u8 density; an = (struct ath_node *)sta->drv_priv; + an->sc = sc; an->sta = sta; an->vif = vif; @@ -2275,7 +2276,12 @@ struct ieee80211_ops ath9k_ops = { #ifdef CONFIG_ATH9K_DEBUGFS .get_et_sset_count = ath9k_get_et_sset_count, - .get_et_stats = ath9k_get_et_stats, - .get_et_strings = ath9k_get_et_strings, + .get_et_stats = ath9k_get_et_stats, + .get_et_strings = ath9k_get_et_strings, +#endif + +#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS) + .sta_add_debugfs = ath9k_sta_add_debugfs, + .sta_remove_debugfs = ath9k_sta_remove_debugfs, #endif }; -- cgit v1.2.3-70-g09d2 From a56c919f05eb73f6d791cc14992f4b451059b68c Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Wed, 28 Nov 2012 15:08:55 +0530 Subject: ath9k: Remove redundant NULL assignment 'bf_next' is cleared using ATH_TXBUF_RESET() in both the callsites of ath_tx_get_buffer(). Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/xmit.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index a0a3063dae7..90e48a0fafe 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -312,7 +312,6 @@ static struct ath_buf *ath_tx_get_buffer(struct ath_softc *sc) } bf = list_first_entry(&sc->tx.txbuf, struct ath_buf, list); - bf->bf_next = NULL; list_del(&bf->list); spin_unlock_bh(&sc->tx.txbuflock); -- cgit v1.2.3-70-g09d2 From 23c1d7f65975762386760e36c00f149059e537e2 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Wed, 28 Nov 2012 18:57:19 +0200 Subject: wireless: allow Atheros card to not depend on ath.ko There are 2 different things: - sub-menu for "Atheros Wireless cards" family - module ath.ko with common Atheros code Until now, they both used to depend on the same Kconfig variable ATH_COMMON. Thus, being "Atheros card" and "depending on ath.ko" was the same. To allow module to belong to the "Atheros Wireless cards" family but not use ath.ko, 2 conditions above need to be separated. So, this patch introduce new Kconfig variable ATH_CARDS for belonging to the "Atheros Wireless Cards" family; while ATH_COMMON becomes hidden variable to express dependency on common Atheros code in ath.ko. Modules that depend on this common code now express it by setting ATH_COMMON. Right now, ath6kl do not depend on common code and thus do not set ATH_COMMON. Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/Kconfig | 7 +++++-- drivers/net/wireless/ath/ar5523/Kconfig | 1 + drivers/net/wireless/ath/ath5k/Kconfig | 1 + drivers/net/wireless/ath/ath9k/Kconfig | 1 + drivers/net/wireless/ath/carl9170/Kconfig | 1 + 5 files changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index c25dcf192fe..1a67a4f829f 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -1,4 +1,7 @@ -menuconfig ATH_COMMON +config ATH_COMMON + tristate + +menuconfig ATH_CARDS tristate "Atheros Wireless Cards" depends on CFG80211 && (!UML || BROKEN) ---help--- @@ -14,7 +17,7 @@ menuconfig ATH_COMMON http://wireless.kernel.org/en/users/Drivers/Atheros -if ATH_COMMON +if ATH_CARDS config ATH_DEBUG bool "Atheros wireless debugging" diff --git a/drivers/net/wireless/ath/ar5523/Kconfig b/drivers/net/wireless/ath/ar5523/Kconfig index 11d99ee8de5..0d320cc7769 100644 --- a/drivers/net/wireless/ath/ar5523/Kconfig +++ b/drivers/net/wireless/ath/ar5523/Kconfig @@ -1,6 +1,7 @@ config AR5523 tristate "Atheros AR5523 wireless driver support" depends on MAC80211 && USB + select ATH_COMMON select FW_LOADER ---help--- This module add support for AR5523 based USB dongles such as D-Link diff --git a/drivers/net/wireless/ath/ath5k/Kconfig b/drivers/net/wireless/ath/ath5k/Kconfig index 338c5c42357..c9f81a388f1 100644 --- a/drivers/net/wireless/ath/ath5k/Kconfig +++ b/drivers/net/wireless/ath/ath5k/Kconfig @@ -1,6 +1,7 @@ config ATH5K tristate "Atheros 5xxx wireless cards support" depends on (PCI || ATHEROS_AR231X) && MAC80211 + select ATH_COMMON select MAC80211_LEDS select LEDS_CLASS select NEW_LEDS diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index c7aa6646123..5fc15bf8be0 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -17,6 +17,7 @@ config ATH9K_BTCOEX_SUPPORT config ATH9K tristate "Atheros 802.11n wireless cards support" depends on MAC80211 + select ATH_COMMON select ATH9K_HW select MAC80211_LEDS select LEDS_CLASS diff --git a/drivers/net/wireless/ath/carl9170/Kconfig b/drivers/net/wireless/ath/carl9170/Kconfig index 267d5dcf82d..13a20459876 100644 --- a/drivers/net/wireless/ath/carl9170/Kconfig +++ b/drivers/net/wireless/ath/carl9170/Kconfig @@ -1,6 +1,7 @@ config CARL9170 tristate "Linux Community AR9170 802.11n USB support" depends on USB && MAC80211 && EXPERIMENTAL + select ATH_COMMON select FW_LOADER select CRC32 help -- cgit v1.2.3-70-g09d2 From 88427588993db079a1b2cdbcba69f3c162f8ee21 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Thu, 29 Nov 2012 23:27:15 +0100 Subject: ath9k: use SIMPLE_DEV_PM_OPS ath9k does not provide any runtime pm callbacks, so support for PM_RUNTIME is not needed and we could go to PM_SLEEP. This also makes it possible to use SIMPLE_DEV_PM_OPS instead of manually filling struct dev_pm_ops. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/pci.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 9553203ee62..8e9b826f878 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -287,7 +287,7 @@ static void ath_pci_remove(struct pci_dev *pdev) pci_release_region(pdev, 0); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int ath_pci_suspend(struct device *device) { @@ -333,22 +333,15 @@ static int ath_pci_resume(struct device *device) return 0; } -static const struct dev_pm_ops ath9k_pm_ops = { - .suspend = ath_pci_suspend, - .resume = ath_pci_resume, - .freeze = ath_pci_suspend, - .thaw = ath_pci_resume, - .poweroff = ath_pci_suspend, - .restore = ath_pci_resume, -}; +static SIMPLE_DEV_PM_OPS(ath9k_pm_ops, ath_pci_suspend, ath_pci_resume); #define ATH9K_PM_OPS (&ath9k_pm_ops) -#else /* !CONFIG_PM */ +#else /* !CONFIG_PM_SLEEP */ #define ATH9K_PM_OPS NULL -#endif /* !CONFIG_PM */ +#endif /* !CONFIG_PM_SLEEP */ MODULE_DEVICE_TABLE(pci, ath_pci_id_table); -- cgit v1.2.3-70-g09d2 From 649a6ac4a7d6eab2752893d69f722ccd9674dd4e Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sun, 2 Dec 2012 16:52:00 +0100 Subject: carl9170: fix signal strength reporting issues On A-MPDU frames, the hardware only reports valid signal strength data for the last subframe. This patch fixes it by flagging everything but the last subframe in an A-MPDU to tell mac80211 to ignore the signal strength entirely. Otherwise the empty value (= 0 dbm) will distort the average quite badly. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/rx.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index 6d22382875b..876a773fd0b 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -814,6 +814,8 @@ static void carl9170_rx_untie_data(struct ar9170 *ar, u8 *buf, int len) if (phy) carl9170_rx_phy_status(ar, phy, &status); + else + status.flag |= RX_FLAG_NO_SIGNAL_VAL; if (carl9170_handle_mpdu(ar, buf, mpdu_len, &status)) goto drop; -- cgit v1.2.3-70-g09d2 From 1981e881fd32160bf886a09b12794c640aafffb3 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Mon, 3 Dec 2012 18:48:05 +0100 Subject: carl9170: explain why sta cannot be NULL for ampdus Dan Carpenter reported that smatch detected a potential problem with the code [1]: drivers/net/wireless/ath/carl9170/tx.c:1488 carl9170_op_tx() error: we previously assumed 'sta' could be null (see line 1482) drivers/net/wireless/ath/carl9170/tx.c 1482 if (sta) { ^^^^^ New check. [...] 1485 } 1487 if (info->flags & IEEE80211_TX_CTL_AMPDU) { 1488 run = carl9170_tx_ampdu_queue(ar, sta, skb); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Old dereference of "sta" inside the call to carl9170_tx_ampdu_queue(). A range of solutions have been discussed in [2] and we agreed on the following: " > we might as well add a comment to carl9170_tx_ampdu_queue > and explain the situation [in a way that's obvious to a > human reader]. This way we can save the "if"... which is > a small win since carl9170_op_tx is sort of a hot-path. Putting a comment there is fine. Without the comment it's easy for a human reader to get confused why the check is there. So long as humans can read the code, that's all that matters." [1] [2] Reported-by: Dan Carpenter Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/tx.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c index 84377cf580e..ef4ec0da6e4 100644 --- a/drivers/net/wireless/ath/carl9170/tx.c +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -1485,6 +1485,13 @@ void carl9170_op_tx(struct ieee80211_hw *hw, } if (info->flags & IEEE80211_TX_CTL_AMPDU) { + /* to static code analyzers and reviewers: + * mac80211 guarantees that a valid "sta" + * reference is present, if a frame is to + * be part of an ampdu. Hence any extra + * sta == NULL checks are redundant in this + * special case. + */ run = carl9170_tx_ampdu_queue(ar, sta, skb); if (run) carl9170_tx_ampdu(ar); -- cgit v1.2.3-70-g09d2 From e829cf961bfd3c2c5ef0a2c5a51734c1628ce77e Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Mon, 3 Dec 2012 09:56:28 -0500 Subject: ath5k: remove __dev* attributes CONFIG_HOTPLUG is going away as an option. As result the __dev* markings will be going away. Remove use of __devinit, __devexit_p, __devinitdata, __devinitconst, and __devexit. Signed-off-by: Bill Pemberton Cc: Jiri Slaby Cc: Nick Kossifidis Cc: Luis R. Rodriguez Cc: linux-wireless@vger.kernel.org Cc: ath5k-devel@lists.ath5k.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 4 ++-- drivers/net/wireless/ath/ath5k/led.c | 2 +- drivers/net/wireless/ath/ath5k/pci.c | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 2fd5bab2e22..3a5a61e0392 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -2435,7 +2435,7 @@ static const struct ieee80211_iface_combination if_comb = { .num_different_channels = 1, }; -int __devinit +int ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops) { struct ieee80211_hw *hw = ah->hw; @@ -2861,7 +2861,7 @@ static void ath5k_reset_work(struct work_struct *work) mutex_unlock(&ah->lock); } -static int __devinit +static int ath5k_init(struct ieee80211_hw *hw) { diff --git a/drivers/net/wireless/ath/ath5k/led.c b/drivers/net/wireless/ath/ath5k/led.c index b9f708a45f4..f77ef36acf8 100644 --- a/drivers/net/wireless/ath/ath5k/led.c +++ b/drivers/net/wireless/ath/ath5k/led.c @@ -158,7 +158,7 @@ void ath5k_unregister_leds(struct ath5k_hw *ah) ath5k_unregister_led(&ah->tx_led); } -int __devinit ath5k_init_leds(struct ath5k_hw *ah) +int ath5k_init_leds(struct ath5k_hw *ah) { int ret = 0; struct ieee80211_hw *hw = ah->hw; diff --git a/drivers/net/wireless/ath/ath5k/pci.c b/drivers/net/wireless/ath/ath5k/pci.c index dff48fbc63b..859db7c34f8 100644 --- a/drivers/net/wireless/ath/ath5k/pci.c +++ b/drivers/net/wireless/ath/ath5k/pci.c @@ -155,7 +155,7 @@ static const struct ath_bus_ops ath_pci_bus_ops = { * PCI Initialization * \********************/ -static int __devinit +static int ath5k_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -285,7 +285,7 @@ err: return ret; } -static void __devexit +static void ath5k_pci_remove(struct pci_dev *pdev) { struct ieee80211_hw *hw = pci_get_drvdata(pdev); @@ -336,7 +336,7 @@ static struct pci_driver ath5k_pci_driver = { .name = KBUILD_MODNAME, .id_table = ath5k_pci_id_table, .probe = ath5k_pci_probe, - .remove = __devexit_p(ath5k_pci_remove), + .remove = ath5k_pci_remove, .driver.pm = ATH5K_PM_OPS, }; -- cgit v1.2.3-70-g09d2 From a875621ec15318f0ba35907726ee2cb9e9e0c6b7 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 5 Dec 2012 13:51:19 -0700 Subject: ath9k: Use standard #defines for PCIe Capability ASPM fields Use the standard #defines for PCIe Capability ASPM fields. Previously we used PCIE_LINK_STATE_L0S and PCIE_LINK_STATE_L1 directly, but these are defined for the Linux ASPM interfaces, e.g., pci_disable_link_state(), and only coincidentally match the actual register bits. PCIE_LINK_STATE_CLKPM, also part of that interface, does not match the register bit. Signed-off-by: Bjorn Helgaas --- drivers/net/wireless/ath/ath9k/pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index f088f4bf9a2..71d82078fc7 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -125,23 +125,23 @@ static void ath_pci_aspm_init(struct ath_common *common) if ((ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) && (AR_SREV_9285(ah))) { - /* Bluetooth coexistance requires disabling ASPM. */ + /* Bluetooth coexistence requires disabling ASPM. */ pcie_capability_clear_word(pdev, PCI_EXP_LNKCTL, - PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); + PCI_EXP_LNKCTL_ASPM_L0S | PCI_EXP_LNKCTL_ASPM_L1); /* * Both upstream and downstream PCIe components should * have the same ASPM settings. */ pcie_capability_clear_word(parent, PCI_EXP_LNKCTL, - PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); + PCI_EXP_LNKCTL_ASPM_L0S | PCI_EXP_LNKCTL_ASPM_L1); ath_info(common, "Disabling ASPM since BTCOEX is enabled\n"); return; } pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &aspm); - if (aspm & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1)) { + if (aspm & (PCI_EXP_LNKCTL_ASPM_L0S | PCI_EXP_LNKCTL_ASPM_L1)) { ah->aspm_enabled = true; /* Initialize PCIe PM and SERDES registers. */ ath9k_hw_configpcipowersave(ah, false); -- cgit v1.2.3-70-g09d2 From 9c170e068636deb3e3f96114034bb711675f0faa Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 6 Dec 2012 18:40:11 +0100 Subject: Revert "ath9k_hw: Update AR9003 high_power tx gain table" This reverts commit f74b9d365ddd33a375802b064f96a5d0e99af7c0. Turns out reverting commit a240dc7b3c7463bd60cf0a9b2a90f52f78aae0fd "ath9k_hw: Updated AR9003 tx gain table for 5GHz" was not enough to bring the tx power back to normal levels on devices like the Buffalo WZR-HP-G450H, this one needs to be reverted as well. This revert improves tx power by ~10 db on that device Signed-off-by: Felix Fietkau Cc: stable@vger.kernel.org Cc: rmanohar@qca.qualcomm.com Signed-off-by: John W. Linville --- .../net/wireless/ath/ath9k/ar9003_2p2_initvals.h | 172 ++++++++++----------- 1 file changed, 86 insertions(+), 86 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h index 6f7cf49eff4..262e1e036fd 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h @@ -534,98 +534,98 @@ static const u32 ar9300_2p2_baseband_core[][2] = { static const u32 ar9300Modes_high_power_tx_gain_table_2p2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x0000a2dc, 0x00033800, 0x00033800, 0x03aaa352, 0x03aaa352}, - {0x0000a2e0, 0x0003c000, 0x0003c000, 0x03ccc584, 0x03ccc584}, - {0x0000a2e4, 0x03fc0000, 0x03fc0000, 0x03f0f800, 0x03f0f800}, + {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, + {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, + {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, - {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, - {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, - {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, - {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, - {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, - {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400}, - {0x0000a518, 0x21002220, 0x21002220, 0x16000402, 0x16000402}, - {0x0000a51c, 0x27002223, 0x27002223, 0x19000404, 0x19000404}, - {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603}, - {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02}, - {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04}, - {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20}, - {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20}, - {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22}, - {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24}, - {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640}, - {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660}, - {0x0000a544, 0x52022470, 0x52022470, 0x3f001861, 0x3f001861}, - {0x0000a548, 0x55022490, 0x55022490, 0x43001a81, 0x43001a81}, - {0x0000a54c, 0x59022492, 0x59022492, 0x47001a83, 0x47001a83}, - {0x0000a550, 0x5d022692, 0x5d022692, 0x4a001c84, 0x4a001c84}, - {0x0000a554, 0x61022892, 0x61022892, 0x4e001ce3, 0x4e001ce3}, - {0x0000a558, 0x65024890, 0x65024890, 0x52001ce5, 0x52001ce5}, - {0x0000a55c, 0x69024892, 0x69024892, 0x56001ce9, 0x56001ce9}, - {0x0000a560, 0x6e024c92, 0x6e024c92, 0x5a001ceb, 0x5a001ceb}, - {0x0000a564, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec}, - {0x0000a568, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec}, - {0x0000a56c, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec}, - {0x0000a570, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec}, - {0x0000a574, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec}, - {0x0000a578, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec}, - {0x0000a57c, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec}, - {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, - {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002}, - {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004}, - {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200}, - {0x0000a590, 0x16800220, 0x16800220, 0x0f800202, 0x0f800202}, - {0x0000a594, 0x1c800223, 0x1c800223, 0x12800400, 0x12800400}, - {0x0000a598, 0x21802220, 0x21802220, 0x16800402, 0x16800402}, - {0x0000a59c, 0x27802223, 0x27802223, 0x19800404, 0x19800404}, - {0x0000a5a0, 0x2b822220, 0x2b822220, 0x1c800603, 0x1c800603}, - {0x0000a5a4, 0x2f822222, 0x2f822222, 0x21800a02, 0x21800a02}, - {0x0000a5a8, 0x34822225, 0x34822225, 0x25800a04, 0x25800a04}, - {0x0000a5ac, 0x3a82222a, 0x3a82222a, 0x28800a20, 0x28800a20}, - {0x0000a5b0, 0x3e82222c, 0x3e82222c, 0x2c800e20, 0x2c800e20}, - {0x0000a5b4, 0x4282242a, 0x4282242a, 0x30800e22, 0x30800e22}, - {0x0000a5b8, 0x4782244a, 0x4782244a, 0x34800e24, 0x34800e24}, - {0x0000a5bc, 0x4b82244c, 0x4b82244c, 0x38801640, 0x38801640}, - {0x0000a5c0, 0x4e82246c, 0x4e82246c, 0x3c801660, 0x3c801660}, - {0x0000a5c4, 0x52822470, 0x52822470, 0x3f801861, 0x3f801861}, - {0x0000a5c8, 0x55822490, 0x55822490, 0x43801a81, 0x43801a81}, - {0x0000a5cc, 0x59822492, 0x59822492, 0x47801a83, 0x47801a83}, - {0x0000a5d0, 0x5d822692, 0x5d822692, 0x4a801c84, 0x4a801c84}, - {0x0000a5d4, 0x61822892, 0x61822892, 0x4e801ce3, 0x4e801ce3}, - {0x0000a5d8, 0x65824890, 0x65824890, 0x52801ce5, 0x52801ce5}, - {0x0000a5dc, 0x69824892, 0x69824892, 0x56801ce9, 0x56801ce9}, - {0x0000a5e0, 0x6e824c92, 0x6e824c92, 0x5a801ceb, 0x5a801ceb}, - {0x0000a5e4, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec}, - {0x0000a5e8, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec}, - {0x0000a5ec, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec}, - {0x0000a5f0, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec}, - {0x0000a5f4, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec}, - {0x0000a5f8, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec}, - {0x0000a5fc, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec}, + {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9}, + {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, + {0x0000a504, 0x04002222, 0x04002222, 0x04000002, 0x04000002}, + {0x0000a508, 0x09002421, 0x09002421, 0x08000004, 0x08000004}, + {0x0000a50c, 0x0d002621, 0x0d002621, 0x0b000200, 0x0b000200}, + {0x0000a510, 0x13004620, 0x13004620, 0x0f000202, 0x0f000202}, + {0x0000a514, 0x19004a20, 0x19004a20, 0x11000400, 0x11000400}, + {0x0000a518, 0x1d004e20, 0x1d004e20, 0x15000402, 0x15000402}, + {0x0000a51c, 0x21005420, 0x21005420, 0x19000404, 0x19000404}, + {0x0000a520, 0x26005e20, 0x26005e20, 0x1b000603, 0x1b000603}, + {0x0000a524, 0x2b005e40, 0x2b005e40, 0x1f000a02, 0x1f000a02}, + {0x0000a528, 0x2f005e42, 0x2f005e42, 0x23000a04, 0x23000a04}, + {0x0000a52c, 0x33005e44, 0x33005e44, 0x26000a20, 0x26000a20}, + {0x0000a530, 0x38005e65, 0x38005e65, 0x2a000e20, 0x2a000e20}, + {0x0000a534, 0x3c005e69, 0x3c005e69, 0x2e000e22, 0x2e000e22}, + {0x0000a538, 0x40005e6b, 0x40005e6b, 0x31000e24, 0x31000e24}, + {0x0000a53c, 0x44005e6d, 0x44005e6d, 0x34001640, 0x34001640}, + {0x0000a540, 0x49005e72, 0x49005e72, 0x38001660, 0x38001660}, + {0x0000a544, 0x4e005eb2, 0x4e005eb2, 0x3b001861, 0x3b001861}, + {0x0000a548, 0x53005f12, 0x53005f12, 0x3e001a81, 0x3e001a81}, + {0x0000a54c, 0x59025eb2, 0x59025eb2, 0x42001a83, 0x42001a83}, + {0x0000a550, 0x5e025f12, 0x5e025f12, 0x44001c84, 0x44001c84}, + {0x0000a554, 0x61027f12, 0x61027f12, 0x48001ce3, 0x48001ce3}, + {0x0000a558, 0x6702bf12, 0x6702bf12, 0x4c001ce5, 0x4c001ce5}, + {0x0000a55c, 0x6b02bf14, 0x6b02bf14, 0x50001ce9, 0x50001ce9}, + {0x0000a560, 0x6f02bf16, 0x6f02bf16, 0x54001ceb, 0x54001ceb}, + {0x0000a564, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, + {0x0000a568, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, + {0x0000a56c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, + {0x0000a570, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, + {0x0000a574, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, + {0x0000a578, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, + {0x0000a57c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, + {0x0000a580, 0x00802220, 0x00802220, 0x00800000, 0x00800000}, + {0x0000a584, 0x04802222, 0x04802222, 0x04800002, 0x04800002}, + {0x0000a588, 0x09802421, 0x09802421, 0x08800004, 0x08800004}, + {0x0000a58c, 0x0d802621, 0x0d802621, 0x0b800200, 0x0b800200}, + {0x0000a590, 0x13804620, 0x13804620, 0x0f800202, 0x0f800202}, + {0x0000a594, 0x19804a20, 0x19804a20, 0x11800400, 0x11800400}, + {0x0000a598, 0x1d804e20, 0x1d804e20, 0x15800402, 0x15800402}, + {0x0000a59c, 0x21805420, 0x21805420, 0x19800404, 0x19800404}, + {0x0000a5a0, 0x26805e20, 0x26805e20, 0x1b800603, 0x1b800603}, + {0x0000a5a4, 0x2b805e40, 0x2b805e40, 0x1f800a02, 0x1f800a02}, + {0x0000a5a8, 0x2f805e42, 0x2f805e42, 0x23800a04, 0x23800a04}, + {0x0000a5ac, 0x33805e44, 0x33805e44, 0x26800a20, 0x26800a20}, + {0x0000a5b0, 0x38805e65, 0x38805e65, 0x2a800e20, 0x2a800e20}, + {0x0000a5b4, 0x3c805e69, 0x3c805e69, 0x2e800e22, 0x2e800e22}, + {0x0000a5b8, 0x40805e6b, 0x40805e6b, 0x31800e24, 0x31800e24}, + {0x0000a5bc, 0x44805e6d, 0x44805e6d, 0x34801640, 0x34801640}, + {0x0000a5c0, 0x49805e72, 0x49805e72, 0x38801660, 0x38801660}, + {0x0000a5c4, 0x4e805eb2, 0x4e805eb2, 0x3b801861, 0x3b801861}, + {0x0000a5c8, 0x53805f12, 0x53805f12, 0x3e801a81, 0x3e801a81}, + {0x0000a5cc, 0x59825eb2, 0x59825eb2, 0x42801a83, 0x42801a83}, + {0x0000a5d0, 0x5e825f12, 0x5e825f12, 0x44801c84, 0x44801c84}, + {0x0000a5d4, 0x61827f12, 0x61827f12, 0x48801ce3, 0x48801ce3}, + {0x0000a5d8, 0x6782bf12, 0x6782bf12, 0x4c801ce5, 0x4c801ce5}, + {0x0000a5dc, 0x6b82bf14, 0x6b82bf14, 0x50801ce9, 0x50801ce9}, + {0x0000a5e0, 0x6f82bf16, 0x6f82bf16, 0x54801ceb, 0x54801ceb}, + {0x0000a5e4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, + {0x0000a5e8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, + {0x0000a5ec, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, + {0x0000a5f0, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, + {0x0000a5f4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, + {0x0000a5f8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, + {0x0000a5fc, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a614, 0x02004000, 0x02004000, 0x01404000, 0x01404000}, - {0x0000a618, 0x02004801, 0x02004801, 0x01404501, 0x01404501}, - {0x0000a61c, 0x02808a02, 0x02808a02, 0x02008501, 0x02008501}, - {0x0000a620, 0x0380ce03, 0x0380ce03, 0x0280ca03, 0x0280ca03}, - {0x0000a624, 0x04411104, 0x04411104, 0x03010c04, 0x03010c04}, - {0x0000a628, 0x04411104, 0x04411104, 0x04014c04, 0x04014c04}, - {0x0000a62c, 0x04411104, 0x04411104, 0x04015005, 0x04015005}, - {0x0000a630, 0x04411104, 0x04411104, 0x04015005, 0x04015005}, - {0x0000a634, 0x04411104, 0x04411104, 0x04015005, 0x04015005}, - {0x0000a638, 0x04411104, 0x04411104, 0x04015005, 0x04015005}, - {0x0000a63c, 0x04411104, 0x04411104, 0x04015005, 0x04015005}, - {0x0000b2dc, 0x00033800, 0x00033800, 0x03aaa352, 0x03aaa352}, - {0x0000b2e0, 0x0003c000, 0x0003c000, 0x03ccc584, 0x03ccc584}, - {0x0000b2e4, 0x03fc0000, 0x03fc0000, 0x03f0f800, 0x03f0f800}, + {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000}, + {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000}, + {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501}, + {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501}, + {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03}, + {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04}, + {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04}, + {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, + {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, + {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, - {0x0000c2dc, 0x00033800, 0x00033800, 0x03aaa352, 0x03aaa352}, - {0x0000c2e0, 0x0003c000, 0x0003c000, 0x03ccc584, 0x03ccc584}, - {0x0000c2e4, 0x03fc0000, 0x03fc0000, 0x03f0f800, 0x03f0f800}, + {0x0000c2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, + {0x0000c2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, + {0x0000c2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, {0x00016048, 0x66480001, 0x66480001, 0x66480001, 0x66480001}, -- cgit v1.2.3-70-g09d2 From 4f7dc951e2dad489fb89b03d3f968d3a229dc9ec Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Fri, 7 Dec 2012 20:25:30 +0530 Subject: ath9k: Fix regression in 'xmit' debugfs file Commit "ath9k: Fix the 'xmit' debugfs file" changed the the array size of ath_stats.txstats to IEEE80211_NUM_ACS, which is wrong because the HW queue number is used to update the statistics. Revert back to using ATH9K_NUM_TX_QUEUES. Reported-by: Felix Fietkau Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/debug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 72d4893311b..375c3b46411 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -242,7 +242,7 @@ struct ath_rx_stats { struct ath_stats { struct ath_interrupt_stats istats; - struct ath_tx_stats txstats[IEEE80211_NUM_ACS]; + struct ath_tx_stats txstats[ATH9K_NUM_TX_QUEUES]; struct ath_rx_stats rxstats; struct ath_dfs_stats dfs_stats; u32 reset[__RESET_TYPE_MAX]; -- cgit v1.2.3-70-g09d2 From fc5e286ff4ff6da3e76e6eb58c535e250ad2ff18 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 8 Dec 2012 15:24:56 +0100 Subject: carl9170: fix copy and paste mishap in carl9170_handle_mpdu This patch fixes a regression which was introduced by: "carl9170: split up carl9170_handle_mpdu" Previously, the ieee80211_rx_status was kept on the stack of carl9170_handle_mpdu. Now it's passed into the function as a pointer parameter. Hence, the old memcpy call needs to be fixed. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/rx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index 876a773fd0b..4684dd98949 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -684,7 +684,7 @@ static int carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len, if (!skb) return -ENOMEM; - memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); + memcpy(IEEE80211_SKB_RXCB(skb), status, sizeof(*status)); ieee80211_rx(ar->hw, skb); return 0; } -- cgit v1.2.3-70-g09d2 From b3cd8021379306c0be6932e4d3b4b01efc681769 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Sun, 9 Dec 2012 23:57:09 +0100 Subject: ath9k: ar9003: fix OTP register offsets for AR9340 Trying to access the OTP memory on the AR9340 causes a data bus error like this: Data bus error, epc == 86e84164, ra == 86e84164 Oops[#1]: Cpu 0 $ 0 : 00000000 00000061 deadc0de 00000000 $ 4 : b8115f18 00015f18 00000007 00000004 $ 8 : 00000001 7c7c3c7c 7c7c7c7c 7c7c7c7c $12 : 7c7c3c7c 001f0041 00000000 7c7c7c3c $16 : 86ee0000 00015f18 00000000 00000007 $20 : 00000004 00000064 00000004 86d71c44 $24 : 00000000 86e6ca00 $28 : 86d70000 86d71b20 86ece0c0 86e84164 Hi : 00000000 Lo : 00000064 epc : 86e84164 ath9k_hw_wait+0x58/0xb0 [ath9k_hw] Tainted: G O ra : 86e84164 ath9k_hw_wait+0x58/0xb0 [ath9k_hw] Status: 1100d403 KERNEL EXL IE Cause : 4080801c PrId : 0001974c (MIPS 74Kc) Modules linked in: ath9k(O+) ath9k_common(O) ath9k_hw(O) ath(O) ar934x_nfc mac80211(O) usbcore usb_common scsi_mod nls_base nand nand_ecc nand_ids crc_ccitt cfg80211(O) compat(O) arc4 aes_generic crypto_blkcipher cryptomgr aead crypto_hash crypto_algapi ledtrig_timer ledtrig_default_on leds_gpio Process insmod (pid: 459, threadinfo=86d70000, task=87942140, tls=779ac440) Stack : 802fb500 000200da 804db150 804e0000 87816130 86ee0000 00010000 86d71b88 86d71bc0 00000004 00000003 86e9fcd0 80305300 0002c0d0 86e74c50 800b4c20 000003e8 00000001 00000000 86ee0000 000003ff 86e9fd64 80305300 80123938 fffffffc 00000004 000058bc 00000000 86ea0000 86ee0000 000001ff 878d6000 99999999 86e9fdc0 86ee0fcc 86e9e664 0000c0d0 86ee0000 0000700000007000 ... Call Trace: [<86e84164>] ath9k_hw_wait+0x58/0xb0 [ath9k_hw] [<86e9fcd0>] ath9k_hw_setup_statusring+0x16b8/0x1c7c [ath9k_hw] Code: 0000a812 0040f809 00000000 <00531024> 1054000b 24020001 0c05b5dc 2404000a 26520001 The cause of the error is that the OTP register offsets are different on the AR9340 than the actually used values. Cc: # 3.0+ Signed-off-by: Gabor Juhos Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_eeprom.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h index 41b1a75e6be..54ba42f4108 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h @@ -68,13 +68,13 @@ #define AR9300_BASE_ADDR 0x3ff #define AR9300_BASE_ADDR_512 0x1ff -#define AR9300_OTP_BASE 0x14000 -#define AR9300_OTP_STATUS 0x15f18 +#define AR9300_OTP_BASE (AR_SREV_9340(ah) ? 0x30000 : 0x14000) +#define AR9300_OTP_STATUS (AR_SREV_9340(ah) ? 0x30018 : 0x15f18) #define AR9300_OTP_STATUS_TYPE 0x7 #define AR9300_OTP_STATUS_VALID 0x4 #define AR9300_OTP_STATUS_ACCESS_BUSY 0x2 #define AR9300_OTP_STATUS_SM_BUSY 0x1 -#define AR9300_OTP_READ_DATA 0x15f1c +#define AR9300_OTP_READ_DATA (AR_SREV_9340(ah) ? 0x3001c : 0x15f1c) enum targetPowerHTRates { HT_TARGET_RATE_0_8_16, -- cgit v1.2.3-70-g09d2 From b8a95db7df48b5b2d378ff526f674c877c7fa8a1 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 10 Dec 2012 07:22:31 +0530 Subject: ath9k_hw: Fix PAPRD registers for AR9485 Various PAPRD registers are at addresses that are different from those for the rest of the chips in the AR9003 family. Fix them. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_phy.h | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h index 4c3d06de711..10795629848 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h @@ -1145,9 +1145,8 @@ #define AR_PHY_PAPRD_CTRL1_PAPRD_MAG_SCALE_FACT 0x0ffe0000 #define AR_PHY_PAPRD_CTRL1_PAPRD_MAG_SCALE_FACT_S 17 -#define AR_PHY_PAPRD_TRAINER_CNTL1 (AR_SM_BASE + \ - (AR_SREV_9485(ah) ? \ - 0x580 : 0x490)) +#define AR_PHY_PAPRD_TRAINER_CNTL1 (AR_SM_BASE + (AR_SREV_9485(ah) ? 0x580 : 0x490)) + #define AR_PHY_PAPRD_TRAINER_CNTL1_CF_CF_PAPRD_TRAIN_ENABLE 0x00000001 #define AR_PHY_PAPRD_TRAINER_CNTL1_CF_CF_PAPRD_TRAIN_ENABLE_S 0 #define AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_AGC2_SETTLING 0x0000007e @@ -1163,15 +1162,13 @@ #define AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_LB_SKIP 0x0003f000 #define AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_LB_SKIP_S 12 -#define AR_PHY_PAPRD_TRAINER_CNTL2 (AR_SM_BASE + \ - (AR_SREV_9485(ah) ? \ - 0x584 : 0x494)) +#define AR_PHY_PAPRD_TRAINER_CNTL2 (AR_SM_BASE + (AR_SREV_9485(ah) ? 0x584 : 0x494)) + #define AR_PHY_PAPRD_TRAINER_CNTL2_CF_PAPRD_INIT_RX_BB_GAIN 0xFFFFFFFF #define AR_PHY_PAPRD_TRAINER_CNTL2_CF_PAPRD_INIT_RX_BB_GAIN_S 0 -#define AR_PHY_PAPRD_TRAINER_CNTL3 (AR_SM_BASE + \ - (AR_SREV_9485(ah) ? \ - 0x588 : 0x498)) +#define AR_PHY_PAPRD_TRAINER_CNTL3 (AR_SM_BASE + (AR_SREV_9485(ah) ? 0x588 : 0x498)) + #define AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_ADC_DESIRED_SIZE 0x0000003f #define AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_ADC_DESIRED_SIZE_S 0 #define AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP 0x00000fc0 @@ -1187,9 +1184,8 @@ #define AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_BBTXMIX_DISABLE 0x20000000 #define AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_BBTXMIX_DISABLE_S 29 -#define AR_PHY_PAPRD_TRAINER_CNTL4 (AR_SM_BASE + \ - (AR_SREV_9485(ah) ? \ - 0x58c : 0x49c)) +#define AR_PHY_PAPRD_TRAINER_CNTL4 (AR_SM_BASE + (AR_SREV_9485(ah) ? 0x58c : 0x49c)) + #define AR_PHY_PAPRD_TRAINER_CNTL4_CF_PAPRD_NUM_TRAIN_SAMPLES 0x03ff0000 #define AR_PHY_PAPRD_TRAINER_CNTL4_CF_PAPRD_NUM_TRAIN_SAMPLES_S 16 #define AR_PHY_PAPRD_TRAINER_CNTL4_CF_PAPRD_SAFETY_DELTA 0x0000f000 @@ -1208,7 +1204,8 @@ #define AR_PHY_PAPRD_PRE_POST_SCALING 0x3FFFF #define AR_PHY_PAPRD_PRE_POST_SCALING_S 0 -#define AR_PHY_PAPRD_TRAINER_STAT1 (AR_SM_BASE + 0x4a0) +#define AR_PHY_PAPRD_TRAINER_STAT1 (AR_SM_BASE + (AR_SREV_9485(ah) ? 0x590 : 0x4a0)) + #define AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE 0x00000001 #define AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE_S 0 #define AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_INCOMPLETE 0x00000002 @@ -1222,7 +1219,8 @@ #define AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_AGC2_PWR 0x0001fe00 #define AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_AGC2_PWR_S 9 -#define AR_PHY_PAPRD_TRAINER_STAT2 (AR_SM_BASE + 0x4a4) +#define AR_PHY_PAPRD_TRAINER_STAT2 (AR_SM_BASE + (AR_SREV_9485(ah) ? 0x594 : 0x4a4)) + #define AR_PHY_PAPRD_TRAINER_STAT2_PAPRD_FINE_VAL 0x0000ffff #define AR_PHY_PAPRD_TRAINER_STAT2_PAPRD_FINE_VAL_S 0 #define AR_PHY_PAPRD_TRAINER_STAT2_PAPRD_COARSE_IDX 0x001f0000 @@ -1230,7 +1228,8 @@ #define AR_PHY_PAPRD_TRAINER_STAT2_PAPRD_FINE_IDX 0x00600000 #define AR_PHY_PAPRD_TRAINER_STAT2_PAPRD_FINE_IDX_S 21 -#define AR_PHY_PAPRD_TRAINER_STAT3 (AR_SM_BASE + 0x4a8) +#define AR_PHY_PAPRD_TRAINER_STAT3 (AR_SM_BASE + (AR_SREV_9485(ah) ? 0x598 : 0x4a8)) + #define AR_PHY_PAPRD_TRAINER_STAT3_PAPRD_TRAIN_SAMPLES_CNT 0x000fffff #define AR_PHY_PAPRD_TRAINER_STAT3_PAPRD_TRAIN_SAMPLES_CNT_S 0 -- cgit v1.2.3-70-g09d2 From 8c723e2df2411eb02e697efb31f309136b747f7a Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 10 Dec 2012 07:22:32 +0530 Subject: ath9k_hw: Fix PAPRD training The PAPRD training control registers have to be programmed with values that depend on the chip. This patch ensures that the correct values are chosen for the chip in use. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_paprd.c | 36 +++++++++++++++++++++------ 1 file changed, 29 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c index 0ed3846f9cb..df9158f53fe 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c @@ -204,7 +204,20 @@ static int ar9003_paprd_setup_single_table(struct ath_hw *ah) AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_AGC2_SETTLING, 28); REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1, AR_PHY_PAPRD_TRAINER_CNTL1_CF_CF_PAPRD_TRAIN_ENABLE, 1); - val = AR_SREV_9462(ah) ? 0x91 : 147; + + if (AR_SREV_9485(ah)) { + val = 148; + } else { + if (IS_CHAN_2GHZ(ah->curchan)) { + if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) + val = 145; + else + val = 147; + } else { + val = 137; + } + } + REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL2, AR_PHY_PAPRD_TRAINER_CNTL2_CF_PAPRD_INIT_RX_BB_GAIN, val); REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, @@ -215,15 +228,24 @@ static int ar9003_paprd_setup_single_table(struct ath_hw *ah) AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_NUM_CORR_STAGES, 7); REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_MIN_LOOPBACK_DEL, 1); - if (AR_SREV_9485(ah) || AR_SREV_9462(ah) || AR_SREV_9550(ah)) + + if (AR_SREV_9485(ah) || + AR_SREV_9462(ah) || + AR_SREV_9565(ah) || + AR_SREV_9550(ah) || + AR_SREV_9330(ah) || + AR_SREV_9340(ah)) REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, - AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP, - -3); + AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP, -3); else REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, - AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP, - -6); - val = AR_SREV_9462(ah) ? -10 : -15; + AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP, -6); + + val = -10; + + if (IS_CHAN_2GHZ(ah->curchan) && !AR_SREV_9462(ah) && !AR_SREV_9565(ah)) + val = -15; + REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_ADC_DESIRED_SIZE, val); -- cgit v1.2.3-70-g09d2 From 914d0f4def3817c295d3e8cbb7ea53bef8b0efcb Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 10 Dec 2012 07:22:33 +0530 Subject: ath9k: Add a few debug messages for PAPRD Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_paprd.c | 3 +++ drivers/net/wireless/ath/ath9k/link.c | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c index df9158f53fe..4945fdf61ba 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c @@ -169,6 +169,9 @@ static int ar9003_paprd_setup_single_table(struct ath_hw *ah) REG_RMW_FIELD(ah, AR_PHY_PAPRD_HT40, AR_PHY_PAPRD_HT40_MASK, ah->paprd_ratemask_ht40); + ath_dbg(common, CALIBRATE, "PAPRD HT20 mask: 0x%x, HT40 mask: 0x%x\n", + ah->paprd_ratemask, ah->paprd_ratemask_ht40); + for (i = 0; i < ah->caps.max_txchains; i++) { REG_RMW_FIELD(ah, ctrl0[i], AR_PHY_PAPRD_CTRL0_USE_SINGLE_TABLE_MASK, 1); diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index fc6b075ad63..3c996ffe63c 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -179,11 +179,14 @@ void ath_rx_poll(unsigned long data) static void ath_paprd_activate(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_cal_data *caldata = ah->caldata; int chain; - if (!caldata || !caldata->paprd_done) + if (!caldata || !caldata->paprd_done) { + ath_dbg(common, CALIBRATE, "Failed to activate PAPRD\n"); return; + } ath9k_ps_wakeup(sc); ar9003_paprd_enable(ah, false); @@ -194,6 +197,7 @@ static void ath_paprd_activate(struct ath_softc *sc) ar9003_paprd_populate_single_table(ah, caldata, chain); } + ath_dbg(common, CALIBRATE, "Activating PAPRD\n"); ar9003_paprd_enable(ah, true); ath9k_ps_restore(sc); } @@ -253,8 +257,10 @@ void ath_paprd_calibrate(struct work_struct *work) int len = 1800; int ret; - if (!caldata || !caldata->paprd_packet_sent || caldata->paprd_done) + if (!caldata || !caldata->paprd_packet_sent || caldata->paprd_done) { + ath_dbg(common, CALIBRATE, "Skipping PAPRD calibration\n"); return; + } ath9k_ps_wakeup(sc); -- cgit v1.2.3-70-g09d2 From 19f7842240eb30b1fdf4eacdd444bc9f61f97382 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 10 Dec 2012 07:22:34 +0530 Subject: ath9k: Fix redundant PS wrappers Move the PowerSave wrappers outside ath_paprd_activate(), since they are already being used in ath_paprd_calibrate(). Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/link.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index 3c996ffe63c..c70b31a7255 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -188,7 +188,6 @@ static void ath_paprd_activate(struct ath_softc *sc) return; } - ath9k_ps_wakeup(sc); ar9003_paprd_enable(ah, false); for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { if (!(ah->txchainmask & BIT(chain))) @@ -199,7 +198,6 @@ static void ath_paprd_activate(struct ath_softc *sc) ath_dbg(common, CALIBRATE, "Activating PAPRD\n"); ar9003_paprd_enable(ah, true); - ath9k_ps_restore(sc); } static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int chain) @@ -436,11 +434,15 @@ set_timer: cal_interval = min(cal_interval, (u32)short_cal_interval); mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval)); + if (ah->eep_ops->get_eeprom(ah, EEP_PAPRD) && ah->caldata) { - if (!ah->caldata->paprd_done) + if (!ah->caldata->paprd_done) { ieee80211_queue_work(sc->hw, &sc->paprd_work); - else if (!ah->paprd_table_write_done) + } else if (!ah->paprd_table_write_done) { + ath9k_ps_wakeup(sc); ath_paprd_activate(sc); + ath9k_ps_restore(sc); + } } } -- cgit v1.2.3-70-g09d2 From 36d2943ba738a601e29cbd86fa6ab6ac838f3905 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 10 Dec 2012 07:22:35 +0530 Subject: ath9k_hw: Various trivial fixes for PAPRD * Remove unneeded memset. All the values in the PAPRD gain table are filled, so there is no need to zero out the arrays. * Use GFP_KERNEL in ar9003_paprd_create_curve This is called from the PAPRD work, so the atomic variant is not needed. * Change return type of ar9003_paprd_setup_gain_table Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_paprd.c | 9 ++------- drivers/net/wireless/ath/ath9k/hw.h | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c index 4945fdf61ba..b28565e8078 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c @@ -287,9 +287,6 @@ static void ar9003_paprd_get_gain_table(struct ath_hw *ah) u32 reg = AR_PHY_TXGAIN_TABLE; int i; - memset(entry, 0, sizeof(ah->paprd_gain_table_entries)); - memset(index, 0, sizeof(ah->paprd_gain_table_index)); - for (i = 0; i < PAPRD_GAIN_TABLE_ENTRIES; i++) { entry[i] = REG_READ(ah, reg); index[i] = (entry[i] >> 24) & 0xff; @@ -788,7 +785,7 @@ void ar9003_paprd_populate_single_table(struct ath_hw *ah, } EXPORT_SYMBOL(ar9003_paprd_populate_single_table); -int ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain) +void ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain) { unsigned int i, desired_gain, gain_index; unsigned int train_power = ah->paprd_training_power; @@ -806,8 +803,6 @@ int ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain) REG_CLR_BIT(ah, AR_PHY_PAPRD_TRAINER_STAT1, AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE); - - return 0; } EXPORT_SYMBOL(ar9003_paprd_setup_gain_table); @@ -919,7 +914,7 @@ int ar9003_paprd_create_curve(struct ath_hw *ah, memset(caldata->pa_table[chain], 0, sizeof(caldata->pa_table[chain])); - buf = kmalloc(2 * 48 * sizeof(u32), GFP_ATOMIC); + buf = kmalloc(2 * 48 * sizeof(u32), GFP_KERNEL); if (!buf) return -ENOMEM; diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 3636dabf03e..fe87d0e67a1 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -1058,7 +1058,7 @@ void ar9003_paprd_populate_single_table(struct ath_hw *ah, int chain); int ar9003_paprd_create_curve(struct ath_hw *ah, struct ath9k_hw_cal_data *caldata, int chain); -int ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain); +void ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain); int ar9003_paprd_init_table(struct ath_hw *ah); bool ar9003_paprd_is_done(struct ath_hw *ah); -- cgit v1.2.3-70-g09d2 From d882d242e4bfb2016e611a5aab0112fcece7a8ac Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 10 Dec 2012 07:22:36 +0530 Subject: ath9k_hw: Fix PAPRD retraining for AR9485 Retraining of PAPRD based on agc2_pwr is required for chips other than AR9485. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_paprd.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c index b28565e8078..fb3a87eeb67 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c @@ -965,9 +965,13 @@ EXPORT_SYMBOL(ar9003_paprd_init_table); bool ar9003_paprd_is_done(struct ath_hw *ah) { int paprd_done, agc2_pwr; + paprd_done = REG_READ_FIELD(ah, AR_PHY_PAPRD_TRAINER_STAT1, AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE); + if (AR_SREV_9485(ah)) + goto exit; + if (paprd_done == 0x1) { agc2_pwr = REG_READ_FIELD(ah, AR_PHY_PAPRD_TRAINER_STAT1, AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_AGC2_PWR); @@ -983,7 +987,7 @@ bool ar9003_paprd_is_done(struct ath_hw *ah) if (agc2_pwr <= PAPRD_IDEAL_AGC2_PWR_RANGE) paprd_done = 0; } - +exit: return !!paprd_done; } EXPORT_SYMBOL(ar9003_paprd_is_done); -- cgit v1.2.3-70-g09d2 From 0f21ee8d9c8a041b974cfb75d81d07b61bd0869f Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 10 Dec 2012 07:22:37 +0530 Subject: ath9k_hw: Add HW cap for PAPRD Add a HW capability to indicate whether PAPRD is enabled for the card, since PAPRD could be enabled in the EEPROM, but disabled in the driver. This makes things clearer. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_eeprom.c | 8 ++------ drivers/net/wireless/ath/ath9k/ar9003_paprd.c | 9 +++++++++ drivers/net/wireless/ath/ath9k/hw.c | 4 ++++ drivers/net/wireless/ath/ath9k/hw.h | 2 ++ drivers/net/wireless/ath/ath9k/link.c | 2 +- 5 files changed, 18 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index c86cb640004..6b8861c559e 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -2987,10 +2987,6 @@ static u32 ath9k_hw_ar9300_get_eeprom(struct ath_hw *ah, case EEP_RX_MASK: return pBase->txrxMask & 0xf; case EEP_PAPRD: - if (AR_SREV_9462(ah)) - return false; - if (!ah->config.enable_paprd) - return false; return !!(pBase->featureEnable & BIT(5)); case EEP_CHAIN_MASK_REDUCE: return (pBase->miscConfiguration >> 0x3) & 0x1; @@ -5097,7 +5093,7 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah, */ ar9003_hw_get_target_power_eeprom(ah, chan, targetPowerValT2); - if (ah->eep_ops->get_eeprom(ah, EEP_PAPRD)) { + if (ar9003_is_paprd_enabled(ah)) { if (IS_CHAN_2GHZ(chan)) modal_hdr = &eep->modalHeader2G; else @@ -5138,7 +5134,7 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah, twiceAntennaReduction, powerLimit); - if (ah->eep_ops->get_eeprom(ah, EEP_PAPRD)) { + if (ar9003_is_paprd_enabled(ah)) { for (i = 0; i < ar9300RateSize; i++) { if ((ah->paprd_ratemask & (1 << i)) && (abs(targetPowerValT2[i] - diff --git a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c index fb3a87eeb67..aea19088314 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c @@ -991,3 +991,12 @@ exit: return !!paprd_done; } EXPORT_SYMBOL(ar9003_paprd_is_done); + +bool ar9003_is_paprd_enabled(struct ath_hw *ah) +{ + if ((ah->caps.hw_caps & ATH9K_HW_CAP_PAPRD) && ah->config.enable_paprd) + return true; + + return false; +} +EXPORT_SYMBOL(ar9003_is_paprd_enabled); diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index e06bcec655a..7cb78706591 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2599,6 +2599,10 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) pCap->hw_caps |= ATH9K_HW_WOW_PATTERN_MATCH_DWORD; } + if (AR_SREV_9300_20_OR_LATER(ah) && + ah->eep_ops->get_eeprom(ah, EEP_PAPRD)) + pCap->hw_caps |= ATH9K_HW_CAP_PAPRD; + return 0; } diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index fe87d0e67a1..eff67592db1 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -247,6 +247,7 @@ enum ath9k_hw_caps { ATH9K_HW_WOW_DEVICE_CAPABLE = BIT(17), ATH9K_HW_WOW_PATTERN_MATCH_EXACT = BIT(18), ATH9K_HW_WOW_PATTERN_MATCH_DWORD = BIT(19), + ATH9K_HW_CAP_PAPRD = BIT(20), }; /* @@ -1061,6 +1062,7 @@ int ar9003_paprd_create_curve(struct ath_hw *ah, void ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain); int ar9003_paprd_init_table(struct ath_hw *ah); bool ar9003_paprd_is_done(struct ath_hw *ah); +bool ar9003_is_paprd_enabled(struct ath_hw *ah); /* Hardware family op attach helpers */ void ar5008_hw_attach_phy_ops(struct ath_hw *ah); diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index c70b31a7255..ade3afb21f9 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -435,7 +435,7 @@ set_timer: mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval)); - if (ah->eep_ops->get_eeprom(ah, EEP_PAPRD) && ah->caldata) { + if (ar9003_is_paprd_enabled(ah) && ah->caldata) { if (!ah->caldata->paprd_done) { ieee80211_queue_work(sc->hw, &sc->paprd_work); } else if (!ah->paprd_table_write_done) { -- cgit v1.2.3-70-g09d2 From 1562580e37e7a6936d591fd04cf9d7b79a096bf0 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 10 Dec 2012 07:22:38 +0530 Subject: ath9k_hw: Calculate the correct training power for PAPRD Assign the training power for PAPRD based on the chip. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_eeprom.c | 41 ++++++++++++++++++-------- drivers/net/wireless/ath/ath9k/ar9003_paprd.c | 22 +++++++++----- 2 files changed, 43 insertions(+), 20 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index 6b8861c559e..11082b417d2 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -5072,6 +5072,33 @@ static inline u8 mcsidx_to_tgtpwridx(unsigned int mcs_idx, u8 base_pwridx) return base_pwridx + 4 * (mcs_idx / 8) + mod_idx - 2; } +static void ar9003_paprd_set_txpower(struct ath_hw *ah, + struct ath9k_channel *chan, + u8 *targetPowerValT2) +{ + int i; + + if (!ar9003_is_paprd_enabled(ah)) + return; + + if (IS_CHAN_HT40(chan)) + i = ALL_TARGET_HT40_7; + else + i = ALL_TARGET_HT20_7; + + if (IS_CHAN_2GHZ(chan)) { + if (!AR_SREV_9330(ah) && !AR_SREV_9340(ah) && + !AR_SREV_9462(ah) && !AR_SREV_9565(ah)) { + if (IS_CHAN_HT40(chan)) + i = ALL_TARGET_HT40_0_8_16; + else + i = ALL_TARGET_HT20_0_8_16; + } + } + + ah->paprd_target_power = targetPowerValT2[i]; +} + static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah, struct ath9k_channel *chan, u16 cfgCtl, u8 twiceAntennaReduction, @@ -5166,19 +5193,7 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah, /* Write target power array to registers */ ar9003_hw_tx_power_regwrite(ah, targetPowerValT2); ar9003_hw_calibration_apply(ah, chan->channel); - - if (IS_CHAN_2GHZ(chan)) { - if (IS_CHAN_HT40(chan)) - i = ALL_TARGET_HT40_0_8_16; - else - i = ALL_TARGET_HT20_0_8_16; - } else { - if (IS_CHAN_HT40(chan)) - i = ALL_TARGET_HT40_7; - else - i = ALL_TARGET_HT20_7; - } - ah->paprd_target_power = targetPowerValT2[i]; + ar9003_paprd_set_txpower(ah, chan, targetPowerValT2); } static u16 ath9k_hw_ar9300_get_spur_channel(struct ath_hw *ah, diff --git a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c index aea19088314..09c1f9da67a 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c @@ -74,15 +74,23 @@ static int ar9003_get_training_power_2g(struct ath_hw *ah) unsigned int power, scale, delta; scale = ar9003_get_paprd_scale_factor(ah, chan); - power = REG_READ_FIELD(ah, AR_PHY_POWERTX_RATE5, - AR_PHY_POWERTX_RATE5_POWERTXHT20_0); - delta = abs((int) ah->paprd_target_power - (int) power); - if (delta > scale) - return -1; + if (AR_SREV_9330(ah) || AR_SREV_9340(ah) || + AR_SREV_9462(ah) || AR_SREV_9565(ah)) { + power = ah->paprd_target_power + 2; + } else if (AR_SREV_9485(ah)) { + power = 25; + } else { + power = REG_READ_FIELD(ah, AR_PHY_POWERTX_RATE5, + AR_PHY_POWERTX_RATE5_POWERTXHT20_0); + + delta = abs((int) ah->paprd_target_power - (int) power); + if (delta > scale) + return -1; - if (delta < 4) - power -= 4 - delta; + if (delta < 4) + power -= 4 - delta; + } return power; } -- cgit v1.2.3-70-g09d2 From 3edfd10b45ae4ebe8ff905f626708c49e4fdc167 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 10 Dec 2012 08:20:27 +0530 Subject: ath9k_hw: Update intivals for AR9340 Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9340_initvals.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9340_initvals.h b/drivers/net/wireless/ath/ath9k/ar9340_initvals.h index 1d8235e19f0..f69d292bdc0 100644 --- a/drivers/net/wireless/ath/ath9k/ar9340_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9340_initvals.h @@ -211,6 +211,8 @@ static const u32 ar9340_1p0_radio_core_40M[][2] = { {0x0001609c, 0x02566f3a}, {0x000160ac, 0xa4647c00}, {0x000160b0, 0x01885f5a}, + {0x00008244, 0x0010f400}, + {0x0000824c, 0x0001e800}, }; #define ar9340_1p0_mac_postamble ar9300_2p2_mac_postamble @@ -1273,9 +1275,9 @@ static const u32 ar9340_1p0_mac_core[][2] = { {0x000081f8, 0x00000000}, {0x000081fc, 0x00000000}, {0x00008240, 0x00100000}, - {0x00008244, 0x0010f424}, + {0x00008244, 0x0010f3d7}, {0x00008248, 0x00000800}, - {0x0000824c, 0x0001e848}, + {0x0000824c, 0x0001e7ae}, {0x00008250, 0x00000000}, {0x00008254, 0x00000000}, {0x00008258, 0x00000000}, -- cgit v1.2.3-70-g09d2 From b7c0c238898d200e80487516e2b67aba2a522cc0 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 10 Dec 2012 14:03:17 +0100 Subject: ath9k_hw: Fix signal strength / channel noise reporting While AR_PHY_CCA_NOM_VAL_* does contain the expected internal noise floor for a chip measured in clean air, it refers to the lowest expected reading. Depending on the frequency, this measurement can vary by about 6db, thus causing a higher reported channel noise and signal strength. Factor in the 6db offset when converting internal noisefloor to channel noise. This patch makes the reported values more accurate for all chips without affecting NF calibration behavior. Signed-off-by: Felix Fietkau Cc: stable@vger.kernel.org Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/calib.c | 1 + drivers/net/wireless/ath/ath9k/calib.h | 3 +++ 2 files changed, 4 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index f3448a032e6..1e8508530e9 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -69,6 +69,7 @@ s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan) if (chan && chan->noisefloor) { s8 delta = chan->noisefloor - + ATH9K_NF_CAL_NOISE_THRESH - ath9k_hw_get_default_nf(ah, chan); if (delta > 0) noise += delta; diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h index 1060c19a501..60dcb6c22db 100644 --- a/drivers/net/wireless/ath/ath9k/calib.h +++ b/drivers/net/wireless/ath/ath9k/calib.h @@ -21,6 +21,9 @@ #define AR_PHY_CCA_FILTERWINDOW_LENGTH 5 +/* Internal noise floor can vary by about 6db depending on the frequency */ +#define ATH9K_NF_CAL_NOISE_THRESH 6 + #define NUM_NF_READINGS 6 #define ATH9K_NF_CAL_HIST_MAX 5 -- cgit v1.2.3-70-g09d2 From 2fd2cdfb6d2f3c3d18f2e47f41679a7bde9bcbf3 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Mon, 10 Dec 2012 15:30:25 +0100 Subject: ath9k: move duplicated debug message to 'ath9k_hw_nvram_read' The fill_eeprom functions are printing the same debug message in case the 'ath9k_hw_nvram_read' function fails. Remove the duplicated code from fill_eeprom functions and add the ath_dbg call directly into 'ath9k_hw_nvram_read'. Signed-off-by: Gabor Juhos Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/eeprom.c | 8 +++++++- drivers/net/wireless/ath/ath9k/eeprom_4k.c | 6 ++---- drivers/net/wireless/ath/ath9k/eeprom_9287.c | 5 +---- drivers/net/wireless/ath/ath9k/eeprom_def.c | 5 +---- 4 files changed, 11 insertions(+), 13 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c index 0512397a293..4b6a9350adc 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.c +++ b/drivers/net/wireless/ath/ath9k/eeprom.c @@ -115,7 +115,13 @@ void ath9k_hw_usb_gen_fill_eeprom(struct ath_hw *ah, u16 *eep_data, bool ath9k_hw_nvram_read(struct ath_common *common, u32 off, u16 *data) { - return common->bus_ops->eeprom_read(common, off, data); + bool ret; + + ret = common->bus_ops->eeprom_read(common, off, data); + if (!ret) + ath_dbg(common, EEPROM, "Unable to read eeprom region\n"); + + return ret; } void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList, diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index 7d075105a85..17c843d3721 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -37,11 +37,9 @@ static bool __ath9k_hw_4k_fill_eeprom(struct ath_hw *ah) int addr, eep_start_loc = 64; for (addr = 0; addr < SIZE_EEPROM_4K; addr++) { - if (!ath9k_hw_nvram_read(common, addr + eep_start_loc, eep_data)) { - ath_dbg(common, EEPROM, - "Unable to read eeprom region\n"); + if (!ath9k_hw_nvram_read(common, addr + eep_start_loc, + eep_data)) return false; - } eep_data++; } diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c index cd742fb944c..f2c32bc36f1 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c @@ -40,11 +40,8 @@ static bool __ath9k_hw_ar9287_fill_eeprom(struct ath_hw *ah) for (addr = 0; addr < SIZE_EEPROM_AR9287; addr++) { if (!ath9k_hw_nvram_read(common, addr + eep_start_loc, - eep_data)) { - ath_dbg(common, EEPROM, - "Unable to read eeprom region\n"); + eep_data)) return false; - } eep_data++; } diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c index a8ac30a0072..2654f741b8b 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -97,11 +97,8 @@ static bool __ath9k_hw_def_fill_eeprom(struct ath_hw *ah) for (addr = 0; addr < SIZE_EEPROM_DEF; addr++) { if (!ath9k_hw_nvram_read(common, addr + ar5416_eep_start_loc, - eep_data)) { - ath_err(ath9k_hw_common(ah), - "Unable to read eeprom region\n"); + eep_data)) return false; - } eep_data++; } return true; -- cgit v1.2.3-70-g09d2 From 7177d8f998c452701ad5352cca12408e26f80b07 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Mon, 10 Dec 2012 15:30:26 +0100 Subject: ath9k: add EEPROM offset to debug message Show the EEPROM offset of the failed read operation in 'ath9k_hw_nvram_read'. The debug message is more informative this way. Signed-off-by: Gabor Juhos Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/eeprom.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c index 4b6a9350adc..bf52ae1fdff 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.c +++ b/drivers/net/wireless/ath/ath9k/eeprom.c @@ -119,7 +119,8 @@ bool ath9k_hw_nvram_read(struct ath_common *common, u32 off, u16 *data) ret = common->bus_ops->eeprom_read(common, off, data); if (!ret) - ath_dbg(common, EEPROM, "Unable to read eeprom region\n"); + ath_dbg(common, EEPROM, + "unable to read eeprom region at offset %u\n", off); return ret; } -- cgit v1.2.3-70-g09d2 From 0e4b9f2f12e94686855605eda64ea534a95d77f2 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Mon, 10 Dec 2012 15:30:27 +0100 Subject: ath9k: use 'struct ath_hw *' as the first argument for 'ath9k_hw_nvram_read' The 'ath9k_hw_nvram_read' function takes a 'struct ath_common *' as its first argument. Almost each of its caller has a 'struct ath_hw *' parameter in their argument list, and that is dereferenced in order to get the 'struct ath_common' pointer. Change the first argument of 'ath9k_hw_nvram_read' to be a 'struct ath_hw *', and remove the dereference calls from the callers. Also change the type of the first argument of the ar9300_eeprom_read_{byte,word} functions. Signed-off-by: Gabor Juhos Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_eeprom.c | 17 ++++++++--------- drivers/net/wireless/ath/ath9k/eeprom.c | 3 ++- drivers/net/wireless/ath/ath9k/eeprom.h | 2 +- drivers/net/wireless/ath/ath9k/eeprom_4k.c | 6 ++---- drivers/net/wireless/ath/ath9k/eeprom_9287.c | 6 ++---- drivers/net/wireless/ath/ath9k/eeprom_def.c | 5 ++--- 6 files changed, 17 insertions(+), 22 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index 11082b417d2..562186ca9b5 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -3001,24 +3001,24 @@ static u32 ath9k_hw_ar9300_get_eeprom(struct ath_hw *ah, } } -static bool ar9300_eeprom_read_byte(struct ath_common *common, int address, +static bool ar9300_eeprom_read_byte(struct ath_hw *ah, int address, u8 *buffer) { u16 val; - if (unlikely(!ath9k_hw_nvram_read(common, address / 2, &val))) + if (unlikely(!ath9k_hw_nvram_read(ah, address / 2, &val))) return false; *buffer = (val >> (8 * (address % 2))) & 0xff; return true; } -static bool ar9300_eeprom_read_word(struct ath_common *common, int address, +static bool ar9300_eeprom_read_word(struct ath_hw *ah, int address, u8 *buffer) { u16 val; - if (unlikely(!ath9k_hw_nvram_read(common, address / 2, &val))) + if (unlikely(!ath9k_hw_nvram_read(ah, address / 2, &val))) return false; buffer[0] = val >> 8; @@ -3044,14 +3044,14 @@ static bool ar9300_read_eeprom(struct ath_hw *ah, int address, u8 *buffer, * the 16-bit word at that address */ if (address % 2 == 0) { - if (!ar9300_eeprom_read_byte(common, address--, buffer++)) + if (!ar9300_eeprom_read_byte(ah, address--, buffer++)) goto error; count--; } for (i = 0; i < count / 2; i++) { - if (!ar9300_eeprom_read_word(common, address, buffer)) + if (!ar9300_eeprom_read_word(ah, address, buffer)) goto error; address -= 2; @@ -3059,7 +3059,7 @@ static bool ar9300_read_eeprom(struct ath_hw *ah, int address, u8 *buffer, } if (count % 2) - if (!ar9300_eeprom_read_byte(common, address, buffer)) + if (!ar9300_eeprom_read_byte(ah, address, buffer)) goto error; return true; @@ -3236,12 +3236,11 @@ static bool ar9300_check_eeprom_header(struct ath_hw *ah, eeprom_read_op read, static int ar9300_eeprom_restore_flash(struct ath_hw *ah, u8 *mptr, int mdata_size) { - struct ath_common *common = ath9k_hw_common(ah); u16 *data = (u16 *) mptr; int i; for (i = 0; i < mdata_size / 2; i++, data++) - ath9k_hw_nvram_read(common, i, data); + ath9k_hw_nvram_read(ah, i, data); return 0; } diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c index bf52ae1fdff..eb9ac5ea618 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.c +++ b/drivers/net/wireless/ath/ath9k/eeprom.c @@ -113,8 +113,9 @@ void ath9k_hw_usb_gen_fill_eeprom(struct ath_hw *ah, u16 *eep_data, } } -bool ath9k_hw_nvram_read(struct ath_common *common, u32 off, u16 *data) +bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data) { + struct ath_common *common = ath9k_hw_common(ah); bool ret; ret = common->bus_ops->eeprom_read(common, off, data); diff --git a/drivers/net/wireless/ath/ath9k/eeprom.h b/drivers/net/wireless/ath/ath9k/eeprom.h index 319c651fa6c..40d4f62d0f1 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.h +++ b/drivers/net/wireless/ath/ath9k/eeprom.h @@ -663,7 +663,7 @@ int16_t ath9k_hw_interpolate(u16 target, u16 srcLeft, u16 srcRight, int16_t targetRight); bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, u16 listSize, u16 *indexL, u16 *indexR); -bool ath9k_hw_nvram_read(struct ath_common *common, u32 off, u16 *data); +bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data); void ath9k_hw_usb_gen_fill_eeprom(struct ath_hw *ah, u16 *eep_data, int eep_start_loc, int size); void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList, diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index 17c843d3721..c2bfd748eed 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -32,13 +32,11 @@ static int ath9k_hw_4k_get_eeprom_rev(struct ath_hw *ah) static bool __ath9k_hw_4k_fill_eeprom(struct ath_hw *ah) { - struct ath_common *common = ath9k_hw_common(ah); u16 *eep_data = (u16 *)&ah->eeprom.map4k; int addr, eep_start_loc = 64; for (addr = 0; addr < SIZE_EEPROM_4K; addr++) { - if (!ath9k_hw_nvram_read(common, addr + eep_start_loc, - eep_data)) + if (!ath9k_hw_nvram_read(ah, addr + eep_start_loc, eep_data)) return false; eep_data++; } @@ -194,7 +192,7 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) if (!ath9k_hw_use_flash(ah)) { - if (!ath9k_hw_nvram_read(common, AR5416_EEPROM_MAGIC_OFFSET, + if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) { ath_err(common, "Reading Magic # failed\n"); return false; diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c index f2c32bc36f1..3ae1f3df063 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c @@ -33,14 +33,12 @@ static int ath9k_hw_ar9287_get_eeprom_rev(struct ath_hw *ah) static bool __ath9k_hw_ar9287_fill_eeprom(struct ath_hw *ah) { struct ar9287_eeprom *eep = &ah->eeprom.map9287; - struct ath_common *common = ath9k_hw_common(ah); u16 *eep_data; int addr, eep_start_loc = AR9287_EEP_START_LOC; eep_data = (u16 *)eep; for (addr = 0; addr < SIZE_EEPROM_AR9287; addr++) { - if (!ath9k_hw_nvram_read(common, addr + eep_start_loc, - eep_data)) + if (!ath9k_hw_nvram_read(ah, addr + eep_start_loc, eep_data)) return false; eep_data++; } @@ -187,7 +185,7 @@ static int ath9k_hw_ar9287_check_eeprom(struct ath_hw *ah) struct ath_common *common = ath9k_hw_common(ah); if (!ath9k_hw_use_flash(ah)) { - if (!ath9k_hw_nvram_read(common, AR5416_EEPROM_MAGIC_OFFSET, + if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) { ath_err(common, "Reading Magic # failed\n"); return false; diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c index 2654f741b8b..1c25368b383 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -91,12 +91,11 @@ static int ath9k_hw_def_get_eeprom_rev(struct ath_hw *ah) static bool __ath9k_hw_def_fill_eeprom(struct ath_hw *ah) { - struct ath_common *common = ath9k_hw_common(ah); u16 *eep_data = (u16 *)&ah->eeprom.def; int addr, ar5416_eep_start_loc = 0x100; for (addr = 0; addr < SIZE_EEPROM_DEF; addr++) { - if (!ath9k_hw_nvram_read(common, addr + ar5416_eep_start_loc, + if (!ath9k_hw_nvram_read(ah, addr + ar5416_eep_start_loc, eep_data)) return false; eep_data++; @@ -268,7 +267,7 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) bool need_swap = false; int i, addr, size; - if (!ath9k_hw_nvram_read(common, AR5416_EEPROM_MAGIC_OFFSET, &magic)) { + if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) { ath_err(common, "Reading Magic # failed\n"); return false; } -- cgit v1.2.3-70-g09d2 From ab5c4f71d8c7add173a2d32e5beefdaaf1b7cbbc Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Mon, 10 Dec 2012 15:30:28 +0100 Subject: ath9k: allow to load EEPROM content via firmware API The calibration data for devices w/o a separate EEPROM chip can be specified via the 'eeprom_data' field of 'ath9k_platform_data'. The 'eeprom_data' is usually filled from board specific setup functions. It is easy if the EEPROM data is mapped to the memory, but it can be complicated if it is stored elsewhere. The patch adds support for loading of the EEPROM data via the firmware API to avoid this limitation. Signed-off-by: Gabor Juhos Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/eeprom.c | 19 ++++++++++- drivers/net/wireless/ath/ath9k/hw.h | 3 ++ drivers/net/wireless/ath/ath9k/init.c | 60 ++++++++++++++++++++++++++++++++- include/linux/ath9k_platform.h | 2 ++ 4 files changed, 82 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c index eb9ac5ea618..971d770722c 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.c +++ b/drivers/net/wireless/ath/ath9k/eeprom.c @@ -113,12 +113,29 @@ void ath9k_hw_usb_gen_fill_eeprom(struct ath_hw *ah, u16 *eep_data, } } +static bool ath9k_hw_nvram_read_blob(struct ath_hw *ah, u32 off, + u16 *data) +{ + u16 *blob_data; + + if (off * sizeof(u16) > ah->eeprom_blob->size) + return false; + + blob_data = (u16 *)ah->eeprom_blob->data; + *data = blob_data[off]; + return true; +} + bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data) { struct ath_common *common = ath9k_hw_common(ah); bool ret; - ret = common->bus_ops->eeprom_read(common, off, data); + if (ah->eeprom_blob) + ret = ath9k_hw_nvram_read_blob(ah, off, data); + else + ret = common->bus_ops->eeprom_read(common, off, data); + if (!ret) ath_dbg(common, EEPROM, "unable to read eeprom region at offset %u\n", off); diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index eff67592db1..7f1a8e91c90 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "mac.h" #include "ani.h" @@ -921,6 +922,8 @@ struct ath_hw { bool is_clk_25mhz; int (*get_mac_revision)(void); int (*external_reset)(void); + + const struct firmware *eeprom_blob; }; struct ath_bus_ops { diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 80cae53a33e..27703a5e48d 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -23,6 +23,11 @@ #include "ath9k.h" +struct ath9k_eeprom_ctx { + struct completion complete; + struct ath_hw *ah; +}; + static char *dev_info = "ath9k"; MODULE_AUTHOR("Atheros Communications"); @@ -506,6 +511,51 @@ static void ath9k_init_misc(struct ath_softc *sc) sc->ant_comb.count = ATH_ANT_DIV_COMB_INIT_COUNT; } +static void ath9k_eeprom_request_cb(const struct firmware *eeprom_blob, + void *ctx) +{ + struct ath9k_eeprom_ctx *ec = ctx; + + if (eeprom_blob) + ec->ah->eeprom_blob = eeprom_blob; + + complete(&ec->complete); +} + +static int ath9k_eeprom_request(struct ath_softc *sc, const char *name) +{ + struct ath9k_eeprom_ctx ec; + struct ath_hw *ah = ah = sc->sc_ah; + int err; + + /* try to load the EEPROM content asynchronously */ + init_completion(&ec.complete); + ec.ah = sc->sc_ah; + + err = request_firmware_nowait(THIS_MODULE, 1, name, sc->dev, GFP_KERNEL, + &ec, ath9k_eeprom_request_cb); + if (err < 0) { + ath_err(ath9k_hw_common(ah), + "EEPROM request failed\n"); + return err; + } + + wait_for_completion(&ec.complete); + + if (!ah->eeprom_blob) { + ath_err(ath9k_hw_common(ah), + "Unable to load EEPROM file %s\n", name); + return -EINVAL; + } + + return 0; +} + +static void ath9k_eeprom_release(struct ath_softc *sc) +{ + release_firmware(sc->sc_ah->eeprom_blob); +} + static int ath9k_init_softc(u16 devid, struct ath_softc *sc, const struct ath_bus_ops *bus_ops) { @@ -583,6 +633,12 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, ath_read_cachesize(common, &csz); common->cachelsz = csz << 2; /* convert to bytes */ + if (pdata->eeprom_name) { + ret = ath9k_eeprom_request(sc, pdata->eeprom_name); + if (ret) + goto err_eeprom; + } + /* Initializes the hardware for all supported chipsets */ ret = ath9k_hw_init(ah); if (ret) @@ -619,7 +675,8 @@ err_btcoex: err_queues: ath9k_hw_deinit(ah); err_hw: - + ath9k_eeprom_release(sc); +err_eeprom: kfree(ah); sc->sc_ah = NULL; @@ -882,6 +939,7 @@ static void ath9k_deinit_softc(struct ath_softc *sc) if (sc->dfs_detector != NULL) sc->dfs_detector->exit(sc->dfs_detector); + ath9k_eeprom_release(sc); kfree(sc->sc_ah); sc->sc_ah = NULL; } diff --git a/include/linux/ath9k_platform.h b/include/linux/ath9k_platform.h index 6e3f54f3784..fcdd81bd531 100644 --- a/include/linux/ath9k_platform.h +++ b/include/linux/ath9k_platform.h @@ -22,6 +22,8 @@ #define ATH9K_PLAT_EEP_MAX_WORDS 2048 struct ath9k_platform_data { + const char *eeprom_name; + u16 eeprom_data[ATH9K_PLAT_EEP_MAX_WORDS]; u8 *macaddr; -- cgit v1.2.3-70-g09d2 From 596ab5ec3bf10a22be30d7cb1d903a4b83fd607c Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 10 Dec 2012 16:40:41 +0100 Subject: ath5k: fix tx path skb leaks ieee80211_free_txskb() needs to be used instead of dev_kfree_skb_any for tx packets passed to the driver from mac80211 Signed-off-by: Felix Fietkau Cc: stable@vger.kernel.org Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 4 ++-- drivers/net/wireless/ath/ath5k/mac80211-ops.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 3a5a61e0392..4ad40cfe9ce 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -849,7 +849,7 @@ ath5k_txbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf) return; dma_unmap_single(ah->dev, bf->skbaddr, bf->skb->len, DMA_TO_DEVICE); - dev_kfree_skb_any(bf->skb); + ieee80211_free_txskb(ah->hw, bf->skb); bf->skb = NULL; bf->skbaddr = 0; bf->desc->ds_data = 0; @@ -1576,7 +1576,7 @@ ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, return; drop_packet: - dev_kfree_skb_any(skb); + ieee80211_free_txskb(hw, skb); } static void diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index 1ea8c8795c8..4264341533e 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -62,7 +62,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, u16 qnum = skb_get_queue_mapping(skb); if (WARN_ON(qnum >= ah->ah_capabilities.cap_queues.q_tx_num)) { - dev_kfree_skb_any(skb); + ieee80211_free_txskb(hw, skb); return; } -- cgit v1.2.3-70-g09d2 From 96d21371ac8583d0935e5c41a798004084789606 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Mon, 10 Dec 2012 14:48:01 -0800 Subject: ath9k: RX timestamp is reported at end of frame Accurate RX timestamp reporting is important for proper IBSS merging, mesh synchronization, and MCCA scheduling. Namely, knowing where the TSF is recorded is needed to sync with the beacon timestamp field. Tested with AR9280. Signed-off-by: Thomas Pedersen Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/recv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 6aafbb77c49..d4df98a938b 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -976,7 +976,7 @@ static int ath9k_rx_skb_preprocess(struct ath_common *common, rx_status->freq = hw->conf.channel->center_freq; rx_status->signal = ah->noise + rx_stats->rs_rssi; rx_status->antenna = rx_stats->rs_antenna; - rx_status->flag |= RX_FLAG_MACTIME_START; + rx_status->flag |= RX_FLAG_MACTIME_END; if (rx_stats->rs_moreaggr) rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; -- cgit v1.2.3-70-g09d2 From 75d7dbc280bb75d2bdce59c5969a812f38255f54 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Mon, 10 Dec 2012 14:48:02 -0800 Subject: ath9k_htc: RX timestamp is reported at end of frame Accurate RX timestamp reporting is important for proper IBSS merging, mesh synchronization, and MCCA scheduling. Namely, knowing where the TSF is recorded is needed to sync with the beacon timestamp field. Tested with AR9271. Signed-off-by: Thomas Pedersen Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/htc_drv_txrx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index 28cd50ee521..b6a5a08810b 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -1082,7 +1082,7 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv, rx_status->freq = hw->conf.channel->center_freq; rx_status->signal = rxbuf->rxstatus.rs_rssi + ATH_DEFAULT_NOISE_FLOOR; rx_status->antenna = rxbuf->rxstatus.rs_antenna; - rx_status->flag |= RX_FLAG_MACTIME_START; + rx_status->flag |= RX_FLAG_MACTIME_END; return true; -- cgit v1.2.3-70-g09d2 From e576defd1fe1ade5afe590eadd556dce5473fc26 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Mon, 10 Dec 2012 14:48:03 -0800 Subject: ath5k: RX timestamp is reported at end of frame This is true for at least AR5213, and shouldn't be different for other ath5k PHYs. Tested on AR2413 and AR5414. Signed-off-by: Thomas Pedersen Tested-by: Bob Copeland Acked-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 4ad40cfe9ce..30ca0a60a64 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1336,20 +1336,9 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb, * 15bit only. that means TSF extension has to be done within * 32768usec (about 32ms). it might be necessary to move this to * the interrupt handler, like it is done in madwifi. - * - * Unfortunately we don't know when the hardware takes the rx - * timestamp (beginning of phy frame, data frame, end of rx?). - * The only thing we know is that it is hardware specific... - * On AR5213 it seems the rx timestamp is at the end of the - * frame, but I'm not sure. - * - * NOTE: mac80211 defines mactime at the beginning of the first - * data symbol. Since we don't have any time references it's - * impossible to comply to that. This affects IBSS merge only - * right now, so it's not too bad... */ rxs->mactime = ath5k_extend_tsf(ah, rs->rs_tstamp); - rxs->flag |= RX_FLAG_MACTIME_START; + rxs->flag |= RX_FLAG_MACTIME_END; rxs->freq = ah->curchan->center_freq; rxs->band = ah->curchan->band; -- cgit v1.2.3-70-g09d2 From 36b07d15a656b657beaf9e6357a341768192fad2 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Tue, 11 Dec 2012 00:06:41 +0100 Subject: ath9k: check pdata variable before dereferencing it Due to my recent commit (ath9k: allow to load EEPROM content via firmware API) smatch complains about that the 'pdata' variable in 'ath9k_hw_init' can be NULL and it is dereferenced before checking that. That is absolutely correct. Check the 'pdata' variable before using it to avoid a NULL pointer dereference. Reported-by: Dan Carpenter Signed-off-by: Gabor Juhos Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 27703a5e48d..f69ef5d48c7 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -633,7 +633,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, ath_read_cachesize(common, &csz); common->cachelsz = csz << 2; /* convert to bytes */ - if (pdata->eeprom_name) { + if (pdata && pdata->eeprom_name) { ret = ath9k_eeprom_request(sc, pdata->eeprom_name); if (ret) goto err_eeprom; -- cgit v1.2.3-70-g09d2 From 12e9432707dad5a09d8c51857b275c62bb6f91c8 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Thu, 20 Dec 2012 08:38:00 +0530 Subject: ath9k: Fix compilation breakage Since ath9k makes use of mac80211's debugfs hooks to maintain station statistics, make ATH9K_DEBUGFS select MAC80211_DEBUGFS. This fixes the issue reported by Fengguang Wu: drivers/net/wireless/ath/ath9k/debug.c: In function 'ath9k_sta_add_debugfs': drivers/net/wireless/ath/ath9k/debug.c:1589:4: error: 'struct ath_node' has no member named 'node_stat' drivers/net/wireless/ath/ath9k/debug.c: In function 'ath9k_sta_remove_debugfs': drivers/net/wireless/ath/ath9k/debug.c:1599:19: error: 'struct ath_node' has no member named 'node_stat' Reported-by: Fengguang Wu Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index 5fc15bf8be0..581913ee6ef 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -56,7 +56,8 @@ config ATH9K_AHB config ATH9K_DEBUGFS bool "Atheros ath9k debugging" - depends on ATH9K && DEBUG_FS + depends on ATH9K + select MAC80211_DEBUGFS ---help--- Say Y, if you need access to ath9k's statistics for interrupts, rate control, etc. -- cgit v1.2.3-70-g09d2 From 6c653f66772c39c5e25db715bbd4730596fccd9e Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 22 Dec 2012 04:35:24 +0100 Subject: carl9170: fix -EINVAL bailout during init with !CONFIG_MAC80211_MESH Sean reported that as of 3.7, his AR9170 device no longer works because the driver fails during initialization. He noted this is due to: "In carl9170/fw.c, ar->hw->wiphy is tagged with NL80211_IFTYPE_MESH_POINT support if the firmware has Content after Beacon Queuing. This is both in interface_modes and the only iface_combinations entry. If CONFIG_MAC80211_MESH is not set, ieee80211_register_hw removes NL80211_IFTYPE_MESH_POINT from interface_modes, but not iface_combinations. wiphy_register then checks to see if every interface type in every interface combination is in interface_modes. NL80211_IFTYPE_MESH_POINT was removed, so you get a WARN_ON warning and it returns -EINVAL, giving up." Unfortunately, the iface_combination (types) feature bitmap in ieee80211_iface_limit is part of a const member in the ieee80211_iface_combination struct. Hence, the MESH_POINT feature flag can't be masked by wiphy_register in the same way as interface_modes in ieee80211_register_hw. Cc: Reported-by: Sean Patrick Santos Signed-off-by: Christian Lamparter Tested-by: Sean Patrick Santos Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/fw.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c index aaebecd19e5..63fd9af3fd3 100644 --- a/drivers/net/wireless/ath/carl9170/fw.c +++ b/drivers/net/wireless/ath/carl9170/fw.c @@ -336,8 +336,12 @@ static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len) if (SUPP(CARL9170FW_WLANTX_CAB)) { if_comb_types |= BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_MESH_POINT) | BIT(NL80211_IFTYPE_P2P_GO); + +#ifdef CONFIG_MAC80211_MESH + if_comb_types |= + BIT(NL80211_IFTYPE_MESH_POINT); +#endif /* CONFIG_MAC80211_MESH */ } } -- cgit v1.2.3-70-g09d2 From a796a1dd5da9645ad77aa687d1a890ecd63ab5a6 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Wed, 26 Dec 2012 12:27:39 +0530 Subject: ath9k_hw: Fix RX gain initvals for AR9485 Populate iniModesRxGain with the correct initvals array for AR9485 v1.1 Cc: stable@vger.kernel.org Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_hw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index 74fd3977fee..59bf5f31e21 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -544,7 +544,7 @@ static void ar9003_rx_gain_table_mode0(struct ath_hw *ah) ar9340Common_rx_gain_table_1p0); else if (AR_SREV_9485_11(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, - ar9485Common_wo_xlna_rx_gain_1_1); + ar9485_common_rx_gain_1_1); else if (AR_SREV_9550(ah)) { INIT_INI_ARRAY(&ah->iniModesRxGain, ar955x_1p0_common_rx_gain_table); -- cgit v1.2.3-70-g09d2 From 9a6f7347f59f55433ec50578e8d42d305e0d53ae Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Thu, 3 Jan 2013 10:31:19 +0200 Subject: ath9k: Fix Kconfig for ATH9K_HTC Ath9k consists of 2 different sub-modules: ATH9K and ATH9K_HTC. Both uses common Atheros code from ath.ko and need ATH_COMMON. However, while ATH9K selects ATH_COMMON, ATH9K_HTC does not. As result, if ATH9K_HTC is the only Atheros card selected, compilation fails with unresolved symbols. This patch moves ATH_COMMON selection to the common part for both ATH9K and ATH9K_HTC Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index 581913ee6ef..7647ed6b73d 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -2,6 +2,7 @@ config ATH9K_HW tristate config ATH9K_COMMON tristate + select ATH_COMMON config ATH9K_DFS_DEBUGFS def_bool y depends on ATH9K_DEBUGFS && ATH9K_DFS_CERTIFIED @@ -17,7 +18,6 @@ config ATH9K_BTCOEX_SUPPORT config ATH9K tristate "Atheros 802.11n wireless cards support" depends on MAC80211 - select ATH_COMMON select ATH9K_HW select MAC80211_LEDS select LEDS_CLASS -- cgit v1.2.3-70-g09d2 From 2be7d22f062535de59babdb4b5e9de9ff31e817e Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Thu, 20 Dec 2012 13:13:19 -0800 Subject: wireless: add new wil6210 802.11ad 60GHz driver This adds support for the 60 GHz 802.11ad Wilocity card through a new driver, wil6210. Wilocity implemented the firmware, QCA maintains the device driver. Currently supported: - STA: with security - AP: limited to 1 connected STA, security disabled - Monitor: due to a hardware/firmware limitation either control or non-control frames are monitored Using a STA and AP with this drive, one can assemble a fully functional BSS. Throughput of 1.2Gbps is achieved with iperf. The wil6210 cards have on-board flash memory for the firmware, the cards comes pre-flashed and no firmware download is required. For more details see: http://wireless.kernel.org/en/users/Drivers/wil6210 Signed-off-by: Vladimir Kondratiev Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- MAINTAINERS | 8 + drivers/net/wireless/ath/Kconfig | 1 + drivers/net/wireless/ath/Makefile | 1 + drivers/net/wireless/ath/wil6210/Kconfig | 29 + drivers/net/wireless/ath/wil6210/Makefile | 13 + drivers/net/wireless/ath/wil6210/cfg80211.c | 573 ++++++++++++ drivers/net/wireless/ath/wil6210/dbg_hexdump.h | 30 + drivers/net/wireless/ath/wil6210/debugfs.c | 603 +++++++++++++ drivers/net/wireless/ath/wil6210/interrupt.c | 471 ++++++++++ drivers/net/wireless/ath/wil6210/main.c | 407 +++++++++ drivers/net/wireless/ath/wil6210/netdev.c | 157 ++++ drivers/net/wireless/ath/wil6210/pcie_bus.c | 223 +++++ drivers/net/wireless/ath/wil6210/txrx.c | 871 ++++++++++++++++++ drivers/net/wireless/ath/wil6210/txrx.h | 362 ++++++++ drivers/net/wireless/ath/wil6210/wil6210.h | 363 ++++++++ drivers/net/wireless/ath/wil6210/wmi.c | 975 +++++++++++++++++++++ drivers/net/wireless/ath/wil6210/wmi.h | 1116 ++++++++++++++++++++++++ 17 files changed, 6203 insertions(+) create mode 100644 drivers/net/wireless/ath/wil6210/Kconfig create mode 100644 drivers/net/wireless/ath/wil6210/Makefile create mode 100644 drivers/net/wireless/ath/wil6210/cfg80211.c create mode 100644 drivers/net/wireless/ath/wil6210/dbg_hexdump.h create mode 100644 drivers/net/wireless/ath/wil6210/debugfs.c create mode 100644 drivers/net/wireless/ath/wil6210/interrupt.c create mode 100644 drivers/net/wireless/ath/wil6210/main.c create mode 100644 drivers/net/wireless/ath/wil6210/netdev.c create mode 100644 drivers/net/wireless/ath/wil6210/pcie_bus.c create mode 100644 drivers/net/wireless/ath/wil6210/txrx.c create mode 100644 drivers/net/wireless/ath/wil6210/txrx.h create mode 100644 drivers/net/wireless/ath/wil6210/wil6210.h create mode 100644 drivers/net/wireless/ath/wil6210/wmi.c create mode 100644 drivers/net/wireless/ath/wil6210/wmi.h (limited to 'drivers/net/wireless/ath') diff --git a/MAINTAINERS b/MAINTAINERS index 4e2a1f67a1f..16c3506da16 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1353,6 +1353,14 @@ W: http://wireless.kernel.org/en/users/Drivers/ath9k S: Supported F: drivers/net/wireless/ath/ath9k/ +WILOCITY WIL6210 WIRELESS DRIVER +M: Vladimir Kondratiev +L: linux-wireless@vger.kernel.org +L: wil6210@qca.qualcomm.com +S: Supported +W: http://wireless.kernel.org/en/users/Drivers/wil6210 +F: drivers/net/wireless/ath/wil6210/ + CARL9170 LINUX COMMUNITY WIRELESS DRIVER M: Christian Lamparter L: linux-wireless@vger.kernel.org diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index 1a67a4f829f..2c02b4e8409 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -30,5 +30,6 @@ source "drivers/net/wireless/ath/ath9k/Kconfig" source "drivers/net/wireless/ath/carl9170/Kconfig" source "drivers/net/wireless/ath/ath6kl/Kconfig" source "drivers/net/wireless/ath/ar5523/Kconfig" +source "drivers/net/wireless/ath/wil6210/Kconfig" endif diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile index 1e18621326d..97b964ded2b 100644 --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_ATH9K_HW) += ath9k/ obj-$(CONFIG_CARL9170) += carl9170/ obj-$(CONFIG_ATH6KL) += ath6kl/ obj-$(CONFIG_AR5523) += ar5523/ +obj-$(CONFIG_WIL6210) += wil6210/ obj-$(CONFIG_ATH_COMMON) += ath.o diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig new file mode 100644 index 00000000000..bac3d98a0cf --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/Kconfig @@ -0,0 +1,29 @@ +config WIL6210 + tristate "Wilocity 60g WiFi card wil6210 support" + depends on CFG80211 + depends on PCI + default n + ---help--- + This module adds support for wireless adapter based on + wil6210 chip by Wilocity. It supports operation on the + 60 GHz band, covered by the IEEE802.11ad standard. + + http://wireless.kernel.org/en/users/Drivers/wil6210 + + If you choose to build it as a module, it will be called + wil6210 + +config WIL6210_ISR_COR + bool "Use Clear-On-Read mode for ISR registers for wil6210" + depends on WIL6210 + default y + ---help--- + ISR registers on wil6210 chip may operate in either + COR (Clear-On-Read) or W1C (Write-1-to-Clear) mode. + For production code, use COR (say y); is default since + it saves extra target transaction; + For ISR debug, use W1C (say n); is allows to monitor ISR + registers with debugfs. If COR were used, ISR would + self-clear when accessed for debug purposes, it makes + such monitoring impossible. + Say y unless you debug interrupts diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile new file mode 100644 index 00000000000..9396dc9fe3c --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/Makefile @@ -0,0 +1,13 @@ +obj-$(CONFIG_WIL6210) += wil6210.o + +wil6210-objs := main.o +wil6210-objs += netdev.o +wil6210-objs += cfg80211.o +wil6210-objs += pcie_bus.o +wil6210-objs += debugfs.o +wil6210-objs += wmi.o +wil6210-objs += interrupt.o +wil6210-objs += txrx.o + +subdir-ccflags-y += -Werror +subdir-ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c new file mode 100644 index 00000000000..116f4e807ae --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -0,0 +1,573 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wil6210.h" +#include "wmi.h" + +#define CHAN60G(_channel, _flags) { \ + .band = IEEE80211_BAND_60GHZ, \ + .center_freq = 56160 + (2160 * (_channel)), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 40, \ +} + +static struct ieee80211_channel wil_60ghz_channels[] = { + CHAN60G(1, 0), + CHAN60G(2, 0), + CHAN60G(3, 0), +/* channel 4 not supported yet */ +}; + +static struct ieee80211_supported_band wil_band_60ghz = { + .channels = wil_60ghz_channels, + .n_channels = ARRAY_SIZE(wil_60ghz_channels), + .ht_cap = { + .ht_supported = true, + .cap = 0, /* TODO */ + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, /* TODO */ + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, /* TODO */ + .mcs = { + /* MCS 1..12 - SC PHY */ + .rx_mask = {0xfe, 0x1f}, /* 1..12 */ + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, /* TODO */ + }, + }, +}; + +static const struct ieee80211_txrx_stypes +wil_mgmt_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_STATION] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_AP] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_P2P_CLIENT] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_P2P_GO] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, +}; + +static const u32 wil_cipher_suites[] = { + WLAN_CIPHER_SUITE_GCMP, +}; + +int wil_iftype_nl2wmi(enum nl80211_iftype type) +{ + static const struct { + enum nl80211_iftype nl; + enum wmi_network_type wmi; + } __nl2wmi[] = { + {NL80211_IFTYPE_ADHOC, WMI_NETTYPE_ADHOC}, + {NL80211_IFTYPE_STATION, WMI_NETTYPE_INFRA}, + {NL80211_IFTYPE_AP, WMI_NETTYPE_AP}, + {NL80211_IFTYPE_P2P_CLIENT, WMI_NETTYPE_P2P}, + {NL80211_IFTYPE_P2P_GO, WMI_NETTYPE_P2P}, + {NL80211_IFTYPE_MONITOR, WMI_NETTYPE_ADHOC}, /* FIXME */ + }; + uint i; + + for (i = 0; i < ARRAY_SIZE(__nl2wmi); i++) { + if (__nl2wmi[i].nl == type) + return __nl2wmi[i].wmi; + } + + return -EOPNOTSUPP; +} + +static int wil_cfg80211_get_station(struct wiphy *wiphy, + struct net_device *ndev, + u8 *mac, struct station_info *sinfo) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + int rc; + struct wmi_notify_req_cmd cmd = { + .cid = 0, + .interval_usec = 0, + }; + + if (memcmp(mac, wil->dst_addr[0], ETH_ALEN)) + return -ENOENT; + + /* WMI_NOTIFY_REQ_DONE_EVENTID handler fills wil->stats.bf_mcs */ + rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd), + WMI_NOTIFY_REQ_DONE_EVENTID, NULL, 0, 20); + if (rc) + return rc; + + sinfo->generation = wil->sinfo_gen; + + sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; + sinfo->txrate.mcs = wil->stats.bf_mcs; + sinfo->filled |= STATION_INFO_RX_BITRATE; + sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; + sinfo->rxrate.mcs = wil->stats.last_mcs_rx; + + if (test_bit(wil_status_fwconnected, &wil->status)) { + sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->signal = 12; /* TODO: provide real value */ + } + + return 0; +} + +static int wil_cfg80211_change_iface(struct wiphy *wiphy, + struct net_device *ndev, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = wil->wdev; + + switch (type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + break; + case NL80211_IFTYPE_MONITOR: + if (flags) + wil->monitor_flags = *flags; + else + wil->monitor_flags = 0; + + break; + default: + return -EOPNOTSUPP; + } + + wdev->iftype = type; + + return 0; +} + +static int wil_cfg80211_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = wil->wdev; + struct { + struct wmi_start_scan_cmd cmd; + u16 chnl[4]; + } __packed cmd; + uint i, n; + + if (wil->scan_request) { + wil_err(wil, "Already scanning\n"); + return -EAGAIN; + } + + /* check we are client side */ + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + break; + default: + return -EOPNOTSUPP; + + } + + /* FW don't support scan after connection attempt */ + if (test_bit(wil_status_dontscan, &wil->status)) { + wil_err(wil, "Scan after connect attempt not supported\n"); + return -EBUSY; + } + + wil->scan_request = request; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd.num_channels = 0; + n = min(request->n_channels, 4U); + for (i = 0; i < n; i++) { + int ch = request->channels[i]->hw_value; + if (ch == 0) { + wil_err(wil, + "Scan requested for unknown frequency %dMhz\n", + request->channels[i]->center_freq); + continue; + } + /* 0-based channel indexes */ + cmd.cmd.channel_list[cmd.cmd.num_channels++].channel = ch - 1; + wil_dbg(wil, "Scan for ch %d : %d MHz\n", ch, + request->channels[i]->center_freq); + } + + return wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) + + cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0])); +} + +static int wil_cfg80211_connect(struct wiphy *wiphy, + struct net_device *ndev, + struct cfg80211_connect_params *sme) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct cfg80211_bss *bss; + struct wmi_connect_cmd conn; + const u8 *ssid_eid; + const u8 *rsn_eid; + int ch; + int rc = 0; + + bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, + sme->ssid, sme->ssid_len, + WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + if (!bss) { + wil_err(wil, "Unable to find BSS\n"); + return -ENOENT; + } + + ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); + if (!ssid_eid) { + wil_err(wil, "No SSID\n"); + rc = -ENOENT; + goto out; + } + + rsn_eid = sme->ie ? + cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len) : + NULL; + if (rsn_eid) { + if (sme->ie_len > WMI_MAX_IE_LEN) { + rc = -ERANGE; + wil_err(wil, "IE too large (%td bytes)\n", + sme->ie_len); + goto out; + } + /* + * For secure assoc, send: + * (1) WMI_DELETE_CIPHER_KEY_CMD + * (2) WMI_SET_APPIE_CMD + */ + rc = wmi_del_cipher_key(wil, 0, bss->bssid); + if (rc) { + wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD failed\n"); + goto out; + } + /* WMI_SET_APPIE_CMD */ + rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie); + if (rc) { + wil_err(wil, "WMI_SET_APPIE_CMD failed\n"); + goto out; + } + } + + /* WMI_CONNECT_CMD */ + memset(&conn, 0, sizeof(conn)); + switch (bss->capability & 0x03) { + case WLAN_CAPABILITY_DMG_TYPE_AP: + conn.network_type = WMI_NETTYPE_INFRA; + break; + case WLAN_CAPABILITY_DMG_TYPE_PBSS: + conn.network_type = WMI_NETTYPE_P2P; + break; + default: + wil_err(wil, "Unsupported BSS type, capability= 0x%04x\n", + bss->capability); + goto out; + } + if (rsn_eid) { + conn.dot11_auth_mode = WMI_AUTH11_SHARED; + conn.auth_mode = WMI_AUTH_WPA2_PSK; + conn.pairwise_crypto_type = WMI_CRYPT_AES_GCMP; + conn.pairwise_crypto_len = 16; + } else { + conn.dot11_auth_mode = WMI_AUTH11_OPEN; + conn.auth_mode = WMI_AUTH_NONE; + } + + conn.ssid_len = min_t(u8, ssid_eid[1], 32); + memcpy(conn.ssid, ssid_eid+2, conn.ssid_len); + + ch = bss->channel->hw_value; + if (ch == 0) { + wil_err(wil, "BSS at unknown frequency %dMhz\n", + bss->channel->center_freq); + rc = -EOPNOTSUPP; + goto out; + } + conn.channel = ch - 1; + + memcpy(conn.bssid, bss->bssid, 6); + memcpy(conn.dst_mac, bss->bssid, 6); + /* + * FW don't support scan after connection attempt + */ + set_bit(wil_status_dontscan, &wil->status); + + rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn)); + if (rc == 0) { + /* Connect can take lots of time */ + mod_timer(&wil->connect_timer, + jiffies + msecs_to_jiffies(2000)); + } + + out: + cfg80211_put_bss(bss); + + return rc; +} + +static int wil_cfg80211_disconnect(struct wiphy *wiphy, + struct net_device *ndev, + u16 reason_code) +{ + int rc; + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + + rc = wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0); + + return rc; +} + +static int wil_cfg80211_set_channel(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = wil->wdev; + + wdev->preset_chandef = *chandef; + + return 0; +} + +static int wil_cfg80211_add_key(struct wiphy *wiphy, + struct net_device *ndev, + u8 key_index, bool pairwise, + const u8 *mac_addr, + struct key_params *params) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + + /* group key is not used */ + if (!pairwise) + return 0; + + return wmi_add_cipher_key(wil, key_index, mac_addr, + params->key_len, params->key); +} + +static int wil_cfg80211_del_key(struct wiphy *wiphy, + struct net_device *ndev, + u8 key_index, bool pairwise, + const u8 *mac_addr) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + + /* group key is not used */ + if (!pairwise) + return 0; + + return wmi_del_cipher_key(wil, key_index, mac_addr); +} + +/* Need to be present or wiphy_new() will WARN */ +static int wil_cfg80211_set_default_key(struct wiphy *wiphy, + struct net_device *ndev, + u8 key_index, bool unicast, + bool multicast) +{ + return 0; +} + +static int wil_cfg80211_start_ap(struct wiphy *wiphy, + struct net_device *ndev, + struct cfg80211_ap_settings *info) +{ + int rc = 0; + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = ndev->ieee80211_ptr; + struct ieee80211_channel *channel = info->chandef.chan; + struct cfg80211_beacon_data *bcon = &info->beacon; + u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); + + if (!channel) { + wil_err(wil, "AP: No channel???\n"); + return -EINVAL; + } + + wil_dbg(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value, + channel->center_freq, info->privacy ? "secure" : "open"); + print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET, + info->ssid, info->ssid_len); + + rc = wil_reset(wil); + if (rc) + return rc; + + rc = wmi_set_ssid(wil, info->ssid_len, info->ssid); + if (rc) + return rc; + + rc = wmi_set_channel(wil, channel->hw_value); + if (rc) + return rc; + + /* MAC address - pre-requisite for other commands */ + wmi_set_mac_address(wil, ndev->dev_addr); + + /* IE's */ + /* bcon 'head IE's are not relevant for 60g band */ + wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->beacon_ies_len, + bcon->beacon_ies); + wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, bcon->proberesp_ies_len, + bcon->proberesp_ies); + wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, bcon->assocresp_ies_len, + bcon->assocresp_ies); + + wil->secure_pcp = info->privacy; + + rc = wmi_set_bcon(wil, info->beacon_interval, wmi_nettype); + if (rc) + return rc; + + /* Rx VRING. After MAC and beacon */ + rc = wil_rx_init(wil); + + netif_carrier_on(ndev); + + return rc; +} + +static int wil_cfg80211_stop_ap(struct wiphy *wiphy, + struct net_device *ndev) +{ + int rc = 0; + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = ndev->ieee80211_ptr; + u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); + + /* To stop beaconing, set BI to 0 */ + rc = wmi_set_bcon(wil, 0, wmi_nettype); + + return rc; +} + +static struct cfg80211_ops wil_cfg80211_ops = { + .scan = wil_cfg80211_scan, + .connect = wil_cfg80211_connect, + .disconnect = wil_cfg80211_disconnect, + .change_virtual_intf = wil_cfg80211_change_iface, + .get_station = wil_cfg80211_get_station, + .set_monitor_channel = wil_cfg80211_set_channel, + .add_key = wil_cfg80211_add_key, + .del_key = wil_cfg80211_del_key, + .set_default_key = wil_cfg80211_set_default_key, + /* AP mode */ + .start_ap = wil_cfg80211_start_ap, + .stop_ap = wil_cfg80211_stop_ap, +}; + +static void wil_wiphy_init(struct wiphy *wiphy) +{ + /* TODO: set real value */ + wiphy->max_scan_ssids = 10; + wiphy->max_num_pmkids = 0 /* TODO: */; + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MONITOR); + /* TODO: enable P2P when integrated with supplicant: + * BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO) + */ + wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | + WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; + dev_warn(wiphy_dev(wiphy), "%s : flags = 0x%08x\n", + __func__, wiphy->flags); + wiphy->probe_resp_offload = + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; + + wiphy->bands[IEEE80211_BAND_60GHZ] = &wil_band_60ghz; + + /* TODO: figure this out */ + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + + wiphy->cipher_suites = wil_cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites); + wiphy->mgmt_stypes = wil_mgmt_stypes; +} + +struct wireless_dev *wil_cfg80211_init(struct device *dev) +{ + int rc = 0; + struct wireless_dev *wdev; + + wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); + if (!wdev) + return ERR_PTR(-ENOMEM); + + wdev->wiphy = wiphy_new(&wil_cfg80211_ops, + sizeof(struct wil6210_priv)); + if (!wdev->wiphy) { + rc = -ENOMEM; + goto out; + } + + set_wiphy_dev(wdev->wiphy, dev); + wil_wiphy_init(wdev->wiphy); + + rc = wiphy_register(wdev->wiphy); + if (rc < 0) + goto out_failed_reg; + + return wdev; + +out_failed_reg: + wiphy_free(wdev->wiphy); +out: + kfree(wdev); + + return ERR_PTR(rc); +} + +void wil_wdev_free(struct wil6210_priv *wil) +{ + struct wireless_dev *wdev = wil_to_wdev(wil); + + if (!wdev) + return; + + wiphy_unregister(wdev->wiphy); + wiphy_free(wdev->wiphy); + kfree(wdev); +} diff --git a/drivers/net/wireless/ath/wil6210/dbg_hexdump.h b/drivers/net/wireless/ath/wil6210/dbg_hexdump.h new file mode 100644 index 00000000000..6a315ba5aa7 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/dbg_hexdump.h @@ -0,0 +1,30 @@ +#ifndef WIL_DBG_HEXDUMP_H_ +#define WIL_DBG_HEXDUMP_H_ + +#if defined(CONFIG_DYNAMIC_DEBUG) +#define wil_dynamic_hex_dump(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ +do { \ + DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, \ + __builtin_constant_p(prefix_str) ? prefix_str : "hexdump");\ + if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \ + print_hex_dump(KERN_DEBUG, prefix_str, \ + prefix_type, rowsize, groupsize, \ + buf, len, ascii); \ +} while (0) + +#define wil_print_hex_dump_debug(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ + wil_dynamic_hex_dump(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) + +#define print_hex_dump_bytes(prefix_str, prefix_type, buf, len) \ + wil_dynamic_hex_dump(prefix_str, prefix_type, 16, 1, buf, len, true) +#else /* defined(CONFIG_DYNAMIC_DEBUG) */ +#define wil_print_hex_dump_debug(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ + print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) +#endif /* defined(CONFIG_DYNAMIC_DEBUG) */ + +#endif /* WIL_DBG_HEXDUMP_H_ */ diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c new file mode 100644 index 00000000000..65fc9683bfd --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -0,0 +1,603 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, 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 +#include +#include +#include +#include + +#include "wil6210.h" +#include "txrx.h" + +/* Nasty hack. Better have per device instances */ +static u32 mem_addr; +static u32 dbg_txdesc_index; + +static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil, + const char *name, struct vring *vring) +{ + void __iomem *x = wmi_addr(wil, vring->hwtail); + + seq_printf(s, "VRING %s = {\n", name); + seq_printf(s, " pa = 0x%016llx\n", (unsigned long long)vring->pa); + seq_printf(s, " va = 0x%p\n", vring->va); + seq_printf(s, " size = %d\n", vring->size); + seq_printf(s, " swtail = %d\n", vring->swtail); + seq_printf(s, " swhead = %d\n", vring->swhead); + seq_printf(s, " hwtail = [0x%08x] -> ", vring->hwtail); + if (x) + seq_printf(s, "0x%08x\n", ioread32(x)); + else + seq_printf(s, "???\n"); + + if (vring->va && (vring->size < 1025)) { + uint i; + for (i = 0; i < vring->size; i++) { + volatile struct vring_tx_desc *d = &vring->va[i].tx; + if ((i % 64) == 0 && (i != 0)) + seq_printf(s, "\n"); + seq_printf(s, "%s", (d->dma.status & BIT(0)) ? + "S" : (vring->ctx[i] ? "H" : "h")); + } + seq_printf(s, "\n"); + } + seq_printf(s, "}\n"); +} + +static int wil_vring_debugfs_show(struct seq_file *s, void *data) +{ + uint i; + struct wil6210_priv *wil = s->private; + + wil_print_vring(s, wil, "rx", &wil->vring_rx); + + for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { + struct vring *vring = &(wil->vring_tx[i]); + if (vring->va) { + char name[10]; + snprintf(name, sizeof(name), "tx_%2d", i); + wil_print_vring(s, wil, name, vring); + } + } + + return 0; +} + +static int wil_vring_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_vring_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_vring = { + .open = wil_vring_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +static void wil_print_ring(struct seq_file *s, const char *prefix, + void __iomem *off) +{ + struct wil6210_priv *wil = s->private; + struct wil6210_mbox_ring r; + int rsize; + uint i; + + wil_memcpy_fromio_32(&r, off, sizeof(r)); + wil_mbox_ring_le2cpus(&r); + /* + * we just read memory block from NIC. This memory may be + * garbage. Check validity before using it. + */ + rsize = r.size / sizeof(struct wil6210_mbox_ring_desc); + + seq_printf(s, "ring %s = {\n", prefix); + seq_printf(s, " base = 0x%08x\n", r.base); + seq_printf(s, " size = 0x%04x bytes -> %d entries\n", r.size, rsize); + seq_printf(s, " tail = 0x%08x\n", r.tail); + seq_printf(s, " head = 0x%08x\n", r.head); + seq_printf(s, " entry size = %d\n", r.entry_size); + + if (r.size % sizeof(struct wil6210_mbox_ring_desc)) { + seq_printf(s, " ??? size is not multiple of %zd, garbage?\n", + sizeof(struct wil6210_mbox_ring_desc)); + goto out; + } + + if (!wmi_addr(wil, r.base) || + !wmi_addr(wil, r.tail) || + !wmi_addr(wil, r.head)) { + seq_printf(s, " ??? pointers are garbage?\n"); + goto out; + } + + for (i = 0; i < rsize; i++) { + struct wil6210_mbox_ring_desc d; + struct wil6210_mbox_hdr hdr; + size_t delta = i * sizeof(d); + void __iomem *x = wil->csr + HOSTADDR(r.base) + delta; + + wil_memcpy_fromio_32(&d, x, sizeof(d)); + + seq_printf(s, " [%2x] %s %s%s 0x%08x", i, + d.sync ? "F" : "E", + (r.tail - r.base == delta) ? "t" : " ", + (r.head - r.base == delta) ? "h" : " ", + le32_to_cpu(d.addr)); + if (0 == wmi_read_hdr(wil, d.addr, &hdr)) { + u16 len = le16_to_cpu(hdr.len); + seq_printf(s, " -> %04x %04x %04x %02x\n", + le16_to_cpu(hdr.seq), len, + le16_to_cpu(hdr.type), hdr.flags); + if (len <= MAX_MBOXITEM_SIZE) { + int n = 0; + unsigned char printbuf[16 * 3 + 2]; + unsigned char databuf[MAX_MBOXITEM_SIZE]; + void __iomem *src = wmi_buffer(wil, d.addr) + + sizeof(struct wil6210_mbox_hdr); + /* + * No need to check @src for validity - + * we already validated @d.addr while + * reading header + */ + wil_memcpy_fromio_32(databuf, src, len); + while (n < len) { + int l = min(len - n, 16); + hex_dump_to_buffer(databuf + n, l, + 16, 1, printbuf, + sizeof(printbuf), + false); + seq_printf(s, " : %s\n", printbuf); + n += l; + } + } + } else { + seq_printf(s, "\n"); + } + } + out: + seq_printf(s, "}\n"); +} + +static int wil_mbox_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + + wil_print_ring(s, "tx", wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, tx)); + wil_print_ring(s, "rx", wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, rx)); + + return 0; +} + +static int wil_mbox_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_mbox_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_mbox = { + .open = wil_mbox_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +static int wil_debugfs_iomem_x32_set(void *data, u64 val) +{ + iowrite32(val, (void __iomem *)data); + wmb(); /* make sure write propagated to HW */ + + return 0; +} + +static int wil_debugfs_iomem_x32_get(void *data, u64 *val) +{ + *val = ioread32((void __iomem *)data); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, wil_debugfs_iomem_x32_get, + wil_debugfs_iomem_x32_set, "0x%08llx\n"); + +static struct dentry *wil_debugfs_create_iomem_x32(const char *name, + mode_t mode, + struct dentry *parent, + void __iomem *value) +{ + return debugfs_create_file(name, mode, parent, (void * __force)value, + &fops_iomem_x32); +} + +static int wil6210_debugfs_create_ISR(struct wil6210_priv *wil, + const char *name, + struct dentry *parent, u32 off) +{ + struct dentry *d = debugfs_create_dir(name, parent); + + if (IS_ERR_OR_NULL(d)) + return -ENODEV; + + wil_debugfs_create_iomem_x32("ICC", S_IRUGO | S_IWUSR, d, + wil->csr + off); + wil_debugfs_create_iomem_x32("ICR", S_IRUGO | S_IWUSR, d, + wil->csr + off + 4); + wil_debugfs_create_iomem_x32("ICM", S_IRUGO | S_IWUSR, d, + wil->csr + off + 8); + wil_debugfs_create_iomem_x32("ICS", S_IWUSR, d, + wil->csr + off + 12); + wil_debugfs_create_iomem_x32("IMV", S_IRUGO | S_IWUSR, d, + wil->csr + off + 16); + wil_debugfs_create_iomem_x32("IMS", S_IWUSR, d, + wil->csr + off + 20); + wil_debugfs_create_iomem_x32("IMC", S_IWUSR, d, + wil->csr + off + 24); + + return 0; +} + +static int wil6210_debugfs_create_pseudo_ISR(struct wil6210_priv *wil, + struct dentry *parent) +{ + struct dentry *d = debugfs_create_dir("PSEUDO_ISR", parent); + + if (IS_ERR_OR_NULL(d)) + return -ENODEV; + + wil_debugfs_create_iomem_x32("CAUSE", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_PSEUDO_CAUSE)); + wil_debugfs_create_iomem_x32("MASK_SW", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); + wil_debugfs_create_iomem_x32("MASK_FW", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_FW)); + + return 0; +} + +static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil, + struct dentry *parent) +{ + struct dentry *d = debugfs_create_dir("ITR_CNT", parent); + + if (IS_ERR_OR_NULL(d)) + return -ENODEV; + + wil_debugfs_create_iomem_x32("TRSH", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_ITR_CNT_TRSH)); + wil_debugfs_create_iomem_x32("DATA", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_ITR_CNT_DATA)); + wil_debugfs_create_iomem_x32("CTL", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_ITR_CNT_CRL)); + + return 0; +} + +static int wil_memread_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + void __iomem *a = wmi_buffer(wil, cpu_to_le32(mem_addr)); + + if (a) + seq_printf(s, "[0x%08x] = 0x%08x\n", mem_addr, ioread32(a)); + else + seq_printf(s, "[0x%08x] = INVALID\n", mem_addr); + + return 0; +} + +static int wil_memread_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_memread_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_memread = { + .open = wil_memread_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +static int wil_default_open(struct inode *inode, struct file *file) +{ + if (inode->i_private) + file->private_data = inode->i_private; + + return 0; +} + +static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + enum { max_count = 4096 }; + struct debugfs_blob_wrapper *blob = file->private_data; + loff_t pos = *ppos; + size_t available = blob->size; + void *buf; + size_t ret; + + if (pos < 0) + return -EINVAL; + + if (pos >= available || !count) + return 0; + + if (count > available - pos) + count = available - pos; + if (count > max_count) + count = max_count; + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + wil_memcpy_fromio_32(buf, (const volatile void __iomem *)blob->data + + pos, count); + + ret = copy_to_user(user_buf, buf, count); + kfree(buf); + if (ret == count) + return -EFAULT; + + count -= ret; + *ppos = pos + count; + + return count; +} + +static const struct file_operations fops_ioblob = { + .read = wil_read_file_ioblob, + .open = wil_default_open, + .llseek = default_llseek, +}; + +static +struct dentry *wil_debugfs_create_ioblob(const char *name, + mode_t mode, + struct dentry *parent, + struct debugfs_blob_wrapper *blob) +{ + return debugfs_create_file(name, mode, parent, blob, &fops_ioblob); +} +/*---reset---*/ +static ssize_t wil_write_file_reset(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + struct wil6210_priv *wil = file->private_data; + struct net_device *ndev = wil_to_ndev(wil); + + /** + * BUG: + * this code does NOT sync device state with the rest of system + * use with care, debug only!!! + */ + rtnl_lock(); + dev_close(ndev); + ndev->flags &= ~IFF_UP; + rtnl_unlock(); + wil_reset(wil); + + return len; +} + +static const struct file_operations fops_reset = { + .write = wil_write_file_reset, + .open = wil_default_open, +}; +/*---------Tx descriptor------------*/ + +static int wil_txdesc_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + struct vring *vring = &(wil->vring_tx[0]); + + if (!vring->va) { + seq_printf(s, "No Tx VRING\n"); + return 0; + } + + if (dbg_txdesc_index < vring->size) { + volatile struct vring_tx_desc *d = + &(vring->va[dbg_txdesc_index].tx); + volatile u32 *u = (volatile u32 *)d; + struct sk_buff *skb = vring->ctx[dbg_txdesc_index]; + + seq_printf(s, "Tx[%3d] = {\n", dbg_txdesc_index); + seq_printf(s, " MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n", + u[0], u[1], u[2], u[3]); + seq_printf(s, " DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n", + u[4], u[5], u[6], u[7]); + seq_printf(s, " SKB = %p\n", skb); + + if (skb) { + unsigned char printbuf[16 * 3 + 2]; + int i = 0; + int len = skb_headlen(skb); + void *p = skb->data; + + seq_printf(s, " len = %d\n", len); + + while (i < len) { + int l = min(len - i, 16); + hex_dump_to_buffer(p + i, l, 16, 1, printbuf, + sizeof(printbuf), false); + seq_printf(s, " : %s\n", printbuf); + i += l; + } + } + seq_printf(s, "}\n"); + } else { + seq_printf(s, "TxDesc index (%d) >= size (%d)\n", + dbg_txdesc_index, vring->size); + } + + return 0; +} + +static int wil_txdesc_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_txdesc_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_txdesc = { + .open = wil_txdesc_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +/*---------beamforming------------*/ +static int wil_bf_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + seq_printf(s, + "TSF : 0x%016llx\n" + "TxMCS : %d\n" + "Sectors(rx:tx) my %2d:%2d peer %2d:%2d\n", + wil->stats.tsf, wil->stats.bf_mcs, + wil->stats.my_rx_sector, wil->stats.my_tx_sector, + wil->stats.peer_rx_sector, wil->stats.peer_tx_sector); + return 0; +} + +static int wil_bf_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_bf_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_bf = { + .open = wil_bf_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; +/*---------SSID------------*/ +static ssize_t wil_read_file_ssid(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wil6210_priv *wil = file->private_data; + struct wireless_dev *wdev = wil_to_wdev(wil); + + return simple_read_from_buffer(user_buf, count, ppos, + wdev->ssid, wdev->ssid_len); +} + +static ssize_t wil_write_file_ssid(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct wil6210_priv *wil = file->private_data; + struct wireless_dev *wdev = wil_to_wdev(wil); + struct net_device *ndev = wil_to_ndev(wil); + + if (*ppos != 0) { + wil_err(wil, "Unable to set SSID substring from [%d]\n", + (int)*ppos); + return -EINVAL; + } + + if (count > sizeof(wdev->ssid)) { + wil_err(wil, "SSID too long, len = %d\n", (int)count); + return -EINVAL; + } + if (netif_running(ndev)) { + wil_err(wil, "Unable to change SSID on running interface\n"); + return -EINVAL; + } + + wdev->ssid_len = count; + return simple_write_to_buffer(wdev->ssid, wdev->ssid_len, ppos, + buf, count); +} + +static const struct file_operations fops_ssid = { + .read = wil_read_file_ssid, + .write = wil_write_file_ssid, + .open = wil_default_open, +}; + +/*----------------*/ +int wil6210_debugfs_init(struct wil6210_priv *wil) +{ + struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME, + wil_to_wiphy(wil)->debugfsdir); + + if (IS_ERR_OR_NULL(dbg)) + return -ENODEV; + + debugfs_create_file("mbox", S_IRUGO, dbg, wil, &fops_mbox); + debugfs_create_file("vrings", S_IRUGO, dbg, wil, &fops_vring); + debugfs_create_file("txdesc", S_IRUGO, dbg, wil, &fops_txdesc); + debugfs_create_u32("txdesc_index", S_IRUGO | S_IWUSR, dbg, + &dbg_txdesc_index); + debugfs_create_file("bf", S_IRUGO, dbg, wil, &fops_bf); + debugfs_create_file("ssid", S_IRUGO | S_IWUSR, dbg, wil, &fops_ssid); + debugfs_create_u32("secure_pcp", S_IRUGO | S_IWUSR, dbg, + &wil->secure_pcp); + + wil6210_debugfs_create_ISR(wil, "USER_ICR", dbg, + HOSTADDR(RGF_USER_USER_ICR)); + wil6210_debugfs_create_ISR(wil, "DMA_EP_TX_ICR", dbg, + HOSTADDR(RGF_DMA_EP_TX_ICR)); + wil6210_debugfs_create_ISR(wil, "DMA_EP_RX_ICR", dbg, + HOSTADDR(RGF_DMA_EP_RX_ICR)); + wil6210_debugfs_create_ISR(wil, "DMA_EP_MISC_ICR", dbg, + HOSTADDR(RGF_DMA_EP_MISC_ICR)); + wil6210_debugfs_create_pseudo_ISR(wil, dbg); + wil6210_debugfs_create_ITR_CNT(wil, dbg); + + debugfs_create_u32("mem_addr", S_IRUGO | S_IWUSR, dbg, &mem_addr); + debugfs_create_file("mem_val", S_IRUGO, dbg, wil, &fops_memread); + + debugfs_create_file("reset", S_IWUSR, dbg, wil, &fops_reset); + + wil->rgf_blob.data = (void * __force)wil->csr + 0; + wil->rgf_blob.size = 0xa000; + wil_debugfs_create_ioblob("blob_rgf", S_IRUGO, dbg, &wil->rgf_blob); + + wil->fw_code_blob.data = (void * __force)wil->csr + 0x40000; + wil->fw_code_blob.size = 0x40000; + wil_debugfs_create_ioblob("blob_fw_code", S_IRUGO, dbg, + &wil->fw_code_blob); + + wil->fw_data_blob.data = (void * __force)wil->csr + 0x80000; + wil->fw_data_blob.size = 0x8000; + wil_debugfs_create_ioblob("blob_fw_data", S_IRUGO, dbg, + &wil->fw_data_blob); + + wil->fw_peri_blob.data = (void * __force)wil->csr + 0x88000; + wil->fw_peri_blob.size = 0x18000; + wil_debugfs_create_ioblob("blob_fw_peri", S_IRUGO, dbg, + &wil->fw_peri_blob); + + wil->uc_code_blob.data = (void * __force)wil->csr + 0xa0000; + wil->uc_code_blob.size = 0x10000; + wil_debugfs_create_ioblob("blob_uc_code", S_IRUGO, dbg, + &wil->uc_code_blob); + + wil->uc_data_blob.data = (void * __force)wil->csr + 0xb0000; + wil->uc_data_blob.size = 0x4000; + wil_debugfs_create_ioblob("blob_uc_data", S_IRUGO, dbg, + &wil->uc_data_blob); + + return 0; +} + +void wil6210_debugfs_remove(struct wil6210_priv *wil) +{ + debugfs_remove_recursive(wil->debug); + wil->debug = NULL; +} diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c new file mode 100644 index 00000000000..38049da7104 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, 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 + +#include "wil6210.h" + +/** + * Theory of operation: + * + * There is ISR pseudo-cause register, + * dma_rgf->DMA_RGF.PSEUDO_CAUSE.PSEUDO_CAUSE + * Its bits represents OR'ed bits from 3 real ISR registers: + * TX, RX, and MISC. + * + * Registers may be configured to either "write 1 to clear" or + * "clear on read" mode + * + * When handling interrupt, one have to mask/unmask interrupts for the + * real ISR registers, or hardware may malfunction. + * + */ + +#define WIL6210_IRQ_DISABLE (0xFFFFFFFFUL) +#define WIL6210_IMC_RX BIT_DMA_EP_RX_ICR_RX_DONE +#define WIL6210_IMC_TX (BIT_DMA_EP_TX_ICR_TX_DONE | \ + BIT_DMA_EP_TX_ICR_TX_DONE_N(0)) +#define WIL6210_IMC_MISC (ISR_MISC_FW_READY | ISR_MISC_MBOX_EVT) + +#define WIL6210_IRQ_PSEUDO_MASK (u32)(~(BIT_DMA_PSEUDO_CAUSE_RX | \ + BIT_DMA_PSEUDO_CAUSE_TX | \ + BIT_DMA_PSEUDO_CAUSE_MISC)) + +#if defined(CONFIG_WIL6210_ISR_COR) +/* configure to Clear-On-Read mode */ +#define WIL_ICR_ICC_VALUE (0xFFFFFFFFUL) + +static inline void wil_icr_clear(u32 x, void __iomem *addr) +{ + +} +#else /* defined(CONFIG_WIL6210_ISR_COR) */ +/* configure to Write-1-to-Clear mode */ +#define WIL_ICR_ICC_VALUE (0UL) + +static inline void wil_icr_clear(u32 x, void __iomem *addr) +{ + iowrite32(x, addr); +} +#endif /* defined(CONFIG_WIL6210_ISR_COR) */ + +static inline u32 wil_ioread32_and_clear(void __iomem *addr) +{ + u32 x = ioread32(addr); + + wil_icr_clear(x, addr); + + return x; +} + +static void wil6210_mask_irq_tx(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IRQ_DISABLE, wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, IMS)); +} + +static void wil6210_mask_irq_rx(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IRQ_DISABLE, wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, IMS)); +} + +static void wil6210_mask_irq_misc(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IRQ_DISABLE, wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, IMS)); +} + +static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil) +{ + wil_dbg_IRQ(wil, "%s()\n", __func__); + + iowrite32(WIL6210_IRQ_DISABLE, wil->csr + + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); + + clear_bit(wil_status_irqen, &wil->status); +} + +static void wil6210_unmask_irq_tx(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IMC_TX, wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, IMC)); +} + +static void wil6210_unmask_irq_rx(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IMC_RX, wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, IMC)); +} + +static void wil6210_unmask_irq_misc(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IMC_MISC, wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, IMC)); +} + +static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil) +{ + wil_dbg_IRQ(wil, "%s()\n", __func__); + + set_bit(wil_status_irqen, &wil->status); + + iowrite32(WIL6210_IRQ_PSEUDO_MASK, wil->csr + + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); +} + +void wil6210_disable_irq(struct wil6210_priv *wil) +{ + wil_dbg_IRQ(wil, "%s()\n", __func__); + + wil6210_mask_irq_tx(wil); + wil6210_mask_irq_rx(wil); + wil6210_mask_irq_misc(wil); + wil6210_mask_irq_pseudo(wil); +} + +void wil6210_enable_irq(struct wil6210_priv *wil) +{ + wil_dbg_IRQ(wil, "%s()\n", __func__); + + iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICC)); + iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICC)); + iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICC)); + + wil6210_unmask_irq_pseudo(wil); + wil6210_unmask_irq_tx(wil); + wil6210_unmask_irq_rx(wil); + wil6210_unmask_irq_misc(wil); +} + +static irqreturn_t wil6210_irq_rx(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + u32 isr = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICR)); + + wil_dbg_IRQ(wil, "ISR RX 0x%08x\n", isr); + + if (!isr) { + wil_err(wil, "spurious IRQ: RX\n"); + return IRQ_NONE; + } + + wil6210_mask_irq_rx(wil); + + if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) { + wil_dbg_IRQ(wil, "RX done\n"); + isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE; + wil_rx_handle(wil); + } + + if (isr) + wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr); + + wil6210_unmask_irq_rx(wil); + + return IRQ_HANDLED; +} + +static irqreturn_t wil6210_irq_tx(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + u32 isr = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICR)); + + wil_dbg_IRQ(wil, "ISR TX 0x%08x\n", isr); + + if (!isr) { + wil_err(wil, "spurious IRQ: TX\n"); + return IRQ_NONE; + } + + wil6210_mask_irq_tx(wil); + + if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) { + uint i; + wil_dbg_IRQ(wil, "TX done\n"); + isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE; + for (i = 0; i < 24; i++) { + u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i); + if (isr & mask) { + isr &= ~mask; + wil_dbg_IRQ(wil, "TX done(%i)\n", i); + wil_tx_complete(wil, i); + } + } + } + + if (isr) + wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr); + + wil6210_unmask_irq_tx(wil); + + return IRQ_HANDLED; +} + +static irqreturn_t wil6210_irq_misc(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + u32 isr = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICR)); + + wil_dbg_IRQ(wil, "ISR MISC 0x%08x\n", isr); + + if (!isr) { + wil_err(wil, "spurious IRQ: MISC\n"); + return IRQ_NONE; + } + + wil6210_mask_irq_misc(wil); + + if (isr & ISR_MISC_FW_READY) { + wil_dbg_IRQ(wil, "IRQ: FW ready\n"); + /** + * Actual FW ready indicated by the + * WMI_FW_READY_EVENTID + */ + isr &= ~ISR_MISC_FW_READY; + } + + wil->isr_misc = isr; + + if (isr) { + return IRQ_WAKE_THREAD; + } else { + wil6210_unmask_irq_misc(wil); + return IRQ_HANDLED; + } +} + +static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + u32 isr = wil->isr_misc; + + wil_dbg_IRQ(wil, "Thread ISR MISC 0x%08x\n", isr); + + if (isr & ISR_MISC_MBOX_EVT) { + wil_dbg_IRQ(wil, "MBOX event\n"); + wmi_recv_cmd(wil); + isr &= ~ISR_MISC_MBOX_EVT; + } + + if (isr) + wil_err(wil, "un-handled MISC ISR bits 0x%08x\n", isr); + + wil->isr_misc = 0; + + wil6210_unmask_irq_misc(wil); + + return IRQ_HANDLED; +} + +/** + * thread IRQ handler + */ +static irqreturn_t wil6210_thread_irq(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + + wil_dbg_IRQ(wil, "Thread IRQ\n"); + /* Discover real IRQ cause */ + if (wil->isr_misc) + wil6210_irq_misc_thread(irq, cookie); + + wil6210_unmask_irq_pseudo(wil); + + return IRQ_HANDLED; +} + +/* DEBUG + * There is subtle bug in hardware that causes IRQ to raise when it should be + * masked. It is quite rare and hard to debug. + * + * Catch irq issue if it happens and print all I can. + */ +static int wil6210_debug_irq_mask(struct wil6210_priv *wil, u32 pseudo_cause) +{ + if (!test_bit(wil_status_irqen, &wil->status)) { + u32 icm_rx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICM)); + u32 icr_rx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICR)); + u32 imv_rx = ioread32(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, IMV)); + u32 icm_tx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICM)); + u32 icr_tx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICR)); + u32 imv_tx = ioread32(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, IMV)); + u32 icm_misc = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICM)); + u32 icr_misc = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICR)); + u32 imv_misc = ioread32(wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, IMV)); + wil_err(wil, "IRQ when it should be masked: pseudo 0x%08x\n" + "Rx icm:icr:imv 0x%08x 0x%08x 0x%08x\n" + "Tx icm:icr:imv 0x%08x 0x%08x 0x%08x\n" + "Misc icm:icr:imv 0x%08x 0x%08x 0x%08x\n", + pseudo_cause, + icm_rx, icr_rx, imv_rx, + icm_tx, icr_tx, imv_tx, + icm_misc, icr_misc, imv_misc); + + return -EINVAL; + } + + return 0; +} + +static irqreturn_t wil6210_hardirq(int irq, void *cookie) +{ + irqreturn_t rc = IRQ_HANDLED; + struct wil6210_priv *wil = cookie; + u32 pseudo_cause = ioread32(wil->csr + HOSTADDR(RGF_DMA_PSEUDO_CAUSE)); + + /** + * pseudo_cause is Clear-On-Read, no need to ACK + */ + if ((pseudo_cause == 0) || ((pseudo_cause & 0xff) == 0xff)) + return IRQ_NONE; + + /* FIXME: IRQ mask debug */ + if (wil6210_debug_irq_mask(wil, pseudo_cause)) + return IRQ_NONE; + + wil6210_mask_irq_pseudo(wil); + + /* Discover real IRQ cause + * There are 2 possible phases for every IRQ: + * - hard IRQ handler called right here + * - threaded handler called later + * + * Hard IRQ handler reads and clears ISR. + * + * If threaded handler requested, hard IRQ handler + * returns IRQ_WAKE_THREAD and saves ISR register value + * for the threaded handler use. + * + * voting for wake thread - need at least 1 vote + */ + if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_RX) && + (wil6210_irq_rx(irq, cookie) == IRQ_WAKE_THREAD)) + rc = IRQ_WAKE_THREAD; + + if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_TX) && + (wil6210_irq_tx(irq, cookie) == IRQ_WAKE_THREAD)) + rc = IRQ_WAKE_THREAD; + + if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_MISC) && + (wil6210_irq_misc(irq, cookie) == IRQ_WAKE_THREAD)) + rc = IRQ_WAKE_THREAD; + + /* if thread is requested, it will unmask IRQ */ + if (rc != IRQ_WAKE_THREAD) + wil6210_unmask_irq_pseudo(wil); + + wil_dbg_IRQ(wil, "Hard IRQ 0x%08x\n", pseudo_cause); + + return rc; +} + +static int wil6210_request_3msi(struct wil6210_priv *wil, int irq) +{ + int rc; + /* + * IRQ's are in the following order: + * - Tx + * - Rx + * - Misc + */ + + rc = request_irq(irq, wil6210_irq_tx, IRQF_SHARED, + WIL_NAME"_tx", wil); + if (rc) + return rc; + + rc = request_irq(irq + 1, wil6210_irq_rx, IRQF_SHARED, + WIL_NAME"_rx", wil); + if (rc) + goto free0; + + rc = request_threaded_irq(irq + 2, wil6210_irq_misc, + wil6210_irq_misc_thread, + IRQF_SHARED, WIL_NAME"_misc", wil); + if (rc) + goto free1; + + return 0; + /* error branch */ +free1: + free_irq(irq + 1, wil); +free0: + free_irq(irq, wil); + + return rc; +} + +int wil6210_init_irq(struct wil6210_priv *wil, int irq) +{ + int rc; + if (wil->n_msi == 3) + rc = wil6210_request_3msi(wil, irq); + else + rc = request_threaded_irq(irq, wil6210_hardirq, + wil6210_thread_irq, + wil->n_msi ? 0 : IRQF_SHARED, + WIL_NAME, wil); + if (rc) + return rc; + + wil6210_enable_irq(wil); + + return 0; +} + +void wil6210_fini_irq(struct wil6210_priv *wil, int irq) +{ + wil6210_disable_irq(wil); + free_irq(irq, wil); + if (wil->n_msi == 3) { + free_irq(irq + 1, wil); + free_irq(irq + 2, wil); + } +} diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c new file mode 100644 index 00000000000..95fcd361322 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, 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 +#include +#include +#include +#include +#include +#include +#include + +#include "wil6210.h" + +/* + * Due to a hardware issue, + * one has to read/write to/from NIC in 32-bit chunks; + * regular memcpy_fromio and siblings will + * not work on 64-bit platform - it uses 64-bit transactions + * + * Force 32-bit transactions to enable NIC on 64-bit platforms + * + * To avoid byte swap on big endian host, __raw_{read|write}l + * should be used - {read|write}l would swap bytes to provide + * little endian on PCI value in host endianness. + */ +void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, + size_t count) +{ + u32 *d = dst; + const volatile u32 __iomem *s = src; + + /* size_t is unsigned, if (count%4 != 0) it will wrap */ + for (count += 4; count > 4; count -= 4) + *d++ = __raw_readl(s++); +} + +void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, + size_t count) +{ + volatile u32 __iomem *d = dst; + const u32 *s = src; + + for (count += 4; count > 4; count -= 4) + __raw_writel(*s++, d++); +} + +static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) +{ + uint i; + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + + wil_dbg(wil, "%s()\n", __func__); + + wil_link_off(wil); + clear_bit(wil_status_fwconnected, &wil->status); + + switch (wdev->sme_state) { + case CFG80211_SME_CONNECTED: + cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE, + NULL, 0, GFP_KERNEL); + break; + case CFG80211_SME_CONNECTING: + cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); + break; + default: + ; + } + + for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) + wil_vring_fini_tx(wil, i); +} + +static void wil_disconnect_worker(struct work_struct *work) +{ + struct wil6210_priv *wil = container_of(work, + struct wil6210_priv, disconnect_worker); + + _wil6210_disconnect(wil, NULL); +} + +static void wil_connect_timer_fn(ulong x) +{ + struct wil6210_priv *wil = (void *)x; + + wil_dbg(wil, "Connect timeout\n"); + + /* reschedule to thread context - disconnect won't + * run from atomic context + */ + schedule_work(&wil->disconnect_worker); +} + +int wil_priv_init(struct wil6210_priv *wil) +{ + wil_dbg(wil, "%s()\n", __func__); + + mutex_init(&wil->mutex); + mutex_init(&wil->wmi_mutex); + + init_completion(&wil->wmi_ready); + + wil->pending_connect_cid = -1; + setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil); + + INIT_WORK(&wil->wmi_connect_worker, wmi_connect_worker); + INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker); + INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); + + INIT_LIST_HEAD(&wil->pending_wmi_ev); + spin_lock_init(&wil->wmi_ev_lock); + + wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi"); + if (!wil->wmi_wq) + return -EAGAIN; + + wil->wmi_wq_conn = create_singlethread_workqueue(WIL_NAME"_connect"); + if (!wil->wmi_wq_conn) { + destroy_workqueue(wil->wmi_wq); + return -EAGAIN; + } + + /* make shadow copy of registers that should not change on run time */ + wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX, + sizeof(struct wil6210_mbox_ctl)); + wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx); + wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx); + + return 0; +} + +void wil6210_disconnect(struct wil6210_priv *wil, void *bssid) +{ + del_timer_sync(&wil->connect_timer); + _wil6210_disconnect(wil, bssid); +} + +void wil_priv_deinit(struct wil6210_priv *wil) +{ + cancel_work_sync(&wil->disconnect_worker); + wil6210_disconnect(wil, NULL); + wmi_event_flush(wil); + destroy_workqueue(wil->wmi_wq_conn); + destroy_workqueue(wil->wmi_wq); +} + +static void wil_target_reset(struct wil6210_priv *wil) +{ + wil_dbg(wil, "Resetting...\n"); + + /* register write */ +#define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a)) + /* register set = read, OR, write */ +#define S(a, v) iowrite32(ioread32(wil->csr + HOSTADDR(a)) | v, \ + wil->csr + HOSTADDR(a)) + + /* hpal_perst_from_pad_src_n_mask */ + S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6)); + /* car_perst_rst_src_n_mask */ + S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7)); + + W(RGF_USER_MAC_CPU_0, BIT(1)); /* mac_cpu_man_rst */ + W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */ + + msleep(100); + + W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00); + + msleep(100); + + W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); + + W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); + + msleep(2000); + + W(RGF_USER_USER_CPU_0, BIT(0)); /* user_cpu_man_de_rst */ + + msleep(2000); + + wil_dbg(wil, "Reset completed\n"); + +#undef W +#undef S +} + +void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) +{ + le32_to_cpus(&r->base); + le16_to_cpus(&r->entry_size); + le16_to_cpus(&r->size); + le32_to_cpus(&r->tail); + le32_to_cpus(&r->head); +} + +static int wil_wait_for_fw_ready(struct wil6210_priv *wil) +{ + ulong to = msecs_to_jiffies(1000); + ulong left = wait_for_completion_timeout(&wil->wmi_ready, to); + if (0 == left) { + wil_err(wil, "Firmware not ready\n"); + return -ETIME; + } else { + wil_dbg(wil, "FW ready after %d ms\n", + jiffies_to_msecs(to-left)); + } + return 0; +} + +/* + * We reset all the structures, and we reset the UMAC. + * After calling this routine, you're expected to reload + * the firmware. + */ +int wil_reset(struct wil6210_priv *wil) +{ + int rc; + + cancel_work_sync(&wil->disconnect_worker); + wil6210_disconnect(wil, NULL); + + wmi_event_flush(wil); + + flush_workqueue(wil->wmi_wq); + flush_workqueue(wil->wmi_wq_conn); + + wil6210_disable_irq(wil); + wil->status = 0; + + /* TODO: put MAC in reset */ + wil_target_reset(wil); + + /* init after reset */ + wil->pending_connect_cid = -1; + INIT_COMPLETION(wil->wmi_ready); + + /* make shadow copy of registers that should not change on run time */ + wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX, + sizeof(struct wil6210_mbox_ctl)); + wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx); + wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx); + + /* TODO: release MAC reset */ + wil6210_enable_irq(wil); + + /* we just started MAC, wait for FW ready */ + rc = wil_wait_for_fw_ready(wil); + + return rc; +} + + +void wil_link_on(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + + wil_dbg(wil, "%s()\n", __func__); + + netif_carrier_on(ndev); + netif_tx_wake_all_queues(ndev); +} + +void wil_link_off(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + + wil_dbg(wil, "%s()\n", __func__); + + netif_tx_stop_all_queues(ndev); + netif_carrier_off(ndev); +} + +static int __wil_up(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + struct ieee80211_channel *channel = wdev->preset_chandef.chan; + int rc; + int bi; + u16 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); + + rc = wil_reset(wil); + if (rc) + return rc; + + /* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */ + wmi_nettype = wil_iftype_nl2wmi(NL80211_IFTYPE_ADHOC); + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + wil_dbg(wil, "type: STATION\n"); + bi = 0; + ndev->type = ARPHRD_ETHER; + break; + case NL80211_IFTYPE_AP: + wil_dbg(wil, "type: AP\n"); + bi = 100; + ndev->type = ARPHRD_ETHER; + break; + case NL80211_IFTYPE_P2P_CLIENT: + wil_dbg(wil, "type: P2P_CLIENT\n"); + bi = 0; + ndev->type = ARPHRD_ETHER; + break; + case NL80211_IFTYPE_P2P_GO: + wil_dbg(wil, "type: P2P_GO\n"); + bi = 100; + ndev->type = ARPHRD_ETHER; + break; + case NL80211_IFTYPE_MONITOR: + wil_dbg(wil, "type: Monitor\n"); + bi = 0; + ndev->type = ARPHRD_IEEE80211_RADIOTAP; + /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */ + break; + default: + return -EOPNOTSUPP; + } + + /* Apply profile in the following order: */ + /* SSID and channel for the AP */ + switch (wdev->iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + if (wdev->ssid_len == 0) { + wil_err(wil, "SSID not set\n"); + return -EINVAL; + } + wmi_set_ssid(wil, wdev->ssid_len, wdev->ssid); + if (channel) + wmi_set_channel(wil, channel->hw_value); + break; + default: + ; + } + + /* MAC address - pre-requisite for other commands */ + wmi_set_mac_address(wil, ndev->dev_addr); + + /* Set up beaconing if required. */ + rc = wmi_set_bcon(wil, bi, wmi_nettype); + if (rc) + return rc; + + /* Rx VRING. After MAC and beacon */ + wil_rx_init(wil); + + return 0; +} + +int wil_up(struct wil6210_priv *wil) +{ + int rc; + + mutex_lock(&wil->mutex); + rc = __wil_up(wil); + mutex_unlock(&wil->mutex); + + return rc; +} + +static int __wil_down(struct wil6210_priv *wil) +{ + if (wil->scan_request) { + cfg80211_scan_done(wil->scan_request, true); + wil->scan_request = NULL; + } + + wil6210_disconnect(wil, NULL); + wil_rx_fini(wil); + + return 0; +} + +int wil_down(struct wil6210_priv *wil) +{ + int rc; + + mutex_lock(&wil->mutex); + rc = __wil_down(wil); + mutex_unlock(&wil->mutex); + + return rc; +} diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c new file mode 100644 index 00000000000..3068b5cb53a --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, 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 +#include +#include +#include + +#include "wil6210.h" + +static int wil_open(struct net_device *ndev) +{ + struct wil6210_priv *wil = ndev_to_wil(ndev); + + return wil_up(wil); +} + +static int wil_stop(struct net_device *ndev) +{ + struct wil6210_priv *wil = ndev_to_wil(ndev); + + return wil_down(wil); +} + +/* + * AC to queue mapping + * + * AC_VO -> queue 3 + * AC_VI -> queue 2 + * AC_BE -> queue 1 + * AC_BK -> queue 0 + */ +static u16 wil_select_queue(struct net_device *ndev, struct sk_buff *skb) +{ + static const u16 wil_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; + struct wil6210_priv *wil = ndev_to_wil(ndev); + u16 rc; + + skb->priority = cfg80211_classify8021d(skb); + + rc = wil_1d_to_queue[skb->priority]; + + wil_dbg_TXRX(wil, "%s() %d -> %d\n", __func__, (int)skb->priority, + (int)rc); + + return rc; +} + +static const struct net_device_ops wil_netdev_ops = { + .ndo_open = wil_open, + .ndo_stop = wil_stop, + .ndo_start_xmit = wil_start_xmit, + .ndo_select_queue = wil_select_queue, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +void *wil_if_alloc(struct device *dev, void __iomem *csr) +{ + struct net_device *ndev; + struct wireless_dev *wdev; + struct wil6210_priv *wil; + struct ieee80211_channel *ch; + int rc = 0; + + wdev = wil_cfg80211_init(dev); + if (IS_ERR(wdev)) { + dev_err(dev, "wil_cfg80211_init failed\n"); + return wdev; + } + + wil = wdev_to_wil(wdev); + wil->csr = csr; + wil->wdev = wdev; + + rc = wil_priv_init(wil); + if (rc) { + dev_err(dev, "wil_priv_init failed\n"); + goto out_wdev; + } + + wdev->iftype = NL80211_IFTYPE_STATION; /* TODO */ + /* default monitor channel */ + ch = wdev->wiphy->bands[IEEE80211_BAND_60GHZ]->channels; + cfg80211_chandef_create(&wdev->preset_chandef, ch, NL80211_CHAN_NO_HT); + + ndev = alloc_netdev_mqs(0, "wlan%d", ether_setup, WIL6210_TX_QUEUES, 1); + if (!ndev) { + dev_err(dev, "alloc_netdev_mqs failed\n"); + rc = -ENOMEM; + goto out_priv; + } + + ndev->netdev_ops = &wil_netdev_ops; + ndev->ieee80211_ptr = wdev; + SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); + wdev->netdev = ndev; + + wil_link_off(wil); + + return wil; + + out_priv: + wil_priv_deinit(wil); + + out_wdev: + wil_wdev_free(wil); + + return ERR_PTR(rc); +} + +void wil_if_free(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + if (!ndev) + return; + + free_netdev(ndev); + wil_priv_deinit(wil); + wil_wdev_free(wil); +} + +int wil_if_add(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + int rc; + + rc = register_netdev(ndev); + if (rc < 0) { + dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc); + return rc; + } + + wil_link_off(wil); + + return 0; +} + +void wil_if_remove(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + + unregister_netdev(ndev); +} diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c new file mode 100644 index 00000000000..0fc83edd6ba --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, 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 +#include +#include +#include +#include +#include +#include + +#include "wil6210.h" + +static int use_msi = 1; +module_param(use_msi, int, S_IRUGO); +MODULE_PARM_DESC(use_msi, + " Use MSI interrupt: " + "0 - don't, 1 - (default) - single, or 3"); + +/* Bus ops */ +static int wil_if_pcie_enable(struct wil6210_priv *wil) +{ + struct pci_dev *pdev = wil->pdev; + int rc; + + pci_set_master(pdev); + + /* + * how many MSI interrupts to request? + */ + switch (use_msi) { + case 3: + case 1: + case 0: + break; + default: + wil_err(wil, "Invalid use_msi=%d, default to 1\n", + use_msi); + use_msi = 1; + } + wil->n_msi = use_msi; + if (wil->n_msi) { + wil_dbg(wil, "Setup %d MSI interrupts\n", use_msi); + rc = pci_enable_msi_block(pdev, wil->n_msi); + if (rc && (wil->n_msi == 3)) { + wil_err(wil, "3 MSI mode failed, try 1 MSI\n"); + wil->n_msi = 1; + rc = pci_enable_msi_block(pdev, wil->n_msi); + } + if (rc) { + wil_err(wil, "pci_enable_msi failed, use INTx\n"); + wil->n_msi = 0; + } + } else { + wil_dbg(wil, "MSI interrupts disabled, use INTx\n"); + } + + rc = wil6210_init_irq(wil, pdev->irq); + if (rc) + goto stop_master; + + /* need reset here to obtain MAC */ + rc = wil_reset(wil); + if (rc) + goto release_irq; + + return 0; + + release_irq: + wil6210_fini_irq(wil, pdev->irq); + /* safe to call if no MSI */ + pci_disable_msi(pdev); + stop_master: + pci_clear_master(pdev); + return rc; +} + +static int wil_if_pcie_disable(struct wil6210_priv *wil) +{ + struct pci_dev *pdev = wil->pdev; + + pci_clear_master(pdev); + /* disable and release IRQ */ + wil6210_fini_irq(wil, pdev->irq); + /* safe to call if no MSI */ + pci_disable_msi(pdev); + /* TODO: disable HW */ + + return 0; +} + +static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct wil6210_priv *wil; + struct device *dev = &pdev->dev; + void __iomem *csr; + int rc; + + /* check HW */ + dev_info(&pdev->dev, WIL_NAME " device found [%04x:%04x] (rev %x)\n", + (int)pdev->vendor, (int)pdev->device, (int)pdev->revision); + + if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) { + dev_err(&pdev->dev, "Not " WIL_NAME "? " + "BAR0 size is %lu while expecting %lu\n", + (ulong)pci_resource_len(pdev, 0), WIL6210_MEM_SIZE); + return -ENODEV; + } + + rc = pci_enable_device(pdev); + if (rc) { + dev_err(&pdev->dev, "pci_enable_device failed\n"); + return -ENODEV; + } + /* rollback to err_disable_pdev */ + + rc = pci_request_region(pdev, 0, WIL_NAME); + if (rc) { + dev_err(&pdev->dev, "pci_request_region failed\n"); + goto err_disable_pdev; + } + /* rollback to err_release_reg */ + + csr = pci_ioremap_bar(pdev, 0); + if (!csr) { + dev_err(&pdev->dev, "pci_ioremap_bar failed\n"); + rc = -ENODEV; + goto err_release_reg; + } + /* rollback to err_iounmap */ + dev_info(&pdev->dev, "CSR at %pR -> %p\n", &pdev->resource[0], csr); + + wil = wil_if_alloc(dev, csr); + if (IS_ERR(wil)) { + rc = (int)PTR_ERR(wil); + dev_err(dev, "wil_if_alloc failed: %d\n", rc); + goto err_iounmap; + } + /* rollback to if_free */ + + pci_set_drvdata(pdev, wil); + wil->pdev = pdev; + + /* FW should raise IRQ when ready */ + rc = wil_if_pcie_enable(wil); + if (rc) { + wil_err(wil, "Enable device failed\n"); + goto if_free; + } + /* rollback to bus_disable */ + + rc = wil_if_add(wil); + if (rc) { + wil_err(wil, "wil_if_add failed: %d\n", rc); + goto bus_disable; + } + + wil6210_debugfs_init(wil); + + /* check FW is alive */ + wmi_echo(wil); + + return 0; + + bus_disable: + wil_if_pcie_disable(wil); + if_free: + wil_if_free(wil); + err_iounmap: + pci_iounmap(pdev, csr); + err_release_reg: + pci_release_region(pdev, 0); + err_disable_pdev: + pci_disable_device(pdev); + + return rc; +} + +static void wil_pcie_remove(struct pci_dev *pdev) +{ + struct wil6210_priv *wil = pci_get_drvdata(pdev); + + wil6210_debugfs_remove(wil); + wil_if_pcie_disable(wil); + wil_if_remove(wil); + wil_if_free(wil); + pci_iounmap(pdev, wil->csr); + pci_release_region(pdev, 0); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +} + +static DEFINE_PCI_DEVICE_TABLE(wil6210_pcie_ids) = { + { PCI_DEVICE(0x1ae9, 0x0301) }, + { /* end: all zeroes */ }, +}; +MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids); + +static struct pci_driver wil6210_driver = { + .probe = wil_pcie_probe, + .remove = wil_pcie_remove, + .id_table = wil6210_pcie_ids, + .name = WIL_NAME, +}; + +module_pci_driver(wil6210_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Qualcomm Atheros "); +MODULE_DESCRIPTION("Driver for 60g WiFi WIL6210 card"); diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c new file mode 100644 index 00000000000..f29c294413c --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -0,0 +1,871 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, 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 +#include +#include +#include +#include +#include +#include + +#include "wil6210.h" +#include "wmi.h" +#include "txrx.h" + +static bool rtap_include_phy_info; +module_param(rtap_include_phy_info, bool, S_IRUGO); +MODULE_PARM_DESC(rtap_include_phy_info, + " Include PHY info in the radiotap header, default - no"); + +static inline int wil_vring_is_empty(struct vring *vring) +{ + return vring->swhead == vring->swtail; +} + +static inline u32 wil_vring_next_tail(struct vring *vring) +{ + return (vring->swtail + 1) % vring->size; +} + +static inline void wil_vring_advance_head(struct vring *vring, int n) +{ + vring->swhead = (vring->swhead + n) % vring->size; +} + +static inline int wil_vring_is_full(struct vring *vring) +{ + return wil_vring_next_tail(vring) == vring->swhead; +} +/* + * Available space in Tx Vring + */ +static inline int wil_vring_avail_tx(struct vring *vring) +{ + u32 swhead = vring->swhead; + u32 swtail = vring->swtail; + int used = (vring->size + swhead - swtail) % vring->size; + + return vring->size - used - 1; +} + +static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring) +{ + struct device *dev = wil_to_dev(wil); + size_t sz = vring->size * sizeof(vring->va[0]); + uint i; + + BUILD_BUG_ON(sizeof(vring->va[0]) != 32); + + vring->swhead = 0; + vring->swtail = 0; + vring->ctx = kzalloc(vring->size * sizeof(vring->ctx[0]), GFP_KERNEL); + if (!vring->ctx) { + wil_err(wil, "vring_alloc [%d] failed to alloc ctx mem\n", + vring->size); + vring->va = NULL; + return -ENOMEM; + } + /* + * vring->va should be aligned on its size rounded up to power of 2 + * This is granted by the dma_alloc_coherent + */ + vring->va = dma_alloc_coherent(dev, sz, &vring->pa, GFP_KERNEL); + if (!vring->va) { + wil_err(wil, "vring_alloc [%d] failed to alloc DMA mem\n", + vring->size); + kfree(vring->ctx); + vring->ctx = NULL; + return -ENOMEM; + } + /* initially, all descriptors are SW owned + * For Tx and Rx, ownership bit is at the same location, thus + * we can use any + */ + for (i = 0; i < vring->size; i++) { + volatile struct vring_tx_desc *d = &(vring->va[i].tx); + d->dma.status = TX_DMA_STATUS_DU; + } + + wil_dbg(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size, + vring->va, (unsigned long long)vring->pa, vring->ctx); + + return 0; +} + +static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring, + int tx) +{ + struct device *dev = wil_to_dev(wil); + size_t sz = vring->size * sizeof(vring->va[0]); + + while (!wil_vring_is_empty(vring)) { + if (tx) { + volatile struct vring_tx_desc *d = + &vring->va[vring->swtail].tx; + dma_addr_t pa = d->dma.addr_low | + ((u64)d->dma.addr_high << 32); + struct sk_buff *skb = vring->ctx[vring->swtail]; + if (skb) { + dma_unmap_single(dev, pa, d->dma.length, + DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + vring->ctx[vring->swtail] = NULL; + } else { + dma_unmap_page(dev, pa, d->dma.length, + DMA_TO_DEVICE); + } + vring->swtail = wil_vring_next_tail(vring); + } else { /* rx */ + volatile struct vring_rx_desc *d = + &vring->va[vring->swtail].rx; + dma_addr_t pa = d->dma.addr_low | + ((u64)d->dma.addr_high << 32); + struct sk_buff *skb = vring->ctx[vring->swhead]; + dma_unmap_single(dev, pa, d->dma.length, + DMA_FROM_DEVICE); + kfree_skb(skb); + wil_vring_advance_head(vring, 1); + } + } + dma_free_coherent(dev, sz, (void *)vring->va, vring->pa); + kfree(vring->ctx); + vring->pa = 0; + vring->va = NULL; + vring->ctx = NULL; +} + +/** + * Allocate one skb for Rx VRING + * + * Safe to call from IRQ + */ +static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring, + u32 i, int headroom) +{ + struct device *dev = wil_to_dev(wil); + unsigned int sz = RX_BUF_LEN; + volatile struct vring_rx_desc *d = &(vring->va[i].rx); + dma_addr_t pa; + + /* TODO align */ + struct sk_buff *skb = dev_alloc_skb(sz + headroom); + if (unlikely(!skb)) + return -ENOMEM; + + skb_reserve(skb, headroom); + skb_put(skb, sz); + + pa = dma_map_single(dev, skb->data, skb->len, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(dev, pa))) { + kfree_skb(skb); + return -ENOMEM; + } + + d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT; + d->dma.addr_low = lower_32_bits(pa); + d->dma.addr_high = (u16)upper_32_bits(pa); + /* ip_length don't care */ + /* b11 don't care */ + /* error don't care */ + d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ + d->dma.length = sz; + vring->ctx[i] = skb; + + return 0; +} + +/** + * Adds radiotap header + * + * Any error indicated as "Bad FCS" + * + * Vendor data for 04:ce:14-1 (Wilocity-1) consists of: + * - Rx descriptor: 32 bytes + * - Phy info + */ +static void wil_rx_add_radiotap_header(struct wil6210_priv *wil, + struct sk_buff *skb, + volatile struct vring_rx_desc *d) +{ + struct wireless_dev *wdev = wil->wdev; + struct wil6210_rtap { + struct ieee80211_radiotap_header rthdr; + /* fields should be in the order of bits in rthdr.it_present */ + /* flags */ + u8 flags; + /* channel */ + __le16 chnl_freq __aligned(2); + __le16 chnl_flags; + /* MCS */ + u8 mcs_present; + u8 mcs_flags; + u8 mcs_index; + } __packed; + struct wil6210_rtap_vendor { + struct wil6210_rtap rtap; + /* vendor */ + u8 vendor_oui[3] __aligned(2); + u8 vendor_ns; + __le16 vendor_skip; + u8 vendor_data[0]; + } __packed; + struct wil6210_rtap_vendor *rtap_vendor; + int rtap_len = sizeof(struct wil6210_rtap); + int phy_length = 0; /* phy info header size, bytes */ + static char phy_data[128]; + struct ieee80211_channel *ch = wdev->preset_chandef.chan; + + if (rtap_include_phy_info) { + rtap_len = sizeof(*rtap_vendor) + sizeof(*d); + /* calculate additional length */ + if (d->dma.status & RX_DMA_STATUS_PHY_INFO) { + /** + * PHY info starts from 8-byte boundary + * there are 8-byte lines, last line may be partially + * written (HW bug), thus FW configures for last line + * to be excessive. Driver skips this last line. + */ + int len = min_t(int, 8 + sizeof(phy_data), + wil_rxdesc_phy_length(d)); + if (len > 8) { + void *p = skb_tail_pointer(skb); + void *pa = PTR_ALIGN(p, 8); + if (skb_tailroom(skb) >= len + (pa - p)) { + phy_length = len - 8; + memcpy(phy_data, pa, phy_length); + } + } + } + rtap_len += phy_length; + } + + if (skb_headroom(skb) < rtap_len && + pskb_expand_head(skb, rtap_len, 0, GFP_ATOMIC)) { + wil_err(wil, "Unable to expand headrom to %d\n", rtap_len); + return; + } + + rtap_vendor = (void *)skb_push(skb, rtap_len); + memset(rtap_vendor, 0, rtap_len); + + rtap_vendor->rtap.rthdr.it_version = PKTHDR_RADIOTAP_VERSION; + rtap_vendor->rtap.rthdr.it_len = cpu_to_le16(rtap_len); + rtap_vendor->rtap.rthdr.it_present = cpu_to_le32( + (1 << IEEE80211_RADIOTAP_FLAGS) | + (1 << IEEE80211_RADIOTAP_CHANNEL) | + (1 << IEEE80211_RADIOTAP_MCS)); + if (d->dma.status & RX_DMA_STATUS_ERROR) + rtap_vendor->rtap.flags |= IEEE80211_RADIOTAP_F_BADFCS; + + rtap_vendor->rtap.chnl_freq = cpu_to_le16(ch ? ch->center_freq : 58320); + rtap_vendor->rtap.chnl_flags = cpu_to_le16(0); + + rtap_vendor->rtap.mcs_present = IEEE80211_RADIOTAP_MCS_HAVE_MCS; + rtap_vendor->rtap.mcs_flags = 0; + rtap_vendor->rtap.mcs_index = wil_rxdesc_mcs(d); + + if (rtap_include_phy_info) { + rtap_vendor->rtap.rthdr.it_present |= cpu_to_le32(1 << + IEEE80211_RADIOTAP_VENDOR_NAMESPACE); + /* OUI for Wilocity 04:ce:14 */ + rtap_vendor->vendor_oui[0] = 0x04; + rtap_vendor->vendor_oui[1] = 0xce; + rtap_vendor->vendor_oui[2] = 0x14; + rtap_vendor->vendor_ns = 1; + /* Rx descriptor + PHY data */ + rtap_vendor->vendor_skip = cpu_to_le16(sizeof(*d) + + phy_length); + memcpy(rtap_vendor->vendor_data, (void *)d, sizeof(*d)); + memcpy(rtap_vendor->vendor_data + sizeof(*d), phy_data, + phy_length); + } +} + +/* + * Fast swap in place between 2 registers + */ +static void wil_swap_u16(u16 *a, u16 *b) +{ + *a ^= *b; + *b ^= *a; + *a ^= *b; +} + +static void wil_swap_ethaddr(void *data) +{ + struct ethhdr *eth = data; + u16 *s = (u16 *)eth->h_source; + u16 *d = (u16 *)eth->h_dest; + + wil_swap_u16(s++, d++); + wil_swap_u16(s++, d++); + wil_swap_u16(s, d); +} + +/** + * reap 1 frame from @swhead + * + * Safe to call from IRQ + */ +static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, + struct vring *vring) +{ + struct device *dev = wil_to_dev(wil); + struct net_device *ndev = wil_to_ndev(wil); + volatile struct vring_rx_desc *d; + struct sk_buff *skb; + dma_addr_t pa; + unsigned int sz = RX_BUF_LEN; + u8 ftype; + u8 ds_bits; + + if (wil_vring_is_empty(vring)) + return NULL; + + d = &(vring->va[vring->swhead].rx); + if (!(d->dma.status & RX_DMA_STATUS_DU)) { + /* it is not error, we just reached end of Rx done area */ + return NULL; + } + + pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); + skb = vring->ctx[vring->swhead]; + dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE); + skb_trim(skb, d->dma.length); + + wil->stats.last_mcs_rx = wil_rxdesc_mcs(d); + + /* use radiotap header only if required */ + if (ndev->type == ARPHRD_IEEE80211_RADIOTAP) + wil_rx_add_radiotap_header(wil, skb, d); + + wil_dbg_TXRX(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length); + wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_NONE, 32, 4, + (const void *)d, sizeof(*d), false); + + wil_vring_advance_head(vring, 1); + + /* no extra checks if in sniffer mode */ + if (ndev->type != ARPHRD_ETHER) + return skb; + /* + * Non-data frames may be delivered through Rx DMA channel (ex: BAR) + * Driver should recognize it by frame type, that is found + * in Rx descriptor. If type is not data, it is 802.11 frame as is + */ + ftype = wil_rxdesc_ftype(d) << 2; + if (ftype != IEEE80211_FTYPE_DATA) { + wil_dbg_TXRX(wil, "Non-data frame ftype 0x%08x\n", ftype); + /* TODO: process it */ + kfree_skb(skb); + return NULL; + } + + if (skb->len < ETH_HLEN) { + wil_err(wil, "Short frame, len = %d\n", skb->len); + /* TODO: process it (i.e. BAR) */ + kfree_skb(skb); + return NULL; + } + + ds_bits = wil_rxdesc_ds_bits(d); + if (ds_bits == 1) { + /* + * HW bug - in ToDS mode, i.e. Rx on AP side, + * addresses get swapped + */ + wil_swap_ethaddr(skb->data); + } + + return skb; +} + +/** + * allocate and fill up to @count buffers in rx ring + * buffers posted at @swtail + */ +static int wil_rx_refill(struct wil6210_priv *wil, int count) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct vring *v = &wil->vring_rx; + u32 next_tail; + int rc = 0; + int headroom = ndev->type == ARPHRD_IEEE80211_RADIOTAP ? + WIL6210_RTAP_SIZE : 0; + + for (; next_tail = wil_vring_next_tail(v), + (next_tail != v->swhead) && (count-- > 0); + v->swtail = next_tail) { + rc = wil_vring_alloc_skb(wil, v, v->swtail, headroom); + if (rc) { + wil_err(wil, "Error %d in wil_rx_refill[%d]\n", + rc, v->swtail); + break; + } + } + iowrite32(v->swtail, wil->csr + HOSTADDR(v->hwtail)); + + return rc; +} + +/* + * Pass Rx packet to the netif. Update statistics. + */ +static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) +{ + int rc; + unsigned int len = skb->len; + + if (in_interrupt()) + rc = netif_rx(skb); + else + rc = netif_rx_ni(skb); + + if (likely(rc == NET_RX_SUCCESS)) { + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += len; + + } else { + ndev->stats.rx_dropped++; + } +} + +/** + * Proceed all completed skb's from Rx VRING + * + * Safe to call from IRQ + */ +void wil_rx_handle(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct vring *v = &wil->vring_rx; + struct sk_buff *skb; + + if (!v->va) { + wil_err(wil, "Rx IRQ while Rx not yet initialized\n"); + return; + } + wil_dbg_TXRX(wil, "%s()\n", __func__); + while (NULL != (skb = wil_vring_reap_rx(wil, v))) { + wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb_headlen(skb), false); + + skb_orphan(skb); + + if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { + skb->dev = ndev; + skb_reset_mac_header(skb); + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_802_2); + + } else { + skb->protocol = eth_type_trans(skb, ndev); + } + + wil_netif_rx_any(skb, ndev); + } + wil_rx_refill(wil, v->size); +} + +int wil_rx_init(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + struct vring *vring = &wil->vring_rx; + int rc; + struct wmi_cfg_rx_chain_cmd cmd = { + .action = WMI_RX_CHAIN_ADD, + .rx_sw_ring = { + .max_mpdu_size = cpu_to_le16(RX_BUF_LEN), + }, + .mid = 0, /* TODO - what is it? */ + .decap_trans_type = WMI_DECAP_TYPE_802_3, + }; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_cfg_rx_chain_done_event evt; + } __packed evt; + + vring->size = WIL6210_RX_RING_SIZE; + rc = wil_vring_alloc(wil, vring); + if (rc) + return rc; + + cmd.rx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); + cmd.rx_sw_ring.ring_size = cpu_to_le16(vring->size); + if (wdev->iftype == NL80211_IFTYPE_MONITOR) { + struct ieee80211_channel *ch = wdev->preset_chandef.chan; + + cmd.sniffer_cfg.mode = cpu_to_le32(WMI_SNIFFER_ON); + if (ch) + cmd.sniffer_cfg.channel = ch->hw_value - 1; + cmd.sniffer_cfg.phy_info_mode = + cpu_to_le32(ndev->type == ARPHRD_IEEE80211_RADIOTAP); + cmd.sniffer_cfg.phy_support = + cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL) + ? WMI_SNIFFER_CP : WMI_SNIFFER_DP); + } + /* typical time for secure PCP is 840ms */ + rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd), + WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000); + if (rc) + goto err_free; + + vring->hwtail = le32_to_cpu(evt.evt.rx_ring_tail_ptr); + + wil_dbg(wil, "Rx init: status %d tail 0x%08x\n", + le32_to_cpu(evt.evt.status), vring->hwtail); + + rc = wil_rx_refill(wil, vring->size); + if (rc) + goto err_free; + + return 0; + err_free: + wil_vring_free(wil, vring, 0); + + return rc; +} + +void wil_rx_fini(struct wil6210_priv *wil) +{ + struct vring *vring = &wil->vring_rx; + + if (vring->va) { + int rc; + struct wmi_cfg_rx_chain_cmd cmd = { + .action = cpu_to_le32(WMI_RX_CHAIN_DEL), + .rx_sw_ring = { + .max_mpdu_size = cpu_to_le16(RX_BUF_LEN), + }, + }; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_cfg_rx_chain_done_event cfg; + } __packed wmi_rx_cfg_reply; + + rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd), + WMI_CFG_RX_CHAIN_DONE_EVENTID, + &wmi_rx_cfg_reply, sizeof(wmi_rx_cfg_reply), + 100); + wil_vring_free(wil, vring, 0); + } +} + +int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, + int cid, int tid) +{ + int rc; + struct wmi_vring_cfg_cmd cmd = { + .action = cpu_to_le32(WMI_VRING_CMD_ADD), + .vring_cfg = { + .tx_sw_ring = { + .max_mpdu_size = cpu_to_le16(TX_BUF_LEN), + }, + .ringid = id, + .cidxtid = (cid & 0xf) | ((tid & 0xf) << 4), + .encap_trans_type = WMI_VRING_ENC_TYPE_802_3, + .mac_ctrl = 0, + .to_resolution = 0, + .agg_max_wsize = 16, + .schd_params = { + .priority = cpu_to_le16(0), + .timeslot_us = cpu_to_le16(0xfff), + }, + }, + }; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_vring_cfg_done_event cmd; + } __packed reply; + struct vring *vring = &wil->vring_tx[id]; + + if (vring->va) { + wil_err(wil, "Tx ring [%d] already allocated\n", id); + rc = -EINVAL; + goto out; + } + + vring->size = size; + rc = wil_vring_alloc(wil, vring); + if (rc) + goto out; + + cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); + cmd.vring_cfg.tx_sw_ring.ring_size = cpu_to_le16(vring->size); + + rc = wmi_call(wil, WMI_VRING_CFG_CMDID, &cmd, sizeof(cmd), + WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100); + if (rc) + goto out_free; + + if (reply.cmd.status != WMI_VRING_CFG_SUCCESS) { + wil_err(wil, "Tx config failed, status 0x%02x\n", + reply.cmd.status); + goto out_free; + } + vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr); + + return 0; + out_free: + wil_vring_free(wil, vring, 1); + out: + + return rc; +} + +void wil_vring_fini_tx(struct wil6210_priv *wil, int id) +{ + struct vring *vring = &wil->vring_tx[id]; + + if (!vring->va) + return; + + wil_vring_free(wil, vring, 1); +} + +static struct vring *wil_find_tx_vring(struct wil6210_priv *wil, + struct sk_buff *skb) +{ + struct vring *v = &wil->vring_tx[0]; + + if (v->va) + return v; + + return NULL; +} + +static int wil_tx_desc_map(volatile struct vring_tx_desc *d, + dma_addr_t pa, u32 len) +{ + d->dma.addr_low = lower_32_bits(pa); + d->dma.addr_high = (u16)upper_32_bits(pa); + d->dma.ip_length = 0; + /* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/ + d->dma.b11 = 0/*14 | BIT(7)*/; + d->dma.error = 0; + d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ + d->dma.length = len; + d->dma.d0 = 0; + d->mac.d[0] = 0; + d->mac.d[1] = 0; + d->mac.d[2] = 0; + d->mac.ucode_cmd = 0; + /* use dst index 0 */ + d->mac.d[1] |= BIT(MAC_CFG_DESC_TX_1_DST_INDEX_EN_POS) | + (0 << MAC_CFG_DESC_TX_1_DST_INDEX_POS); + /* translation type: 0 - bypass; 1 - 802.3; 2 - native wifi */ + d->mac.d[2] = BIT(MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS) | + (1 << MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS); + + return 0; +} + +static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, + struct sk_buff *skb) +{ + struct device *dev = wil_to_dev(wil); + volatile struct vring_tx_desc *d; + u32 swhead = vring->swhead; + int avail = wil_vring_avail_tx(vring); + int nr_frags = skb_shinfo(skb)->nr_frags; + uint f; + int vring_index = vring - wil->vring_tx; + uint i = swhead; + dma_addr_t pa; + + wil_dbg_TXRX(wil, "%s()\n", __func__); + + if (avail < vring->size/8) + netif_tx_stop_all_queues(wil_to_ndev(wil)); + if (avail < 1 + nr_frags) { + wil_err(wil, "Tx ring full. No space for %d fragments\n", + 1 + nr_frags); + return -ENOMEM; + } + d = &(vring->va[i].tx); + + /* FIXME FW can accept only unicast frames for the peer */ + memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN); + + pa = dma_map_single(dev, skb->data, + skb_headlen(skb), DMA_TO_DEVICE); + + wil_dbg_TXRX(wil, "Tx skb %d bytes %p -> %#08llx\n", skb_headlen(skb), + skb->data, (unsigned long long)pa); + wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb_headlen(skb), false); + + if (unlikely(dma_mapping_error(dev, pa))) + return -EINVAL; + /* 1-st segment */ + wil_tx_desc_map(d, pa, skb_headlen(skb)); + d->mac.d[2] |= ((nr_frags + 1) << + MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS); + /* middle segments */ + for (f = 0; f < nr_frags; f++) { + const struct skb_frag_struct *frag = + &skb_shinfo(skb)->frags[f]; + int len = skb_frag_size(frag); + i = (swhead + f + 1) % vring->size; + d = &(vring->va[i].tx); + pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag), + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev, pa))) + goto dma_error; + wil_tx_desc_map(d, pa, len); + vring->ctx[i] = NULL; + } + /* for the last seg only */ + d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS); + d->dma.d0 |= BIT(9); /* BUG: undocumented bit */ + d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS); + d->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_0_QID_POS); + + wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_NONE, 32, 4, + (const void *)d, sizeof(*d), false); + + /* advance swhead */ + wil_vring_advance_head(vring, nr_frags + 1); + wil_dbg_TXRX(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead); + iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail)); + /* hold reference to skb + * to prevent skb release before accounting + * in case of immediate "tx done" + */ + vring->ctx[i] = skb_get(skb); + + return 0; + dma_error: + /* unmap what we have mapped */ + /* Note: increment @f to operate with positive index */ + for (f++; f > 0; f--) { + i = (swhead + f) % vring->size; + d = &(vring->va[i].tx); + d->dma.status = TX_DMA_STATUS_DU; + pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); + if (vring->ctx[i]) + dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); + else + dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); + } + + return -EINVAL; +} + + +netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct wil6210_priv *wil = ndev_to_wil(ndev); + struct vring *vring; + int rc; + + wil_dbg_TXRX(wil, "%s()\n", __func__); + if (!test_bit(wil_status_fwready, &wil->status)) { + wil_err(wil, "FW not ready\n"); + goto drop; + } + if (!test_bit(wil_status_fwconnected, &wil->status)) { + wil_err(wil, "FW not connected\n"); + goto drop; + } + if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { + wil_err(wil, "Xmit in monitor mode not supported\n"); + goto drop; + } + if (skb->protocol == cpu_to_be16(ETH_P_PAE)) { + rc = wmi_tx_eapol(wil, skb); + } else { + /* find vring */ + vring = wil_find_tx_vring(wil, skb); + if (!vring) { + wil_err(wil, "No Tx VRING available\n"); + goto drop; + } + /* set up vring entry */ + rc = wil_tx_vring(wil, vring, skb); + } + switch (rc) { + case 0: + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += skb->len; + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + case -ENOMEM: + return NETDEV_TX_BUSY; + default: + ; /* goto drop; */ + break; + } + drop: + netif_tx_stop_all_queues(ndev); + ndev->stats.tx_dropped++; + dev_kfree_skb_any(skb); + + return NET_XMIT_DROP; +} + +/** + * Clean up transmitted skb's from the Tx VRING + * + * Safe to call from IRQ + */ +void wil_tx_complete(struct wil6210_priv *wil, int ringid) +{ + struct device *dev = wil_to_dev(wil); + struct vring *vring = &wil->vring_tx[ringid]; + + if (!vring->va) { + wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid); + return; + } + + wil_dbg_TXRX(wil, "%s(%d)\n", __func__, ringid); + + while (!wil_vring_is_empty(vring)) { + volatile struct vring_tx_desc *d = &vring->va[vring->swtail].tx; + dma_addr_t pa; + struct sk_buff *skb; + if (!(d->dma.status & TX_DMA_STATUS_DU)) + break; + + wil_dbg_TXRX(wil, + "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n", + vring->swtail, d->dma.length, d->dma.status, + d->dma.error); + wil_hex_dump_TXRX("TxC ", DUMP_PREFIX_NONE, 32, 4, + (const void *)d, sizeof(*d), false); + + pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); + skb = vring->ctx[vring->swtail]; + if (skb) { + dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + vring->ctx[vring->swtail] = NULL; + } else { + dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); + } + d->dma.addr_low = 0; + d->dma.addr_high = 0; + d->dma.length = 0; + d->dma.status = TX_DMA_STATUS_DU; + vring->swtail = wil_vring_next_tail(vring); + } + if (wil_vring_avail_tx(vring) > vring->size/4) + netif_tx_wake_all_queues(wil_to_ndev(wil)); +} diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h new file mode 100644 index 00000000000..45a61f597c5 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, 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. + */ + +#ifndef WIL6210_TXRX_H +#define WIL6210_TXRX_H + +#define BUF_SW_OWNED (1) +#define BUF_HW_OWNED (0) + +/* size of max. Rx packet */ +#define RX_BUF_LEN (2048) +#define TX_BUF_LEN (2048) +/* how many bytes to reserve for rtap header? */ +#define WIL6210_RTAP_SIZE (128) + +/* Tx/Rx path */ +/* + * Tx descriptor - MAC part + * [dword 0] + * bit 0.. 9 : lifetime_expiry_value:10 + * bit 10 : interrup_en:1 + * bit 11 : status_en:1 + * bit 12..13 : txss_override:2 + * bit 14 : timestamp_insertion:1 + * bit 15 : duration_preserve:1 + * bit 16..21 : reserved0:6 + * bit 22..26 : mcs_index:5 + * bit 27 : mcs_en:1 + * bit 28..29 : reserved1:2 + * bit 30 : reserved2:1 + * bit 31 : sn_preserved:1 + * [dword 1] + * bit 0.. 3 : pkt_mode:4 + * bit 4 : pkt_mode_en:1 + * bit 5.. 7 : reserved0:3 + * bit 8..13 : reserved1:6 + * bit 14 : reserved2:1 + * bit 15 : ack_policy_en:1 + * bit 16..19 : dst_index:4 + * bit 20 : dst_index_en:1 + * bit 21..22 : ack_policy:2 + * bit 23 : lifetime_en:1 + * bit 24..30 : max_retry:7 + * bit 31 : max_retry_en:1 + * [dword 2] + * bit 0.. 7 : num_of_descriptors:8 + * bit 8..17 : reserved:10 + * bit 18..19 : l2_translation_type:2 + * bit 20 : snap_hdr_insertion_en:1 + * bit 21 : vlan_removal_en:1 + * bit 22..31 : reserved0:10 + * [dword 3] + * bit 0.. 31: ucode_cmd:32 + */ +struct vring_tx_mac { + u32 d[3]; + u32 ucode_cmd; +} __packed; + +/* TX MAC Dword 0 */ +#define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_POS 0 +#define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_LEN 10 +#define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_MSK 0x3FF + +#define MAC_CFG_DESC_TX_0_INTERRUP_EN_POS 10 +#define MAC_CFG_DESC_TX_0_INTERRUP_EN_LEN 1 +#define MAC_CFG_DESC_TX_0_INTERRUP_EN_MSK 0x400 + +#define MAC_CFG_DESC_TX_0_STATUS_EN_POS 11 +#define MAC_CFG_DESC_TX_0_STATUS_EN_LEN 1 +#define MAC_CFG_DESC_TX_0_STATUS_EN_MSK 0x800 + +#define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_POS 12 +#define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_LEN 2 +#define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_MSK 0x3000 + +#define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_POS 14 +#define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_LEN 1 +#define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_MSK 0x4000 + +#define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_POS 15 +#define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_LEN 1 +#define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_MSK 0x8000 + +#define MAC_CFG_DESC_TX_0_MCS_INDEX_POS 22 +#define MAC_CFG_DESC_TX_0_MCS_INDEX_LEN 5 +#define MAC_CFG_DESC_TX_0_MCS_INDEX_MSK 0x7C00000 + +#define MAC_CFG_DESC_TX_0_MCS_EN_POS 27 +#define MAC_CFG_DESC_TX_0_MCS_EN_LEN 1 +#define MAC_CFG_DESC_TX_0_MCS_EN_MSK 0x8000000 + +#define MAC_CFG_DESC_TX_0_SN_PRESERVED_POS 31 +#define MAC_CFG_DESC_TX_0_SN_PRESERVED_LEN 1 +#define MAC_CFG_DESC_TX_0_SN_PRESERVED_MSK 0x80000000 + +/* TX MAC Dword 1 */ +#define MAC_CFG_DESC_TX_1_PKT_MODE_POS 0 +#define MAC_CFG_DESC_TX_1_PKT_MODE_LEN 4 +#define MAC_CFG_DESC_TX_1_PKT_MODE_MSK 0xF + +#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_POS 4 +#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_MSK 0x10 + +#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_POS 15 +#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_MSK 0x8000 + +#define MAC_CFG_DESC_TX_1_DST_INDEX_POS 16 +#define MAC_CFG_DESC_TX_1_DST_INDEX_LEN 4 +#define MAC_CFG_DESC_TX_1_DST_INDEX_MSK 0xF0000 + +#define MAC_CFG_DESC_TX_1_DST_INDEX_EN_POS 20 +#define MAC_CFG_DESC_TX_1_DST_INDEX_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_DST_INDEX_EN_MSK 0x100000 + +#define MAC_CFG_DESC_TX_1_ACK_POLICY_POS 21 +#define MAC_CFG_DESC_TX_1_ACK_POLICY_LEN 2 +#define MAC_CFG_DESC_TX_1_ACK_POLICY_MSK 0x600000 + +#define MAC_CFG_DESC_TX_1_LIFETIME_EN_POS 23 +#define MAC_CFG_DESC_TX_1_LIFETIME_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_LIFETIME_EN_MSK 0x800000 + +#define MAC_CFG_DESC_TX_1_MAX_RETRY_POS 24 +#define MAC_CFG_DESC_TX_1_MAX_RETRY_LEN 7 +#define MAC_CFG_DESC_TX_1_MAX_RETRY_MSK 0x7F000000 + +#define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_POS 31 +#define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_MSK 0x80000000 + +/* TX MAC Dword 2 */ +#define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS 0 +#define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_LEN 8 +#define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_MSK 0xFF + +#define MAC_CFG_DESC_TX_2_RESERVED_POS 8 +#define MAC_CFG_DESC_TX_2_RESERVED_LEN 10 +#define MAC_CFG_DESC_TX_2_RESERVED_MSK 0x3FF00 + +#define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS 18 +#define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_LEN 2 +#define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_MSK 0xC0000 + +#define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS 20 +#define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_LEN 1 +#define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_MSK 0x100000 + +#define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_POS 21 +#define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_LEN 1 +#define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_MSK 0x200000 + +/* TX MAC Dword 3 */ +#define MAC_CFG_DESC_TX_3_UCODE_CMD_POS 0 +#define MAC_CFG_DESC_TX_3_UCODE_CMD_LEN 32 +#define MAC_CFG_DESC_TX_3_UCODE_CMD_MSK 0xFFFFFFFF + +/* TX DMA Dword 0 */ +#define DMA_CFG_DESC_TX_0_L4_LENGTH_POS 0 +#define DMA_CFG_DESC_TX_0_L4_LENGTH_LEN 8 +#define DMA_CFG_DESC_TX_0_L4_LENGTH_MSK 0xFF + +#define DMA_CFG_DESC_TX_0_CMD_EOP_POS 8 +#define DMA_CFG_DESC_TX_0_CMD_EOP_LEN 1 +#define DMA_CFG_DESC_TX_0_CMD_EOP_MSK 0x100 + +#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS 10 +#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_LEN 1 +#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_MSK 0x400 + +#define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_POS 11 +#define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_LEN 2 +#define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_MSK 0x1800 + +#define DMA_CFG_DESC_TX_0_TCP_SEG_EN_POS 13 +#define DMA_CFG_DESC_TX_0_TCP_SEG_EN_LEN 1 +#define DMA_CFG_DESC_TX_0_TCP_SEG_EN_MSK 0x2000 + +#define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_POS 14 +#define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_LEN 1 +#define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_MSK 0x4000 + +#define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_POS 15 +#define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_LEN 1 +#define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_MSK 0x8000 + +#define DMA_CFG_DESC_TX_0_QID_POS 16 +#define DMA_CFG_DESC_TX_0_QID_LEN 5 +#define DMA_CFG_DESC_TX_0_QID_MSK 0x1F0000 + +#define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_POS 21 +#define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_LEN 1 +#define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_MSK 0x200000 + +#define DMA_CFG_DESC_TX_0_L4_TYPE_POS 30 +#define DMA_CFG_DESC_TX_0_L4_TYPE_LEN 2 +#define DMA_CFG_DESC_TX_0_L4_TYPE_MSK 0xC0000000 + + +#define TX_DMA_STATUS_DU BIT(0) + +struct vring_tx_dma { + u32 d0; + u32 addr_low; + u16 addr_high; + u8 ip_length; + u8 b11; /* 0..6: mac_length; 7:ip_version */ + u8 error; /* 0..2: err; 3..7: reserved; */ + u8 status; /* 0: used; 1..7; reserved */ + u16 length; +} __packed; + +/* + * Rx descriptor - MAC part + * [dword 0] + * bit 0.. 3 : tid:4 The QoS (b3-0) TID Field + * bit 4.. 6 : connection_id:3 :The Source index that was found during + * Parsing the TA. This field is used to define the source of the packet + * bit 7 : reserved:1 + * bit 8.. 9 : mac_id:2 : The MAC virtual Ring number (always zero) + * bit 10..11 : frame_type:2 : The FC Control (b3-2) - MPDU Type + * (management, data, control and extension) + * bit 12..15 : frame_subtype:4 : The FC Control (b7-4) - Frame Subtype + * bit 16..27 : seq_number:12 The received Sequence number field + * bit 28..31 : extended:4 extended subtype + * [dword 1] + * bit 0.. 3 : reserved + * bit 4.. 5 : key_id:2 + * bit 6 : decrypt_bypass:1 + * bit 7 : security:1 + * bit 8.. 9 : ds_bits:2 + * bit 10 : a_msdu_present:1 from qos header + * bit 11 : a_msdu_type:1 from qos header + * bit 12 : a_mpdu:1 part of AMPDU aggregation + * bit 13 : broadcast:1 + * bit 14 : mutlicast:1 + * bit 15 : reserved:1 + * bit 16..20 : rx_mac_qid:5 The Queue Identifier that the packet + * is received from + * bit 21..24 : mcs:4 + * bit 25..28 : mic_icr:4 + * bit 29..31 : reserved:3 + * [dword 2] + * bit 0.. 2 : time_slot:3 The timeslot that the MPDU is received + * bit 3 : fc_protocol_ver:1 The FC Control (b0) - Protocol Version + * bit 4 : fc_order:1 The FC Control (b15) -Order + * bit 5.. 7 : qos_ack_policy:3 The QoS (b6-5) ack policy Field + * bit 8 : esop:1 The QoS (b4) ESOP field + * bit 9 : qos_rdg_more_ppdu:1 The QoS (b9) RDG field + * bit 10..14 : qos_reserved:5 The QoS (b14-10) Reserved field + * bit 15 : qos_ac_constraint:1 + * bit 16..31 : pn_15_0:16 low 2 bytes of PN + * [dword 3] + * bit 0..31 : pn_47_16:32 high 4 bytes of PN + */ +struct vring_rx_mac { + u32 d0; + u32 d1; + u16 w4; + u16 pn_15_0; + u32 pn_47_16; +} __packed; + +/* + * Rx descriptor - DMA part + * [dword 0] + * bit 0.. 7 : l4_length:8 layer 4 length + * bit 8.. 9 : reserved:2 + * bit 10 : cmd_dma_it:1 + * bit 11..15 : reserved:5 + * bit 16..29 : phy_info_length:14 + * bit 30..31 : l4_type:2 valid if the L4I bit is set in the status field + * [dword 1] + * bit 0..31 : addr_low:32 The payload buffer low address + * [dword 2] + * bit 0..15 : addr_high:16 The payload buffer high address + * bit 16..23 : ip_length:8 + * bit 24..30 : mac_length:7 + * bit 31 : ip_version:1 + * [dword 3] + * [byte 12] error + * [byte 13] status + * bit 0 : du:1 + * bit 1 : eop:1 + * bit 2 : error:1 + * bit 3 : mi:1 + * bit 4 : l3_identified:1 + * bit 5 : l4_identified:1 + * bit 6 : phy_info_included:1 + * bit 7 : reserved:1 + * [word 7] length + * + */ + +#define RX_DMA_D0_CMD_DMA_IT BIT(10) + +#define RX_DMA_STATUS_DU BIT(0) +#define RX_DMA_STATUS_ERROR BIT(2) +#define RX_DMA_STATUS_PHY_INFO BIT(6) + +struct vring_rx_dma { + u32 d0; + u32 addr_low; + u16 addr_high; + u8 ip_length; + u8 b11; + u8 error; + u8 status; + u16 length; +} __packed; + +struct vring_tx_desc { + struct vring_tx_mac mac; + struct vring_tx_dma dma; +} __packed; + +struct vring_rx_desc { + struct vring_rx_mac mac; + struct vring_rx_dma dma; +} __packed; + +union vring_desc { + struct vring_tx_desc tx; + struct vring_rx_desc rx; +} __packed; + +static inline int wil_rxdesc_phy_length(volatile struct vring_rx_desc *d) +{ + return WIL_GET_BITS(d->dma.d0, 16, 29); +} + +static inline int wil_rxdesc_mcs(volatile struct vring_rx_desc *d) +{ + return WIL_GET_BITS(d->mac.d1, 21, 24); +} + +static inline int wil_rxdesc_ds_bits(volatile struct vring_rx_desc *d) +{ + return WIL_GET_BITS(d->mac.d1, 8, 9); +} + +static inline int wil_rxdesc_ftype(volatile struct vring_rx_desc *d) +{ + return WIL_GET_BITS(d->mac.d0, 10, 11); +} + +#endif /* WIL6210_TXRX_H */ diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h new file mode 100644 index 00000000000..9bcfffa4006 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, 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. + */ + +#ifndef __WIL6210_H__ +#define __WIL6210_H__ + +#include +#include +#include + +#include "dbg_hexdump.h" + +#define WIL_NAME "wil6210" + +/** + * extract bits [@b0:@b1] (inclusive) from the value @x + * it should be @b0 <= @b1, or result is incorrect + */ +static inline u32 WIL_GET_BITS(u32 x, int b0, int b1) +{ + return (x >> b0) & ((1 << (b1 - b0 + 1)) - 1); +} + +#define WIL6210_MEM_SIZE (2*1024*1024UL) + +#define WIL6210_TX_QUEUES (4) + +#define WIL6210_RX_RING_SIZE (128) +#define WIL6210_TX_RING_SIZE (128) +#define WIL6210_MAX_TX_RINGS (24) + +/* Hardware definitions begin */ + +/* + * Mapping + * RGF File | Host addr | FW addr + * | | + * user_rgf | 0x000000 | 0x880000 + * dma_rgf | 0x001000 | 0x881000 + * pcie_rgf | 0x002000 | 0x882000 + * | | + */ + +/* Where various structures placed in host address space */ +#define WIL6210_FW_HOST_OFF (0x880000UL) + +#define HOSTADDR(fwaddr) (fwaddr - WIL6210_FW_HOST_OFF) + +/* + * Interrupt control registers block + * + * each interrupt controlled by the same bit in all registers + */ +struct RGF_ICR { + u32 ICC; /* Cause Control, RW: 0 - W1C, 1 - COR */ + u32 ICR; /* Cause, W1C/COR depending on ICC */ + u32 ICM; /* Cause masked (ICR & ~IMV), W1C/COR depending on ICC */ + u32 ICS; /* Cause Set, WO */ + u32 IMV; /* Mask, RW+S/C */ + u32 IMS; /* Mask Set, write 1 to set */ + u32 IMC; /* Mask Clear, write 1 to clear */ +} __packed; + +/* registers - FW addresses */ +#define RGF_USER_USER_SCRATCH_PAD (0x8802bc) +#define RGF_USER_USER_ICR (0x880b4c) /* struct RGF_ICR */ + #define BIT_USER_USER_ICR_SW_INT_2 BIT(18) +#define RGF_USER_CLKS_CTL_SW_RST_MASK_0 (0x880b14) +#define RGF_USER_MAC_CPU_0 (0x8801fc) +#define RGF_USER_USER_CPU_0 (0x8801e0) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_0 (0x880b04) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_1 (0x880b08) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_2 (0x880b0c) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_3 (0x880b10) + +#define RGF_DMA_PSEUDO_CAUSE (0x881c68) +#define RGF_DMA_PSEUDO_CAUSE_MASK_SW (0x881c6c) +#define RGF_DMA_PSEUDO_CAUSE_MASK_FW (0x881c70) + #define BIT_DMA_PSEUDO_CAUSE_RX BIT(0) + #define BIT_DMA_PSEUDO_CAUSE_TX BIT(1) + #define BIT_DMA_PSEUDO_CAUSE_MISC BIT(2) + +#define RGF_DMA_EP_TX_ICR (0x881bb4) /* struct RGF_ICR */ + #define BIT_DMA_EP_TX_ICR_TX_DONE BIT(0) + #define BIT_DMA_EP_TX_ICR_TX_DONE_N(n) BIT(n+1) /* n = [0..23] */ +#define RGF_DMA_EP_RX_ICR (0x881bd0) /* struct RGF_ICR */ + #define BIT_DMA_EP_RX_ICR_RX_DONE BIT(0) +#define RGF_DMA_EP_MISC_ICR (0x881bec) /* struct RGF_ICR */ + #define BIT_DMA_EP_MISC_ICR_RX_HTRSH BIT(0) + #define BIT_DMA_EP_MISC_ICR_TX_NO_ACT BIT(1) + #define BIT_DMA_EP_MISC_ICR_FW_INT0 BIT(28) + #define BIT_DMA_EP_MISC_ICR_FW_INT1 BIT(29) + +/* Interrupt moderation control */ +#define RGF_DMA_ITR_CNT_TRSH (0x881c5c) +#define RGF_DMA_ITR_CNT_DATA (0x881c60) +#define RGF_DMA_ITR_CNT_CRL (0x881C64) + #define BIT_DMA_ITR_CNT_CRL_EN BIT(0) + #define BIT_DMA_ITR_CNT_CRL_EXT_TICK BIT(1) + #define BIT_DMA_ITR_CNT_CRL_FOREVER BIT(2) + #define BIT_DMA_ITR_CNT_CRL_CLR BIT(3) + #define BIT_DMA_ITR_CNT_CRL_REACH_TRSH BIT(4) + +/* popular locations */ +#define HOST_MBOX HOSTADDR(RGF_USER_USER_SCRATCH_PAD) +#define HOST_SW_INT (HOSTADDR(RGF_USER_USER_ICR) + \ + offsetof(struct RGF_ICR, ICS)) +#define SW_INT_MBOX BIT_USER_USER_ICR_SW_INT_2 + +/* ISR register bits */ +#define ISR_MISC_FW_READY BIT_DMA_EP_MISC_ICR_FW_INT0 +#define ISR_MISC_MBOX_EVT BIT_DMA_EP_MISC_ICR_FW_INT1 + +/* Hardware definitions end */ + +struct wil6210_mbox_ring { + u32 base; + u16 entry_size; /* max. size of mbox entry, incl. all headers */ + u16 size; + u32 tail; + u32 head; +} __packed; + +struct wil6210_mbox_ring_desc { + __le32 sync; + __le32 addr; +} __packed; + +/* at HOST_OFF_WIL6210_MBOX_CTL */ +struct wil6210_mbox_ctl { + struct wil6210_mbox_ring tx; + struct wil6210_mbox_ring rx; +} __packed; + +struct wil6210_mbox_hdr { + __le16 seq; + __le16 len; /* payload, bytes after this header */ + __le16 type; + u8 flags; + u8 reserved; +} __packed; + +#define WIL_MBOX_HDR_TYPE_WMI (0) + +/* max. value for wil6210_mbox_hdr.len */ +#define MAX_MBOXITEM_SIZE (240) + +struct wil6210_mbox_hdr_wmi { + u8 reserved0[2]; + __le16 id; + __le16 info1; /* bits [0..3] - device_id, rest - unused */ + u8 reserved1[2]; +} __packed; + +struct pending_wmi_event { + struct list_head list; + struct { + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_hdr_wmi wmi; + u8 data[0]; + } __packed event; +}; + +union vring_desc; + +struct vring { + dma_addr_t pa; + volatile union vring_desc *va; /* vring_desc[size], WriteBack by DMA */ + u16 size; /* number of vring_desc elements */ + u32 swtail; + u32 swhead; + u32 hwtail; /* write here to inform hw */ + void **ctx; /* void *ctx[size] - software context */ +}; + +enum { /* for wil6210_priv.status */ + wil_status_fwready = 0, + wil_status_fwconnected, + wil_status_dontscan, + wil_status_irqen, /* FIXME: interrupts enabled - for debug */ +}; + +struct pci_dev; + +struct wil6210_stats { + u64 tsf; + u32 snr; + u16 last_mcs_rx; + u16 bf_mcs; /* last BF, used for Tx */ + u16 my_rx_sector; + u16 my_tx_sector; + u16 peer_rx_sector; + u16 peer_tx_sector; +}; + +struct wil6210_priv { + struct pci_dev *pdev; + int n_msi; + struct wireless_dev *wdev; + void __iomem *csr; + ulong status; + /* profile */ + u32 monitor_flags; + u32 secure_pcp; /* create secure PCP? */ + int sinfo_gen; + /* cached ISR registers */ + u32 isr_misc; + /* mailbox related */ + struct mutex wmi_mutex; + struct wil6210_mbox_ctl mbox_ctl; + struct completion wmi_ready; + u16 wmi_seq; + u16 reply_id; /**< wait for this WMI event */ + void *reply_buf; + u16 reply_size; + struct workqueue_struct *wmi_wq; /* for deferred calls */ + struct work_struct wmi_event_worker; + struct workqueue_struct *wmi_wq_conn; /* for connect worker */ + struct work_struct wmi_connect_worker; + struct work_struct disconnect_worker; + struct timer_list connect_timer; + int pending_connect_cid; + struct list_head pending_wmi_ev; + /* + * protect pending_wmi_ev + * - fill in IRQ from wil6210_irq_misc, + * - consumed in thread by wmi_event_worker + */ + spinlock_t wmi_ev_lock; + /* DMA related */ + struct vring vring_rx; + struct vring vring_tx[WIL6210_MAX_TX_RINGS]; + u8 dst_addr[WIL6210_MAX_TX_RINGS][ETH_ALEN]; + /* scan */ + struct cfg80211_scan_request *scan_request; + + struct mutex mutex; /* for wil6210_priv access in wil_{up|down} */ + /* statistics */ + struct wil6210_stats stats; + /* debugfs */ + struct dentry *debug; + struct debugfs_blob_wrapper fw_code_blob; + struct debugfs_blob_wrapper fw_data_blob; + struct debugfs_blob_wrapper fw_peri_blob; + struct debugfs_blob_wrapper uc_code_blob; + struct debugfs_blob_wrapper uc_data_blob; + struct debugfs_blob_wrapper rgf_blob; +}; + +#define wil_to_wiphy(i) (i->wdev->wiphy) +#define wil_to_dev(i) (wiphy_dev(wil_to_wiphy(i))) +#define wiphy_to_wil(w) (struct wil6210_priv *)(wiphy_priv(w)) +#define wil_to_wdev(i) (i->wdev) +#define wdev_to_wil(w) (struct wil6210_priv *)(wdev_priv(w)) +#define wil_to_ndev(i) (wil_to_wdev(i)->netdev) +#define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr)) + +#define wil_dbg(wil, fmt, arg...) netdev_dbg(wil_to_ndev(wil), fmt, ##arg) +#define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg) +#define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg) + +#define wil_dbg_IRQ(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg) +#define wil_dbg_TXRX(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg) +#define wil_dbg_WMI(wil, fmt, arg...) wil_dbg(wil, "DBG[ WMI]" fmt, ##arg) + +#define wil_hex_dump_TXRX(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ + wil_print_hex_dump_debug("DBG[TXRX]" prefix_str,\ + prefix_type, rowsize, \ + groupsize, buf, len, ascii) + +#define wil_hex_dump_WMI(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ + wil_print_hex_dump_debug("DBG[ WMI]" prefix_str,\ + prefix_type, rowsize, \ + groupsize, buf, len, ascii) + +void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, + size_t count); +void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, + size_t count); + +void *wil_if_alloc(struct device *dev, void __iomem *csr); +void wil_if_free(struct wil6210_priv *wil); +int wil_if_add(struct wil6210_priv *wil); +void wil_if_remove(struct wil6210_priv *wil); +int wil_priv_init(struct wil6210_priv *wil); +void wil_priv_deinit(struct wil6210_priv *wil); +int wil_reset(struct wil6210_priv *wil); +void wil_link_on(struct wil6210_priv *wil); +void wil_link_off(struct wil6210_priv *wil); +int wil_up(struct wil6210_priv *wil); +int wil_down(struct wil6210_priv *wil); +void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r); + +void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr); +void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr); +int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr, + struct wil6210_mbox_hdr *hdr); +int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len); +void wmi_recv_cmd(struct wil6210_priv *wil); +int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, + u16 reply_id, void *reply, u8 reply_size, int to_msec); +void wmi_connect_worker(struct work_struct *work); +void wmi_event_worker(struct work_struct *work); +void wmi_event_flush(struct wil6210_priv *wil); +int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid); +int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid); +int wmi_set_channel(struct wil6210_priv *wil, int channel); +int wmi_get_channel(struct wil6210_priv *wil, int *channel); +int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb); +int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, + const void *mac_addr); +int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, + const void *mac_addr, int key_len, const void *key); +int wmi_echo(struct wil6210_priv *wil); +int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie); + +int wil6210_init_irq(struct wil6210_priv *wil, int irq); +void wil6210_fini_irq(struct wil6210_priv *wil, int irq); +void wil6210_disable_irq(struct wil6210_priv *wil); +void wil6210_enable_irq(struct wil6210_priv *wil); + +int wil6210_debugfs_init(struct wil6210_priv *wil); +void wil6210_debugfs_remove(struct wil6210_priv *wil); + +struct wireless_dev *wil_cfg80211_init(struct device *dev); +void wil_wdev_free(struct wil6210_priv *wil); + +int wmi_set_mac_address(struct wil6210_priv *wil, void *addr); +int wmi_set_bcon(struct wil6210_priv *wil, int bi, u8 wmi_nettype); +void wil6210_disconnect(struct wil6210_priv *wil, void *bssid); + +int wil_rx_init(struct wil6210_priv *wil); +void wil_rx_fini(struct wil6210_priv *wil); + +/* TX API */ +int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, + int cid, int tid); +void wil_vring_fini_tx(struct wil6210_priv *wil, int id); + +netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev); +void wil_tx_complete(struct wil6210_priv *wil, int ringid); + +/* RX API */ +void wil_rx_handle(struct wil6210_priv *wil); + +int wil_iftype_nl2wmi(enum nl80211_iftype type); + +#endif /* __WIL6210_H__ */ diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c new file mode 100644 index 00000000000..12915f6e761 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -0,0 +1,975 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, 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 +#include +#include +#include + +#include "wil6210.h" +#include "wmi.h" + +/** + * WMI event receiving - theory of operations + * + * When firmware about to report WMI event, it fills memory area + * in the mailbox and raises misc. IRQ. Thread interrupt handler invoked for + * the misc IRQ, function @wmi_recv_cmd called by thread IRQ handler. + * + * @wmi_recv_cmd reads event, allocates memory chunk and attaches it to the + * event list @wil->pending_wmi_ev. Then, work queue @wil->wmi_wq wakes up + * and handles events within the @wmi_event_worker. Every event get detached + * from list, processed and deleted. + * + * Purpose for this mechanism is to release IRQ thread; otherwise, + * if WMI event handling involves another WMI command flow, this 2-nd flow + * won't be completed because of blocked IRQ thread. + */ + +/** + * Addressing - theory of operations + * + * There are several buses present on the WIL6210 card. + * Same memory areas are visible at different address on + * the different busses. There are 3 main bus masters: + * - MAC CPU (ucode) + * - User CPU (firmware) + * - AHB (host) + * + * On the PCI bus, there is one BAR (BAR0) of 2Mb size, exposing + * AHB addresses starting from 0x880000 + * + * Internally, firmware uses addresses that allows faster access but + * are invisible from the host. To read from these addresses, alternative + * AHB address must be used. + * + * Memory mapping + * Linker address PCI/Host address + * 0x880000 .. 0xa80000 2Mb BAR0 + * 0x800000 .. 0x807000 0x900000 .. 0x907000 28k DCCM + * 0x840000 .. 0x857000 0x908000 .. 0x91f000 92k PERIPH + */ + +/** + * @fw_mapping provides memory remapping table + */ +static const struct { + u32 from; /* linker address - from, inclusive */ + u32 to; /* linker address - to, exclusive */ + u32 host; /* PCI/Host address - BAR0 + 0x880000 */ +} fw_mapping[] = { + {0x000000, 0x040000, 0x8c0000}, /* FW code RAM 256k */ + {0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */ + {0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */ + {0x880000, 0x88a000, 0x880000}, /* various RGF */ + {0x8c0000, 0x932000, 0x8c0000}, /* trivial mapping for upper area */ + /* + * 920000..930000 ucode code RAM + * 930000..932000 ucode data RAM + */ +}; + +/** + * return AHB address for given firmware/ucode internal (linker) address + * @x - internal address + * If address have no valid AHB mapping, return 0 + */ +static u32 wmi_addr_remap(u32 x) +{ + uint i; + + for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) { + if ((x >= fw_mapping[i].from) && (x < fw_mapping[i].to)) + return x + fw_mapping[i].host - fw_mapping[i].from; + } + + return 0; +} + +/** + * Check address validity for WMI buffer; remap if needed + * @ptr - internal (linker) fw/ucode address + * + * Valid buffer should be DWORD aligned + * + * return address for accessing buffer from the host; + * if buffer is not valid, return NULL. + */ +void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_) +{ + u32 off; + u32 ptr = le32_to_cpu(ptr_); + + if (ptr % 4) + return NULL; + + ptr = wmi_addr_remap(ptr); + if (ptr < WIL6210_FW_HOST_OFF) + return NULL; + + off = HOSTADDR(ptr); + if (off > WIL6210_MEM_SIZE - 4) + return NULL; + + return wil->csr + off; +} + +/** + * Check address validity + */ +void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr) +{ + u32 off; + + if (ptr % 4) + return NULL; + + if (ptr < WIL6210_FW_HOST_OFF) + return NULL; + + off = HOSTADDR(ptr); + if (off > WIL6210_MEM_SIZE - 4) + return NULL; + + return wil->csr + off; +} + +int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr, + struct wil6210_mbox_hdr *hdr) +{ + void __iomem *src = wmi_buffer(wil, ptr); + if (!src) + return -EINVAL; + + wil_memcpy_fromio_32(hdr, src, sizeof(*hdr)); + + return 0; +} + +static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) +{ + struct { + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_hdr_wmi wmi; + } __packed cmd = { + .hdr = { + .type = WIL_MBOX_HDR_TYPE_WMI, + .flags = 0, + .len = cpu_to_le16(sizeof(cmd.wmi) + len), + }, + .wmi = { + .id = cpu_to_le16(cmdid), + .info1 = 0, + }, + }; + struct wil6210_mbox_ring *r = &wil->mbox_ctl.tx; + struct wil6210_mbox_ring_desc d_head; + u32 next_head; + void __iomem *dst; + void __iomem *head = wmi_addr(wil, r->head); + uint retry; + + if (sizeof(cmd) + len > r->entry_size) { + wil_err(wil, "WMI size too large: %d bytes, max is %d\n", + (int)(sizeof(cmd) + len), r->entry_size); + return -ERANGE; + + } + + might_sleep(); + + if (!test_bit(wil_status_fwready, &wil->status)) { + wil_err(wil, "FW not ready\n"); + return -EAGAIN; + } + + if (!head) { + wil_err(wil, "WMI head is garbage: 0x%08x\n", r->head); + return -EINVAL; + } + /* read Tx head till it is not busy */ + for (retry = 5; retry > 0; retry--) { + wil_memcpy_fromio_32(&d_head, head, sizeof(d_head)); + if (d_head.sync == 0) + break; + msleep(20); + } + if (d_head.sync != 0) { + wil_err(wil, "WMI head busy\n"); + return -EBUSY; + } + /* next head */ + next_head = r->base + ((r->head - r->base + sizeof(d_head)) % r->size); + wil_dbg_WMI(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head); + /* wait till FW finish with previous command */ + for (retry = 5; retry > 0; retry--) { + r->tail = ioread32(wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, tx.tail)); + if (next_head != r->tail) + break; + msleep(20); + } + if (next_head == r->tail) { + wil_err(wil, "WMI ring full\n"); + return -EBUSY; + } + dst = wmi_buffer(wil, d_head.addr); + if (!dst) { + wil_err(wil, "invalid WMI buffer: 0x%08x\n", + le32_to_cpu(d_head.addr)); + return -EINVAL; + } + cmd.hdr.seq = cpu_to_le16(++wil->wmi_seq); + /* set command */ + wil_dbg_WMI(wil, "WMI command 0x%04x [%d]\n", cmdid, len); + wil_hex_dump_WMI("Cmd ", DUMP_PREFIX_OFFSET, 16, 1, &cmd, + sizeof(cmd), true); + wil_hex_dump_WMI("cmd ", DUMP_PREFIX_OFFSET, 16, 1, buf, + len, true); + wil_memcpy_toio_32(dst, &cmd, sizeof(cmd)); + wil_memcpy_toio_32(dst + sizeof(cmd), buf, len); + /* mark entry as full */ + iowrite32(1, wil->csr + HOSTADDR(r->head) + + offsetof(struct wil6210_mbox_ring_desc, sync)); + /* advance next ptr */ + iowrite32(r->head = next_head, wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, tx.head)); + + /* interrupt to FW */ + iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT); + + return 0; +} + +int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) +{ + int rc; + + mutex_lock(&wil->wmi_mutex); + rc = __wmi_send(wil, cmdid, buf, len); + mutex_unlock(&wil->wmi_mutex); + + return rc; +} + +/*=== Event handlers ===*/ +static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + struct wmi_ready_event *evt = d; + u32 ver = le32_to_cpu(evt->sw_version); + + wil_dbg_WMI(wil, "FW ver. %d; MAC %pM\n", ver, evt->mac); + + if (!is_valid_ether_addr(ndev->dev_addr)) { + memcpy(ndev->dev_addr, evt->mac, ETH_ALEN); + memcpy(ndev->perm_addr, evt->mac, ETH_ALEN); + } + snprintf(wdev->wiphy->fw_version, sizeof(wdev->wiphy->fw_version), + "%d", ver); +} + +static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d, + int len) +{ + wil_dbg_WMI(wil, "WMI: FW ready\n"); + + set_bit(wil_status_fwready, &wil->status); + /* reuse wmi_ready for the firmware ready indication */ + complete(&wil->wmi_ready); +} + +static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) +{ + struct wmi_rx_mgmt_packet_event *data = d; + struct wiphy *wiphy = wil_to_wiphy(wil); + struct ieee80211_mgmt *rx_mgmt_frame = + (struct ieee80211_mgmt *)data->payload; + int ch_no = data->info.channel+1; + u32 freq = ieee80211_channel_to_frequency(ch_no, + IEEE80211_BAND_60GHZ); + struct ieee80211_channel *channel = ieee80211_get_channel(wiphy, freq); + /* TODO convert LE to CPU */ + s32 signal = 0; /* TODO */ + __le16 fc = rx_mgmt_frame->frame_control; + u32 d_len = le32_to_cpu(data->info.len); + u16 d_status = le16_to_cpu(data->info.status); + + wil_dbg_WMI(wil, "MGMT: channel %d MCS %d SNR %d\n", + data->info.channel, data->info.mcs, data->info.snr); + wil_dbg_WMI(wil, "status 0x%04x len %d stype %04x\n", d_status, d_len, + le16_to_cpu(data->info.stype)); + wil_dbg_WMI(wil, "qid %d mid %d cid %d\n", + data->info.qid, data->info.mid, data->info.cid); + + if (!channel) { + wil_err(wil, "Frame on unsupported channel\n"); + return; + } + + if (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc)) { + struct cfg80211_bss *bss; + u64 tsf = le64_to_cpu(rx_mgmt_frame->u.beacon.timestamp); + u16 cap = le16_to_cpu(rx_mgmt_frame->u.beacon.capab_info); + u16 bi = le16_to_cpu(rx_mgmt_frame->u.beacon.beacon_int); + const u8 *ie_buf = rx_mgmt_frame->u.beacon.variable; + size_t ie_len = d_len - offsetof(struct ieee80211_mgmt, + u.beacon.variable); + wil_dbg_WMI(wil, "Capability info : 0x%04x\n", cap); + + bss = cfg80211_inform_bss(wiphy, channel, rx_mgmt_frame->bssid, + tsf, cap, bi, ie_buf, ie_len, + signal, GFP_KERNEL); + if (bss) { + wil_dbg_WMI(wil, "Added BSS %pM\n", + rx_mgmt_frame->bssid); + cfg80211_put_bss(bss); + } else { + wil_err(wil, "cfg80211_inform_bss() failed\n"); + } + } +} + +static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id, + void *d, int len) +{ + if (wil->scan_request) { + struct wmi_scan_complete_event *data = d; + bool aborted = (data->status != 0); + + wil_dbg_WMI(wil, "SCAN_COMPLETE(0x%08x)\n", data->status); + cfg80211_scan_done(wil->scan_request, aborted); + wil->scan_request = NULL; + } else { + wil_err(wil, "SCAN_COMPLETE while not scanning\n"); + } +} + +static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + struct wmi_connect_event *evt = d; + int ch; /* channel number */ + struct station_info sinfo; + u8 *assoc_req_ie, *assoc_resp_ie; + size_t assoc_req_ielen, assoc_resp_ielen; + /* capinfo(u16) + listen_interval(u16) + IEs */ + const size_t assoc_req_ie_offset = sizeof(u16) * 2; + /* capinfo(u16) + status_code(u16) + associd(u16) + IEs */ + const size_t assoc_resp_ie_offset = sizeof(u16) * 3; + + if (len < sizeof(*evt)) { + wil_err(wil, "Connect event too short : %d bytes\n", len); + return; + } + if (len != sizeof(*evt) + evt->beacon_ie_len + evt->assoc_req_len + + evt->assoc_resp_len) { + wil_err(wil, + "Connect event corrupted : %d != %d + %d + %d + %d\n", + len, (int)sizeof(*evt), evt->beacon_ie_len, + evt->assoc_req_len, evt->assoc_resp_len); + return; + } + ch = evt->channel + 1; + wil_dbg_WMI(wil, "Connect %pM channel [%d] cid %d\n", + evt->bssid, ch, evt->cid); + wil_hex_dump_WMI("connect AI : ", DUMP_PREFIX_OFFSET, 16, 1, + evt->assoc_info, len - sizeof(*evt), true); + + /* figure out IE's */ + assoc_req_ie = &evt->assoc_info[evt->beacon_ie_len + + assoc_req_ie_offset]; + assoc_req_ielen = evt->assoc_req_len - assoc_req_ie_offset; + if (evt->assoc_req_len <= assoc_req_ie_offset) { + assoc_req_ie = NULL; + assoc_req_ielen = 0; + } + + assoc_resp_ie = &evt->assoc_info[evt->beacon_ie_len + + evt->assoc_req_len + + assoc_resp_ie_offset]; + assoc_resp_ielen = evt->assoc_resp_len - assoc_resp_ie_offset; + if (evt->assoc_resp_len <= assoc_resp_ie_offset) { + assoc_resp_ie = NULL; + assoc_resp_ielen = 0; + } + + if ((wdev->iftype == NL80211_IFTYPE_STATION) || + (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { + if (wdev->sme_state != CFG80211_SME_CONNECTING) { + wil_err(wil, "Not in connecting state\n"); + return; + } + del_timer_sync(&wil->connect_timer); + cfg80211_connect_result(ndev, evt->bssid, + assoc_req_ie, assoc_req_ielen, + assoc_resp_ie, assoc_resp_ielen, + WLAN_STATUS_SUCCESS, GFP_KERNEL); + + } else if ((wdev->iftype == NL80211_IFTYPE_AP) || + (wdev->iftype == NL80211_IFTYPE_P2P_GO)) { + memset(&sinfo, 0, sizeof(sinfo)); + + sinfo.generation = wil->sinfo_gen++; + + if (assoc_req_ie) { + sinfo.assoc_req_ies = assoc_req_ie; + sinfo.assoc_req_ies_len = assoc_req_ielen; + sinfo.filled |= STATION_INFO_ASSOC_REQ_IES; + } + + cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL); + } + set_bit(wil_status_fwconnected, &wil->status); + + /* FIXME FW can transmit only ucast frames to peer */ + /* FIXME real ring_id instead of hard coded 0 */ + memcpy(wil->dst_addr[0], evt->bssid, ETH_ALEN); + + wil->pending_connect_cid = evt->cid; + queue_work(wil->wmi_wq_conn, &wil->wmi_connect_worker); +} + +static void wmi_evt_disconnect(struct wil6210_priv *wil, int id, + void *d, int len) +{ + struct wmi_disconnect_event *evt = d; + + wil_dbg_WMI(wil, "Disconnect %pM reason %d proto %d wmi\n", + evt->bssid, + evt->protocol_reason_status, evt->disconnect_reason); + + wil->sinfo_gen++; + + wil6210_disconnect(wil, evt->bssid); + clear_bit(wil_status_dontscan, &wil->status); +} + +static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len) +{ + struct wmi_notify_req_done_event *evt = d; + + if (len < sizeof(*evt)) { + wil_err(wil, "Short NOTIFY event\n"); + return; + } + + wil->stats.tsf = le64_to_cpu(evt->tsf); + wil->stats.snr = le32_to_cpu(evt->snr_val); + wil->stats.bf_mcs = le16_to_cpu(evt->bf_mcs); + wil->stats.my_rx_sector = le16_to_cpu(evt->my_rx_sector); + wil->stats.my_tx_sector = le16_to_cpu(evt->my_tx_sector); + wil->stats.peer_rx_sector = le16_to_cpu(evt->other_rx_sector); + wil->stats.peer_tx_sector = le16_to_cpu(evt->other_tx_sector); + wil_dbg_WMI(wil, "Link status, MCS %d TSF 0x%016llx\n" + "BF status 0x%08x SNR 0x%08x\n" + "Tx Tpt %d goodput %d Rx goodput %d\n" + "Sectors(rx:tx) my %d:%d peer %d:%d\n", + wil->stats.bf_mcs, wil->stats.tsf, evt->status, + wil->stats.snr, le32_to_cpu(evt->tx_tpt), + le32_to_cpu(evt->tx_goodput), le32_to_cpu(evt->rx_goodput), + wil->stats.my_rx_sector, wil->stats.my_tx_sector, + wil->stats.peer_rx_sector, wil->stats.peer_tx_sector); +} + +/* + * Firmware reports EAPOL frame using WME event. + * Reconstruct Ethernet frame and deliver it via normal Rx + */ +static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id, + void *d, int len) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wmi_eapol_rx_event *evt = d; + u16 eapol_len = le16_to_cpu(evt->eapol_len); + int sz = eapol_len + ETH_HLEN; + struct sk_buff *skb; + struct ethhdr *eth; + + wil_dbg_WMI(wil, "EAPOL len %d from %pM\n", eapol_len, + evt->src_mac); + + if (eapol_len > 196) { /* TODO: revisit size limit */ + wil_err(wil, "EAPOL too large\n"); + return; + } + + skb = alloc_skb(sz, GFP_KERNEL); + if (!skb) { + wil_err(wil, "Failed to allocate skb\n"); + return; + } + eth = (struct ethhdr *)skb_put(skb, ETH_HLEN); + memcpy(eth->h_dest, ndev->dev_addr, ETH_ALEN); + memcpy(eth->h_source, evt->src_mac, ETH_ALEN); + eth->h_proto = cpu_to_be16(ETH_P_PAE); + memcpy(skb_put(skb, eapol_len), evt->eapol, eapol_len); + skb->protocol = eth_type_trans(skb, ndev); + if (likely(netif_rx_ni(skb) == NET_RX_SUCCESS)) { + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += skb->len; + } else { + ndev->stats.rx_dropped++; + } +} + +static const struct { + int eventid; + void (*handler)(struct wil6210_priv *wil, int eventid, + void *data, int data_len); +} wmi_evt_handlers[] = { + {WMI_READY_EVENTID, wmi_evt_ready}, + {WMI_FW_READY_EVENTID, wmi_evt_fw_ready}, + {WMI_RX_MGMT_PACKET_EVENTID, wmi_evt_rx_mgmt}, + {WMI_SCAN_COMPLETE_EVENTID, wmi_evt_scan_complete}, + {WMI_CONNECT_EVENTID, wmi_evt_connect}, + {WMI_DISCONNECT_EVENTID, wmi_evt_disconnect}, + {WMI_NOTIFY_REQ_DONE_EVENTID, wmi_evt_notify}, + {WMI_EAPOL_RX_EVENTID, wmi_evt_eapol_rx}, +}; + +/* + * Run in IRQ context + * Extract WMI command from mailbox. Queue it to the @wil->pending_wmi_ev + * that will be eventually handled by the @wmi_event_worker in the thread + * context of thread "wil6210_wmi" + */ +void wmi_recv_cmd(struct wil6210_priv *wil) +{ + struct wil6210_mbox_ring_desc d_tail; + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_ring *r = &wil->mbox_ctl.rx; + struct pending_wmi_event *evt; + u8 *cmd; + void __iomem *src; + ulong flags; + + for (;;) { + u16 len; + + r->head = ioread32(wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, rx.head)); + if (r->tail == r->head) + return; + + /* read cmd from tail */ + wil_memcpy_fromio_32(&d_tail, wil->csr + HOSTADDR(r->tail), + sizeof(struct wil6210_mbox_ring_desc)); + if (d_tail.sync == 0) { + wil_err(wil, "Mbox evt not owned by FW?\n"); + return; + } + + if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) { + wil_err(wil, "Mbox evt at 0x%08x?\n", + le32_to_cpu(d_tail.addr)); + return; + } + + len = le16_to_cpu(hdr.len); + src = wmi_buffer(wil, d_tail.addr) + + sizeof(struct wil6210_mbox_hdr); + evt = kmalloc(ALIGN(offsetof(struct pending_wmi_event, + event.wmi) + len, 4), + GFP_KERNEL); + if (!evt) { + wil_err(wil, "kmalloc for WMI event (%d) failed\n", + len); + return; + } + evt->event.hdr = hdr; + cmd = (void *)&evt->event.wmi; + wil_memcpy_fromio_32(cmd, src, len); + /* mark entry as empty */ + iowrite32(0, wil->csr + HOSTADDR(r->tail) + + offsetof(struct wil6210_mbox_ring_desc, sync)); + /* indicate */ + wil_dbg_WMI(wil, "Mbox evt %04x %04x %04x %02x\n", + le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type), + hdr.flags); + if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) && + (len >= sizeof(struct wil6210_mbox_hdr_wmi))) { + wil_dbg_WMI(wil, "WMI event 0x%04x\n", + evt->event.wmi.id); + } + wil_hex_dump_WMI("evt ", DUMP_PREFIX_OFFSET, 16, 1, + &evt->event.hdr, sizeof(hdr) + len, true); + + /* advance tail */ + r->tail = r->base + ((r->tail - r->base + + sizeof(struct wil6210_mbox_ring_desc)) % r->size); + iowrite32(r->tail, wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, rx.tail)); + + /* add to the pending list */ + spin_lock_irqsave(&wil->wmi_ev_lock, flags); + list_add_tail(&evt->list, &wil->pending_wmi_ev); + spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); + { + int q = queue_work(wil->wmi_wq, + &wil->wmi_event_worker); + wil_dbg_WMI(wil, "queue_work -> %d\n", q); + } + } +} + +int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, + u16 reply_id, void *reply, u8 reply_size, int to_msec) +{ + int rc; + int remain; + + mutex_lock(&wil->wmi_mutex); + + rc = __wmi_send(wil, cmdid, buf, len); + if (rc) + goto out; + + wil->reply_id = reply_id; + wil->reply_buf = reply; + wil->reply_size = reply_size; + remain = wait_for_completion_timeout(&wil->wmi_ready, + msecs_to_jiffies(to_msec)); + if (0 == remain) { + wil_err(wil, "wmi_call(0x%04x->0x%04x) timeout %d msec\n", + cmdid, reply_id, to_msec); + rc = -ETIME; + } else { + wil_dbg_WMI(wil, + "wmi_call(0x%04x->0x%04x) completed in %d msec\n", + cmdid, reply_id, + to_msec - jiffies_to_msecs(remain)); + } + wil->reply_id = 0; + wil->reply_buf = NULL; + wil->reply_size = 0; + out: + mutex_unlock(&wil->wmi_mutex); + + return rc; +} + +int wmi_echo(struct wil6210_priv *wil) +{ + struct wmi_echo_cmd cmd = { + .value = cpu_to_le32(0x12345678), + }; + + return wmi_call(wil, WMI_ECHO_CMDID, &cmd, sizeof(cmd), + WMI_ECHO_RSP_EVENTID, NULL, 0, 20); +} + +int wmi_set_mac_address(struct wil6210_priv *wil, void *addr) +{ + struct wmi_set_mac_address_cmd cmd; + + memcpy(cmd.mac, addr, ETH_ALEN); + + wil_dbg_WMI(wil, "Set MAC %pM\n", addr); + + return wmi_send(wil, WMI_SET_MAC_ADDRESS_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_set_bcon(struct wil6210_priv *wil, int bi, u8 wmi_nettype) +{ + struct wmi_bcon_ctrl_cmd cmd = { + .bcon_interval = cpu_to_le16(bi), + .network_type = wmi_nettype, + .disable_sec_offload = 1, + }; + + if (!wil->secure_pcp) + cmd.disable_sec = 1; + + return wmi_send(wil, WMI_BCON_CTRL_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid) +{ + struct wmi_set_ssid_cmd cmd = { + .ssid_len = cpu_to_le32(ssid_len), + }; + + if (ssid_len > sizeof(cmd.ssid)) + return -EINVAL; + + memcpy(cmd.ssid, ssid, ssid_len); + + return wmi_send(wil, WMI_SET_SSID_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid) +{ + int rc; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_set_ssid_cmd cmd; + } __packed reply; + int len; /* reply.cmd.ssid_len in CPU order */ + + rc = wmi_call(wil, WMI_GET_SSID_CMDID, NULL, 0, WMI_GET_SSID_EVENTID, + &reply, sizeof(reply), 20); + if (rc) + return rc; + + len = le32_to_cpu(reply.cmd.ssid_len); + if (len > sizeof(reply.cmd.ssid)) + return -EINVAL; + + *ssid_len = len; + memcpy(ssid, reply.cmd.ssid, len); + + return 0; +} + +int wmi_set_channel(struct wil6210_priv *wil, int channel) +{ + struct wmi_set_pcp_channel_cmd cmd = { + .channel = channel - 1, + }; + + return wmi_send(wil, WMI_SET_PCP_CHANNEL_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_get_channel(struct wil6210_priv *wil, int *channel) +{ + int rc; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_set_pcp_channel_cmd cmd; + } __packed reply; + + rc = wmi_call(wil, WMI_GET_PCP_CHANNEL_CMDID, NULL, 0, + WMI_GET_PCP_CHANNEL_EVENTID, &reply, sizeof(reply), 20); + if (rc) + return rc; + + if (reply.cmd.channel > 3) + return -EINVAL; + + *channel = reply.cmd.channel + 1; + + return 0; +} + +int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb) +{ + struct wmi_eapol_tx_cmd *cmd; + struct ethhdr *eth; + u16 eapol_len = skb->len - ETH_HLEN; + void *eapol = skb->data + ETH_HLEN; + uint i; + int rc; + + skb_set_mac_header(skb, 0); + eth = eth_hdr(skb); + wil_dbg_WMI(wil, "EAPOL %d bytes to %pM\n", eapol_len, eth->h_dest); + for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { + if (memcmp(wil->dst_addr[i], eth->h_dest, ETH_ALEN) == 0) + goto found_dest; + } + + return -EINVAL; + + found_dest: + /* find out eapol data & len */ + cmd = kzalloc(sizeof(*cmd) + eapol_len, GFP_KERNEL); + if (!cmd) + return -EINVAL; + + memcpy(cmd->dst_mac, eth->h_dest, ETH_ALEN); + cmd->eapol_len = cpu_to_le16(eapol_len); + memcpy(cmd->eapol, eapol, eapol_len); + rc = wmi_send(wil, WMI_EAPOL_TX_CMDID, cmd, sizeof(*cmd) + eapol_len); + kfree(cmd); + + return rc; +} + +int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, + const void *mac_addr) +{ + struct wmi_delete_cipher_key_cmd cmd = { + .key_index = key_index, + }; + + if (mac_addr) + memcpy(cmd.mac, mac_addr, WMI_MAC_LEN); + + return wmi_send(wil, WMI_DELETE_CIPHER_KEY_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, + const void *mac_addr, int key_len, const void *key) +{ + struct wmi_add_cipher_key_cmd cmd = { + .key_index = key_index, + .key_usage = WMI_KEY_USE_PAIRWISE, + .key_len = key_len, + }; + + if (!key || (key_len > sizeof(cmd.key))) + return -EINVAL; + + memcpy(cmd.key, key, key_len); + if (mac_addr) + memcpy(cmd.mac, mac_addr, WMI_MAC_LEN); + + return wmi_send(wil, WMI_ADD_CIPHER_KEY_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie) +{ + int rc; + u16 len = sizeof(struct wmi_set_appie_cmd) + ie_len; + struct wmi_set_appie_cmd *cmd = kzalloc(len, GFP_KERNEL); + if (!cmd) { + wil_err(wil, "kmalloc(%d) failed\n", len); + return -ENOMEM; + } + + cmd->mgmt_frm_type = type; + /* BUG: FW API define ieLen as u8. Will fix FW */ + cmd->ie_len = cpu_to_le16(ie_len); + memcpy(cmd->ie_info, ie, ie_len); + rc = wmi_send(wil, WMI_SET_APPIE_CMDID, &cmd, len); + kfree(cmd); + + return rc; +} + +void wmi_event_flush(struct wil6210_priv *wil) +{ + struct pending_wmi_event *evt, *t; + + wil_dbg_WMI(wil, "%s()\n", __func__); + + list_for_each_entry_safe(evt, t, &wil->pending_wmi_ev, list) { + list_del(&evt->list); + kfree(evt); + } +} + +static bool wmi_evt_call_handler(struct wil6210_priv *wil, int id, + void *d, int len) +{ + uint i; + + for (i = 0; i < ARRAY_SIZE(wmi_evt_handlers); i++) { + if (wmi_evt_handlers[i].eventid == id) { + wmi_evt_handlers[i].handler(wil, id, d, len); + return true; + } + } + + return false; +} + +static void wmi_event_handle(struct wil6210_priv *wil, + struct wil6210_mbox_hdr *hdr) +{ + u16 len = le16_to_cpu(hdr->len); + + if ((hdr->type == WIL_MBOX_HDR_TYPE_WMI) && + (len >= sizeof(struct wil6210_mbox_hdr_wmi))) { + struct wil6210_mbox_hdr_wmi *wmi = (void *)(&hdr[1]); + void *evt_data = (void *)(&wmi[1]); + u16 id = le16_to_cpu(wmi->id); + /* check if someone waits for this event */ + if (wil->reply_id && wil->reply_id == id) { + if (wil->reply_buf) { + memcpy(wil->reply_buf, wmi, + min(len, wil->reply_size)); + } else { + wmi_evt_call_handler(wil, id, evt_data, + len - sizeof(*wmi)); + } + wil_dbg_WMI(wil, "Complete WMI 0x%04x\n", id); + complete(&wil->wmi_ready); + return; + } + /* unsolicited event */ + /* search for handler */ + if (!wmi_evt_call_handler(wil, id, evt_data, + len - sizeof(*wmi))) { + wil_err(wil, "Unhandled event 0x%04x\n", id); + } + } else { + wil_err(wil, "Unknown event type\n"); + print_hex_dump(KERN_ERR, "evt?? ", DUMP_PREFIX_OFFSET, 16, 1, + hdr, sizeof(*hdr) + len, true); + } +} + +/* + * Retrieve next WMI event from the pending list + */ +static struct list_head *next_wmi_ev(struct wil6210_priv *wil) +{ + ulong flags; + struct list_head *ret = NULL; + + spin_lock_irqsave(&wil->wmi_ev_lock, flags); + + if (!list_empty(&wil->pending_wmi_ev)) { + ret = wil->pending_wmi_ev.next; + list_del(ret); + } + + spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); + + return ret; +} + +/* + * Handler for the WMI events + */ +void wmi_event_worker(struct work_struct *work) +{ + struct wil6210_priv *wil = container_of(work, struct wil6210_priv, + wmi_event_worker); + struct pending_wmi_event *evt; + struct list_head *lh; + + while ((lh = next_wmi_ev(wil)) != NULL) { + evt = list_entry(lh, struct pending_wmi_event, list); + wmi_event_handle(wil, &evt->event.hdr); + kfree(evt); + } +} + +void wmi_connect_worker(struct work_struct *work) +{ + int rc; + struct wil6210_priv *wil = container_of(work, struct wil6210_priv, + wmi_connect_worker); + + if (wil->pending_connect_cid < 0) { + wil_err(wil, "No connection pending\n"); + return; + } + + wil_dbg_WMI(wil, "Configure for connection CID %d\n", + wil->pending_connect_cid); + + rc = wil_vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE, + wil->pending_connect_cid, 0); + wil->pending_connect_cid = -1; + if (rc == 0) + wil_link_on(wil); +} diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h new file mode 100644 index 00000000000..3bbf87572b0 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -0,0 +1,1116 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * Copyright (c) 2006-2012 Wilocity . + * + * 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. + */ + +/* + * This file contains the definitions of the WMI protocol specified in the + * Wireless Module Interface (WMI) for the Wilocity + * MARLON 60 Gigabit wireless solution. + * It includes definitions of all the commands and events. + * Commands are messages from the host to the WM. + * Events are messages from the WM to the host. + */ + +#ifndef __WILOCITY_WMI_H__ +#define __WILOCITY_WMI_H__ + +/* General */ + +#define WMI_MAC_LEN (6) +#define WMI_PROX_RANGE_NUM (3) + +/* List of Commands */ +enum wmi_command_id { + WMI_CONNECT_CMDID = 0x0001, + WMI_DISCONNECT_CMDID = 0x0003, + WMI_START_SCAN_CMDID = 0x0007, + WMI_SET_BSS_FILTER_CMDID = 0x0009, + WMI_SET_PROBED_SSID_CMDID = 0x000a, + WMI_SET_LISTEN_INT_CMDID = 0x000b, + WMI_BCON_CTRL_CMDID = 0x000f, + WMI_ADD_CIPHER_KEY_CMDID = 0x0016, + WMI_DELETE_CIPHER_KEY_CMDID = 0x0017, + WMI_SET_APPIE_CMDID = 0x003f, + WMI_GET_APPIE_CMDID = 0x0040, + WMI_SET_WSC_STATUS_CMDID = 0x0041, + WMI_PXMT_RANGE_CFG_CMDID = 0x0042, + WMI_PXMT_SNR2_RANGE_CFG_CMDID = 0x0043, + WMI_FAST_MEM_ACC_MODE_CMDID = 0x0300, + WMI_MEM_READ_CMDID = 0x0800, + WMI_MEM_WR_CMDID = 0x0801, + WMI_ECHO_CMDID = 0x0803, + WMI_DEEP_ECHO_CMDID = 0x0804, + WMI_CONFIG_MAC_CMDID = 0x0805, + WMI_CONFIG_PHY_DEBUG_CMDID = 0x0806, + WMI_ADD_STATION_CMDID = 0x0807, + WMI_ADD_DEBUG_TX_PCKT_CMDID = 0x0808, + WMI_PHY_GET_STATISTICS_CMDID = 0x0809, + WMI_FS_TUNE_CMDID = 0x080a, + WMI_CORR_MEASURE_CMDID = 0x080b, + WMI_TEMP_SENSE_CMDID = 0x080e, + WMI_DC_CALIB_CMDID = 0x080f, + WMI_SEND_TONE_CMDID = 0x0810, + WMI_IQ_TX_CALIB_CMDID = 0x0811, + WMI_IQ_RX_CALIB_CMDID = 0x0812, + WMI_SET_UCODE_IDLE_CMDID = 0x0813, + WMI_SET_WORK_MODE_CMDID = 0x0815, + WMI_LO_LEAKAGE_CALIB_CMDID = 0x0816, + WMI_MARLON_R_ACTIVATE_CMDID = 0x0817, + WMI_MARLON_R_READ_CMDID = 0x0818, + WMI_MARLON_R_WRITE_CMDID = 0x0819, + WMI_MARLON_R_TXRX_SEL_CMDID = 0x081a, + MAC_IO_STATIC_PARAMS_CMDID = 0x081b, + MAC_IO_DYNAMIC_PARAMS_CMDID = 0x081c, + WMI_SILENT_RSSI_CALIB_CMDID = 0x081d, + WMI_CFG_RX_CHAIN_CMDID = 0x0820, + WMI_VRING_CFG_CMDID = 0x0821, + WMI_RX_ON_CMDID = 0x0822, + WMI_VRING_BA_EN_CMDID = 0x0823, + WMI_VRING_BA_DIS_CMDID = 0x0824, + WMI_RCP_ADDBA_RESP_CMDID = 0x0825, + WMI_RCP_DELBA_CMDID = 0x0826, + WMI_SET_SSID_CMDID = 0x0827, + WMI_GET_SSID_CMDID = 0x0828, + WMI_SET_PCP_CHANNEL_CMDID = 0x0829, + WMI_GET_PCP_CHANNEL_CMDID = 0x082a, + WMI_SW_TX_REQ_CMDID = 0x082b, + WMI_RX_OFF_CMDID = 0x082c, + WMI_READ_MAC_RXQ_CMDID = 0x0830, + WMI_READ_MAC_TXQ_CMDID = 0x0831, + WMI_WRITE_MAC_RXQ_CMDID = 0x0832, + WMI_WRITE_MAC_TXQ_CMDID = 0x0833, + WMI_WRITE_MAC_XQ_FIELD_CMDID = 0x0834, + WMI_MLME_PUSH_CMDID = 0x0835, + WMI_BEAMFORMING_MGMT_CMDID = 0x0836, + WMI_BF_TXSS_MGMT_CMDID = 0x0837, + WMI_BF_SM_MGMT_CMDID = 0x0838, + WMI_BF_RXSS_MGMT_CMDID = 0x0839, + WMI_SET_SECTORS_CMDID = 0x0849, + WMI_MAINTAIN_PAUSE_CMDID = 0x0850, + WMI_MAINTAIN_RESUME_CMDID = 0x0851, + WMI_RS_MGMT_CMDID = 0x0852, + WMI_RF_MGMT_CMDID = 0x0853, + /* Performance monitoring commands */ + WMI_BF_CTRL_CMDID = 0x0862, + WMI_NOTIFY_REQ_CMDID = 0x0863, + WMI_GET_STATUS_CMDID = 0x0864, + WMI_UNIT_TEST_CMDID = 0x0900, + WMI_HICCUP_CMDID = 0x0901, + WMI_FLASH_READ_CMDID = 0x0902, + WMI_FLASH_WRITE_CMDID = 0x0903, + WMI_SECURITY_UNIT_TEST_CMDID = 0x0904, + + WMI_SET_MAC_ADDRESS_CMDID = 0xf003, + WMI_ABORT_SCAN_CMDID = 0xf007, + WMI_SET_PMK_CMDID = 0xf028, + + WMI_SET_PROMISCUOUS_MODE_CMDID = 0xf041, + WMI_GET_PMK_CMDID = 0xf048, + WMI_SET_PASSPHRASE_CMDID = 0xf049, + WMI_SEND_ASSOC_RES_CMDID = 0xf04a, + WMI_SET_ASSOC_REQ_RELAY_CMDID = 0xf04b, + WMI_EAPOL_TX_CMDID = 0xf04c, + WMI_MAC_ADDR_REQ_CMDID = 0xf04d, + WMI_FW_VER_CMDID = 0xf04e, +}; + +/* + * Commands data structures + */ + +/* + * Frame Types + */ +enum wmi_mgmt_frame_type { + WMI_FRAME_BEACON = 0, + WMI_FRAME_PROBE_REQ = 1, + WMI_FRAME_PROBE_RESP = 2, + WMI_FRAME_ASSOC_REQ = 3, + WMI_FRAME_ASSOC_RESP = 4, + WMI_NUM_MGMT_FRAME, +}; + +/* + * WMI_CONNECT_CMDID + */ +enum wmi_network_type { + WMI_NETTYPE_INFRA = 0x01, + WMI_NETTYPE_ADHOC = 0x02, + WMI_NETTYPE_ADHOC_CREATOR = 0x04, + WMI_NETTYPE_AP = 0x10, + WMI_NETTYPE_P2P = 0x20, + WMI_NETTYPE_WBE = 0x40, /* PCIE over 60g */ +}; + +enum wmi_dot11_auth_mode { + WMI_AUTH11_OPEN = 0x01, + WMI_AUTH11_SHARED = 0x02, + WMI_AUTH11_LEAP = 0x04, + WMI_AUTH11_WSC = 0x08, +}; + +enum wmi_auth_mode { + WMI_AUTH_NONE = 0x01, + WMI_AUTH_WPA = 0x02, + WMI_AUTH_WPA2 = 0x04, + WMI_AUTH_WPA_PSK = 0x08, + WMI_AUTH_WPA2_PSK = 0x10, + WMI_AUTH_WPA_CCKM = 0x20, + WMI_AUTH_WPA2_CCKM = 0x40, +}; + +enum wmi_crypto_type { + WMI_CRYPT_NONE = 0x01, + WMI_CRYPT_WEP = 0x02, + WMI_CRYPT_TKIP = 0x04, + WMI_CRYPT_AES = 0x08, + WMI_CRYPT_AES_GCMP = 0x20, +}; + + +enum wmi_connect_ctrl_flag_bits { + WMI_CONNECT_ASSOC_POLICY_USER = 0x0001, + WMI_CONNECT_SEND_REASSOC = 0x0002, + WMI_CONNECT_IGNORE_WPAx_GROUP_CIPHER = 0x0004, + WMI_CONNECT_PROFILE_MATCH_DONE = 0x0008, + WMI_CONNECT_IGNORE_AAC_BEACON = 0x0010, + WMI_CONNECT_CSA_FOLLOW_BSS = 0x0020, + WMI_CONNECT_DO_WPA_OFFLOAD = 0x0040, + WMI_CONNECT_DO_NOT_DEAUTH = 0x0080, +}; + +#define WMI_MAX_SSID_LEN (32) + +struct wmi_connect_cmd { + u8 network_type; + u8 dot11_auth_mode; + u8 auth_mode; + u8 pairwise_crypto_type; + u8 pairwise_crypto_len; + u8 group_crypto_type; + u8 group_crypto_len; + u8 ssid_len; + u8 ssid[WMI_MAX_SSID_LEN]; + u8 channel; + u8 reserved0; + u8 bssid[WMI_MAC_LEN]; + __le32 ctrl_flags; + u8 dst_mac[WMI_MAC_LEN]; + u8 reserved1[2]; +} __packed; + + +/* + * WMI_RECONNECT_CMDID + */ +struct wmi_reconnect_cmd { + u8 channel; /* hint */ + u8 reserved; + u8 bssid[WMI_MAC_LEN]; /* mandatory if set */ +} __packed; + + +/* + * WMI_SET_PMK_CMDID + */ + +#define WMI_MIN_KEY_INDEX (0) +#define WMI_MAX_KEY_INDEX (3) +#define WMI_MAX_KEY_LEN (32) +#define WMI_PASSPHRASE_LEN (64) +#define WMI_PMK_LEN (32) + +struct wmi_set_pmk_cmd { + u8 pmk[WMI_PMK_LEN]; +} __packed; + + +/* + * WMI_SET_PASSPHRASE_CMDID + */ +struct wmi_set_passphrase_cmd { + u8 ssid[WMI_MAX_SSID_LEN]; + u8 passphrase[WMI_PASSPHRASE_LEN]; + u8 ssid_len; + u8 passphrase_len; +} __packed; + +/* + * WMI_ADD_CIPHER_KEY_CMDID + */ +enum wmi_key_usage { + WMI_KEY_USE_PAIRWISE = 0, + WMI_KEY_USE_GROUP = 1, + WMI_KEY_USE_TX = 2, /* default Tx Key - Static WEP only */ +}; + +struct wmi_add_cipher_key_cmd { + u8 key_index; + u8 key_type; + u8 key_usage; /* enum wmi_key_usage */ + u8 key_len; + u8 key_rsc[8]; /* key replay sequence counter */ + u8 key[WMI_MAX_KEY_LEN]; + u8 key_op_ctrl; /* Additional Key Control information */ + u8 mac[WMI_MAC_LEN]; +} __packed; + +/* + * WMI_DELETE_CIPHER_KEY_CMDID + */ +struct wmi_delete_cipher_key_cmd { + u8 key_index; + u8 mac[WMI_MAC_LEN]; +} __packed; + + +/* + * WMI_START_SCAN_CMDID + * + * Start L1 scan operation + * + * Returned events: + * - WMI_RX_MGMT_PACKET_EVENTID - for every probe resp. + * - WMI_SCAN_COMPLETE_EVENTID + */ +enum wmi_scan_type { + WMI_LONG_SCAN = 0, + WMI_SHORT_SCAN = 1, +}; + +struct wmi_start_scan_cmd { + u8 reserved[8]; + __le32 home_dwell_time; /* Max duration in the home channel(ms) */ + __le32 force_scan_interval; /* Time interval between scans (ms)*/ + u8 scan_type; /* wmi_scan_type */ + u8 num_channels; /* how many channels follow */ + struct { + u8 channel; + u8 reserved; + } channel_list[0]; /* channels ID's */ + /* 0 - 58320 MHz */ + /* 1 - 60480 MHz */ + /* 2 - 62640 MHz */ +} __packed; + +/* + * WMI_SET_PROBED_SSID_CMDID + */ +#define MAX_PROBED_SSID_INDEX (15) + +enum wmi_ssid_flag { + WMI_SSID_FLAG_DISABLE = 0, /* disables entry */ + WMI_SSID_FLAG_SPECIFIC = 1, /* probes specified ssid */ + WMI_SSID_FLAG_ANY = 2, /* probes for any ssid */ +}; + +struct wmi_probed_ssid_cmd { + u8 entry_index; /* 0 to MAX_PROBED_SSID_INDEX */ + u8 flag; /* enum wmi_ssid_flag */ + u8 ssid_len; + u8 ssid[WMI_MAX_SSID_LEN]; +} __packed; + +/* + * WMI_SET_APPIE_CMDID + * Add Application specified IE to a management frame + */ +struct wmi_set_appie_cmd { + u8 mgmt_frm_type; /* enum wmi_mgmt_frame_type */ + u8 reserved; + __le16 ie_len; /* Length of the IE to be added to MGMT frame */ + u8 ie_info[0]; +} __packed; + +#define WMI_MAX_IE_LEN (1024) + +struct wmi_pxmt_range_cfg_cmd { + u8 dst_mac[WMI_MAC_LEN]; + __le16 range; +} __packed; + +struct wmi_pxmt_snr2_range_cfg_cmd { + s8 snr2range_arr[WMI_PROX_RANGE_NUM-1]; +} __packed; + +/* + * WMI_RF_MGMT_CMDID + */ +enum wmi_rf_mgmt_type { + WMI_RF_MGMT_W_DISABLE = 0, + WMI_RF_MGMT_W_ENABLE = 1, + WMI_RF_MGMT_GET_STATUS = 2, +}; + +struct wmi_rf_mgmt_cmd { + __le32 rf_mgmt_type; +} __packed; + +/* + * WMI_SET_SSID_CMDID + */ +struct wmi_set_ssid_cmd { + __le32 ssid_len; + u8 ssid[WMI_MAX_SSID_LEN]; +} __packed; + +/* + * WMI_SET_PCP_CHANNEL_CMDID + */ +struct wmi_set_pcp_channel_cmd { + u8 channel; + u8 reserved[3]; +} __packed; + +/* + * WMI_BCON_CTRL_CMDID + */ +struct wmi_bcon_ctrl_cmd { + __le16 bcon_interval; + __le16 frag_num; + __le64 ss_mask; + u8 network_type; + u8 reserved; + u8 disable_sec_offload; + u8 disable_sec; +} __packed; + +/* + * WMI_SW_TX_REQ_CMDID + */ +struct wmi_sw_tx_req_cmd { + u8 dst_mac[WMI_MAC_LEN]; + __le16 len; + u8 payload[0]; +} __packed; + +/* + * WMI_VRING_CFG_CMDID + */ + +struct wmi_sw_ring_cfg { + __le64 ring_mem_base; + __le16 ring_size; + __le16 max_mpdu_size; +} __packed; + +struct wmi_vring_cfg_schd { + __le16 priority; + __le16 timeslot_us; +} __packed; + +enum wmi_vring_cfg_encap_trans_type { + WMI_VRING_ENC_TYPE_802_3 = 0, + WMI_VRING_ENC_TYPE_NATIVE_WIFI = 1, +}; + +enum wmi_vring_cfg_ds_cfg { + WMI_VRING_DS_PBSS = 0, + WMI_VRING_DS_STATION = 1, + WMI_VRING_DS_AP = 2, + WMI_VRING_DS_ADDR4 = 3, +}; + +enum wmi_vring_cfg_nwifi_ds_trans_type { + WMI_NWIFI_TX_TRANS_MODE_NO = 0, + WMI_NWIFI_TX_TRANS_MODE_AP2PBSS = 1, + WMI_NWIFI_TX_TRANS_MODE_STA2PBSS = 2, +}; + +enum wmi_vring_cfg_schd_params_priority { + WMI_SCH_PRIO_REGULAR = 0, + WMI_SCH_PRIO_HIGH = 1, +}; + +struct wmi_vring_cfg { + struct wmi_sw_ring_cfg tx_sw_ring; + u8 ringid; /* 0-23 vrings */ + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 encap_trans_type; + u8 ds_cfg; /* 802.3 DS cfg */ + u8 nwifi_ds_trans_type; + + #define VRING_CFG_MAC_CTRL_LIFETIME_EN_POS (0) + #define VRING_CFG_MAC_CTRL_LIFETIME_EN_LEN (1) + #define VRING_CFG_MAC_CTRL_LIFETIME_EN_MSK (0x1) + #define VRING_CFG_MAC_CTRL_AGGR_EN_POS (1) + #define VRING_CFG_MAC_CTRL_AGGR_EN_LEN (1) + #define VRING_CFG_MAC_CTRL_AGGR_EN_MSK (0x2) + u8 mac_ctrl; + + #define VRING_CFG_TO_RESOLUTION_VALUE_POS (0) + #define VRING_CFG_TO_RESOLUTION_VALUE_LEN (6) + #define VRING_CFG_TO_RESOLUTION_VALUE_MSK (0x3F) + u8 to_resolution; + u8 agg_max_wsize; + struct wmi_vring_cfg_schd schd_params; +} __packed; + +enum wmi_vring_cfg_cmd_action { + WMI_VRING_CMD_ADD = 0, + WMI_VRING_CMD_MODIFY = 1, + WMI_VRING_CMD_DELETE = 2, +}; + +struct wmi_vring_cfg_cmd { + __le32 action; + struct wmi_vring_cfg vring_cfg; +} __packed; + +/* + * WMI_VRING_BA_EN_CMDID + */ +struct wmi_vring_ba_en_cmd { + u8 ringid; + u8 agg_max_wsize; + __le16 ba_timeout; +} __packed; + +/* + * WMI_VRING_BA_DIS_CMDID + */ +struct wmi_vring_ba_dis_cmd { + u8 ringid; + u8 reserved; + __le16 reason; +} __packed; + +/* + * WMI_NOTIFY_REQ_CMDID + */ +struct wmi_notify_req_cmd { + u8 cid; + u8 reserved[3]; + __le32 interval_usec; +} __packed; + +/* + * WMI_CFG_RX_CHAIN_CMDID + */ +enum wmi_sniffer_cfg_mode { + WMI_SNIFFER_OFF = 0, + WMI_SNIFFER_ON = 1, +}; + +enum wmi_sniffer_cfg_phy_info_mode { + WMI_SNIFFER_PHY_INFO_DISABLED = 0, + WMI_SNIFFER_PHY_INFO_ENABLED = 1, +}; + +enum wmi_sniffer_cfg_phy_support { + WMI_SNIFFER_CP = 0, + WMI_SNIFFER_DP = 1, + WMI_SNIFFER_BOTH_PHYS = 2, +}; + +struct wmi_sniffer_cfg { + __le32 mode; /* enum wmi_sniffer_cfg_mode */ + __le32 phy_info_mode; /* enum wmi_sniffer_cfg_phy_info_mode */ + __le32 phy_support; /* enum wmi_sniffer_cfg_phy_support */ + u8 channel; + u8 reserved[3]; +} __packed; + +enum wmi_cfg_rx_chain_cmd_action { + WMI_RX_CHAIN_ADD = 0, + WMI_RX_CHAIN_DEL = 1, +}; + +enum wmi_cfg_rx_chain_cmd_decap_trans_type { + WMI_DECAP_TYPE_802_3 = 0, + WMI_DECAP_TYPE_NATIVE_WIFI = 1, +}; + +enum wmi_cfg_rx_chain_cmd_nwifi_ds_trans_type { + WMI_NWIFI_RX_TRANS_MODE_NO = 0, + WMI_NWIFI_RX_TRANS_MODE_PBSS2AP = 1, + WMI_NWIFI_RX_TRANS_MODE_PBSS2STA = 2, +}; + +struct wmi_cfg_rx_chain_cmd { + __le32 action; + struct wmi_sw_ring_cfg rx_sw_ring; + u8 mid; + u8 decap_trans_type; + + #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_POS (0) + #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_LEN (1) + #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_MSK (0x1) + u8 l2_802_3_offload_ctrl; + + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_POS (0) + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_LEN (1) + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_MSK (0x1) + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_POS (1) + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_LEN (1) + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_MSK (0x2) + u8 l2_nwifi_offload_ctrl; + + u8 vlan_id; + u8 nwifi_ds_trans_type; + + #define L3_L4_CTRL_IPV4_CHECKSUM_EN_POS (0) + #define L3_L4_CTRL_IPV4_CHECKSUM_EN_LEN (1) + #define L3_L4_CTRL_IPV4_CHECKSUM_EN_MSK (0x1) + #define L3_L4_CTRL_TCPIP_CHECKSUM_EN_POS (1) + #define L3_L4_CTRL_TCPIP_CHECKSUM_EN_LEN (1) + #define L3_L4_CTRL_TCPIP_CHECKSUM_EN_MSK (0x2) + u8 l3_l4_ctrl; + + #define RING_CTRL_OVERRIDE_PREFETCH_THRSH_POS (0) + #define RING_CTRL_OVERRIDE_PREFETCH_THRSH_LEN (1) + #define RING_CTRL_OVERRIDE_PREFETCH_THRSH_MSK (0x1) + #define RING_CTRL_OVERRIDE_WB_THRSH_POS (1) + #define RING_CTRL_OVERRIDE_WB_THRSH_LEN (1) + #define RING_CTRL_OVERRIDE_WB_THRSH_MSK (0x2) + #define RING_CTRL_OVERRIDE_ITR_THRSH_POS (2) + #define RING_CTRL_OVERRIDE_ITR_THRSH_LEN (1) + #define RING_CTRL_OVERRIDE_ITR_THRSH_MSK (0x4) + #define RING_CTRL_OVERRIDE_HOST_THRSH_POS (3) + #define RING_CTRL_OVERRIDE_HOST_THRSH_LEN (1) + #define RING_CTRL_OVERRIDE_HOST_THRSH_MSK (0x8) + u8 ring_ctrl; + + __le16 prefetch_thrsh; + __le16 wb_thrsh; + __le32 itr_value; + __le16 host_thrsh; + u8 reserved[2]; + struct wmi_sniffer_cfg sniffer_cfg; +} __packed; + +/* + * WMI_RCP_ADDBA_RESP_CMDID + */ +struct wmi_rcp_addba_resp_cmd { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 dialog_token; + __le16 status_code; + __le16 ba_param_set; /* ieee80211_ba_parameterset field to send */ + __le16 ba_timeout; +} __packed; + +/* + * WMI_RCP_DELBA_CMDID + */ +struct wmi_rcp_delba_cmd { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 reserved; + __le16 reason; +} __packed; + +/* + * WMI_RCP_ADDBA_REQ_CMDID + */ +struct wmi_rcp_addba_req_cmd { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 dialog_token; + /* ieee80211_ba_parameterset field as it received */ + __le16 ba_param_set; + __le16 ba_timeout; + /* ieee80211_ba_seqstrl field as it received */ + __le16 ba_seq_ctrl; +} __packed; + +/* + * WMI_SET_MAC_ADDRESS_CMDID + */ +struct wmi_set_mac_address_cmd { + u8 mac[WMI_MAC_LEN]; + u8 reserved[2]; +} __packed; + + +/* +* WMI_EAPOL_TX_CMDID +*/ +struct wmi_eapol_tx_cmd { + u8 dst_mac[WMI_MAC_LEN]; + __le16 eapol_len; + u8 eapol[0]; +} __packed; + +/* + * WMI_ECHO_CMDID + * + * Check FW is alive + * + * WMI_DEEP_ECHO_CMDID + * + * Check FW and ucode are alive + * + * Returned event: WMI_ECHO_RSP_EVENTID + * same event for both commands + */ +struct wmi_echo_cmd { + __le32 value; +} __packed; + +/* + * WMI Events + */ + +/* + * List of Events (target to host) + */ +enum wmi_event_id { + WMI_IMM_RSP_EVENTID = 0x0000, + WMI_READY_EVENTID = 0x1001, + WMI_CONNECT_EVENTID = 0x1002, + WMI_DISCONNECT_EVENTID = 0x1003, + WMI_SCAN_COMPLETE_EVENTID = 0x100a, + WMI_REPORT_STATISTICS_EVENTID = 0x100b, + WMI_RD_MEM_RSP_EVENTID = 0x1800, + WMI_FW_READY_EVENTID = 0x1801, + WMI_EXIT_FAST_MEM_ACC_MODE_EVENTID = 0x0200, + WMI_ECHO_RSP_EVENTID = 0x1803, + WMI_CONFIG_MAC_DONE_EVENTID = 0x1805, + WMI_CONFIG_PHY_DEBUG_DONE_EVENTID = 0x1806, + WMI_ADD_STATION_DONE_EVENTID = 0x1807, + WMI_ADD_DEBUG_TX_PCKT_DONE_EVENTID = 0x1808, + WMI_PHY_GET_STATISTICS_EVENTID = 0x1809, + WMI_FS_TUNE_DONE_EVENTID = 0x180a, + WMI_CORR_MEASURE_DONE_EVENTID = 0x180b, + WMI_TEMP_SENSE_DONE_EVENTID = 0x180e, + WMI_DC_CALIB_DONE_EVENTID = 0x180f, + WMI_IQ_TX_CALIB_DONE_EVENTID = 0x1811, + WMI_IQ_RX_CALIB_DONE_EVENTID = 0x1812, + WMI_SET_WORK_MODE_DONE_EVENTID = 0x1815, + WMI_LO_LEAKAGE_CALIB_DONE_EVENTID = 0x1816, + WMI_MARLON_R_ACTIVATE_DONE_EVENTID = 0x1817, + WMI_MARLON_R_READ_DONE_EVENTID = 0x1818, + WMI_MARLON_R_WRITE_DONE_EVENTID = 0x1819, + WMI_MARLON_R_TXRX_SEL_DONE_EVENTID = 0x181a, + WMI_SILENT_RSSI_CALIB_DONE_EVENTID = 0x181d, + + WMI_CFG_RX_CHAIN_DONE_EVENTID = 0x1820, + WMI_VRING_CFG_DONE_EVENTID = 0x1821, + WMI_RX_ON_DONE_EVENTID = 0x1822, + WMI_BA_STATUS_EVENTID = 0x1823, + WMI_RCP_ADDBA_REQ_EVENTID = 0x1824, + WMI_ADDBA_RESP_SENT_EVENTID = 0x1825, + WMI_DELBA_EVENTID = 0x1826, + WMI_GET_SSID_EVENTID = 0x1828, + WMI_GET_PCP_CHANNEL_EVENTID = 0x182a, + WMI_SW_TX_COMPLETE_EVENTID = 0x182b, + WMI_RX_OFF_DONE_EVENTID = 0x182c, + + WMI_READ_MAC_RXQ_EVENTID = 0x1830, + WMI_READ_MAC_TXQ_EVENTID = 0x1831, + WMI_WRITE_MAC_RXQ_EVENTID = 0x1832, + WMI_WRITE_MAC_TXQ_EVENTID = 0x1833, + WMI_WRITE_MAC_XQ_FIELD_EVENTID = 0x1834, + + WMI_BEAFORMING_MGMT_DONE_EVENTID = 0x1836, + WMI_BF_TXSS_MGMT_DONE_EVENTID = 0x1837, + WMI_BF_RXSS_MGMT_DONE_EVENTID = 0x1839, + WMI_RS_MGMT_DONE_EVENTID = 0x1852, + WMI_RF_MGMT_STATUS_EVENTID = 0x1853, + WMI_BF_SM_MGMT_DONE_EVENTID = 0x1838, + WMI_RX_MGMT_PACKET_EVENTID = 0x1840, + + /* Performance monitoring events */ + WMI_DATA_PORT_OPEN_EVENTID = 0x1860, + WMI_WBE_LINKDOWN_EVENTID = 0x1861, + + WMI_BF_CTRL_DONE_EVENTID = 0x1862, + WMI_NOTIFY_REQ_DONE_EVENTID = 0x1863, + WMI_GET_STATUS_DONE_EVENTID = 0x1864, + + WMI_UNIT_TEST_EVENTID = 0x1900, + WMI_FLASH_READ_DONE_EVENTID = 0x1902, + WMI_FLASH_WRITE_DONE_EVENTID = 0x1903, + + WMI_SET_CHANNEL_EVENTID = 0x9000, + WMI_ASSOC_REQ_EVENTID = 0x9001, + WMI_EAPOL_RX_EVENTID = 0x9002, + WMI_MAC_ADDR_RESP_EVENTID = 0x9003, + WMI_FW_VER_EVENTID = 0x9004, +}; + +/* + * Events data structures + */ + +/* + * WMI_RF_MGMT_STATUS_EVENTID + */ +enum wmi_rf_status { + WMI_RF_ENABLED = 0, + WMI_RF_DISABLED_HW = 1, + WMI_RF_DISABLED_SW = 2, + WMI_RF_DISABLED_HW_SW = 3, +}; + +struct wmi_rf_mgmt_status_event { + __le32 rf_status; +} __packed; + +/* + * WMI_GET_STATUS_DONE_EVENTID + */ +struct wmi_get_status_done_event { + __le32 is_associated; + u8 cid; + u8 reserved0[3]; + u8 bssid[WMI_MAC_LEN]; + u8 channel; + u8 reserved1; + u8 network_type; + u8 reserved2[3]; + __le32 ssid_len; + u8 ssid[WMI_MAX_SSID_LEN]; + __le32 rf_status; + __le32 is_secured; +} __packed; + +/* + * WMI_FW_VER_EVENTID + */ +struct wmi_fw_ver_event { + u8 major; + u8 minor; + __le16 subminor; + __le16 build; +} __packed; + +/* +* WMI_MAC_ADDR_RESP_EVENTID +*/ +struct wmi_mac_addr_resp_event { + u8 mac[WMI_MAC_LEN]; + u8 auth_mode; + u8 crypt_mode; + __le32 offload_mode; +} __packed; + +/* +* WMI_EAPOL_RX_EVENTID +*/ +struct wmi_eapol_rx_event { + u8 src_mac[WMI_MAC_LEN]; + __le16 eapol_len; + u8 eapol[0]; +} __packed; + +/* +* WMI_READY_EVENTID +*/ +enum wmi_phy_capability { + WMI_11A_CAPABILITY = 1, + WMI_11G_CAPABILITY = 2, + WMI_11AG_CAPABILITY = 3, + WMI_11NA_CAPABILITY = 4, + WMI_11NG_CAPABILITY = 5, + WMI_11NAG_CAPABILITY = 6, + WMI_11AD_CAPABILITY = 7, + WMI_11N_CAPABILITY_OFFSET = WMI_11NA_CAPABILITY - WMI_11A_CAPABILITY, +}; + +struct wmi_ready_event { + __le32 sw_version; + __le32 abi_version; + u8 mac[WMI_MAC_LEN]; + u8 phy_capability; /* enum wmi_phy_capability */ + u8 reserved; +} __packed; + +/* + * WMI_NOTIFY_REQ_DONE_EVENTID + */ +struct wmi_notify_req_done_event { + __le32 status; + __le64 tsf; + __le32 snr_val; + __le32 tx_tpt; + __le32 tx_goodput; + __le32 rx_goodput; + __le16 bf_mcs; + __le16 my_rx_sector; + __le16 my_tx_sector; + __le16 other_rx_sector; + __le16 other_tx_sector; + __le16 range; +} __packed; + +/* + * WMI_CONNECT_EVENTID + */ +struct wmi_connect_event { + u8 channel; + u8 reserved0; + u8 bssid[WMI_MAC_LEN]; + __le16 listen_interval; + __le16 beacon_interval; + u8 network_type; + u8 reserved1[3]; + u8 beacon_ie_len; + u8 assoc_req_len; + u8 assoc_resp_len; + u8 cid; + u8 reserved2[3]; + u8 assoc_info[0]; +} __packed; + +/* + * WMI_DISCONNECT_EVENTID + */ +enum wmi_disconnect_reason { + WMI_DIS_REASON_NO_NETWORK_AVAIL = 1, + WMI_DIS_REASON_LOST_LINK = 2, /* bmiss */ + WMI_DIS_REASON_DISCONNECT_CMD = 3, + WMI_DIS_REASON_BSS_DISCONNECTED = 4, + WMI_DIS_REASON_AUTH_FAILED = 5, + WMI_DIS_REASON_ASSOC_FAILED = 6, + WMI_DIS_REASON_NO_RESOURCES_AVAIL = 7, + WMI_DIS_REASON_CSERV_DISCONNECT = 8, + WMI_DIS_REASON_INVALID_PROFILE = 10, + WMI_DIS_REASON_DOT11H_CHANNEL_SWITCH = 11, + WMI_DIS_REASON_PROFILE_MISMATCH = 12, + WMI_DIS_REASON_CONNECTION_EVICTED = 13, + WMI_DIS_REASON_IBSS_MERGE = 14, +}; + +struct wmi_disconnect_event { + __le16 protocol_reason_status; /* reason code, see 802.11 spec. */ + u8 bssid[WMI_MAC_LEN]; /* set if known */ + u8 disconnect_reason; /* see wmi_disconnect_reason_e */ + u8 assoc_resp_len; + u8 assoc_info[0]; +} __packed; + +/* + * WMI_SCAN_COMPLETE_EVENTID + */ +struct wmi_scan_complete_event { + __le32 status; +} __packed; + +/* + * WMI_BA_STATUS_EVENTID + */ +enum wmi_vring_ba_status { + WMI_BA_AGREED = 0, + WMI_BA_NON_AGREED = 1, +}; + +struct wmi_vring_ba_status_event { + __le16 status; + u8 reserved[2]; + u8 ringid; + u8 agg_wsize; + __le16 ba_timeout; +} __packed; + +/* + * WMI_DELBA_EVENTID + */ +struct wmi_delba_event { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 from_initiator; + __le16 reason; +} __packed; + +/* + * WMI_VRING_CFG_DONE_EVENTID + */ +enum wmi_vring_cfg_done_event_status { + WMI_VRING_CFG_SUCCESS = 0, + WMI_VRING_CFG_FAILURE = 1, +}; + +struct wmi_vring_cfg_done_event { + u8 ringid; + u8 status; + u8 reserved[2]; + __le32 tx_vring_tail_ptr; +} __packed; + +/* + * WMI_ADDBA_RESP_SENT_EVENTID + */ +enum wmi_rcp_addba_resp_sent_event_status { + WMI_ADDBA_SUCCESS = 0, + WMI_ADDBA_FAIL = 1, +}; + +struct wmi_rcp_addba_resp_sent_event { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 reserved; + __le16 status; +} __packed; + +/* + * WMI_RCP_ADDBA_REQ_EVENTID + */ +struct wmi_rcp_addba_req_event { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 dialog_token; + __le16 ba_param_set; /* ieee80211_ba_parameterset as it received */ + __le16 ba_timeout; + __le16 ba_seq_ctrl; /* ieee80211_ba_seqstrl field as it received */ +} __packed; + +/* + * WMI_CFG_RX_CHAIN_DONE_EVENTID + */ +enum wmi_cfg_rx_chain_done_event_status { + WMI_CFG_RX_CHAIN_SUCCESS = 1, +}; + +struct wmi_cfg_rx_chain_done_event { + __le32 rx_ring_tail_ptr; /* Rx V-Ring Tail pointer */ + __le32 status; +} __packed; + +/* + * WMI_WBE_LINKDOWN_EVENTID + */ +enum wmi_wbe_link_down_event_reason { + WMI_WBE_REASON_USER_REQUEST = 0, + WMI_WBE_REASON_RX_DISASSOC = 1, + WMI_WBE_REASON_BAD_PHY_LINK = 2, +}; + +struct wmi_wbe_link_down_event { + u8 cid; + u8 reserved[3]; + __le32 reason; +} __packed; + +/* + * WMI_DATA_PORT_OPEN_EVENTID + */ +struct wmi_data_port_open_event { + u8 cid; + u8 reserved[3]; +} __packed; + +/* + * WMI_GET_PCP_CHANNEL_EVENTID + */ +struct wmi_get_pcp_channel_event { + u8 channel; + u8 reserved[3]; +} __packed; + +/* + * WMI_SW_TX_COMPLETE_EVENTID + */ +enum wmi_sw_tx_status { + WMI_TX_SW_STATUS_SUCCESS = 0, + WMI_TX_SW_STATUS_FAILED_NO_RESOURCES = 1, + WMI_TX_SW_STATUS_FAILED_TX = 2, +}; + +struct wmi_sw_tx_complete_event { + u8 status; /* enum wmi_sw_tx_status */ + u8 reserved[3]; +} __packed; + +/* + * WMI_GET_SSID_EVENTID + */ +struct wmi_get_ssid_event { + __le32 ssid_len; + u8 ssid[WMI_MAX_SSID_LEN]; +} __packed; + +/* + * WMI_RX_MGMT_PACKET_EVENTID + */ +struct wmi_rx_mgmt_info { + u8 mcs; + s8 snr; + __le16 range; + __le16 stype; + __le16 status; + __le32 len; + u8 qid; + u8 mid; + u8 cid; + u8 channel; /* From Radio MNGR */ +} __packed; + +struct wmi_rx_mgmt_packet_event { + struct wmi_rx_mgmt_info info; + u8 payload[0]; +} __packed; + +/* + * WMI_ECHO_RSP_EVENTID + */ +struct wmi_echo_event { + __le32 echoed_value; +} __packed; + +#endif /* __WILOCITY_WMI_H__ */ -- cgit v1.2.3-70-g09d2 From 0981c3b24ef664f5611008a6e6d0622fac6d892b Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Wed, 9 Jan 2013 16:07:48 +0530 Subject: ath9k_htc: Fix memory leak SKBs that are allocated in the HTC layer do not have callbacks registered and hence ended up not being freed, Fix this by freeing them properly in the TX completion routine. Cc: Reported-by: Larry Finger Signed-off-by: Sujith Manoharan Tested-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/htc_hst.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c index 4a9570dfba7..aac4a406a51 100644 --- a/drivers/net/wireless/ath/ath9k/htc_hst.c +++ b/drivers/net/wireless/ath/ath9k/htc_hst.c @@ -344,6 +344,8 @@ void ath9k_htc_txcompletion_cb(struct htc_target *htc_handle, endpoint->ep_callbacks.tx(endpoint->ep_callbacks.priv, skb, htc_hdr->endpoint_id, txok); + } else { + kfree_skb(skb); } } -- cgit v1.2.3-70-g09d2 From a3dc48e82bb146ef11cf75676c8410c1df29b0c4 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 9 Jan 2013 16:16:52 +0100 Subject: ath9k: do not link receive buffers during flush On AR9300 the rx FIFO needs to be empty during reset to ensure that no further DMA activity is generated, otherwise it might lead to memory corruption issues. Cc: stable@vger.kernel.org Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/recv.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index d4df98a938b..01c3ba47a5b 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -744,6 +744,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc, return NULL; } + list_del(&bf->list); if (!bf->bf_mpdu) return bf; @@ -1254,14 +1255,15 @@ requeue_drop_frag: sc->rx.frag = NULL; } requeue: + list_add_tail(&bf->list, &sc->rx.rxbuf); + if (flush) + continue; + if (edma) { - list_add_tail(&bf->list, &sc->rx.rxbuf); ath_rx_edma_buf_link(sc, qtype); } else { - list_move_tail(&bf->list, &sc->rx.rxbuf); ath_rx_buf_link(sc, bf); - if (!flush) - ath9k_hw_rxena(ah); + ath9k_hw_rxena(ah); } } while (1); -- cgit v1.2.3-70-g09d2 From 1adb2e2b5f85023d17eb4f95386a57029df27c88 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 9 Jan 2013 16:16:53 +0100 Subject: ath9k: fix double-free bug on beacon generate failure When the next beacon is sent, the ath_buf from the previous run is reused. If getting a new beacon from mac80211 fails, bf->bf_mpdu is not reset, yet the skb is freed, leading to a double-free on the next beacon tx attempt, resulting in a system crash. Cc: stable@vger.kernel.org Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/beacon.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 531fffd801a..4eb269d6b97 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -147,6 +147,7 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, skb->len, DMA_TO_DEVICE); dev_kfree_skb_any(skb); bf->bf_buf_addr = 0; + bf->bf_mpdu = NULL; } skb = ieee80211_beacon_get(hw, vif); -- cgit v1.2.3-70-g09d2 From 3adcf20afb585993ffee24de36d1975f6b26b120 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 9 Jan 2013 16:16:54 +0100 Subject: ath9k: remove the WARN_ON that triggers if generating a beacon fails During teardown, mac80211 will not return a new beacon. This is normal and handled properly in the driver, so there's no need to spam the user with a kernel warning here. Cc: stable@vger.kernel.org Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/beacon.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 4eb269d6b97..2ca355e94da 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -360,7 +360,6 @@ void ath9k_beacon_tasklet(unsigned long data) return; bf = ath9k_beacon_generate(sc->hw, vif); - WARN_ON(!bf); if (sc->beacon.bmisscnt != 0) { ath_dbg(common, BSTUCK, "resume beacon xmit after %u misses\n", -- cgit v1.2.3-70-g09d2 From 7fc00a3054b70b1794c2d64db703eb467ad0365c Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 9 Jan 2013 16:16:55 +0100 Subject: ath9k: add a better fix for the rx tasklet vs rx flush race Ensure that the rx tasklet is no longer running when entering the reset path. Also remove the distinction between flush and no-flush frame processing. If a frame has been received and ACKed by the hardware, the stack needs to see it, so that the BA receive window does not go out of sync. Cc: stable@vger.kernel.org Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 - drivers/net/wireless/ath/ath9k/debug.c | 1 - drivers/net/wireless/ath/ath9k/debug.h | 2 -- drivers/net/wireless/ath/ath9k/main.c | 4 ++++ drivers/net/wireless/ath/ath9k/recv.c | 15 --------------- 5 files changed, 4 insertions(+), 19 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 86e26a19efd..7335e9ef024 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -646,7 +646,6 @@ void ath_ant_comb_update(struct ath_softc *sc); enum sc_op_flags { SC_OP_INVALID, SC_OP_BEACONS, - SC_OP_RXFLUSH, SC_OP_ANI_RUN, SC_OP_PRIM_STA_VIF, SC_OP_HW_RESET, diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 13ff9edc240..e585fc827c5 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -861,7 +861,6 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, RXS_ERR("RX-LENGTH-ERR", rx_len_err); RXS_ERR("RX-OOM-ERR", rx_oom_err); RXS_ERR("RX-RATE-ERR", rx_rate_err); - RXS_ERR("RX-DROP-RXFLUSH", rx_drop_rxflush); RXS_ERR("RX-TOO-MANY-FRAGS", rx_too_many_frags_err); PHY_ERR("UNDERRUN ERR", ATH9K_PHYERR_UNDERRUN); diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 375c3b46411..6df2ab62dcb 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -216,7 +216,6 @@ struct ath_tx_stats { * @rx_oom_err: No. of frames dropped due to OOM issues. * @rx_rate_err: No. of frames dropped due to rate errors. * @rx_too_many_frags_err: Frames dropped due to too-many-frags received. - * @rx_drop_rxflush: No. of frames dropped due to RX-FLUSH. * @rx_beacons: No. of beacons received. * @rx_frags: No. of rx-fragements received. */ @@ -235,7 +234,6 @@ struct ath_rx_stats { u32 rx_oom_err; u32 rx_rate_err; u32 rx_too_many_frags_err; - u32 rx_drop_rxflush; u32 rx_beacons; u32 rx_frags; }; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index be30a9af152..d3cf01ec2d1 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -196,6 +196,8 @@ static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush) ath9k_debug_samp_bb_mac(sc); ath9k_hw_disable_interrupts(ah); + tasklet_disable(&sc->intr_tq); + if (!ath_stoprecv(sc)) ret = false; @@ -210,6 +212,8 @@ static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush) ath_flushrecv(sc); } + tasklet_enable(&sc->intr_tq); + return ret; } diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 01c3ba47a5b..6b83b3bbfe8 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -286,7 +286,6 @@ int ath_rx_init(struct ath_softc *sc, int nbufs) spin_lock_init(&sc->sc_pcu_lock); spin_lock_init(&sc->rx.rxbuflock); - clear_bit(SC_OP_RXFLUSH, &sc->sc_flags); common->rx_bufsize = IEEE80211_MAX_MPDU_LEN / 2 + sc->sc_ah->caps.rx_status_len; @@ -501,11 +500,9 @@ bool ath_stoprecv(struct ath_softc *sc) void ath_flushrecv(struct ath_softc *sc) { - set_bit(SC_OP_RXFLUSH, &sc->sc_flags); if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ath_rx_tasklet(sc, 1, true); ath_rx_tasklet(sc, 1, false); - clear_bit(SC_OP_RXFLUSH, &sc->sc_flags); } static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb) @@ -1067,9 +1064,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) do { bool decrypt_error = false; - /* If handling rx interrupt and flush is in progress => exit */ - if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags) && (flush == 0)) - break; memset(&rs, 0, sizeof(rs)); if (edma) @@ -1112,15 +1106,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) ath_debug_stat_rx(sc, &rs); - /* - * If we're asked to flush receive queue, directly - * chain it back at the queue without processing it. - */ - if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags)) { - RX_STAT_INC(rx_drop_rxflush); - goto requeue_drop_frag; - } - memset(rxs, 0, sizeof(struct ieee80211_rx_status)); rxs->mactime = (tsf & ~0xffffffffULL) | rs.rs_tstamp; -- cgit v1.2.3-70-g09d2 From 4b883f021b9ccf2df3d14425e6e610281fb6a35e Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 9 Jan 2013 16:16:56 +0100 Subject: ath9k: fix rx flush handling Right now the rx flush is not doing anything useful on AR9003+, as it only works if the buffers in the rx FIFO have not been purged yet, as is done by ath_stoprecv. To fix this, always call ath_flushrecv from within ath_stoprecv before the FIFO is emptied, but still after the hw receive path has been stopped. This ensures that frames received (and ACKed by the hardware) shortly before a reset will be seen by the software, which should improve A-MPDU session stability. Cc: stable@vger.kernel.org Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 - drivers/net/wireless/ath/ath9k/main.c | 16 +++------------- drivers/net/wireless/ath/ath9k/recv.c | 16 +++++++++------- 3 files changed, 12 insertions(+), 21 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 7335e9ef024..32b2d5cd9a5 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -328,7 +328,6 @@ struct ath_rx { int ath_startrecv(struct ath_softc *sc); bool ath_stoprecv(struct ath_softc *sc); -void ath_flushrecv(struct ath_softc *sc); u32 ath_calcrxfilter(struct ath_softc *sc); int ath_rx_init(struct ath_softc *sc, int nbufs); void ath_rx_cleanup(struct ath_softc *sc); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index d3cf01ec2d1..9844b758f81 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -182,7 +182,7 @@ static void ath_restart_work(struct ath_softc *sc) ath_start_ani(sc); } -static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush) +static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx) { struct ath_hw *ah = sc->sc_ah; bool ret = true; @@ -204,14 +204,6 @@ static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush) if (!ath_drain_all_txq(sc, retry_tx)) ret = false; - if (!flush) { - if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) - ath_rx_tasklet(sc, 1, true); - ath_rx_tasklet(sc, 1, false); - } else { - ath_flushrecv(sc); - } - tasklet_enable(&sc->intr_tq); return ret; @@ -266,7 +258,6 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan, struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_cal_data *caldata = NULL; bool fastcc = true; - bool flush = false; int r; __ath_cancel_work(sc); @@ -280,11 +271,10 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan, if (!hchan) { fastcc = false; - flush = true; hchan = ah->curchan; } - if (!ath_prepare_reset(sc, retry_tx, flush)) + if (!ath_prepare_reset(sc, retry_tx)) fastcc = false; ath_dbg(common, CONFIG, "Reset to %u MHz, HT40: %d fastcc: %d\n", @@ -808,7 +798,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) ath9k_hw_cfg_gpio_input(ah, ah->led_pin); } - ath_prepare_reset(sc, false, true); + ath_prepare_reset(sc, false); if (sc->rx.frag) { dev_kfree_skb_any(sc->rx.frag); diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 6b83b3bbfe8..67f58d4bb10 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -472,6 +472,13 @@ start_recv: return 0; } +static void ath_flushrecv(struct ath_softc *sc) +{ + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) + ath_rx_tasklet(sc, 1, true); + ath_rx_tasklet(sc, 1, false); +} + bool ath_stoprecv(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; @@ -482,6 +489,8 @@ bool ath_stoprecv(struct ath_softc *sc) ath9k_hw_setrxfilter(ah, 0); stopped = ath9k_hw_stopdmarecv(ah, &reset); + ath_flushrecv(sc); + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ath_edma_stop_recv(sc); else @@ -498,13 +507,6 @@ bool ath_stoprecv(struct ath_softc *sc) return stopped && !reset; } -void ath_flushrecv(struct ath_softc *sc) -{ - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) - ath_rx_tasklet(sc, 1, true); - ath_rx_tasklet(sc, 1, false); -} - static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb) { /* Check whether the Beacon frame has DTIM indicating buffered bc/mc */ -- cgit v1.2.3-70-g09d2 From 463e3ed3eacc8f47866e5d612bd8ee0bcee5e2f0 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 14 Jan 2013 10:50:15 +0100 Subject: ath9k: remove sc->rx.rxbuflock to fix a deadlock The commit "ath9k: fix rx flush handling" added a deadlock that happens because ath_rx_tasklet is called in a section that has already taken the rx buffer lock. It seems that the only purpose of the rxbuflock was a band-aid fix to the reset vs rx tasklet race, which has been properly fixed in the commit "ath9k: add a better fix for the rx tasklet vs rx flush race". Now that the fix is in, we can safely remove the lock to avoid such issues. Cc: stable@vger.kernel.org Reported-by: Sujith Manoharan Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 - drivers/net/wireless/ath/ath9k/recv.c | 13 ------------- 2 files changed, 14 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 32b2d5cd9a5..42794c546a4 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -317,7 +317,6 @@ struct ath_rx { u32 *rxlink; u32 num_pkts; unsigned int rxfilter; - spinlock_t rxbuflock; struct list_head rxbuf; struct ath_descdma rxdma; struct ath_buf *rx_bufptr; diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 67f58d4bb10..90752f24697 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -254,8 +254,6 @@ rx_init_fail: static void ath_edma_start_recv(struct ath_softc *sc) { - spin_lock_bh(&sc->rx.rxbuflock); - ath9k_hw_rxena(sc->sc_ah); ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_HP, @@ -267,8 +265,6 @@ static void ath_edma_start_recv(struct ath_softc *sc) ath_opmode_init(sc); ath9k_hw_startpcureceive(sc->sc_ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)); - - spin_unlock_bh(&sc->rx.rxbuflock); } static void ath_edma_stop_recv(struct ath_softc *sc) @@ -285,7 +281,6 @@ int ath_rx_init(struct ath_softc *sc, int nbufs) int error = 0; spin_lock_init(&sc->sc_pcu_lock); - spin_lock_init(&sc->rx.rxbuflock); common->rx_bufsize = IEEE80211_MAX_MPDU_LEN / 2 + sc->sc_ah->caps.rx_status_len; @@ -446,7 +441,6 @@ int ath_startrecv(struct ath_softc *sc) return 0; } - spin_lock_bh(&sc->rx.rxbuflock); if (list_empty(&sc->rx.rxbuf)) goto start_recv; @@ -467,8 +461,6 @@ start_recv: ath_opmode_init(sc); ath9k_hw_startpcureceive(ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)); - spin_unlock_bh(&sc->rx.rxbuflock); - return 0; } @@ -484,7 +476,6 @@ bool ath_stoprecv(struct ath_softc *sc) struct ath_hw *ah = sc->sc_ah; bool stopped, reset = false; - spin_lock_bh(&sc->rx.rxbuflock); ath9k_hw_abortpcurecv(ah); ath9k_hw_setrxfilter(ah, 0); stopped = ath9k_hw_stopdmarecv(ah, &reset); @@ -495,7 +486,6 @@ bool ath_stoprecv(struct ath_softc *sc) ath_edma_stop_recv(sc); else sc->rx.rxlink = NULL; - spin_unlock_bh(&sc->rx.rxbuflock); if (!(ah->ah_flags & AH_UNPLUGGED) && unlikely(!stopped)) { @@ -1059,7 +1049,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) dma_type = DMA_FROM_DEVICE; qtype = hp ? ATH9K_RX_QUEUE_HP : ATH9K_RX_QUEUE_LP; - spin_lock_bh(&sc->rx.rxbuflock); tsf = ath9k_hw_gettsf64(ah); tsf_lower = tsf & 0xffffffff; @@ -1254,8 +1243,6 @@ requeue: } } while (1); - spin_unlock_bh(&sc->rx.rxbuflock); - if (!(ah->imask & ATH9K_INT_RXEOL)) { ah->imask |= (ATH9K_INT_RXEOL | ATH9K_INT_RXORN); ath9k_hw_set_interrupts(ah); -- cgit v1.2.3-70-g09d2 From 4668cce527acb3bd048c5e6c99b157a14b214671 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 14 Jan 2013 16:56:46 +0100 Subject: ath9k: disable the tasklet before taking the PCU lock Fixes a reported CPU soft lockup where the tasklet tries to acquire the lock and blocks while ath_prepare_reset (holding the lock) waits for it to complete. Cc: stable@vger.kernel.org Reported-by: Robert Shade Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 9844b758f81..3796e65c26f 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -196,16 +196,12 @@ static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx) ath9k_debug_samp_bb_mac(sc); ath9k_hw_disable_interrupts(ah); - tasklet_disable(&sc->intr_tq); - if (!ath_stoprecv(sc)) ret = false; if (!ath_drain_all_txq(sc, retry_tx)) ret = false; - tasklet_enable(&sc->intr_tq); - return ret; } @@ -262,6 +258,7 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan, __ath_cancel_work(sc); + tasklet_disable(&sc->intr_tq); spin_lock_bh(&sc->sc_pcu_lock); if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) { @@ -296,6 +293,8 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan, out: spin_unlock_bh(&sc->sc_pcu_lock); + tasklet_enable(&sc->intr_tq); + return r; } -- cgit v1.2.3-70-g09d2 From 4a8f199508d79ff8a7d1e22f47b912baaf225336 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 20 Jan 2013 21:55:20 +0100 Subject: ath9k_hw: fix calibration issues on chainmask that don't include chain 0 Cc: stable@vger.kernel.org Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_calib.c | 2 ++ drivers/net/wireless/ath/ath9k/ar9003_phy.c | 2 +- drivers/net/wireless/ath/ath9k/hw.h | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 8b0d8dcd762..56317b0fb6b 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -976,6 +976,8 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, AR_PHY_CL_TAB_1, AR_PHY_CL_TAB_2 }; + ar9003_hw_set_chain_masks(ah, ah->caps.rx_chainmask, ah->caps.tx_chainmask); + if (rtt) { if (!ar9003_hw_rtt_restore(ah, chan)) run_rtt_cal = true; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index ce19c09fa8e..8290eddaf0e 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -586,7 +586,7 @@ static void ar9003_hw_init_bb(struct ath_hw *ah, ath9k_hw_synth_delay(ah, chan, synthDelay); } -static void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx) +void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx) { switch (rx) { case 0x5: diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 7f1a8e91c90..9d26fc56ca5 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -1066,6 +1066,7 @@ void ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain); int ar9003_paprd_init_table(struct ath_hw *ah); bool ar9003_paprd_is_done(struct ath_hw *ah); bool ar9003_is_paprd_enabled(struct ath_hw *ah); +void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx); /* Hardware family op attach helpers */ void ar5008_hw_attach_phy_ops(struct ath_hw *ah); -- cgit v1.2.3-70-g09d2 From 24171dd92096fc370b195f3f6bdc0798855dc3f9 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 20 Jan 2013 21:55:21 +0100 Subject: ath9k_hw: fix chain swap setting when setting rx chainmask to 5 Chain swapping should only be enabled when the EEPROM chainmask is set to 5, regardless of what the runtime chainmask is. Cc: stable@vger.kernel.org Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_phy.c | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 8290eddaf0e..3afc24bde6d 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -588,30 +588,17 @@ static void ar9003_hw_init_bb(struct ath_hw *ah, void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx) { - switch (rx) { - case 0x5: + if (ah->caps.tx_chainmask == 5 || ah->caps.rx_chainmask == 5) REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); - case 0x3: - case 0x1: - case 0x2: - case 0x7: - REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx); - REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx); - break; - default: - break; - } + + REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx); + REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx); if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) && (tx == 0x7)) - REG_WRITE(ah, AR_SELFGEN_MASK, 0x3); - else - REG_WRITE(ah, AR_SELFGEN_MASK, tx); + tx = 3; - if (tx == 0x5) { - REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, - AR_PHY_SWAP_ALT_CHAIN); - } + REG_WRITE(ah, AR_SELFGEN_MASK, tx); } /* -- cgit v1.2.3-70-g09d2 From fea92cbf0850d788683827990670d3968f893327 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 20 Jan 2013 21:55:22 +0100 Subject: ath9k: allow setting arbitrary antenna masks on AR9003+ Cc: stable@vger.kernel.org Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 3796e65c26f..dd91f8fdc01 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1826,6 +1826,9 @@ static u32 fill_chainmask(u32 cap, u32 new) static bool validate_antenna_mask(struct ath_hw *ah, u32 val) { + if (AR_SREV_9300_20_OR_LATER(ah)) + return true; + switch (val & 0x7) { case 0x1: case 0x3: -- cgit v1.2.3-70-g09d2