brcmfmac: map 802.1d priority to precedence level based on AP WMM params
authorSaravanan Shanmugham <saravanan.shanmugham@cypress.com>
Tue, 5 May 2020 06:51:26 +0000 (01:51 -0500)
committerKalle Valo <kvalo@codeaurora.org>
Tue, 12 May 2020 08:50:40 +0000 (11:50 +0300)
In WLAN, priority among various access categories of traffic is
always set by the AP using WMM parameters and this may not always
follow the standard 802.1d priority.

In this change, priority is adjusted based on the AP WMM params
received as part of the Assoc Response and the same is later used
to map the priority of all incoming traffic.

In a specific scenario where EDCA parameters are configured to be same
for all ACs, use the default FW priority definition to avoid queuing
packets of all ACs to the same priority queue.

This change fixes the following 802.11 certification tests:
* 11n - 5.2.31 ACM Bit Conformance test
* 11n - 5.2.32 AC Parameter Modification test
* 11ac - 5.2.33 TXOP Limit test

Signed-off-by: Saravanan Shanmugham <saravanan.shanmugham@cypress.com>
Signed-off-by: Justin Li <justin.li@cypress.com>
Signed-off-by: Madhan Mohan R <madhanmohan.r@cypress.com>
Signed-off-by: Chi-hsien Lin <chi-hsien.lin@cypress.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/1588661487-21884-2-git-send-email-chi-hsien.lin@cypress.com
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c

index f2f84af..537e5ae 100644 (file)
@@ -23,6 +23,7 @@
 #include "p2p.h"
 #include "btcoex.h"
 #include "pno.h"
+#include "fwsignal.h"
 #include "cfg80211.h"
 #include "feature.h"
 #include "fwil.h"
@@ -5586,12 +5587,151 @@ static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
        conn_info->resp_ie_len = 0;
 }
 
+u8 brcmf_map_prio_to_prec(void *config, u8 prio)
+{
+       struct brcmf_cfg80211_info *cfg = (struct brcmf_cfg80211_info *)config;
+
+       if (!cfg)
+               return (prio == PRIO_8021D_NONE || prio == PRIO_8021D_BE) ?
+                      (prio ^ 2) : prio;
+
+       /* For those AC(s) with ACM flag set to 1, convert its 4-level priority
+        * to an 8-level precedence which is the same as BE's
+        */
+       if (prio > PRIO_8021D_EE &&
+           cfg->ac_priority[prio] == cfg->ac_priority[PRIO_8021D_BE])
+               return cfg->ac_priority[prio] * 2;
+
+       /* Conversion of 4-level priority to 8-level precedence */
+       if (prio == PRIO_8021D_BE || prio == PRIO_8021D_BK ||
+           prio == PRIO_8021D_CL || prio == PRIO_8021D_VO)
+               return cfg->ac_priority[prio] * 2;
+       else
+               return cfg->ac_priority[prio] * 2 + 1;
+}
+
+u8 brcmf_map_prio_to_aci(void *config, u8 prio)
+{
+       /* Prio here refers to the 802.1d priority in range of 0 to 7.
+        * ACI here refers to the WLAN AC Index in range of 0 to 3.
+        * This function will return ACI corresponding to input prio.
+        */
+       struct brcmf_cfg80211_info *cfg = (struct brcmf_cfg80211_info *)config;
+
+       if (cfg)
+               return cfg->ac_priority[prio];
+
+       return prio;
+}
+
+static void brcmf_init_wmm_prio(u8 *priority)
+{
+       /* Initialize AC priority array to default
+        * 802.1d priority as per following table:
+        * 802.1d prio 0,3 maps to BE
+        * 802.1d prio 1,2 maps to BK
+        * 802.1d prio 4,5 maps to VI
+        * 802.1d prio 6,7 maps to VO
+        */
+       priority[0] = BRCMF_FWS_FIFO_AC_BE;
+       priority[3] = BRCMF_FWS_FIFO_AC_BE;
+       priority[1] = BRCMF_FWS_FIFO_AC_BK;
+       priority[2] = BRCMF_FWS_FIFO_AC_BK;
+       priority[4] = BRCMF_FWS_FIFO_AC_VI;
+       priority[5] = BRCMF_FWS_FIFO_AC_VI;
+       priority[6] = BRCMF_FWS_FIFO_AC_VO;
+       priority[7] = BRCMF_FWS_FIFO_AC_VO;
+}
+
+static void brcmf_wifi_prioritize_acparams(const
+       struct brcmf_cfg80211_edcf_acparam *acp, u8 *priority)
+{
+       u8 aci;
+       u8 aifsn;
+       u8 ecwmin;
+       u8 ecwmax;
+       u8 acm;
+       u8 ranking_basis[EDCF_AC_COUNT];
+       u8 aci_prio[EDCF_AC_COUNT]; /* AC_BE, AC_BK, AC_VI, AC_VO */
+       u8 index;
+
+       for (aci = 0; aci < EDCF_AC_COUNT; aci++, acp++) {
+               aifsn  = acp->ACI & EDCF_AIFSN_MASK;
+               acm = (acp->ACI & EDCF_ACM_MASK) ? 1 : 0;
+               ecwmin = acp->ECW & EDCF_ECWMIN_MASK;
+               ecwmax = (acp->ECW & EDCF_ECWMAX_MASK) >> EDCF_ECWMAX_SHIFT;
+               brcmf_dbg(CONN, "ACI %d aifsn %d acm %d ecwmin %d ecwmax %d\n",
+                         aci, aifsn, acm, ecwmin, ecwmax);
+               /* Default AC_VO will be the lowest ranking value */
+               ranking_basis[aci] = aifsn + ecwmin + ecwmax;
+               /* Initialise priority starting at 0 (AC_BE) */
+               aci_prio[aci] = 0;
+
+               /* If ACM is set, STA can't use this AC as per 802.11.
+                * Change the ranking to BE
+                */
+               if (aci != AC_BE && aci != AC_BK && acm == 1)
+                       ranking_basis[aci] = ranking_basis[AC_BE];
+       }
+
+       /* Ranking method which works for AC priority
+        * swapping when values for cwmin, cwmax and aifsn are varied
+        * Compare each aci_prio against each other aci_prio
+        */
+       for (aci = 0; aci < EDCF_AC_COUNT; aci++) {
+               for (index = 0; index < EDCF_AC_COUNT; index++) {
+                       if (index != aci) {
+                               /* Smaller ranking value has higher priority,
+                                * so increment priority for each ACI which has
+                                * a higher ranking value
+                                */
+                               if (ranking_basis[aci] < ranking_basis[index])
+                                       aci_prio[aci]++;
+                       }
+               }
+       }
+
+       /* By now, aci_prio[] will be in range of 0 to 3.
+        * Use ACI prio to get the new priority value for
+        * each 802.1d traffic type, in this range.
+        */
+       if (!(aci_prio[AC_BE] == aci_prio[AC_BK] &&
+             aci_prio[AC_BK] == aci_prio[AC_VI] &&
+             aci_prio[AC_VI] == aci_prio[AC_VO])) {
+               /* 802.1d 0,3 maps to BE */
+               priority[0] = aci_prio[AC_BE];
+               priority[3] = aci_prio[AC_BE];
+
+               /* 802.1d 1,2 maps to BK */
+               priority[1] = aci_prio[AC_BK];
+               priority[2] = aci_prio[AC_BK];
+
+               /* 802.1d 4,5 maps to VO */
+               priority[4] = aci_prio[AC_VI];
+               priority[5] = aci_prio[AC_VI];
+
+               /* 802.1d 6,7 maps to VO */
+               priority[6] = aci_prio[AC_VO];
+               priority[7] = aci_prio[AC_VO];
+       } else {
+               /* Initialize to default priority */
+               brcmf_init_wmm_prio(priority);
+       }
+
+       brcmf_dbg(CONN, "Adj prio BE 0->%d, BK 1->%d, BK 2->%d, BE 3->%d\n",
+                 priority[0], priority[1], priority[2], priority[3]);
+
+       brcmf_dbg(CONN, "Adj prio VI 4->%d, VI 5->%d, VO 6->%d, VO 7->%d\n",
+                 priority[4], priority[5], priority[6], priority[7]);
+}
+
 static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
                               struct brcmf_if *ifp)
 {
        struct brcmf_pub *drvr = cfg->pub;
        struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
        struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
+       struct brcmf_cfg80211_edcf_acparam edcf_acparam_info[EDCF_AC_COUNT];
        u32 req_len;
        u32 resp_len;
        s32 err = 0;
@@ -5640,6 +5780,17 @@ static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
                            GFP_KERNEL);
                if (!conn_info->resp_ie)
                        conn_info->resp_ie_len = 0;
+
+               err = brcmf_fil_iovar_data_get(ifp, "wme_ac_sta",
+                                              edcf_acparam_info,
+                                              sizeof(edcf_acparam_info));
+               if (err) {
+                       brcmf_err("could not get wme_ac_sta (%d)\n", err);
+                       return err;
+               }
+
+               brcmf_wifi_prioritize_acparams(edcf_acparam_info,
+                                              cfg->ac_priority);
        } else {
                conn_info->resp_ie_len = 0;
                conn_info->resp_ie = NULL;
@@ -6056,6 +6207,7 @@ static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
        mutex_init(&cfg->usr_sync);
        brcmf_init_escan(cfg);
        brcmf_init_conf(cfg->conf);
+       brcmf_init_wmm_prio(cfg->ac_priority);
        init_completion(&cfg->vif_disabled);
        return err;
 }
index 3ca8c07..333fdf3 100644 (file)
 #define WL_ROAM_TRIGGER_LEVEL          -75
 #define WL_ROAM_DELTA                  20
 
+/* WME Access Category Indices (ACIs) */
+#define AC_BE                  0       /* Best Effort */
+#define AC_BK                  1       /* Background */
+#define AC_VI                  2       /* Video */
+#define AC_VO                  3       /* Voice */
+#define EDCF_AC_COUNT          4
+#define MAX_8021D_PRIO         8
+
+#define EDCF_ACI_MASK                  0x60
+#define EDCF_ACI_SHIFT                 5
+#define EDCF_ACM_MASK                  0x10
+#define EDCF_ECWMIN_MASK               0x0f
+#define EDCF_ECWMAX_SHIFT              4
+#define EDCF_AIFSN_MASK                        0x0f
+#define EDCF_AIFSN_MAX                 15
+#define EDCF_ECWMAX_MASK               0xf0
+
 /* Keep BRCMF_ESCAN_BUF_SIZE below 64K (65536). Allocing over 64K can be
  * problematic on some systems and should be avoided.
  */
@@ -209,6 +226,12 @@ struct brcmf_cfg80211_assoc_ielen_le {
        __le32 resp_len;
 };
 
+struct brcmf_cfg80211_edcf_acparam {
+       u8 ACI;
+       u8 ECW;
+       u16 TXOP;        /* stored in network order (ls octet first) */
+};
+
 /* dongle escan state */
 enum wl_escan_state {
        WL_ESCAN_STATE_IDLE,
@@ -327,6 +350,7 @@ struct brcmf_cfg80211_info {
        struct brcmf_assoclist_le assoclist;
        struct brcmf_cfg80211_wowl wowl;
        struct brcmf_pno_info *pno;
+       u8 ac_priority[MAX_8021D_PRIO];
 };
 
 /**
index 144cf45..8b5f499 100644 (file)
@@ -72,4 +72,8 @@ static inline void
 brcmf_dmi_probe(struct brcmf_mp_device *settings, u32 chip, u32 chiprev) {}
 #endif
 
+u8 brcmf_map_prio_to_prec(void *cfg, u8 prio);
+
+u8 brcmf_map_prio_to_aci(void *cfg, u8 prio);
+
 #endif /* BRCMFMAC_COMMON_H */
index 2b78378..0970126 100644 (file)
@@ -311,28 +311,6 @@ struct brcmf_skbuff_cb {
 /* How long to defer borrowing in jiffies */
 #define BRCMF_FWS_BORROW_DEFER_PERIOD          (HZ / 10)
 
-/**
- * enum brcmf_fws_fifo - fifo indices used by dongle firmware.
- *
- * @BRCMF_FWS_FIFO_FIRST: first fifo, ie. background.
- * @BRCMF_FWS_FIFO_AC_BK: fifo for background traffic.
- * @BRCMF_FWS_FIFO_AC_BE: fifo for best-effort traffic.
- * @BRCMF_FWS_FIFO_AC_VI: fifo for video traffic.
- * @BRCMF_FWS_FIFO_AC_VO: fifo for voice traffic.
- * @BRCMF_FWS_FIFO_BCMC: fifo for broadcast/multicast (AP only).
- * @BRCMF_FWS_FIFO_ATIM: fifo for ATIM (AP only).
- * @BRCMF_FWS_FIFO_COUNT: number of fifos.
- */
-enum brcmf_fws_fifo {
-       BRCMF_FWS_FIFO_FIRST,
-       BRCMF_FWS_FIFO_AC_BK = BRCMF_FWS_FIFO_FIRST,
-       BRCMF_FWS_FIFO_AC_BE,
-       BRCMF_FWS_FIFO_AC_VI,
-       BRCMF_FWS_FIFO_AC_VO,
-       BRCMF_FWS_FIFO_BCMC,
-       BRCMF_FWS_FIFO_ATIM,
-       BRCMF_FWS_FIFO_COUNT
-};
 
 /**
  * enum brcmf_fws_txstatus - txstatus flag values.
@@ -2130,8 +2108,10 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
        skcb->if_flags = 0;
        skcb->state = BRCMF_FWS_SKBSTATE_NEW;
        brcmf_skb_if_flags_set_field(skb, INDEX, ifp->ifidx);
+
+       /* mapping from 802.1d priority to firmware fifo index */
        if (!multicast)
-               fifo = brcmf_fws_prio2fifo[skb->priority];
+               fifo = brcmf_map_prio_to_aci(drvr->config, skb->priority);
 
        brcmf_fws_lock(fws);
        if (fifo != BRCMF_FWS_FIFO_AC_BE && fifo < BRCMF_FWS_FIFO_BCMC)
index b486d57..b16a9d1 100644 (file)
@@ -6,6 +6,29 @@
 #ifndef FWSIGNAL_H_
 #define FWSIGNAL_H_
 
+/**
+ * enum brcmf_fws_fifo - fifo indices used by dongle firmware.
+ *
+ * @BRCMF_FWS_FIFO_FIRST: first fifo, ie. background.
+ * @BRCMF_FWS_FIFO_AC_BK: fifo for background traffic.
+ * @BRCMF_FWS_FIFO_AC_BE: fifo for best-effort traffic.
+ * @BRCMF_FWS_FIFO_AC_VI: fifo for video traffic.
+ * @BRCMF_FWS_FIFO_AC_VO: fifo for voice traffic.
+ * @BRCMF_FWS_FIFO_BCMC: fifo for broadcast/multicast (AP only).
+ * @BRCMF_FWS_FIFO_ATIM: fifo for ATIM (AP only).
+ * @BRCMF_FWS_FIFO_COUNT: number of fifos.
+ */
+enum brcmf_fws_fifo {
+       BRCMF_FWS_FIFO_FIRST,
+       BRCMF_FWS_FIFO_AC_BK = BRCMF_FWS_FIFO_FIRST,
+       BRCMF_FWS_FIFO_AC_BE,
+       BRCMF_FWS_FIFO_AC_VI,
+       BRCMF_FWS_FIFO_AC_VO,
+       BRCMF_FWS_FIFO_BCMC,
+       BRCMF_FWS_FIFO_ATIM,
+       BRCMF_FWS_FIFO_COUNT
+};
+
 struct brcmf_fws_info *brcmf_fws_attach(struct brcmf_pub *drvr);
 void brcmf_fws_detach(struct brcmf_fws_info *fws);
 void brcmf_fws_debugfs_create(struct brcmf_pub *drvr);
index 3a08252..ce6f152 100644 (file)
@@ -315,15 +315,6 @@ struct rte_console {
 #define MAX_KSO_ATTEMPTS (PMU_MAX_TRANSITION_DLY/KSO_WAIT_US)
 #define BRCMF_SDIO_MAX_ACCESS_ERRORS   5
 
-/*
- * Conversion of 802.1D priority to precedence level
- */
-static uint prio2prec(u32 prio)
-{
-       return (prio == PRIO_8021D_NONE || prio == PRIO_8021D_BE) ?
-              (prio^2) : prio;
-}
-
 #ifdef DEBUG
 /* Device console log buffer state */
 struct brcmf_console {
@@ -2774,7 +2765,13 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt)
        skb_push(pkt, bus->tx_hdrlen);
        /* precondition: IS_ALIGNED((unsigned long)(pkt->data), 2) */
 
-       prec = prio2prec((pkt->priority & PRIOMASK));
+       /* In WLAN, priority is always set by the AP using WMM parameters
+        * and this need not always follow the standard 802.1d priority.
+        * Based on AP WMM config, map from 802.1d priority to corresponding
+        * precedence level.
+        */
+       prec = brcmf_map_prio_to_prec(bus_if->drvr->config,
+                                     (pkt->priority & PRIOMASK));
 
        /* Check for existing queue, current flow-control,
                         pending event, or pending clock */