162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2004, Instant802 Networks, Inc.
462306a36Sopenharmony_ci * Copyright 2013-2014  Intel Mobile Communications GmbH
562306a36Sopenharmony_ci * Copyright (C) 2022 Intel Corporation
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/netdevice.h>
962306a36Sopenharmony_ci#include <linux/skbuff.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/if_arp.h>
1262306a36Sopenharmony_ci#include <linux/types.h>
1362306a36Sopenharmony_ci#include <net/ip.h>
1462306a36Sopenharmony_ci#include <net/pkt_sched.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <net/mac80211.h>
1762306a36Sopenharmony_ci#include "ieee80211_i.h"
1862306a36Sopenharmony_ci#include "wme.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* Default mapping in classifier to work with default
2162306a36Sopenharmony_ci * queue setup.
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_ciconst int ieee802_1d_to_ac[8] = {
2462306a36Sopenharmony_ci	IEEE80211_AC_BE,
2562306a36Sopenharmony_ci	IEEE80211_AC_BK,
2662306a36Sopenharmony_ci	IEEE80211_AC_BK,
2762306a36Sopenharmony_ci	IEEE80211_AC_BE,
2862306a36Sopenharmony_ci	IEEE80211_AC_VI,
2962306a36Sopenharmony_ci	IEEE80211_AC_VI,
3062306a36Sopenharmony_ci	IEEE80211_AC_VO,
3162306a36Sopenharmony_ci	IEEE80211_AC_VO
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic int wme_downgrade_ac(struct sk_buff *skb)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	switch (skb->priority) {
3762306a36Sopenharmony_ci	case 6:
3862306a36Sopenharmony_ci	case 7:
3962306a36Sopenharmony_ci		skb->priority = 5; /* VO -> VI */
4062306a36Sopenharmony_ci		return 0;
4162306a36Sopenharmony_ci	case 4:
4262306a36Sopenharmony_ci	case 5:
4362306a36Sopenharmony_ci		skb->priority = 3; /* VI -> BE */
4462306a36Sopenharmony_ci		return 0;
4562306a36Sopenharmony_ci	case 0:
4662306a36Sopenharmony_ci	case 3:
4762306a36Sopenharmony_ci		skb->priority = 2; /* BE -> BK */
4862306a36Sopenharmony_ci		return 0;
4962306a36Sopenharmony_ci	default:
5062306a36Sopenharmony_ci		return -1;
5162306a36Sopenharmony_ci	}
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/**
5562306a36Sopenharmony_ci * ieee80211_fix_reserved_tid - return the TID to use if this one is reserved
5662306a36Sopenharmony_ci * @tid: the assumed-reserved TID
5762306a36Sopenharmony_ci *
5862306a36Sopenharmony_ci * Returns: the alternative TID to use, or 0 on error
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_cistatic inline u8 ieee80211_fix_reserved_tid(u8 tid)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	switch (tid) {
6362306a36Sopenharmony_ci	case 0:
6462306a36Sopenharmony_ci		return 3;
6562306a36Sopenharmony_ci	case 1:
6662306a36Sopenharmony_ci		return 2;
6762306a36Sopenharmony_ci	case 2:
6862306a36Sopenharmony_ci		return 1;
6962306a36Sopenharmony_ci	case 3:
7062306a36Sopenharmony_ci		return 0;
7162306a36Sopenharmony_ci	case 4:
7262306a36Sopenharmony_ci		return 5;
7362306a36Sopenharmony_ci	case 5:
7462306a36Sopenharmony_ci		return 4;
7562306a36Sopenharmony_ci	case 6:
7662306a36Sopenharmony_ci		return 7;
7762306a36Sopenharmony_ci	case 7:
7862306a36Sopenharmony_ci		return 6;
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	return 0;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic u16 ieee80211_downgrade_queue(struct ieee80211_sub_if_data *sdata,
8562306a36Sopenharmony_ci				     struct sta_info *sta, struct sk_buff *skb)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/* in case we are a client verify acm is not set for this ac */
9062306a36Sopenharmony_ci	while (sdata->wmm_acm & BIT(skb->priority)) {
9162306a36Sopenharmony_ci		int ac = ieee802_1d_to_ac[skb->priority];
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci		if (ifmgd->tx_tspec[ac].admitted_time &&
9462306a36Sopenharmony_ci		    skb->priority == ifmgd->tx_tspec[ac].up)
9562306a36Sopenharmony_ci			return ac;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci		if (wme_downgrade_ac(skb)) {
9862306a36Sopenharmony_ci			/*
9962306a36Sopenharmony_ci			 * This should not really happen. The AP has marked all
10062306a36Sopenharmony_ci			 * lower ACs to require admission control which is not
10162306a36Sopenharmony_ci			 * a reasonable configuration. Allow the frame to be
10262306a36Sopenharmony_ci			 * transmitted using AC_BK as a workaround.
10362306a36Sopenharmony_ci			 */
10462306a36Sopenharmony_ci			break;
10562306a36Sopenharmony_ci		}
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	/* Check to see if this is a reserved TID */
10962306a36Sopenharmony_ci	if (sta && sta->reserved_tid == skb->priority)
11062306a36Sopenharmony_ci		skb->priority = ieee80211_fix_reserved_tid(skb->priority);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	/* look up which queue to use for frames with this 1d tag */
11362306a36Sopenharmony_ci	return ieee802_1d_to_ac[skb->priority];
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci/* Indicate which queue to use for this fully formed 802.11 frame */
11762306a36Sopenharmony_ciu16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata,
11862306a36Sopenharmony_ci				 struct sk_buff *skb,
11962306a36Sopenharmony_ci				 struct ieee80211_hdr *hdr)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
12262306a36Sopenharmony_ci	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
12362306a36Sopenharmony_ci	u8 *p;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/* Ensure hash is set prior to potential SW encryption */
12662306a36Sopenharmony_ci	skb_get_hash(skb);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if ((info->control.flags & IEEE80211_TX_CTRL_DONT_REORDER) ||
12962306a36Sopenharmony_ci	    local->hw.queues < IEEE80211_NUM_ACS)
13062306a36Sopenharmony_ci		return 0;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (!ieee80211_is_data(hdr->frame_control)) {
13362306a36Sopenharmony_ci		skb->priority = 7;
13462306a36Sopenharmony_ci		return ieee802_1d_to_ac[skb->priority];
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci	if (!ieee80211_is_data_qos(hdr->frame_control)) {
13762306a36Sopenharmony_ci		skb->priority = 0;
13862306a36Sopenharmony_ci		return ieee802_1d_to_ac[skb->priority];
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	p = ieee80211_get_qos_ctl(hdr);
14262306a36Sopenharmony_ci	skb->priority = *p & IEEE80211_QOS_CTL_TAG1D_MASK;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	return ieee80211_downgrade_queue(sdata, NULL, skb);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ciu16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
14862306a36Sopenharmony_ci			   struct sta_info *sta, struct sk_buff *skb)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	const struct ethhdr *eth = (void *)skb->data;
15162306a36Sopenharmony_ci	struct mac80211_qos_map *qos_map;
15262306a36Sopenharmony_ci	bool qos;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	/* Ensure hash is set prior to potential SW encryption */
15562306a36Sopenharmony_ci	skb_get_hash(skb);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	/* all mesh/ocb stations are required to support WME */
15862306a36Sopenharmony_ci	if ((sdata->vif.type == NL80211_IFTYPE_MESH_POINT &&
15962306a36Sopenharmony_ci	    !is_multicast_ether_addr(eth->h_dest)) ||
16062306a36Sopenharmony_ci	    (sdata->vif.type == NL80211_IFTYPE_OCB && sta))
16162306a36Sopenharmony_ci		qos = true;
16262306a36Sopenharmony_ci	else if (sta)
16362306a36Sopenharmony_ci		qos = sta->sta.wme;
16462306a36Sopenharmony_ci	else
16562306a36Sopenharmony_ci		qos = false;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (!qos) {
16862306a36Sopenharmony_ci		skb->priority = 0; /* required for correct WPA/11i MIC */
16962306a36Sopenharmony_ci		return IEEE80211_AC_BE;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (skb->protocol == sdata->control_port_protocol) {
17362306a36Sopenharmony_ci		skb->priority = 7;
17462306a36Sopenharmony_ci		goto downgrade;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	/* use the data classifier to determine what 802.1d tag the
17862306a36Sopenharmony_ci	 * data frame has */
17962306a36Sopenharmony_ci	qos_map = rcu_dereference(sdata->qos_map);
18062306a36Sopenharmony_ci	skb->priority = cfg80211_classify8021d(skb, qos_map ?
18162306a36Sopenharmony_ci					       &qos_map->qos_map : NULL);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci downgrade:
18462306a36Sopenharmony_ci	return ieee80211_downgrade_queue(sdata, sta, skb);
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci/**
18862306a36Sopenharmony_ci * ieee80211_set_qos_hdr - Fill in the QoS header if there is one.
18962306a36Sopenharmony_ci *
19062306a36Sopenharmony_ci * @sdata: local subif
19162306a36Sopenharmony_ci * @skb: packet to be updated
19262306a36Sopenharmony_ci */
19362306a36Sopenharmony_civoid ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata,
19462306a36Sopenharmony_ci			   struct sk_buff *skb)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	struct ieee80211_hdr *hdr = (void *)skb->data;
19762306a36Sopenharmony_ci	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
19862306a36Sopenharmony_ci	u8 tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
19962306a36Sopenharmony_ci	u8 flags;
20062306a36Sopenharmony_ci	u8 *p;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (!ieee80211_is_data_qos(hdr->frame_control))
20362306a36Sopenharmony_ci		return;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	p = ieee80211_get_qos_ctl(hdr);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	/* don't overwrite the QoS field of injected frames */
20862306a36Sopenharmony_ci	if (info->flags & IEEE80211_TX_CTL_INJECTED) {
20962306a36Sopenharmony_ci		/* do take into account Ack policy of injected frames */
21062306a36Sopenharmony_ci		if (*p & IEEE80211_QOS_CTL_ACK_POLICY_NOACK)
21162306a36Sopenharmony_ci			info->flags |= IEEE80211_TX_CTL_NO_ACK;
21262306a36Sopenharmony_ci		return;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* set up the first byte */
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/*
21862306a36Sopenharmony_ci	 * preserve everything but the TID and ACK policy
21962306a36Sopenharmony_ci	 * (which we both write here)
22062306a36Sopenharmony_ci	 */
22162306a36Sopenharmony_ci	flags = *p & ~(IEEE80211_QOS_CTL_TID_MASK |
22262306a36Sopenharmony_ci		       IEEE80211_QOS_CTL_ACK_POLICY_MASK);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	if (is_multicast_ether_addr(hdr->addr1) ||
22562306a36Sopenharmony_ci	    sdata->noack_map & BIT(tid)) {
22662306a36Sopenharmony_ci		flags |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK;
22762306a36Sopenharmony_ci		info->flags |= IEEE80211_TX_CTL_NO_ACK;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	*p = flags | tid;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	/* set up the second byte */
23362306a36Sopenharmony_ci	p++;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (ieee80211_vif_is_mesh(&sdata->vif)) {
23662306a36Sopenharmony_ci		/* preserve RSPI and Mesh PS Level bit */
23762306a36Sopenharmony_ci		*p &= ((IEEE80211_QOS_CTL_RSPI |
23862306a36Sopenharmony_ci			IEEE80211_QOS_CTL_MESH_PS_LEVEL) >> 8);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci		/* Nulls don't have a mesh header (frame body) */
24162306a36Sopenharmony_ci		if (!ieee80211_is_qos_nullfunc(hdr->frame_control))
24262306a36Sopenharmony_ci			*p |= (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8);
24362306a36Sopenharmony_ci	} else {
24462306a36Sopenharmony_ci		*p = 0;
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci}
247