diff options
Diffstat (limited to 'drivers/net/wireless/ath')
-rw-r--r-- | drivers/net/wireless/ath/ath.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/core.c | 36 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/core.h | 47 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/debug.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/htt_rx.c | 35 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/htt_tx.c | 4 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/hw.h | 6 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/mac.c | 634 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/pci.c | 147 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/txrx.c | 4 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/wmi.c | 118 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/wmi.h | 34 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/usb.c | 6 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/wmi.c | 4 | ||||
-rw-r--r-- | drivers/net/wireless/ath/regd.c | 10 |
15 files changed, 895 insertions, 194 deletions
diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index 6260b834a86..56d559939cf 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -63,7 +63,7 @@ enum ath_bus_type { }; struct reg_dmn_pair_mapping { - u16 regDmnEnum; + u16 reg_domain; u16 reg_5ghz_ctl; u16 reg_2ghz_ctl; }; diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 3b59af3bddf..ebc5fc2ede7 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -55,8 +55,7 @@ static void ath10k_send_suspend_complete(struct ath10k *ar) { ath10k_dbg(ATH10K_DBG_BOOT, "boot suspend complete\n"); - ar->is_target_paused = true; - wake_up(&ar->event_queue); + complete(&ar->target_suspend); } static int ath10k_init_connect_htc(struct ath10k *ar) @@ -470,8 +469,12 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) if (index == ie_len) break; - if (data[index] & (1 << bit)) + if (data[index] & (1 << bit)) { + ath10k_dbg(ATH10K_DBG_BOOT, + "Enabling feature bit: %i\n", + i); __set_bit(i, ar->fw_features); + } } ath10k_dbg_dump(ATH10K_DBG_BOOT, "features", "", @@ -699,6 +702,7 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, init_completion(&ar->scan.started); init_completion(&ar->scan.completed); init_completion(&ar->scan.on_channel); + init_completion(&ar->target_suspend); init_completion(&ar->install_key_done); init_completion(&ar->vdev_setup_done); @@ -722,8 +726,6 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, INIT_WORK(&ar->wmi_mgmt_tx_work, ath10k_mgmt_over_wmi_tx_work); skb_queue_head_init(&ar->wmi_mgmt_tx_queue); - init_waitqueue_head(&ar->event_queue); - INIT_WORK(&ar->restart_work, ath10k_core_restart); return ar; @@ -856,10 +858,34 @@ err: } EXPORT_SYMBOL(ath10k_core_start); +int ath10k_wait_for_suspend(struct ath10k *ar, u32 suspend_opt) +{ + int ret; + + reinit_completion(&ar->target_suspend); + + ret = ath10k_wmi_pdev_suspend_target(ar, suspend_opt); + if (ret) { + ath10k_warn("could not suspend target (%d)\n", ret); + return ret; + } + + ret = wait_for_completion_timeout(&ar->target_suspend, 1 * HZ); + + if (ret == 0) { + ath10k_warn("suspend timed out - target pause event never came\n"); + return -ETIMEDOUT; + } + + return 0; +} + void ath10k_core_stop(struct ath10k *ar) { lockdep_assert_held(&ar->conf_mutex); + /* try to suspend target */ + ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND_AND_DISABLE_INTR); ath10k_debug_stop(ar); ath10k_htc_stop(&ar->htc); ath10k_htt_detach(&ar->htt); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index ade1781c718..1fc26fe057e 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -46,6 +46,18 @@ #define ATH10K_MAX_NUM_MGMT_PENDING 128 +/* number of failed packets */ +#define ATH10K_KICKOUT_THRESHOLD 50 + +/* + * Use insanely high numbers to make sure that the firmware implementation + * won't start, we have the same functionality already in hostapd. Unit + * is seconds. + */ +#define ATH10K_KEEPALIVE_MIN_IDLE 3747 +#define ATH10K_KEEPALIVE_MAX_IDLE 3895 +#define ATH10K_KEEPALIVE_MAX_UNRESPONSIVE 3900 + struct ath10k; struct ath10k_skb_cb { @@ -61,6 +73,11 @@ struct ath10k_skb_cb { u8 frag_len; u8 pad_len; } __packed htt; + + struct { + bool dtim_zero; + bool deliver_cab; + } bcn; } __packed; static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb) @@ -211,6 +228,18 @@ struct ath10k_peer { struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1]; }; +struct ath10k_sta { + struct ath10k_vif *arvif; + + /* the following are protected by ar->data_lock */ + u32 changed; /* IEEE80211_RC_* */ + u32 bw; + u32 nss; + u32 smps; + + struct work_struct update_wk; +}; + #define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5*HZ) struct ath10k_vif { @@ -222,10 +251,17 @@ struct ath10k_vif { u32 beacon_interval; u32 dtim_period; struct sk_buff *beacon; + /* protected by data_lock */ + bool beacon_sent; struct ath10k *ar; struct ieee80211_vif *vif; + bool is_started; + bool is_up; + u32 aid; + u8 bssid[ETH_ALEN]; + struct work_struct wep_key_work; struct ieee80211_key_conf *wep_keys[WMI_MAX_KEY_INDEX + 1]; u8 def_wep_key_idx; @@ -235,7 +271,6 @@ struct ath10k_vif { union { struct { - u8 bssid[ETH_ALEN]; u32 uapsd; } sta; struct { @@ -249,9 +284,6 @@ struct ath10k_vif { u32 noa_len; u8 *noa_data; } ap; - struct { - u8 bssid[ETH_ALEN]; - } ibss; } u; u8 fixed_rate; @@ -355,8 +387,7 @@ struct ath10k { const struct ath10k_hif_ops *ops; } hif; - wait_queue_head_t event_queue; - bool is_target_paused; + struct completion target_suspend; struct ath10k_bmi bmi; struct ath10k_wmi wmi; @@ -412,6 +443,9 @@ struct ath10k { /* valid during scan; needed for mgmt rx during scan */ struct ieee80211_channel *scan_channel; + /* current operating channel definition */ + struct cfg80211_chan_def chandef; + int free_vdev_map; int monitor_vdev_id; bool monitor_enabled; @@ -470,6 +504,7 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, void ath10k_core_destroy(struct ath10k *ar); int ath10k_core_start(struct ath10k *ar); +int ath10k_wait_for_suspend(struct ath10k *ar, u32 suspend_opt); void ath10k_core_stop(struct ath10k *ar); int ath10k_core_register(struct ath10k *ar, u32 chip_id); void ath10k_core_unregister(struct ath10k *ar); diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h index 1773c36c71a..a5824990bd2 100644 --- a/drivers/net/wireless/ath/ath10k/debug.h +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -92,7 +92,7 @@ static inline void ath10k_debug_read_target_stats(struct ath10k *ar, #ifdef CONFIG_ATH10K_DEBUG __printf(2, 3) void ath10k_dbg(enum ath10k_debug_mask mask, - const char *fmt, ...); + const char *fmt, ...); void ath10k_dbg_dump(enum ath10k_debug_mask mask, const char *msg, const char *prefix, const void *buf, size_t len); diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index fe8bd1b59f0..4767c24bf81 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -324,7 +324,7 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, msdu->len + skb_tailroom(msdu), DMA_FROM_DEVICE); - ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx: ", + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx pop: ", msdu->data, msdu->len + skb_tailroom(msdu)); rx_desc = (struct htt_rx_desc *)msdu->data; @@ -417,8 +417,8 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, next->len + skb_tailroom(next), DMA_FROM_DEVICE); - ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx: ", - next->data, + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, + "htt rx chained: ", next->data, next->len + skb_tailroom(next)); skb_trim(next, 0); @@ -430,12 +430,6 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, msdu_chaining = 1; } - if (msdu_len > 0) { - /* This may suggest FW bug? */ - ath10k_warn("htt rx msdu len not consumed (%d)\n", - msdu_len); - } - last_msdu = __le32_to_cpu(rx_desc->msdu_end.info0) & RX_MSDU_END_INFO0_LAST_MSDU; @@ -751,7 +745,7 @@ static void ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info) /* This shouldn't happen. If it does than it may be a FW bug. */ if (skb->next) { - ath10k_warn("received chained non A-MSDU frame\n"); + ath10k_warn("htt rx received chained non A-MSDU frame\n"); ath10k_htt_rx_free_msdu_chain(skb->next); skb->next = NULL; } @@ -937,6 +931,8 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, } if (ath10k_htt_rx_has_decrypt_err(msdu_head)) { + ath10k_dbg(ATH10K_DBG_HTT, + "htt rx dropping due to decrypt-err\n"); ath10k_htt_rx_free_msdu_chain(msdu_head); continue; } @@ -945,12 +941,14 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, /* Skip mgmt frames while we handle this in WMI */ if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL) { + ath10k_dbg(ATH10K_DBG_HTT, "htt rx mgmt ctrl\n"); ath10k_htt_rx_free_msdu_chain(msdu_head); continue; } if (status != HTT_RX_IND_MPDU_STATUS_OK && status != HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR && + status != HTT_RX_IND_MPDU_STATUS_ERR_INV_PEER && !htt->ar->monitor_enabled) { ath10k_dbg(ATH10K_DBG_HTT, "htt rx ignoring frame w/ status %d\n", @@ -960,6 +958,8 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, } if (test_bit(ATH10K_CAC_RUNNING, &htt->ar->dev_flags)) { + ath10k_dbg(ATH10K_DBG_HTT, + "htt rx CAC running\n"); ath10k_htt_rx_free_msdu_chain(msdu_head); continue; } @@ -967,7 +967,7 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, /* FIXME: we do not support chaining yet. * this needs investigation */ if (msdu_chaining) { - ath10k_warn("msdu_chaining is true\n"); + ath10k_warn("htt rx msdu_chaining is true\n"); ath10k_htt_rx_free_msdu_chain(msdu_head); continue; } @@ -975,6 +975,15 @@ 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); + + if (info.fcs_err) + ath10k_dbg(ATH10K_DBG_HTT, + "htt rx has FCS err\n"); + + if (info.mic_err) + ath10k_dbg(ATH10K_DBG_HTT, + "htt rx has MIC err\n"); + info.signal = ATH10K_DEFAULT_NOISE_FLOOR; info.signal += rx->ppdu.combined_rssi; @@ -1095,7 +1104,7 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, skb_trim(info.skb, info.skb->len - trim); - ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt frag mpdu: ", + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx frag mpdu: ", info.skb->data, info.skb->len); ath10k_process_rx(htt->ar, &info); @@ -1116,7 +1125,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) if (!IS_ALIGNED((unsigned long)skb->data, 4)) ath10k_warn("unaligned htt message, expect trouble\n"); - ath10k_dbg(ATH10K_DBG_HTT, "HTT RX, msg_type: 0x%0X\n", + ath10k_dbg(ATH10K_DBG_HTT, "htt rx, msg_type: 0x%0X\n", resp->hdr.msg_type); switch (resp->hdr.msg_type) { case HTT_T2H_MSG_TYPE_VERSION_CONF: { diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index f1d36d2d272..acaa046dc93 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -460,9 +460,9 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) DMA_TO_DEVICE); } - ath10k_dbg(ATH10K_DBG_HTT, "msdu 0x%llx\n", + ath10k_dbg(ATH10K_DBG_HTT, "tx-msdu 0x%llx\n", (unsigned long long) ATH10K_SKB_CB(msdu)->paddr); - ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "msdu: ", + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "tx-msdu: ", msdu->data, msdu->len); skb_put(txdesc, desc_len); diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index f1505a25d81..35fc44e281f 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -205,8 +205,11 @@ enum ath10k_mcast2ucast_mode { #define WLAN_ANALOG_INTF_PCIE_BASE_ADDRESS 0x0006c000 #define PCIE_LOCAL_BASE_ADDRESS 0x00080000 +#define SOC_RESET_CONTROL_ADDRESS 0x00000000 #define SOC_RESET_CONTROL_OFFSET 0x00000000 #define SOC_RESET_CONTROL_SI0_RST_MASK 0x00000001 +#define SOC_RESET_CONTROL_CE_RST_MASK 0x00040000 +#define SOC_RESET_CONTROL_CPU_WARM_RST_MASK 0x00000040 #define SOC_CPU_CLOCK_OFFSET 0x00000020 #define SOC_CPU_CLOCK_STANDARD_LSB 0 #define SOC_CPU_CLOCK_STANDARD_MASK 0x00000003 @@ -216,6 +219,8 @@ enum ath10k_mcast2ucast_mode { #define SOC_LPO_CAL_OFFSET 0x000000e0 #define SOC_LPO_CAL_ENABLE_LSB 20 #define SOC_LPO_CAL_ENABLE_MASK 0x00100000 +#define SOC_LF_TIMER_CONTROL0_ADDRESS 0x00000050 +#define SOC_LF_TIMER_CONTROL0_ENABLE_MASK 0x00000004 #define SOC_CHIP_ID_ADDRESS 0x000000ec #define SOC_CHIP_ID_REV_LSB 8 @@ -273,6 +278,7 @@ enum ath10k_mcast2ucast_mode { #define PCIE_INTR_CAUSE_ADDRESS 0x000c #define PCIE_INTR_CLR_ADDRESS 0x0014 #define SCRATCH_3_ADDRESS 0x0030 +#define CPU_INTR_ADDRESS 0x0010 /* Firmware indications to the Host via SCRATCH_3 register. */ #define FW_INDICATOR_ADDRESS (SOC_CORE_BASE_ADDRESS + SCRATCH_3_ADDRESS) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 776e364eadc..e17f5d732b5 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -339,6 +339,50 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr) return 0; } +static int ath10k_mac_set_kickout(struct ath10k_vif *arvif) +{ + struct ath10k *ar = arvif->ar; + u32 param; + int ret; + + param = ar->wmi.pdev_param->sta_kickout_th; + ret = ath10k_wmi_pdev_set_param(ar, param, + ATH10K_KICKOUT_THRESHOLD); + if (ret) { + ath10k_warn("Failed to set kickout threshold: %d\n", ret); + return ret; + } + + param = ar->wmi.vdev_param->ap_keepalive_min_idle_inactive_time_secs; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param, + ATH10K_KEEPALIVE_MIN_IDLE); + if (ret) { + ath10k_warn("Failed to set keepalive minimum idle time : %d\n", + ret); + return ret; + } + + param = ar->wmi.vdev_param->ap_keepalive_max_idle_inactive_time_secs; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param, + ATH10K_KEEPALIVE_MAX_IDLE); + if (ret) { + ath10k_warn("Failed to set keepalive maximum idle time: %d\n", + ret); + return ret; + } + + param = ar->wmi.vdev_param->ap_keepalive_max_unresponsive_time_secs; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param, + ATH10K_KEEPALIVE_MAX_UNRESPONSIVE); + if (ret) { + ath10k_warn("Failed to set keepalive maximum unresponsive time: %d\n", + ret); + return ret; + } + + return 0; +} + static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value) { struct ath10k *ar = arvif->ar; @@ -444,8 +488,7 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar) static int ath10k_vdev_start(struct ath10k_vif *arvif) { struct ath10k *ar = arvif->ar; - struct ieee80211_conf *conf = &ar->hw->conf; - struct ieee80211_channel *channel = conf->chandef.chan; + struct cfg80211_chan_def *chandef = &ar->chandef; struct wmi_vdev_start_request_arg arg = {}; int ret = 0; @@ -457,16 +500,14 @@ static int ath10k_vdev_start(struct ath10k_vif *arvif) arg.dtim_period = arvif->dtim_period; arg.bcn_intval = arvif->beacon_interval; - arg.channel.freq = channel->center_freq; - - arg.channel.band_center_freq1 = conf->chandef.center_freq1; - - arg.channel.mode = chan_to_phymode(&conf->chandef); + arg.channel.freq = chandef->chan->center_freq; + arg.channel.band_center_freq1 = chandef->center_freq1; + arg.channel.mode = chan_to_phymode(chandef); arg.channel.min_power = 0; - arg.channel.max_power = channel->max_power * 2; - arg.channel.max_reg_power = channel->max_reg_power * 2; - arg.channel.max_antenna_gain = channel->max_antenna_gain * 2; + arg.channel.max_power = chandef->chan->max_power * 2; + arg.channel.max_reg_power = chandef->chan->max_reg_power * 2; + arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain * 2; if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { arg.ssid = arvif->u.ap.ssid; @@ -475,7 +516,7 @@ static int ath10k_vdev_start(struct ath10k_vif *arvif) /* For now allow DFS for AP mode */ arg.channel.chan_radar = - !!(channel->flags & IEEE80211_CHAN_RADAR); + !!(chandef->chan->flags & IEEE80211_CHAN_RADAR); } else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) { arg.ssid = arvif->vif->bss_conf.ssid; arg.ssid_len = arvif->vif->bss_conf.ssid_len; @@ -527,7 +568,8 @@ static int ath10k_vdev_stop(struct ath10k_vif *arvif) static int ath10k_monitor_start(struct ath10k *ar, int vdev_id) { - struct ieee80211_channel *channel = ar->hw->conf.chandef.chan; + struct cfg80211_chan_def *chandef = &ar->chandef; + struct ieee80211_channel *channel = chandef->chan; struct wmi_vdev_start_request_arg arg = {}; int ret = 0; @@ -540,11 +582,11 @@ static int ath10k_monitor_start(struct ath10k *ar, int vdev_id) arg.vdev_id = vdev_id; arg.channel.freq = channel->center_freq; - arg.channel.band_center_freq1 = ar->hw->conf.chandef.center_freq1; + arg.channel.band_center_freq1 = chandef->center_freq1; /* TODO setup this dynamically, what in case we don't have any vifs? */ - arg.channel.mode = chan_to_phymode(&ar->hw->conf.chandef); + arg.channel.mode = chan_to_phymode(chandef); arg.channel.chan_radar = !!(channel->flags & IEEE80211_CHAN_RADAR); @@ -791,6 +833,20 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif, if (!info->enable_beacon) { ath10k_vdev_stop(arvif); + + arvif->is_started = false; + arvif->is_up = false; + + spin_lock_bh(&arvif->ar->data_lock); + if (arvif->beacon) { + ath10k_skb_unmap(arvif->ar->dev, arvif->beacon); + dev_kfree_skb_any(arvif->beacon); + + arvif->beacon = NULL; + arvif->beacon_sent = false; + } + spin_unlock_bh(&arvif->ar->data_lock); + return; } @@ -800,12 +856,21 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif, if (ret) return; - ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, 0, info->bssid); + arvif->aid = 0; + memcpy(arvif->bssid, info->bssid, ETH_ALEN); + + ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, + arvif->bssid); if (ret) { ath10k_warn("Failed to bring up VDEV: %d\n", arvif->vdev_id); + ath10k_vdev_stop(arvif); return; } + + arvif->is_started = true; + arvif->is_up = true; + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d up\n", arvif->vdev_id); } @@ -824,18 +889,18 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif, ath10k_warn("Failed to delete IBSS self peer:%pM for VDEV:%d ret:%d\n", self_peer, arvif->vdev_id, ret); - if (is_zero_ether_addr(arvif->u.ibss.bssid)) + if (is_zero_ether_addr(arvif->bssid)) return; ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, - arvif->u.ibss.bssid); + arvif->bssid); if (ret) { ath10k_warn("Failed to delete IBSS BSSID peer:%pM for VDEV:%d ret:%d\n", - arvif->u.ibss.bssid, arvif->vdev_id, ret); + arvif->bssid, arvif->vdev_id, ret); return; } - memset(arvif->u.ibss.bssid, 0, ETH_ALEN); + memset(arvif->bssid, 0, ETH_ALEN); return; } @@ -1017,7 +1082,6 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar, struct wmi_peer_assoc_complete_arg *arg) { const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - int smps; int i, n; lockdep_assert_held(&ar->conf_mutex); @@ -1063,17 +1127,6 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar, arg->peer_flags |= WMI_PEER_STBC; } - smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS; - smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT; - - if (smps == WLAN_HT_CAP_SM_PS_STATIC) { - arg->peer_flags |= WMI_PEER_SPATIAL_MUX; - arg->peer_flags |= WMI_PEER_STATIC_MIMOPS; - } else if (smps == WLAN_HT_CAP_SM_PS_DYNAMIC) { - arg->peer_flags |= WMI_PEER_SPATIAL_MUX; - arg->peer_flags |= WMI_PEER_DYN_MIMOPS; - } - if (ht_cap->mcs.rx_mask[1] && ht_cap->mcs.rx_mask[2]) arg->peer_rate_caps |= WMI_RC_TS_FLAG; else if (ht_cap->mcs.rx_mask[1]) @@ -1083,8 +1136,23 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar, if (ht_cap->mcs.rx_mask[i/8] & (1 << i%8)) arg->peer_ht_rates.rates[n++] = i; - arg->peer_ht_rates.num_rates = n; - arg->peer_num_spatial_streams = max((n+7) / 8, 1); + /* + * This is a workaround for HT-enabled STAs which break the spec + * and have no HT capabilities RX mask (no HT RX MCS map). + * + * As per spec, in section 20.3.5 Modulation and coding scheme (MCS), + * MCS 0 through 7 are mandatory in 20MHz with 800 ns GI at all STAs. + * + * Firmware asserts if such situation occurs. + */ + if (n == 0) { + arg->peer_ht_rates.num_rates = 8; + for (i = 0; i < arg->peer_ht_rates.num_rates; i++) + arg->peer_ht_rates.rates[i] = i; + } else { + arg->peer_ht_rates.num_rates = n; + arg->peer_num_spatial_streams = sta->rx_nss; + } ath10k_dbg(ATH10K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n", arg->addr, @@ -1092,27 +1160,20 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar, arg->peer_num_spatial_streams); } -static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar, - struct ath10k_vif *arvif, - struct ieee80211_sta *sta, - struct ieee80211_bss_conf *bss_conf, - struct wmi_peer_assoc_complete_arg *arg) +static int ath10k_peer_assoc_qos_ap(struct ath10k *ar, + struct ath10k_vif *arvif, + struct ieee80211_sta *sta) { u32 uapsd = 0; u32 max_sp = 0; + int ret = 0; lockdep_assert_held(&ar->conf_mutex); - if (sta->wme) - arg->peer_flags |= WMI_PEER_QOS; - if (sta->wme && sta->uapsd_queues) { ath10k_dbg(ATH10K_DBG_MAC, "mac uapsd_queues 0x%x max_sp %d\n", sta->uapsd_queues, sta->max_sp); - arg->peer_flags |= WMI_PEER_APSD; - arg->peer_rate_caps |= WMI_RC_UAPSD_FLAG; - if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) uapsd |= WMI_AP_PS_UAPSD_AC3_DELIVERY_EN | WMI_AP_PS_UAPSD_AC3_TRIGGER_EN; @@ -1130,35 +1191,40 @@ static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar, if (sta->max_sp < MAX_WMI_AP_PS_PEER_PARAM_MAX_SP) max_sp = sta->max_sp; - ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, - sta->addr, - WMI_AP_PS_PEER_PARAM_UAPSD, - uapsd); + ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, + sta->addr, + WMI_AP_PS_PEER_PARAM_UAPSD, + uapsd); + if (ret) { + ath10k_warn("failed to set ap ps peer param uapsd: %d\n", + ret); + return ret; + } - ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, - sta->addr, - WMI_AP_PS_PEER_PARAM_MAX_SP, - max_sp); + ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, + sta->addr, + WMI_AP_PS_PEER_PARAM_MAX_SP, + max_sp); + if (ret) { + ath10k_warn("failed to set ap ps peer param max sp: %d\n", + ret); + return ret; + } /* TODO setup this based on STA listen interval and beacon interval. Currently we don't know sta->listen_interval - mac80211 patch required. Currently use 10 seconds */ - ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, - sta->addr, - WMI_AP_PS_PEER_PARAM_AGEOUT_TIME, - 10); + ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, sta->addr, + WMI_AP_PS_PEER_PARAM_AGEOUT_TIME, 10); + if (ret) { + ath10k_warn("failed to set ap ps peer param ageout time: %d\n", + ret); + return ret; + } } -} -static void ath10k_peer_assoc_h_qos_sta(struct ath10k *ar, - struct ath10k_vif *arvif, - struct ieee80211_sta *sta, - struct ieee80211_bss_conf *bss_conf, - struct wmi_peer_assoc_complete_arg *arg) -{ - if (bss_conf->qos) - arg->peer_flags |= WMI_PEER_QOS; + return 0; } static void ath10k_peer_assoc_h_vht(struct ath10k *ar, @@ -1211,10 +1277,17 @@ static void ath10k_peer_assoc_h_qos(struct ath10k *ar, { switch (arvif->vdev_type) { case WMI_VDEV_TYPE_AP: - ath10k_peer_assoc_h_qos_ap(ar, arvif, sta, bss_conf, arg); + if (sta->wme) + arg->peer_flags |= WMI_PEER_QOS; + + if (sta->wme && sta->uapsd_queues) { + arg->peer_flags |= WMI_PEER_APSD; + arg->peer_rate_caps |= WMI_RC_UAPSD_FLAG; + } break; case WMI_VDEV_TYPE_STA: - ath10k_peer_assoc_h_qos_sta(ar, arvif, sta, bss_conf, arg); + if (bss_conf->qos) + arg->peer_flags |= WMI_PEER_QOS; break; default: break; @@ -1293,6 +1366,33 @@ static int ath10k_peer_assoc_prepare(struct ath10k *ar, return 0; } +static const u32 ath10k_smps_map[] = { + [WLAN_HT_CAP_SM_PS_STATIC] = WMI_PEER_SMPS_STATIC, + [WLAN_HT_CAP_SM_PS_DYNAMIC] = WMI_PEER_SMPS_DYNAMIC, + [WLAN_HT_CAP_SM_PS_INVALID] = WMI_PEER_SMPS_PS_NONE, + [WLAN_HT_CAP_SM_PS_DISABLED] = WMI_PEER_SMPS_PS_NONE, +}; + +static int ath10k_setup_peer_smps(struct ath10k *ar, struct ath10k_vif *arvif, + const u8 *addr, + const struct ieee80211_sta_ht_cap *ht_cap) +{ + int smps; + + if (!ht_cap->ht_supported) + return 0; + + smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS; + smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT; + + if (smps >= ARRAY_SIZE(ath10k_smps_map)) + return -EINVAL; + + return ath10k_wmi_peer_set_param(ar, arvif->vdev_id, addr, + WMI_PEER_SMPS_STATE, + ath10k_smps_map[smps]); +} + /* can be called only in mac80211 callbacks due to `key_count` usage */ static void ath10k_bss_assoc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -1300,6 +1400,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw, { struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct ieee80211_sta_ht_cap ht_cap; struct wmi_peer_assoc_complete_arg peer_arg; struct ieee80211_sta *ap_sta; int ret; @@ -1316,6 +1417,10 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw, return; } + /* ap_sta must be accessed only within rcu section which must be left + * before calling ath10k_setup_peer_smps() which might sleep. */ + ht_cap = ap_sta->ht_cap; + ret = ath10k_peer_assoc_prepare(ar, arvif, ap_sta, bss_conf, &peer_arg); if (ret) { @@ -1334,15 +1439,27 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw, return; } + ret = ath10k_setup_peer_smps(ar, arvif, bss_conf->bssid, &ht_cap); + if (ret) { + ath10k_warn("failed to setup peer SMPS: %d\n", ret); + return; + } + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d up (associated) bssid %pM aid %d\n", arvif->vdev_id, bss_conf->bssid, bss_conf->aid); - ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, bss_conf->aid, - bss_conf->bssid); - if (ret) + arvif->aid = bss_conf->aid; + memcpy(arvif->bssid, bss_conf->bssid, ETH_ALEN); + + ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, arvif->aid, arvif->bssid); + if (ret) { ath10k_warn("VDEV: %d up failed: ret %d\n", arvif->vdev_id, ret); + return; + } + + arvif->is_up = true; } /* @@ -1382,6 +1499,9 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw, ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id); arvif->def_wep_key_idx = 0; + + arvif->is_started = false; + arvif->is_up = false; } static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif, @@ -1406,12 +1526,25 @@ static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif, return ret; } + ret = ath10k_setup_peer_smps(ar, arvif, sta->addr, &sta->ht_cap); + if (ret) { + ath10k_warn("failed to setup peer SMPS: %d\n", ret); + return ret; + } + ret = ath10k_install_peer_wep_keys(arvif, sta->addr); if (ret) { ath10k_warn("could not install peer wep keys (%d)\n", ret); return ret; } + ret = ath10k_peer_assoc_qos_ap(ar, arvif, sta); + if (ret) { + ath10k_warn("could not set qos params for STA %pM, %d\n", + sta->addr, ret); + return ret; + } + return ret; } @@ -1547,9 +1680,9 @@ static void ath10k_regd_update(struct ath10k *ar) /* Target allows setting up per-band regdomain but ath_common provides * a combined one only */ ret = ath10k_wmi_pdev_set_regdomain(ar, - regpair->regDmnEnum, - regpair->regDmnEnum, /* 2ghz */ - regpair->regDmnEnum, /* 5ghz */ + regpair->reg_domain, + regpair->reg_domain, /* 2ghz */ + regpair->reg_domain, /* 5ghz */ regpair->reg_2ghz_ctl, regpair->reg_5ghz_ctl); if (ret) @@ -2100,11 +2233,29 @@ static int ath10k_start(struct ieee80211_hw *hw) ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n", ret); + /* + * By default FW set ARP frames ac to voice (6). In that case ARP + * exchange is not working properly for UAPSD enabled AP. ARP requests + * which arrives with access category 0 are processed by network stack + * and send back with access category 0, but FW changes access category + * to 6. Set ARP frames access category to best effort (0) solves + * this problem. + */ + + ret = ath10k_wmi_pdev_set_param(ar, + ar->wmi.pdev_param->arp_ac_override, 0); + if (ret) { + ath10k_warn("could not set arp ac override parameter: %d\n", + ret); + goto exit; + } + ath10k_regd_update(ar); + ret = 0; exit: mutex_unlock(&ar->conf_mutex); - return 0; + return ret; } static void ath10k_stop(struct ieee80211_hw *hw) @@ -2145,6 +2296,98 @@ static int ath10k_config_ps(struct ath10k *ar) return ret; } +static const char *chandef_get_width(enum nl80211_chan_width width) +{ + switch (width) { + case NL80211_CHAN_WIDTH_20_NOHT: + return "20 (noht)"; + case NL80211_CHAN_WIDTH_20: + return "20"; + case NL80211_CHAN_WIDTH_40: + return "40"; + case NL80211_CHAN_WIDTH_80: + return "80"; + case NL80211_CHAN_WIDTH_80P80: + return "80+80"; + case NL80211_CHAN_WIDTH_160: + return "160"; + case NL80211_CHAN_WIDTH_5: + return "5"; + case NL80211_CHAN_WIDTH_10: + return "10"; + } + return "?"; +} + +static void ath10k_config_chan(struct ath10k *ar) +{ + struct ath10k_vif *arvif; + bool monitor_was_enabled; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ath10k_dbg(ATH10K_DBG_MAC, + "mac config channel to %dMHz (cf1 %dMHz cf2 %dMHz width %s)\n", + ar->chandef.chan->center_freq, + ar->chandef.center_freq1, + ar->chandef.center_freq2, + chandef_get_width(ar->chandef.width)); + + /* First stop monitor interface. Some FW versions crash if there's a + * lone monitor interface. */ + monitor_was_enabled = ar->monitor_enabled; + + if (ar->monitor_enabled) + ath10k_monitor_stop(ar); + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (!arvif->is_started) + continue; + + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) + continue; + + ret = ath10k_vdev_stop(arvif); + if (ret) { + ath10k_warn("could not stop vdev %d (%d)\n", + arvif->vdev_id, ret); + continue; + } + } + + /* all vdevs are now stopped - now attempt to restart them */ + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (!arvif->is_started) + continue; + + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) + continue; + + ret = ath10k_vdev_start(arvif); + if (ret) { + ath10k_warn("could not start vdev %d (%d)\n", + arvif->vdev_id, ret); + continue; + } + + if (!arvif->is_up) + continue; + + ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, + arvif->bssid); + if (ret) { + ath10k_warn("could not bring vdev up %d (%d)\n", + arvif->vdev_id, ret); + continue; + } + } + + if (monitor_was_enabled) + ath10k_monitor_start(ar, ar->monitor_vdev_id); +} + static int ath10k_config(struct ieee80211_hw *hw, u32 changed) { struct ath10k *ar = hw->priv; @@ -2165,6 +2408,11 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed) spin_unlock_bh(&ar->data_lock); ath10k_config_radar_detection(ar); + + if (!cfg80211_chandef_identical(&ar->chandef, &conf->chandef)) { + ar->chandef = conf->chandef; + ath10k_config_chan(ar); + } } if (changed & IEEE80211_CONF_CHANGE_POWER) { @@ -2214,7 +2462,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, param_id; + u32 value; int bit; u32 vdev_param; @@ -2307,12 +2555,12 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, 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"); + ret = ath10k_mac_set_kickout(arvif); + if (ret) { + ath10k_warn("Failed to set kickout parameters: %d\n", + ret); + goto err_peer_delete; + } } if (arvif->vdev_type == WMI_VDEV_TYPE_STA) { @@ -2559,15 +2807,20 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, * this is never erased as we it for crypto key * clearing; this is FW requirement */ - memcpy(arvif->u.sta.bssid, info->bssid, - ETH_ALEN); + memcpy(arvif->bssid, info->bssid, ETH_ALEN); ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d start %pM\n", arvif->vdev_id, info->bssid); - /* FIXME: check return value */ ret = ath10k_vdev_start(arvif); + if (ret) { + ath10k_warn("failed to start vdev: %d\n", + ret); + goto exit; + } + + arvif->is_started = true; } /* @@ -2576,7 +2829,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, * IBSS in order to remove BSSID peer. */ if (vif->type == NL80211_IFTYPE_ADHOC) - memcpy(arvif->u.ibss.bssid, info->bssid, + memcpy(arvif->bssid, info->bssid, ETH_ALEN); } } @@ -2645,6 +2898,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, ath10k_bss_assoc(hw, vif, info); } +exit: mutex_unlock(&ar->conf_mutex); } @@ -2850,6 +3104,69 @@ exit: return ret; } +static void ath10k_sta_rc_update_wk(struct work_struct *wk) +{ + struct ath10k *ar; + struct ath10k_vif *arvif; + struct ath10k_sta *arsta; + struct ieee80211_sta *sta; + u32 changed, bw, nss, smps; + int err; + + arsta = container_of(wk, struct ath10k_sta, update_wk); + sta = container_of((void *)arsta, struct ieee80211_sta, drv_priv); + arvif = arsta->arvif; + ar = arvif->ar; + + spin_lock_bh(&ar->data_lock); + + changed = arsta->changed; + arsta->changed = 0; + + bw = arsta->bw; + nss = arsta->nss; + smps = arsta->smps; + + spin_unlock_bh(&ar->data_lock); + + mutex_lock(&ar->conf_mutex); + + if (changed & IEEE80211_RC_BW_CHANGED) { + ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM peer bw %d\n", + sta->addr, bw); + + err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr, + WMI_PEER_CHAN_WIDTH, bw); + if (err) + ath10k_warn("failed to update STA %pM peer bw %d: %d\n", + sta->addr, bw, err); + } + + if (changed & IEEE80211_RC_NSS_CHANGED) { + ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM nss %d\n", + sta->addr, nss); + + err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr, + WMI_PEER_NSS, nss); + if (err) + ath10k_warn("failed to update STA %pM nss %d: %d\n", + sta->addr, nss, err); + } + + if (changed & IEEE80211_RC_SMPS_CHANGED) { + ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM smps %d\n", + sta->addr, smps); + + err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr, + WMI_PEER_SMPS_STATE, smps); + if (err) + ath10k_warn("failed to update STA %pM smps %d: %d\n", + sta->addr, smps, err); + } + + mutex_unlock(&ar->conf_mutex); +} + static int ath10k_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -2858,9 +3175,15 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, { struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv; int max_num_peers; int ret = 0; + /* cancel must be done outside the mutex to avoid deadlock */ + if ((old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST)) + cancel_work_sync(&arsta->update_wk); + mutex_lock(&ar->conf_mutex); if (old_state == IEEE80211_STA_NOTEXIST && @@ -2885,6 +3208,10 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, "mac vdev %d peer create %pM (new sta) num_peers %d\n", arvif->vdev_id, sta->addr, ar->num_peers); + memset(arsta, 0, sizeof(*arsta)); + arsta->arvif = arvif; + INIT_WORK(&arsta->update_wk, ath10k_sta_rc_update_wk); + ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr); if (ret) ath10k_warn("Failed to add peer %pM for vdev %d when adding a new sta: %i\n", @@ -3234,23 +3561,14 @@ static int ath10k_suspend(struct ieee80211_hw *hw, struct ath10k *ar = hw->priv; int ret; - ar->is_target_paused = false; + mutex_lock(&ar->conf_mutex); - ret = ath10k_wmi_pdev_suspend_target(ar); + ret = ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND); if (ret) { - ath10k_warn("could not suspend target (%d)\n", ret); - return 1; - } - - ret = wait_event_interruptible_timeout(ar->event_queue, - ar->is_target_paused == true, - 1 * HZ); - if (ret < 0) { - ath10k_warn("suspend interrupted (%d)\n", ret); - goto resume; - } else if (ret == 0) { - ath10k_warn("suspend timed out - target pause event never came\n"); - goto resume; + if (ret == -ETIMEDOUT) + goto resume; + ret = 1; + goto exit; } ret = ath10k_hif_suspend(ar); @@ -3259,12 +3577,17 @@ static int ath10k_suspend(struct ieee80211_hw *hw, goto resume; } - return 0; + ret = 0; + goto exit; resume: ret = ath10k_wmi_pdev_resume_target(ar); if (ret) ath10k_warn("could not resume target (%d)\n", ret); - return 1; + + ret = 1; +exit: + mutex_unlock(&ar->conf_mutex); + return ret; } static int ath10k_resume(struct ieee80211_hw *hw) @@ -3272,19 +3595,26 @@ static int ath10k_resume(struct ieee80211_hw *hw) struct ath10k *ar = hw->priv; int ret; + mutex_lock(&ar->conf_mutex); + ret = ath10k_hif_resume(ar); if (ret) { ath10k_warn("could not resume hif (%d)\n", ret); - return 1; + ret = 1; + goto exit; } ret = ath10k_wmi_pdev_resume_target(ar); if (ret) { ath10k_warn("could not resume target (%d)\n", ret); - return 1; + ret = 1; + goto exit; } - return 0; + ret = 0; +exit: + mutex_unlock(&ar->conf_mutex); + return ret; } #endif @@ -3640,6 +3970,96 @@ static int ath10k_set_bitrate_mask(struct ieee80211_hw *hw, return ath10k_set_fixed_rate_param(arvif, fixed_rate, fixed_nss); } +static void ath10k_channel_switch_beacon(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_chan_def *chandef) +{ + /* there's no need to do anything here. vif->csa_active is enough */ + return; +} + +static void ath10k_sta_rc_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u32 changed) +{ + struct ath10k *ar = hw->priv; + struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv; + u32 bw, smps; + + spin_lock_bh(&ar->data_lock); + + ath10k_dbg(ATH10K_DBG_MAC, + "mac sta rc update for %pM changed %08x bw %d nss %d smps %d\n", + sta->addr, changed, sta->bandwidth, sta->rx_nss, + sta->smps_mode); + + if (changed & IEEE80211_RC_BW_CHANGED) { + bw = WMI_PEER_CHWIDTH_20MHZ; + + switch (sta->bandwidth) { + case IEEE80211_STA_RX_BW_20: + bw = WMI_PEER_CHWIDTH_20MHZ; + break; + case IEEE80211_STA_RX_BW_40: + bw = WMI_PEER_CHWIDTH_40MHZ; + break; + case IEEE80211_STA_RX_BW_80: + bw = WMI_PEER_CHWIDTH_80MHZ; + break; + case IEEE80211_STA_RX_BW_160: + ath10k_warn("mac sta rc update for %pM: invalid bw %d\n", + sta->addr, sta->bandwidth); + bw = WMI_PEER_CHWIDTH_20MHZ; + break; + } + + arsta->bw = bw; + } + + if (changed & IEEE80211_RC_NSS_CHANGED) + arsta->nss = sta->rx_nss; + + if (changed & IEEE80211_RC_SMPS_CHANGED) { + smps = WMI_PEER_SMPS_PS_NONE; + + switch (sta->smps_mode) { + case IEEE80211_SMPS_AUTOMATIC: + case IEEE80211_SMPS_OFF: + smps = WMI_PEER_SMPS_PS_NONE; + break; + case IEEE80211_SMPS_STATIC: + smps = WMI_PEER_SMPS_STATIC; + break; + case IEEE80211_SMPS_DYNAMIC: + smps = WMI_PEER_SMPS_DYNAMIC; + break; + case IEEE80211_SMPS_NUM_MODES: + ath10k_warn("mac sta rc update for %pM: invalid smps: %d\n", + sta->addr, sta->smps_mode); + smps = WMI_PEER_SMPS_PS_NONE; + break; + } + + arsta->smps = smps; + } + + if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) { + /* FIXME: Not implemented. Probably the only way to do it would + * be to re-assoc the peer. */ + changed &= ~IEEE80211_RC_SUPP_RATES_CHANGED; + ath10k_dbg(ATH10K_DBG_MAC, + "mac sta rc update for %pM: changing supported rates not implemented\n", + sta->addr); + } + + arsta->changed |= changed; + + spin_unlock_bh(&ar->data_lock); + + ieee80211_queue_work(hw, &arsta->update_wk); +} + static const struct ieee80211_ops ath10k_ops = { .tx = ath10k_tx, .start = ath10k_start, @@ -3663,6 +4083,8 @@ static const struct ieee80211_ops ath10k_ops = { .restart_complete = ath10k_restart_complete, .get_survey = ath10k_get_survey, .set_bitrate_mask = ath10k_set_bitrate_mask, + .channel_switch_beacon = ath10k_channel_switch_beacon, + .sta_rc_update = ath10k_sta_rc_update, #ifdef CONFIG_PM .suspend = ath10k_suspend, .resume = ath10k_resume, @@ -4038,10 +4460,12 @@ int ath10k_mac_register(struct ath10k *ar) ar->hw->wiphy->max_scan_ie_len = WLAN_SCAN_PARAMS_MAX_IE_LEN; ar->hw->vif_data_size = sizeof(struct ath10k_vif); + ar->hw->sta_data_size = sizeof(struct ath10k_sta); ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL; ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; ar->hw->wiphy->max_remain_on_channel_duration = 5000; ar->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 29fd197d1fd..34f09106f42 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -64,7 +64,8 @@ static int ath10k_pci_post_rx_pipe(struct ath10k_pci_pipe *pipe_info, int num); static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info); static void ath10k_pci_stop_ce(struct ath10k *ar); -static int ath10k_pci_device_reset(struct ath10k *ar); +static int ath10k_pci_cold_reset(struct ath10k *ar); +static int ath10k_pci_warm_reset(struct ath10k *ar); static int ath10k_pci_wait_for_target_init(struct ath10k *ar); static int ath10k_pci_init_irq(struct ath10k *ar); static int ath10k_pci_deinit_irq(struct ath10k *ar); @@ -833,9 +834,7 @@ static void ath10k_pci_hif_dump_area(struct ath10k *ar) ath10k_err("firmware crashed!\n"); ath10k_err("hardware name %s version 0x%x\n", ar->hw_params.name, ar->target_version); - ath10k_err("firmware version: %u.%u.%u.%u\n", ar->fw_version_major, - ar->fw_version_minor, ar->fw_version_release, - ar->fw_version_build); + ath10k_err("firmware version: %s\n", ar->hw->wiphy->fw_version); host_addr = host_interest_item_address(HI_ITEM(hi_failure_state)); ret = ath10k_pci_diag_read_mem(ar, host_addr, @@ -1502,7 +1501,7 @@ static void ath10k_pci_hif_stop(struct ath10k *ar) * configuration during init. If ringbuffers are freed and the device * were to access them this could lead to memory corruption on the * host. */ - ath10k_pci_device_reset(ar); + ath10k_pci_warm_reset(ar); ar_pci->started = 0; } @@ -1993,7 +1992,94 @@ static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar) ath10k_pci_sleep(ar); } -static int ath10k_pci_hif_power_up(struct ath10k *ar) +static int ath10k_pci_warm_reset(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret = 0; + u32 val; + + ath10k_dbg(ATH10K_DBG_BOOT, "boot performing warm chip reset\n"); + + ret = ath10k_do_pci_wake(ar); + if (ret) { + ath10k_err("failed to wake up target: %d\n", ret); + return ret; + } + + /* debug */ + val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + + PCIE_INTR_CAUSE_ADDRESS); + ath10k_dbg(ATH10K_DBG_BOOT, "boot host cpu intr cause: 0x%08x\n", val); + + val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + + CPU_INTR_ADDRESS); + ath10k_dbg(ATH10K_DBG_BOOT, "boot target cpu intr cause: 0x%08x\n", + val); + + /* disable pending irqs */ + ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + + PCIE_INTR_ENABLE_ADDRESS, 0); + + ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + + PCIE_INTR_CLR_ADDRESS, ~0); + + msleep(100); + + /* clear fw indicator */ + ath10k_pci_write32(ar, ar_pci->fw_indicator_address, 0); + + /* clear target LF timer interrupts */ + val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS + + SOC_LF_TIMER_CONTROL0_ADDRESS); + ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + + SOC_LF_TIMER_CONTROL0_ADDRESS, + val & ~SOC_LF_TIMER_CONTROL0_ENABLE_MASK); + + /* reset CE */ + val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS + + SOC_RESET_CONTROL_ADDRESS); + ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS, + val | SOC_RESET_CONTROL_CE_RST_MASK); + val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS + + SOC_RESET_CONTROL_ADDRESS); + msleep(10); + + /* unreset CE */ + ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS, + val & ~SOC_RESET_CONTROL_CE_RST_MASK); + val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS + + SOC_RESET_CONTROL_ADDRESS); + msleep(10); + + /* debug */ + val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + + PCIE_INTR_CAUSE_ADDRESS); + ath10k_dbg(ATH10K_DBG_BOOT, "boot host cpu intr cause: 0x%08x\n", val); + + val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + + CPU_INTR_ADDRESS); + ath10k_dbg(ATH10K_DBG_BOOT, "boot target cpu intr cause: 0x%08x\n", + val); + + /* CPU warm reset */ + val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS + + SOC_RESET_CONTROL_ADDRESS); + ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS, + val | SOC_RESET_CONTROL_CPU_WARM_RST_MASK); + + val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS + + SOC_RESET_CONTROL_ADDRESS); + ath10k_dbg(ATH10K_DBG_BOOT, "boot target reset state: 0x%08x\n", val); + + msleep(100); + + ath10k_dbg(ATH10K_DBG_BOOT, "boot warm reset complete\n"); + + ath10k_do_pci_sleep(ar); + return ret; +} + +static int __ath10k_pci_hif_power_up(struct ath10k *ar, bool cold_reset) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); const char *irq_mode; @@ -2009,7 +2095,11 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) * is in an unexpected state. We try to catch that here in order to * reset the Target and retry the probe. */ - ret = ath10k_pci_device_reset(ar); + if (cold_reset) + ret = ath10k_pci_cold_reset(ar); + else + ret = ath10k_pci_warm_reset(ar); + if (ret) { ath10k_err("failed to reset target: %d\n", ret); goto err; @@ -2079,7 +2169,7 @@ err_deinit_irq: ath10k_pci_deinit_irq(ar); err_ce: ath10k_pci_ce_deinit(ar); - ath10k_pci_device_reset(ar); + ath10k_pci_warm_reset(ar); err_ps: if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features)) ath10k_do_pci_sleep(ar); @@ -2087,6 +2177,34 @@ err: return ret; } +static int ath10k_pci_hif_power_up(struct ath10k *ar) +{ + int ret; + + /* + * Hardware CUS232 version 2 has some issues with cold reset and the + * preferred (and safer) way to perform a device reset is through a + * warm reset. + * + * Warm reset doesn't always work though (notably after a firmware + * crash) so fall back to cold reset if necessary. + */ + ret = __ath10k_pci_hif_power_up(ar, false); + if (ret) { + ath10k_warn("failed to power up target using warm reset (%d), trying cold reset\n", + ret); + + ret = __ath10k_pci_hif_power_up(ar, true); + if (ret) { + ath10k_err("failed to power up target using cold reset too (%d)\n", + ret); + return ret; + } + } + + return 0; +} + static void ath10k_pci_hif_power_down(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); @@ -2094,7 +2212,7 @@ static void ath10k_pci_hif_power_down(struct ath10k *ar) ath10k_pci_free_early_irq(ar); ath10k_pci_kill_tasklet(ar); ath10k_pci_deinit_irq(ar); - ath10k_pci_device_reset(ar); + ath10k_pci_warm_reset(ar); ath10k_pci_ce_deinit(ar); if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features)) @@ -2411,11 +2529,10 @@ static int ath10k_pci_init_irq(struct ath10k *ar) /* Try MSI-X */ if (ath10k_pci_irq_mode == ATH10K_PCI_IRQ_AUTO && msix_supported) { ar_pci->num_msi_intrs = MSI_NUM_REQUEST; - ret = pci_enable_msi_block(ar_pci->pdev, ar_pci->num_msi_intrs); - if (ret == 0) - return 0; + ret = pci_enable_msi_range(ar_pci->pdev, ar_pci->num_msi_intrs, + ar_pci->num_msi_intrs); if (ret > 0) - pci_disable_msi(ar_pci->pdev); + return 0; /* fall-through */ } @@ -2482,6 +2599,8 @@ static int ath10k_pci_deinit_irq(struct ath10k *ar) case MSI_NUM_REQUEST: pci_disable_msi(ar_pci->pdev); return 0; + default: + pci_disable_msi(ar_pci->pdev); } ath10k_warn("unknown irq configuration upon deinit\n"); @@ -2523,7 +2642,7 @@ out: return ret; } -static int ath10k_pci_device_reset(struct ath10k *ar) +static int ath10k_pci_cold_reset(struct ath10k *ar) { int i, ret; u32 val; diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index 27f20e0510f..ec6f82521b0 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -259,7 +259,7 @@ void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info) status->freq = ch->center_freq; ath10k_dbg(ATH10K_DBG_DATA, - "rx skb %p len %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u\n", + "rx skb %p len %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i\n", info->skb, info->skb->len, status->flag == 0 ? "legacy" : "", @@ -271,7 +271,7 @@ void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info) status->rate_idx, status->vht_nss, status->freq, - status->band); + status->band, status->flag, info->fcs_err); ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "rx skb: ", info->skb->data, info->skb->len); diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 712a606a080..91e501b5499 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -213,7 +213,7 @@ static struct wmi_cmd_map wmi_10x_cmd_map = { .p2p_go_set_beacon_ie = WMI_10X_P2P_GO_SET_BEACON_IE, .p2p_go_set_probe_resp_ie = WMI_10X_P2P_GO_SET_PROBE_RESP_IE, .p2p_set_vendor_ie_data_cmdid = WMI_CMD_UNSUPPORTED, - .ap_ps_peer_param_cmdid = WMI_CMD_UNSUPPORTED, + .ap_ps_peer_param_cmdid = WMI_10X_AP_PS_PEER_PARAM_CMDID, .ap_ps_peer_uapsd_coex_cmdid = WMI_CMD_UNSUPPORTED, .peer_rate_retry_sched_cmdid = WMI_10X_PEER_RATE_RETRY_SCHED_CMDID, .wlan_profile_trigger_cmdid = WMI_10X_WLAN_PROFILE_TRIGGER_CMDID, @@ -420,7 +420,6 @@ static struct wmi_pdev_param_map wmi_pdev_param_map = { .bcnflt_stats_update_period = WMI_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD, .pmf_qos = WMI_PDEV_PARAM_PMF_QOS, .arp_ac_override = WMI_PDEV_PARAM_ARP_AC_OVERRIDE, - .arpdhcp_ac_override = WMI_PDEV_PARAM_UNSUPPORTED, .dcs = WMI_PDEV_PARAM_DCS, .ani_enable = WMI_PDEV_PARAM_ANI_ENABLE, .ani_poll_period = WMI_PDEV_PARAM_ANI_POLL_PERIOD, @@ -472,8 +471,7 @@ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = { .bcnflt_stats_update_period = WMI_10X_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD, .pmf_qos = WMI_10X_PDEV_PARAM_PMF_QOS, - .arp_ac_override = WMI_PDEV_PARAM_UNSUPPORTED, - .arpdhcp_ac_override = WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE, + .arp_ac_override = WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE, .dcs = WMI_10X_PDEV_PARAM_DCS, .ani_enable = WMI_10X_PDEV_PARAM_ANI_ENABLE, .ani_poll_period = WMI_10X_PDEV_PARAM_ANI_POLL_PERIOD, @@ -561,7 +559,6 @@ err_pull: static void ath10k_wmi_tx_beacon_nowait(struct ath10k_vif *arvif) { - struct wmi_bcn_tx_arg arg = {0}; int ret; lockdep_assert_held(&arvif->ar->data_lock); @@ -569,18 +566,16 @@ static void ath10k_wmi_tx_beacon_nowait(struct ath10k_vif *arvif) if (arvif->beacon == NULL) return; - arg.vdev_id = arvif->vdev_id; - arg.tx_rate = 0; - arg.tx_power = 0; - arg.bcn = arvif->beacon->data; - arg.bcn_len = arvif->beacon->len; + if (arvif->beacon_sent) + return; - ret = ath10k_wmi_beacon_send_nowait(arvif->ar, &arg); + ret = ath10k_wmi_beacon_send_ref_nowait(arvif); if (ret) return; - dev_kfree_skb_any(arvif->beacon); - arvif->beacon = NULL; + /* We need to retain the arvif->beacon reference for DMA unmapping and + * freeing the skbuff later. */ + arvif->beacon_sent = true; } static void ath10k_wmi_tx_beacons_iter(void *data, u8 *mac, @@ -1116,7 +1111,27 @@ static void ath10k_wmi_event_vdev_stopped(struct ath10k *ar, static void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_PEER_STA_KICKOUT_EVENTID\n"); + struct wmi_peer_sta_kickout_event *ev; + struct ieee80211_sta *sta; + + ev = (struct wmi_peer_sta_kickout_event *)skb->data; + + ath10k_dbg(ATH10K_DBG_WMI, "wmi event peer sta kickout %pM\n", + ev->peer_macaddr.addr); + + rcu_read_lock(); + + sta = ieee80211_find_sta_by_ifaddr(ar->hw, ev->peer_macaddr.addr, NULL); + if (!sta) { + ath10k_warn("Spurious quick kickout for STA %pM\n", + ev->peer_macaddr.addr); + goto exit; + } + + ieee80211_report_low_ack(sta, 10); + +exit: + rcu_read_unlock(); } /* @@ -1217,6 +1232,13 @@ static void ath10k_wmi_update_tim(struct ath10k *ar, tim->bitmap_ctrl = !!__le32_to_cpu(bcn_info->tim_info.tim_mcast); memcpy(tim->virtual_map, arvif->u.ap.tim_bitmap, pvm_len); + if (tim->dtim_count == 0) { + ATH10K_SKB_CB(bcn)->bcn.dtim_zero = true; + + if (__le32_to_cpu(bcn_info->tim_info.tim_mcast) == 1) + ATH10K_SKB_CB(bcn)->bcn.deliver_cab = true; + } + ath10k_dbg(ATH10K_DBG_MGMT, "dtim %d/%d mcast %d pvmlen %d\n", tim->dtim_count, tim->dtim_period, tim->bitmap_ctrl, pvm_len); @@ -1385,6 +1407,17 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) continue; } + /* There are no completions for beacons so wait for next SWBA + * before telling mac80211 to decrement CSA counter + * + * Once CSA counter is completed stop sending beacons until + * actual channel switch is done */ + if (arvif->vif->csa_active && + ieee80211_csa_is_complete(arvif->vif)) { + ieee80211_csa_finish(arvif->vif); + continue; + } + bcn = ieee80211_beacon_get(ar->hw, arvif->vif); if (!bcn) { ath10k_warn("could not get mac80211 beacon\n"); @@ -1396,13 +1429,20 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) ath10k_wmi_update_noa(ar, arvif, bcn, bcn_info); spin_lock_bh(&ar->data_lock); + if (arvif->beacon) { - ath10k_warn("SWBA overrun on vdev %d\n", - arvif->vdev_id); + if (!arvif->beacon_sent) + ath10k_warn("SWBA overrun on vdev %d\n", + arvif->vdev_id); + + ath10k_skb_unmap(ar->dev, arvif->beacon); dev_kfree_skb_any(arvif->beacon); } + ath10k_skb_map(ar->dev, bcn); + arvif->beacon = bcn; + arvif->beacon_sent = false; ath10k_wmi_tx_beacon_nowait(arvif); spin_unlock_bh(&ar->data_lock); @@ -2031,11 +2071,11 @@ static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb) memcpy(ar->mac_addr, ev->mac_addr.addr, ETH_ALEN); ath10k_dbg(ATH10K_DBG_WMI, - "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d\n", + "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d skb->len %i ev-sz %zu\n", __le32_to_cpu(ev->sw_version), __le32_to_cpu(ev->abi_version), ev->mac_addr.addr, - __le32_to_cpu(ev->status)); + __le32_to_cpu(ev->status), skb->len, sizeof(*ev)); complete(&ar->wmi.unified_ready); return 0; @@ -2403,7 +2443,7 @@ int ath10k_wmi_pdev_set_channel(struct ath10k *ar, ar->wmi.cmd->pdev_set_channel_cmdid); } -int ath10k_wmi_pdev_suspend_target(struct ath10k *ar) +int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt) { struct wmi_pdev_suspend_cmd *cmd; struct sk_buff *skb; @@ -2413,7 +2453,7 @@ int ath10k_wmi_pdev_suspend_target(struct ath10k *ar) return -ENOMEM; cmd = (struct wmi_pdev_suspend_cmd *)skb->data; - cmd->suspend_opt = WMI_PDEV_SUSPEND; + cmd->suspend_opt = __cpu_to_le32(suspend_opt); return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_suspend_cmdid); } @@ -3411,25 +3451,41 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar, return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_assoc_cmdid); } -int ath10k_wmi_beacon_send_nowait(struct ath10k *ar, - const struct wmi_bcn_tx_arg *arg) +/* This function assumes the beacon is already DMA mapped */ +int ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif) { - struct wmi_bcn_tx_cmd *cmd; + struct wmi_bcn_tx_ref_cmd *cmd; struct sk_buff *skb; + struct sk_buff *beacon = arvif->beacon; + struct ath10k *ar = arvif->ar; + struct ieee80211_hdr *hdr; int ret; + u16 fc; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd) + arg->bcn_len); + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); if (!skb) return -ENOMEM; - cmd = (struct wmi_bcn_tx_cmd *)skb->data; - cmd->hdr.vdev_id = __cpu_to_le32(arg->vdev_id); - cmd->hdr.tx_rate = __cpu_to_le32(arg->tx_rate); - cmd->hdr.tx_power = __cpu_to_le32(arg->tx_power); - cmd->hdr.bcn_len = __cpu_to_le32(arg->bcn_len); - memcpy(cmd->bcn, arg->bcn, arg->bcn_len); + hdr = (struct ieee80211_hdr *)beacon->data; + fc = le16_to_cpu(hdr->frame_control); + + cmd = (struct wmi_bcn_tx_ref_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(arvif->vdev_id); + cmd->data_len = __cpu_to_le32(beacon->len); + cmd->data_ptr = __cpu_to_le32(ATH10K_SKB_CB(beacon)->paddr); + cmd->msdu_id = 0; + cmd->frame_control = __cpu_to_le32(fc); + cmd->flags = 0; + + if (ATH10K_SKB_CB(beacon)->bcn.dtim_zero) + cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DTIM_ZERO); + + if (ATH10K_SKB_CB(beacon)->bcn.deliver_cab) + cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DELIVER_CAB); + + ret = ath10k_wmi_cmd_send_nowait(ar, skb, + ar->wmi.cmd->pdev_send_bcn_cmdid); - ret = ath10k_wmi_cmd_send_nowait(ar, skb, ar->wmi.cmd->bcn_tx_cmdid); if (ret) dev_kfree_skb(skb); diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 4b5e7d3d32b..4fcc96aa951 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -2277,7 +2277,6 @@ struct wmi_pdev_param_map { u32 bcnflt_stats_update_period; u32 pmf_qos; u32 arp_ac_override; - u32 arpdhcp_ac_override; u32 dcs; u32 ani_enable; u32 ani_poll_period; @@ -3403,6 +3402,24 @@ struct wmi_bcn_tx_arg { const void *bcn; }; +enum wmi_bcn_tx_ref_flags { + WMI_BCN_TX_REF_FLAG_DTIM_ZERO = 0x1, + WMI_BCN_TX_REF_FLAG_DELIVER_CAB = 0x2, +}; + +struct wmi_bcn_tx_ref_cmd { + __le32 vdev_id; + __le32 data_len; + /* physical address of the frame - dma pointer */ + __le32 data_ptr; + /* id for host to track */ + __le32 msdu_id; + /* frame ctrl to setup PPDU desc */ + __le32 frame_control; + /* to control CABQ traffic: WMI_BCN_TX_REF_FLAG_ */ + __le32 flags; +} __packed; + /* Beacon filter */ #define WMI_BCN_FILTER_ALL 0 /* Filter all beacons */ #define WMI_BCN_FILTER_NONE 1 /* Pass all beacons */ @@ -3859,6 +3876,12 @@ enum wmi_peer_smps_state { WMI_PEER_SMPS_DYNAMIC = 0x2 }; +enum wmi_peer_chwidth { + WMI_PEER_CHWIDTH_20MHZ = 0, + WMI_PEER_CHWIDTH_40MHZ = 1, + WMI_PEER_CHWIDTH_80MHZ = 2, +}; + enum wmi_peer_param { WMI_PEER_SMPS_STATE = 0x1, /* see %wmi_peer_smps_state */ WMI_PEER_AMPDU = 0x2, @@ -4039,6 +4062,10 @@ struct wmi_chan_info_event { __le32 cycle_count; } __packed; +struct wmi_peer_sta_kickout_event { + struct wmi_mac_addr peer_macaddr; +} __packed; + #define WMI_CHAN_INFO_FLAG_COMPLETE BIT(0) /* FIXME: empirically extrapolated */ @@ -4172,7 +4199,7 @@ int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar); int ath10k_wmi_connect_htc_service(struct ath10k *ar); int ath10k_wmi_pdev_set_channel(struct ath10k *ar, const struct wmi_channel_arg *); -int ath10k_wmi_pdev_suspend_target(struct ath10k *ar); +int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt); int ath10k_wmi_pdev_resume_target(struct ath10k *ar); int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g, u16 rd5g, u16 ctl2g, u16 ctl5g); @@ -4219,8 +4246,7 @@ int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, enum wmi_ap_ps_peer_param param_id, u32 value); int ath10k_wmi_scan_chan_list(struct ath10k *ar, const struct wmi_scan_chan_list_arg *arg); -int ath10k_wmi_beacon_send_nowait(struct ath10k *ar, - const struct wmi_bcn_tx_arg *arg); +int ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif); int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar, const struct wmi_pdev_set_wmm_params_arg *arg); int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id); diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c index f38ff6a6255..56c3fd5cef6 100644 --- a/drivers/net/wireless/ath/ath6kl/usb.c +++ b/drivers/net/wireless/ath/ath6kl/usb.c @@ -24,7 +24,7 @@ /* constants */ #define TX_URB_COUNT 32 #define RX_URB_COUNT 32 -#define ATH6KL_USB_RX_BUFFER_SIZE 1700 +#define ATH6KL_USB_RX_BUFFER_SIZE 4096 /* tx/rx pipes for usb */ enum ATH6KL_USB_PIPE_ID { @@ -481,8 +481,8 @@ static void ath6kl_usb_start_recv_pipes(struct ath6kl_usb *ar_usb) * ATH6KL_USB_RX_BUFFER_SIZE); */ - ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA].urb_cnt_thresh = - ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA].urb_alloc / 2; + ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA].urb_cnt_thresh = 1; + ath6kl_usb_post_recv_transfers(&ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA], ATH6KL_USB_RX_BUFFER_SIZE); } diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 4f16d79c9eb..8b4ce28e3ce 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -914,7 +914,7 @@ ath6kl_get_regpair(u16 regdmn) return NULL; for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) { - if (regDomainPairs[i].regDmnEnum == regdmn) + if (regDomainPairs[i].reg_domain == regdmn) return ®DomainPairs[i]; } @@ -954,7 +954,7 @@ static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len) country = ath6kl_regd_find_country_by_rd((u16) reg_code); if (regpair) ath6kl_dbg(ATH6KL_DBG_WMI, "Regpair used: 0x%0x\n", - regpair->regDmnEnum); + regpair->reg_domain); else ath6kl_warn("Regpair not found reg_code 0x%0x\n", reg_code); diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index e5e905910db..415393dfb6f 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -222,7 +222,7 @@ static const struct ieee80211_regdomain *ath_default_world_regdomain(void) static const struct ieee80211_regdomain *ath_world_regdomain(struct ath_regulatory *reg) { - switch (reg->regpair->regDmnEnum) { + switch (reg->regpair->reg_domain) { case 0x60: case 0x61: case 0x62: @@ -431,7 +431,7 @@ static void ath_reg_apply_world_flags(struct wiphy *wiphy, enum nl80211_reg_initiator initiator, struct ath_regulatory *reg) { - switch (reg->regpair->regDmnEnum) { + switch (reg->regpair->reg_domain) { case 0x60: case 0x63: case 0x66: @@ -560,7 +560,7 @@ static bool ath_regd_is_eeprom_valid(struct ath_regulatory *reg) printk(KERN_DEBUG "ath: EEPROM indicates we " "should expect a direct regpair map\n"); for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) - if (regDomainPairs[i].regDmnEnum == rd) + if (regDomainPairs[i].reg_domain == rd) return true; } printk(KERN_DEBUG @@ -617,7 +617,7 @@ ath_get_regpair(int regdmn) if (regdmn == NO_ENUMRD) return NULL; for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) { - if (regDomainPairs[i].regDmnEnum == regdmn) + if (regDomainPairs[i].reg_domain == regdmn) return ®DomainPairs[i]; } return NULL; @@ -741,7 +741,7 @@ static int __ath_regd_init(struct ath_regulatory *reg) printk(KERN_DEBUG "ath: Country alpha2 being used: %c%c\n", reg->alpha2[0], reg->alpha2[1]); printk(KERN_DEBUG "ath: Regpair used: 0x%0x\n", - reg->regpair->regDmnEnum); + reg->regpair->reg_domain); return 0; } |