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

Commit d740d8fd authored by Michal Kazior's avatar Michal Kazior Committed by Kalle Valo
Browse files

ath10k: unify tx mode and dispatch



There are a few different tx paths depending on
firmware and frame itself.

Creating a uniform decision will make it possible
to switch between different txmode easier, both
for testing and for future features as well.

Signed-off-by: default avatarMichal Kazior <michal.kazior@tieto.com>
Signed-off-by: default avatarMarek Puzyniak <marek.puzyniak@tieto.com>
Signed-off-by: default avatarKalle Valo <kvalo@qca.qualcomm.com>
parent 6fcafef7
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -84,6 +84,8 @@ struct ath10k_skb_cb {
	dma_addr_t paddr;
	u8 eid;
	u8 vdev_id;
	enum ath10k_hw_txrx_mode txmode;
	bool is_protected;

	struct {
		u8 tid;
+0 −8
Original line number Diff line number Diff line
@@ -637,14 +637,6 @@ static int ath10k_htt_rx_crypto_tail_len(struct ath10k *ar,
	return 0;
}

struct rfc1042_hdr {
	u8 llc_dsap;
	u8 llc_ssap;
	u8 llc_ctrl;
	u8 snap_oui[3];
	__be16 snap_type;
} __packed;

struct amsdu_subframe_hdr {
	u8 dst[ETH_ALEN];
	u8 src[ETH_ALEN];
+14 −16
Original line number Diff line number Diff line
@@ -420,9 +420,8 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
	int res;
	u8 flags0 = 0;
	u16 msdu_id, flags1 = 0;
	dma_addr_t paddr;
	u32 frags_paddr;
	bool use_frags;
	dma_addr_t paddr = 0;
	u32 frags_paddr = 0;

	res = ath10k_htt_tx_inc_pending(htt);
	if (res)
@@ -440,12 +439,6 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
	prefetch_len = min(htt->prefetch_len, msdu->len);
	prefetch_len = roundup(prefetch_len, 4);

	/* Since HTT 3.0 there is no separate mgmt tx command. However in case
	 * of mgmt tx using TX_FRM there is not tx fragment list. Instead of tx
	 * fragment list host driver specifies directly frame pointer. */
	use_frags = htt->target_version_major < 3 ||
		    !ieee80211_is_mgmt(hdr->frame_control);

	skb_cb->htt.txbuf = dma_pool_alloc(htt->tx_pool, GFP_ATOMIC,
					   &paddr);
	if (!skb_cb->htt.txbuf) {
@@ -466,7 +459,12 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
	if (res)
		goto err_free_txbuf;

	if (likely(use_frags)) {
	switch (skb_cb->txmode) {
	case ATH10K_HW_TXRX_RAW:
	case ATH10K_HW_TXRX_NATIVE_WIFI:
		flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT;
		/* pass through */
	case ATH10K_HW_TXRX_ETHERNET:
		frags = skb_cb->htt.txbuf->frags;

		frags[0].paddr = __cpu_to_le32(skb_cb->paddr);
@@ -474,15 +472,17 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
		frags[1].paddr = 0;
		frags[1].len = 0;

		flags0 |= SM(ATH10K_HW_TXRX_NATIVE_WIFI,
			     HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
		flags0 |= SM(skb_cb->txmode, HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);

		frags_paddr = skb_cb->htt.txbuf_paddr;
	} else {
		break;
	case ATH10K_HW_TXRX_MGMT:
		flags0 |= SM(ATH10K_HW_TXRX_MGMT,
			     HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
		flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT;

		frags_paddr = skb_cb->paddr;
		break;
	}

	/* Normally all commands go through HTC which manages tx credits for
@@ -508,11 +508,9 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
			prefetch_len);
	skb_cb->htt.txbuf->htc_hdr.flags = 0;

	if (!ieee80211_has_protected(hdr->frame_control))
	if (!skb_cb->is_protected)
		flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT;

	flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT;

	flags1 |= SM((u16)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID);
	flags1 |= SM((u16)tid, HTT_DATA_TX_DESC_FLAGS1_EXT_TID);
	if (msdu->ip_summed == CHECKSUM_PARTIAL) {
+120 −35
Original line number Diff line number Diff line
@@ -2522,6 +2522,43 @@ static u8 ath10k_tx_h_get_vdev_id(struct ath10k *ar, struct ieee80211_vif *vif)
	return 0;
}

static enum ath10k_hw_txrx_mode
ath10k_tx_h_get_txmode(struct ath10k *ar, struct ieee80211_vif *vif,
		       struct sk_buff *skb)
{
	const struct ieee80211_hdr *hdr = (void *)skb->data;
	__le16 fc = hdr->frame_control;

	if (!vif || vif->type == NL80211_IFTYPE_MONITOR)
		return ATH10K_HW_TXRX_RAW;

	if (ieee80211_is_mgmt(fc))
		return ATH10K_HW_TXRX_MGMT;

	/* Workaround:
	 *
	 * NullFunc frames are mostly used to ping if a client or AP are still
	 * reachable and responsive. This implies tx status reports must be
	 * accurate - otherwise either mac80211 or userspace (e.g. hostapd) can
	 * come to a conclusion that the other end disappeared and tear down
	 * BSS connection or it can never disconnect from BSS/client (which is
	 * the case).
	 *
	 * Firmware with HTT older than 3.0 delivers incorrect tx status for
	 * NullFunc frames to driver. However there's a HTT Mgmt Tx command
	 * which seems to deliver correct tx reports for NullFunc frames. The
	 * downside of using it is it ignores client powersave state so it can
	 * end up disconnecting sleeping clients in AP mode. It should fix STA
	 * mode though because AP don't sleep.
	 */
	if (ar->htt.target_version_major < 3 &&
	    (ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc)) &&
	    !test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX, ar->fw_features))
		return ATH10K_HW_TXRX_MGMT;

	return ATH10K_HW_TXRX_NATIVE_WIFI;
}

/* HTT Tx uses Native Wifi tx mode which expects 802.11 frames without QoS
 * Control in the header.
 */
@@ -2550,6 +2587,33 @@ static void ath10k_tx_h_nwifi(struct ieee80211_hw *hw, struct sk_buff *skb)
	hdr->frame_control &= ~__cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
}

static void ath10k_tx_h_8023(struct sk_buff *skb)
{
	struct ieee80211_hdr *hdr;
	struct rfc1042_hdr *rfc1042;
	struct ethhdr *eth;
	size_t hdrlen;
	u8 da[ETH_ALEN];
	u8 sa[ETH_ALEN];
	__be16 type;

	hdr = (void *)skb->data;
	hdrlen = ieee80211_hdrlen(hdr->frame_control);
	rfc1042 = (void *)skb->data + hdrlen;

	ether_addr_copy(da, ieee80211_get_DA(hdr));
	ether_addr_copy(sa, ieee80211_get_SA(hdr));
	type = rfc1042->snap_type;

	skb_pull(skb, hdrlen + sizeof(*rfc1042));
	skb_push(skb, sizeof(*eth));

	eth = (void *)skb->data;
	ether_addr_copy(eth->h_dest, da);
	ether_addr_copy(eth->h_source, sa);
	eth->h_proto = type;
}

static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar,
				       struct ieee80211_vif *vif,
				       struct sk_buff *skb)
@@ -2586,45 +2650,51 @@ static bool ath10k_mac_need_offchan_tx_work(struct ath10k *ar)
		 ar->htt.target_version_minor >= 4);
}

static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
static int ath10k_mac_tx_wmi_mgmt(struct ath10k *ar, struct sk_buff *skb)
{
	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
	struct sk_buff_head *q = &ar->wmi_mgmt_tx_queue;
	int ret = 0;

	if (ar->htt.target_version_major >= 3) {
		/* Since HTT 3.0 there is no separate mgmt tx command */
		ret = ath10k_htt_tx(&ar->htt, skb);
		goto exit;
	}
	spin_lock_bh(&ar->data_lock);

	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(ar, "reached WMI management transmit queue limit\n");
				ret = -EBUSY;
				goto exit;
	if (skb_queue_len(q) == ATH10K_MAX_NUM_MGMT_PENDING) {
		ath10k_warn(ar, "wmi mgmt tx queue is full\n");
		ret = -ENOSPC;
		goto unlock;
	}

			skb_queue_tail(&ar->wmi_mgmt_tx_queue, skb);
	__skb_queue_tail(q, 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. */
		ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
	} else {
		ret = ath10k_htt_tx(&ar->htt, skb);

unlock:
	spin_unlock_bh(&ar->data_lock);

	return ret;
}

static void ath10k_mac_tx(struct ath10k *ar, struct sk_buff *skb)
{
	struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
	struct ath10k_htt *htt = &ar->htt;
	int ret = 0;

	switch (cb->txmode) {
	case ATH10K_HW_TXRX_RAW:
	case ATH10K_HW_TXRX_NATIVE_WIFI:
	case ATH10K_HW_TXRX_ETHERNET:
		ret = ath10k_htt_tx(htt, skb);
		break;
	case ATH10K_HW_TXRX_MGMT:
		if (test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
			     ar->fw_features))
			ret = ath10k_mac_tx_wmi_mgmt(ar, skb);
		else if (ar->htt.target_version_major >= 3)
			ret = ath10k_htt_tx(htt, skb);
		else
			ret = ath10k_htt_mgmt_tx(htt, skb);
		break;
	}

exit:
	if (ret) {
		ath10k_warn(ar, "failed to transmit packet, dropping: %d\n",
			    ret);
@@ -2697,7 +2767,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
		ar->offchan_tx_skb = skb;
		spin_unlock_bh(&ar->data_lock);

		ath10k_tx_htt(ar, skb);
		ath10k_mac_tx(ar, skb);

		ret = wait_for_completion_timeout(&ar->offchan_tx_completed,
						  3 * HZ);
@@ -2922,6 +2992,7 @@ static void ath10k_tx(struct ieee80211_hw *hw,
	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
	struct ieee80211_vif *vif = info->control.vif;
	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
	__le16 fc = hdr->frame_control;

	/* We should disable CCK RATE due to P2P */
	if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
@@ -2931,12 +3002,26 @@ static void ath10k_tx(struct ieee80211_hw *hw,
	ATH10K_SKB_CB(skb)->htt.freq = 0;
	ATH10K_SKB_CB(skb)->htt.tid = ath10k_tx_h_get_tid(hdr);
	ATH10K_SKB_CB(skb)->vdev_id = ath10k_tx_h_get_vdev_id(ar, vif);
	ATH10K_SKB_CB(skb)->txmode = ath10k_tx_h_get_txmode(ar, vif, skb);
	ATH10K_SKB_CB(skb)->is_protected = ieee80211_has_protected(fc);

	/* it makes no sense to process injected frames like that */
	if (vif && vif->type != NL80211_IFTYPE_MONITOR) {
	switch (ATH10K_SKB_CB(skb)->txmode) {
	case ATH10K_HW_TXRX_MGMT:
	case ATH10K_HW_TXRX_NATIVE_WIFI:
		ath10k_tx_h_nwifi(hw, skb);
		ath10k_tx_h_add_p2p_noa_ie(ar, vif, skb);
		ath10k_tx_h_seq_no(vif, skb);
		break;
	case ATH10K_HW_TXRX_ETHERNET:
		ath10k_tx_h_8023(skb);
		break;
	case ATH10K_HW_TXRX_RAW:
		/* FIXME: Packet injection isn't implemented. It should be
		 * doable with firmware 10.2 on qca988x.
		 */
		WARN_ON_ONCE(1);
		ieee80211_free_txskb(hw, skb);
		return;
	}

	if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
@@ -2958,7 +3043,7 @@ static void ath10k_tx(struct ieee80211_hw *hw,
		}
	}

	ath10k_tx_htt(ar, skb);
	ath10k_mac_tx(ar, skb);
}

/* Must not be called with conf_mutex held as workers can use that also. */
+8 −0
Original line number Diff line number Diff line
@@ -28,6 +28,14 @@ struct ath10k_generic_iter {
	int ret;
};

struct rfc1042_hdr {
	u8 llc_dsap;
	u8 llc_ssap;
	u8 llc_ctrl;
	u8 snap_oui[3];
	__be16 snap_type;
} __packed;

struct ath10k *ath10k_mac_create(size_t priv_size);
void ath10k_mac_destroy(struct ath10k *ar);
int ath10k_mac_register(struct ath10k *ar);