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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 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 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 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 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 1909 hdd_wmm_traffic_end_indication_is_enable(struct hdd_adapter *adapter) 1910 { 1911 return false; 1912 } 1913 #endif 1914 1915 static 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 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 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 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 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 */ 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 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 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 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 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 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 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)) 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)) 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)) 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)) 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 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 */ 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 */ 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 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 */ 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 */ 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 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 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 */ 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 */ 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 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 */ 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 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