@@ -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"
@@ -5566,12 +5567,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;
@@ -5620,6 +5760,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;
@@ -6036,6 +6187,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;
}
@@ -23,6 +23,23 @@
#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.
*/
@@ -205,6 +222,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,
@@ -323,6 +346,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];
};
/**
@@ -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 */
@@ -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)
@@ -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);
@@ -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 */