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: Michal Kazior <michal.kazior@tieto.com> Signed-off-by: Marek Puzyniak <marek.puzyniak@tieto.com> Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
This commit is contained in:
parent
6fcafef736
commit
d740d8fd24
|
@ -84,6 +84,8 @@ struct ath10k_skb_cb {
|
||||||
dma_addr_t paddr;
|
dma_addr_t paddr;
|
||||||
u8 eid;
|
u8 eid;
|
||||||
u8 vdev_id;
|
u8 vdev_id;
|
||||||
|
enum ath10k_hw_txrx_mode txmode;
|
||||||
|
bool is_protected;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
u8 tid;
|
u8 tid;
|
||||||
|
|
|
@ -637,14 +637,6 @@ static int ath10k_htt_rx_crypto_tail_len(struct ath10k *ar,
|
||||||
return 0;
|
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 {
|
struct amsdu_subframe_hdr {
|
||||||
u8 dst[ETH_ALEN];
|
u8 dst[ETH_ALEN];
|
||||||
u8 src[ETH_ALEN];
|
u8 src[ETH_ALEN];
|
||||||
|
|
|
@ -420,9 +420,8 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
|
||||||
int res;
|
int res;
|
||||||
u8 flags0 = 0;
|
u8 flags0 = 0;
|
||||||
u16 msdu_id, flags1 = 0;
|
u16 msdu_id, flags1 = 0;
|
||||||
dma_addr_t paddr;
|
dma_addr_t paddr = 0;
|
||||||
u32 frags_paddr;
|
u32 frags_paddr = 0;
|
||||||
bool use_frags;
|
|
||||||
|
|
||||||
res = ath10k_htt_tx_inc_pending(htt);
|
res = ath10k_htt_tx_inc_pending(htt);
|
||||||
if (res)
|
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 = min(htt->prefetch_len, msdu->len);
|
||||||
prefetch_len = roundup(prefetch_len, 4);
|
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,
|
skb_cb->htt.txbuf = dma_pool_alloc(htt->tx_pool, GFP_ATOMIC,
|
||||||
&paddr);
|
&paddr);
|
||||||
if (!skb_cb->htt.txbuf) {
|
if (!skb_cb->htt.txbuf) {
|
||||||
|
@ -466,7 +459,12 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
|
||||||
if (res)
|
if (res)
|
||||||
goto err_free_txbuf;
|
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 = skb_cb->htt.txbuf->frags;
|
||||||
|
|
||||||
frags[0].paddr = __cpu_to_le32(skb_cb->paddr);
|
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].paddr = 0;
|
||||||
frags[1].len = 0;
|
frags[1].len = 0;
|
||||||
|
|
||||||
flags0 |= SM(ATH10K_HW_TXRX_NATIVE_WIFI,
|
flags0 |= SM(skb_cb->txmode, HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
|
||||||
HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
|
|
||||||
|
|
||||||
frags_paddr = skb_cb->htt.txbuf_paddr;
|
frags_paddr = skb_cb->htt.txbuf_paddr;
|
||||||
} else {
|
break;
|
||||||
|
case ATH10K_HW_TXRX_MGMT:
|
||||||
flags0 |= SM(ATH10K_HW_TXRX_MGMT,
|
flags0 |= SM(ATH10K_HW_TXRX_MGMT,
|
||||||
HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
|
HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
|
||||||
|
flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT;
|
||||||
|
|
||||||
frags_paddr = skb_cb->paddr;
|
frags_paddr = skb_cb->paddr;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Normally all commands go through HTC which manages tx credits for
|
/* 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);
|
prefetch_len);
|
||||||
skb_cb->htt.txbuf->htc_hdr.flags = 0;
|
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_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)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID);
|
||||||
flags1 |= SM((u16)tid, HTT_DATA_TX_DESC_FLAGS1_EXT_TID);
|
flags1 |= SM((u16)tid, HTT_DATA_TX_DESC_FLAGS1_EXT_TID);
|
||||||
if (msdu->ip_summed == CHECKSUM_PARTIAL) {
|
if (msdu->ip_summed == CHECKSUM_PARTIAL) {
|
||||||
|
|
|
@ -2522,6 +2522,43 @@ static u8 ath10k_tx_h_get_vdev_id(struct ath10k *ar, struct ieee80211_vif *vif)
|
||||||
return 0;
|
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
|
/* HTT Tx uses Native Wifi tx mode which expects 802.11 frames without QoS
|
||||||
* Control in the header.
|
* 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);
|
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,
|
static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar,
|
||||||
struct ieee80211_vif *vif,
|
struct ieee80211_vif *vif,
|
||||||
struct sk_buff *skb)
|
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);
|
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;
|
int ret = 0;
|
||||||
|
|
||||||
if (ar->htt.target_version_major >= 3) {
|
spin_lock_bh(&ar->data_lock);
|
||||||
/* Since HTT 3.0 there is no separate mgmt tx command */
|
|
||||||
ret = ath10k_htt_tx(&ar->htt, skb);
|
if (skb_queue_len(q) == ATH10K_MAX_NUM_MGMT_PENDING) {
|
||||||
goto exit;
|
ath10k_warn(ar, "wmi mgmt tx queue is full\n");
|
||||||
|
ret = -ENOSPC;
|
||||||
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ieee80211_is_mgmt(hdr->frame_control)) {
|
__skb_queue_tail(q, skb);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
skb_queue_tail(&ar->wmi_mgmt_tx_queue, skb);
|
|
||||||
ieee80211_queue_work(ar->hw, &ar->wmi_mgmt_tx_work);
|
ieee80211_queue_work(ar->hw, &ar->wmi_mgmt_tx_work);
|
||||||
} else {
|
|
||||||
ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
|
unlock:
|
||||||
}
|
spin_unlock_bh(&ar->data_lock);
|
||||||
} else if (!test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
|
|
||||||
ar->fw_features) &&
|
return ret;
|
||||||
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
|
static void ath10k_mac_tx(struct ath10k *ar, struct sk_buff *skb)
|
||||||
* those frames when it detects link/beacon loss and depends
|
{
|
||||||
* on the tx status to be correct. */
|
struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
|
||||||
ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
|
struct ath10k_htt *htt = &ar->htt;
|
||||||
} else {
|
int ret = 0;
|
||||||
ret = ath10k_htt_tx(&ar->htt, skb);
|
|
||||||
|
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) {
|
if (ret) {
|
||||||
ath10k_warn(ar, "failed to transmit packet, dropping: %d\n",
|
ath10k_warn(ar, "failed to transmit packet, dropping: %d\n",
|
||||||
ret);
|
ret);
|
||||||
|
@ -2697,7 +2767,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
|
||||||
ar->offchan_tx_skb = skb;
|
ar->offchan_tx_skb = skb;
|
||||||
spin_unlock_bh(&ar->data_lock);
|
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,
|
ret = wait_for_completion_timeout(&ar->offchan_tx_completed,
|
||||||
3 * HZ);
|
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_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||||
struct ieee80211_vif *vif = info->control.vif;
|
struct ieee80211_vif *vif = info->control.vif;
|
||||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
||||||
|
__le16 fc = hdr->frame_control;
|
||||||
|
|
||||||
/* We should disable CCK RATE due to P2P */
|
/* We should disable CCK RATE due to P2P */
|
||||||
if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
|
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.freq = 0;
|
||||||
ATH10K_SKB_CB(skb)->htt.tid = ath10k_tx_h_get_tid(hdr);
|
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)->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 */
|
switch (ATH10K_SKB_CB(skb)->txmode) {
|
||||||
if (vif && vif->type != NL80211_IFTYPE_MONITOR) {
|
case ATH10K_HW_TXRX_MGMT:
|
||||||
|
case ATH10K_HW_TXRX_NATIVE_WIFI:
|
||||||
ath10k_tx_h_nwifi(hw, skb);
|
ath10k_tx_h_nwifi(hw, skb);
|
||||||
ath10k_tx_h_add_p2p_noa_ie(ar, vif, skb);
|
ath10k_tx_h_add_p2p_noa_ie(ar, vif, skb);
|
||||||
ath10k_tx_h_seq_no(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) {
|
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. */
|
/* Must not be called with conf_mutex held as workers can use that also. */
|
||||||
|
|
|
@ -28,6 +28,14 @@ struct ath10k_generic_iter {
|
||||||
int ret;
|
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);
|
struct ath10k *ath10k_mac_create(size_t priv_size);
|
||||||
void ath10k_mac_destroy(struct ath10k *ar);
|
void ath10k_mac_destroy(struct ath10k *ar);
|
||||||
int ath10k_mac_register(struct ath10k *ar);
|
int ath10k_mac_register(struct ath10k *ar);
|
||||||
|
|
Loading…
Reference in New Issue