1*73b01e57SJeff Chen // SPDX-License-Identifier: GPL-2.0-only
2*73b01e57SJeff Chen /*
3*73b01e57SJeff Chen * NXP Wireless LAN device driver: WMM
4*73b01e57SJeff Chen *
5*73b01e57SJeff Chen * Copyright 2011-2024 NXP
6*73b01e57SJeff Chen */
7*73b01e57SJeff Chen
8*73b01e57SJeff Chen #include "cfg.h"
9*73b01e57SJeff Chen #include "util.h"
10*73b01e57SJeff Chen #include "fw.h"
11*73b01e57SJeff Chen #include "main.h"
12*73b01e57SJeff Chen #include "wmm.h"
13*73b01e57SJeff Chen #include "11n.h"
14*73b01e57SJeff Chen
15*73b01e57SJeff Chen /* Maximum value FW can accept for driver delay in packet transmission */
16*73b01e57SJeff Chen #define DRV_PKT_DELAY_TO_FW_MAX 512
17*73b01e57SJeff Chen
18*73b01e57SJeff Chen #define WMM_QUEUED_PACKET_LOWER_LIMIT 180
19*73b01e57SJeff Chen
20*73b01e57SJeff Chen #define WMM_QUEUED_PACKET_UPPER_LIMIT 200
21*73b01e57SJeff Chen
22*73b01e57SJeff Chen /* Offset for TOS field in the IP header */
23*73b01e57SJeff Chen #define IPTOS_OFFSET 5
24*73b01e57SJeff Chen
25*73b01e57SJeff Chen static bool disable_tx_amsdu;
26*73b01e57SJeff Chen
27*73b01e57SJeff Chen /*
28*73b01e57SJeff Chen * This table inverses the tos_to_tid operation to get a priority
29*73b01e57SJeff Chen * which is in sequential order, and can be compared.
30*73b01e57SJeff Chen * Use this to compare the priority of two different TIDs.
31*73b01e57SJeff Chen */
32*73b01e57SJeff Chen static const u8 tos_to_tid_inv[] = {
33*73b01e57SJeff Chen 0x02, /* from tos_to_tid[2] = 0 */
34*73b01e57SJeff Chen 0x00, /* from tos_to_tid[0] = 1 */
35*73b01e57SJeff Chen 0x01, /* from tos_to_tid[1] = 2 */
36*73b01e57SJeff Chen 0x03,
37*73b01e57SJeff Chen 0x04,
38*73b01e57SJeff Chen 0x05,
39*73b01e57SJeff Chen 0x06,
40*73b01e57SJeff Chen 0x07
41*73b01e57SJeff Chen };
42*73b01e57SJeff Chen
43*73b01e57SJeff Chen /* WMM information element */
44*73b01e57SJeff Chen static const u8 wmm_info_ie[] = { WLAN_EID_VENDOR_SPECIFIC, 0x07,
45*73b01e57SJeff Chen 0x00, 0x50, 0xf2, 0x02,
46*73b01e57SJeff Chen 0x00, 0x01, 0x00
47*73b01e57SJeff Chen };
48*73b01e57SJeff Chen
49*73b01e57SJeff Chen static const u8 wmm_aci_to_qidx_map[] = { WMM_AC_BE,
50*73b01e57SJeff Chen WMM_AC_BK,
51*73b01e57SJeff Chen WMM_AC_VI,
52*73b01e57SJeff Chen WMM_AC_VO
53*73b01e57SJeff Chen };
54*73b01e57SJeff Chen
55*73b01e57SJeff Chen static u8 tos_to_tid[] = {
56*73b01e57SJeff Chen /* TID DSCP_P2 DSCP_P1 DSCP_P0 WMM_AC */
57*73b01e57SJeff Chen 0x01, /* 0 1 0 AC_BK */
58*73b01e57SJeff Chen 0x02, /* 0 0 0 AC_BK */
59*73b01e57SJeff Chen 0x00, /* 0 0 1 AC_BE */
60*73b01e57SJeff Chen 0x03, /* 0 1 1 AC_BE */
61*73b01e57SJeff Chen 0x04, /* 1 0 0 AC_VI */
62*73b01e57SJeff Chen 0x05, /* 1 0 1 AC_VI */
63*73b01e57SJeff Chen 0x06, /* 1 1 0 AC_VO */
64*73b01e57SJeff Chen 0x07 /* 1 1 1 AC_VO */
65*73b01e57SJeff Chen };
66*73b01e57SJeff Chen
67*73b01e57SJeff Chen static u8 ac_to_tid[4][2] = { {1, 2}, {0, 3}, {4, 5}, {6, 7} };
68*73b01e57SJeff Chen
69*73b01e57SJeff Chen /* Debug prints the priority parameters for a WMM AC. */
70*73b01e57SJeff Chen static void
nxpwifi_wmm_ac_debug_print(const struct ieee80211_wmm_ac_param * ac_param)71*73b01e57SJeff Chen nxpwifi_wmm_ac_debug_print(const struct ieee80211_wmm_ac_param *ac_param)
72*73b01e57SJeff Chen {
73*73b01e57SJeff Chen static const char * const ac_str[] = { "BK", "BE", "VI", "VO" };
74*73b01e57SJeff Chen
75*73b01e57SJeff Chen pr_debug("info: WMM AC_%s: ACI=%d, ACM=%d, Aifsn=%d, ",
76*73b01e57SJeff Chen ac_str[wmm_aci_to_qidx_map[(ac_param->aci_aifsn
77*73b01e57SJeff Chen & NXPWIFI_ACI) >> 5]],
78*73b01e57SJeff Chen (ac_param->aci_aifsn & NXPWIFI_ACI) >> 5,
79*73b01e57SJeff Chen (ac_param->aci_aifsn & NXPWIFI_ACM) >> 4,
80*73b01e57SJeff Chen ac_param->aci_aifsn & NXPWIFI_AIFSN);
81*73b01e57SJeff Chen pr_debug("EcwMin=%d, EcwMax=%d, TxopLimit=%d\n",
82*73b01e57SJeff Chen ac_param->cw & NXPWIFI_ECW_MIN,
83*73b01e57SJeff Chen (ac_param->cw & NXPWIFI_ECW_MAX) >> 4,
84*73b01e57SJeff Chen le16_to_cpu(ac_param->txop_limit));
85*73b01e57SJeff Chen }
86*73b01e57SJeff Chen
87*73b01e57SJeff Chen /* Allocates a route address list. */
88*73b01e57SJeff Chen static struct nxpwifi_ra_list_tbl *
nxpwifi_wmm_allocate_ralist_node(struct nxpwifi_adapter * adapter,const u8 * ra)89*73b01e57SJeff Chen nxpwifi_wmm_allocate_ralist_node(struct nxpwifi_adapter *adapter, const u8 *ra)
90*73b01e57SJeff Chen {
91*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl *ra_list;
92*73b01e57SJeff Chen
93*73b01e57SJeff Chen ra_list = kzalloc_obj(*ra_list, GFP_ATOMIC);
94*73b01e57SJeff Chen if (!ra_list)
95*73b01e57SJeff Chen return NULL;
96*73b01e57SJeff Chen
97*73b01e57SJeff Chen INIT_LIST_HEAD(&ra_list->list);
98*73b01e57SJeff Chen skb_queue_head_init(&ra_list->skb_head);
99*73b01e57SJeff Chen
100*73b01e57SJeff Chen memcpy(ra_list->ra, ra, ETH_ALEN);
101*73b01e57SJeff Chen
102*73b01e57SJeff Chen ra_list->total_pkt_count = 0;
103*73b01e57SJeff Chen
104*73b01e57SJeff Chen nxpwifi_dbg(adapter, INFO, "info: allocated ra_list %p\n", ra_list);
105*73b01e57SJeff Chen
106*73b01e57SJeff Chen return ra_list;
107*73b01e57SJeff Chen }
108*73b01e57SJeff Chen
109*73b01e57SJeff Chen /*
110*73b01e57SJeff Chen * Returns random no between 16 and 32 to be used as threshold for no of
111*73b01e57SJeff Chen * packets after which BA setup is initiated.
112*73b01e57SJeff Chen */
nxpwifi_get_random_ba_threshold(void)113*73b01e57SJeff Chen static u8 nxpwifi_get_random_ba_threshold(void)
114*73b01e57SJeff Chen {
115*73b01e57SJeff Chen u64 ns;
116*73b01e57SJeff Chen /*
117*73b01e57SJeff Chen * setup ba_packet_threshold here random number between
118*73b01e57SJeff Chen * [BA_SETUP_PACKET_OFFSET,
119*73b01e57SJeff Chen * BA_SETUP_PACKET_OFFSET+BA_SETUP_MAX_PACKET_THRESHOLD-1]
120*73b01e57SJeff Chen */
121*73b01e57SJeff Chen ns = ktime_get_ns();
122*73b01e57SJeff Chen ns += (ns >> 32) + (ns >> 16);
123*73b01e57SJeff Chen
124*73b01e57SJeff Chen return ((u8)ns % BA_SETUP_MAX_PACKET_THRESHOLD) + BA_SETUP_PACKET_OFFSET;
125*73b01e57SJeff Chen }
126*73b01e57SJeff Chen
127*73b01e57SJeff Chen /* Allocates and adds a RA list for all TIDs with the given RA. */
nxpwifi_ralist_add(struct nxpwifi_private * priv,const u8 * ra)128*73b01e57SJeff Chen void nxpwifi_ralist_add(struct nxpwifi_private *priv, const u8 *ra)
129*73b01e57SJeff Chen {
130*73b01e57SJeff Chen int i;
131*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl *ra_list;
132*73b01e57SJeff Chen struct nxpwifi_adapter *adapter = priv->adapter;
133*73b01e57SJeff Chen struct nxpwifi_sta_node *node;
134*73b01e57SJeff Chen
135*73b01e57SJeff Chen for (i = 0; i < MAX_NUM_TID; ++i) {
136*73b01e57SJeff Chen ra_list = nxpwifi_wmm_allocate_ralist_node(adapter, ra);
137*73b01e57SJeff Chen nxpwifi_dbg(adapter, INFO,
138*73b01e57SJeff Chen "info: created ra_list %p\n", ra_list);
139*73b01e57SJeff Chen
140*73b01e57SJeff Chen if (!ra_list)
141*73b01e57SJeff Chen break;
142*73b01e57SJeff Chen
143*73b01e57SJeff Chen ra_list->is_11n_enabled = 0;
144*73b01e57SJeff Chen ra_list->ba_status = BA_SETUP_NONE;
145*73b01e57SJeff Chen ra_list->amsdu_in_ampdu = false;
146*73b01e57SJeff Chen if (!nxpwifi_queuing_ra_based(priv)) {
147*73b01e57SJeff Chen ra_list->is_11n_enabled = IS_11N_ENABLED(priv);
148*73b01e57SJeff Chen } else {
149*73b01e57SJeff Chen rcu_read_lock();
150*73b01e57SJeff Chen node = nxpwifi_get_sta_entry(priv, ra);
151*73b01e57SJeff Chen if (node)
152*73b01e57SJeff Chen ra_list->tx_paused = node->tx_pause;
153*73b01e57SJeff Chen ra_list->is_11n_enabled =
154*73b01e57SJeff Chen nxpwifi_is_sta_11n_enabled(priv, node);
155*73b01e57SJeff Chen if (ra_list->is_11n_enabled)
156*73b01e57SJeff Chen ra_list->max_amsdu = node->max_amsdu;
157*73b01e57SJeff Chen rcu_read_unlock();
158*73b01e57SJeff Chen }
159*73b01e57SJeff Chen
160*73b01e57SJeff Chen nxpwifi_dbg(adapter, DATA, "data: ralist %p: is_11n_enabled=%d\n",
161*73b01e57SJeff Chen ra_list, ra_list->is_11n_enabled);
162*73b01e57SJeff Chen
163*73b01e57SJeff Chen if (ra_list->is_11n_enabled) {
164*73b01e57SJeff Chen ra_list->ba_pkt_count = 0;
165*73b01e57SJeff Chen ra_list->ba_packet_thr =
166*73b01e57SJeff Chen nxpwifi_get_random_ba_threshold();
167*73b01e57SJeff Chen }
168*73b01e57SJeff Chen list_add_tail(&ra_list->list,
169*73b01e57SJeff Chen &priv->wmm.tid_tbl_ptr[i].ra_list);
170*73b01e57SJeff Chen }
171*73b01e57SJeff Chen }
172*73b01e57SJeff Chen
173*73b01e57SJeff Chen /* Sets the WMM queue priorities to their default values. */
nxpwifi_wmm_default_queue_priorities(struct nxpwifi_private * priv)174*73b01e57SJeff Chen static void nxpwifi_wmm_default_queue_priorities(struct nxpwifi_private *priv)
175*73b01e57SJeff Chen {
176*73b01e57SJeff Chen /* Default queue priorities: VO->VI->BE->BK */
177*73b01e57SJeff Chen priv->wmm.queue_priority[0] = WMM_AC_VO;
178*73b01e57SJeff Chen priv->wmm.queue_priority[1] = WMM_AC_VI;
179*73b01e57SJeff Chen priv->wmm.queue_priority[2] = WMM_AC_BE;
180*73b01e57SJeff Chen priv->wmm.queue_priority[3] = WMM_AC_BK;
181*73b01e57SJeff Chen }
182*73b01e57SJeff Chen
183*73b01e57SJeff Chen /* Map ACs to TIDs. */
184*73b01e57SJeff Chen static void
nxpwifi_wmm_queue_priorities_tid(struct nxpwifi_private * priv)185*73b01e57SJeff Chen nxpwifi_wmm_queue_priorities_tid(struct nxpwifi_private *priv)
186*73b01e57SJeff Chen {
187*73b01e57SJeff Chen struct nxpwifi_wmm_desc *wmm = &priv->wmm;
188*73b01e57SJeff Chen u8 *queue_priority = wmm->queue_priority;
189*73b01e57SJeff Chen int i;
190*73b01e57SJeff Chen
191*73b01e57SJeff Chen for (i = 0; i < 4; ++i) {
192*73b01e57SJeff Chen tos_to_tid[7 - (i * 2)] = ac_to_tid[queue_priority[i]][1];
193*73b01e57SJeff Chen tos_to_tid[6 - (i * 2)] = ac_to_tid[queue_priority[i]][0];
194*73b01e57SJeff Chen }
195*73b01e57SJeff Chen
196*73b01e57SJeff Chen for (i = 0; i < MAX_NUM_TID; ++i)
197*73b01e57SJeff Chen priv->tos_to_tid_inv[tos_to_tid[i]] = (u8)i;
198*73b01e57SJeff Chen
199*73b01e57SJeff Chen atomic_set(&wmm->highest_queued_prio, HIGH_PRIO_TID);
200*73b01e57SJeff Chen }
201*73b01e57SJeff Chen
202*73b01e57SJeff Chen /* Initializes WMM priority queues. */
203*73b01e57SJeff Chen void
nxpwifi_wmm_setup_queue_priorities(struct nxpwifi_private * priv,struct ieee80211_wmm_param_ie * wmm_ie)204*73b01e57SJeff Chen nxpwifi_wmm_setup_queue_priorities(struct nxpwifi_private *priv,
205*73b01e57SJeff Chen struct ieee80211_wmm_param_ie *wmm_ie)
206*73b01e57SJeff Chen {
207*73b01e57SJeff Chen u16 cw_min, avg_back_off, tmp[4];
208*73b01e57SJeff Chen u32 i, j, num_ac;
209*73b01e57SJeff Chen u8 ac_idx;
210*73b01e57SJeff Chen
211*73b01e57SJeff Chen if (!wmm_ie || !priv->wmm_enabled) {
212*73b01e57SJeff Chen /* WMM is not enabled, just set the defaults and return */
213*73b01e57SJeff Chen nxpwifi_wmm_default_queue_priorities(priv);
214*73b01e57SJeff Chen return;
215*73b01e57SJeff Chen }
216*73b01e57SJeff Chen
217*73b01e57SJeff Chen nxpwifi_dbg(priv->adapter, INFO,
218*73b01e57SJeff Chen "info: WMM Parameter element: version=%d,\t"
219*73b01e57SJeff Chen "qos_info Parameter Set Count=%d, Reserved=%#x\n",
220*73b01e57SJeff Chen wmm_ie->version, wmm_ie->qos_info &
221*73b01e57SJeff Chen IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK,
222*73b01e57SJeff Chen wmm_ie->reserved);
223*73b01e57SJeff Chen
224*73b01e57SJeff Chen for (num_ac = 0; num_ac < ARRAY_SIZE(wmm_ie->ac); num_ac++) {
225*73b01e57SJeff Chen u8 ecw = wmm_ie->ac[num_ac].cw;
226*73b01e57SJeff Chen u8 aci_aifsn = wmm_ie->ac[num_ac].aci_aifsn;
227*73b01e57SJeff Chen
228*73b01e57SJeff Chen cw_min = (1 << (ecw & NXPWIFI_ECW_MIN)) - 1;
229*73b01e57SJeff Chen avg_back_off = (cw_min >> 1) + (aci_aifsn & NXPWIFI_AIFSN);
230*73b01e57SJeff Chen
231*73b01e57SJeff Chen ac_idx = wmm_aci_to_qidx_map[(aci_aifsn & NXPWIFI_ACI) >> 5];
232*73b01e57SJeff Chen priv->wmm.queue_priority[ac_idx] = ac_idx;
233*73b01e57SJeff Chen tmp[ac_idx] = avg_back_off;
234*73b01e57SJeff Chen
235*73b01e57SJeff Chen nxpwifi_dbg(priv->adapter, INFO,
236*73b01e57SJeff Chen "info: WMM: CWmax=%d CWmin=%d Avg Back-off=%d\n",
237*73b01e57SJeff Chen (1 << ((ecw & NXPWIFI_ECW_MAX) >> 4)) - 1,
238*73b01e57SJeff Chen cw_min, avg_back_off);
239*73b01e57SJeff Chen nxpwifi_wmm_ac_debug_print(&wmm_ie->ac[num_ac]);
240*73b01e57SJeff Chen }
241*73b01e57SJeff Chen
242*73b01e57SJeff Chen /* Bubble sort */
243*73b01e57SJeff Chen for (i = 0; i < num_ac; i++) {
244*73b01e57SJeff Chen for (j = 1; j < num_ac - i; j++) {
245*73b01e57SJeff Chen if (tmp[j - 1] > tmp[j]) {
246*73b01e57SJeff Chen swap(tmp[j - 1], tmp[j]);
247*73b01e57SJeff Chen swap(priv->wmm.queue_priority[j - 1],
248*73b01e57SJeff Chen priv->wmm.queue_priority[j]);
249*73b01e57SJeff Chen } else if (tmp[j - 1] == tmp[j]) {
250*73b01e57SJeff Chen if (priv->wmm.queue_priority[j - 1]
251*73b01e57SJeff Chen < priv->wmm.queue_priority[j])
252*73b01e57SJeff Chen swap(priv->wmm.queue_priority[j - 1],
253*73b01e57SJeff Chen priv->wmm.queue_priority[j]);
254*73b01e57SJeff Chen }
255*73b01e57SJeff Chen }
256*73b01e57SJeff Chen }
257*73b01e57SJeff Chen
258*73b01e57SJeff Chen nxpwifi_wmm_queue_priorities_tid(priv);
259*73b01e57SJeff Chen }
260*73b01e57SJeff Chen
261*73b01e57SJeff Chen /* Evaluates whether or not an AC is to be downgraded. */
262*73b01e57SJeff Chen static enum nxpwifi_wmm_ac_e
nxpwifi_wmm_eval_downgrade_ac(struct nxpwifi_private * priv,enum nxpwifi_wmm_ac_e eval_ac)263*73b01e57SJeff Chen nxpwifi_wmm_eval_downgrade_ac(struct nxpwifi_private *priv,
264*73b01e57SJeff Chen enum nxpwifi_wmm_ac_e eval_ac)
265*73b01e57SJeff Chen {
266*73b01e57SJeff Chen int down_ac;
267*73b01e57SJeff Chen enum nxpwifi_wmm_ac_e ret_ac;
268*73b01e57SJeff Chen struct nxpwifi_wmm_ac_status *ac_status;
269*73b01e57SJeff Chen
270*73b01e57SJeff Chen ac_status = &priv->wmm.ac_status[eval_ac];
271*73b01e57SJeff Chen
272*73b01e57SJeff Chen if (!ac_status->disabled)
273*73b01e57SJeff Chen /* Okay to use this AC, its enabled */
274*73b01e57SJeff Chen return eval_ac;
275*73b01e57SJeff Chen
276*73b01e57SJeff Chen /* Setup a default return value of the lowest priority */
277*73b01e57SJeff Chen ret_ac = WMM_AC_BK;
278*73b01e57SJeff Chen
279*73b01e57SJeff Chen /*
280*73b01e57SJeff Chen * Find the highest AC that is enabled and does not require
281*73b01e57SJeff Chen * admission control. The spec disallows downgrading to an AC,
282*73b01e57SJeff Chen * which is enabled due to a completed admission control.
283*73b01e57SJeff Chen * Unadmitted traffic is not to be sent on an AC with admitted
284*73b01e57SJeff Chen * traffic.
285*73b01e57SJeff Chen */
286*73b01e57SJeff Chen for (down_ac = WMM_AC_BK; down_ac < eval_ac; down_ac++) {
287*73b01e57SJeff Chen ac_status = &priv->wmm.ac_status[down_ac];
288*73b01e57SJeff Chen
289*73b01e57SJeff Chen if (!ac_status->disabled && !ac_status->flow_required)
290*73b01e57SJeff Chen /*
291*73b01e57SJeff Chen * AC is enabled and does not require admission
292*73b01e57SJeff Chen * control
293*73b01e57SJeff Chen */
294*73b01e57SJeff Chen ret_ac = (enum nxpwifi_wmm_ac_e)down_ac;
295*73b01e57SJeff Chen }
296*73b01e57SJeff Chen
297*73b01e57SJeff Chen return ret_ac;
298*73b01e57SJeff Chen }
299*73b01e57SJeff Chen
300*73b01e57SJeff Chen /* Downgrades WMM priority queue. */
301*73b01e57SJeff Chen void
nxpwifi_wmm_setup_ac_downgrade(struct nxpwifi_private * priv)302*73b01e57SJeff Chen nxpwifi_wmm_setup_ac_downgrade(struct nxpwifi_private *priv)
303*73b01e57SJeff Chen {
304*73b01e57SJeff Chen int ac_val;
305*73b01e57SJeff Chen
306*73b01e57SJeff Chen nxpwifi_dbg(priv->adapter, INFO, "info: WMM: AC Priorities:\t"
307*73b01e57SJeff Chen "BK(0), BE(1), VI(2), VO(3)\n");
308*73b01e57SJeff Chen
309*73b01e57SJeff Chen if (!priv->wmm_enabled) {
310*73b01e57SJeff Chen /* WMM is not enabled, default priorities */
311*73b01e57SJeff Chen for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++)
312*73b01e57SJeff Chen priv->wmm.ac_down_graded_vals[ac_val] =
313*73b01e57SJeff Chen (enum nxpwifi_wmm_ac_e)ac_val;
314*73b01e57SJeff Chen } else {
315*73b01e57SJeff Chen for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) {
316*73b01e57SJeff Chen priv->wmm.ac_down_graded_vals[ac_val] =
317*73b01e57SJeff Chen nxpwifi_wmm_eval_downgrade_ac
318*73b01e57SJeff Chen (priv, (enum nxpwifi_wmm_ac_e)ac_val);
319*73b01e57SJeff Chen nxpwifi_dbg(priv->adapter, INFO,
320*73b01e57SJeff Chen "info: WMM: AC PRIO %d maps to %d\n",
321*73b01e57SJeff Chen ac_val,
322*73b01e57SJeff Chen priv->wmm.ac_down_graded_vals[ac_val]);
323*73b01e57SJeff Chen }
324*73b01e57SJeff Chen }
325*73b01e57SJeff Chen }
326*73b01e57SJeff Chen
327*73b01e57SJeff Chen /* Converts the IP TOS field to an WMM AC Queue assignment. */
328*73b01e57SJeff Chen static enum nxpwifi_wmm_ac_e
nxpwifi_wmm_convert_tos_to_ac(struct nxpwifi_adapter * adapter,u32 tos)329*73b01e57SJeff Chen nxpwifi_wmm_convert_tos_to_ac(struct nxpwifi_adapter *adapter, u32 tos)
330*73b01e57SJeff Chen {
331*73b01e57SJeff Chen /* Map of TOS UP values to WMM AC */
332*73b01e57SJeff Chen static const enum nxpwifi_wmm_ac_e tos_to_ac[] = {
333*73b01e57SJeff Chen WMM_AC_BE,
334*73b01e57SJeff Chen WMM_AC_BK,
335*73b01e57SJeff Chen WMM_AC_BK,
336*73b01e57SJeff Chen WMM_AC_BE,
337*73b01e57SJeff Chen WMM_AC_VI,
338*73b01e57SJeff Chen WMM_AC_VI,
339*73b01e57SJeff Chen WMM_AC_VO,
340*73b01e57SJeff Chen WMM_AC_VO
341*73b01e57SJeff Chen };
342*73b01e57SJeff Chen
343*73b01e57SJeff Chen if (tos >= ARRAY_SIZE(tos_to_ac))
344*73b01e57SJeff Chen return WMM_AC_BE;
345*73b01e57SJeff Chen
346*73b01e57SJeff Chen return tos_to_ac[tos];
347*73b01e57SJeff Chen }
348*73b01e57SJeff Chen
349*73b01e57SJeff Chen /*
350*73b01e57SJeff Chen * Evaluates a given TID and downgrades it to a lower TID if the WMM Parameter
351*73b01e57SJeff Chen * element received from the AP indicates that the AP is disabled (due to call
352*73b01e57SJeff Chen * admission control (ACM bit).
353*73b01e57SJeff Chen */
nxpwifi_wmm_downgrade_tid(struct nxpwifi_private * priv,u32 tid)354*73b01e57SJeff Chen u8 nxpwifi_wmm_downgrade_tid(struct nxpwifi_private *priv, u32 tid)
355*73b01e57SJeff Chen {
356*73b01e57SJeff Chen enum nxpwifi_wmm_ac_e ac, ac_down;
357*73b01e57SJeff Chen u8 new_tid;
358*73b01e57SJeff Chen
359*73b01e57SJeff Chen ac = nxpwifi_wmm_convert_tos_to_ac(priv->adapter, tid);
360*73b01e57SJeff Chen ac_down = priv->wmm.ac_down_graded_vals[ac];
361*73b01e57SJeff Chen
362*73b01e57SJeff Chen /*
363*73b01e57SJeff Chen * Send the index to tid array, picking from the array will be
364*73b01e57SJeff Chen * taken care by dequeuing function
365*73b01e57SJeff Chen */
366*73b01e57SJeff Chen new_tid = ac_to_tid[ac_down][tid % 2];
367*73b01e57SJeff Chen
368*73b01e57SJeff Chen return new_tid;
369*73b01e57SJeff Chen }
370*73b01e57SJeff Chen
371*73b01e57SJeff Chen /* Initializes the WMM state information and the WMM data path queues. */
372*73b01e57SJeff Chen void
nxpwifi_wmm_init(struct nxpwifi_adapter * adapter)373*73b01e57SJeff Chen nxpwifi_wmm_init(struct nxpwifi_adapter *adapter)
374*73b01e57SJeff Chen {
375*73b01e57SJeff Chen int i, j;
376*73b01e57SJeff Chen struct nxpwifi_private *priv;
377*73b01e57SJeff Chen
378*73b01e57SJeff Chen for (j = 0; j < adapter->priv_num; ++j) {
379*73b01e57SJeff Chen priv = adapter->priv[j];
380*73b01e57SJeff Chen
381*73b01e57SJeff Chen for (i = 0; i < MAX_NUM_TID; ++i) {
382*73b01e57SJeff Chen if (!disable_tx_amsdu &&
383*73b01e57SJeff Chen adapter->tx_buf_size > NXPWIFI_TX_DATA_BUF_SIZE_2K)
384*73b01e57SJeff Chen priv->aggr_prio_tbl[i].amsdu =
385*73b01e57SJeff Chen priv->tos_to_tid_inv[i];
386*73b01e57SJeff Chen else
387*73b01e57SJeff Chen priv->aggr_prio_tbl[i].amsdu =
388*73b01e57SJeff Chen BA_STREAM_NOT_ALLOWED;
389*73b01e57SJeff Chen priv->aggr_prio_tbl[i].ampdu_ap =
390*73b01e57SJeff Chen priv->tos_to_tid_inv[i];
391*73b01e57SJeff Chen priv->aggr_prio_tbl[i].ampdu_user =
392*73b01e57SJeff Chen priv->tos_to_tid_inv[i];
393*73b01e57SJeff Chen }
394*73b01e57SJeff Chen
395*73b01e57SJeff Chen priv->aggr_prio_tbl[6].amsdu =
396*73b01e57SJeff Chen priv->aggr_prio_tbl[6].ampdu_ap =
397*73b01e57SJeff Chen priv->aggr_prio_tbl[6].ampdu_user =
398*73b01e57SJeff Chen BA_STREAM_NOT_ALLOWED;
399*73b01e57SJeff Chen
400*73b01e57SJeff Chen priv->aggr_prio_tbl[7].amsdu =
401*73b01e57SJeff Chen priv->aggr_prio_tbl[7].ampdu_ap =
402*73b01e57SJeff Chen priv->aggr_prio_tbl[7].ampdu_user =
403*73b01e57SJeff Chen BA_STREAM_NOT_ALLOWED;
404*73b01e57SJeff Chen
405*73b01e57SJeff Chen nxpwifi_set_ba_params(priv);
406*73b01e57SJeff Chen nxpwifi_reset_11n_rx_seq_num(priv);
407*73b01e57SJeff Chen
408*73b01e57SJeff Chen priv->wmm.drv_pkt_delay_max = NXPWIFI_WMM_DRV_DELAY_MAX;
409*73b01e57SJeff Chen atomic_set(&priv->wmm.tx_pkts_queued, 0);
410*73b01e57SJeff Chen atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID);
411*73b01e57SJeff Chen }
412*73b01e57SJeff Chen }
413*73b01e57SJeff Chen
nxpwifi_bypass_txlist_empty(struct nxpwifi_adapter * adapter)414*73b01e57SJeff Chen bool nxpwifi_bypass_txlist_empty(struct nxpwifi_adapter *adapter)
415*73b01e57SJeff Chen {
416*73b01e57SJeff Chen struct nxpwifi_private *priv;
417*73b01e57SJeff Chen int i;
418*73b01e57SJeff Chen
419*73b01e57SJeff Chen for (i = 0; i < adapter->priv_num; i++) {
420*73b01e57SJeff Chen priv = adapter->priv[i];
421*73b01e57SJeff Chen if (!skb_queue_empty(&priv->bypass_txq))
422*73b01e57SJeff Chen return false;
423*73b01e57SJeff Chen }
424*73b01e57SJeff Chen
425*73b01e57SJeff Chen return true;
426*73b01e57SJeff Chen }
427*73b01e57SJeff Chen
428*73b01e57SJeff Chen /* Checks if WMM Tx queue is empty. */
nxpwifi_wmm_lists_empty(struct nxpwifi_adapter * adapter)429*73b01e57SJeff Chen bool nxpwifi_wmm_lists_empty(struct nxpwifi_adapter *adapter)
430*73b01e57SJeff Chen {
431*73b01e57SJeff Chen int i;
432*73b01e57SJeff Chen struct nxpwifi_private *priv;
433*73b01e57SJeff Chen
434*73b01e57SJeff Chen for (i = 0; i < adapter->priv_num; ++i) {
435*73b01e57SJeff Chen priv = adapter->priv[i];
436*73b01e57SJeff Chen if (!priv->port_open)
437*73b01e57SJeff Chen continue;
438*73b01e57SJeff Chen if (atomic_read(&priv->wmm.tx_pkts_queued))
439*73b01e57SJeff Chen return false;
440*73b01e57SJeff Chen }
441*73b01e57SJeff Chen
442*73b01e57SJeff Chen return true;
443*73b01e57SJeff Chen }
444*73b01e57SJeff Chen
445*73b01e57SJeff Chen /* Deletes all packets in an RA list node. */
446*73b01e57SJeff Chen static void
nxpwifi_wmm_del_pkts_in_ralist_node(struct nxpwifi_private * priv,struct nxpwifi_ra_list_tbl * ra_list)447*73b01e57SJeff Chen nxpwifi_wmm_del_pkts_in_ralist_node(struct nxpwifi_private *priv,
448*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl *ra_list)
449*73b01e57SJeff Chen {
450*73b01e57SJeff Chen struct nxpwifi_adapter *adapter = priv->adapter;
451*73b01e57SJeff Chen struct sk_buff *skb, *tmp;
452*73b01e57SJeff Chen
453*73b01e57SJeff Chen skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) {
454*73b01e57SJeff Chen skb_unlink(skb, &ra_list->skb_head);
455*73b01e57SJeff Chen nxpwifi_write_data_complete(adapter, skb, 0, -1);
456*73b01e57SJeff Chen }
457*73b01e57SJeff Chen }
458*73b01e57SJeff Chen
459*73b01e57SJeff Chen /* Deletes all packets in an RA list. */
460*73b01e57SJeff Chen static void
nxpwifi_wmm_del_pkts_in_ralist(struct nxpwifi_private * priv,struct list_head * ra_list_head)461*73b01e57SJeff Chen nxpwifi_wmm_del_pkts_in_ralist(struct nxpwifi_private *priv,
462*73b01e57SJeff Chen struct list_head *ra_list_head)
463*73b01e57SJeff Chen {
464*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl *ra_list;
465*73b01e57SJeff Chen
466*73b01e57SJeff Chen list_for_each_entry(ra_list, ra_list_head, list)
467*73b01e57SJeff Chen nxpwifi_wmm_del_pkts_in_ralist_node(priv, ra_list);
468*73b01e57SJeff Chen }
469*73b01e57SJeff Chen
470*73b01e57SJeff Chen /* Deletes all packets in all RA lists. */
nxpwifi_wmm_cleanup_queues(struct nxpwifi_private * priv)471*73b01e57SJeff Chen static void nxpwifi_wmm_cleanup_queues(struct nxpwifi_private *priv)
472*73b01e57SJeff Chen {
473*73b01e57SJeff Chen int i;
474*73b01e57SJeff Chen
475*73b01e57SJeff Chen for (i = 0; i < MAX_NUM_TID; i++)
476*73b01e57SJeff Chen nxpwifi_wmm_del_pkts_in_ralist
477*73b01e57SJeff Chen (priv, &priv->wmm.tid_tbl_ptr[i].ra_list);
478*73b01e57SJeff Chen
479*73b01e57SJeff Chen atomic_set(&priv->wmm.tx_pkts_queued, 0);
480*73b01e57SJeff Chen atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID);
481*73b01e57SJeff Chen }
482*73b01e57SJeff Chen
483*73b01e57SJeff Chen /* Deletes all route addresses from all RA lists. */
nxpwifi_wmm_delete_all_ralist(struct nxpwifi_private * priv)484*73b01e57SJeff Chen static void nxpwifi_wmm_delete_all_ralist(struct nxpwifi_private *priv)
485*73b01e57SJeff Chen {
486*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl *ra_list, *tmp_node;
487*73b01e57SJeff Chen int i;
488*73b01e57SJeff Chen
489*73b01e57SJeff Chen for (i = 0; i < MAX_NUM_TID; ++i) {
490*73b01e57SJeff Chen nxpwifi_dbg(priv->adapter, INFO,
491*73b01e57SJeff Chen "info: ra_list: freeing buf for tid %d\n", i);
492*73b01e57SJeff Chen list_for_each_entry_safe(ra_list, tmp_node,
493*73b01e57SJeff Chen &priv->wmm.tid_tbl_ptr[i].ra_list,
494*73b01e57SJeff Chen list) {
495*73b01e57SJeff Chen list_del(&ra_list->list);
496*73b01e57SJeff Chen kfree(ra_list);
497*73b01e57SJeff Chen }
498*73b01e57SJeff Chen
499*73b01e57SJeff Chen INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[i].ra_list);
500*73b01e57SJeff Chen }
501*73b01e57SJeff Chen }
502*73b01e57SJeff Chen
nxpwifi_free_ack_frame(int id,void * p,void * data)503*73b01e57SJeff Chen static int nxpwifi_free_ack_frame(int id, void *p, void *data)
504*73b01e57SJeff Chen {
505*73b01e57SJeff Chen pr_warn("Have pending ack frames!\n");
506*73b01e57SJeff Chen kfree_skb(p);
507*73b01e57SJeff Chen return 0;
508*73b01e57SJeff Chen }
509*73b01e57SJeff Chen
510*73b01e57SJeff Chen /* Cleans up the Tx and Rx queues. */
511*73b01e57SJeff Chen void
nxpwifi_clean_txrx(struct nxpwifi_private * priv)512*73b01e57SJeff Chen nxpwifi_clean_txrx(struct nxpwifi_private *priv)
513*73b01e57SJeff Chen {
514*73b01e57SJeff Chen struct sk_buff *skb, *tmp;
515*73b01e57SJeff Chen unsigned long index;
516*73b01e57SJeff Chen void *entry;
517*73b01e57SJeff Chen
518*73b01e57SJeff Chen nxpwifi_11n_cleanup_reorder_tbl(priv);
519*73b01e57SJeff Chen spin_lock_bh(&priv->wmm.ra_list_spinlock);
520*73b01e57SJeff Chen
521*73b01e57SJeff Chen nxpwifi_wmm_cleanup_queues(priv);
522*73b01e57SJeff Chen nxpwifi_11n_delete_all_tx_ba_stream_tbl(priv);
523*73b01e57SJeff Chen
524*73b01e57SJeff Chen if (priv->adapter->if_ops.cleanup_mpa_buf)
525*73b01e57SJeff Chen priv->adapter->if_ops.cleanup_mpa_buf(priv->adapter);
526*73b01e57SJeff Chen
527*73b01e57SJeff Chen nxpwifi_wmm_delete_all_ralist(priv);
528*73b01e57SJeff Chen memcpy(tos_to_tid, ac_to_tid, sizeof(tos_to_tid));
529*73b01e57SJeff Chen
530*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
531*73b01e57SJeff Chen
532*73b01e57SJeff Chen skb_queue_walk_safe(&priv->bypass_txq, skb, tmp) {
533*73b01e57SJeff Chen skb_unlink(skb, &priv->bypass_txq);
534*73b01e57SJeff Chen nxpwifi_write_data_complete(priv->adapter, skb, 0, -1);
535*73b01e57SJeff Chen }
536*73b01e57SJeff Chen atomic_set(&priv->adapter->bypass_tx_pending, 0);
537*73b01e57SJeff Chen
538*73b01e57SJeff Chen xa_for_each(&priv->ack_status_frames, index, entry) {
539*73b01e57SJeff Chen nxpwifi_free_ack_frame(index, entry, NULL);
540*73b01e57SJeff Chen xa_erase(&priv->ack_status_frames, index);
541*73b01e57SJeff Chen }
542*73b01e57SJeff Chen
543*73b01e57SJeff Chen xa_destroy(&priv->ack_status_frames);
544*73b01e57SJeff Chen }
545*73b01e57SJeff Chen
546*73b01e57SJeff Chen /* Retrieves a particular RA list node, matching with the given TID and RA address. */
547*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl *
nxpwifi_wmm_get_ralist_node(struct nxpwifi_private * priv,u8 tid,const u8 * ra_addr)548*73b01e57SJeff Chen nxpwifi_wmm_get_ralist_node(struct nxpwifi_private *priv, u8 tid,
549*73b01e57SJeff Chen const u8 *ra_addr)
550*73b01e57SJeff Chen {
551*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl *ra_list;
552*73b01e57SJeff Chen
553*73b01e57SJeff Chen list_for_each_entry(ra_list, &priv->wmm.tid_tbl_ptr[tid].ra_list,
554*73b01e57SJeff Chen list) {
555*73b01e57SJeff Chen if (!memcmp(ra_list->ra, ra_addr, ETH_ALEN))
556*73b01e57SJeff Chen return ra_list;
557*73b01e57SJeff Chen }
558*73b01e57SJeff Chen
559*73b01e57SJeff Chen return NULL;
560*73b01e57SJeff Chen }
561*73b01e57SJeff Chen
nxpwifi_update_ralist_tx_pause(struct nxpwifi_private * priv,u8 * mac,u8 tx_pause)562*73b01e57SJeff Chen void nxpwifi_update_ralist_tx_pause(struct nxpwifi_private *priv, u8 *mac,
563*73b01e57SJeff Chen u8 tx_pause)
564*73b01e57SJeff Chen {
565*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl *ra_list;
566*73b01e57SJeff Chen u32 pkt_cnt = 0, tx_pkts_queued;
567*73b01e57SJeff Chen int i;
568*73b01e57SJeff Chen
569*73b01e57SJeff Chen spin_lock_bh(&priv->wmm.ra_list_spinlock);
570*73b01e57SJeff Chen
571*73b01e57SJeff Chen for (i = 0; i < MAX_NUM_TID; ++i) {
572*73b01e57SJeff Chen ra_list = nxpwifi_wmm_get_ralist_node(priv, i, mac);
573*73b01e57SJeff Chen if (ra_list && ra_list->tx_paused != tx_pause) {
574*73b01e57SJeff Chen pkt_cnt += ra_list->total_pkt_count;
575*73b01e57SJeff Chen ra_list->tx_paused = tx_pause;
576*73b01e57SJeff Chen if (tx_pause)
577*73b01e57SJeff Chen priv->wmm.pkts_paused[i] +=
578*73b01e57SJeff Chen ra_list->total_pkt_count;
579*73b01e57SJeff Chen else
580*73b01e57SJeff Chen priv->wmm.pkts_paused[i] -=
581*73b01e57SJeff Chen ra_list->total_pkt_count;
582*73b01e57SJeff Chen }
583*73b01e57SJeff Chen }
584*73b01e57SJeff Chen
585*73b01e57SJeff Chen if (pkt_cnt) {
586*73b01e57SJeff Chen tx_pkts_queued = atomic_read(&priv->wmm.tx_pkts_queued);
587*73b01e57SJeff Chen if (tx_pause)
588*73b01e57SJeff Chen tx_pkts_queued -= pkt_cnt;
589*73b01e57SJeff Chen else
590*73b01e57SJeff Chen tx_pkts_queued += pkt_cnt;
591*73b01e57SJeff Chen
592*73b01e57SJeff Chen atomic_set(&priv->wmm.tx_pkts_queued, tx_pkts_queued);
593*73b01e57SJeff Chen atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID);
594*73b01e57SJeff Chen }
595*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
596*73b01e57SJeff Chen }
597*73b01e57SJeff Chen
598*73b01e57SJeff Chen /* Retrieves an RA list node for a given TID and RA address pair. */
599*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl *
nxpwifi_wmm_get_queue_raptr(struct nxpwifi_private * priv,u8 tid,const u8 * ra_addr)600*73b01e57SJeff Chen nxpwifi_wmm_get_queue_raptr(struct nxpwifi_private *priv, u8 tid,
601*73b01e57SJeff Chen const u8 *ra_addr)
602*73b01e57SJeff Chen {
603*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl *ra_list;
604*73b01e57SJeff Chen
605*73b01e57SJeff Chen ra_list = nxpwifi_wmm_get_ralist_node(priv, tid, ra_addr);
606*73b01e57SJeff Chen if (ra_list)
607*73b01e57SJeff Chen return ra_list;
608*73b01e57SJeff Chen nxpwifi_ralist_add(priv, ra_addr);
609*73b01e57SJeff Chen
610*73b01e57SJeff Chen return nxpwifi_wmm_get_ralist_node(priv, tid, ra_addr);
611*73b01e57SJeff Chen }
612*73b01e57SJeff Chen
613*73b01e57SJeff Chen /* Deletes RA list nodes for given mac for all TIDs. */
614*73b01e57SJeff Chen void
nxpwifi_wmm_del_peer_ra_list(struct nxpwifi_private * priv,const u8 * ra_addr)615*73b01e57SJeff Chen nxpwifi_wmm_del_peer_ra_list(struct nxpwifi_private *priv, const u8 *ra_addr)
616*73b01e57SJeff Chen {
617*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl *ra_list;
618*73b01e57SJeff Chen int i;
619*73b01e57SJeff Chen
620*73b01e57SJeff Chen spin_lock_bh(&priv->wmm.ra_list_spinlock);
621*73b01e57SJeff Chen
622*73b01e57SJeff Chen for (i = 0; i < MAX_NUM_TID; ++i) {
623*73b01e57SJeff Chen ra_list = nxpwifi_wmm_get_ralist_node(priv, i, ra_addr);
624*73b01e57SJeff Chen
625*73b01e57SJeff Chen if (!ra_list)
626*73b01e57SJeff Chen continue;
627*73b01e57SJeff Chen nxpwifi_wmm_del_pkts_in_ralist_node(priv, ra_list);
628*73b01e57SJeff Chen if (ra_list->tx_paused)
629*73b01e57SJeff Chen priv->wmm.pkts_paused[i] -= ra_list->total_pkt_count;
630*73b01e57SJeff Chen else
631*73b01e57SJeff Chen atomic_sub(ra_list->total_pkt_count,
632*73b01e57SJeff Chen &priv->wmm.tx_pkts_queued);
633*73b01e57SJeff Chen list_del(&ra_list->list);
634*73b01e57SJeff Chen kfree(ra_list);
635*73b01e57SJeff Chen }
636*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
637*73b01e57SJeff Chen }
638*73b01e57SJeff Chen
639*73b01e57SJeff Chen /* Checks if a particular RA list node exists in a given TID table index. */
nxpwifi_is_ralist_valid(struct nxpwifi_private * priv,struct nxpwifi_ra_list_tbl * ra_list,int ptr_index)640*73b01e57SJeff Chen bool nxpwifi_is_ralist_valid(struct nxpwifi_private *priv,
641*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl *ra_list, int ptr_index)
642*73b01e57SJeff Chen {
643*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl *rlist;
644*73b01e57SJeff Chen
645*73b01e57SJeff Chen list_for_each_entry(rlist, &priv->wmm.tid_tbl_ptr[ptr_index].ra_list,
646*73b01e57SJeff Chen list) {
647*73b01e57SJeff Chen if (rlist == ra_list)
648*73b01e57SJeff Chen return true;
649*73b01e57SJeff Chen }
650*73b01e57SJeff Chen
651*73b01e57SJeff Chen return false;
652*73b01e57SJeff Chen }
653*73b01e57SJeff Chen
654*73b01e57SJeff Chen /* Adds a packet to bypass TX queue. */
655*73b01e57SJeff Chen void
nxpwifi_wmm_add_buf_bypass_txqueue(struct nxpwifi_private * priv,struct sk_buff * skb)656*73b01e57SJeff Chen nxpwifi_wmm_add_buf_bypass_txqueue(struct nxpwifi_private *priv,
657*73b01e57SJeff Chen struct sk_buff *skb)
658*73b01e57SJeff Chen {
659*73b01e57SJeff Chen skb_queue_tail(&priv->bypass_txq, skb);
660*73b01e57SJeff Chen }
661*73b01e57SJeff Chen
662*73b01e57SJeff Chen /* Adds a packet to WMM queue. */
663*73b01e57SJeff Chen void
nxpwifi_wmm_add_buf_txqueue(struct nxpwifi_private * priv,struct sk_buff * skb)664*73b01e57SJeff Chen nxpwifi_wmm_add_buf_txqueue(struct nxpwifi_private *priv,
665*73b01e57SJeff Chen struct sk_buff *skb)
666*73b01e57SJeff Chen {
667*73b01e57SJeff Chen struct nxpwifi_adapter *adapter = priv->adapter;
668*73b01e57SJeff Chen u32 tid;
669*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl *ra_list = NULL;
670*73b01e57SJeff Chen struct list_head list_head;
671*73b01e57SJeff Chen u8 ra[ETH_ALEN], tid_down;
672*73b01e57SJeff Chen struct ethhdr *eth_hdr = (struct ethhdr *)skb->data;
673*73b01e57SJeff Chen
674*73b01e57SJeff Chen memcpy(ra, eth_hdr->h_dest, ETH_ALEN);
675*73b01e57SJeff Chen
676*73b01e57SJeff Chen if (!priv->media_connected && !nxpwifi_is_skb_mgmt_frame(skb)) {
677*73b01e57SJeff Chen nxpwifi_dbg(adapter, DATA, "data: drop packet in disconnect\n");
678*73b01e57SJeff Chen nxpwifi_write_data_complete(adapter, skb, 0, -1);
679*73b01e57SJeff Chen return;
680*73b01e57SJeff Chen }
681*73b01e57SJeff Chen
682*73b01e57SJeff Chen tid = skb->priority;
683*73b01e57SJeff Chen
684*73b01e57SJeff Chen spin_lock_bh(&priv->wmm.ra_list_spinlock);
685*73b01e57SJeff Chen
686*73b01e57SJeff Chen tid_down = nxpwifi_wmm_downgrade_tid(priv, tid);
687*73b01e57SJeff Chen
688*73b01e57SJeff Chen /*
689*73b01e57SJeff Chen * In case of infra as we have already created the list during
690*73b01e57SJeff Chen * association we just don't have to call get_queue_raptr, we will
691*73b01e57SJeff Chen * have only 1 raptr for a tid in case of infra
692*73b01e57SJeff Chen */
693*73b01e57SJeff Chen if (!nxpwifi_queuing_ra_based(priv) &&
694*73b01e57SJeff Chen !nxpwifi_is_skb_mgmt_frame(skb)) {
695*73b01e57SJeff Chen list_head = priv->wmm.tid_tbl_ptr[tid_down].ra_list;
696*73b01e57SJeff Chen ra_list = list_first_entry_or_null(&list_head,
697*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl,
698*73b01e57SJeff Chen list);
699*73b01e57SJeff Chen } else {
700*73b01e57SJeff Chen memcpy(ra, skb->data, ETH_ALEN);
701*73b01e57SJeff Chen if (is_multicast_ether_addr(ra) ||
702*73b01e57SJeff Chen nxpwifi_is_skb_mgmt_frame(skb))
703*73b01e57SJeff Chen eth_broadcast_addr(ra);
704*73b01e57SJeff Chen ra_list = nxpwifi_wmm_get_queue_raptr(priv, tid_down, ra);
705*73b01e57SJeff Chen }
706*73b01e57SJeff Chen
707*73b01e57SJeff Chen if (!ra_list) {
708*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
709*73b01e57SJeff Chen nxpwifi_write_data_complete(adapter, skb, 0, -1);
710*73b01e57SJeff Chen return;
711*73b01e57SJeff Chen }
712*73b01e57SJeff Chen
713*73b01e57SJeff Chen skb_queue_tail(&ra_list->skb_head, skb);
714*73b01e57SJeff Chen
715*73b01e57SJeff Chen ra_list->ba_pkt_count++;
716*73b01e57SJeff Chen ra_list->total_pkt_count++;
717*73b01e57SJeff Chen
718*73b01e57SJeff Chen if (atomic_read(&priv->wmm.highest_queued_prio) <
719*73b01e57SJeff Chen priv->tos_to_tid_inv[tid_down])
720*73b01e57SJeff Chen atomic_set(&priv->wmm.highest_queued_prio,
721*73b01e57SJeff Chen priv->tos_to_tid_inv[tid_down]);
722*73b01e57SJeff Chen
723*73b01e57SJeff Chen if (ra_list->tx_paused)
724*73b01e57SJeff Chen priv->wmm.pkts_paused[tid_down]++;
725*73b01e57SJeff Chen else
726*73b01e57SJeff Chen atomic_inc(&priv->wmm.tx_pkts_queued);
727*73b01e57SJeff Chen
728*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
729*73b01e57SJeff Chen }
730*73b01e57SJeff Chen
731*73b01e57SJeff Chen /* Processes the get WMM status command response from firmware. */
nxpwifi_ret_wmm_get_status(struct nxpwifi_private * priv,const struct host_cmd_ds_command * resp)732*73b01e57SJeff Chen int nxpwifi_ret_wmm_get_status(struct nxpwifi_private *priv,
733*73b01e57SJeff Chen const struct host_cmd_ds_command *resp)
734*73b01e57SJeff Chen {
735*73b01e57SJeff Chen u8 *curr;
736*73b01e57SJeff Chen u16 resp_len = le16_to_cpu(resp->size), tlv_len;
737*73b01e57SJeff Chen bool valid = true;
738*73b01e57SJeff Chen
739*73b01e57SJeff Chen struct nxpwifi_ie_types_data *tlv_hdr;
740*73b01e57SJeff Chen struct nxpwifi_ie_types_wmm_queue_status *wmm_qs;
741*73b01e57SJeff Chen struct ieee80211_wmm_param_ie *wmm_param_ie = NULL;
742*73b01e57SJeff Chen struct nxpwifi_wmm_ac_status *ac_status;
743*73b01e57SJeff Chen u32 base;
744*73b01e57SJeff Chen
745*73b01e57SJeff Chen nxpwifi_dbg(priv->adapter, INFO,
746*73b01e57SJeff Chen "info: WMM: WMM_GET_STATUS cmdresp received: %d\n",
747*73b01e57SJeff Chen resp_len);
748*73b01e57SJeff Chen
749*73b01e57SJeff Chen base = offsetofend(struct host_cmd_ds_command, params.get_wmm_status);
750*73b01e57SJeff Chen
751*73b01e57SJeff Chen if (resp_len < base)
752*73b01e57SJeff Chen return -EINVAL;
753*73b01e57SJeff Chen
754*73b01e57SJeff Chen curr = (u8 *)&resp->params.get_wmm_status;
755*73b01e57SJeff Chen resp_len -= base;
756*73b01e57SJeff Chen
757*73b01e57SJeff Chen while (resp_len >= sizeof(tlv_hdr->header) && valid) {
758*73b01e57SJeff Chen tlv_hdr = (struct nxpwifi_ie_types_data *)curr;
759*73b01e57SJeff Chen tlv_len = le16_to_cpu(tlv_hdr->header.len);
760*73b01e57SJeff Chen
761*73b01e57SJeff Chen if (resp_len < tlv_len + sizeof(tlv_hdr->header))
762*73b01e57SJeff Chen break;
763*73b01e57SJeff Chen
764*73b01e57SJeff Chen switch (le16_to_cpu(tlv_hdr->header.type)) {
765*73b01e57SJeff Chen case TLV_TYPE_WMMQSTATUS:
766*73b01e57SJeff Chen if (tlv_len <
767*73b01e57SJeff Chen sizeof(struct nxpwifi_ie_types_wmm_queue_status) -
768*73b01e57SJeff Chen sizeof(tlv_hdr->header))
769*73b01e57SJeff Chen break;
770*73b01e57SJeff Chen
771*73b01e57SJeff Chen wmm_qs =
772*73b01e57SJeff Chen (struct nxpwifi_ie_types_wmm_queue_status *)tlv_hdr;
773*73b01e57SJeff Chen
774*73b01e57SJeff Chen if (wmm_qs->queue_index >= IEEE80211_NUM_ACS)
775*73b01e57SJeff Chen break;
776*73b01e57SJeff Chen
777*73b01e57SJeff Chen ac_status = &priv->wmm.ac_status[wmm_qs->queue_index];
778*73b01e57SJeff Chen ac_status->disabled = wmm_qs->disabled;
779*73b01e57SJeff Chen ac_status->flow_required = wmm_qs->flow_required;
780*73b01e57SJeff Chen ac_status->flow_created = wmm_qs->flow_created;
781*73b01e57SJeff Chen break;
782*73b01e57SJeff Chen
783*73b01e57SJeff Chen case WLAN_EID_VENDOR_SPECIFIC:
784*73b01e57SJeff Chen /* Need at least OUI(4) + WMM fixed fields */
785*73b01e57SJeff Chen if (tlv_len + sizeof(tlv_hdr->header) <
786*73b01e57SJeff Chen offsetofend(struct ieee80211_wmm_param_ie,
787*73b01e57SJeff Chen qos_info))
788*73b01e57SJeff Chen break;
789*73b01e57SJeff Chen
790*73b01e57SJeff Chen wmm_param_ie =
791*73b01e57SJeff Chen (struct ieee80211_wmm_param_ie *)(curr + 2);
792*73b01e57SJeff Chen
793*73b01e57SJeff Chen if (tlv_len + 2 > sizeof(struct ieee80211_wmm_param_ie))
794*73b01e57SJeff Chen break;
795*73b01e57SJeff Chen
796*73b01e57SJeff Chen wmm_param_ie->len = (u8)tlv_len;
797*73b01e57SJeff Chen wmm_param_ie->element_id = WLAN_EID_VENDOR_SPECIFIC;
798*73b01e57SJeff Chen
799*73b01e57SJeff Chen memcpy(&priv->curr_bss_params.bss_descriptor.wmm_ie,
800*73b01e57SJeff Chen wmm_param_ie, wmm_param_ie->len + 2);
801*73b01e57SJeff Chen break;
802*73b01e57SJeff Chen
803*73b01e57SJeff Chen default:
804*73b01e57SJeff Chen valid = false;
805*73b01e57SJeff Chen break;
806*73b01e57SJeff Chen }
807*73b01e57SJeff Chen
808*73b01e57SJeff Chen curr += sizeof(tlv_hdr->header) + tlv_len;
809*73b01e57SJeff Chen resp_len -= sizeof(tlv_hdr->header) + tlv_len;
810*73b01e57SJeff Chen }
811*73b01e57SJeff Chen
812*73b01e57SJeff Chen nxpwifi_wmm_setup_queue_priorities(priv, wmm_param_ie);
813*73b01e57SJeff Chen nxpwifi_wmm_setup_ac_downgrade(priv);
814*73b01e57SJeff Chen
815*73b01e57SJeff Chen return 0;
816*73b01e57SJeff Chen }
817*73b01e57SJeff Chen
818*73b01e57SJeff Chen /*
819*73b01e57SJeff Chen * Callback handler from the command module to allow insertion of a WMM TLV.
820*73b01e57SJeff Chen *
821*73b01e57SJeff Chen * If the BSS we are associating to supports WMM, this function adds the
822*73b01e57SJeff Chen * required WMM Information element to the association request command buffer in
823*73b01e57SJeff Chen * the form of a NXP extended IEEE element.
824*73b01e57SJeff Chen */
825*73b01e57SJeff Chen u32
nxpwifi_wmm_process_association_req(struct nxpwifi_private * priv,u8 ** assoc_buf,struct ieee80211_wmm_param_ie * wmm_ie,struct ieee80211_ht_cap * ht_cap)826*73b01e57SJeff Chen nxpwifi_wmm_process_association_req(struct nxpwifi_private *priv,
827*73b01e57SJeff Chen u8 **assoc_buf,
828*73b01e57SJeff Chen struct ieee80211_wmm_param_ie *wmm_ie,
829*73b01e57SJeff Chen struct ieee80211_ht_cap *ht_cap)
830*73b01e57SJeff Chen {
831*73b01e57SJeff Chen struct nxpwifi_ie_types_wmm_param_set *wmm_tlv;
832*73b01e57SJeff Chen u32 ret_len = 0;
833*73b01e57SJeff Chen
834*73b01e57SJeff Chen /* Null checks */
835*73b01e57SJeff Chen if (!assoc_buf)
836*73b01e57SJeff Chen return 0;
837*73b01e57SJeff Chen if (!(*assoc_buf))
838*73b01e57SJeff Chen return 0;
839*73b01e57SJeff Chen
840*73b01e57SJeff Chen if (!wmm_ie)
841*73b01e57SJeff Chen return 0;
842*73b01e57SJeff Chen
843*73b01e57SJeff Chen nxpwifi_dbg(priv->adapter, INFO,
844*73b01e57SJeff Chen "info: WMM: process assoc req: bss->wmm_ie=%#x\n",
845*73b01e57SJeff Chen wmm_ie->element_id);
846*73b01e57SJeff Chen
847*73b01e57SJeff Chen if ((priv->wmm_required ||
848*73b01e57SJeff Chen (ht_cap && (priv->config_bands & BAND_GN ||
849*73b01e57SJeff Chen priv->config_bands & BAND_AN))) &&
850*73b01e57SJeff Chen wmm_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) {
851*73b01e57SJeff Chen wmm_tlv = (struct nxpwifi_ie_types_wmm_param_set *)*assoc_buf;
852*73b01e57SJeff Chen wmm_tlv->header.type = cpu_to_le16((u16)wmm_info_ie[0]);
853*73b01e57SJeff Chen wmm_tlv->header.len = cpu_to_le16((u16)wmm_info_ie[1]);
854*73b01e57SJeff Chen memcpy(wmm_tlv->wmm_ie, &wmm_info_ie[2],
855*73b01e57SJeff Chen le16_to_cpu(wmm_tlv->header.len));
856*73b01e57SJeff Chen if (wmm_ie->qos_info & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD)
857*73b01e57SJeff Chen memcpy((u8 *)(wmm_tlv->wmm_ie
858*73b01e57SJeff Chen + le16_to_cpu(wmm_tlv->header.len)
859*73b01e57SJeff Chen - sizeof(priv->wmm_qosinfo)),
860*73b01e57SJeff Chen &priv->wmm_qosinfo, sizeof(priv->wmm_qosinfo));
861*73b01e57SJeff Chen
862*73b01e57SJeff Chen ret_len = sizeof(wmm_tlv->header)
863*73b01e57SJeff Chen + le16_to_cpu(wmm_tlv->header.len);
864*73b01e57SJeff Chen
865*73b01e57SJeff Chen *assoc_buf += ret_len;
866*73b01e57SJeff Chen }
867*73b01e57SJeff Chen
868*73b01e57SJeff Chen return ret_len;
869*73b01e57SJeff Chen }
870*73b01e57SJeff Chen
871*73b01e57SJeff Chen /* Computes the time delay in the driver queues for a given packet. */
872*73b01e57SJeff Chen u8
nxpwifi_wmm_compute_drv_pkt_delay(struct nxpwifi_private * priv,const struct sk_buff * skb)873*73b01e57SJeff Chen nxpwifi_wmm_compute_drv_pkt_delay(struct nxpwifi_private *priv,
874*73b01e57SJeff Chen const struct sk_buff *skb)
875*73b01e57SJeff Chen {
876*73b01e57SJeff Chen u32 queue_delay = ktime_to_ms(net_timedelta(skb->tstamp));
877*73b01e57SJeff Chen u8 ret_val;
878*73b01e57SJeff Chen
879*73b01e57SJeff Chen /*
880*73b01e57SJeff Chen * Queue delay is passed as a uint8 in units of 2ms (ms shifted
881*73b01e57SJeff Chen * by 1). Min value (other than 0) is therefore 2ms, max is 510ms.
882*73b01e57SJeff Chen *
883*73b01e57SJeff Chen * Pass max value if queue_delay is beyond the uint8 range
884*73b01e57SJeff Chen */
885*73b01e57SJeff Chen ret_val = (u8)(min(queue_delay, priv->wmm.drv_pkt_delay_max) >> 1);
886*73b01e57SJeff Chen
887*73b01e57SJeff Chen nxpwifi_dbg(priv->adapter, DATA, "data: WMM: Pkt Delay: %d ms,\t"
888*73b01e57SJeff Chen "%d ms sent to FW\n", queue_delay, ret_val);
889*73b01e57SJeff Chen
890*73b01e57SJeff Chen return ret_val;
891*73b01e57SJeff Chen }
892*73b01e57SJeff Chen
893*73b01e57SJeff Chen /* Retrieves the highest priority RA list table pointer. */
894*73b01e57SJeff Chen static struct nxpwifi_ra_list_tbl *
nxpwifi_wmm_get_highest_priolist_ptr(struct nxpwifi_adapter * adapter,struct nxpwifi_private ** priv,int * tid)895*73b01e57SJeff Chen nxpwifi_wmm_get_highest_priolist_ptr(struct nxpwifi_adapter *adapter,
896*73b01e57SJeff Chen struct nxpwifi_private **priv, int *tid)
897*73b01e57SJeff Chen {
898*73b01e57SJeff Chen struct nxpwifi_private *priv_tmp;
899*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl *ptr;
900*73b01e57SJeff Chen struct nxpwifi_tid_tbl *tid_ptr;
901*73b01e57SJeff Chen atomic_t *hqp;
902*73b01e57SJeff Chen int i, j;
903*73b01e57SJeff Chen u8 to_tid;
904*73b01e57SJeff Chen
905*73b01e57SJeff Chen /* check the BSS with highest priority first */
906*73b01e57SJeff Chen for (j = adapter->priv_num - 1; j >= 0; --j) {
907*73b01e57SJeff Chen /* iterate over BSS with the equal priority */
908*73b01e57SJeff Chen list_for_each_entry(adapter->bss_prio_tbl[j].bss_prio_cur,
909*73b01e57SJeff Chen &adapter->bss_prio_tbl[j].bss_prio_head,
910*73b01e57SJeff Chen list) {
911*73b01e57SJeff Chen try_again:
912*73b01e57SJeff Chen priv_tmp = adapter->bss_prio_tbl[j].bss_prio_cur->priv;
913*73b01e57SJeff Chen
914*73b01e57SJeff Chen if (!priv_tmp->port_open ||
915*73b01e57SJeff Chen (atomic_read(&priv_tmp->wmm.tx_pkts_queued) == 0))
916*73b01e57SJeff Chen continue;
917*73b01e57SJeff Chen
918*73b01e57SJeff Chen /* iterate over the WMM queues of the BSS */
919*73b01e57SJeff Chen hqp = &priv_tmp->wmm.highest_queued_prio;
920*73b01e57SJeff Chen for (i = atomic_read(hqp); i >= LOW_PRIO_TID; --i) {
921*73b01e57SJeff Chen spin_lock_bh(&priv_tmp->wmm.ra_list_spinlock);
922*73b01e57SJeff Chen
923*73b01e57SJeff Chen to_tid = tos_to_tid[i];
924*73b01e57SJeff Chen tid_ptr = &(priv_tmp)->wmm.tid_tbl_ptr[to_tid];
925*73b01e57SJeff Chen
926*73b01e57SJeff Chen /* iterate over receiver addresses */
927*73b01e57SJeff Chen list_for_each_entry(ptr, &tid_ptr->ra_list,
928*73b01e57SJeff Chen list) {
929*73b01e57SJeff Chen if (!ptr->tx_paused &&
930*73b01e57SJeff Chen !skb_queue_empty(&ptr->skb_head))
931*73b01e57SJeff Chen /* holds both locks */
932*73b01e57SJeff Chen goto found;
933*73b01e57SJeff Chen }
934*73b01e57SJeff Chen
935*73b01e57SJeff Chen spin_unlock_bh(&priv_tmp->wmm.ra_list_spinlock);
936*73b01e57SJeff Chen }
937*73b01e57SJeff Chen
938*73b01e57SJeff Chen if (atomic_read(&priv_tmp->wmm.tx_pkts_queued) != 0) {
939*73b01e57SJeff Chen atomic_set(&priv_tmp->wmm.highest_queued_prio,
940*73b01e57SJeff Chen HIGH_PRIO_TID);
941*73b01e57SJeff Chen /*
942*73b01e57SJeff Chen * Iterate current private once more, since
943*73b01e57SJeff Chen * there still exist packets in data queue
944*73b01e57SJeff Chen */
945*73b01e57SJeff Chen goto try_again;
946*73b01e57SJeff Chen } else {
947*73b01e57SJeff Chen atomic_set(&priv_tmp->wmm.highest_queued_prio,
948*73b01e57SJeff Chen NO_PKT_PRIO_TID);
949*73b01e57SJeff Chen }
950*73b01e57SJeff Chen }
951*73b01e57SJeff Chen }
952*73b01e57SJeff Chen
953*73b01e57SJeff Chen return NULL;
954*73b01e57SJeff Chen
955*73b01e57SJeff Chen found:
956*73b01e57SJeff Chen /* holds ra_list_spinlock */
957*73b01e57SJeff Chen if (atomic_read(hqp) > i)
958*73b01e57SJeff Chen atomic_set(hqp, i);
959*73b01e57SJeff Chen spin_unlock_bh(&priv_tmp->wmm.ra_list_spinlock);
960*73b01e57SJeff Chen
961*73b01e57SJeff Chen *priv = priv_tmp;
962*73b01e57SJeff Chen *tid = tos_to_tid[i];
963*73b01e57SJeff Chen
964*73b01e57SJeff Chen return ptr;
965*73b01e57SJeff Chen }
966*73b01e57SJeff Chen
967*73b01e57SJeff Chen /* Rotates ra and bss lists so packets are picked round robin. */
nxpwifi_rotate_priolists(struct nxpwifi_private * priv,struct nxpwifi_ra_list_tbl * ra,int tid)968*73b01e57SJeff Chen void nxpwifi_rotate_priolists(struct nxpwifi_private *priv,
969*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl *ra,
970*73b01e57SJeff Chen int tid)
971*73b01e57SJeff Chen {
972*73b01e57SJeff Chen struct nxpwifi_adapter *adapter = priv->adapter;
973*73b01e57SJeff Chen struct nxpwifi_bss_prio_tbl *tbl = adapter->bss_prio_tbl;
974*73b01e57SJeff Chen struct nxpwifi_tid_tbl *tid_ptr = &priv->wmm.tid_tbl_ptr[tid];
975*73b01e57SJeff Chen
976*73b01e57SJeff Chen spin_lock_bh(&tbl[priv->bss_priority].bss_prio_lock);
977*73b01e57SJeff Chen /*
978*73b01e57SJeff Chen * dirty trick: we remove 'head' temporarily and reinsert it after
979*73b01e57SJeff Chen * curr bss node. imagine list to stay fixed while head is moved
980*73b01e57SJeff Chen */
981*73b01e57SJeff Chen list_move(&tbl[priv->bss_priority].bss_prio_head,
982*73b01e57SJeff Chen &tbl[priv->bss_priority].bss_prio_cur->list);
983*73b01e57SJeff Chen spin_unlock_bh(&tbl[priv->bss_priority].bss_prio_lock);
984*73b01e57SJeff Chen
985*73b01e57SJeff Chen spin_lock_bh(&priv->wmm.ra_list_spinlock);
986*73b01e57SJeff Chen if (nxpwifi_is_ralist_valid(priv, ra, tid)) {
987*73b01e57SJeff Chen priv->wmm.packets_out[tid]++;
988*73b01e57SJeff Chen /* same as above */
989*73b01e57SJeff Chen list_move(&tid_ptr->ra_list, &ra->list);
990*73b01e57SJeff Chen }
991*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
992*73b01e57SJeff Chen }
993*73b01e57SJeff Chen
994*73b01e57SJeff Chen /* Checks if 11n aggregation is possible. */
995*73b01e57SJeff Chen static bool
nxpwifi_is_11n_aggragation_possible(struct nxpwifi_private * priv,struct nxpwifi_ra_list_tbl * ptr,int max_buf_size)996*73b01e57SJeff Chen nxpwifi_is_11n_aggragation_possible(struct nxpwifi_private *priv,
997*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl *ptr,
998*73b01e57SJeff Chen int max_buf_size)
999*73b01e57SJeff Chen {
1000*73b01e57SJeff Chen int count = 0, total_size = 0;
1001*73b01e57SJeff Chen struct sk_buff *skb, *tmp;
1002*73b01e57SJeff Chen int max_amsdu_size;
1003*73b01e57SJeff Chen
1004*73b01e57SJeff Chen if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP && priv->ap_11n_enabled &&
1005*73b01e57SJeff Chen ptr->is_11n_enabled)
1006*73b01e57SJeff Chen max_amsdu_size = min_t(int, ptr->max_amsdu, max_buf_size);
1007*73b01e57SJeff Chen else
1008*73b01e57SJeff Chen max_amsdu_size = max_buf_size;
1009*73b01e57SJeff Chen
1010*73b01e57SJeff Chen skb_queue_walk_safe(&ptr->skb_head, skb, tmp) {
1011*73b01e57SJeff Chen total_size += skb->len;
1012*73b01e57SJeff Chen if (total_size >= max_amsdu_size)
1013*73b01e57SJeff Chen break;
1014*73b01e57SJeff Chen if (++count >= MIN_NUM_AMSDU)
1015*73b01e57SJeff Chen return true;
1016*73b01e57SJeff Chen }
1017*73b01e57SJeff Chen
1018*73b01e57SJeff Chen return false;
1019*73b01e57SJeff Chen }
1020*73b01e57SJeff Chen
1021*73b01e57SJeff Chen /* Sends a single packet to firmware for transmission. */
1022*73b01e57SJeff Chen static void
nxpwifi_send_single_packet(struct nxpwifi_private * priv,struct nxpwifi_ra_list_tbl * ptr,int ptr_index)1023*73b01e57SJeff Chen nxpwifi_send_single_packet(struct nxpwifi_private *priv,
1024*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl *ptr, int ptr_index)
1025*73b01e57SJeff Chen __releases(&priv->wmm.ra_list_spinlock)
1026*73b01e57SJeff Chen {
1027*73b01e57SJeff Chen struct sk_buff *skb, *skb_next;
1028*73b01e57SJeff Chen struct nxpwifi_tx_param tx_param;
1029*73b01e57SJeff Chen struct nxpwifi_adapter *adapter = priv->adapter;
1030*73b01e57SJeff Chen struct nxpwifi_txinfo *tx_info;
1031*73b01e57SJeff Chen
1032*73b01e57SJeff Chen if (skb_queue_empty(&ptr->skb_head)) {
1033*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
1034*73b01e57SJeff Chen nxpwifi_dbg(adapter, DATA, "data: nothing to send\n");
1035*73b01e57SJeff Chen return;
1036*73b01e57SJeff Chen }
1037*73b01e57SJeff Chen
1038*73b01e57SJeff Chen skb = skb_dequeue(&ptr->skb_head);
1039*73b01e57SJeff Chen
1040*73b01e57SJeff Chen tx_info = NXPWIFI_SKB_TXCB(skb);
1041*73b01e57SJeff Chen nxpwifi_dbg(adapter, DATA,
1042*73b01e57SJeff Chen "data: dequeuing the packet %p %p\n", ptr, skb);
1043*73b01e57SJeff Chen
1044*73b01e57SJeff Chen ptr->total_pkt_count--;
1045*73b01e57SJeff Chen
1046*73b01e57SJeff Chen if (!skb_queue_empty(&ptr->skb_head))
1047*73b01e57SJeff Chen skb_next = skb_peek(&ptr->skb_head);
1048*73b01e57SJeff Chen else
1049*73b01e57SJeff Chen skb_next = NULL;
1050*73b01e57SJeff Chen
1051*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
1052*73b01e57SJeff Chen
1053*73b01e57SJeff Chen tx_param.next_pkt_len = ((skb_next) ? skb_next->len +
1054*73b01e57SJeff Chen sizeof(struct txpd) : 0);
1055*73b01e57SJeff Chen
1056*73b01e57SJeff Chen if (nxpwifi_process_tx(priv, skb, &tx_param) == -EBUSY) {
1057*73b01e57SJeff Chen /* Queue the packet back at the head */
1058*73b01e57SJeff Chen spin_lock_bh(&priv->wmm.ra_list_spinlock);
1059*73b01e57SJeff Chen
1060*73b01e57SJeff Chen if (!nxpwifi_is_ralist_valid(priv, ptr, ptr_index)) {
1061*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
1062*73b01e57SJeff Chen nxpwifi_write_data_complete(adapter, skb, 0, -1);
1063*73b01e57SJeff Chen return;
1064*73b01e57SJeff Chen }
1065*73b01e57SJeff Chen
1066*73b01e57SJeff Chen skb_queue_tail(&ptr->skb_head, skb);
1067*73b01e57SJeff Chen
1068*73b01e57SJeff Chen ptr->total_pkt_count++;
1069*73b01e57SJeff Chen ptr->ba_pkt_count++;
1070*73b01e57SJeff Chen tx_info->flags |= NXPWIFI_BUF_FLAG_REQUEUED_PKT;
1071*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
1072*73b01e57SJeff Chen } else {
1073*73b01e57SJeff Chen nxpwifi_rotate_priolists(priv, ptr, ptr_index);
1074*73b01e57SJeff Chen atomic_dec(&priv->wmm.tx_pkts_queued);
1075*73b01e57SJeff Chen }
1076*73b01e57SJeff Chen }
1077*73b01e57SJeff Chen
1078*73b01e57SJeff Chen /* Checks if the first packet in the given RA list is already processed or not. */
1079*73b01e57SJeff Chen static bool
nxpwifi_is_ptr_processed(struct nxpwifi_private * priv,struct nxpwifi_ra_list_tbl * ptr)1080*73b01e57SJeff Chen nxpwifi_is_ptr_processed(struct nxpwifi_private *priv,
1081*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl *ptr)
1082*73b01e57SJeff Chen {
1083*73b01e57SJeff Chen struct sk_buff *skb;
1084*73b01e57SJeff Chen struct nxpwifi_txinfo *tx_info;
1085*73b01e57SJeff Chen
1086*73b01e57SJeff Chen if (skb_queue_empty(&ptr->skb_head))
1087*73b01e57SJeff Chen return false;
1088*73b01e57SJeff Chen
1089*73b01e57SJeff Chen skb = skb_peek(&ptr->skb_head);
1090*73b01e57SJeff Chen
1091*73b01e57SJeff Chen tx_info = NXPWIFI_SKB_TXCB(skb);
1092*73b01e57SJeff Chen if (tx_info->flags & NXPWIFI_BUF_FLAG_REQUEUED_PKT)
1093*73b01e57SJeff Chen return true;
1094*73b01e57SJeff Chen
1095*73b01e57SJeff Chen return false;
1096*73b01e57SJeff Chen }
1097*73b01e57SJeff Chen
1098*73b01e57SJeff Chen /* Sends a single processed packet to firmware for transmission. */
1099*73b01e57SJeff Chen static void
nxpwifi_send_processed_packet(struct nxpwifi_private * priv,struct nxpwifi_ra_list_tbl * ptr,int ptr_index)1100*73b01e57SJeff Chen nxpwifi_send_processed_packet(struct nxpwifi_private *priv,
1101*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl *ptr, int ptr_index)
1102*73b01e57SJeff Chen __releases(&priv->wmm.ra_list_spinlock)
1103*73b01e57SJeff Chen {
1104*73b01e57SJeff Chen struct nxpwifi_tx_param tx_param;
1105*73b01e57SJeff Chen struct nxpwifi_adapter *adapter = priv->adapter;
1106*73b01e57SJeff Chen int ret;
1107*73b01e57SJeff Chen struct sk_buff *skb, *skb_next;
1108*73b01e57SJeff Chen struct nxpwifi_txinfo *tx_info;
1109*73b01e57SJeff Chen
1110*73b01e57SJeff Chen if (skb_queue_empty(&ptr->skb_head)) {
1111*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
1112*73b01e57SJeff Chen return;
1113*73b01e57SJeff Chen }
1114*73b01e57SJeff Chen
1115*73b01e57SJeff Chen skb = skb_dequeue(&ptr->skb_head);
1116*73b01e57SJeff Chen
1117*73b01e57SJeff Chen if (adapter->data_sent || adapter->tx_lock_flag) {
1118*73b01e57SJeff Chen ptr->total_pkt_count--;
1119*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
1120*73b01e57SJeff Chen skb_queue_tail(&adapter->tx_data_q, skb);
1121*73b01e57SJeff Chen atomic_dec(&priv->wmm.tx_pkts_queued);
1122*73b01e57SJeff Chen atomic_inc(&adapter->tx_queued);
1123*73b01e57SJeff Chen return;
1124*73b01e57SJeff Chen }
1125*73b01e57SJeff Chen
1126*73b01e57SJeff Chen if (!skb_queue_empty(&ptr->skb_head))
1127*73b01e57SJeff Chen skb_next = skb_peek(&ptr->skb_head);
1128*73b01e57SJeff Chen else
1129*73b01e57SJeff Chen skb_next = NULL;
1130*73b01e57SJeff Chen
1131*73b01e57SJeff Chen tx_info = NXPWIFI_SKB_TXCB(skb);
1132*73b01e57SJeff Chen
1133*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
1134*73b01e57SJeff Chen
1135*73b01e57SJeff Chen tx_param.next_pkt_len =
1136*73b01e57SJeff Chen ((skb_next) ? skb_next->len +
1137*73b01e57SJeff Chen sizeof(struct txpd) : 0);
1138*73b01e57SJeff Chen
1139*73b01e57SJeff Chen ret = adapter->if_ops.host_to_card(adapter, NXPWIFI_TYPE_DATA,
1140*73b01e57SJeff Chen skb, &tx_param);
1141*73b01e57SJeff Chen
1142*73b01e57SJeff Chen switch (ret) {
1143*73b01e57SJeff Chen case -EBUSY:
1144*73b01e57SJeff Chen nxpwifi_dbg(adapter, ERROR, "data: -EBUSY is returned\n");
1145*73b01e57SJeff Chen spin_lock_bh(&priv->wmm.ra_list_spinlock);
1146*73b01e57SJeff Chen
1147*73b01e57SJeff Chen if (!nxpwifi_is_ralist_valid(priv, ptr, ptr_index)) {
1148*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
1149*73b01e57SJeff Chen nxpwifi_write_data_complete(adapter, skb, 0, -1);
1150*73b01e57SJeff Chen return;
1151*73b01e57SJeff Chen }
1152*73b01e57SJeff Chen
1153*73b01e57SJeff Chen skb_queue_tail(&ptr->skb_head, skb);
1154*73b01e57SJeff Chen
1155*73b01e57SJeff Chen tx_info->flags |= NXPWIFI_BUF_FLAG_REQUEUED_PKT;
1156*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
1157*73b01e57SJeff Chen break;
1158*73b01e57SJeff Chen case -EINPROGRESS:
1159*73b01e57SJeff Chen break;
1160*73b01e57SJeff Chen case 0:
1161*73b01e57SJeff Chen nxpwifi_write_data_complete(adapter, skb, 0, ret);
1162*73b01e57SJeff Chen break;
1163*73b01e57SJeff Chen default:
1164*73b01e57SJeff Chen nxpwifi_dbg(adapter, ERROR, "host_to_card failed: %#x\n", ret);
1165*73b01e57SJeff Chen adapter->dbg.num_tx_host_to_card_failure++;
1166*73b01e57SJeff Chen nxpwifi_write_data_complete(adapter, skb, 0, ret);
1167*73b01e57SJeff Chen break;
1168*73b01e57SJeff Chen }
1169*73b01e57SJeff Chen
1170*73b01e57SJeff Chen if (ret != -EBUSY) {
1171*73b01e57SJeff Chen nxpwifi_rotate_priolists(priv, ptr, ptr_index);
1172*73b01e57SJeff Chen atomic_dec(&priv->wmm.tx_pkts_queued);
1173*73b01e57SJeff Chen spin_lock_bh(&priv->wmm.ra_list_spinlock);
1174*73b01e57SJeff Chen ptr->total_pkt_count--;
1175*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
1176*73b01e57SJeff Chen }
1177*73b01e57SJeff Chen }
1178*73b01e57SJeff Chen
1179*73b01e57SJeff Chen /* Dequeues a packet from the highest priority list and transmits it. */
1180*73b01e57SJeff Chen static int
nxpwifi_dequeue_tx_packet(struct nxpwifi_adapter * adapter)1181*73b01e57SJeff Chen nxpwifi_dequeue_tx_packet(struct nxpwifi_adapter *adapter)
1182*73b01e57SJeff Chen {
1183*73b01e57SJeff Chen struct nxpwifi_ra_list_tbl *ptr;
1184*73b01e57SJeff Chen struct nxpwifi_private *priv = NULL;
1185*73b01e57SJeff Chen int ptr_index = 0;
1186*73b01e57SJeff Chen u8 ra[ETH_ALEN];
1187*73b01e57SJeff Chen int tid_del = 0, tid = 0;
1188*73b01e57SJeff Chen
1189*73b01e57SJeff Chen ptr = nxpwifi_wmm_get_highest_priolist_ptr(adapter, &priv, &ptr_index);
1190*73b01e57SJeff Chen if (!ptr)
1191*73b01e57SJeff Chen return -ENOENT;
1192*73b01e57SJeff Chen
1193*73b01e57SJeff Chen tid = nxpwifi_get_tid(ptr);
1194*73b01e57SJeff Chen
1195*73b01e57SJeff Chen nxpwifi_dbg(adapter, DATA, "data: tid=%d\n", tid);
1196*73b01e57SJeff Chen
1197*73b01e57SJeff Chen spin_lock_bh(&priv->wmm.ra_list_spinlock);
1198*73b01e57SJeff Chen if (!nxpwifi_is_ralist_valid(priv, ptr, ptr_index)) {
1199*73b01e57SJeff Chen spin_unlock_bh(&priv->wmm.ra_list_spinlock);
1200*73b01e57SJeff Chen return -EINVAL;
1201*73b01e57SJeff Chen }
1202*73b01e57SJeff Chen
1203*73b01e57SJeff Chen if (nxpwifi_is_ptr_processed(priv, ptr)) {
1204*73b01e57SJeff Chen nxpwifi_send_processed_packet(priv, ptr, ptr_index);
1205*73b01e57SJeff Chen /*
1206*73b01e57SJeff Chen * ra_list_spinlock has been freed in
1207*73b01e57SJeff Chen * nxpwifi_send_processed_packet()
1208*73b01e57SJeff Chen */
1209*73b01e57SJeff Chen return 0;
1210*73b01e57SJeff Chen }
1211*73b01e57SJeff Chen
1212*73b01e57SJeff Chen if (!ptr->is_11n_enabled ||
1213*73b01e57SJeff Chen ptr->ba_status ||
1214*73b01e57SJeff Chen priv->wps.session_enable) {
1215*73b01e57SJeff Chen if (ptr->is_11n_enabled &&
1216*73b01e57SJeff Chen ptr->ba_status &&
1217*73b01e57SJeff Chen ptr->amsdu_in_ampdu &&
1218*73b01e57SJeff Chen nxpwifi_is_amsdu_allowed(priv, tid) &&
1219*73b01e57SJeff Chen nxpwifi_is_11n_aggragation_possible(priv, ptr,
1220*73b01e57SJeff Chen adapter->tx_buf_size))
1221*73b01e57SJeff Chen nxpwifi_11n_aggregate_pkt(priv, ptr, ptr_index);
1222*73b01e57SJeff Chen /*
1223*73b01e57SJeff Chen * ra_list_spinlock has been freed in
1224*73b01e57SJeff Chen * nxpwifi_11n_aggregate_pkt()
1225*73b01e57SJeff Chen */
1226*73b01e57SJeff Chen else
1227*73b01e57SJeff Chen nxpwifi_send_single_packet(priv, ptr, ptr_index);
1228*73b01e57SJeff Chen /*
1229*73b01e57SJeff Chen * ra_list_spinlock has been freed in
1230*73b01e57SJeff Chen * nxpwifi_send_single_packet()
1231*73b01e57SJeff Chen */
1232*73b01e57SJeff Chen } else {
1233*73b01e57SJeff Chen if (nxpwifi_is_ampdu_allowed(priv, ptr, tid) &&
1234*73b01e57SJeff Chen ptr->ba_pkt_count > ptr->ba_packet_thr) {
1235*73b01e57SJeff Chen if (nxpwifi_space_avail_for_new_ba_stream(adapter)) {
1236*73b01e57SJeff Chen nxpwifi_create_ba_tbl(priv, ptr->ra, tid,
1237*73b01e57SJeff Chen BA_SETUP_INPROGRESS);
1238*73b01e57SJeff Chen nxpwifi_send_addba(priv, tid, ptr->ra);
1239*73b01e57SJeff Chen } else if (nxpwifi_find_stream_to_delete
1240*73b01e57SJeff Chen (priv, tid, &tid_del, ra)) {
1241*73b01e57SJeff Chen nxpwifi_create_ba_tbl(priv, ptr->ra, tid,
1242*73b01e57SJeff Chen BA_SETUP_INPROGRESS);
1243*73b01e57SJeff Chen nxpwifi_send_delba(priv, tid_del, ra, 1);
1244*73b01e57SJeff Chen }
1245*73b01e57SJeff Chen }
1246*73b01e57SJeff Chen if (nxpwifi_is_amsdu_allowed(priv, tid) &&
1247*73b01e57SJeff Chen nxpwifi_is_11n_aggragation_possible(priv, ptr,
1248*73b01e57SJeff Chen adapter->tx_buf_size))
1249*73b01e57SJeff Chen nxpwifi_11n_aggregate_pkt(priv, ptr, ptr_index);
1250*73b01e57SJeff Chen /*
1251*73b01e57SJeff Chen * ra_list_spinlock has been freed in
1252*73b01e57SJeff Chen * nxpwifi_11n_aggregate_pkt()
1253*73b01e57SJeff Chen */
1254*73b01e57SJeff Chen else
1255*73b01e57SJeff Chen nxpwifi_send_single_packet(priv, ptr, ptr_index);
1256*73b01e57SJeff Chen /*
1257*73b01e57SJeff Chen * ra_list_spinlock has been freed in
1258*73b01e57SJeff Chen * nxpwifi_send_single_packet()
1259*73b01e57SJeff Chen */
1260*73b01e57SJeff Chen }
1261*73b01e57SJeff Chen return 0;
1262*73b01e57SJeff Chen }
1263*73b01e57SJeff Chen
nxpwifi_process_bypass_tx(struct nxpwifi_adapter * adapter)1264*73b01e57SJeff Chen void nxpwifi_process_bypass_tx(struct nxpwifi_adapter *adapter)
1265*73b01e57SJeff Chen {
1266*73b01e57SJeff Chen struct nxpwifi_tx_param tx_param;
1267*73b01e57SJeff Chen struct sk_buff *skb;
1268*73b01e57SJeff Chen struct nxpwifi_txinfo *tx_info;
1269*73b01e57SJeff Chen struct nxpwifi_private *priv;
1270*73b01e57SJeff Chen int i;
1271*73b01e57SJeff Chen
1272*73b01e57SJeff Chen if (adapter->data_sent || adapter->tx_lock_flag)
1273*73b01e57SJeff Chen return;
1274*73b01e57SJeff Chen
1275*73b01e57SJeff Chen for (i = 0; i < adapter->priv_num; ++i) {
1276*73b01e57SJeff Chen priv = adapter->priv[i];
1277*73b01e57SJeff Chen
1278*73b01e57SJeff Chen if (skb_queue_empty(&priv->bypass_txq))
1279*73b01e57SJeff Chen continue;
1280*73b01e57SJeff Chen
1281*73b01e57SJeff Chen skb = skb_dequeue(&priv->bypass_txq);
1282*73b01e57SJeff Chen tx_info = NXPWIFI_SKB_TXCB(skb);
1283*73b01e57SJeff Chen
1284*73b01e57SJeff Chen /* no aggregation for bypass packets */
1285*73b01e57SJeff Chen tx_param.next_pkt_len = 0;
1286*73b01e57SJeff Chen
1287*73b01e57SJeff Chen if (nxpwifi_process_tx(priv, skb, &tx_param) == -EBUSY) {
1288*73b01e57SJeff Chen skb_queue_head(&priv->bypass_txq, skb);
1289*73b01e57SJeff Chen tx_info->flags |= NXPWIFI_BUF_FLAG_REQUEUED_PKT;
1290*73b01e57SJeff Chen } else {
1291*73b01e57SJeff Chen atomic_dec(&adapter->bypass_tx_pending);
1292*73b01e57SJeff Chen }
1293*73b01e57SJeff Chen }
1294*73b01e57SJeff Chen }
1295*73b01e57SJeff Chen
1296*73b01e57SJeff Chen /* Transmits the highest priority packet awaiting in the WMM Queues. */
1297*73b01e57SJeff Chen void
nxpwifi_wmm_process_tx(struct nxpwifi_adapter * adapter)1298*73b01e57SJeff Chen nxpwifi_wmm_process_tx(struct nxpwifi_adapter *adapter)
1299*73b01e57SJeff Chen {
1300*73b01e57SJeff Chen do {
1301*73b01e57SJeff Chen if (nxpwifi_dequeue_tx_packet(adapter))
1302*73b01e57SJeff Chen break;
1303*73b01e57SJeff Chen if (adapter->iface_type != NXPWIFI_SDIO) {
1304*73b01e57SJeff Chen if (adapter->data_sent ||
1305*73b01e57SJeff Chen adapter->tx_lock_flag)
1306*73b01e57SJeff Chen break;
1307*73b01e57SJeff Chen } else {
1308*73b01e57SJeff Chen if (atomic_read(&adapter->tx_queued) >=
1309*73b01e57SJeff Chen NXPWIFI_MAX_PKTS_TXQ)
1310*73b01e57SJeff Chen break;
1311*73b01e57SJeff Chen }
1312*73b01e57SJeff Chen } while (!nxpwifi_wmm_lists_empty(adapter));
1313*73b01e57SJeff Chen }
1314*73b01e57SJeff Chen
nxpwifi_wmm_init_tos_to_tid_inv(struct nxpwifi_private * priv)1315*73b01e57SJeff Chen void nxpwifi_wmm_init_tos_to_tid_inv(struct nxpwifi_private *priv)
1316*73b01e57SJeff Chen {
1317*73b01e57SJeff Chen memcpy(priv->tos_to_tid_inv, tos_to_tid_inv, sizeof(priv->tos_to_tid_inv));
1318*73b01e57SJeff Chen }
1319