ath10k: unify rx undecapping

This creates a single, common path for MSDU,
A-MSDU and fragmented Rx.

Hopefully this will make it easier to understand
Rx path and make it easier to work with.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
This commit is contained in:
Michal Kazior 2014-11-18 09:24:48 +02:00 committed by Kalle Valo
parent e0bd7513bb
commit 581c25f82f

View File

@ -601,35 +601,6 @@ static int ath10k_htt_rx_crypto_tail_len(struct ath10k *ar,
return 0; return 0;
} }
/* Applies for first msdu in chain, before altering it. */
static struct ieee80211_hdr *ath10k_htt_rx_skb_get_hdr(struct sk_buff *skb)
{
struct htt_rx_desc *rxd;
enum rx_msdu_decap_format fmt;
rxd = (void *)skb->data - sizeof(*rxd);
fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
RX_MSDU_START_INFO1_DECAP_FORMAT);
if (fmt == RX_MSDU_DECAP_RAW)
return (void *)skb->data;
return (void *)skb->data - RX_HTT_HDR_STATUS_LEN;
}
/* This function only applies for first msdu in an msdu chain */
static bool ath10k_htt_rx_hdr_is_amsdu(struct ieee80211_hdr *hdr)
{
u8 *qc;
if (ieee80211_is_data_qos(hdr->frame_control)) {
qc = ieee80211_get_qos_ctl(hdr);
if (qc[0] & 0x80)
return true;
}
return false;
}
struct rfc1042_hdr { struct rfc1042_hdr {
u8 llc_dsap; u8 llc_dsap;
u8 llc_ssap; u8 llc_ssap;
@ -755,41 +726,6 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar,
} }
} }
static void ath10k_htt_rx_h_protected(struct ath10k_htt *htt,
struct ieee80211_rx_status *rx_status,
struct sk_buff *skb,
enum htt_rx_mpdu_encrypt_type enctype,
enum rx_msdu_decap_format fmt,
bool dot11frag)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
rx_status->flag &= ~(RX_FLAG_DECRYPTED |
RX_FLAG_IV_STRIPPED |
RX_FLAG_MMIC_STRIPPED);
if (enctype == HTT_RX_MPDU_ENCRYPT_NONE)
return;
/*
* There's no explicit rx descriptor flag to indicate whether a given
* frame has been decrypted or not. We're forced to use the decap
* format as an implicit indication. However fragmentation rx is always
* raw and it probably never reports undecrypted raws.
*
* This makes sure sniffed frames are reported as-is without stripping
* the protected flag.
*/
if (fmt == RX_MSDU_DECAP_RAW && !dot11frag)
return;
rx_status->flag |= RX_FLAG_DECRYPTED |
RX_FLAG_IV_STRIPPED |
RX_FLAG_MMIC_STRIPPED;
hdr->frame_control = __cpu_to_le16(__le16_to_cpu(hdr->frame_control) &
~IEEE80211_FCTL_PROTECTED);
}
static bool ath10k_htt_rx_h_channel(struct ath10k *ar, static bool ath10k_htt_rx_h_channel(struct ath10k *ar,
struct ieee80211_rx_status *status) struct ieee80211_rx_status *status)
{ {
@ -886,178 +822,263 @@ static int ath10k_htt_rx_nwifi_hdrlen(struct ieee80211_hdr *hdr)
return round_up(ieee80211_hdrlen(hdr->frame_control), 4); return round_up(ieee80211_hdrlen(hdr->frame_control), 4);
} }
static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt, static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar,
struct ieee80211_rx_status *rx_status, struct sk_buff *msdu,
struct sk_buff_head *amsdu) struct ieee80211_rx_status *status,
enum htt_rx_mpdu_encrypt_type enctype,
bool is_decrypted)
{ {
struct ath10k *ar = htt->ar;
struct htt_rx_desc *rxd;
struct sk_buff *skb;
struct sk_buff *first;
enum rx_msdu_decap_format fmt;
enum htt_rx_mpdu_encrypt_type enctype;
struct ieee80211_hdr *hdr; struct ieee80211_hdr *hdr;
u8 hdr_buf[64], da[ETH_ALEN], sa[ETH_ALEN], *qos; struct htt_rx_desc *rxd;
unsigned int hdr_len; size_t hdr_len;
size_t crypto_len;
bool is_first;
bool is_last;
first = skb_peek(amsdu); rxd = (void *)msdu->data - sizeof(*rxd);
is_first = !!(rxd->msdu_end.info0 &
__cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU));
is_last = !!(rxd->msdu_end.info0 &
__cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU));
rxd = (void *)first->data - sizeof(*rxd); /* Delivered decapped frame:
enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0), * [802.11 header]
RX_MPDU_START_INFO0_ENCRYPT_TYPE); * [crypto param] <-- can be trimmed if !fcs_err &&
* !decrypt_err && !peer_idx_invalid
* [amsdu header] <-- only if A-MSDU
* [rfc1042/llc]
* [payload]
* [FCS] <-- at end, needs to be trimmed
*/
hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status; /* This probably shouldn't happen but warn just in case */
if (unlikely(WARN_ON_ONCE(!is_first)))
return;
/* This probably shouldn't happen but warn just in case */
if (unlikely(WARN_ON_ONCE(!(is_first && is_last))))
return;
skb_trim(msdu, msdu->len - FCS_LEN);
/* In most cases this will be true for sniffed frames. It makes sense
* to deliver them as-is without stripping the crypto param. This would
* also make sense for software based decryption (which is not
* implemented in ath10k).
*
* If there's no error then the frame is decrypted. At least that is
* the case for frames that come in via fragmented rx indication.
*/
if (!is_decrypted)
return;
/* The payload is decrypted so strip crypto params. Start from tail
* since hdr is used to compute some stuff.
*/
hdr = (void *)msdu->data;
/* Tail */
skb_trim(msdu, msdu->len - ath10k_htt_rx_crypto_tail_len(ar, enctype));
/* MMIC */
if (!ieee80211_has_morefrags(hdr->frame_control) &&
enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
skb_trim(msdu, msdu->len - 8);
/* Head */
hdr_len = ieee80211_hdrlen(hdr->frame_control); hdr_len = ieee80211_hdrlen(hdr->frame_control);
memcpy(hdr_buf, hdr, hdr_len); crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype);
hdr = (struct ieee80211_hdr *)hdr_buf;
while ((skb = __skb_dequeue(amsdu))) { memmove((void *)msdu->data + crypto_len,
void *decap_hdr; (void *)msdu->data, hdr_len);
int len; skb_pull(msdu, crypto_len);
rxd = (void *)skb->data - sizeof(*rxd);
fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
RX_MSDU_START_INFO1_DECAP_FORMAT);
decap_hdr = (void *)rxd->rx_hdr_status;
skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
/* First frame in an A-MSDU chain has more decapped data. */
if (skb == first) {
len = round_up(ieee80211_hdrlen(hdr->frame_control), 4);
len += round_up(ath10k_htt_rx_crypto_param_len(ar,
enctype), 4);
decap_hdr += len;
}
switch (fmt) {
case RX_MSDU_DECAP_RAW:
/* remove trailing FCS */
skb_trim(skb, skb->len - FCS_LEN);
break;
case RX_MSDU_DECAP_NATIVE_WIFI:
/* pull decapped header and copy SA & DA */
hdr = (struct ieee80211_hdr *)skb->data;
hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr);
ether_addr_copy(da, ieee80211_get_DA(hdr));
ether_addr_copy(sa, ieee80211_get_SA(hdr));
skb_pull(skb, hdr_len);
/* push original 802.11 header */
hdr = (struct ieee80211_hdr *)hdr_buf;
hdr_len = ieee80211_hdrlen(hdr->frame_control);
memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
/* original A-MSDU header has the bit set but we're
* not including A-MSDU subframe header */
hdr = (struct ieee80211_hdr *)skb->data;
qos = ieee80211_get_qos_ctl(hdr);
qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
/* original 802.11 header has a different DA and in
* case of 4addr it may also have different SA
*/
ether_addr_copy(ieee80211_get_DA(hdr), da);
ether_addr_copy(ieee80211_get_SA(hdr), sa);
break;
case RX_MSDU_DECAP_ETHERNET2_DIX:
/* strip ethernet header and insert decapped 802.11
* header, amsdu subframe header and rfc1042 header */
len = 0;
len += sizeof(struct rfc1042_hdr);
len += sizeof(struct amsdu_subframe_hdr);
skb_pull(skb, sizeof(struct ethhdr));
memcpy(skb_push(skb, len), decap_hdr, len);
memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
break;
case RX_MSDU_DECAP_8023_SNAP_LLC:
/* insert decapped 802.11 header making a singly
* A-MSDU */
memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
break;
}
ath10k_htt_rx_h_protected(htt, rx_status, skb, enctype, fmt,
false);
if (skb_queue_empty(amsdu))
rx_status->flag &= ~RX_FLAG_AMSDU_MORE;
else
rx_status->flag |= RX_FLAG_AMSDU_MORE;
ath10k_process_rx(htt->ar, rx_status, skb);
}
/* FIXME: It might be nice to re-assemble the A-MSDU when there's a
* monitor interface active for sniffing purposes. */
} }
static void ath10k_htt_rx_msdu(struct ath10k_htt *htt, static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar,
struct ieee80211_rx_status *rx_status, struct sk_buff *msdu,
struct sk_buff *skb) struct ieee80211_rx_status *status,
const u8 first_hdr[64])
{ {
struct ath10k *ar = htt->ar;
struct htt_rx_desc *rxd;
struct ieee80211_hdr *hdr; struct ieee80211_hdr *hdr;
enum rx_msdu_decap_format fmt; size_t hdr_len;
enum htt_rx_mpdu_encrypt_type enctype; u8 da[ETH_ALEN];
int hdr_len; u8 sa[ETH_ALEN];
void *rfc1042;
rxd = (void *)skb->data - sizeof(*rxd); /* Delivered decapped frame:
fmt = MS(__le32_to_cpu(rxd->msdu_start.info1), * [nwifi 802.11 header] <-- replaced with 802.11 hdr
RX_MSDU_START_INFO1_DECAP_FORMAT); * [rfc1042/llc]
enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0), *
RX_MPDU_START_INFO0_ENCRYPT_TYPE); * Note: The nwifi header doesn't have QoS Control and is
hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status; * (always?) a 3addr frame.
*
* Note2: There's no A-MSDU subframe header. Even if it's part
* of an A-MSDU.
*/
/* pull decapped header and copy SA & DA */
hdr = (struct ieee80211_hdr *)msdu->data;
hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr);
ether_addr_copy(da, ieee80211_get_DA(hdr));
ether_addr_copy(sa, ieee80211_get_SA(hdr));
skb_pull(msdu, hdr_len);
/* push original 802.11 header */
hdr = (struct ieee80211_hdr *)first_hdr;
hdr_len = ieee80211_hdrlen(hdr->frame_control); hdr_len = ieee80211_hdrlen(hdr->frame_control);
memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
skb->ip_summed = ath10k_htt_rx_get_csum_state(skb); /* original 802.11 header has a different DA and in
* case of 4addr it may also have different SA
*/
hdr = (struct ieee80211_hdr *)msdu->data;
ether_addr_copy(ieee80211_get_DA(hdr), da);
ether_addr_copy(ieee80211_get_SA(hdr), sa);
}
switch (fmt) { static void *ath10k_htt_rx_h_find_rfc1042(struct ath10k *ar,
case RX_MSDU_DECAP_RAW: struct sk_buff *msdu,
/* remove trailing FCS */ enum htt_rx_mpdu_encrypt_type enctype)
skb_trim(skb, skb->len - FCS_LEN); {
break; struct ieee80211_hdr *hdr;
case RX_MSDU_DECAP_NATIVE_WIFI: struct htt_rx_desc *rxd;
/* Pull decapped header */ size_t hdr_len, crypto_len;
hdr = (struct ieee80211_hdr *)skb->data; void *rfc1042;
hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr); bool is_first, is_last, is_amsdu;
skb_pull(skb, hdr_len);
/* Push original header */ rxd = (void *)msdu->data - sizeof(*rxd);
hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status; hdr = (void *)rxd->rx_hdr_status;
is_first = !!(rxd->msdu_end.info0 &
__cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU));
is_last = !!(rxd->msdu_end.info0 &
__cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU));
is_amsdu = !(is_first && is_last);
rfc1042 = hdr;
if (is_first) {
hdr_len = ieee80211_hdrlen(hdr->frame_control); hdr_len = ieee80211_hdrlen(hdr->frame_control);
memcpy(skb_push(skb, hdr_len), hdr, hdr_len); crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype);
break;
case RX_MSDU_DECAP_ETHERNET2_DIX:
/* strip ethernet header and insert decapped 802.11 header and
* rfc1042 header */
rfc1042 = hdr; rfc1042 += round_up(hdr_len, 4) +
rfc1042 += roundup(hdr_len, 4); round_up(crypto_len, 4);
rfc1042 += roundup(ath10k_htt_rx_crypto_param_len(ar,
enctype), 4);
skb_pull(skb, sizeof(struct ethhdr));
memcpy(skb_push(skb, sizeof(struct rfc1042_hdr)),
rfc1042, sizeof(struct rfc1042_hdr));
memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
break;
case RX_MSDU_DECAP_8023_SNAP_LLC:
/* remove A-MSDU subframe header and insert
* decapped 802.11 header. rfc1042 header is already there */
skb_pull(skb, sizeof(struct amsdu_subframe_hdr));
memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
break;
} }
ath10k_htt_rx_h_protected(htt, rx_status, skb, enctype, fmt, false); if (is_amsdu)
rfc1042 += sizeof(struct amsdu_subframe_hdr);
ath10k_process_rx(htt->ar, rx_status, skb); return rfc1042;
}
static void ath10k_htt_rx_h_undecap_eth(struct ath10k *ar,
struct sk_buff *msdu,
struct ieee80211_rx_status *status,
const u8 first_hdr[64],
enum htt_rx_mpdu_encrypt_type enctype)
{
struct ieee80211_hdr *hdr;
struct ethhdr *eth;
size_t hdr_len;
void *rfc1042;
u8 da[ETH_ALEN];
u8 sa[ETH_ALEN];
/* Delivered decapped frame:
* [eth header] <-- replaced with 802.11 hdr & rfc1042/llc
* [payload]
*/
rfc1042 = ath10k_htt_rx_h_find_rfc1042(ar, msdu, enctype);
if (WARN_ON_ONCE(!rfc1042))
return;
/* pull decapped header and copy SA & DA */
eth = (struct ethhdr *)msdu->data;
ether_addr_copy(da, eth->h_dest);
ether_addr_copy(sa, eth->h_source);
skb_pull(msdu, sizeof(struct ethhdr));
/* push rfc1042/llc/snap */
memcpy(skb_push(msdu, sizeof(struct rfc1042_hdr)), rfc1042,
sizeof(struct rfc1042_hdr));
/* push original 802.11 header */
hdr = (struct ieee80211_hdr *)first_hdr;
hdr_len = ieee80211_hdrlen(hdr->frame_control);
memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
/* original 802.11 header has a different DA and in
* case of 4addr it may also have different SA
*/
hdr = (struct ieee80211_hdr *)msdu->data;
ether_addr_copy(ieee80211_get_DA(hdr), da);
ether_addr_copy(ieee80211_get_SA(hdr), sa);
}
static void ath10k_htt_rx_h_undecap_snap(struct ath10k *ar,
struct sk_buff *msdu,
struct ieee80211_rx_status *status,
const u8 first_hdr[64])
{
struct ieee80211_hdr *hdr;
size_t hdr_len;
/* Delivered decapped frame:
* [amsdu header] <-- replaced with 802.11 hdr
* [rfc1042/llc]
* [payload]
*/
skb_pull(msdu, sizeof(struct amsdu_subframe_hdr));
hdr = (struct ieee80211_hdr *)first_hdr;
hdr_len = ieee80211_hdrlen(hdr->frame_control);
memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
}
static void ath10k_htt_rx_h_undecap(struct ath10k *ar,
struct sk_buff *msdu,
struct ieee80211_rx_status *status,
u8 first_hdr[64],
enum htt_rx_mpdu_encrypt_type enctype,
bool is_decrypted)
{
struct htt_rx_desc *rxd;
enum rx_msdu_decap_format decap;
struct ieee80211_hdr *hdr;
/* First msdu's decapped header:
* [802.11 header] <-- padded to 4 bytes long
* [crypto param] <-- padded to 4 bytes long
* [amsdu header] <-- only if A-MSDU
* [rfc1042/llc]
*
* Other (2nd, 3rd, ..) msdu's decapped header:
* [amsdu header] <-- only if A-MSDU
* [rfc1042/llc]
*/
rxd = (void *)msdu->data - sizeof(*rxd);
hdr = (void *)rxd->rx_hdr_status;
decap = MS(__le32_to_cpu(rxd->msdu_start.info1),
RX_MSDU_START_INFO1_DECAP_FORMAT);
switch (decap) {
case RX_MSDU_DECAP_RAW:
ath10k_htt_rx_h_undecap_raw(ar, msdu, status, enctype,
is_decrypted);
break;
case RX_MSDU_DECAP_NATIVE_WIFI:
ath10k_htt_rx_h_undecap_nwifi(ar, msdu, status, first_hdr);
break;
case RX_MSDU_DECAP_ETHERNET2_DIX:
ath10k_htt_rx_h_undecap_eth(ar, msdu, status, first_hdr, enctype);
break;
case RX_MSDU_DECAP_8023_SNAP_LLC:
ath10k_htt_rx_h_undecap_snap(ar, msdu, status, first_hdr);
break;
}
} }
static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb) static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb)
@ -1091,6 +1112,125 @@ static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb)
return CHECKSUM_UNNECESSARY; return CHECKSUM_UNNECESSARY;
} }
static void ath10k_htt_rx_h_csum_offload(struct sk_buff *msdu)
{
msdu->ip_summed = ath10k_htt_rx_get_csum_state(msdu);
}
static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
struct sk_buff_head *amsdu,
struct ieee80211_rx_status *status)
{
struct sk_buff *first;
struct sk_buff *last;
struct sk_buff *msdu;
struct htt_rx_desc *rxd;
struct ieee80211_hdr *hdr;
enum htt_rx_mpdu_encrypt_type enctype;
u8 first_hdr[64];
u8 *qos;
size_t hdr_len;
bool has_fcs_err;
bool has_crypto_err;
bool has_tkip_err;
bool has_peer_idx_invalid;
bool is_decrypted;
u32 attention;
if (skb_queue_empty(amsdu))
return;
first = skb_peek(amsdu);
rxd = (void *)first->data - sizeof(*rxd);
enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
RX_MPDU_START_INFO0_ENCRYPT_TYPE);
/* First MSDU's Rx descriptor in an A-MSDU contains full 802.11
* decapped header. It'll be used for undecapping of each MSDU.
*/
hdr = (void *)rxd->rx_hdr_status;
hdr_len = ieee80211_hdrlen(hdr->frame_control);
memcpy(first_hdr, hdr, hdr_len);
/* Each A-MSDU subframe will use the original header as the base and be
* reported as a separate MSDU so strip the A-MSDU bit from QoS Ctl.
*/
hdr = (void *)first_hdr;
qos = ieee80211_get_qos_ctl(hdr);
qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
/* Some attention flags are valid only in the last MSDU. */
last = skb_peek_tail(amsdu);
rxd = (void *)last->data - sizeof(*rxd);
attention = __le32_to_cpu(rxd->attention.flags);
has_fcs_err = !!(attention & RX_ATTENTION_FLAGS_FCS_ERR);
has_crypto_err = !!(attention & RX_ATTENTION_FLAGS_DECRYPT_ERR);
has_tkip_err = !!(attention & RX_ATTENTION_FLAGS_TKIP_MIC_ERR);
has_peer_idx_invalid = !!(attention & RX_ATTENTION_FLAGS_PEER_IDX_INVALID);
/* Note: If hardware captures an encrypted frame that it can't decrypt,
* e.g. due to fcs error, missing peer or invalid key data it will
* report the frame as raw.
*/
is_decrypted = (enctype != HTT_RX_MPDU_ENCRYPT_NONE &&
!has_fcs_err &&
!has_crypto_err &&
!has_peer_idx_invalid);
/* Clear per-MPDU flags while leaving per-PPDU flags intact. */
status->flag &= ~(RX_FLAG_FAILED_FCS_CRC |
RX_FLAG_MMIC_ERROR |
RX_FLAG_DECRYPTED |
RX_FLAG_IV_STRIPPED |
RX_FLAG_MMIC_STRIPPED);
if (has_fcs_err)
status->flag |= RX_FLAG_FAILED_FCS_CRC;
if (has_tkip_err)
status->flag |= RX_FLAG_MMIC_ERROR;
if (is_decrypted)
status->flag |= RX_FLAG_DECRYPTED |
RX_FLAG_IV_STRIPPED |
RX_FLAG_MMIC_STRIPPED;
skb_queue_walk(amsdu, msdu) {
ath10k_htt_rx_h_csum_offload(msdu);
ath10k_htt_rx_h_undecap(ar, msdu, status, first_hdr, enctype,
is_decrypted);
/* Undecapping involves copying the original 802.11 header back
* to sk_buff. If frame is protected and hardware has decrypted
* it then remove the protected bit.
*/
if (!is_decrypted)
continue;
hdr = (void *)msdu->data;
hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
}
}
static void ath10k_htt_rx_h_deliver(struct ath10k *ar,
struct sk_buff_head *amsdu,
struct ieee80211_rx_status *status)
{
struct sk_buff *msdu;
while ((msdu = __skb_dequeue(amsdu))) {
/* Setup per-MSDU flags */
if (skb_queue_empty(amsdu))
status->flag &= ~RX_FLAG_AMSDU_MORE;
else
status->flag |= RX_FLAG_AMSDU_MORE;
ath10k_process_rx(ar, status, msdu);
}
}
static int ath10k_unchain_msdu(struct sk_buff_head *amsdu) static int ath10k_unchain_msdu(struct sk_buff_head *amsdu)
{ {
struct sk_buff *skb, *first; struct sk_buff *skb, *first;
@ -1134,45 +1274,86 @@ static int ath10k_unchain_msdu(struct sk_buff_head *amsdu)
return 0; return 0;
} }
static bool ath10k_htt_rx_amsdu_allowed(struct ath10k_htt *htt, static void ath10k_htt_rx_h_unchain(struct ath10k *ar,
struct sk_buff *head, struct sk_buff_head *amsdu,
bool channel_set, bool chained)
u32 attention)
{ {
struct ath10k *ar = htt->ar; struct sk_buff *first;
struct htt_rx_desc *rxd;
enum rx_msdu_decap_format decap;
if (head->len == 0) { first = skb_peek(amsdu);
ath10k_dbg(ar, ATH10K_DBG_HTT, rxd = (void *)first->data - sizeof(*rxd);
"htt rx dropping due to zero-len\n"); decap = MS(__le32_to_cpu(rxd->msdu_start.info1),
RX_MSDU_START_INFO1_DECAP_FORMAT);
if (!chained)
return;
/* FIXME: Current unchaining logic can only handle simple case of raw
* msdu chaining. If decapping is other than raw the chaining may be
* more complex and this isn't handled by the current code. Don't even
* try re-constructing such frames - it'll be pretty much garbage.
*/
if (decap != RX_MSDU_DECAP_RAW ||
skb_queue_len(amsdu) != 1 + rxd->frag_info.ring2_more_count) {
__skb_queue_purge(amsdu);
return;
}
ath10k_unchain_msdu(amsdu);
}
static bool ath10k_htt_rx_amsdu_allowed(struct ath10k *ar,
struct sk_buff_head *amsdu,
struct ieee80211_rx_status *rx_status)
{
struct sk_buff *msdu;
struct htt_rx_desc *rxd;
msdu = skb_peek(amsdu);
rxd = (void *)msdu->data - sizeof(*rxd);
/* FIXME: It might be a good idea to do some fuzzy-testing to drop
* invalid/dangerous frames.
*/
if (!rx_status->freq) {
ath10k_warn(ar, "no channel configured; ignoring frame(s)!\n");
return false; return false;
} }
if (attention & RX_ATTENTION_FLAGS_DECRYPT_ERR) { /* Management frames are handled via WMI events. The pros of such
ath10k_dbg(ar, ATH10K_DBG_HTT, * approach is that channel is explicitly provided in WMI events
"htt rx dropping due to decrypt-err\n"); * whereas HTT doesn't provide channel information for Rxed frames.
return false; */
} if (rxd->attention.flags &
__cpu_to_le32(RX_ATTENTION_FLAGS_MGMT_TYPE)) {
if (!channel_set) {
ath10k_warn(ar, "no channel configured; ignoring frame!\n");
return false;
}
/* Skip mgmt frames while we handle this in WMI */
if (attention & RX_ATTENTION_FLAGS_MGMT_TYPE) {
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx mgmt ctrl\n"); ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx mgmt ctrl\n");
return false; return false;
} }
if (test_bit(ATH10K_CAC_RUNNING, &htt->ar->dev_flags)) { if (test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)) {
ath10k_dbg(ar, ATH10K_DBG_HTT, ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx cac running\n");
"htt rx CAC running\n");
return false; return false;
} }
return true; return true;
} }
static void ath10k_htt_rx_h_filter(struct ath10k *ar,
struct sk_buff_head *amsdu,
struct ieee80211_rx_status *rx_status)
{
if (skb_queue_empty(amsdu))
return;
if (ath10k_htt_rx_amsdu_allowed(ar, amsdu, rx_status))
return;
__skb_queue_purge(amsdu);
}
static void ath10k_htt_rx_handler(struct ath10k_htt *htt, static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
struct htt_rx_indication *rx) struct htt_rx_indication *rx)
{ {
@ -1180,7 +1361,6 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
struct ieee80211_rx_status *rx_status = &htt->rx_status; struct ieee80211_rx_status *rx_status = &htt->rx_status;
struct htt_rx_indication_mpdu_range *mpdu_ranges; struct htt_rx_indication_mpdu_range *mpdu_ranges;
struct sk_buff_head amsdu; struct sk_buff_head amsdu;
struct ieee80211_hdr *hdr;
int num_mpdu_ranges; int num_mpdu_ranges;
u32 attention; u32 attention;
int fw_desc_len; int fw_desc_len;
@ -1247,34 +1427,10 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
break; break;
} }
if (!ath10k_htt_rx_amsdu_allowed(htt, skb_peek(&amsdu), ath10k_htt_rx_h_unchain(ar, &amsdu, ret > 0);
channel_set, attention)) { ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
__skb_queue_purge(&amsdu); ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
continue; ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
}
if (ret > 0 && ath10k_unchain_msdu(&amsdu) < 0) {
__skb_queue_purge(&amsdu);
continue;
}
if (attention & RX_ATTENTION_FLAGS_FCS_ERR)
rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
else
rx_status->flag &= ~RX_FLAG_FAILED_FCS_CRC;
if (attention & RX_ATTENTION_FLAGS_TKIP_MIC_ERR)
rx_status->flag |= RX_FLAG_MMIC_ERROR;
else
rx_status->flag &= ~RX_FLAG_MMIC_ERROR;
hdr = ath10k_htt_rx_skb_get_hdr(skb_peek(&amsdu));
if (ath10k_htt_rx_hdr_is_amsdu(hdr))
ath10k_htt_rx_amsdu(htt, rx_status, &amsdu);
else
ath10k_htt_rx_msdu(htt, rx_status,
__skb_dequeue(&amsdu));
} }
tasklet_schedule(&htt->rx_replenish_task); tasklet_schedule(&htt->rx_replenish_task);
@ -1284,19 +1440,11 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
struct htt_rx_fragment_indication *frag) struct htt_rx_fragment_indication *frag)
{ {
struct ath10k *ar = htt->ar; struct ath10k *ar = htt->ar;
struct sk_buff *msdu;
enum htt_rx_mpdu_encrypt_type enctype;
struct htt_rx_desc *rxd;
enum rx_msdu_decap_format fmt;
struct ieee80211_rx_status *rx_status = &htt->rx_status; struct ieee80211_rx_status *rx_status = &htt->rx_status;
struct ieee80211_hdr *hdr;
struct sk_buff_head amsdu; struct sk_buff_head amsdu;
int ret; int ret;
bool tkip_mic_err;
bool decrypt_err;
u8 *fw_desc; u8 *fw_desc;
int fw_desc_len, hdrlen, paramlen; int fw_desc_len;
int trim;
u32 attention = 0; u32 attention = 0;
fw_desc_len = __le16_to_cpu(frag->fw_rx_desc_bytes); fw_desc_len = __le16_to_cpu(frag->fw_rx_desc_bytes);
@ -1326,75 +1474,13 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
return; return;
} }
msdu = __skb_dequeue(&amsdu);
/* FIXME: implement signal strength */ /* FIXME: implement signal strength */
rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
hdr = (struct ieee80211_hdr *)msdu->data; ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
rxd = (void *)msdu->data - sizeof(*rxd); ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
tkip_mic_err = !!(attention & RX_ATTENTION_FLAGS_TKIP_MIC_ERR); ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
decrypt_err = !!(attention & RX_ATTENTION_FLAGS_DECRYPT_ERR);
fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
RX_MSDU_START_INFO1_DECAP_FORMAT);
if (fmt != RX_MSDU_DECAP_RAW) {
ath10k_warn(ar, "we dont support non-raw fragmented rx yet\n");
dev_kfree_skb_any(msdu);
goto end;
}
enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
RX_MPDU_START_INFO0_ENCRYPT_TYPE);
ath10k_htt_rx_h_protected(htt, rx_status, msdu, enctype, fmt,
true);
msdu->ip_summed = ath10k_htt_rx_get_csum_state(msdu);
if (tkip_mic_err)
ath10k_warn(ar, "tkip mic error\n");
if (decrypt_err) {
ath10k_warn(ar, "decryption err in fragmented rx\n");
dev_kfree_skb_any(msdu);
goto end;
}
if (enctype != HTT_RX_MPDU_ENCRYPT_NONE) {
hdrlen = ieee80211_hdrlen(hdr->frame_control);
paramlen = ath10k_htt_rx_crypto_param_len(ar, enctype);
/* It is more efficient to move the header than the payload */
memmove((void *)msdu->data + paramlen,
(void *)msdu->data,
hdrlen);
skb_pull(msdu, paramlen);
hdr = (struct ieee80211_hdr *)msdu->data;
}
/* remove trailing FCS */
trim = 4;
/* remove crypto trailer */
trim += ath10k_htt_rx_crypto_tail_len(ar, enctype);
/* last fragment of TKIP frags has MIC */
if (!ieee80211_has_morefrags(hdr->frame_control) &&
enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
trim += MICHAEL_MIC_LEN;
if (trim > msdu->len) {
ath10k_warn(ar, "htt rx fragment: trailer longer than the frame itself? drop\n");
dev_kfree_skb_any(msdu);
goto end;
}
skb_trim(msdu, msdu->len - trim);
ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx frag mpdu: ",
msdu->data, msdu->len);
ath10k_process_rx(htt->ar, rx_status, msdu);
end:
if (fw_desc_len > 0) { if (fw_desc_len > 0) {
ath10k_dbg(ar, ATH10K_DBG_HTT, ath10k_dbg(ar, ATH10K_DBG_HTT,
"expecting more fragmented rx in one indication %d\n", "expecting more fragmented rx in one indication %d\n",