Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5e00d31a authored by Bartosz Markowski's avatar Bartosz Markowski Committed by Kalle Valo
Browse files

ath10k: bring back the WMI path for mgmt frames



This is still the only way to submit mgmt frames in case
of 10.X firmware.

This patch introduces wmi_mgmt_tx queue, because of the
fact WMI command can block. This is a problem for
ath10k_tx_htt(), since it's called from atomic context.
The skb queue and worker are introduced to move the mgmt
frame handling out of .tx callback context and not block.

Signed-off-by: default avatarBartosz Markowski <bartosz.markowski@tieto.com>
Signed-off-by: default avatarKalle Valo <kvalo@qca.qualcomm.com>
parent b3effe61
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -520,6 +520,9 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
	INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work);
	skb_queue_head_init(&ar->offchan_tx_queue);

	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);
+9 −1
Original line number Diff line number Diff line
@@ -43,15 +43,17 @@
/* Antenna noise floor */
#define ATH10K_DEFAULT_NOISE_FLOOR -95

#define ATH10K_MAX_NUM_MGMT_PENDING 16

struct ath10k;

struct ath10k_skb_cb {
	dma_addr_t paddr;
	bool is_mapped;
	bool is_aborted;
	u8 vdev_id;

	struct {
		u8 vdev_id;
		u8 tid;
		bool is_offchan;

@@ -284,6 +286,9 @@ enum ath10k_fw_features {
	/* firmware from 10X branch */
	ATH10K_FW_FEATURE_WMI_10X = 1,

	/* firmware support tx frame management over WMI, otherwise it's HTT */
	ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX = 2,

	/* keep last */
	ATH10K_FW_FEATURE_COUNT,
};
@@ -393,6 +398,9 @@ struct ath10k {
	struct completion offchan_tx_completed;
	struct sk_buff *offchan_tx_skb;

	struct work_struct wmi_mgmt_tx_work;
	struct sk_buff_head wmi_mgmt_tx_queue;

	enum ath10k_state state;

	struct work_struct restart_work;
+2 −2
Original line number Diff line number Diff line
@@ -308,7 +308,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
	struct sk_buff *txdesc = NULL;
	struct htt_cmd *cmd;
	struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu);
	u8 vdev_id = skb_cb->htt.vdev_id;
	u8 vdev_id = skb_cb->vdev_id;
	int len = 0;
	int msdu_id = -1;
	int res;
@@ -384,7 +384,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
	struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu);
	struct sk_buff *txdesc = NULL;
	bool use_frags;
	u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id;
	u8 vdev_id = ATH10K_SKB_CB(msdu)->vdev_id;
	u8 tid;
	int prefetch_len, desc_len;
	int msdu_id = -1;
+60 −10
Original line number Diff line number Diff line
@@ -1486,7 +1486,7 @@ static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb)
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 */
@@ -1494,16 +1494,32 @@ static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
		goto exit;
	}

	if (ieee80211_is_mgmt(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 (ieee80211_is_nullfunc(hdr->frame_control))
		}
	} 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) {
@@ -1554,7 +1570,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)

		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);
@@ -1596,6 +1612,36 @@ void ath10k_offchan_tx_work(struct work_struct *work)
	}
}

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 */
/************/
@@ -1754,14 +1800,14 @@ static void ath10k_tx(struct ieee80211_hw *hw,
		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);
@@ -1783,6 +1829,7 @@ void ath10k_halt(struct ath10k *ar)

	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);
@@ -1859,7 +1906,10 @@ static void ath10k_stop(struct ieee80211_hw *hw)
	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);
}

+2 −0
Original line number Diff line number Diff line
@@ -34,6 +34,8 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id);
void ath10k_reset_scan(unsigned long ptr);
void ath10k_offchan_tx_purge(struct ath10k *ar);
void ath10k_offchan_tx_work(struct work_struct *work);
void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar);
void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work);
void ath10k_halt(struct ath10k *ar);

static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
Loading