static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- int ret;
+ int ret = 0;
if (ar->htt.target_version_major >= 3) {
/* Since HTT 3.0 there is no separate mgmt tx command */
goto exit;
}
- if (ieee80211_is_mgmt(hdr->frame_control))
- ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
- else if (ieee80211_is_nullfunc(hdr->frame_control))
+ if (ieee80211_is_mgmt(hdr->frame_control)) {
+ if (test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
+ ar->fw_features)) {
+ if (skb_queue_len(&ar->wmi_mgmt_tx_queue) >=
+ ATH10K_MAX_NUM_MGMT_PENDING) {
+ ath10k_warn("wmi mgmt_tx queue limit reached\n");
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ skb_queue_tail(&ar->wmi_mgmt_tx_queue, skb);
+ ieee80211_queue_work(ar->hw, &ar->wmi_mgmt_tx_work);
+ } else {
+ ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
+ }
+ } else if (!test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
+ ar->fw_features) &&
+ ieee80211_is_nullfunc(hdr->frame_control)) {
/* FW does not report tx status properly for NullFunc frames
* unless they are sent through mgmt tx path. mac80211 sends
- * those frames when it detects link/beacon loss and depends on
- * the tx status to be correct. */
+ * those frames when it detects link/beacon loss and depends
+ * on the tx status to be correct. */
ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
- else
+ } else {
ret = ath10k_htt_tx(&ar->htt, skb);
+ }
exit:
if (ret) {
hdr = (struct ieee80211_hdr *)skb->data;
peer_addr = ieee80211_get_DA(hdr);
- vdev_id = ATH10K_SKB_CB(skb)->htt.vdev_id;
+ vdev_id = ATH10K_SKB_CB(skb)->vdev_id;
spin_lock_bh(&ar->data_lock);
peer = ath10k_peer_find(ar, vdev_id, peer_addr);
}
}
+void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar)
+{
+ struct sk_buff *skb;
+
+ for (;;) {
+ skb = skb_dequeue(&ar->wmi_mgmt_tx_queue);
+ if (!skb)
+ break;
+
+ ieee80211_free_txskb(ar->hw, skb);
+ }
+}
+
+void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work)
+{
+ struct ath10k *ar = container_of(work, struct ath10k, wmi_mgmt_tx_work);
+ struct sk_buff *skb;
+ int ret;
+
+ for (;;) {
+ skb = skb_dequeue(&ar->wmi_mgmt_tx_queue);
+ if (!skb)
+ break;
+
+ ret = ath10k_wmi_mgmt_tx(ar, skb);
+ if (ret)
+ ath10k_warn("wmi mgmt_tx failed (%d)\n", ret);
+ }
+}
+
/************/
/* Scanning */
/************/
ath10k_tx_h_seq_no(skb);
}
+ ATH10K_SKB_CB(skb)->vdev_id = vdev_id;
ATH10K_SKB_CB(skb)->htt.is_offchan = false;
- ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id;
ATH10K_SKB_CB(skb)->htt.tid = tid;
if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
spin_lock_bh(&ar->data_lock);
ATH10K_SKB_CB(skb)->htt.is_offchan = true;
- ATH10K_SKB_CB(skb)->htt.vdev_id = ar->scan.vdev_id;
+ ATH10K_SKB_CB(skb)->vdev_id = ar->scan.vdev_id;
spin_unlock_bh(&ar->data_lock);
ath10k_dbg(ATH10K_DBG_MAC, "queued offchannel skb %p\n", skb);
del_timer_sync(&ar->scan.timeout);
ath10k_offchan_tx_purge(ar);
+ ath10k_mgmt_over_wmi_tx_purge(ar);
ath10k_peer_cleanup_all(ar);
ath10k_core_stop(ar);
ath10k_hif_power_down(ar);
ar->state = ATH10K_STATE_OFF;
mutex_unlock(&ar->conf_mutex);
+ ath10k_mgmt_over_wmi_tx_purge(ar);
+
cancel_work_sync(&ar->offchan_tx_work);
+ cancel_work_sync(&ar->wmi_mgmt_tx_work);
cancel_work_sync(&ar->restart_work);
}