diff options
author | John W. Linville <linville@tuxdriver.com> | 2014-01-13 14:40:03 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2014-01-13 14:40:03 -0500 |
commit | 559c33d84da71e07816e692bfd73ad92675256a6 (patch) | |
tree | d599948e9c88f115cf697b2a52f0359b3ecd77d4 | |
parent | ec665facde820179d71d0f064e692f449df2b94f (diff) | |
parent | 51ab1a0a09a894442e8b091d57222c5425b6d700 (diff) | |
download | linux-559c33d84da71e07816e692bfd73ad92675256a6.tar.bz2 |
Merge branch 'for-linville' of git://github.com/kvalo/ath
-rw-r--r-- | drivers/net/wireless/ath/ath10k/Kconfig | 7 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/core.h | 11 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/debug.c | 66 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/htt.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/htt_rx.c | 15 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/hw.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/mac.c | 374 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/trace.h | 21 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/txrx.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/wmi.c | 106 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/wmi.h | 61 |
11 files changed, 642 insertions, 23 deletions
diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig index 82e8088ca9b4..a6f5285235af 100644 --- a/drivers/net/wireless/ath/ath10k/Kconfig +++ b/drivers/net/wireless/ath/ath10k/Kconfig @@ -37,3 +37,10 @@ config ATH10K_TRACING ---help--- Select this to ath10k use tracing infrastructure. +config ATH10K_DFS_CERTIFIED + bool "Atheros DFS support for certified platforms" + depends on ATH10K && CFG80211_CERTIFICATION_ONUS + default n + ---help--- + This option enables DFS support for initiating radiation on + ath10k. diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 79726e0fe2f0..ade1781c7186 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -253,6 +253,9 @@ struct ath10k_vif { u8 bssid[ETH_ALEN]; } ibss; } u; + + u8 fixed_rate; + u8 fixed_nss; }; struct ath10k_vif_iter { @@ -272,6 +275,8 @@ struct ath10k_debug { struct delayed_work htt_stats_dwork; struct ath10k_dfs_stats dfs_stats; struct ath_dfs_pool_stats dfs_pool_stats; + + u32 fw_dbglog_mask; }; enum ath10k_state { @@ -306,6 +311,9 @@ enum ath10k_fw_features { /* firmware support tx frame management over WMI, otherwise it's HTT */ ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX = 2, + /* Firmware does not support P2P */ + ATH10K_FW_FEATURE_NO_P2P = 3, + /* keep last */ ATH10K_FW_FEATURE_COUNT, }; @@ -429,6 +437,9 @@ struct ath10k { struct list_head peers; wait_queue_head_t peer_mapping_wq; + /* number of created peers; protected by data_lock */ + int num_peers; + struct work_struct offchan_tx_work; struct sk_buff_head offchan_tx_queue; struct completion offchan_tx_completed; diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 6bdfad3144af..6debd281350a 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -614,6 +614,61 @@ static const struct file_operations fops_htt_stats_mask = { .llseek = default_llseek, }; +static ssize_t ath10k_read_fw_dbglog(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + unsigned int len; + char buf[32]; + + len = scnprintf(buf, sizeof(buf), "0x%08x\n", + ar->debug.fw_dbglog_mask); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath10k_write_fw_dbglog(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + unsigned long mask; + int ret; + + ret = kstrtoul_from_user(user_buf, count, 0, &mask); + if (ret) + return ret; + + mutex_lock(&ar->conf_mutex); + + ar->debug.fw_dbglog_mask = mask; + + if (ar->state == ATH10K_STATE_ON) { + ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask); + if (ret) { + ath10k_warn("dbglog cfg failed from debugfs: %d\n", + ret); + goto exit; + } + } + + ret = count; + +exit: + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static const struct file_operations fops_fw_dbglog = { + .read = ath10k_read_fw_dbglog, + .write = ath10k_write_fw_dbglog, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath10k_debug_start(struct ath10k *ar) { int ret; @@ -625,6 +680,14 @@ int ath10k_debug_start(struct ath10k *ar) /* continue normally anyway, this isn't serious */ ath10k_warn("failed to start htt stats workqueue: %d\n", ret); + if (ar->debug.fw_dbglog_mask) { + ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask); + if (ret) + /* not serious */ + ath10k_warn("failed to enable dbglog during start: %d", + ret); + } + return 0; } @@ -747,6 +810,9 @@ int ath10k_debug_create(struct ath10k *ar) debugfs_create_file("htt_stats_mask", S_IRUSR, ar->debug.debugfs_phy, ar, &fops_htt_stats_mask); + debugfs_create_file("fw_dbglog", S_IRUSR, ar->debug.debugfs_phy, + ar, &fops_fw_dbglog); + if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED)) { debugfs_create_file("dfs_simulate_radar", S_IWUSR, ar->debug.debugfs_phy, ar, diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 7fc7919ea5f5..b93ae355bc08 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -1183,6 +1183,7 @@ struct htt_rx_info { } rate; bool fcs_err; bool amsdu_more; + bool mic_err; }; struct ath10k_htt { diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index fcb534f2f28f..fe8bd1b59f0e 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -838,6 +838,20 @@ static bool ath10k_htt_rx_has_fcs_err(struct sk_buff *skb) return false; } +static bool ath10k_htt_rx_has_mic_err(struct sk_buff *skb) +{ + struct htt_rx_desc *rxd; + u32 flags; + + rxd = (void *)skb->data - sizeof(*rxd); + flags = __le32_to_cpu(rxd->attention.flags); + + if (flags & RX_ATTENTION_FLAGS_TKIP_MIC_ERR) + return true; + + return false; +} + static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb) { struct htt_rx_desc *rxd; @@ -960,6 +974,7 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, info.skb = msdu_head; info.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head); + info.mic_err = ath10k_htt_rx_has_mic_err(msdu_head); info.signal = ATH10K_DEFAULT_NOISE_FLOOR; info.signal += rx->ppdu.combined_rssi; diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 9535eaa09f09..f1505a25d810 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -115,6 +115,7 @@ enum ath10k_mcast2ucast_mode { #define TARGET_10X_MAC_AGGR_DELIM 0 #define TARGET_10X_AST_SKID_LIMIT 16 #define TARGET_10X_NUM_PEERS (128 + (TARGET_10X_NUM_VDEVS)) +#define TARGET_10X_NUM_PEERS_MAX 128 #define TARGET_10X_NUM_OFFLOAD_PEERS 0 #define TARGET_10X_NUM_OFFLOAD_REORDER_BUFS 0 #define TARGET_10X_NUM_PEER_KEYS 2 diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index ce9ef3499ecb..7aa6c4d702d6 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -332,6 +332,9 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr) ath10k_warn("Failed to wait for created wmi peer: %i\n", ret); return ret; } + spin_lock_bh(&ar->data_lock); + ar->num_peers++; + spin_unlock_bh(&ar->data_lock); return 0; } @@ -377,6 +380,10 @@ static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr) if (ret) return ret; + spin_lock_bh(&ar->data_lock); + ar->num_peers--; + spin_unlock_bh(&ar->data_lock); + return 0; } @@ -396,6 +403,7 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id) list_del(&peer->list); kfree(peer); + ar->num_peers--; } spin_unlock_bh(&ar->data_lock); } @@ -411,6 +419,7 @@ static void ath10k_peer_cleanup_all(struct ath10k *ar) list_del(&peer->list); kfree(peer); } + ar->num_peers = 0; spin_unlock_bh(&ar->data_lock); } @@ -2205,7 +2214,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); enum wmi_sta_powersave_param param; int ret = 0; - u32 value; + u32 value, param_id; int bit; u32 vdev_param; @@ -2297,6 +2306,13 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, ath10k_warn("Failed to create peer for AP: %d\n", ret); goto err_vdev_delete; } + + param_id = ar->wmi.pdev_param->sta_kickout_th; + + /* Disable STA KICKOUT functionality in FW */ + ret = ath10k_wmi_pdev_set_param(ar, param_id, 0); + if (ret) + ath10k_warn("Failed to disable STA KICKOUT\n"); } if (arvif->vdev_type == WMI_VDEV_TYPE_STA) { @@ -2842,6 +2858,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, { struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + int max_num_peers; int ret = 0; mutex_lock(&ar->conf_mutex); @@ -2852,9 +2869,21 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, /* * New station addition. */ + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) + max_num_peers = TARGET_10X_NUM_PEERS_MAX - 1; + else + max_num_peers = TARGET_NUM_PEERS; + + if (ar->num_peers >= max_num_peers) { + ath10k_warn("Number of peers exceeded: peers number %d (max peers %d)\n", + ar->num_peers, max_num_peers); + ret = -ENOBUFS; + goto exit; + } + ath10k_dbg(ATH10K_DBG_MAC, - "mac vdev %d peer create %pM (new sta)\n", - arvif->vdev_id, sta->addr); + "mac vdev %d peer create %pM (new sta) num_peers %d\n", + arvif->vdev_id, sta->addr, ar->num_peers); ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr); if (ret) @@ -2904,7 +2933,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, ath10k_warn("Failed to disassociate station: %pM\n", sta->addr); } - +exit: mutex_unlock(&ar->conf_mutex); return ret; } @@ -3310,6 +3339,307 @@ exit: return ret; } +/* Helper table for legacy fixed_rate/bitrate_mask */ +static const u8 cck_ofdm_rate[] = { + /* CCK */ + 3, /* 1Mbps */ + 2, /* 2Mbps */ + 1, /* 5.5Mbps */ + 0, /* 11Mbps */ + /* OFDM */ + 3, /* 6Mbps */ + 7, /* 9Mbps */ + 2, /* 12Mbps */ + 6, /* 18Mbps */ + 1, /* 24Mbps */ + 5, /* 36Mbps */ + 0, /* 48Mbps */ + 4, /* 54Mbps */ +}; + +/* Check if only one bit set */ +static int ath10k_check_single_mask(u32 mask) +{ + int bit; + + bit = ffs(mask); + if (!bit) + return 0; + + mask &= ~BIT(bit - 1); + if (mask) + return 2; + + return 1; +} + +static bool +ath10k_default_bitrate_mask(struct ath10k *ar, + enum ieee80211_band band, + const struct cfg80211_bitrate_mask *mask) +{ + u32 legacy = 0x00ff; + u8 ht = 0xff, i; + u16 vht = 0x3ff; + + switch (band) { + case IEEE80211_BAND_2GHZ: + legacy = 0x00fff; + vht = 0; + break; + case IEEE80211_BAND_5GHZ: + break; + default: + return false; + } + + if (mask->control[band].legacy != legacy) + return false; + + for (i = 0; i < ar->num_rf_chains; i++) + if (mask->control[band].ht_mcs[i] != ht) + return false; + + for (i = 0; i < ar->num_rf_chains; i++) + if (mask->control[band].vht_mcs[i] != vht) + return false; + + return true; +} + +static bool +ath10k_bitrate_mask_nss(const struct cfg80211_bitrate_mask *mask, + enum ieee80211_band band, + u8 *fixed_nss) +{ + int ht_nss = 0, vht_nss = 0, i; + + /* check legacy */ + if (ath10k_check_single_mask(mask->control[band].legacy)) + return false; + + /* check HT */ + for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) { + if (mask->control[band].ht_mcs[i] == 0xff) + continue; + else if (mask->control[band].ht_mcs[i] == 0x00) + break; + else + return false; + } + + ht_nss = i; + + /* check VHT */ + for (i = 0; i < NL80211_VHT_NSS_MAX; i++) { + if (mask->control[band].vht_mcs[i] == 0x03ff) + continue; + else if (mask->control[band].vht_mcs[i] == 0x0000) + break; + else + return false; + } + + vht_nss = i; + + if (ht_nss > 0 && vht_nss > 0) + return false; + + if (ht_nss) + *fixed_nss = ht_nss; + else if (vht_nss) + *fixed_nss = vht_nss; + else + return false; + + return true; +} + +static bool +ath10k_bitrate_mask_correct(const struct cfg80211_bitrate_mask *mask, + enum ieee80211_band band, + enum wmi_rate_preamble *preamble) +{ + int legacy = 0, ht = 0, vht = 0, i; + + *preamble = WMI_RATE_PREAMBLE_OFDM; + + /* check legacy */ + legacy = ath10k_check_single_mask(mask->control[band].legacy); + if (legacy > 1) + return false; + + /* check HT */ + for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) + ht += ath10k_check_single_mask(mask->control[band].ht_mcs[i]); + if (ht > 1) + return false; + + /* check VHT */ + for (i = 0; i < NL80211_VHT_NSS_MAX; i++) + vht += ath10k_check_single_mask(mask->control[band].vht_mcs[i]); + if (vht > 1) + return false; + + /* Currently we support only one fixed_rate */ + if ((legacy + ht + vht) != 1) + return false; + + if (ht) + *preamble = WMI_RATE_PREAMBLE_HT; + else if (vht) + *preamble = WMI_RATE_PREAMBLE_VHT; + + return true; +} + +static bool +ath10k_bitrate_mask_rate(const struct cfg80211_bitrate_mask *mask, + enum ieee80211_band band, + u8 *fixed_rate, + u8 *fixed_nss) +{ + u8 rate = 0, pream = 0, nss = 0, i; + enum wmi_rate_preamble preamble; + + /* Check if single rate correct */ + if (!ath10k_bitrate_mask_correct(mask, band, &preamble)) + return false; + + pream = preamble; + + switch (preamble) { + case WMI_RATE_PREAMBLE_CCK: + case WMI_RATE_PREAMBLE_OFDM: + i = ffs(mask->control[band].legacy) - 1; + + if (band == IEEE80211_BAND_2GHZ && i < 4) + pream = WMI_RATE_PREAMBLE_CCK; + + if (band == IEEE80211_BAND_5GHZ) + i += 4; + + if (i >= ARRAY_SIZE(cck_ofdm_rate)) + return false; + + rate = cck_ofdm_rate[i]; + break; + case WMI_RATE_PREAMBLE_HT: + for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) + if (mask->control[band].ht_mcs[i]) + break; + + if (i == IEEE80211_HT_MCS_MASK_LEN) + return false; + + rate = ffs(mask->control[band].ht_mcs[i]) - 1; + nss = i; + break; + case WMI_RATE_PREAMBLE_VHT: + for (i = 0; i < NL80211_VHT_NSS_MAX; i++) + if (mask->control[band].vht_mcs[i]) + break; + + if (i == NL80211_VHT_NSS_MAX) + return false; + + rate = ffs(mask->control[band].vht_mcs[i]) - 1; + nss = i; + break; + } + + *fixed_nss = nss + 1; + nss <<= 4; + pream <<= 6; + + ath10k_dbg(ATH10K_DBG_MAC, "mac fixed rate pream 0x%02x nss 0x%02x rate 0x%02x\n", + pream, nss, rate); + + *fixed_rate = pream | nss | rate; + + return true; +} + +static bool ath10k_get_fixed_rate_nss(const struct cfg80211_bitrate_mask *mask, + enum ieee80211_band band, + u8 *fixed_rate, + u8 *fixed_nss) +{ + /* First check full NSS mask, if we can simply limit NSS */ + if (ath10k_bitrate_mask_nss(mask, band, fixed_nss)) + return true; + + /* Next Check single rate is set */ + return ath10k_bitrate_mask_rate(mask, band, fixed_rate, fixed_nss); +} + +static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif, + u8 fixed_rate, + u8 fixed_nss) +{ + struct ath10k *ar = arvif->ar; + u32 vdev_param; + int ret = 0; + + mutex_lock(&ar->conf_mutex); + + if (arvif->fixed_rate == fixed_rate && + arvif->fixed_nss == fixed_nss) + goto exit; + + if (fixed_rate == WMI_FIXED_RATE_NONE) + ath10k_dbg(ATH10K_DBG_MAC, "mac disable fixed bitrate mask\n"); + + vdev_param = ar->wmi.vdev_param->fixed_rate; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + vdev_param, fixed_rate); + if (ret) { + ath10k_warn("Could not set fixed_rate param 0x%02x: %d\n", + fixed_rate, ret); + ret = -EINVAL; + goto exit; + } + + arvif->fixed_rate = fixed_rate; + + vdev_param = ar->wmi.vdev_param->nss; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + vdev_param, fixed_nss); + + if (ret) { + ath10k_warn("Could not set fixed_nss param %d: %d\n", + fixed_nss, ret); + ret = -EINVAL; + goto exit; + } + + arvif->fixed_nss = fixed_nss; + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static int ath10k_set_bitrate_mask(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const struct cfg80211_bitrate_mask *mask) +{ + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct ath10k *ar = arvif->ar; + enum ieee80211_band band = ar->hw->conf.chandef.chan->band; + u8 fixed_rate = WMI_FIXED_RATE_NONE; + u8 fixed_nss = ar->num_rf_chains; + + if (!ath10k_default_bitrate_mask(ar, band, mask)) { + if (!ath10k_get_fixed_rate_nss(mask, band, + &fixed_rate, + &fixed_nss)) + return -EINVAL; + } + + return ath10k_set_fixed_rate_param(arvif, fixed_rate, fixed_nss); +} + static const struct ieee80211_ops ath10k_ops = { .tx = ath10k_tx, .start = ath10k_start, @@ -3332,6 +3662,7 @@ static const struct ieee80211_ops ath10k_ops = { .tx_last_beacon = ath10k_tx_last_beacon, .restart_complete = ath10k_restart_complete, .get_survey = ath10k_get_survey, + .set_bitrate_mask = ath10k_set_bitrate_mask, #ifdef CONFIG_PM .suspend = ath10k_suspend, .resume = ath10k_resume, @@ -3464,14 +3795,12 @@ static const struct ieee80211_iface_limit ath10k_if_limits[] = { }, }; -#ifdef CONFIG_ATH10K_DFS_CERTIFIED -static const struct ieee80211_iface_limit ath10k_if_dfs_limits[] = { +static const struct ieee80211_iface_limit ath10k_10x_if_limits[] = { { .max = 8, .types = BIT(NL80211_IFTYPE_AP) }, }; -#endif static const struct ieee80211_iface_combination ath10k_if_comb[] = { { @@ -3481,19 +3810,22 @@ static const struct ieee80211_iface_combination ath10k_if_comb[] = { .num_different_channels = 1, .beacon_int_infra_match = true, }, -#ifdef CONFIG_ATH10K_DFS_CERTIFIED +}; + +static const struct ieee80211_iface_combination ath10k_10x_if_comb[] = { { - .limits = ath10k_if_dfs_limits, - .n_limits = ARRAY_SIZE(ath10k_if_dfs_limits), + .limits = ath10k_10x_if_limits, + .n_limits = ARRAY_SIZE(ath10k_10x_if_limits), .max_interfaces = 8, .num_different_channels = 1, .beacon_int_infra_match = true, +#ifdef CONFIG_ATH10K_DFS_CERTIFIED .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | BIT(NL80211_CHAN_WIDTH_20) | BIT(NL80211_CHAN_WIDTH_40) | BIT(NL80211_CHAN_WIDTH_80), - } #endif + }, }; static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar) @@ -3672,9 +4004,12 @@ int ath10k_mac_register(struct ath10k *ar) ar->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO); + BIT(NL80211_IFTYPE_AP); + + if (!test_bit(ATH10K_FW_FEATURE_NO_P2P, ar->fw_features)) + ar->hw->wiphy->interface_modes |= + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO); ar->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | @@ -3717,8 +4052,15 @@ int ath10k_mac_register(struct ath10k *ar) */ ar->hw->queues = 4; - ar->hw->wiphy->iface_combinations = ath10k_if_comb; - ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ath10k_if_comb); + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { + ar->hw->wiphy->iface_combinations = ath10k_10x_if_comb; + ar->hw->wiphy->n_iface_combinations = + ARRAY_SIZE(ath10k_10x_if_comb); + } else { + ar->hw->wiphy->iface_combinations = ath10k_if_comb; + ar->hw->wiphy->n_iface_combinations = + ARRAY_SIZE(ath10k_if_comb); + } ar->hw->netdev_features = NETIF_F_HW_CSUM; diff --git a/drivers/net/wireless/ath/ath10k/trace.h b/drivers/net/wireless/ath/ath10k/trace.h index 90817ddc92ba..4eb2ecbc06ef 100644 --- a/drivers/net/wireless/ath/ath10k/trace.h +++ b/drivers/net/wireless/ath/ath10k/trace.h @@ -182,6 +182,27 @@ TRACE_EVENT(ath10k_htt_stats, ) ); +TRACE_EVENT(ath10k_wmi_dbglog, + TP_PROTO(void *buf, size_t buf_len), + + TP_ARGS(buf, buf_len), + + TP_STRUCT__entry( + __field(size_t, buf_len) + __dynamic_array(u8, buf, buf_len) + ), + + TP_fast_assign( + __entry->buf_len = buf_len; + memcpy(__get_dynamic_array(buf), buf, buf_len); + ), + + TP_printk( + "len %zu", + __entry->buf_len + ) +); + #endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/ /* we don't want to use include/trace/events */ diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index 22829803f087..74f45fa6f428 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -231,7 +231,7 @@ void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info) ~IEEE80211_FCTL_PROTECTED); } - if (info->status == HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR) + if (info->mic_err) status->flag |= RX_FLAG_MMIC_ERROR; if (info->fcs_err) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 1260a8d15dc3..712a606a080a 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -16,6 +16,7 @@ */ #include <linux/skbuff.h> +#include <linux/ctype.h> #include "core.h" #include "htc.h" @@ -875,6 +876,7 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) struct wmi_mgmt_rx_event_v2 *ev_v2; struct wmi_mgmt_rx_hdr_v1 *ev_hdr; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_channel *ch; struct ieee80211_hdr *hdr; u32 rx_status; u32 channel; @@ -927,7 +929,25 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) if (rx_status & WMI_RX_STATUS_ERR_MIC) status->flag |= RX_FLAG_MMIC_ERROR; - status->band = phy_mode_to_band(phy_mode); + /* HW can Rx CCK rates on 5GHz. In that case phy_mode is set to + * MODE_11B. This means phy_mode is not a reliable source for the band + * of mgmt rx. */ + + ch = ar->scan_channel; + if (!ch) + ch = ar->rx_channel; + + if (ch) { + status->band = ch->band; + + if (phy_mode == MODE_11B && + status->band == IEEE80211_BAND_5GHZ) + ath10k_dbg(ATH10K_DBG_MGMT, "wmi mgmt rx 11b (CCK) on 5GHz\n"); + } else { + ath10k_warn("using (unreliable) phy_mode to extract band for mgmt rx\n"); + status->band = phy_mode_to_band(phy_mode); + } + status->freq = ieee80211_channel_to_frequency(channel, status->band); status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR; status->rate_idx = get_rate_idx(rate, status->band); @@ -937,7 +957,11 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) hdr = (struct ieee80211_hdr *)skb->data; fc = le16_to_cpu(hdr->frame_control); - if (fc & IEEE80211_FCTL_PROTECTED) { + /* FW delivers WEP Shared Auth frame with Protected Bit set and + * encrypted payload. However in case of PMF it delivers decrypted + * frames with Protected Bit set. */ + if (ieee80211_has_protected(hdr->frame_control) && + !ieee80211_is_auth(hdr->frame_control)) { status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; hdr->frame_control = __cpu_to_le16(fc & @@ -1047,9 +1071,14 @@ static void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb) ath10k_dbg(ATH10K_DBG_WMI, "WMI_ECHO_EVENTID\n"); } -static void ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb) +static int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_DEBUG_MESG_EVENTID\n"); + ath10k_dbg(ATH10K_DBG_WMI, "wmi event debug mesg len %d\n", + skb->len); + + trace_ath10k_wmi_dbglog(skb->data, skb->len); + + return 0; } static void ath10k_wmi_event_update_stats(struct ath10k *ar, @@ -1653,9 +1682,37 @@ static void ath10k_wmi_event_profile_match(struct ath10k *ar, } static void ath10k_wmi_event_debug_print(struct ath10k *ar, - struct sk_buff *skb) + struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_DEBUG_PRINT_EVENTID\n"); + char buf[101], c; + int i; + + for (i = 0; i < sizeof(buf) - 1; i++) { + if (i >= skb->len) + break; + + c = skb->data[i]; + + if (c == '\0') + break; + + if (isascii(c) && isprint(c)) + buf[i] = c; + else + buf[i] = '.'; + } + + if (i == sizeof(buf) - 1) + ath10k_warn("wmi debug print truncated: %d\n", skb->len); + + /* for some reason the debug prints end with \n, remove that */ + if (skb->data[i - 1] == '\n') + i--; + + /* the last byte is always reserved for the null character */ + buf[i] = '\0'; + + ath10k_dbg(ATH10K_DBG_WMI, "wmi event debug print '%s'\n", buf); } static void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb) @@ -3445,3 +3502,40 @@ int ath10k_wmi_force_fw_hang(struct ath10k *ar, type, delay_ms); return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->force_fw_hang_cmdid); } + +int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable) +{ + struct wmi_dbglog_cfg_cmd *cmd; + struct sk_buff *skb; + u32 cfg; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_dbglog_cfg_cmd *)skb->data; + + if (module_enable) { + cfg = SM(ATH10K_DBGLOG_LEVEL_VERBOSE, + ATH10K_DBGLOG_CFG_LOG_LVL); + } else { + /* set back defaults, all modules with WARN level */ + cfg = SM(ATH10K_DBGLOG_LEVEL_WARN, + ATH10K_DBGLOG_CFG_LOG_LVL); + module_enable = ~0; + } + + cmd->module_enable = __cpu_to_le32(module_enable); + cmd->module_valid = __cpu_to_le32(~0); + cmd->config_enable = __cpu_to_le32(cfg); + cmd->config_valid = __cpu_to_le32(ATH10K_DBGLOG_CFG_LOG_LVL_MASK); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi dbglog cfg modules %08x %08x config %08x %08x\n", + __le32_to_cpu(cmd->module_enable), + __le32_to_cpu(cmd->module_valid), + __le32_to_cpu(cmd->config_enable), + __le32_to_cpu(cmd->config_valid)); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->dbglog_cfg_cmdid); +} diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 0087d699b85b..4b5e7d3d32b6 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -3003,6 +3003,18 @@ struct wmi_vdev_install_key_arg { const void *key_data; }; +/* + * vdev fixed rate format: + * - preamble - b7:b6 - see WMI_RATE_PREMABLE_ + * - nss - b5:b4 - ss number (0 mean 1ss) + * - rate_mcs - b3:b0 - as below + * CCK: 0 - 11Mbps, 1 - 5,5Mbps, 2 - 2Mbps, 3 - 1Mbps, + * 4 - 11Mbps (s), 5 - 5,5Mbps (s), 6 - 2Mbps (s) + * OFDM: 0 - 48Mbps, 1 - 24Mbps, 2 - 12Mbps, 3 - 6Mbps, + * 4 - 54Mbps, 5 - 36Mbps, 6 - 18Mbps, 7 - 9Mbps + * HT/VHT: MCS index + */ + /* Preamble types to be used with VDEV fixed rate configuration */ enum wmi_rate_preamble { WMI_RATE_PREAMBLE_OFDM, @@ -4090,6 +4102,54 @@ struct wmi_force_fw_hang_cmd { __le32 delay_ms; } __packed; +enum ath10k_dbglog_level { + ATH10K_DBGLOG_LEVEL_VERBOSE = 0, + ATH10K_DBGLOG_LEVEL_INFO = 1, + ATH10K_DBGLOG_LEVEL_WARN = 2, + ATH10K_DBGLOG_LEVEL_ERR = 3, +}; + +/* VAP ids to enable dbglog */ +#define ATH10K_DBGLOG_CFG_VAP_LOG_LSB 0 +#define ATH10K_DBGLOG_CFG_VAP_LOG_MASK 0x0000ffff + +/* to enable dbglog in the firmware */ +#define ATH10K_DBGLOG_CFG_REPORTING_ENABLE_LSB 16 +#define ATH10K_DBGLOG_CFG_REPORTING_ENABLE_MASK 0x00010000 + +/* timestamp resolution */ +#define ATH10K_DBGLOG_CFG_RESOLUTION_LSB 17 +#define ATH10K_DBGLOG_CFG_RESOLUTION_MASK 0x000E0000 + +/* number of queued messages before sending them to the host */ +#define ATH10K_DBGLOG_CFG_REPORT_SIZE_LSB 20 +#define ATH10K_DBGLOG_CFG_REPORT_SIZE_MASK 0x0ff00000 + +/* + * Log levels to enable. This defines the minimum level to enable, this is + * not a bitmask. See enum ath10k_dbglog_level for the values. + */ +#define ATH10K_DBGLOG_CFG_LOG_LVL_LSB 28 +#define ATH10K_DBGLOG_CFG_LOG_LVL_MASK 0x70000000 + +/* + * Note: this is a cleaned up version of a struct firmware uses. For + * example, config_valid was hidden inside an array. + */ +struct wmi_dbglog_cfg_cmd { + /* bitmask to hold mod id config*/ + __le32 module_enable; + + /* see ATH10K_DBGLOG_CFG_ */ + __le32 config_enable; + + /* mask of module id bits to be changed */ + __le32 module_valid; + + /* mask of config bits to be changed, see ATH10K_DBGLOG_CFG_ */ + __le32 config_valid; +} __packed; + #define ATH10K_RTS_MAX 2347 #define ATH10K_FRAGMT_THRESHOLD_MIN 540 #define ATH10K_FRAGMT_THRESHOLD_MAX 2346 @@ -4167,5 +4227,6 @@ int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id); int ath10k_wmi_force_fw_hang(struct ath10k *ar, enum wmi_force_fw_hang_type type, u32 delay_ms); int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb); +int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable); #endif /* _WMI_H_ */ |