1 /*
2 * Copyright (c) 2013-2021 The Linux Foundation. All rights reserved.
3 * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for
6 * any purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
8 * copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 /**
21 * DOC: HDD WMM
22 *
23 * This module (wlan_hdd_wmm.h interface + wlan_hdd_wmm.c implementation)
24 * houses all the logic for WMM in HDD.
25 *
26 * On the control path, it has the logic to setup QoS, modify QoS and delete
27 * QoS (QoS here refers to a TSPEC). The setup QoS comes in two flavors: an
28 * explicit application invoked and an internal HDD invoked. The implicit QoS
29 * is for applications that do NOT call the custom QCT WLAN OIDs for QoS but
30 * which DO mark their traffic for priortization. It also has logic to start,
31 * update and stop the U-APSD trigger frame generation. It also has logic to
32 * read WMM related config parameters from the registry.
33 *
34 * On the data path, it has the logic to figure out the WMM AC of an egress
35 * packet and when to signal TL to serve a particular AC queue. It also has the
36 * logic to retrieve a packet based on WMM priority in response to a fetch from
37 * TL.
38 *
39 * The remaining functions are utility functions for information hiding.
40 */
41
42 /* Include files */
43 #include <linux/netdevice.h>
44 #include <linux/skbuff.h>
45 #include <linux/etherdevice.h>
46 #include <linux/if_vlan.h>
47 #include <linux/ip.h>
48 #include <linux/semaphore.h>
49 #include <linux/ipv6.h>
50 #include "osif_sync.h"
51 #include "os_if_fwol.h"
52 #include <wlan_hdd_tx_rx.h>
53 #include <wlan_hdd_wmm.h>
54 #include <wlan_hdd_ether.h>
55 #include <wlan_hdd_hostapd.h>
56 #include <wlan_hdd_softap_tx_rx.h>
57 #include <cds_sched.h>
58 #include "sme_api.h"
59 #include "wlan_mlme_ucfg_api.h"
60 #include "cfg_ucfg_api.h"
61 #include "wlan_hdd_object_manager.h"
62 #include "wlan_hdd_cm_api.h"
63 #include "wlan_dp_ucfg_api.h"
64
65 #define HDD_WMM_UP_TO_AC_MAP_SIZE 8
66 #define DSCP(x) x
67 #define MIN_HANDLE_VALUE 5000
68 #define MAX_HANDLE_VALUE 6000
69
70 const uint8_t hdd_wmm_up_to_ac_map[] = {
71 SME_AC_BE,
72 SME_AC_BK,
73 SME_AC_BK,
74 SME_AC_BE,
75 SME_AC_VI,
76 SME_AC_VI,
77 SME_AC_VO,
78 SME_AC_VO
79 };
80
81 #define CONFIG_TSPEC_OPERATION \
82 QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_OPERATION
83 #define CONFIG_TSPEC_TSID \
84 QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_TSID
85 #define CONFIG_TSPEC_DIRECTION \
86 QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_DIRECTION
87 #define CONFIG_TSPEC_APSD \
88 QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_APSD
89 #define CONFIG_TSPEC_USER_PRIORITY \
90 QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_USER_PRIORITY
91 #define CONFIG_TSPEC_ACK_POLICY \
92 QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_ACK_POLICY
93 #define CONFIG_TSPEC_NOMINAL_MSDU_SIZE \
94 QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_NOMINAL_MSDU_SIZE
95 #define CONFIG_TSPEC_MAXIMUM_MSDU_SIZE \
96 QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAXIMUM_MSDU_SIZE
97 #define CONFIG_TSPEC_MIN_SERVICE_INTERVAL \
98 QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MIN_SERVICE_INTERVAL
99 #define CONFIG_TSPEC_MAX_SERVICE_INTERVAL \
100 QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX_SERVICE_INTERVAL
101 #define CONFIG_TSPEC_INACTIVITY_INTERVAL \
102 QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_INACTIVITY_INTERVAL
103 #define CONFIG_TSPEC_SUSPENSION_INTERVAL \
104 QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SUSPENSION_INTERVAL
105 #define CONFIG_TSPEC_MINIMUM_DATA_RATE \
106 QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_DATA_RATE
107 #define CONFIG_TSPEC_MEAN_DATA_RATE \
108 QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MEAN_DATA_RATE
109 #define CONFIG_TSPEC_PEAK_DATA_RATE \
110 QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_PEAK_DATA_RATE
111 #define CONFIG_TSPEC_BURST_SIZE \
112 QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_BURST_SIZE
113 #define CONFIG_TSPEC_MINIMUM_PHY_RATE \
114 QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_PHY_RATE
115 #define CONFIG_TSPEC_SURPLUS_BANDWIDTH_ALLOWANCE \
116 QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SURPLUS_BANDWIDTH_ALLOWANCE
117
118 const struct nla_policy
119 config_tspec_policy[QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX + 1] = {
120 [CONFIG_TSPEC_OPERATION] = {.type = NLA_U8},
121 [CONFIG_TSPEC_TSID] = {.type = NLA_U8},
122 [CONFIG_TSPEC_DIRECTION] = {.type = NLA_U8},
123 [CONFIG_TSPEC_APSD] = {.type = NLA_FLAG},
124 [CONFIG_TSPEC_USER_PRIORITY] = {.type = NLA_U8},
125 [CONFIG_TSPEC_ACK_POLICY] = {.type = NLA_U8},
126 [CONFIG_TSPEC_NOMINAL_MSDU_SIZE] = {.type = NLA_U16},
127 [CONFIG_TSPEC_MAXIMUM_MSDU_SIZE] = {.type = NLA_U16},
128 [CONFIG_TSPEC_MIN_SERVICE_INTERVAL] = {.type = NLA_U32},
129 [CONFIG_TSPEC_MAX_SERVICE_INTERVAL] = {.type = NLA_U32},
130 [CONFIG_TSPEC_INACTIVITY_INTERVAL] = {.type = NLA_U32},
131 [CONFIG_TSPEC_SUSPENSION_INTERVAL] = {.type = NLA_U32},
132 [CONFIG_TSPEC_MINIMUM_DATA_RATE] = {.type = NLA_U32},
133 [CONFIG_TSPEC_MEAN_DATA_RATE] = {.type = NLA_U32},
134 [CONFIG_TSPEC_PEAK_DATA_RATE] = {.type = NLA_U32},
135 [CONFIG_TSPEC_BURST_SIZE] = {.type = NLA_U32},
136 [CONFIG_TSPEC_MINIMUM_PHY_RATE] = {.type = NLA_U32},
137 [CONFIG_TSPEC_SURPLUS_BANDWIDTH_ALLOWANCE] = {.type = NLA_U16},
138 };
139
140 #ifdef QCA_LL_TX_FLOW_CONTROL_V2
wlan_hdd_process_peer_unauthorised_pause(struct hdd_adapter * adapter)141 void wlan_hdd_process_peer_unauthorised_pause(struct hdd_adapter *adapter)
142 {
143 uint8_t i;
144
145 netif_wake_subqueue(adapter->dev,
146 HDD_LINUX_AC_HI_PRIO * TX_QUEUES_PER_AC);
147
148 for (i = 0; i < TX_QUEUES_PER_AC; i++) {
149 netif_stop_subqueue(adapter->dev,
150 TX_GET_QUEUE_IDX(HDD_LINUX_AC_VO, i));
151 netif_stop_subqueue(adapter->dev,
152 TX_GET_QUEUE_IDX(HDD_LINUX_AC_VI, i));
153 netif_stop_subqueue(adapter->dev,
154 TX_GET_QUEUE_IDX(HDD_LINUX_AC_BE, i));
155 netif_stop_subqueue(adapter->dev,
156 TX_GET_QUEUE_IDX(HDD_LINUX_AC_BK, i));
157 }
158 }
159 #else
wlan_hdd_process_peer_unauthorised_pause(struct hdd_adapter * adapter)160 void wlan_hdd_process_peer_unauthorised_pause(struct hdd_adapter *adapter)
161 {
162 }
163 #endif
164
165 /* Linux based UP -> AC Mapping */
166 const uint8_t hdd_linux_up_to_ac_map[HDD_WMM_UP_TO_AC_MAP_SIZE] = {
167 HDD_LINUX_AC_BE,
168 HDD_LINUX_AC_BK,
169 HDD_LINUX_AC_BK,
170 HDD_LINUX_AC_BE,
171 HDD_LINUX_AC_VI,
172 HDD_LINUX_AC_VI,
173 HDD_LINUX_AC_VO,
174 HDD_LINUX_AC_VO
175 };
176
177 #ifndef WLAN_MDM_CODE_REDUCTION_OPT
178 /**
179 * hdd_wmm_enable_tl_uapsd() - function which decides whether and
180 * how to update UAPSD parameters in TL
181 *
182 * @qos_context: [in] the pointer the QoS instance control block
183 *
184 * Return: None
185 */
hdd_wmm_enable_tl_uapsd(struct hdd_wmm_qos_context * qos_context)186 static void hdd_wmm_enable_tl_uapsd(struct hdd_wmm_qos_context *qos_context)
187 {
188 struct hdd_adapter *adapter = qos_context->adapter;
189 sme_ac_enum_type ac_type = qos_context->ac_type;
190 struct hdd_wmm_ac_status *ac = &adapter->hdd_wmm_status.ac_status[ac_type];
191 struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
192 QDF_STATUS status;
193 uint32_t service_interval;
194 uint32_t suspension_interval;
195 enum sme_qos_wmm_dir_type direction;
196 bool psb;
197 uint32_t delayed_trgr_frm_int;
198
199 /* The TSPEC must be valid */
200 if (ac->is_tspec_valid == false) {
201 hdd_err("Invoked with invalid TSPEC");
202 return;
203 }
204 /* determine the service interval */
205 if (ac->tspec.min_service_interval) {
206 service_interval = ac->tspec.min_service_interval;
207 } else if (ac->tspec.max_service_interval) {
208 service_interval = ac->tspec.max_service_interval;
209 } else {
210 /* no service interval is present in the TSPEC */
211 /* this is OK, there just won't be U-APSD */
212 hdd_debug("No service interval supplied");
213 service_interval = 0;
214 }
215
216 /* determine the suspension interval & direction */
217 suspension_interval = ac->tspec.suspension_interval;
218 direction = ac->tspec.ts_info.direction;
219 psb = ac->tspec.ts_info.psb;
220
221 /* if we have previously enabled U-APSD, have any params changed? */
222 if ((ac->is_uapsd_info_valid) &&
223 (ac->uapsd_service_interval == service_interval) &&
224 (ac->uapsd_suspension_interval == suspension_interval) &&
225 (ac->uapsd_direction == direction) &&
226 (ac->is_uapsd_enabled == psb)) {
227 hdd_debug("No change in U-APSD parameters");
228 return;
229 }
230
231 ucfg_mlme_get_tl_delayed_trgr_frm_int(hdd_ctx->psoc,
232 &delayed_trgr_frm_int);
233 /* everything is in place to notify TL */
234 status =
235 sme_enable_uapsd_for_ac(ac_type, ac->tspec.ts_info.tid,
236 ac->tspec.ts_info.up,
237 service_interval, suspension_interval,
238 direction, psb,
239 adapter->deflink->vdev_id,
240 delayed_trgr_frm_int);
241
242 if (!QDF_IS_STATUS_SUCCESS(status)) {
243 hdd_err("Failed to enable U-APSD for AC=%d", ac_type);
244 return;
245 }
246 /* stash away the parameters that were used */
247 ac->is_uapsd_info_valid = true;
248 ac->uapsd_service_interval = service_interval;
249 ac->uapsd_suspension_interval = suspension_interval;
250 ac->uapsd_direction = direction;
251 ac->is_uapsd_enabled = psb;
252
253 hdd_debug("Enabled UAPSD in TL srv_int=%d susp_int=%d dir=%d AC=%d",
254 service_interval, suspension_interval, direction, ac_type);
255
256 }
257
258 /**
259 * hdd_wmm_disable_tl_uapsd() - function which decides whether
260 * to disable UAPSD parameters in TL
261 *
262 * @qos_context: [in] the pointer the QoS instance control block
263 *
264 * Return: None
265 */
hdd_wmm_disable_tl_uapsd(struct hdd_wmm_qos_context * qos_context)266 static void hdd_wmm_disable_tl_uapsd(struct hdd_wmm_qos_context *qos_context)
267 {
268 struct hdd_adapter *adapter = qos_context->adapter;
269 sme_ac_enum_type ac_type = qos_context->ac_type;
270 struct hdd_wmm_ac_status *ac = &adapter->hdd_wmm_status.ac_status[ac_type];
271 QDF_STATUS status;
272
273 /* have we previously enabled UAPSD? */
274 if (ac->is_uapsd_info_valid == true) {
275 status = sme_disable_uapsd_for_ac(ac_type,
276 adapter->deflink->vdev_id);
277
278 if (!QDF_IS_STATUS_SUCCESS(status)) {
279 hdd_err("Failed to disable U-APSD for AC=%d", ac_type);
280 } else {
281 /* TL no longer has valid UAPSD info */
282 ac->is_uapsd_info_valid = false;
283 hdd_debug("Disabled UAPSD in TL for AC=%d", ac_type);
284 }
285 }
286 }
287
288 #endif
289
290 /**
291 * hdd_wmm_free_context() - function which frees a QoS context
292 *
293 * @qos_context: [in] the pointer the QoS instance control block
294 *
295 * Return: None
296 */
hdd_wmm_free_context(struct hdd_wmm_qos_context * qos_context)297 static void hdd_wmm_free_context(struct hdd_wmm_qos_context *qos_context)
298 {
299 struct hdd_adapter *adapter;
300
301 hdd_debug("Entered, context %pK", qos_context);
302
303 if (unlikely((!qos_context) ||
304 (HDD_WMM_CTX_MAGIC != qos_context->magic))) {
305 /* must have been freed in another thread */
306 return;
307 }
308 /* get pointer to the adapter context */
309 adapter = qos_context->adapter;
310
311 /* take the mutex since we're manipulating the context list */
312 mutex_lock(&adapter->hdd_wmm_status.mutex);
313
314 /* make sure nobody thinks this is a valid context */
315 qos_context->magic = 0;
316
317 /* unlink the context */
318 list_del(&qos_context->node);
319
320 /* done manipulating the list */
321 mutex_unlock(&adapter->hdd_wmm_status.mutex);
322
323 /* reclaim memory */
324 qdf_mem_free(qos_context);
325
326 }
327
328 #ifndef WLAN_MDM_CODE_REDUCTION_OPT
329 /**
330 * hdd_wmm_notify_app() - function which notifies an application
331 * of changes in state of it flow
332 *
333 * @qos_context: [in] the pointer the QoS instance control block
334 *
335 * Return: None
336 */
hdd_wmm_notify_app(struct hdd_wmm_qos_context * qos_context)337 static void hdd_wmm_notify_app(struct hdd_wmm_qos_context *qos_context)
338 {
339 #define MAX_NOTIFY_LEN 50
340 struct hdd_adapter *adapter;
341 union iwreq_data wrqu;
342 char buf[MAX_NOTIFY_LEN + 1];
343
344 hdd_debug("Entered, context %pK", qos_context);
345
346 if (unlikely((!qos_context) ||
347 (HDD_WMM_CTX_MAGIC != qos_context->magic))) {
348 hdd_err("Invalid QoS Context");
349 return;
350 }
351
352 /* create the event */
353 memset(&wrqu, 0, sizeof(wrqu));
354 memset(buf, 0, sizeof(buf));
355
356 snprintf(buf, MAX_NOTIFY_LEN, "QCOM: TS change[%u: %u]",
357 (unsigned int)qos_context->handle,
358 (unsigned int)qos_context->status);
359
360 wrqu.data.pointer = buf;
361 wrqu.data.length = strlen(buf);
362
363 /* get pointer to the adapter */
364 adapter = qos_context->adapter;
365
366 /* send the event */
367 hdd_debug("Sending [%s]", buf);
368 hdd_wext_send_event(adapter->dev, IWEVCUSTOM, &wrqu, buf);
369 }
370
371 #ifdef FEATURE_WLAN_ESE
372 /**
373 * hdd_wmm_inactivity_timer_cb() - inactivity timer callback function
374 *
375 * @user_data: opaque user data registered with the timer. In the
376 * case of this timer, the associated wmm QoS context is registered.
377 *
378 * This timer handler function is called for every inactivity interval
379 * per AC. This function gets the current transmitted packets on the
380 * given AC, and checks if there was any TX activity from the previous
381 * interval. If there was no traffic then it would delete the TS that
382 * was negotiated on that AC.
383 *
384 * Return: None
385 */
hdd_wmm_inactivity_timer_cb(void * user_data)386 static void hdd_wmm_inactivity_timer_cb(void *user_data)
387 {
388 struct hdd_wmm_qos_context *qos_context = user_data;
389 struct hdd_adapter *adapter;
390 struct hdd_wmm_ac_status *ac;
391 hdd_wlan_wmm_status_e status;
392 QDF_STATUS qdf_status;
393 uint32_t traffic_count = 0;
394 sme_ac_enum_type ac_type;
395 unsigned int cpu;
396 struct hdd_tx_rx_stats *tx_rx_stats;
397
398 if (!qos_context) {
399 hdd_err("invalid user data");
400 return;
401 }
402 ac_type = qos_context->ac_type;
403
404 adapter = qos_context->adapter;
405 if ((!adapter) ||
406 (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) {
407 hdd_err("invalid adapter: %pK", adapter);
408 return;
409 }
410
411 ac = &adapter->hdd_wmm_status.ac_status[ac_type];
412 tx_rx_stats = &adapter->deflink->hdd_stats.tx_rx_stats;
413 /* Get the Tx stats for this AC. */
414 for (cpu = 0; cpu < NUM_CPUS; cpu++)
415 traffic_count +=
416 tx_rx_stats->per_cpu[cpu].tx_classified_ac[qos_context->ac_type];
417
418 hdd_warn("WMM inactivity check for AC=%d, count=%u, last=%u",
419 ac_type, traffic_count, ac->last_traffic_count);
420 if (ac->last_traffic_count == traffic_count) {
421 /* there is no traffic activity, delete the TSPEC for this AC */
422 status = hdd_wmm_delts(adapter, qos_context->handle);
423 hdd_warn("Deleted TS on AC %d, due to inactivity with status = %d!!!",
424 ac_type, status);
425 } else {
426 ac->last_traffic_count = traffic_count;
427 if (ac->inactivity_timer.state == QDF_TIMER_STATE_STOPPED) {
428 /* Restart the timer */
429 qdf_status =
430 qdf_mc_timer_start(&ac->inactivity_timer,
431 ac->inactivity_time);
432 if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
433 hdd_err("Restarting inactivity timer failed on AC %d",
434 ac_type);
435 }
436 } else {
437 QDF_ASSERT(qdf_mc_timer_get_current_state
438 (&ac->inactivity_timer) ==
439 QDF_TIMER_STATE_STOPPED);
440 }
441 }
442 }
443
444 /**
445 * hdd_wmm_enable_inactivity_timer() -
446 * function to enable the traffic inactivity timer for the given AC
447 *
448 * @qos_context: [in] pointer to qos_context
449 * @inactivity_time: [in] value of the inactivity interval in millisecs
450 *
451 * When a QoS-Tspec is successfully setup, if the inactivity interval
452 * time specified in the AddTS parameters is non-zero, this function
453 * is invoked to start a traffic inactivity timer for the given AC.
454 *
455 * Return: QDF_STATUS enumeration
456 */
457 static QDF_STATUS
hdd_wmm_enable_inactivity_timer(struct hdd_wmm_qos_context * qos_context,uint32_t inactivity_time)458 hdd_wmm_enable_inactivity_timer(struct hdd_wmm_qos_context *qos_context,
459 uint32_t inactivity_time)
460 {
461 QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE;
462 struct hdd_adapter *adapter = qos_context->adapter;
463 sme_ac_enum_type ac_type = qos_context->ac_type;
464 struct hdd_wmm_ac_status *ac;
465 unsigned int cpu;
466 struct hdd_tx_rx_stats *tx_rx_stats;
467
468 adapter = qos_context->adapter;
469 ac = &adapter->hdd_wmm_status.ac_status[ac_type];
470
471 qdf_status = qdf_mc_timer_init(&ac->inactivity_timer,
472 QDF_TIMER_TYPE_SW,
473 hdd_wmm_inactivity_timer_cb,
474 qos_context);
475 if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
476 hdd_err("Initializing inactivity timer failed on AC %d",
477 ac_type);
478 return qdf_status;
479 }
480 /* Start the inactivity timer */
481 qdf_status = qdf_mc_timer_start(&ac->inactivity_timer,
482 inactivity_time);
483 if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
484 hdd_err("Starting inactivity timer failed on AC %d",
485 ac_type);
486 qdf_status = qdf_mc_timer_destroy(&ac->inactivity_timer);
487 if (!QDF_IS_STATUS_SUCCESS(qdf_status))
488 hdd_err("Failed to destroy inactivity timer");
489
490 return qdf_status;
491 }
492 ac->inactivity_time = inactivity_time;
493
494 ac->last_traffic_count = 0;
495 /* Initialize the current tx traffic count on this AC */
496 tx_rx_stats = &adapter->deflink->hdd_stats.tx_rx_stats;
497 for (cpu = 0; cpu < NUM_CPUS; cpu++) {
498 ac->last_traffic_count +=
499 tx_rx_stats->per_cpu[cpu].tx_classified_ac[qos_context->ac_type];
500 }
501 qos_context->is_inactivity_timer_running = true;
502 return qdf_status;
503 }
504
505 /**
506 * hdd_wmm_disable_inactivity_timer() -
507 * function to disable the traffic inactivity timer for the given AC.
508 *
509 * @qos_context: [in] pointer to qos_context
510 *
511 * This function is invoked to disable the traffic inactivity timer
512 * for the given AC. This is normally done when the TS is deleted.
513 *
514 * Return: QDF_STATUS enumeration
515 */
516 static QDF_STATUS
hdd_wmm_disable_inactivity_timer(struct hdd_wmm_qos_context * qos_context)517 hdd_wmm_disable_inactivity_timer(struct hdd_wmm_qos_context *qos_context)
518 {
519 struct hdd_adapter *adapter = qos_context->adapter;
520 sme_ac_enum_type ac_type = qos_context->ac_type;
521 struct hdd_wmm_ac_status *ac = &adapter->hdd_wmm_status.ac_status[ac_type];
522 QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE;
523
524 /* Clear the timer and the counter */
525 ac->inactivity_time = 0;
526 ac->last_traffic_count = 0;
527
528 if (qos_context->is_inactivity_timer_running == true) {
529 qos_context->is_inactivity_timer_running = false;
530 qdf_status = qdf_mc_timer_stop(&ac->inactivity_timer);
531 if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
532 hdd_err("Failed to stop inactivity timer");
533 return qdf_status;
534 }
535 qdf_status = qdf_mc_timer_destroy(&ac->inactivity_timer);
536 if (!QDF_IS_STATUS_SUCCESS(qdf_status))
537 hdd_err("Failed to destroy inactivity timer:Timer started");
538 }
539
540 return qdf_status;
541 }
542 #else
543
544 static QDF_STATUS
hdd_wmm_disable_inactivity_timer(struct hdd_wmm_qos_context * qos_context)545 hdd_wmm_disable_inactivity_timer(struct hdd_wmm_qos_context *qos_context)
546 {
547 return QDF_STATUS_SUCCESS;
548 }
549 #endif /* FEATURE_WLAN_ESE */
550
551 /**
552 * hdd_wmm_sme_callback() - callback for QoS notifications
553 *
554 * @mac_handle: [in] the MAC handle
555 * @context : [in] the HDD callback context
556 * @tspec_info : [in] the TSPEC params
557 * @sme_status : [in] the QoS related SME status
558 * @flow_id: [in] the unique identifier of the flow
559 *
560 * This callback is registered by HDD with SME for receiving QoS
561 * notifications. Even though this function has a static scope it
562 * gets called externally through some function pointer magic (so
563 * there is a need for rigorous parameter checking).
564 *
565 * Return: QDF_STATUS enumeration
566 */
hdd_wmm_sme_callback(mac_handle_t mac_handle,void * context,struct sme_qos_wmmtspecinfo * tspec_info,enum sme_qos_statustype sme_status,uint32_t flow_id)567 static QDF_STATUS hdd_wmm_sme_callback(mac_handle_t mac_handle,
568 void *context,
569 struct sme_qos_wmmtspecinfo *tspec_info,
570 enum sme_qos_statustype sme_status,
571 uint32_t flow_id)
572 {
573 struct hdd_wmm_qos_context *qos_context = context;
574 struct hdd_adapter *adapter;
575 sme_ac_enum_type ac_type;
576 struct hdd_wmm_ac_status *ac;
577
578 hdd_debug("Entered, context %pK", qos_context);
579
580 if (unlikely((!qos_context) ||
581 (HDD_WMM_CTX_MAGIC != qos_context->magic))) {
582 hdd_err("Invalid QoS Context");
583 return QDF_STATUS_E_FAILURE;
584 }
585
586 adapter = qos_context->adapter;
587 ac_type = qos_context->ac_type;
588 ac = &adapter->hdd_wmm_status.ac_status[ac_type];
589
590 hdd_debug("status %d flowid %d info %pK",
591 sme_status, flow_id, tspec_info);
592
593 switch (sme_status) {
594
595 case SME_QOS_STATUS_SETUP_SUCCESS_IND:
596 hdd_debug("Setup is complete");
597
598 /* there will always be a TSPEC returned with this
599 * status, even if a TSPEC is not exchanged OTA
600 */
601 if (tspec_info) {
602 ac->is_tspec_valid = true;
603 memcpy(&ac->tspec,
604 tspec_info, sizeof(ac->tspec));
605 }
606 ac->is_access_allowed = true;
607 ac->was_access_granted = true;
608 ac->is_access_pending = false;
609 ac->has_access_failed = false;
610
611 if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) {
612
613 hdd_debug("Explicit Qos, notifying user space");
614
615 /* this was triggered by an application */
616 qos_context->status =
617 HDD_WLAN_WMM_STATUS_SETUP_SUCCESS;
618 hdd_wmm_notify_app(qos_context);
619 }
620
621 #ifdef FEATURE_WLAN_ESE
622 /* Check if the inactivity interval is specified */
623 if (tspec_info && tspec_info->inactivity_interval) {
624 hdd_debug("Inactivity timer value = %d for AC=%d",
625 tspec_info->inactivity_interval, ac_type);
626 hdd_wmm_enable_inactivity_timer(qos_context,
627 tspec_info->
628 inactivity_interval);
629 }
630 #endif /* FEATURE_WLAN_ESE */
631
632 /* notify TL to enable trigger frames if necessary */
633 hdd_wmm_enable_tl_uapsd(qos_context);
634
635 break;
636
637 case SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY:
638 hdd_debug("Setup is complete (U-APSD set previously)");
639
640 ac->is_access_allowed = true;
641 ac->was_access_granted = true;
642 ac->is_access_pending = false;
643
644 if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) {
645
646 hdd_debug("Explicit Qos, notifying user space");
647
648 /* this was triggered by an application */
649 qos_context->status =
650 HDD_WLAN_WMM_STATUS_SETUP_SUCCESS_NO_ACM_UAPSD_EXISTING;
651 hdd_wmm_notify_app(qos_context);
652 }
653
654 break;
655
656 case SME_QOS_STATUS_SETUP_FAILURE_RSP:
657 hdd_err("Setup failed");
658 /* QoS setup failed */
659
660 ac->is_access_pending = false;
661 ac->has_access_failed = true;
662 ac->is_access_allowed = false;
663 if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) {
664
665 hdd_debug("Explicit Qos, notifying user space");
666
667 /* this was triggered by an application */
668 qos_context->status =
669 HDD_WLAN_WMM_STATUS_SETUP_FAILED;
670
671 hdd_wmm_notify_app(qos_context);
672 }
673
674 /* disable the inactivity timer */
675 hdd_wmm_disable_inactivity_timer(qos_context);
676
677 /* Setting up QoS Failed, QoS context can be released.
678 * SME is releasing this flow information and if HDD
679 * doesn't release this context, next time if
680 * application uses the same handle to set-up QoS, HDD
681 * (as it has QoS context for this handle) will issue
682 * Modify QoS request to SME but SME will reject as now
683 * it has no information for this flow.
684 */
685 hdd_wmm_free_context(qos_context);
686 break;
687
688 case SME_QOS_STATUS_SETUP_INVALID_PARAMS_RSP:
689 hdd_err("Setup Invalid Params, notify TL");
690 /* QoS setup failed */
691 ac->is_access_allowed = false;
692
693 if (HDD_WMM_HANDLE_IMPLICIT == qos_context->handle) {
694
695 /* we note the failure, but we also mark
696 * access as allowed so that the packets will
697 * flow. Note that the MAC will "do the right
698 * thing"
699 */
700 ac->is_access_pending = false;
701 ac->has_access_failed = true;
702 ac->is_access_allowed = true;
703
704 } else {
705 hdd_debug("Explicit Qos, notifying user space");
706
707 /* this was triggered by an application */
708 qos_context->status =
709 HDD_WLAN_WMM_STATUS_SETUP_FAILED_BAD_PARAM;
710 hdd_wmm_notify_app(qos_context);
711 }
712 break;
713
714 case SME_QOS_STATUS_SETUP_NOT_QOS_AP_RSP:
715 hdd_err("Setup failed, not a QoS AP");
716 if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) {
717 hdd_info("Explicit Qos, notifying user space");
718
719 /* this was triggered by an application */
720 qos_context->status =
721 HDD_WLAN_WMM_STATUS_SETUP_FAILED_NO_WMM;
722 hdd_wmm_notify_app(qos_context);
723 }
724 break;
725
726 case SME_QOS_STATUS_SETUP_REQ_PENDING_RSP:
727 hdd_debug("Setup pending");
728 /* not a callback status -- ignore if we get it */
729 break;
730
731 case SME_QOS_STATUS_SETUP_MODIFIED_IND:
732 hdd_debug("Setup modified");
733 if (tspec_info) {
734 /* update the TSPEC */
735 ac->is_tspec_valid = true;
736 memcpy(&ac->tspec,
737 tspec_info, sizeof(ac->tspec));
738
739 if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) {
740 hdd_debug("Explicit Qos, notifying user space");
741
742 /* this was triggered by an application */
743 qos_context->status =
744 HDD_WLAN_WMM_STATUS_MODIFIED;
745 hdd_wmm_notify_app(qos_context);
746 }
747 /* need to tell TL to update its UAPSD handling */
748 hdd_wmm_enable_tl_uapsd(qos_context);
749 }
750 break;
751
752 case SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP:
753 if (HDD_WMM_HANDLE_IMPLICIT == qos_context->handle) {
754
755 /* this was triggered by implicit QoS so we
756 * know packets are pending
757 */
758 ac->is_access_pending = false;
759 ac->was_access_granted = true;
760 ac->is_access_allowed = true;
761
762 } else {
763 hdd_debug("Explicit Qos, notifying user space");
764
765 /* this was triggered by an application */
766 qos_context->status =
767 HDD_WLAN_WMM_STATUS_SETUP_SUCCESS_NO_ACM_NO_UAPSD;
768 hdd_wmm_notify_app(qos_context);
769 }
770 break;
771
772 case SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_PENDING:
773 /* nothing to do for now */
774 break;
775
776 case SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_SET_FAILED:
777 hdd_err("Setup successful but U-APSD failed");
778
779 if (HDD_WMM_HANDLE_IMPLICIT == qos_context->handle) {
780
781 /* QoS setup was successful but setting U=APSD
782 * failed. Since the OTA part of the request
783 * was successful, we don't mark this as a
784 * failure. the packets will flow. Note that
785 * the MAC will "do the right thing"
786 */
787 ac->was_access_granted = true;
788 ac->is_access_allowed = true;
789 ac->has_access_failed = false;
790 ac->is_access_pending = false;
791
792 } else {
793 hdd_info("Explicit Qos, notifying user space");
794
795 /* this was triggered by an application */
796 qos_context->status =
797 HDD_WLAN_WMM_STATUS_SETUP_UAPSD_SET_FAILED;
798 hdd_wmm_notify_app(qos_context);
799 }
800
801 /* Since U-APSD portion failed disabled trigger frame
802 * generation
803 */
804 hdd_wmm_disable_tl_uapsd(qos_context);
805
806 break;
807
808 case SME_QOS_STATUS_RELEASE_SUCCESS_RSP:
809 hdd_debug("Release is complete");
810
811 if (tspec_info) {
812 hdd_debug("flows still active");
813
814 /* there is still at least one flow active for
815 * this AC so update the AC state
816 */
817 memcpy(&ac->tspec,
818 tspec_info, sizeof(ac->tspec));
819
820 /* need to tell TL to update its UAPSD handling */
821 hdd_wmm_enable_tl_uapsd(qos_context);
822 } else {
823 hdd_debug("last flow");
824
825 /* this is the last flow active for this AC so
826 * update the AC state
827 */
828 ac->is_tspec_valid = false;
829
830 /* DELTS is successful, do not allow */
831 ac->is_access_allowed = false;
832
833 /* need to tell TL to update its UAPSD handling */
834 hdd_wmm_disable_tl_uapsd(qos_context);
835 }
836
837 if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) {
838 hdd_debug("Explicit Qos, notifying user space");
839
840 /* this was triggered by an application */
841 qos_context->status =
842 HDD_WLAN_WMM_STATUS_RELEASE_SUCCESS;
843 hdd_wmm_notify_app(qos_context);
844 }
845 /* disable the inactivity timer */
846 hdd_wmm_disable_inactivity_timer(qos_context);
847
848 /* we are done with this flow */
849 hdd_wmm_free_context(qos_context);
850 break;
851
852 case SME_QOS_STATUS_RELEASE_FAILURE_RSP:
853 hdd_debug("Release failure");
854
855 /* we don't need to update our state or TL since
856 * nothing has changed
857 */
858 if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) {
859 hdd_debug("Explicit Qos, notifying user space");
860
861 /* this was triggered by an application */
862 qos_context->status =
863 HDD_WLAN_WMM_STATUS_RELEASE_FAILED;
864 hdd_wmm_notify_app(qos_context);
865 }
866
867 break;
868
869 case SME_QOS_STATUS_RELEASE_QOS_LOST_IND:
870 hdd_debug("QOS Lost indication received");
871
872 /* current TSPEC is no longer valid */
873 ac->is_tspec_valid = false;
874 /* AP has sent DELTS, do not allow */
875 ac->is_access_allowed = false;
876
877 /* need to tell TL to update its UAPSD handling */
878 hdd_wmm_disable_tl_uapsd(qos_context);
879
880 if (HDD_WMM_HANDLE_IMPLICIT == qos_context->handle) {
881 /* we no longer have implicit access granted */
882 ac->was_access_granted = false;
883 ac->has_access_failed = false;
884 } else {
885 hdd_debug("Explicit Qos, notifying user space");
886
887 /* this was triggered by an application */
888 qos_context->status = HDD_WLAN_WMM_STATUS_LOST;
889 hdd_wmm_notify_app(qos_context);
890 }
891
892 /* disable the inactivity timer */
893 hdd_wmm_disable_inactivity_timer(qos_context);
894
895 /* we are done with this flow */
896 hdd_wmm_free_context(qos_context);
897 break;
898
899 case SME_QOS_STATUS_RELEASE_REQ_PENDING_RSP:
900 hdd_debug("Release pending");
901 /* not a callback status -- ignore if we get it */
902 break;
903
904 case SME_QOS_STATUS_RELEASE_INVALID_PARAMS_RSP:
905 hdd_err("Release Invalid Params");
906 if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) {
907 /* this was triggered by an application */
908 qos_context->status =
909 HDD_WLAN_WMM_STATUS_RELEASE_FAILED_BAD_PARAM;
910 hdd_wmm_notify_app(qos_context);
911 }
912 break;
913
914 case SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND:
915 hdd_debug("Modification is complete, notify TL");
916
917 /* there will always be a TSPEC returned with this
918 * status, even if a TSPEC is not exchanged OTA
919 */
920 if (tspec_info) {
921 ac->is_tspec_valid = true;
922 memcpy(&ac->tspec,
923 tspec_info, sizeof(ac->tspec));
924 }
925
926 if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) {
927 /* this was triggered by an application */
928 qos_context->status =
929 HDD_WLAN_WMM_STATUS_MODIFY_SUCCESS;
930 hdd_wmm_notify_app(qos_context);
931 }
932 /* notify TL to enable trigger frames if necessary */
933 hdd_wmm_enable_tl_uapsd(qos_context);
934
935 break;
936
937 case SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_APSD_SET_ALREADY:
938 if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) {
939 /* this was triggered by an application */
940 qos_context->status =
941 HDD_WLAN_WMM_STATUS_MODIFY_SUCCESS_NO_ACM_UAPSD_EXISTING;
942 hdd_wmm_notify_app(qos_context);
943 }
944 break;
945
946 case SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP:
947 /* the flow modification failed so we'll leave in
948 * place whatever existed beforehand
949 */
950
951 if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) {
952 /* this was triggered by an application */
953 qos_context->status =
954 HDD_WLAN_WMM_STATUS_MODIFY_FAILED;
955 hdd_wmm_notify_app(qos_context);
956 }
957 break;
958
959 case SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP:
960 hdd_debug("modification pending");
961 /* not a callback status -- ignore if we get it */
962 break;
963
964 case SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP:
965 /* the flow modification was successful but no QoS
966 * changes required
967 */
968
969 if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) {
970 /* this was triggered by an application */
971 qos_context->status =
972 HDD_WLAN_WMM_STATUS_MODIFY_SUCCESS_NO_ACM_NO_UAPSD;
973 hdd_wmm_notify_app(qos_context);
974 }
975 break;
976
977 case SME_QOS_STATUS_MODIFY_SETUP_INVALID_PARAMS_RSP:
978 /* invalid params -- notify the application */
979 if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) {
980 /* this was triggered by an application */
981 qos_context->status =
982 HDD_WLAN_WMM_STATUS_MODIFY_FAILED_BAD_PARAM;
983 hdd_wmm_notify_app(qos_context);
984 }
985 break;
986
987 case SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND_APSD_PENDING:
988 /* nothing to do for now. when APSD is established we'll have work to do */
989 break;
990
991 case SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND_APSD_SET_FAILED:
992 hdd_err("Modify successful but U-APSD failed");
993
994 /* QoS modification was successful but setting U=APSD
995 * failed. This will always be an explicit QoS
996 * instance, so all we can do is notify the
997 * application and let it clean up.
998 */
999 if (HDD_WMM_HANDLE_IMPLICIT != qos_context->handle) {
1000 /* this was triggered by an application */
1001 qos_context->status =
1002 HDD_WLAN_WMM_STATUS_MODIFY_UAPSD_SET_FAILED;
1003 hdd_wmm_notify_app(qos_context);
1004 }
1005 /* Since U-APSD portion failed disabled trigger frame
1006 * generation
1007 */
1008 hdd_wmm_disable_tl_uapsd(qos_context);
1009
1010 break;
1011
1012 case SME_QOS_STATUS_HANDING_OFF:
1013 /* no roaming so we won't see this */
1014 break;
1015
1016 case SME_QOS_STATUS_OUT_OF_APSD_POWER_MODE_IND:
1017 /* need to tell TL to stop trigger frame generation */
1018 hdd_wmm_disable_tl_uapsd(qos_context);
1019 break;
1020
1021 case SME_QOS_STATUS_INTO_APSD_POWER_MODE_IND:
1022 /* need to tell TL to start sending trigger frames again */
1023 hdd_wmm_enable_tl_uapsd(qos_context);
1024 break;
1025
1026 default:
1027 hdd_err("unexpected SME Status=%d", sme_status);
1028 QDF_ASSERT(0);
1029 }
1030
1031 /* if Tspec only allows downstream traffic then access is not
1032 * allowed
1033 */
1034 if (ac->is_tspec_valid &&
1035 (ac->tspec.ts_info.direction ==
1036 SME_QOS_WMM_TS_DIR_DOWNLINK)) {
1037 ac->is_access_allowed = false;
1038 }
1039 /* if we have valid Tpsec or if ACM bit is not set, allow access */
1040 if ((ac->is_tspec_valid &&
1041 (ac->tspec.ts_info.direction !=
1042 SME_QOS_WMM_TS_DIR_DOWNLINK)) || !ac->is_access_required) {
1043 ac->is_access_allowed = true;
1044 }
1045
1046 hdd_debug("complete, access for TL AC %d is%sallowed",
1047 ac_type, ac->is_access_allowed ? " " : " not ");
1048
1049 return QDF_STATUS_SUCCESS;
1050 }
1051 #endif
1052
1053 /**
1054 * hdd_wmmps_helper() - Function to set uapsd psb dynamically
1055 *
1056 * @adapter: [in] pointer to adapter structure
1057 * @ptr: [in] pointer to command buffer
1058 *
1059 * Return: Zero on success, appropriate error on failure.
1060 */
hdd_wmmps_helper(struct hdd_adapter * adapter,uint8_t * ptr)1061 int hdd_wmmps_helper(struct hdd_adapter *adapter, uint8_t *ptr)
1062 {
1063 if (!adapter) {
1064 hdd_err("adapter is NULL");
1065 return -EINVAL;
1066 }
1067 if (!ptr) {
1068 hdd_err("ptr is NULL");
1069 return -EINVAL;
1070 }
1071 /* convert ASCII to integer */
1072 adapter->configured_psb = ptr[9] - '0';
1073 adapter->psb_changed = HDD_PSB_CHANGED;
1074
1075 return 0;
1076 }
1077
1078 /**
1079 * __hdd_wmm_do_implicit_qos() - Function which will attempt to setup
1080 * QoS for any AC requiring it.
1081 * @qos_context: the QoS context to operate against
1082 *
1083 * Return: none
1084 */
__hdd_wmm_do_implicit_qos(struct hdd_wmm_qos_context * qos_context)1085 static void __hdd_wmm_do_implicit_qos(struct hdd_wmm_qos_context *qos_context)
1086 {
1087 struct hdd_adapter *adapter;
1088 sme_ac_enum_type ac_type;
1089 struct hdd_wmm_ac_status *ac;
1090 #ifndef WLAN_MDM_CODE_REDUCTION_OPT
1091 enum sme_qos_statustype sme_status;
1092 #endif
1093 struct sme_qos_wmmtspecinfo tspec;
1094 struct hdd_context *hdd_ctx;
1095 mac_handle_t mac_handle;
1096 QDF_STATUS status = QDF_STATUS_SUCCESS;
1097 uint8_t dir_ac, mask = 0;
1098 uint16_t nom_msdu_size_ac = 0;
1099 uint32_t rate_ac = 0;
1100 uint16_t sba_ac = 0;
1101 uint32_t uapsd_value = 0;
1102 bool is_ts_burst_enable;
1103 enum mlme_ts_info_ack_policy ack_policy;
1104
1105 hdd_debug("Entered, context %pK", qos_context);
1106
1107 adapter = qos_context->adapter;
1108
1109 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1110 if (wlan_hdd_validate_context(hdd_ctx))
1111 return;
1112
1113 mac_handle = hdd_ctx->mac_handle;
1114
1115 ac_type = qos_context->ac_type;
1116 ac = &adapter->hdd_wmm_status.ac_status[ac_type];
1117
1118 hdd_debug("adapter %pK ac_type %d", adapter, ac_type);
1119
1120 if (!ac->is_access_needed) {
1121 hdd_err("AC %d doesn't need service", ac_type);
1122 qos_context->magic = 0;
1123 qdf_mem_free(qos_context);
1124 return;
1125 }
1126
1127 ac->is_access_pending = true;
1128 ac->is_access_needed = false;
1129
1130 memset(&tspec, 0, sizeof(tspec));
1131
1132 tspec.ts_info.psb = adapter->configured_psb;
1133
1134 switch (ac_type) {
1135 case SME_AC_VO:
1136 tspec.ts_info.up = SME_QOS_WMM_UP_VO;
1137 /* Check if there is any valid configuration from framework */
1138 if (HDD_PSB_CFG_INVALID == adapter->configured_psb) {
1139 status = ucfg_mlme_get_wmm_uapsd_mask(hdd_ctx->psoc,
1140 &mask);
1141 if (!QDF_IS_STATUS_SUCCESS(status)) {
1142 hdd_err("Get uapsd_mask failed");
1143 return;
1144 }
1145 tspec.ts_info.psb = (mask & SME_QOS_UAPSD_VO) ? 1 : 0;
1146 }
1147 status = ucfg_mlme_get_wmm_dir_ac_vo(hdd_ctx->psoc,
1148 &dir_ac);
1149 if (!QDF_IS_STATUS_SUCCESS(status)) {
1150 hdd_err("Get infra_dir_ac_vo failed");
1151 return;
1152 }
1153 tspec.ts_info.direction = dir_ac;
1154
1155 tspec.ts_info.tid = 255;
1156
1157 status = ucfg_mlme_get_wmm_uapsd_vo_srv_intv(hdd_ctx->psoc,
1158 &uapsd_value);
1159 if (QDF_IS_STATUS_ERROR(status)) {
1160 hdd_err("Get uapsd_srv_intv failed");
1161 return;
1162 }
1163 tspec.min_service_interval = uapsd_value;
1164
1165 status = ucfg_mlme_get_wmm_uapsd_vo_sus_intv(hdd_ctx->psoc,
1166 &uapsd_value);
1167 if (QDF_IS_STATUS_ERROR(status)) {
1168 hdd_err("Get uapsd_vo_sus_intv failed");
1169 return;
1170 }
1171 tspec.suspension_interval = uapsd_value;
1172
1173 status = ucfg_mlme_get_wmm_mean_data_rate_ac_vo(hdd_ctx->psoc,
1174 &rate_ac);
1175 if (QDF_IS_STATUS_ERROR(status)) {
1176 hdd_err("Get mean_data_rate_ac_vo failed");
1177 return;
1178 }
1179 tspec.mean_data_rate = rate_ac;
1180
1181 status = ucfg_mlme_get_wmm_min_phy_rate_ac_vo(hdd_ctx->psoc,
1182 &rate_ac);
1183 if (QDF_IS_STATUS_ERROR(status)) {
1184 hdd_err("Get min_phy_rate_ac_vo failed");
1185 return;
1186 }
1187 tspec.min_phy_rate = rate_ac;
1188
1189 status = ucfg_mlme_get_wmm_nom_msdu_size_ac_vo(hdd_ctx->psoc,
1190 &nom_msdu_size_ac);
1191 if (QDF_IS_STATUS_ERROR(status)) {
1192 hdd_err("Get nom_msdu_size_ac_vo failed");
1193 return;
1194 }
1195 tspec.nominal_msdu_size = nom_msdu_size_ac;
1196
1197 status = ucfg_mlme_get_wmm_sba_ac_vo(hdd_ctx->psoc,
1198 &sba_ac);
1199 if (!QDF_IS_STATUS_SUCCESS(status)) {
1200 hdd_err("Get sba_ac_vo failed");
1201 return;
1202 }
1203 tspec.surplus_bw_allowance = sba_ac;
1204
1205 break;
1206 case SME_AC_VI:
1207 tspec.ts_info.up = SME_QOS_WMM_UP_VI;
1208 /* Check if there is any valid configuration from framework */
1209 if (HDD_PSB_CFG_INVALID == adapter->configured_psb) {
1210 status = ucfg_mlme_get_wmm_uapsd_mask(hdd_ctx->psoc,
1211 &mask);
1212 if (!QDF_IS_STATUS_SUCCESS(status)) {
1213 hdd_err("Get uapsd_mask failed");
1214 return;
1215 }
1216 tspec.ts_info.psb = (mask & SME_QOS_UAPSD_VI) ? 1 : 0;
1217 }
1218 status = ucfg_mlme_get_wmm_dir_ac_vi(
1219 hdd_ctx->psoc, &dir_ac);
1220 if (!QDF_IS_STATUS_SUCCESS(status)) {
1221 hdd_err("Get infra_dir_ac_vi failed");
1222 return;
1223 }
1224 tspec.ts_info.direction = dir_ac;
1225
1226 tspec.ts_info.tid = 255;
1227 status = ucfg_mlme_get_wmm_uapsd_vi_srv_intv(
1228 hdd_ctx->psoc, &uapsd_value);
1229 if (!QDF_IS_STATUS_SUCCESS(status)) {
1230 hdd_err("Get uapsd_vi_srv_intv failed");
1231 return;
1232 }
1233 tspec.min_service_interval = uapsd_value;
1234
1235 status = ucfg_mlme_get_wmm_uapsd_vi_sus_intv(
1236 hdd_ctx->psoc, &uapsd_value);
1237 if (!QDF_IS_STATUS_SUCCESS(status)) {
1238 hdd_err("Get uapsd_vi_sus_intv failed");
1239 return;
1240 }
1241 tspec.suspension_interval = uapsd_value;
1242
1243 status = ucfg_mlme_get_wmm_mean_data_rate_ac_vi(
1244 hdd_ctx->psoc, &rate_ac);
1245 if (!QDF_IS_STATUS_SUCCESS(status)) {
1246 hdd_err("Get mean_data_rate_ac_vi failed");
1247 return;
1248 }
1249 tspec.mean_data_rate = rate_ac;
1250
1251 status = ucfg_mlme_get_wmm_min_phy_rate_ac_vi(
1252 hdd_ctx->psoc, &rate_ac);
1253 if (!QDF_IS_STATUS_SUCCESS(status)) {
1254 hdd_err("Get min_phy_rate_ac_vi failed");
1255 return;
1256 }
1257 tspec.min_phy_rate = rate_ac;
1258
1259 status = ucfg_mlme_get_wmm_nom_msdu_size_ac_vi(
1260 hdd_ctx->psoc, &nom_msdu_size_ac);
1261 if (!QDF_IS_STATUS_SUCCESS(status)) {
1262 hdd_err("Get nom_msdu_size_ac_vi failed");
1263 return;
1264 }
1265 tspec.nominal_msdu_size = nom_msdu_size_ac;
1266
1267 status = ucfg_mlme_get_wmm_sba_ac_vi(
1268 hdd_ctx->psoc, &sba_ac);
1269 if (!QDF_IS_STATUS_SUCCESS(status)) {
1270 hdd_err("Get sba_ac_vi failed");
1271 return;
1272 }
1273 tspec.surplus_bw_allowance = sba_ac;
1274
1275 break;
1276 default:
1277 case SME_AC_BE:
1278 tspec.ts_info.up = SME_QOS_WMM_UP_BE;
1279 /* Check if there is any valid configuration from framework */
1280 if (HDD_PSB_CFG_INVALID == adapter->configured_psb) {
1281 status = ucfg_mlme_get_wmm_uapsd_mask(hdd_ctx->psoc,
1282 &mask);
1283 if (!QDF_IS_STATUS_SUCCESS(status)) {
1284 hdd_err("Get uapsd_mask failed");
1285 return;
1286 }
1287 tspec.ts_info.psb = (mask & SME_QOS_UAPSD_BE) ? 1 : 0;
1288 }
1289 status = ucfg_mlme_get_wmm_dir_ac_be(hdd_ctx->psoc, &dir_ac);
1290 if (!QDF_IS_STATUS_SUCCESS(status)) {
1291 hdd_err("Get infra_dir_ac_be failed");
1292 return;
1293 }
1294 tspec.ts_info.direction = dir_ac;
1295
1296 tspec.ts_info.tid = 255;
1297 status = ucfg_mlme_get_wmm_uapsd_be_srv_intv(hdd_ctx->psoc,
1298 &uapsd_value);
1299 if (!QDF_IS_STATUS_SUCCESS(status)) {
1300 hdd_err("Get uapsd_vi_srv_intv failed");
1301 return;
1302 }
1303 tspec.min_service_interval = uapsd_value;
1304
1305 status = ucfg_mlme_get_wmm_uapsd_be_sus_intv(hdd_ctx->psoc,
1306 &uapsd_value);
1307 if (!QDF_IS_STATUS_SUCCESS(status)) {
1308 hdd_err("Get uapsd_vi_sus_intv failed");
1309 return;
1310 }
1311 tspec.suspension_interval = uapsd_value;
1312
1313 status = ucfg_mlme_get_wmm_mean_data_rate_ac_be(hdd_ctx->psoc,
1314 &rate_ac);
1315 if (!QDF_IS_STATUS_SUCCESS(status)) {
1316 hdd_err("Get mean_data_rate_ac_be failed");
1317 return;
1318 }
1319 tspec.mean_data_rate = rate_ac;
1320
1321 status = ucfg_mlme_get_wmm_min_phy_rate_ac_be(hdd_ctx->psoc,
1322 &rate_ac);
1323 if (!QDF_IS_STATUS_SUCCESS(status)) {
1324 hdd_err("Get min_phy_rate_ac_be failed");
1325 return;
1326 }
1327 tspec.min_phy_rate = rate_ac;
1328
1329 status = ucfg_mlme_get_wmm_nom_msdu_size_ac_be(hdd_ctx->psoc,
1330 &nom_msdu_size_ac);
1331 if (!QDF_IS_STATUS_SUCCESS(status)) {
1332 hdd_err("Get nom_msdu_size_ac_be failed");
1333 return;
1334 }
1335 tspec.nominal_msdu_size = nom_msdu_size_ac;
1336
1337 status = ucfg_mlme_get_wmm_sba_ac_be(hdd_ctx->psoc, &sba_ac);
1338 if (!QDF_IS_STATUS_SUCCESS(status)) {
1339 hdd_err("Get sba_ac_be failed");
1340 return;
1341 }
1342 tspec.surplus_bw_allowance = sba_ac;
1343
1344 break;
1345 case SME_AC_BK:
1346 tspec.ts_info.up = SME_QOS_WMM_UP_BK;
1347 /* Check if there is any valid configuration from framework */
1348 if (HDD_PSB_CFG_INVALID == adapter->configured_psb) {
1349 status = ucfg_mlme_get_wmm_uapsd_mask(hdd_ctx->psoc,
1350 &mask);
1351 if (!QDF_IS_STATUS_SUCCESS(status)) {
1352 hdd_err("Get uapsd_mask failed");
1353 return;
1354 }
1355 tspec.ts_info.psb = (mask & SME_QOS_UAPSD_BK) ? 1 : 0;
1356 }
1357
1358 status = ucfg_mlme_get_wmm_dir_ac_bk(hdd_ctx->psoc, &dir_ac);
1359 if (!QDF_IS_STATUS_SUCCESS(status)) {
1360 hdd_err("Get infra_dir_ac_bk failed");
1361 return;
1362 }
1363 tspec.ts_info.direction = dir_ac;
1364
1365 tspec.ts_info.tid = 255;
1366 status = ucfg_mlme_get_wmm_uapsd_bk_srv_intv(hdd_ctx->psoc,
1367 &uapsd_value);
1368 if (!QDF_IS_STATUS_SUCCESS(status)) {
1369 hdd_err("Get uapsd_bk_srv_intv failed");
1370 return;
1371 }
1372 tspec.min_service_interval = uapsd_value;
1373
1374 status = ucfg_mlme_get_wmm_uapsd_bk_sus_intv(hdd_ctx->psoc,
1375 &uapsd_value);
1376 if (!QDF_IS_STATUS_SUCCESS(status)) {
1377 hdd_err("Get uapsd_bk_sus_intv failed");
1378 return;
1379 }
1380 tspec.suspension_interval = uapsd_value;
1381
1382 status = ucfg_mlme_get_wmm_mean_data_rate_ac_bk(hdd_ctx->psoc,
1383 &rate_ac);
1384 if (!QDF_IS_STATUS_SUCCESS(status)) {
1385 hdd_err("Get mean_data_rate_ac_bk failed");
1386 return;
1387 }
1388 tspec.mean_data_rate = rate_ac;
1389
1390 status = ucfg_mlme_get_wmm_min_phy_rate_ac_bk(hdd_ctx->psoc,
1391 &rate_ac);
1392 if (!QDF_IS_STATUS_SUCCESS(status)) {
1393 hdd_err("Get min_phy_rate_ac_bk failed");
1394 return;
1395 }
1396 tspec.min_phy_rate = rate_ac;
1397
1398 status =
1399 ucfg_mlme_get_wmm_nom_msdu_size_ac_bk(hdd_ctx->psoc,
1400 &nom_msdu_size_ac);
1401 if (!QDF_IS_STATUS_SUCCESS(status)) {
1402 hdd_err("Get nom_msdu_size_ac_bk failed");
1403 return;
1404 }
1405 tspec.nominal_msdu_size = nom_msdu_size_ac;
1406
1407 status = ucfg_mlme_get_wmm_sba_ac_bk(hdd_ctx->psoc, &sba_ac);
1408 if (!QDF_IS_STATUS_SUCCESS(status)) {
1409 hdd_err("Get sba_ac_bk failed");
1410 return;
1411 }
1412 tspec.surplus_bw_allowance = sba_ac;
1413
1414 break;
1415 }
1416 #ifdef FEATURE_WLAN_ESE
1417 ucfg_mlme_get_inactivity_interval(hdd_ctx->psoc, &uapsd_value);
1418 tspec.inactivity_interval = uapsd_value;
1419 #endif
1420 ucfg_mlme_get_is_ts_burst_size_enable(hdd_ctx->psoc,
1421 &is_ts_burst_enable);
1422 tspec.ts_info.burst_size_defn = is_ts_burst_enable;
1423
1424 ucfg_mlme_get_ts_info_ack_policy(hdd_ctx->psoc, &ack_policy);
1425 switch (ack_policy) {
1426 case TS_INFO_ACK_POLICY_NORMAL_ACK:
1427 tspec.ts_info.ack_policy =
1428 SME_QOS_WMM_TS_ACK_POLICY_NORMAL_ACK;
1429 break;
1430
1431 case TS_INFO_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK:
1432 tspec.ts_info.ack_policy =
1433 SME_QOS_WMM_TS_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK;
1434 break;
1435
1436 default:
1437 /* unknown */
1438 tspec.ts_info.ack_policy =
1439 SME_QOS_WMM_TS_ACK_POLICY_NORMAL_ACK;
1440 }
1441
1442 if (tspec.ts_info.ack_policy ==
1443 SME_QOS_WMM_TS_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK) {
1444 if (!sme_qos_is_ts_info_ack_policy_valid(
1445 mac_handle, &tspec,
1446 adapter->deflink->vdev_id)) {
1447 tspec.ts_info.ack_policy =
1448 SME_QOS_WMM_TS_ACK_POLICY_NORMAL_ACK;
1449 }
1450 }
1451
1452 mutex_lock(&adapter->hdd_wmm_status.mutex);
1453 list_add(&qos_context->node, &adapter->hdd_wmm_status.context_list);
1454 mutex_unlock(&adapter->hdd_wmm_status.mutex);
1455
1456 #ifndef WLAN_MDM_CODE_REDUCTION_OPT
1457 sme_status = sme_qos_setup_req(mac_handle,
1458 adapter->deflink->vdev_id,
1459 &tspec,
1460 hdd_wmm_sme_callback,
1461 qos_context,
1462 tspec.ts_info.up,
1463 &qos_context->flow_id);
1464
1465 hdd_debug("sme_qos_setup_req returned %d flowid %d",
1466 sme_status, qos_context->flow_id);
1467
1468 /* need to check the return values and act appropriately */
1469 switch (sme_status) {
1470 case SME_QOS_STATUS_SETUP_REQ_PENDING_RSP:
1471 case SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_PENDING:
1472 /* setup is pending, so no more work to do now. all
1473 * further work will be done in hdd_wmm_sme_callback()
1474 */
1475 hdd_debug("Setup is pending, no further work");
1476
1477 break;
1478
1479 case SME_QOS_STATUS_SETUP_FAILURE_RSP:
1480 /* disable the inactivity timer */
1481 hdd_wmm_disable_inactivity_timer(qos_context);
1482
1483 /* we can't tell the difference between when a request
1484 * fails because AP rejected it versus when SME
1485 * encountered an internal error. in either case SME
1486 * won't ever reference this context so free the
1487 * record
1488 */
1489 hdd_wmm_free_context(qos_context);
1490 /* start packets flowing */
1491 fallthrough;
1492 case SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP:
1493 /* no ACM in effect, no need to setup U-APSD */
1494 case SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY:
1495 /* no ACM in effect, U-APSD is desired but was already setup */
1496
1497 /* for these cases everything is already setup so we
1498 * can signal TL that it has work to do
1499 */
1500 hdd_debug("Setup is complete, notify TL");
1501
1502 ac->is_access_allowed = true;
1503 ac->was_access_granted = true;
1504 ac->is_access_pending = false;
1505
1506 break;
1507
1508 default:
1509 hdd_err("unexpected SME Status=%d", sme_status);
1510 QDF_ASSERT(0);
1511 }
1512 #endif
1513
1514 }
1515
1516 /**
1517 * hdd_wmm_do_implicit_qos() - SSR wrapper function for hdd_wmm_do_implicit_qos
1518 * @work: pointer to work_struct
1519 *
1520 * Return: none
1521 */
hdd_wmm_do_implicit_qos(struct work_struct * work)1522 static void hdd_wmm_do_implicit_qos(struct work_struct *work)
1523 {
1524 struct hdd_wmm_qos_context *qos_ctx =
1525 container_of(work, struct hdd_wmm_qos_context,
1526 implicit_qos_work);
1527 struct osif_vdev_sync *vdev_sync;
1528
1529 if (qos_ctx->magic != HDD_WMM_CTX_MAGIC) {
1530 hdd_err("Invalid QoS Context");
1531 return;
1532 }
1533
1534 if (osif_vdev_sync_op_start(qos_ctx->adapter->dev, &vdev_sync))
1535 return;
1536
1537 __hdd_wmm_do_implicit_qos(qos_ctx);
1538
1539 osif_vdev_sync_op_stop(vdev_sync);
1540 }
1541
hdd_send_dscp_up_map_to_fw(struct hdd_adapter * adapter)1542 QDF_STATUS hdd_send_dscp_up_map_to_fw(struct hdd_adapter *adapter)
1543 {
1544 uint32_t *dscp_to_up_map = adapter->dscp_to_up_map;
1545 struct wlan_objmgr_vdev *vdev;
1546 int ret;
1547
1548 vdev = hdd_objmgr_get_vdev_by_user(adapter->deflink, WLAN_FWOL_NB_ID);
1549
1550 if (vdev) {
1551 /* Send DSCP to TID map table to FW */
1552 ret = os_if_fwol_send_dscp_up_map_to_fw(vdev, dscp_to_up_map);
1553 hdd_objmgr_put_vdev_by_user(vdev, WLAN_FWOL_NB_ID);
1554 if (ret && ret != -EOPNOTSUPP)
1555 return QDF_STATUS_E_FAILURE;
1556 }
1557
1558 return QDF_STATUS_SUCCESS;
1559 }
1560
1561 /**
1562 * hdd_fill_dscp_to_up_map() - Fill up dscp_to_up_map table with default values
1563 * @dscp_to_up_map: Array of DSCP-to-UP map
1564 *
1565 * This function will fill up the DSCP-to-UP map table with default values.
1566 *
1567 * Return: QDF_STATUS enumeration
1568 */
hdd_fill_dscp_to_up_map(enum sme_qos_wmmuptype * dscp_to_up_map)1569 static inline void hdd_fill_dscp_to_up_map(
1570 enum sme_qos_wmmuptype *dscp_to_up_map)
1571 {
1572 uint8_t dscp;
1573
1574 /*
1575 * DSCP to User Priority Lookup Table
1576 * By default use the 3 Precedence bits of DSCP as the User Priority
1577 *
1578 * In case of changing the default map values, need to take care of
1579 * hdd_custom_dscp_up_map as well.
1580 */
1581 for (dscp = 0; dscp <= WLAN_MAX_DSCP; dscp++)
1582 dscp_to_up_map[dscp] = dscp >> 3;
1583
1584 /* Special case for Expedited Forwarding (DSCP 46) in default mapping */
1585 dscp_to_up_map[DSCP(46)] = SME_QOS_WMM_UP_VO;
1586 }
1587
1588 #ifdef WLAN_CUSTOM_DSCP_UP_MAP
1589 /**
1590 * hdd_custom_dscp_up_map() - Customize dscp_to_up_map based on RFC8325
1591 * @dscp_to_up_map: Array of DSCP-to-UP map
1592 *
1593 * This function will customize the DSCP-to-UP map table based on RFC8325..
1594 *
1595 * Return: QDF_STATUS enumeration
1596 */
hdd_custom_dscp_up_map(enum sme_qos_wmmuptype * dscp_to_up_map)1597 static inline QDF_STATUS hdd_custom_dscp_up_map(
1598 enum sme_qos_wmmuptype *dscp_to_up_map)
1599 {
1600 /*
1601 * Customizing few of DSCP to UP mapping based on RFC8325,
1602 * those are different from default hdd_fill_dscp_to_up_map values.
1603 * So, below changes are always relative to hdd_fill_dscp_to_up_map.
1604 */
1605 dscp_to_up_map[DSCP(10)] = SME_QOS_WMM_UP_BE;
1606 dscp_to_up_map[DSCP(12)] = SME_QOS_WMM_UP_BE;
1607 dscp_to_up_map[DSCP(14)] = SME_QOS_WMM_UP_BE;
1608 dscp_to_up_map[DSCP(16)] = SME_QOS_WMM_UP_BE;
1609
1610 dscp_to_up_map[DSCP(18)] = SME_QOS_WMM_UP_EE;
1611 dscp_to_up_map[DSCP(20)] = SME_QOS_WMM_UP_EE;
1612 dscp_to_up_map[DSCP(22)] = SME_QOS_WMM_UP_EE;
1613
1614 dscp_to_up_map[DSCP(24)] = SME_QOS_WMM_UP_CL;
1615 dscp_to_up_map[DSCP(26)] = SME_QOS_WMM_UP_CL;
1616 dscp_to_up_map[DSCP(28)] = SME_QOS_WMM_UP_CL;
1617 dscp_to_up_map[DSCP(30)] = SME_QOS_WMM_UP_CL;
1618
1619 dscp_to_up_map[DSCP(44)] = SME_QOS_WMM_UP_VO;
1620
1621 dscp_to_up_map[DSCP(48)] = SME_QOS_WMM_UP_NC;
1622
1623 return QDF_STATUS_SUCCESS;
1624 }
1625 #else
hdd_custom_dscp_up_map(enum sme_qos_wmmuptype * dscp_to_up_map)1626 static inline QDF_STATUS hdd_custom_dscp_up_map(
1627 enum sme_qos_wmmuptype *dscp_to_up_map)
1628 {
1629 return QDF_STATUS_E_NOSUPPORT;
1630 }
1631 #endif /* WLAN_CUSTOM_DSCP_UP_MAP */
1632
1633 /**
1634 * hdd_wmm_dscp_initial_state() - initialize the WMM DSCP configuration
1635 * @adapter : [in] pointer to Adapter context
1636 *
1637 * This function will initialize the WMM DSCP configuration of an
1638 * adapter to an initial state. The configuration can later be
1639 * overwritten via application APIs or via QoS Map sent OTA.
1640 *
1641 * Return: QDF_STATUS enumeration
1642 */
hdd_wmm_dscp_initial_state(struct hdd_adapter * adapter)1643 QDF_STATUS hdd_wmm_dscp_initial_state(struct hdd_adapter *adapter)
1644 {
1645 enum sme_qos_wmmuptype *dscp_to_up_map = adapter->dscp_to_up_map;
1646 struct wlan_objmgr_psoc *psoc = adapter->hdd_ctx->psoc;
1647 QDF_STATUS status = QDF_STATUS_SUCCESS;
1648
1649 if (!psoc) {
1650 hdd_err("Invalid psoc handle");
1651 return QDF_STATUS_E_FAILURE;
1652 }
1653
1654 hdd_fill_dscp_to_up_map(dscp_to_up_map);
1655
1656 if (hdd_custom_dscp_up_map(dscp_to_up_map) == QDF_STATUS_SUCCESS) {
1657 /* Send DSCP to TID map table to FW */
1658 status = hdd_send_dscp_up_map_to_fw(adapter);
1659 }
1660
1661 return status;
1662 }
1663
1664 /**
1665 * hdd_wmm_adapter_init() - initialize the WMM configuration of an adapter
1666 * @adapter: [in] pointer to Adapter context
1667 *
1668 * This function will initialize the WMM configuration and status of an
1669 * adapter to an initial state. The configuration can later be
1670 * overwritten via application APIs
1671 *
1672 * Return: QDF_STATUS enumeration
1673 */
hdd_wmm_adapter_init(struct hdd_adapter * adapter)1674 QDF_STATUS hdd_wmm_adapter_init(struct hdd_adapter *adapter)
1675 {
1676 struct hdd_wmm_ac_status *ac_status;
1677 sme_ac_enum_type ac_type;
1678
1679 hdd_enter();
1680
1681 hdd_wmm_dscp_initial_state(adapter);
1682
1683 adapter->hdd_wmm_status.qap = false;
1684 INIT_LIST_HEAD(&adapter->hdd_wmm_status.context_list);
1685 mutex_init(&adapter->hdd_wmm_status.mutex);
1686
1687 for (ac_type = 0; ac_type < WLAN_MAX_AC; ac_type++) {
1688 ac_status = &adapter->hdd_wmm_status.ac_status[ac_type];
1689 ac_status->is_access_required = false;
1690 ac_status->is_access_needed = false;
1691 ac_status->is_access_pending = false;
1692 ac_status->has_access_failed = false;
1693 ac_status->was_access_granted = false;
1694 ac_status->is_access_allowed = false;
1695 ac_status->is_tspec_valid = false;
1696 ac_status->is_uapsd_info_valid = false;
1697 }
1698 /* Invalid value(0xff) to indicate psb not configured through
1699 * framework initially.
1700 */
1701 adapter->configured_psb = HDD_PSB_CFG_INVALID;
1702
1703 return QDF_STATUS_SUCCESS;
1704 }
1705
1706 /**
1707 * hdd_wmm_adapter_clear() - Function which will clear the WMM status
1708 * for all the ACs
1709 *
1710 * @adapter: [in] pointer to Adapter context
1711 *
1712 * Return: QDF_STATUS enumeration
1713 */
hdd_wmm_adapter_clear(struct hdd_adapter * adapter)1714 QDF_STATUS hdd_wmm_adapter_clear(struct hdd_adapter *adapter)
1715 {
1716 struct hdd_wmm_ac_status *ac_status;
1717 sme_ac_enum_type ac_type;
1718
1719 hdd_enter();
1720 for (ac_type = 0; ac_type < WLAN_MAX_AC; ac_type++) {
1721 ac_status = &adapter->hdd_wmm_status.ac_status[ac_type];
1722 ac_status->is_access_required = false;
1723 ac_status->is_access_needed = false;
1724 ac_status->is_access_pending = false;
1725 ac_status->has_access_failed = false;
1726 ac_status->was_access_granted = false;
1727 ac_status->is_access_allowed = false;
1728 ac_status->is_tspec_valid = false;
1729 ac_status->is_uapsd_info_valid = false;
1730 }
1731 return QDF_STATUS_SUCCESS;
1732 }
1733
1734 /**
1735 * hdd_wmm_adapter_close() - WMM close function
1736 * @adapter: [in] pointer to adapter context
1737 *
1738 * Function which will perform any necessary work to to clean up the
1739 * WMM functionality prior to the kernel module unload.
1740 *
1741 * Return: QDF_STATUS enumeration
1742 */
hdd_wmm_adapter_close(struct hdd_adapter * adapter)1743 QDF_STATUS hdd_wmm_adapter_close(struct hdd_adapter *adapter)
1744 {
1745 struct hdd_wmm_qos_context *qos_context;
1746
1747 hdd_enter();
1748
1749 /* free any context records that we still have linked */
1750 while (!list_empty(&adapter->hdd_wmm_status.context_list)) {
1751 qos_context =
1752 list_first_entry(&adapter->hdd_wmm_status.context_list,
1753 struct hdd_wmm_qos_context, node);
1754
1755 hdd_wmm_disable_inactivity_timer(qos_context);
1756
1757 if (qos_context->handle == HDD_WMM_HANDLE_IMPLICIT
1758 && qos_context->magic == HDD_WMM_CTX_MAGIC)
1759 cds_flush_work(&qos_context->implicit_qos_work);
1760
1761 hdd_wmm_free_context(qos_context);
1762 }
1763
1764 return QDF_STATUS_SUCCESS;
1765 }
1766
1767 /**
1768 * hdd_check_upgrade_vo_vi_qos() - Check and upgrade QOS for UDP packets
1769 * based on request type received
1770 * @adapter: [in] pointer to the adapter context (Should not be invalid)
1771 * @user_pri: [out] priority set for this packet
1772 *
1773 * This function checks for the request type and upgrade based on request type
1774 *
1775 * UDP_QOS_UPGRADE_ALL: Upgrade QoS of all UDP packets if the current set
1776 * priority is below the pre-configured threshold for upgrade.
1777 *
1778 * UDP_QOS_UPGRADE_BK_BE: Upgrade QoS of all UDP packets if the current set
1779 * priority is below the AC VI.
1780 */
1781 static inline void
hdd_check_upgrade_vo_vi_qos(struct hdd_adapter * adapter,enum sme_qos_wmmuptype * user_pri)1782 hdd_check_upgrade_vo_vi_qos(struct hdd_adapter *adapter,
1783 enum sme_qos_wmmuptype *user_pri)
1784 {
1785 switch (adapter->udp_qos_upgrade_type) {
1786 case UDP_QOS_UPGRADE_ALL:
1787 if (*user_pri <
1788 qca_wlan_ac_to_sme_qos(adapter->upgrade_udp_qos_threshold))
1789 *user_pri = qca_wlan_ac_to_sme_qos(
1790 adapter->upgrade_udp_qos_threshold);
1791 break;
1792 case UDP_QOS_UPGRADE_BK_BE:
1793 if (*user_pri < qca_wlan_ac_to_sme_qos(QCA_WLAN_AC_VI))
1794 *user_pri = qca_wlan_ac_to_sme_qos(
1795 adapter->upgrade_udp_qos_threshold);
1796 break;
1797 default:
1798 break;
1799 }
1800 }
1801
1802 /**
1803 * hdd_check_and_upgrade_udp_qos() - Check and upgrade the qos for UDP packets
1804 * if the current set priority is below the
1805 * pre-configured threshold for upgrade.
1806 * @adapter: [in] pointer to the adapter context (Should not be invalid)
1807 * @skb: [in] pointer to the packet to be transmitted
1808 * @user_pri: [out] priority set for this packet
1809 *
1810 * This function checks if the packet is a UDP packet and upgrades its
1811 * priority if its below the pre-configured upgrade threshold.
1812 * The upgrade order is as below:
1813 * BK -> BE -> VI -> VO
1814 *
1815 * Return: none
1816 */
1817 static inline void
hdd_check_and_upgrade_udp_qos(struct hdd_adapter * adapter,qdf_nbuf_t skb,enum sme_qos_wmmuptype * user_pri)1818 hdd_check_and_upgrade_udp_qos(struct hdd_adapter *adapter,
1819 qdf_nbuf_t skb,
1820 enum sme_qos_wmmuptype *user_pri)
1821 {
1822 /* Upgrade UDP pkt priority alone */
1823 if (!(qdf_nbuf_is_ipv4_udp_pkt(skb) || qdf_nbuf_is_ipv6_udp_pkt(skb)))
1824 return;
1825
1826 switch (adapter->upgrade_udp_qos_threshold) {
1827 case QCA_WLAN_AC_BK:
1828 break;
1829 case QCA_WLAN_AC_BE:
1830 if (*user_pri == qca_wlan_ac_to_sme_qos(QCA_WLAN_AC_BK))
1831 *user_pri = qca_wlan_ac_to_sme_qos(QCA_WLAN_AC_BE);
1832
1833 break;
1834 case QCA_WLAN_AC_VI:
1835 case QCA_WLAN_AC_VO:
1836 hdd_check_upgrade_vo_vi_qos(adapter, user_pri);
1837 break;
1838 default:
1839 break;
1840 }
1841 }
1842
1843 /**
1844 * hdd_wmm_classify_critical_pkt() - Function checks and classifies critical skb
1845 * @skb: pointer to network buffer
1846 * @user_pri: user priority of the OS packet to be determined
1847 * @is_critical: pointer to be marked true for a critical packet
1848 *
1849 * Function checks if the packet is one of the critical packets and determines
1850 * 'user_pri' for it. EAPOL, ARP, DHCP(v4,v6), NS, NA are considered critical.
1851 *
1852 * Note that wlan_hdd_mark_critical_pkt is used to mark packet type in CB for
1853 * these critical packets. This is done as skb->cb amay be overwritten between
1854 * _select_queue and_hard_start_xmit functions. hdd_wmm_classify_critical_pkt
1855 * and wlan_hdd_mark_critical_pkt should be in sync w.r.t packet types.
1856 *
1857 * Return: None
1858 */
1859 static
hdd_wmm_classify_critical_pkt(struct sk_buff * skb,enum sme_qos_wmmuptype * user_pri,bool * is_critical)1860 void hdd_wmm_classify_critical_pkt(struct sk_buff *skb,
1861 enum sme_qos_wmmuptype *user_pri,
1862 bool *is_critical)
1863 {
1864 enum qdf_proto_subtype proto_subtype;
1865
1866 /* Send EAPOL on TID 6(VO). Rest are sent on TID 0(BE). */
1867
1868 if (qdf_nbuf_is_ipv4_eapol_pkt(skb)) {
1869 *is_critical = true;
1870 *user_pri = SME_QOS_WMM_UP_VO;
1871 } else if (qdf_nbuf_is_ipv4_arp_pkt(skb)) {
1872 *is_critical = true;
1873 *user_pri = SME_QOS_WMM_UP_BE;
1874 } else if (qdf_nbuf_is_ipv4_dhcp_pkt(skb)) {
1875 *is_critical = true;
1876 *user_pri = SME_QOS_WMM_UP_BE;
1877 } else if (qdf_nbuf_is_ipv6_dhcp_pkt(skb)) {
1878 *is_critical = true;
1879 *user_pri = SME_QOS_WMM_UP_BE;
1880 } else if (qdf_nbuf_is_icmpv6_pkt(skb)) {
1881 proto_subtype = qdf_nbuf_get_icmpv6_subtype(skb);
1882 switch (proto_subtype) {
1883 case QDF_PROTO_ICMPV6_NA:
1884 case QDF_PROTO_ICMPV6_NS:
1885 *is_critical = true;
1886 *user_pri = SME_QOS_WMM_UP_BE;
1887 break;
1888 default:
1889 break;
1890 }
1891 }
1892 }
1893
1894 #ifdef DP_TRAFFIC_END_INDICATION
1895 /**
1896 * hdd_wmm_traffic_end_indication_is_enable() - Get feature enable/disable
1897 * status
1898 * @adapter: hdd adapter handle
1899 *
1900 * Return: true if feature is enable else false
1901 */
1902 static inline bool
hdd_wmm_traffic_end_indication_is_enable(struct hdd_adapter * adapter)1903 hdd_wmm_traffic_end_indication_is_enable(struct hdd_adapter *adapter)
1904 {
1905 return qdf_unlikely(adapter->traffic_end_ind_en);
1906 }
1907 #else
1908 static inline bool
hdd_wmm_traffic_end_indication_is_enable(struct hdd_adapter * adapter)1909 hdd_wmm_traffic_end_indication_is_enable(struct hdd_adapter *adapter)
1910 {
1911 return false;
1912 }
1913 #endif
1914
1915 static
hdd_wmm_get_user_priority_from_ip_tos(struct hdd_adapter * adapter,struct sk_buff * skb,enum sme_qos_wmmuptype * user_pri)1916 void hdd_wmm_get_user_priority_from_ip_tos(struct hdd_adapter *adapter,
1917 struct sk_buff *skb,
1918 enum sme_qos_wmmuptype *user_pri)
1919
1920 {
1921 unsigned char dscp;
1922 unsigned char tos;
1923 union generic_ethhdr *eth_hdr;
1924 struct iphdr *ip_hdr;
1925 struct ipv6hdr *ipv6hdr;
1926 unsigned char *pkt;
1927 struct wlan_objmgr_psoc *psoc;
1928
1929 /* this code is executed for every packet therefore
1930 * all debug code is kept conditional
1931 */
1932
1933 #ifdef HDD_WMM_DEBUG
1934 hdd_enter();
1935 #endif /* HDD_WMM_DEBUG */
1936
1937 pkt = skb->data;
1938 eth_hdr = (union generic_ethhdr *)pkt;
1939
1940 #ifdef HDD_WMM_DEBUG
1941 hdd_debug("proto is 0x%04x", skb->protocol);
1942 #endif /* HDD_WMM_DEBUG */
1943
1944 if (eth_hdr->eth_II.h_proto == htons(ETH_P_IP)) {
1945 /* case 1: Ethernet II IP packet */
1946 ip_hdr = (struct iphdr *)&pkt[sizeof(eth_hdr->eth_II)];
1947 tos = ip_hdr->tos;
1948 #ifdef HDD_WMM_DEBUG
1949 hdd_debug("Ethernet II IP Packet, tos is %d", tos);
1950 #endif /* HDD_WMM_DEBUG */
1951
1952 } else if (eth_hdr->eth_II.h_proto == htons(ETH_P_IPV6)) {
1953 ipv6hdr = ipv6_hdr(skb);
1954 tos = ntohs(*(const __be16 *)ipv6hdr) >> 4;
1955 #ifdef HDD_WMM_DEBUG
1956 hdd_debug("Ethernet II IPv6 Packet, tos is %d", tos);
1957 #endif /* HDD_WMM_DEBUG */
1958 } else if ((ntohs(eth_hdr->eth_II.h_proto) < WLAN_MIN_PROTO) &&
1959 (eth_hdr->eth_8023.h_snap.dsap == WLAN_SNAP_DSAP) &&
1960 (eth_hdr->eth_8023.h_snap.ssap == WLAN_SNAP_SSAP) &&
1961 (eth_hdr->eth_8023.h_snap.ctrl == WLAN_SNAP_CTRL) &&
1962 (eth_hdr->eth_8023.h_proto == htons(ETH_P_IP))) {
1963 /* case 2: 802.3 LLC/SNAP IP packet */
1964 ip_hdr = (struct iphdr *)&pkt[sizeof(eth_hdr->eth_8023)];
1965 tos = ip_hdr->tos;
1966 #ifdef HDD_WMM_DEBUG
1967 hdd_debug("802.3 LLC/SNAP IP Packet, tos is %d", tos);
1968 #endif /* HDD_WMM_DEBUG */
1969 } else if (eth_hdr->eth_II.h_proto == htons(ETH_P_8021Q)) {
1970 /* VLAN tagged */
1971
1972 if (eth_hdr->eth_IIv.h_vlan_encapsulated_proto ==
1973 htons(ETH_P_IP)) {
1974 /* case 3: Ethernet II vlan-tagged IP packet */
1975 ip_hdr =
1976 (struct iphdr *)
1977 &pkt[sizeof(eth_hdr->eth_IIv)];
1978 tos = ip_hdr->tos;
1979 #ifdef HDD_WMM_DEBUG
1980 hdd_debug("Ether II VLAN tagged IP Packet, tos is %d",
1981 tos);
1982 #endif /* HDD_WMM_DEBUG */
1983 } else if ((ntohs(eth_hdr->eth_IIv.h_vlan_encapsulated_proto)
1984 < WLAN_MIN_PROTO) &&
1985 (eth_hdr->eth_8023v.h_snap.dsap ==
1986 WLAN_SNAP_DSAP)
1987 && (eth_hdr->eth_8023v.h_snap.ssap ==
1988 WLAN_SNAP_SSAP)
1989 && (eth_hdr->eth_8023v.h_snap.ctrl ==
1990 WLAN_SNAP_CTRL)
1991 && (eth_hdr->eth_8023v.h_proto ==
1992 htons(ETH_P_IP))) {
1993 /* case 4: 802.3 LLC/SNAP vlan-tagged IP packet */
1994 ip_hdr =
1995 (struct iphdr *)
1996 &pkt[sizeof(eth_hdr->eth_8023v)];
1997 tos = ip_hdr->tos;
1998 #ifdef HDD_WMM_DEBUG
1999 hdd_debug("802.3 LLC/SNAP VLAN tagged IP Packet, tos is %d",
2000 tos);
2001 #endif /* HDD_WMM_DEBUG */
2002 } else {
2003 /* default */
2004 #ifdef HDD_WMM_DEBUG
2005 hdd_warn("VLAN tagged Unhandled Protocol, using default tos");
2006 #endif /* HDD_WMM_DEBUG */
2007 tos = 0;
2008 }
2009 } else {
2010 /* default */
2011 #ifdef HDD_WMM_DEBUG
2012 hdd_warn("Unhandled Protocol, using default tos");
2013 #endif /* HDD_WMM_DEBUG */
2014 /* Give the highest priority to 802.1x packet */
2015 if (eth_hdr->eth_II.h_proto ==
2016 htons(HDD_ETHERTYPE_802_1_X)) {
2017 tos = 0xC0;
2018 } else
2019 tos = 0;
2020 }
2021
2022 dscp = (tos >> 2) & 0x3f;
2023 if (hdd_wmm_traffic_end_indication_is_enable(adapter)) {
2024 psoc = adapter->hdd_ctx->psoc;
2025 ucfg_dp_traffic_end_indication_update_dscp(
2026 psoc, adapter->deflink->vdev_id, &dscp);
2027 }
2028 *user_pri = adapter->dscp_to_up_map[dscp];
2029
2030 #ifdef HDD_WMM_DEBUG
2031 hdd_debug("tos is %d, dscp is %d, up is %d", tos, dscp, *user_pri);
2032 #endif /* HDD_WMM_DEBUG */
2033 }
2034
2035 /**
2036 * hdd_wmm_classify_pkt() - Function to classify skb into WMM AC based on DSCP
2037 *
2038 * @adapter: adapter upon which the packet is being transmitted
2039 * @skb: pointer to network buffer
2040 * @user_pri: user priority of the OS packet
2041 * @is_critical: pointer to be marked true for a critical packet
2042 *
2043 * Function checks if the packet is one of the critical packets and determines
2044 * 'user_pri' for it. Else it uses IP TOS value to determine 'user_pri'.
2045 * It is the responsibility of caller to set the user_pri to skb->priority.
2046 * Return: None
2047 */
2048 static
hdd_wmm_classify_pkt(struct hdd_adapter * adapter,struct sk_buff * skb,enum sme_qos_wmmuptype * user_pri,bool * is_critical)2049 void hdd_wmm_classify_pkt(struct hdd_adapter *adapter,
2050 struct sk_buff *skb,
2051 enum sme_qos_wmmuptype *user_pri,
2052 bool *is_critical)
2053 {
2054 hdd_wmm_classify_critical_pkt(skb, user_pri, is_critical);
2055
2056 if (false == *is_critical) {
2057 hdd_wmm_get_user_priority_from_ip_tos(adapter, skb, user_pri);
2058 hdd_check_and_upgrade_udp_qos(adapter, skb, user_pri);
2059 }
2060 }
2061
2062 #ifdef QCA_SUPPORT_TX_MIN_RATES_FOR_SPECIAL_FRAMES
hdd_wmm_classify_pkt_cb(void * adapter,struct sk_buff * skb)2063 void hdd_wmm_classify_pkt_cb(void *adapter,
2064 struct sk_buff *skb)
2065 {
2066 enum sme_qos_wmmuptype user_pri = SME_QOS_WMM_UP_BE;
2067 bool is_critical = false;
2068
2069 hdd_wmm_classify_critical_pkt(skb, &user_pri, &is_critical);
2070
2071 if (is_critical) {
2072 skb->priority = user_pri;
2073 QDF_NBUF_CB_TX_EXTRA_IS_CRITICAL(skb) = true;
2074 }
2075 }
2076 #endif
2077
2078 #ifdef TX_MULTIQ_PER_AC
2079 /**
2080 * hdd_get_tx_queue_for_ac() - Get the netdev tx queue index
2081 * based on access category
2082 * @adapter: adapter upon which the packet is being transmitted
2083 * @skb: pointer to network buffer
2084 * @ac: access category
2085 *
2086 * Return: tx queue index
2087 */
2088 static
hdd_get_tx_queue_for_ac(struct hdd_adapter * adapter,struct sk_buff * skb,uint16_t ac)2089 uint16_t hdd_get_tx_queue_for_ac(struct hdd_adapter *adapter,
2090 struct sk_buff *skb, uint16_t ac)
2091 {
2092 struct sock *sk = skb->sk;
2093 int new_index;
2094 int cpu = qdf_get_smp_processor_id();
2095 struct hdd_tx_rx_stats *stats =
2096 &adapter->deflink->hdd_stats.tx_rx_stats;
2097
2098 if (qdf_unlikely(ac == HDD_LINUX_AC_HI_PRIO))
2099 return TX_GET_QUEUE_IDX(HDD_LINUX_AC_HI_PRIO, 0);
2100
2101 if (!sk) {
2102 /*
2103 * Neither valid socket nor skb_hash so default to the
2104 * first queue for the access category.
2105 */
2106 if (qdf_unlikely(!skb->sw_hash && !skb->l4_hash)) {
2107 ++stats->per_cpu[cpu].inv_sk_and_skb_hash;
2108
2109 return TX_GET_QUEUE_IDX(ac, 0);
2110 }
2111 ++stats->per_cpu[cpu].qselect_existing_skb_hash;
2112
2113 return TX_GET_QUEUE_IDX(ac,
2114 reciprocal_scale(skb->hash,
2115 TX_QUEUES_PER_AC));
2116 }
2117
2118 if (sk->sk_tx_queue_mapping != NO_QUEUE_MAPPING &&
2119 sk->sk_tx_queue_mapping < NUM_TX_QUEUES) {
2120 ++stats->per_cpu[cpu].qselect_sk_tx_map;
2121 return sk->sk_tx_queue_mapping;
2122 }
2123
2124 ++stats->per_cpu[cpu].qselect_skb_hash_calc;
2125 new_index = TX_GET_QUEUE_IDX(ac,
2126 reciprocal_scale(skb_get_hash(skb),
2127 TX_QUEUES_PER_AC));
2128
2129 if (sk_fullsock(sk) && rcu_access_pointer(sk->sk_dst_cache))
2130 sk_tx_queue_set(sk, new_index);
2131
2132 return new_index;
2133 }
2134 #else
2135 static inline
hdd_get_tx_queue_for_ac(struct hdd_adapter * adapter,struct sk_buff * skb,uint16_t ac)2136 uint16_t hdd_get_tx_queue_for_ac(struct hdd_adapter *adapter,
2137 struct sk_buff *skb, uint16_t ac) {
2138 return ac;
2139 }
2140 #endif
2141
2142 /**
2143 * __hdd_get_queue_index() - get queue index
2144 * @up: user priority
2145 *
2146 * Return: queue_index
2147 */
__hdd_get_queue_index(uint16_t up)2148 static uint16_t __hdd_get_queue_index(uint16_t up)
2149 {
2150 if (qdf_unlikely(up >= ARRAY_SIZE(hdd_linux_up_to_ac_map)))
2151 return HDD_LINUX_AC_BE;
2152 return hdd_linux_up_to_ac_map[up];
2153 }
2154
2155 #if defined(QCA_LL_TX_FLOW_CONTROL_V2) || \
2156 defined(QCA_HL_NETDEV_FLOW_CONTROL) || \
2157 defined(QCA_LL_PDEV_TX_FLOW_CONTROL)
2158 /**
2159 * hdd_get_queue_index() - get queue index
2160 * @up: user priority
2161 * @is_critical: is_critical flag
2162 *
2163 * Return: queue_index
2164 */
2165 static
hdd_get_queue_index(uint16_t up,bool is_critical)2166 uint16_t hdd_get_queue_index(uint16_t up, bool is_critical)
2167 {
2168 if (qdf_unlikely(is_critical))
2169 return HDD_LINUX_AC_HI_PRIO;
2170 return __hdd_get_queue_index(up);
2171 }
2172 #else
2173 static
hdd_get_queue_index(uint16_t up,bool is_critical)2174 uint16_t hdd_get_queue_index(uint16_t up, bool is_critical)
2175 {
2176 return __hdd_get_queue_index(up);
2177 }
2178 #endif
2179
2180 #ifdef DP_TX_PACKET_INSPECT_FOR_ILP
2181 /**
2182 * hdd_update_pkt_priority_with_inspection() - update TX packets priority
2183 * @skb: network buffer
2184 * @up: user priority
2185 *
2186 * Update TX packets priority, if some special TX packets like TCP ack,
2187 * reuse skb->priority upper 8 bits(bit24 ~ 31) to mark them.
2188 *
2189 * Return: None
2190 */
2191 static inline
hdd_update_pkt_priority_with_inspection(struct sk_buff * skb,enum sme_qos_wmmuptype up)2192 void hdd_update_pkt_priority_with_inspection(struct sk_buff *skb,
2193 enum sme_qos_wmmuptype up)
2194 {
2195 skb->priority = up;
2196
2197 if (qdf_unlikely(qdf_nbuf_is_ipv4_v6_pure_tcp_ack(skb)))
2198 qdf_nbuf_set_priority_pkt_type(
2199 skb, QDF_NBUF_PRIORITY_PKT_TCP_ACK);
2200 }
2201 #else
2202 static inline
hdd_update_pkt_priority_with_inspection(struct sk_buff * skb,enum sme_qos_wmmuptype up)2203 void hdd_update_pkt_priority_with_inspection(struct sk_buff *skb,
2204 enum sme_qos_wmmuptype up)
2205 {
2206 skb->priority = up;
2207 }
2208 #endif
2209
__hdd_wmm_select_queue(struct net_device * dev,struct sk_buff * skb)2210 static uint16_t __hdd_wmm_select_queue(struct net_device *dev,
2211 struct sk_buff *skb)
2212 {
2213 enum sme_qos_wmmuptype up = SME_QOS_WMM_UP_BE;
2214 uint16_t index;
2215 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
2216 bool is_critical = false;
2217 struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
2218
2219 if (qdf_unlikely(!hdd_ctx || cds_is_driver_transitioning())) {
2220 hdd_debug_rl("driver is transitioning! Using default(BE) queue.");
2221 skb->priority = SME_QOS_WMM_UP_BE;
2222 return TX_GET_QUEUE_IDX(HDD_LINUX_AC_BE, 0);
2223 }
2224
2225 /* Get the user priority from IP header */
2226 hdd_wmm_classify_pkt(adapter, skb, &up, &is_critical);
2227
2228 hdd_update_pkt_priority_with_inspection(skb, up);
2229
2230 index = hdd_get_queue_index(up, is_critical);
2231
2232 return hdd_get_tx_queue_for_ac(adapter, skb, index);
2233 }
2234
hdd_wmm_select_queue(struct net_device * dev,struct sk_buff * skb)2235 uint16_t hdd_wmm_select_queue(struct net_device *dev,
2236 struct sk_buff *skb)
2237 {
2238 uint16_t q_index;
2239
2240 q_index = __hdd_wmm_select_queue(dev, skb);
2241
2242 return q_index;
2243 }
2244
2245 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0))
hdd_select_queue(struct net_device * dev,struct sk_buff * skb,struct net_device * sb_dev)2246 uint16_t hdd_select_queue(struct net_device *dev, struct sk_buff *skb,
2247 struct net_device *sb_dev)
2248 {
2249 return hdd_wmm_select_queue(dev, skb);
2250 }
2251 #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0))
hdd_select_queue(struct net_device * dev,struct sk_buff * skb,struct net_device * sb_dev,select_queue_fallback_t fallback)2252 uint16_t hdd_select_queue(struct net_device *dev, struct sk_buff *skb,
2253 struct net_device *sb_dev,
2254 select_queue_fallback_t fallback)
2255 {
2256 return hdd_wmm_select_queue(dev, skb);
2257 }
2258 #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
hdd_select_queue(struct net_device * dev,struct sk_buff * skb,void * accel_priv,select_queue_fallback_t fallback)2259 uint16_t hdd_select_queue(struct net_device *dev, struct sk_buff *skb,
2260 void *accel_priv, select_queue_fallback_t fallback)
2261 {
2262 return hdd_wmm_select_queue(dev, skb);
2263 }
2264 #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0))
hdd_select_queue(struct net_device * dev,struct sk_buff * skb,void * accel_priv)2265 uint16_t hdd_select_queue(struct net_device *dev, struct sk_buff *skb,
2266 void *accel_priv)
2267 {
2268 return hdd_wmm_select_queue(dev, skb);
2269 }
2270 #else
hdd_select_queue(struct net_device * dev,struct sk_buff * skb)2271 uint16_t hdd_select_queue(struct net_device *dev, struct sk_buff *skb)
2272 {
2273 return hdd_wmm_select_queue(dev, skb);
2274 }
2275 #endif
2276
2277
2278 /**
2279 * hdd_wmm_acquire_access_required() - Function which will determine
2280 * acquire admittance for a WMM AC is required or not based on psb configuration
2281 * done in framework
2282 *
2283 * @adapter: [in] pointer to adapter structure
2284 * @ac_type: [in] WMM AC type of OS packet
2285 *
2286 * Return: void
2287 */
hdd_wmm_acquire_access_required(struct hdd_adapter * adapter,sme_ac_enum_type ac_type)2288 void hdd_wmm_acquire_access_required(struct hdd_adapter *adapter,
2289 sme_ac_enum_type ac_type)
2290 {
2291 /* Each bit in the LSB nibble indicates 1 AC.
2292 * Clearing the particular bit in LSB nibble to indicate
2293 * access required
2294 */
2295 switch (ac_type) {
2296 case SME_AC_BK:
2297 /* clear first bit */
2298 adapter->psb_changed &= ~SME_QOS_UAPSD_CFG_BK_CHANGED_MASK;
2299 break;
2300 case SME_AC_BE:
2301 /* clear second bit */
2302 adapter->psb_changed &= ~SME_QOS_UAPSD_CFG_BE_CHANGED_MASK;
2303 break;
2304 case SME_AC_VI:
2305 /* clear third bit */
2306 adapter->psb_changed &= ~SME_QOS_UAPSD_CFG_VI_CHANGED_MASK;
2307 break;
2308 case SME_AC_VO:
2309 /* clear fourth bit */
2310 adapter->psb_changed &= ~SME_QOS_UAPSD_CFG_VO_CHANGED_MASK;
2311 break;
2312 default:
2313 hdd_err("Invalid AC Type");
2314 break;
2315 }
2316 }
2317
2318 /**
2319 * hdd_wmm_acquire_access() - Function which will attempt to acquire
2320 * admittance for a WMM AC
2321 *
2322 * @adapter: [in] pointer to adapter context
2323 * @ac_type: [in] WMM AC type of OS packet
2324 * @granted: [out] pointer to bool flag when indicates if access
2325 * has been granted or not
2326 *
2327 * Return: QDF_STATUS enumeration
2328 */
hdd_wmm_acquire_access(struct hdd_adapter * adapter,sme_ac_enum_type ac_type,bool * granted)2329 QDF_STATUS hdd_wmm_acquire_access(struct hdd_adapter *adapter,
2330 sme_ac_enum_type ac_type, bool *granted)
2331 {
2332 struct hdd_wmm_qos_context *qos_context;
2333 struct hdd_context *hdd_ctx;
2334 /* The ini ImplicitQosIsEnabled is deprecated. By default, the ini
2335 * value is disabled. So, setting the variable is_implicit_qos_enabled
2336 * value to false.
2337 */
2338 bool is_implicit_qos_enabled = false;
2339
2340 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
2341
2342 QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_DEBUG,
2343 "%s: Entered for AC %d", __func__, ac_type);
2344
2345 if (!hdd_wmm_is_active(adapter) || !(is_implicit_qos_enabled) ||
2346 !adapter->hdd_wmm_status.ac_status[ac_type].is_access_required) {
2347 /* either we don't want QoS or the AP doesn't support
2348 * QoS or we don't want to do implicit QoS
2349 */
2350 QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_DEBUG,
2351 "%s: QoS not configured on both ends ", __func__);
2352
2353 *granted =
2354 adapter->hdd_wmm_status.ac_status[ac_type].
2355 is_access_allowed;
2356
2357 return QDF_STATUS_SUCCESS;
2358 }
2359 /* do we already have an implicit QoS request pending for this AC? */
2360 if ((adapter->hdd_wmm_status.ac_status[ac_type].is_access_needed) ||
2361 (adapter->hdd_wmm_status.ac_status[ac_type].is_access_pending)) {
2362 /* request already pending so we need to wait for that
2363 * response
2364 */
2365 QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_DEBUG,
2366 "%s: Implicit QoS for TL AC %d already scheduled",
2367 __func__, ac_type);
2368
2369 *granted = false;
2370 return QDF_STATUS_SUCCESS;
2371 }
2372 /* did we already fail to establish implicit QoS for this AC?
2373 * (if so, access should have been granted when the failure
2374 * was handled)
2375 */
2376 if (adapter->hdd_wmm_status.ac_status[ac_type].has_access_failed) {
2377 /* request previously failed
2378 * allow access, but we'll be downgraded
2379 */
2380 QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_DEBUG,
2381 "%s: Implicit QoS for TL AC %d previously failed",
2382 __func__, ac_type);
2383
2384 if (!adapter->hdd_wmm_status.ac_status[ac_type].
2385 is_access_required) {
2386 adapter->hdd_wmm_status.ac_status[ac_type].
2387 is_access_allowed = true;
2388 *granted = true;
2389 } else {
2390 adapter->hdd_wmm_status.ac_status[ac_type].
2391 is_access_allowed = false;
2392 *granted = false;
2393 }
2394
2395 return QDF_STATUS_SUCCESS;
2396 }
2397 /* we need to establish implicit QoS */
2398 QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_DEBUG,
2399 "%s: Need to schedule implicit QoS for TL AC %d, adapter is %pK",
2400 __func__, ac_type, adapter);
2401
2402 adapter->hdd_wmm_status.ac_status[ac_type].is_access_needed = true;
2403
2404 qos_context = qdf_mem_malloc(sizeof(*qos_context));
2405 if (!qos_context) {
2406 /* no memory for QoS context. Nothing we can do but
2407 * let data flow
2408 */
2409 adapter->hdd_wmm_status.ac_status[ac_type].is_access_allowed =
2410 true;
2411 *granted = true;
2412 return QDF_STATUS_SUCCESS;
2413 }
2414
2415 qos_context->ac_type = ac_type;
2416 qos_context->adapter = adapter;
2417 qos_context->flow_id = 0;
2418 qos_context->handle = HDD_WMM_HANDLE_IMPLICIT;
2419 qos_context->magic = HDD_WMM_CTX_MAGIC;
2420 qos_context->is_inactivity_timer_running = false;
2421
2422 INIT_WORK(&qos_context->implicit_qos_work, hdd_wmm_do_implicit_qos);
2423
2424 QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_DEBUG,
2425 "%s: Scheduling work for AC %d, context %pK",
2426 __func__, ac_type, qos_context);
2427
2428 schedule_work(&qos_context->implicit_qos_work);
2429
2430 /* caller will need to wait until the work takes place and
2431 * TSPEC negotiation completes
2432 */
2433 *granted = false;
2434 return QDF_STATUS_SUCCESS;
2435 }
2436
hdd_wmm_assoc(struct hdd_adapter * adapter,bool is_reassoc,uint8_t uapsd_mask)2437 QDF_STATUS hdd_wmm_assoc(struct hdd_adapter *adapter,
2438 bool is_reassoc, uint8_t uapsd_mask)
2439 {
2440 QDF_STATUS status;
2441 uint32_t srv_value = 0;
2442 uint32_t sus_value = 0;
2443 struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
2444 uint32_t delayed_trgr_frm_int;
2445
2446 /* when we associate we need to notify TL if it needs to
2447 * enable UAPSD for any access categories
2448 */
2449
2450 hdd_enter();
2451
2452 if (is_reassoc) {
2453 /* when we reassociate we should continue to use
2454 * whatever parameters were previously established.
2455 * if we are reassociating due to a U-APSD change for
2456 * a particular Access Category, then the change will
2457 * be communicated to HDD via the QoS callback
2458 * associated with the given flow, and U-APSD
2459 * parameters will be updated there
2460 */
2461
2462 hdd_debug("Reassoc so no work, Exiting");
2463
2464 return QDF_STATUS_SUCCESS;
2465 }
2466
2467 hdd_debug("U-APSD mask is 0x%02x", (int)uapsd_mask);
2468
2469 ucfg_mlme_get_tl_delayed_trgr_frm_int(hdd_ctx->psoc,
2470 &delayed_trgr_frm_int);
2471
2472 if (uapsd_mask & HDD_AC_VO) {
2473 status = ucfg_mlme_get_wmm_uapsd_vo_srv_intv(hdd_ctx->psoc,
2474 &srv_value);
2475 if (QDF_IS_STATUS_ERROR(status)) {
2476 hdd_err("Get uapsd_srv_intv failed");
2477 return QDF_STATUS_SUCCESS;
2478 }
2479 status = ucfg_mlme_get_wmm_uapsd_vo_sus_intv(hdd_ctx->psoc,
2480 &sus_value);
2481 if (QDF_IS_STATUS_ERROR(status)) {
2482 hdd_err("Get uapsd_vo_sus_intv failed");
2483 return QDF_STATUS_SUCCESS;
2484 }
2485
2486 status = sme_enable_uapsd_for_ac(
2487 SME_AC_VO, 7, 7, srv_value, sus_value,
2488 SME_QOS_WMM_TS_DIR_BOTH, 1,
2489 adapter->deflink->vdev_id,
2490 delayed_trgr_frm_int);
2491
2492 QDF_ASSERT(QDF_IS_STATUS_SUCCESS(status));
2493 }
2494
2495 if (uapsd_mask & HDD_AC_VI) {
2496 status = ucfg_mlme_get_wmm_uapsd_vi_srv_intv(
2497 hdd_ctx->psoc, &srv_value);
2498 if (!QDF_IS_STATUS_SUCCESS(status)) {
2499 hdd_err("Get uapsd_vi_srv_intv failed");
2500 return QDF_STATUS_SUCCESS;
2501 }
2502 status = ucfg_mlme_get_wmm_uapsd_vi_sus_intv(
2503 hdd_ctx->psoc, &sus_value);
2504 if (!QDF_IS_STATUS_SUCCESS(status)) {
2505 hdd_err("Get uapsd_vi_sus_intv failed");
2506 return QDF_STATUS_SUCCESS;
2507 }
2508
2509 status = sme_enable_uapsd_for_ac(
2510 SME_AC_VI, 5, 5, srv_value, sus_value,
2511 SME_QOS_WMM_TS_DIR_BOTH, 1,
2512 adapter->deflink->vdev_id,
2513 delayed_trgr_frm_int);
2514
2515 QDF_ASSERT(QDF_IS_STATUS_SUCCESS(status));
2516 }
2517
2518 if (uapsd_mask & HDD_AC_BK) {
2519 status = ucfg_mlme_get_wmm_uapsd_bk_srv_intv(hdd_ctx->psoc,
2520 &srv_value);
2521 if (!QDF_IS_STATUS_SUCCESS(status)) {
2522 hdd_err("Get uapsd_bk_srv_intv failed");
2523 return QDF_STATUS_SUCCESS;
2524 }
2525 status = ucfg_mlme_get_wmm_uapsd_bk_sus_intv(hdd_ctx->psoc,
2526 &sus_value);
2527 if (!QDF_IS_STATUS_SUCCESS(status)) {
2528 hdd_err("Get uapsd_bk_sus_intv failed");
2529 return QDF_STATUS_SUCCESS;
2530 }
2531
2532 status = sme_enable_uapsd_for_ac(
2533 SME_AC_BK, 2, 2, srv_value, sus_value,
2534 SME_QOS_WMM_TS_DIR_BOTH, 1,
2535 adapter->deflink->vdev_id,
2536 delayed_trgr_frm_int);
2537
2538 QDF_ASSERT(QDF_IS_STATUS_SUCCESS(status));
2539 }
2540
2541 if (uapsd_mask & HDD_AC_BE) {
2542 status = ucfg_mlme_get_wmm_uapsd_be_srv_intv(hdd_ctx->psoc,
2543 &srv_value);
2544 if (!QDF_IS_STATUS_SUCCESS(status)) {
2545 hdd_err("Get uapsd_be_srv_intv failed");
2546 return QDF_STATUS_SUCCESS;
2547 }
2548 status = ucfg_mlme_get_wmm_uapsd_be_sus_intv(hdd_ctx->psoc,
2549 &sus_value);
2550 if (!QDF_IS_STATUS_SUCCESS(status)) {
2551 hdd_err("Get uapsd_be_sus_intv failed");
2552 return QDF_STATUS_SUCCESS;
2553 }
2554
2555 status = sme_enable_uapsd_for_ac(
2556 SME_AC_BE, 3, 3, srv_value, sus_value,
2557 SME_QOS_WMM_TS_DIR_BOTH, 1,
2558 adapter->deflink->vdev_id,
2559 delayed_trgr_frm_int);
2560
2561 QDF_ASSERT(QDF_IS_STATUS_SUCCESS(status));
2562 }
2563
2564 status = sme_update_dsc_pto_up_mapping(hdd_ctx->mac_handle,
2565 adapter->dscp_to_up_map,
2566 adapter->deflink->vdev_id);
2567
2568 if (!QDF_IS_STATUS_SUCCESS(status))
2569 hdd_wmm_dscp_initial_state(adapter);
2570
2571 hdd_exit();
2572
2573 return QDF_STATUS_SUCCESS;
2574 }
2575
2576 /**
2577 * hdd_wmm_connect() - Function which will handle the housekeeping
2578 * required by WMM when a connection is established
2579 *
2580 * @adapter : [in] pointer to adapter context
2581 * @roam_info: [in] pointer to roam information
2582 * @bss_type : [in] type of BSS
2583 *
2584 * Return: QDF_STATUS enumeration
2585 */
hdd_wmm_connect(struct hdd_adapter * adapter,struct csr_roam_info * roam_info,eCsrRoamBssType bss_type)2586 QDF_STATUS hdd_wmm_connect(struct hdd_adapter *adapter,
2587 struct csr_roam_info *roam_info,
2588 eCsrRoamBssType bss_type)
2589 {
2590 int ac;
2591 bool qap = true;
2592 bool qos_connection = true;
2593 uint8_t acm_mask = 0x0;
2594
2595 hdd_debug("qap is %d, qos_connection is %d, acm_mask is 0x%x",
2596 qap, qos_connection, acm_mask);
2597
2598 adapter->hdd_wmm_status.qap = qap;
2599 adapter->hdd_wmm_status.qos_connection = qos_connection;
2600
2601 for (ac = 0; ac < WLAN_MAX_AC; ac++) {
2602 /* admission is not required so access is allowed */
2603 adapter->hdd_wmm_status.ac_status[ac].is_access_required = false;
2604 adapter->hdd_wmm_status.ac_status[ac].is_access_allowed = true;
2605 }
2606
2607 return QDF_STATUS_SUCCESS;
2608 }
2609
2610 /**
2611 * hdd_wmm_is_active() - Function which will determine if WMM is
2612 * active on the current connection
2613 *
2614 * @adapter: [in] pointer to adapter context
2615 *
2616 * Return: true if WMM is enabled, false if WMM is not enabled
2617 */
hdd_wmm_is_active(struct hdd_adapter * adapter)2618 bool hdd_wmm_is_active(struct hdd_adapter *adapter)
2619 {
2620 if ((!adapter->hdd_wmm_status.qos_connection) ||
2621 (!adapter->hdd_wmm_status.qap)) {
2622 return false;
2623 } else {
2624 return true;
2625 }
2626 }
2627
hdd_wmm_is_acm_allowed(uint8_t vdev_id)2628 bool hdd_wmm_is_acm_allowed(uint8_t vdev_id)
2629 {
2630 struct hdd_adapter *adapter;
2631 struct hdd_wmm_ac_status *wmm_ac_status;
2632 struct hdd_context *hdd_ctx;
2633 struct wlan_hdd_link_info *link_info;
2634
2635 hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
2636 if (!hdd_ctx)
2637 return false;
2638
2639 link_info = hdd_get_link_info_by_vdev(hdd_ctx, vdev_id);
2640 if (!link_info || hdd_validate_adapter(link_info->adapter))
2641 return false;
2642
2643 adapter = link_info->adapter;
2644 wmm_ac_status = adapter->hdd_wmm_status.ac_status;
2645
2646 if (hdd_wmm_is_active(adapter) &&
2647 !(wmm_ac_status[QCA_WLAN_AC_VI].is_access_allowed))
2648 return false;
2649 return true;
2650 }
2651
hdd_wmm_addts(struct hdd_adapter * adapter,uint32_t handle,struct sme_qos_wmmtspecinfo * tspec)2652 hdd_wlan_wmm_status_e hdd_wmm_addts(struct hdd_adapter *adapter,
2653 uint32_t handle,
2654 struct sme_qos_wmmtspecinfo *tspec)
2655 {
2656 struct hdd_wmm_qos_context *qos_context = NULL;
2657 struct hdd_wmm_qos_context *cur_entry;
2658 hdd_wlan_wmm_status_e status = HDD_WLAN_WMM_STATUS_SETUP_SUCCESS;
2659 #ifndef WLAN_MDM_CODE_REDUCTION_OPT
2660 enum sme_qos_statustype sme_status;
2661 #endif
2662 mac_handle_t mac_handle = hdd_adapter_get_mac_handle(adapter);
2663
2664 hdd_debug("Entered with handle 0x%x", handle);
2665
2666 /* see if a context already exists with the given handle */
2667 mutex_lock(&adapter->hdd_wmm_status.mutex);
2668 list_for_each_entry(cur_entry,
2669 &adapter->hdd_wmm_status.context_list, node) {
2670 if (cur_entry->handle == handle) {
2671 qos_context = cur_entry;
2672 break;
2673 }
2674 }
2675 mutex_unlock(&adapter->hdd_wmm_status.mutex);
2676 if (qos_context) {
2677 /* record with that handle already exists */
2678 hdd_err("Record already exists with handle 0x%x", handle);
2679
2680 /* Application is trying to modify some of the Tspec
2681 * params. Allow it
2682 */
2683 sme_status = sme_qos_modify_req(mac_handle,
2684 tspec, qos_context->flow_id);
2685
2686 /* need to check the return value and act appropriately */
2687 switch (sme_status) {
2688 case SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP:
2689 status = HDD_WLAN_WMM_STATUS_MODIFY_PENDING;
2690 break;
2691 case SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP:
2692 status =
2693 HDD_WLAN_WMM_STATUS_MODIFY_SUCCESS_NO_ACM_NO_UAPSD;
2694 break;
2695 case SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_APSD_SET_ALREADY:
2696 status =
2697 HDD_WLAN_WMM_STATUS_MODIFY_SUCCESS_NO_ACM_UAPSD_EXISTING;
2698 break;
2699 case SME_QOS_STATUS_MODIFY_SETUP_INVALID_PARAMS_RSP:
2700 status = HDD_WLAN_WMM_STATUS_MODIFY_FAILED_BAD_PARAM;
2701 break;
2702 case SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP:
2703 status = HDD_WLAN_WMM_STATUS_MODIFY_FAILED;
2704 break;
2705 case SME_QOS_STATUS_SETUP_NOT_QOS_AP_RSP:
2706 status = HDD_WLAN_WMM_STATUS_SETUP_FAILED_NO_WMM;
2707 break;
2708 default:
2709 /* we didn't get back one of the
2710 * SME_QOS_STATUS_MODIFY_* status codes
2711 */
2712 hdd_err("unexpected SME Status=%d",
2713 sme_status);
2714 QDF_ASSERT(0);
2715 return HDD_WLAN_WMM_STATUS_MODIFY_FAILED;
2716 }
2717
2718 /* we were successful, save the status */
2719 mutex_lock(&adapter->hdd_wmm_status.mutex);
2720 if (qos_context->magic == HDD_WMM_CTX_MAGIC)
2721 qos_context->status = status;
2722 mutex_unlock(&adapter->hdd_wmm_status.mutex);
2723
2724 return status;
2725 }
2726
2727 qos_context = qdf_mem_malloc(sizeof(*qos_context));
2728 if (!qos_context) {
2729 /* no memory for QoS context. Nothing we can do */
2730 return HDD_WLAN_WMM_STATUS_INTERNAL_FAILURE;
2731 }
2732 /* we assume the tspec has already been validated by the caller */
2733
2734 qos_context->handle = handle;
2735 if (tspec->ts_info.up < HDD_WMM_UP_TO_AC_MAP_SIZE)
2736 qos_context->ac_type = hdd_wmm_up_to_ac_map[tspec->ts_info.up];
2737 else {
2738 hdd_err("ts_info.up (%d) larger than max value (%d), use default ac_type (%d)",
2739 tspec->ts_info.up,
2740 HDD_WMM_UP_TO_AC_MAP_SIZE - 1, hdd_wmm_up_to_ac_map[0]);
2741 qos_context->ac_type = hdd_wmm_up_to_ac_map[0];
2742 }
2743 qos_context->adapter = adapter;
2744 qos_context->flow_id = 0;
2745 qos_context->ts_id = tspec->ts_info.tid;
2746 qos_context->magic = HDD_WMM_CTX_MAGIC;
2747 qos_context->is_inactivity_timer_running = false;
2748
2749 hdd_debug("Setting up QoS, context %pK", qos_context);
2750
2751 mutex_lock(&adapter->hdd_wmm_status.mutex);
2752 list_add(&qos_context->node, &adapter->hdd_wmm_status.context_list);
2753 mutex_unlock(&adapter->hdd_wmm_status.mutex);
2754
2755 #ifndef WLAN_MDM_CODE_REDUCTION_OPT
2756 sme_status = sme_qos_setup_req(mac_handle,
2757 adapter->deflink->vdev_id,
2758 tspec,
2759 hdd_wmm_sme_callback,
2760 qos_context,
2761 tspec->ts_info.up,
2762 &qos_context->flow_id);
2763
2764 hdd_debug("sme_qos_setup_req returned %d flowid %d",
2765 sme_status, qos_context->flow_id);
2766
2767 /* need to check the return value and act appropriately */
2768 switch (sme_status) {
2769 case SME_QOS_STATUS_SETUP_REQ_PENDING_RSP:
2770 status = HDD_WLAN_WMM_STATUS_SETUP_PENDING;
2771 break;
2772 case SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP:
2773 status = HDD_WLAN_WMM_STATUS_SETUP_SUCCESS_NO_ACM_NO_UAPSD;
2774 break;
2775 case SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY:
2776 status =
2777 HDD_WLAN_WMM_STATUS_SETUP_SUCCESS_NO_ACM_UAPSD_EXISTING;
2778 break;
2779 case SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_PENDING:
2780 status = HDD_WLAN_WMM_STATUS_SETUP_PENDING;
2781 break;
2782 case SME_QOS_STATUS_SETUP_INVALID_PARAMS_RSP:
2783 /* disable the inactivity timer */
2784 hdd_wmm_disable_inactivity_timer(qos_context);
2785 hdd_wmm_free_context(qos_context);
2786 return HDD_WLAN_WMM_STATUS_SETUP_FAILED_BAD_PARAM;
2787 case SME_QOS_STATUS_SETUP_FAILURE_RSP:
2788 /* disable the inactivity timer */
2789 hdd_wmm_disable_inactivity_timer(qos_context);
2790 /* we can't tell the difference between when a request
2791 * fails because AP rejected it versus when SME
2792 * encountered an internal error
2793 */
2794 hdd_wmm_free_context(qos_context);
2795 return HDD_WLAN_WMM_STATUS_SETUP_FAILED;
2796 case SME_QOS_STATUS_SETUP_NOT_QOS_AP_RSP:
2797 /* disable the inactivity timer */
2798 hdd_wmm_disable_inactivity_timer(qos_context);
2799 hdd_wmm_free_context(qos_context);
2800 return HDD_WLAN_WMM_STATUS_SETUP_FAILED_NO_WMM;
2801 default:
2802 /* disable the inactivity timer */
2803 hdd_wmm_disable_inactivity_timer(qos_context);
2804 /* we didn't get back one of the
2805 * SME_QOS_STATUS_SETUP_* status codes
2806 */
2807 hdd_wmm_free_context(qos_context);
2808 hdd_err("unexpected SME Status=%d", sme_status);
2809 QDF_ASSERT(0);
2810 return HDD_WLAN_WMM_STATUS_SETUP_FAILED;
2811 }
2812 #endif
2813
2814 /* we were successful, save the status */
2815 mutex_lock(&adapter->hdd_wmm_status.mutex);
2816 if (qos_context->magic == HDD_WMM_CTX_MAGIC)
2817 qos_context->status = status;
2818 mutex_unlock(&adapter->hdd_wmm_status.mutex);
2819
2820 return status;
2821 }
2822
2823 /**
2824 * hdd_wmm_delts() - Function which will delete a traffic spec at the
2825 * request of an application
2826 *
2827 * @adapter: [in] pointer to adapter context
2828 * @handle: [in] handle to uniquely identify a TS
2829 *
2830 * Return: HDD_WLAN_WMM_STATUS_*
2831 */
hdd_wmm_delts(struct hdd_adapter * adapter,uint32_t handle)2832 hdd_wlan_wmm_status_e hdd_wmm_delts(struct hdd_adapter *adapter,
2833 uint32_t handle)
2834 {
2835 struct hdd_wmm_qos_context *qos_context = NULL;
2836 struct hdd_wmm_qos_context *cur_entry;
2837 sme_ac_enum_type ac_type = 0;
2838 uint32_t flow_id = 0;
2839 hdd_wlan_wmm_status_e status = HDD_WLAN_WMM_STATUS_SETUP_SUCCESS;
2840 #ifndef WLAN_MDM_CODE_REDUCTION_OPT
2841 enum sme_qos_statustype sme_status;
2842 mac_handle_t mac_handle = hdd_adapter_get_mac_handle(adapter);
2843 #endif
2844
2845 hdd_debug("Entered with handle 0x%x", handle);
2846
2847 /* locate the context with the given handle */
2848 mutex_lock(&adapter->hdd_wmm_status.mutex);
2849 list_for_each_entry(cur_entry,
2850 &adapter->hdd_wmm_status.context_list, node) {
2851 if (cur_entry->handle == handle) {
2852 qos_context = cur_entry;
2853 break;
2854 }
2855 }
2856 mutex_unlock(&adapter->hdd_wmm_status.mutex);
2857
2858 if (!qos_context) {
2859 /* we didn't find the handle, tid is already freed */
2860 hdd_info("tid already freed for handle 0x%x", handle);
2861 return HDD_WLAN_WMM_STATUS_RELEASE_SUCCESS;
2862 }
2863
2864 ac_type = qos_context->ac_type;
2865 flow_id = qos_context->flow_id;
2866
2867 hdd_debug("found handle 0x%x, flow %d, AC %d",
2868 handle, flow_id, ac_type);
2869
2870 #ifndef WLAN_MDM_CODE_REDUCTION_OPT
2871 sme_status = sme_qos_release_req(mac_handle, adapter->deflink->vdev_id,
2872 flow_id);
2873
2874 hdd_debug("SME flow %d released, SME status %d", flow_id, sme_status);
2875
2876 switch (sme_status) {
2877 case SME_QOS_STATUS_RELEASE_SUCCESS_RSP:
2878 /* this flow is the only one on that AC, so go ahead
2879 * and update our TSPEC state for the AC
2880 */
2881 adapter->hdd_wmm_status.ac_status[ac_type].is_tspec_valid =
2882 false;
2883 adapter->hdd_wmm_status.ac_status[ac_type].is_access_allowed =
2884 false;
2885
2886 /* need to tell TL to stop trigger timer, etc */
2887 hdd_wmm_disable_tl_uapsd(qos_context);
2888
2889 /* disable the inactivity timer */
2890 hdd_wmm_disable_inactivity_timer(qos_context);
2891
2892 /* we are done with this context */
2893 hdd_wmm_free_context(qos_context);
2894
2895 /* SME must not fire any more callbacks for this flow
2896 * since the context is no longer valid
2897 */
2898
2899 return HDD_WLAN_WMM_STATUS_RELEASE_SUCCESS;
2900
2901 case SME_QOS_STATUS_RELEASE_REQ_PENDING_RSP:
2902 /* do nothing as we will get a response from SME */
2903 status = HDD_WLAN_WMM_STATUS_RELEASE_PENDING;
2904 break;
2905
2906 case SME_QOS_STATUS_RELEASE_INVALID_PARAMS_RSP:
2907 /* nothing we can do with the existing flow except leave it */
2908 status = HDD_WLAN_WMM_STATUS_RELEASE_FAILED_BAD_PARAM;
2909 break;
2910
2911 case SME_QOS_STATUS_RELEASE_FAILURE_RSP:
2912 /* nothing we can do with the existing flow except leave it */
2913 status = HDD_WLAN_WMM_STATUS_RELEASE_FAILED;
2914 break;
2915
2916 default:
2917 /* we didn't get back one of the
2918 * SME_QOS_STATUS_RELEASE_* status codes
2919 */
2920 hdd_err("unexpected SME Status=%d", sme_status);
2921 QDF_ASSERT(0);
2922 status = HDD_WLAN_WMM_STATUS_RELEASE_FAILED;
2923 }
2924
2925 #endif
2926 mutex_lock(&adapter->hdd_wmm_status.mutex);
2927 if (qos_context->magic == HDD_WMM_CTX_MAGIC)
2928 qos_context->status = status;
2929 mutex_unlock(&adapter->hdd_wmm_status.mutex);
2930
2931 return status;
2932 }
2933
2934 /**
2935 * hdd_wmm_checkts() - Function which will return the status of a traffic
2936 * spec at the request of an application
2937 *
2938 * @adapter: [in] pointer to adapter context
2939 * @handle: [in] handle to uniquely identify a TS
2940 *
2941 * Return: HDD_WLAN_WMM_STATUS_*
2942 */
hdd_wmm_checkts(struct hdd_adapter * adapter,uint32_t handle)2943 hdd_wlan_wmm_status_e hdd_wmm_checkts(struct hdd_adapter *adapter, uint32_t handle)
2944 {
2945 struct hdd_wmm_qos_context *qos_context;
2946 hdd_wlan_wmm_status_e status = HDD_WLAN_WMM_STATUS_LOST;
2947
2948 hdd_debug("Entered with handle 0x%x", handle);
2949
2950 /* locate the context with the given handle */
2951 mutex_lock(&adapter->hdd_wmm_status.mutex);
2952 list_for_each_entry(qos_context,
2953 &adapter->hdd_wmm_status.context_list, node) {
2954 if (qos_context->handle == handle) {
2955 hdd_debug("found handle 0x%x, context %pK",
2956 handle, qos_context);
2957
2958 status = qos_context->status;
2959 break;
2960 }
2961 }
2962 mutex_unlock(&adapter->hdd_wmm_status.mutex);
2963 return status;
2964 }
2965
2966 /**
2967 * hdd_get_handle_from_ts_id() - get handle from ts id
2968 * @adapter : hdd adapter
2969 * @ts_id: ts_id
2970 * @del_tspec_handle: handle to delete the request
2971 *
2972 * Return: None
2973 */
2974 static void
hdd_get_handle_from_ts_id(struct hdd_adapter * adapter,uint8_t ts_id,uint32_t * del_tspec_handle)2975 hdd_get_handle_from_ts_id(struct hdd_adapter *adapter, uint8_t ts_id,
2976 uint32_t *del_tspec_handle)
2977 {
2978 struct hdd_wmm_qos_context *cur_entry;
2979
2980 hdd_debug("Entered with ts_id 0x%x", ts_id);
2981
2982 mutex_lock(&adapter->hdd_wmm_status.mutex);
2983 list_for_each_entry(cur_entry,
2984 &adapter->hdd_wmm_status.context_list, node) {
2985 if (cur_entry->ts_id == ts_id) {
2986 *del_tspec_handle = cur_entry->handle;
2987 break;
2988 }
2989 }
2990 mutex_unlock(&adapter->hdd_wmm_status.mutex);
2991 }
2992
2993 /**
2994 * __wlan_hdd_cfg80211_config_tspec() - config tspec
2995 * @wiphy: pointer to wireless wiphy structure.
2996 * @wdev: pointer to wireless_dev structure.
2997 * @data: pointer to config tspec command parameters.
2998 * @data_len: the length in byte of config tspec command parameters.
2999 *
3000 * Return: An error code or 0 on success.
3001 */
__wlan_hdd_cfg80211_config_tspec(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)3002 static int __wlan_hdd_cfg80211_config_tspec(struct wiphy *wiphy,
3003 struct wireless_dev *wdev,
3004 const void *data,
3005 int data_len)
3006 {
3007 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(wdev->netdev);
3008 struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
3009 struct sme_qos_wmmtspecinfo tspec;
3010 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX + 1];
3011 uint8_t oper, ts_id;
3012 static uint32_t add_tspec_handle = MIN_HANDLE_VALUE;
3013 uint32_t del_tspec_handle = 0;
3014 hdd_wlan_wmm_status_e status;
3015 int ret;
3016
3017 hdd_enter_dev(wdev->netdev);
3018
3019 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
3020 hdd_err("Command not allowed in FTM mode");
3021 return -EPERM;
3022 }
3023
3024 ret = wlan_hdd_validate_context(hdd_ctx);
3025 if (ret != 0)
3026 return ret;
3027
3028 ret = wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX,
3029 data, data_len, config_tspec_policy);
3030 if (ret) {
3031 hdd_err_rl("Invalid ATTR");
3032 return -EINVAL;
3033 }
3034
3035 if (!tb[CONFIG_TSPEC_OPERATION] || !tb[CONFIG_TSPEC_TSID]) {
3036 hdd_err_rl("Mandatory attributes are not present");
3037 return -EINVAL;
3038 }
3039
3040 memset(&tspec, 0, sizeof(tspec));
3041
3042 oper = nla_get_u8(tb[CONFIG_TSPEC_OPERATION]);
3043 ts_id = nla_get_u8(tb[CONFIG_TSPEC_TSID]);
3044
3045 switch (oper) {
3046 case QCA_WLAN_TSPEC_ADD:
3047
3048 tspec.ts_info.tid = ts_id;
3049
3050 /* Mandatory attributes */
3051 if (tb[CONFIG_TSPEC_DIRECTION]) {
3052 uint8_t direction = nla_get_u8(
3053 tb[CONFIG_TSPEC_DIRECTION]);
3054
3055 switch (direction) {
3056 case QCA_WLAN_TSPEC_DIRECTION_UPLINK:
3057 tspec.ts_info.direction =
3058 SME_QOS_WMM_TS_DIR_UPLINK;
3059 break;
3060 case QCA_WLAN_TSPEC_DIRECTION_DOWNLINK:
3061 tspec.ts_info.direction =
3062 SME_QOS_WMM_TS_DIR_DOWNLINK;
3063 break;
3064 case QCA_WLAN_TSPEC_DIRECTION_BOTH:
3065 tspec.ts_info.direction =
3066 SME_QOS_WMM_TS_DIR_BOTH;
3067 break;
3068 default:
3069 hdd_err_rl("Invalid direction %d", direction);
3070 return -EINVAL;
3071 }
3072 } else {
3073 hdd_err_rl("Direction is not present");
3074 return -EINVAL;
3075 }
3076
3077 if (tb[CONFIG_TSPEC_APSD])
3078 tspec.ts_info.psb = 1;
3079
3080 if (tb[CONFIG_TSPEC_ACK_POLICY]) {
3081 uint8_t ack_policy = nla_get_u8(
3082 tb[CONFIG_TSPEC_ACK_POLICY]);
3083
3084 switch (ack_policy) {
3085 case QCA_WLAN_TSPEC_NORMAL_ACK:
3086 tspec.ts_info.ack_policy =
3087 SME_QOS_WMM_TS_ACK_POLICY_NORMAL_ACK;
3088 break;
3089 case QCA_WLAN_TSPEC_BLOCK_ACK:
3090 tspec.ts_info.ack_policy =
3091 SME_QOS_WMM_TS_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK;
3092 break;
3093 default:
3094 hdd_err_rl("Invalid ack policy %d", ack_policy);
3095 return -EINVAL;
3096 }
3097 } else {
3098 hdd_err_rl("ACK policy is not present");
3099 return -EINVAL;
3100 }
3101
3102 if (tb[CONFIG_TSPEC_NOMINAL_MSDU_SIZE]) {
3103 tspec.nominal_msdu_size = nla_get_u16(
3104 tb[CONFIG_TSPEC_NOMINAL_MSDU_SIZE]);
3105 } else {
3106 hdd_err_rl("Nominal msdu size is not present");
3107 return -EINVAL;
3108 }
3109
3110 if (tb[CONFIG_TSPEC_MAXIMUM_MSDU_SIZE]) {
3111 tspec.maximum_msdu_size = nla_get_u16(
3112 tb[CONFIG_TSPEC_MAXIMUM_MSDU_SIZE]);
3113 } else {
3114 hdd_err_rl("Maximum msdu size is not present");
3115 return -EINVAL;
3116 }
3117
3118 if (tb[CONFIG_TSPEC_MIN_SERVICE_INTERVAL]) {
3119 tspec.min_service_interval = nla_get_u32(
3120 tb[CONFIG_TSPEC_MIN_SERVICE_INTERVAL]);
3121 } else {
3122 hdd_err_rl("Min service interval is not present");
3123 return -EINVAL;
3124 }
3125
3126 if (tb[CONFIG_TSPEC_MAX_SERVICE_INTERVAL]) {
3127 tspec.max_service_interval = nla_get_u32(
3128 tb[CONFIG_TSPEC_MAX_SERVICE_INTERVAL]);
3129 } else {
3130 hdd_err_rl("Max service interval is not present");
3131 return -EINVAL;
3132 }
3133
3134 if (tb[CONFIG_TSPEC_INACTIVITY_INTERVAL]) {
3135 tspec.inactivity_interval = nla_get_u32(
3136 tb[CONFIG_TSPEC_INACTIVITY_INTERVAL]);
3137 } else {
3138 hdd_err_rl("Inactivity interval is not present");
3139 return -EINVAL;
3140 }
3141
3142 if (tb[CONFIG_TSPEC_SUSPENSION_INTERVAL]) {
3143 tspec.suspension_interval = nla_get_u32(
3144 tb[CONFIG_TSPEC_SUSPENSION_INTERVAL]);
3145 } else {
3146 hdd_err_rl("Suspension interval is not present");
3147 return -EINVAL;
3148 }
3149
3150 if (tb[CONFIG_TSPEC_SURPLUS_BANDWIDTH_ALLOWANCE]) {
3151 tspec.surplus_bw_allowance = nla_get_u16(
3152 tb[CONFIG_TSPEC_SURPLUS_BANDWIDTH_ALLOWANCE]);
3153 } else {
3154 hdd_err_rl("Surplus bw allowance is not present");
3155 return -EINVAL;
3156 }
3157
3158 /* Optional attributes */
3159 if (tb[CONFIG_TSPEC_USER_PRIORITY])
3160 tspec.ts_info.up = nla_get_u8(
3161 tb[CONFIG_TSPEC_USER_PRIORITY]);
3162
3163 if (tb[CONFIG_TSPEC_MINIMUM_DATA_RATE])
3164 tspec.min_data_rate = nla_get_u32(
3165 tb[CONFIG_TSPEC_MINIMUM_DATA_RATE]);
3166
3167 if (tb[CONFIG_TSPEC_MEAN_DATA_RATE])
3168 tspec.mean_data_rate = nla_get_u32(
3169 tb[CONFIG_TSPEC_MEAN_DATA_RATE]);
3170
3171 if (tb[CONFIG_TSPEC_PEAK_DATA_RATE])
3172 tspec.peak_data_rate = nla_get_u32(
3173 tb[CONFIG_TSPEC_PEAK_DATA_RATE]);
3174
3175 if (tb[CONFIG_TSPEC_BURST_SIZE])
3176 tspec.max_burst_size = nla_get_u32(
3177 tb[CONFIG_TSPEC_BURST_SIZE]);
3178
3179 if (tspec.max_burst_size)
3180 tspec.ts_info.burst_size_defn = 1;
3181
3182 if (tb[CONFIG_TSPEC_MINIMUM_PHY_RATE])
3183 tspec.min_phy_rate = nla_get_u32(
3184 tb[CONFIG_TSPEC_MINIMUM_PHY_RATE]);
3185 /*
3186 * ts_id send by upper layer is always same as handle and host
3187 * doesn't add new TS entry for same handle. To avoid this
3188 * issue host modifies handle internally.
3189 */
3190 status = hdd_wmm_addts(adapter, add_tspec_handle, &tspec);
3191 if (status == HDD_WLAN_WMM_STATUS_SETUP_FAILED ||
3192 status == HDD_WLAN_WMM_STATUS_SETUP_FAILED_BAD_PARAM ||
3193 status == HDD_WLAN_WMM_STATUS_SETUP_FAILED_NO_WMM ||
3194 status == HDD_WLAN_WMM_STATUS_MODIFY_FAILED ||
3195 status == HDD_WLAN_WMM_STATUS_MODIFY_FAILED_BAD_PARAM ||
3196 status == HDD_WLAN_WMM_STATUS_SETUP_UAPSD_SET_FAILED ||
3197 status == HDD_WLAN_WMM_STATUS_MODIFY_UAPSD_SET_FAILED ||
3198 status == HDD_WLAN_WMM_STATUS_INTERNAL_FAILURE) {
3199 hdd_err_rl("hdd_wmm_addts failed %d", status);
3200 return -EINVAL;
3201 }
3202
3203 add_tspec_handle++;
3204 if (add_tspec_handle >= MAX_HANDLE_VALUE)
3205 add_tspec_handle = MIN_HANDLE_VALUE;
3206 break;
3207
3208 case QCA_WLAN_TSPEC_DEL:
3209 /*
3210 * Host modifies handle internally. So, always
3211 * delete the entry for provided ts_id.
3212 */
3213 hdd_get_handle_from_ts_id(adapter, ts_id, &del_tspec_handle);
3214 if (!del_tspec_handle) {
3215 hdd_err_rl("ts_id is already freed %d", ts_id);
3216 break;
3217 }
3218 status = hdd_wmm_delts(adapter, del_tspec_handle);
3219 if (status == HDD_WLAN_WMM_STATUS_RELEASE_FAILED ||
3220 status == HDD_WLAN_WMM_STATUS_RELEASE_FAILED_BAD_PARAM ||
3221 status == HDD_WLAN_WMM_STATUS_INTERNAL_FAILURE) {
3222 hdd_err_rl("hdd_wmm_delts failed %d", status);
3223 return -EINVAL;
3224 }
3225 break;
3226
3227 case QCA_WLAN_TSPEC_GET:
3228
3229 status = hdd_wmm_checkts(adapter, ts_id);
3230 if (status == HDD_WLAN_WMM_STATUS_LOST ||
3231 status == HDD_WLAN_WMM_STATUS_INTERNAL_FAILURE) {
3232 hdd_err_rl("hdd_wmm_checkts failed %d", status);
3233 return -EINVAL;
3234 }
3235 break;
3236
3237 default:
3238 hdd_err_rl("Invalid operation %d", oper);
3239 return -EINVAL;
3240 }
3241
3242 hdd_exit();
3243
3244 return 0;
3245 }
3246
wlan_hdd_cfg80211_config_tspec(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)3247 int wlan_hdd_cfg80211_config_tspec(struct wiphy *wiphy,
3248 struct wireless_dev *wdev,
3249 const void *data, int data_len)
3250 {
3251 int errno;
3252 struct osif_vdev_sync *vdev_sync;
3253
3254 errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
3255 if (errno)
3256 return errno;
3257
3258 errno = __wlan_hdd_cfg80211_config_tspec(wiphy, wdev, data, data_len);
3259
3260 osif_vdev_sync_op_stop(vdev_sync);
3261
3262 return errno;
3263 }
3264