wifi: mac80211: implement support for yet another mesh A-MSDU format
MT7996 hardware supports mesh A-MSDU subframes in hardware, but uses a big-endian length field Signed-off-by: Felix Fietkau <nbd@nbd.name> Signed-off-by: Ryder Lee <ryder.lee@mediatek.com> Link: https://lore.kernel.org/r/20230314095956.62085-7-nbd@nbd.name Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
3468e1e0c6
commit
fe4a6d2db3
4 changed files with 50 additions and 24 deletions
|
@ -6274,10 +6274,13 @@ static inline int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
|
||||||
* mesh control field.
|
* mesh control field.
|
||||||
*
|
*
|
||||||
* @skb: The input A-MSDU frame without any headers.
|
* @skb: The input A-MSDU frame without any headers.
|
||||||
* @mesh_hdr: use standard compliant mesh A-MSDU subframe header
|
* @mesh_hdr: the type of mesh header to test
|
||||||
|
* 0: non-mesh A-MSDU length field
|
||||||
|
* 1: big-endian mesh A-MSDU length field
|
||||||
|
* 2: little-endian mesh A-MSDU length field
|
||||||
* Returns: true if subframe header lengths are valid for the @mesh_hdr mode
|
* Returns: true if subframe header lengths are valid for the @mesh_hdr mode
|
||||||
*/
|
*/
|
||||||
bool ieee80211_is_valid_amsdu(struct sk_buff *skb, bool mesh_hdr);
|
bool ieee80211_is_valid_amsdu(struct sk_buff *skb, u8 mesh_hdr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ieee80211_amsdu_to_8023s - decode an IEEE 802.11n A-MSDU frame
|
* ieee80211_amsdu_to_8023s - decode an IEEE 802.11n A-MSDU frame
|
||||||
|
@ -6294,13 +6297,13 @@ bool ieee80211_is_valid_amsdu(struct sk_buff *skb, bool mesh_hdr);
|
||||||
* @extra_headroom: The hardware extra headroom for SKBs in the @list.
|
* @extra_headroom: The hardware extra headroom for SKBs in the @list.
|
||||||
* @check_da: DA to check in the inner ethernet header, or NULL
|
* @check_da: DA to check in the inner ethernet header, or NULL
|
||||||
* @check_sa: SA to check in the inner ethernet header, or NULL
|
* @check_sa: SA to check in the inner ethernet header, or NULL
|
||||||
* @mesh_control: A-MSDU subframe header includes the mesh control field
|
* @mesh_control: see mesh_hdr in ieee80211_is_valid_amsdu
|
||||||
*/
|
*/
|
||||||
void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
|
void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
|
||||||
const u8 *addr, enum nl80211_iftype iftype,
|
const u8 *addr, enum nl80211_iftype iftype,
|
||||||
const unsigned int extra_headroom,
|
const unsigned int extra_headroom,
|
||||||
const u8 *check_da, const u8 *check_sa,
|
const u8 *check_da, const u8 *check_sa,
|
||||||
bool mesh_control);
|
u8 mesh_control);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ieee80211_get_8023_tunnel_proto - get RFC1042 or bridge tunnel encap protocol
|
* ieee80211_get_8023_tunnel_proto - get RFC1042 or bridge tunnel encap protocol
|
||||||
|
|
|
@ -2983,13 +2983,23 @@ __ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx, u8 data_offset)
|
||||||
return RX_DROP_UNUSABLE;
|
return RX_DROP_UNUSABLE;
|
||||||
|
|
||||||
if (rx->sta && rx->sta->amsdu_mesh_control < 0) {
|
if (rx->sta && rx->sta->amsdu_mesh_control < 0) {
|
||||||
bool valid_std = ieee80211_is_valid_amsdu(skb, true);
|
s8 valid = -1;
|
||||||
bool valid_nonstd = ieee80211_is_valid_amsdu(skb, false);
|
int i;
|
||||||
|
|
||||||
if (valid_std && !valid_nonstd)
|
for (i = 0; i <= 2; i++) {
|
||||||
rx->sta->amsdu_mesh_control = 1;
|
if (!ieee80211_is_valid_amsdu(skb, i))
|
||||||
else if (valid_nonstd && !valid_std)
|
continue;
|
||||||
rx->sta->amsdu_mesh_control = 0;
|
|
||||||
|
if (valid >= 0) {
|
||||||
|
/* ambiguous */
|
||||||
|
valid = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
valid = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
rx->sta->amsdu_mesh_control = valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr,
|
ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr,
|
||||||
|
|
|
@ -623,7 +623,10 @@ struct link_sta_info {
|
||||||
* @cparams: CoDel parameters for this station.
|
* @cparams: CoDel parameters for this station.
|
||||||
* @reserved_tid: reserved TID (if any, otherwise IEEE80211_TID_UNRESERVED)
|
* @reserved_tid: reserved TID (if any, otherwise IEEE80211_TID_UNRESERVED)
|
||||||
* @amsdu_mesh_control: track the mesh A-MSDU format used by the peer
|
* @amsdu_mesh_control: track the mesh A-MSDU format used by the peer
|
||||||
* (-1: not yet known, 0: non-standard [without mesh header], 1: standard)
|
* (-1: not yet known,
|
||||||
|
* 0: non-mesh A-MSDU length field
|
||||||
|
* 1: big-endian mesh A-MSDU length field
|
||||||
|
* 2: little-endian mesh A-MSDU length field)
|
||||||
* @fast_tx: TX fastpath information
|
* @fast_tx: TX fastpath information
|
||||||
* @fast_rx: RX fastpath information
|
* @fast_rx: RX fastpath information
|
||||||
* @tdls_chandef: a TDLS peer can have a wider chandef that is compatible to
|
* @tdls_chandef: a TDLS peer can have a wider chandef that is compatible to
|
||||||
|
|
|
@ -776,7 +776,24 @@ __ieee80211_amsdu_copy(struct sk_buff *skb, unsigned int hlen,
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ieee80211_is_valid_amsdu(struct sk_buff *skb, bool mesh_hdr)
|
static u16
|
||||||
|
ieee80211_amsdu_subframe_length(void *field, u8 mesh_flags, u8 hdr_type)
|
||||||
|
{
|
||||||
|
__le16 *field_le = field;
|
||||||
|
__be16 *field_be = field;
|
||||||
|
u16 len;
|
||||||
|
|
||||||
|
if (hdr_type >= 2)
|
||||||
|
len = le16_to_cpu(*field_le);
|
||||||
|
else
|
||||||
|
len = be16_to_cpu(*field_be);
|
||||||
|
if (hdr_type)
|
||||||
|
len += __ieee80211_get_mesh_hdrlen(mesh_flags);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ieee80211_is_valid_amsdu(struct sk_buff *skb, u8 mesh_hdr)
|
||||||
{
|
{
|
||||||
int offset = 0, remaining, subframe_len, padding;
|
int offset = 0, remaining, subframe_len, padding;
|
||||||
|
|
||||||
|
@ -790,12 +807,8 @@ bool ieee80211_is_valid_amsdu(struct sk_buff *skb, bool mesh_hdr)
|
||||||
if (skb_copy_bits(skb, offset + 2 * ETH_ALEN, &hdr, sizeof(hdr)) < 0)
|
if (skb_copy_bits(skb, offset + 2 * ETH_ALEN, &hdr, sizeof(hdr)) < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (mesh_hdr)
|
len = ieee80211_amsdu_subframe_length(&hdr.len, hdr.mesh_flags,
|
||||||
len = le16_to_cpu(*(__le16 *)&hdr.len) +
|
mesh_hdr);
|
||||||
__ieee80211_get_mesh_hdrlen(hdr.mesh_flags);
|
|
||||||
else
|
|
||||||
len = ntohs(hdr.len);
|
|
||||||
|
|
||||||
subframe_len = sizeof(struct ethhdr) + len;
|
subframe_len = sizeof(struct ethhdr) + len;
|
||||||
padding = (4 - subframe_len) & 0x3;
|
padding = (4 - subframe_len) & 0x3;
|
||||||
remaining = skb->len - offset;
|
remaining = skb->len - offset;
|
||||||
|
@ -812,7 +825,7 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
|
||||||
const u8 *addr, enum nl80211_iftype iftype,
|
const u8 *addr, enum nl80211_iftype iftype,
|
||||||
const unsigned int extra_headroom,
|
const unsigned int extra_headroom,
|
||||||
const u8 *check_da, const u8 *check_sa,
|
const u8 *check_da, const u8 *check_sa,
|
||||||
bool mesh_control)
|
u8 mesh_control)
|
||||||
{
|
{
|
||||||
unsigned int hlen = ALIGN(extra_headroom, 4);
|
unsigned int hlen = ALIGN(extra_headroom, 4);
|
||||||
struct sk_buff *frame = NULL;
|
struct sk_buff *frame = NULL;
|
||||||
|
@ -837,11 +850,8 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
|
||||||
skb_copy_bits(skb, offset, &hdr, copy_len);
|
skb_copy_bits(skb, offset, &hdr, copy_len);
|
||||||
if (iftype == NL80211_IFTYPE_MESH_POINT)
|
if (iftype == NL80211_IFTYPE_MESH_POINT)
|
||||||
mesh_len = __ieee80211_get_mesh_hdrlen(hdr.flags);
|
mesh_len = __ieee80211_get_mesh_hdrlen(hdr.flags);
|
||||||
if (mesh_control)
|
len = ieee80211_amsdu_subframe_length(&hdr.eth.h_proto, hdr.flags,
|
||||||
len = le16_to_cpu(*(__le16 *)&hdr.eth.h_proto) + mesh_len;
|
mesh_control);
|
||||||
else
|
|
||||||
len = ntohs(hdr.eth.h_proto);
|
|
||||||
|
|
||||||
subframe_len = sizeof(struct ethhdr) + len;
|
subframe_len = sizeof(struct ethhdr) + len;
|
||||||
padding = (4 - subframe_len) & 0x3;
|
padding = (4 - subframe_len) & 0x3;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue