1 /* 2 * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. 3 * Copyright (c) 2021-2024 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 /* Include Files */ 21 22 #include "osif_sync.h" 23 #include <wlan_hdd_includes.h> 24 #include <wlan_hdd_wowl.h> 25 #include <wlan_hdd_stats.h> 26 #include "cfg_ucfg_api.h" 27 #include "wlan_hdd_trace.h" 28 #include "wlan_hdd_ioctl.h" 29 #include "wlan_hdd_power.h" 30 #include "wlan_hdd_regulatory.h" 31 #include "wlan_osif_request_manager.h" 32 #include "wlan_hdd_driver_ops.h" 33 #include "wlan_policy_mgr_api.h" 34 #include "wlan_hdd_hostapd.h" 35 #include "scheduler_api.h" 36 #include "wlan_reg_ucfg_api.h" 37 #include "wlan_hdd_p2p.h" 38 #include <linux/ctype.h> 39 #include "wma.h" 40 #include "wlan_hdd_napi.h" 41 #include "wlan_mlme_ucfg_api.h" 42 #include "target_type.h" 43 #ifdef FEATURE_WLAN_ESE 44 #include <sme_api.h> 45 #include <sir_api.h> 46 #endif 47 #include "wlan_hdd_object_manager.h" 48 #include "hif.h" 49 #include "wlan_scan_ucfg_api.h" 50 #include "wlan_reg_ucfg_api.h" 51 #include "qdf_func_tracker.h" 52 #include "wlan_cm_roam_ucfg_api.h" 53 54 #if defined(LINUX_QCMBR) 55 #define SIOCIOCTLTX99 (SIOCDEVPRIVATE+13) 56 #endif 57 58 #define SIZE_OF_WIFI6E_CHAN_LIST 512 59 60 /* 61 * Size of Driver command strings from upper layer 62 */ 63 #define SIZE_OF_SETROAMMODE 11 /* size of SETROAMMODE */ 64 #define SIZE_OF_GETROAMMODE 11 /* size of GETROAMMODE */ 65 #define SIZE_OF_SETSUSPENDMODE 14 66 67 /* 68 * Size of GETCOUNTRYREV output = (sizeof("GETCOUNTRYREV") = 14) + one (space) + 69 * (sizeof("country_code") = 3) + 70 * one (NULL terminating character) 71 */ 72 #define SIZE_OF_GETCOUNTRYREV_OUTPUT 20 73 74 #ifdef FEATURE_WLAN_ESE 75 #define TID_MIN_VALUE 0 76 #define TID_MAX_VALUE 15 77 #endif /* FEATURE_WLAN_ESE */ 78 79 /* 80 * Maximum buffer size used for returning the data back to user space 81 */ 82 #define WLAN_MAX_BUF_SIZE 1024 83 #define WLAN_PRIV_DATA_MAX_LEN 8192 84 85 /* 86 * Driver miracast parameters: 87 * 0-Disabled 88 * 1-Source, 2-Sink 89 * 128: miracast connecting time optimization enabled. At present host 90 * will disable imps to reduce connection time for p2p. 91 * 129: miracast connecting time optimization disabled 92 */ 93 enum miracast_param { 94 MIRACAST_DISABLED, 95 MIRACAST_SOURCE, 96 MIRACAST_SINK, 97 MIRACAST_CONN_OPT_ENABLED = 128, 98 MIRACAST_CONN_OPT_DISABLED = 129, 99 }; 100 101 /* 102 * When ever we need to print IBSSPEERINFOALL for more than 16 STA 103 * we will split the printing. 104 */ 105 #define NUM_OF_STA_DATA_TO_PRINT 16 106 107 /* 108 * The host sends the maximum channel count in RCL(Roam channel list) via a 109 * supplicant vendor event to notify RCL on disconnection. 110 */ 111 #define MAX_RCL_CHANNEL_COUNT 30 112 113 #ifdef WLAN_FEATURE_EXTWOW_SUPPORT 114 /** 115 * struct enable_ext_wow_priv - Private data structure for ext wow 116 * @ext_wow_should_suspend: Suspend status of ext wow 117 */ 118 struct enable_ext_wow_priv { 119 bool ext_wow_should_suspend; 120 }; 121 #endif 122 123 /* 124 * Android DRIVER command structures 125 */ 126 struct android_wifi_reassoc_params { 127 unsigned char bssid[18]; 128 int channel; 129 }; 130 131 #define ANDROID_WIFI_ACTION_FRAME_SIZE 1040 132 struct android_wifi_af_params { 133 unsigned char bssid[18]; 134 int channel; 135 int dwell_time; 136 int len; 137 unsigned char data[ANDROID_WIFI_ACTION_FRAME_SIZE]; 138 }; 139 140 /* 141 * Define HDD driver command handling entry, each contains a command 142 * string and the handler. 143 */ 144 typedef int (*hdd_drv_cmd_handler_t)(struct wlan_hdd_link_info *link_info, 145 struct hdd_context *hdd_ctx, 146 uint8_t *cmd, 147 uint8_t cmd_name_len, 148 struct hdd_priv_data *priv_data); 149 150 /** 151 * struct hdd_drv_cmd - Structure to store ioctl command handling info 152 * @cmd: Name of the command 153 * @handler: Command handler to be invoked 154 * @args: Set to true if command expects input parameters 155 */ 156 struct hdd_drv_cmd { 157 const char *cmd; 158 hdd_drv_cmd_handler_t handler; 159 bool args; 160 }; 161 162 #ifdef WLAN_FEATURE_EXTWOW_SUPPORT 163 #define WLAN_WAIT_TIME_READY_TO_EXTWOW 2000 164 #define WLAN_HDD_MAX_TCP_PORT 65535 165 #endif 166 167 /** 168 * drv_cmd_validate() - Validates for space in hdd driver command 169 * @command: pointer to input data (its a NULL terminated string) 170 * @len: length of command name 171 * 172 * This function checks for space after command name and if no space 173 * is found returns error. 174 * 175 * Return: 0 for success non-zero for failure 176 */ 177 static int drv_cmd_validate(uint8_t *command, int len) 178 { 179 if (command[len] != ' ') 180 return -EINVAL; 181 182 return 0; 183 } 184 185 #ifdef FEATURE_WLAN_ESE 186 struct tsm_priv { 187 tAniTrafStrmMetrics tsm_metrics; 188 }; 189 190 static void hdd_get_tsm_stats_cb(tAniTrafStrmMetrics tsm_metrics, 191 void *context) 192 { 193 struct osif_request *request; 194 struct tsm_priv *priv; 195 196 request = osif_request_get(context); 197 if (!request) { 198 hdd_err("Obsolete request"); 199 return; 200 } 201 priv = osif_request_priv(request); 202 priv->tsm_metrics = tsm_metrics; 203 osif_request_complete(request); 204 osif_request_put(request); 205 hdd_exit(); 206 207 } 208 209 static int hdd_get_tsm_stats(struct hdd_adapter *adapter, 210 const uint8_t tid, 211 tAniTrafStrmMetrics *tsm_metrics) 212 { 213 struct hdd_context *hdd_ctx; 214 struct hdd_station_ctx *hdd_sta_ctx; 215 QDF_STATUS status; 216 int ret; 217 void *cookie; 218 struct osif_request *request; 219 struct tsm_priv *priv; 220 static const struct osif_request_params params = { 221 .priv_size = sizeof(*priv), 222 .timeout_ms = WLAN_WAIT_TIME_STATS, 223 }; 224 225 if (!adapter) { 226 hdd_err("adapter is NULL"); 227 return -EINVAL; 228 } 229 230 hdd_ctx = WLAN_HDD_GET_CTX(adapter); 231 hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter->deflink); 232 233 request = osif_request_alloc(¶ms); 234 if (!request) { 235 hdd_err("Request allocation failure"); 236 return -ENOMEM; 237 } 238 cookie = osif_request_cookie(request); 239 240 status = sme_get_tsm_stats(hdd_ctx->mac_handle, hdd_get_tsm_stats_cb, 241 hdd_sta_ctx->conn_info.bssid, 242 cookie, tid); 243 if (QDF_STATUS_SUCCESS != status) { 244 hdd_err("Unable to retrieve tsm statistics"); 245 ret = qdf_status_to_os_return(status); 246 goto cleanup; 247 } 248 249 ret = osif_request_wait_for_response(request); 250 if (ret) { 251 hdd_err("SME timed out while retrieving tsm statistics"); 252 goto cleanup; 253 } 254 255 priv = osif_request_priv(request); 256 *tsm_metrics = priv->tsm_metrics; 257 258 cleanup: 259 osif_request_put(request); 260 261 return ret; 262 } 263 #endif /*FEATURE_WLAN_ESE */ 264 265 static void hdd_get_band_helper(struct hdd_context *hdd_ctx, int *ui_band) 266 { 267 enum band_info band = -1; 268 269 ucfg_reg_get_band(hdd_ctx->pdev, &band); 270 switch (band) { 271 case BAND_ALL: 272 *ui_band = WLAN_HDD_UI_BAND_AUTO; 273 break; 274 275 case BAND_2G: 276 *ui_band = WLAN_HDD_UI_BAND_2_4_GHZ; 277 break; 278 279 case BAND_5G: 280 *ui_band = WLAN_HDD_UI_BAND_5_GHZ; 281 break; 282 283 default: 284 hdd_warn("Invalid Band %d", band); 285 *ui_band = -1; 286 break; 287 } 288 } 289 290 /** 291 * hdd_check_and_fill_freq() - to validate chan and convert into freq 292 * @in_chan: input as channel number or freq to be checked 293 * @freq: frequency for input in_chan (output parameter) 294 * @pdev: pdev object 295 * 296 * This function checks input "in_chan" is channel number, if yes then fills 297 * appropriate frequency into "freq" out param. If the "in_param" is greater 298 * than WNI_CFG_CURRENT_CHANNEL_STAMAX then checks for valid frequencies. 299 * 300 * Return: true if "in_chan" is valid channel/frequency; false otherwise 301 */ 302 static bool hdd_check_and_fill_freq(uint32_t in_chan, qdf_freq_t *freq, 303 struct wlan_objmgr_pdev *pdev) 304 { 305 if (in_chan <= WNI_CFG_CURRENT_CHANNEL_STAMAX) 306 *freq = wlan_reg_legacy_chan_to_freq(pdev, in_chan); 307 else if (WLAN_REG_IS_24GHZ_CH_FREQ(in_chan) || 308 WLAN_REG_IS_5GHZ_CH_FREQ(in_chan) || 309 WLAN_REG_IS_6GHZ_CHAN_FREQ(in_chan)) 310 *freq = in_chan; 311 else 312 return false; 313 314 return true; 315 } 316 317 /** 318 * _hdd_parse_bssid_and_chan() - helper function to parse bssid and channel 319 * @data: input data 320 * @bssid: pointer to bssid (output parameter) 321 * @freq: pointer to freq (output parameter) 322 * @pdev: pdev object 323 * 324 * Return: 0 if parsing is successful; -EINVAL otherwise 325 */ 326 static int _hdd_parse_bssid_and_chan(const uint8_t **data, 327 uint8_t *bssid, qdf_freq_t *freq, 328 struct wlan_objmgr_pdev *pdev) 329 { 330 const uint8_t *in_ptr; 331 int v = 0; 332 int temp_int; 333 uint8_t temp_buf[32]; 334 335 /* 12 hexa decimal digits, 5 ':' and '\0' */ 336 uint8_t mac_addr[18]; 337 338 if (!data || !*data) 339 return -EINVAL; 340 341 in_ptr = *data; 342 343 in_ptr = strnchr(in_ptr, strlen(in_ptr), SPACE_ASCII_VALUE); 344 /* no argument after the command */ 345 if (!in_ptr) 346 goto error; 347 /* no space after the command */ 348 else if (SPACE_ASCII_VALUE != *in_ptr) 349 goto error; 350 351 /* remove empty spaces */ 352 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) 353 in_ptr++; 354 355 /* no argument followed by spaces */ 356 if ('\0' == *in_ptr) 357 goto error; 358 359 v = sscanf(in_ptr, "%17s", mac_addr); 360 if (!((1 == v) && hdd_is_valid_mac_address(mac_addr))) { 361 hdd_err("Invalid MAC address or All hex inputs are not read (%d)", 362 v); 363 goto error; 364 } 365 366 bssid[0] = hex_to_bin(mac_addr[0]) << 4 | 367 hex_to_bin(mac_addr[1]); 368 bssid[1] = hex_to_bin(mac_addr[3]) << 4 | 369 hex_to_bin(mac_addr[4]); 370 bssid[2] = hex_to_bin(mac_addr[6]) << 4 | 371 hex_to_bin(mac_addr[7]); 372 bssid[3] = hex_to_bin(mac_addr[9]) << 4 | 373 hex_to_bin(mac_addr[10]); 374 bssid[4] = hex_to_bin(mac_addr[12]) << 4 | 375 hex_to_bin(mac_addr[13]); 376 bssid[5] = hex_to_bin(mac_addr[15]) << 4 | 377 hex_to_bin(mac_addr[16]); 378 379 /* point to the next argument */ 380 in_ptr = strnchr(in_ptr, strlen(in_ptr), SPACE_ASCII_VALUE); 381 /* no argument after the command */ 382 if (!in_ptr) 383 goto error; 384 385 /* remove empty spaces */ 386 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) 387 in_ptr++; 388 389 /* no argument followed by spaces */ 390 if ('\0' == *in_ptr) 391 goto error; 392 393 /* get the next argument ie the channel/freq number */ 394 v = sscanf(in_ptr, "%31s ", temp_buf); 395 if (1 != v) 396 goto error; 397 398 v = kstrtos32(temp_buf, 10, &temp_int); 399 if (v < 0 || temp_int < 0) 400 goto error; 401 else if (!hdd_check_and_fill_freq(temp_int, freq, pdev)) 402 goto error; 403 404 *data = in_ptr; 405 return 0; 406 error: 407 *data = in_ptr; 408 return -EINVAL; 409 } 410 411 /** 412 * hdd_parse_send_action_frame_v1_data() - HDD Parse send action frame data 413 * @command: Pointer to input data 414 * @bssid: Pointer to target AP BSSID 415 * @freq: Pointer to the Target AP channel frequency 416 * @dwell_time: Pointer to the time to stay off-channel 417 * after transmitting action frame 418 * @buf: Pointer to data 419 * @buf_len: Pointer to data length 420 * @pdev: pdev object 421 * 422 * This function parses the send action frame data passed in the format 423 * SENDACTIONFRAME<space><bssid><space><channel | frequency><space><dwelltime> 424 * <space><data> 425 * 426 * Return: 0 for success non-zero for failure 427 */ 428 static int 429 hdd_parse_send_action_frame_v1_data(const uint8_t *command, 430 uint8_t *bssid, 431 qdf_freq_t *freq, uint8_t *dwell_time, 432 uint8_t **buf, uint8_t *buf_len, 433 struct wlan_objmgr_pdev *pdev) 434 { 435 const uint8_t *in_ptr = command; 436 const uint8_t *end_ptr; 437 int temp_int; 438 int j = 0; 439 int i = 0; 440 int v = 0; 441 uint8_t temp_buf[32]; 442 uint8_t temp_u8 = 0; 443 444 if (_hdd_parse_bssid_and_chan(&in_ptr, bssid, freq, pdev)) 445 return -EINVAL; 446 447 /* point to the next argument */ 448 in_ptr = strnchr(in_ptr, strlen(in_ptr), SPACE_ASCII_VALUE); 449 /* no argument after the command */ 450 if (!in_ptr) 451 return -EINVAL; 452 /* removing empty spaces */ 453 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) 454 in_ptr++; 455 456 /* no argument followed by spaces */ 457 if ('\0' == *in_ptr) 458 return -EINVAL; 459 460 /* getting the next argument ie the dwell time */ 461 v = sscanf(in_ptr, "%31s ", temp_buf); 462 if (1 != v) 463 return -EINVAL; 464 465 v = kstrtos32(temp_buf, 10, &temp_int); 466 if (v < 0 || temp_int < 0) 467 return -EINVAL; 468 469 *dwell_time = temp_int; 470 471 /* point to the next argument */ 472 in_ptr = strnchr(in_ptr, strlen(in_ptr), SPACE_ASCII_VALUE); 473 /* no argument after the command */ 474 if (!in_ptr) 475 return -EINVAL; 476 /* removing empty spaces */ 477 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) 478 in_ptr++; 479 480 /* no argument followed by spaces */ 481 if ('\0' == *in_ptr) 482 return -EINVAL; 483 484 /* find the length of data */ 485 end_ptr = in_ptr; 486 while (('\0' != *end_ptr)) 487 end_ptr++; 488 489 *buf_len = end_ptr - in_ptr; 490 if (*buf_len <= 0) 491 return -EINVAL; 492 493 /* 494 * Allocate the number of bytes based on the number of input characters 495 * whether it is even or odd. 496 * if the number of input characters are even, then we need N/2 byte. 497 * if the number of input characters are odd, then we need do (N+1)/2 498 * to compensate rounding off. 499 * For example, if N = 18, then (18 + 1)/2 = 9 bytes are enough. 500 * If N = 19, then we need 10 bytes, hence (19 + 1)/2 = 10 bytes 501 */ 502 *buf = qdf_mem_malloc((*buf_len + 1) / 2); 503 if (!*buf) 504 return -ENOMEM; 505 506 /* the buffer received from the upper layer is character buffer, 507 * we need to prepare the buffer taking 2 characters in to a U8 hex 508 * decimal number for example 7f0000f0...form a buffer to contain 7f 509 * in 0th location, 00 in 1st and f0 in 3rd location 510 */ 511 for (i = 0, j = 0; j < *buf_len; j += 2) { 512 if (j + 1 == *buf_len) { 513 temp_u8 = hex_to_bin(in_ptr[j]); 514 } else { 515 temp_u8 = 516 (hex_to_bin(in_ptr[j]) << 4) | 517 (hex_to_bin(in_ptr[j + 1])); 518 } 519 (*buf)[i++] = temp_u8; 520 } 521 *buf_len = i; 522 return 0; 523 } 524 525 /** 526 * hdd_parse_reassoc_command_v1_data() - HDD Parse reassoc command data 527 * @command: Pointer to input data (its a NULL terminated string) 528 * @bssid: Pointer to target Ap bssid 529 * @freq: Pointer to the Target AP frequency 530 * @pdev: pdev object 531 * 532 * This function parses the reasoc command data passed in the format 533 * REASSOC<space><bssid><space><channel/frequency>. 534 * 535 * If reassoc MAC from user space is broadcast MAC as: 536 * "wpa_cli DRIVER FASTREASSOC ff:ff:ff:ff:ff:ff 0", 537 * user space invoked roaming candidate selection will base on firmware score 538 * algorithm, current connection will be kept if current AP has highest 539 * score. It is requirement from customer which can avoid ping-pong roaming. 540 * 541 * Return: 0 for success non-zero for failure 542 */ 543 static int hdd_parse_reassoc_command_v1_data(const uint8_t *command, 544 uint8_t *bssid, qdf_freq_t *freq, 545 struct wlan_objmgr_pdev *pdev) 546 { 547 const uint8_t *in_ptr = command; 548 549 if (_hdd_parse_bssid_and_chan(&in_ptr, bssid, freq, pdev)) 550 return -EINVAL; 551 552 return 0; 553 } 554 555 /** 556 * hdd_parse_reassoc_v1() - parse version 1 of the REASSOC command 557 * @adapter: Adapter upon which the command was received 558 * @command: ASCII text command that was received 559 * 560 * This function parses the v1 REASSOC command with the format 561 * 562 * REASSOC xx:xx:xx:xx:xx:xx CH/FREQ 563 * 564 * Where "xx:xx:xx:xx:xx:xx" is the Hex-ASCII representation of the 565 * BSSID and CH/FREQ is the ASCII representation of the channel/frequency. 566 * For example 567 * 568 * REASSOC 00:0a:0b:11:22:33 48 569 * REASSOC 00:0a:0b:11:22:33 2412 570 * 571 * Return: 0 for success non-zero for failure 572 */ 573 static int hdd_parse_reassoc_v1(struct hdd_adapter *adapter, const char *command) 574 { 575 qdf_freq_t freq = 0; 576 tSirMacAddr bssid; 577 int ret; 578 struct qdf_mac_addr target_bssid; 579 struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); 580 QDF_STATUS status; 581 struct wlan_objmgr_pdev *pdev; 582 583 pdev = wlan_vdev_get_pdev(adapter->deflink->vdev); 584 ret = hdd_parse_reassoc_command_v1_data(command, bssid, &freq, pdev); 585 if (ret) { 586 hdd_err("Failed to parse reassoc command data"); 587 return ret; 588 } 589 590 qdf_mem_copy(target_bssid.bytes, bssid, sizeof(tSirMacAddr)); 591 status = ucfg_wlan_cm_roam_invoke(hdd_ctx->pdev, 592 adapter->deflink->vdev_id, 593 &target_bssid, freq, 594 CM_ROAMING_USER); 595 return qdf_status_to_os_return(status); 596 } 597 598 /** 599 * hdd_parse_reassoc_v2() - parse version 2 of the REASSOC command 600 * @adapter: Adapter upon which the command was received 601 * @command: Command that was received, ASCII command 602 * followed by binary data 603 * @total_len: Total length of the command received 604 * 605 * This function parses the v2 REASSOC command with the format 606 * 607 * REASSOC <android_wifi_reassoc_params> 608 * 609 * Return: 0 for success non-zero for failure 610 */ 611 static int hdd_parse_reassoc_v2(struct hdd_adapter *adapter, 612 const char *command, 613 int total_len) 614 { 615 struct android_wifi_reassoc_params params; 616 tSirMacAddr bssid; 617 qdf_freq_t freq = 0; 618 int ret; 619 struct qdf_mac_addr target_bssid; 620 struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); 621 QDF_STATUS status; 622 struct wlan_objmgr_pdev *pdev; 623 624 pdev = wlan_vdev_get_pdev(adapter->deflink->vdev); 625 if (total_len < sizeof(params) + 8) { 626 hdd_err("Invalid command length"); 627 return -EINVAL; 628 } 629 630 /* The params are located after "REASSOC " */ 631 memcpy(¶ms, command + 8, sizeof(params)); 632 633 if (!mac_pton(params.bssid, (u8 *) &bssid)) { 634 hdd_err("MAC address parsing failed"); 635 ret = -EINVAL; 636 } else { 637 /* 638 * In Reassoc command, user can send channel number or frequency 639 * along with BSSID. If params.channel param of REASSOC command 640 * is less than WNI_CFG_CURRENT_CHANNEL_STAMAX, then host 641 * consider this as channel number else frequency. 642 */ 643 if (!hdd_check_and_fill_freq(params.channel, &freq, pdev)) 644 return -EINVAL; 645 646 qdf_mem_copy(target_bssid.bytes, bssid, sizeof(tSirMacAddr)); 647 status = ucfg_wlan_cm_roam_invoke(hdd_ctx->pdev, 648 adapter->deflink->vdev_id, 649 &target_bssid, freq, 650 CM_ROAMING_USER); 651 ret = qdf_status_to_os_return(status); 652 } 653 654 return ret; 655 } 656 657 /** 658 * hdd_parse_reassoc() - parse the REASSOC command 659 * @adapter: Adapter upon which the command was received 660 * @command: Command that was received 661 * @total_len: Total length of the command received 662 * 663 * There are two different versions of the REASSOC command. Version 1 664 * of the command contains a parameter list that is ASCII characters 665 * whereas version 2 contains a combination of ASCII and binary 666 * payload. Determine if a version 1 or a version 2 command is being 667 * parsed by examining the parameters, and then dispatch the parser 668 * that is appropriate for the command. 669 * 670 * Return: 0 for success non-zero for failure 671 */ 672 static int hdd_parse_reassoc(struct hdd_adapter *adapter, const char *command, 673 int total_len) 674 { 675 int ret; 676 677 /* both versions start with "REASSOC " 678 * v1 has a bssid and channel # as an ASCII string 679 * REASSOC xx:xx:xx:xx:xx:xx CH/FREQ 680 * v2 has a C struct 681 * REASSOC <binary c struct> 682 * 683 * The first field in the v2 struct is also the bssid in ASCII. 684 * But in the case of a v2 message the BSSID is NUL-terminated. 685 * Hence we can peek at that offset to see if this is V1 or V2 686 * REASSOC xx:xx:xx:xx:xx:xx* 687 * 1111111111222222 688 * 01234567890123456789012345 689 */ 690 691 if (total_len < 26) { 692 hdd_err("Invalid command, total_len = %d", total_len); 693 return -EINVAL; 694 } 695 696 if (command[25]) 697 ret = hdd_parse_reassoc_v1(adapter, command); 698 else 699 ret = hdd_parse_reassoc_v2(adapter, command, total_len); 700 701 return ret; 702 } 703 704 /** 705 * hdd_sendactionframe() - send a userspace-supplied action frame 706 * @adapter: Adapter upon which the command was received 707 * @bssid: BSSID target of the action frame 708 * @freq: Frequency upon which to send the frame 709 * @dwell_time: Amount of time to dwell when the frame is sent 710 * @payload_len:Length of the payload 711 * @payload: Payload of the frame 712 * 713 * This function sends a userspace-supplied action frame 714 * 715 * Return: 0 for success non-zero for failure 716 */ 717 static int 718 hdd_sendactionframe(struct hdd_adapter *adapter, const uint8_t *bssid, 719 const qdf_freq_t freq, const uint8_t dwell_time, 720 const int payload_len, const uint8_t *payload) 721 { 722 struct ieee80211_channel chan; 723 int frame_len, ret = 0; 724 uint8_t *frame; 725 struct ieee80211_hdr_3addr *hdr; 726 u64 cookie; 727 struct hdd_station_ctx *sta_ctx; 728 struct hdd_context *hdd_ctx; 729 tpSirMacVendorSpecificFrameHdr vendor = 730 (tpSirMacVendorSpecificFrameHdr) payload; 731 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) 732 struct cfg80211_mgmt_tx_params params; 733 #endif 734 735 if (payload_len < sizeof(tSirMacVendorSpecificFrameHdr)) { 736 hdd_warn("Invalid payload length: %d", payload_len); 737 return -EINVAL; 738 } 739 740 if (QDF_STA_MODE != adapter->device_mode) { 741 hdd_warn("Unsupported in mode %s(%d)", 742 qdf_opmode_str(adapter->device_mode), 743 adapter->device_mode); 744 return -EINVAL; 745 } 746 747 sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter->deflink); 748 hdd_ctx = WLAN_HDD_GET_CTX(adapter); 749 750 /* if not associated, no need to send action frame */ 751 if (!hdd_cm_is_vdev_associated(adapter->deflink)) { 752 hdd_warn("Not associated"); 753 ret = -EINVAL; 754 goto exit; 755 } 756 757 /* 758 * if the target bssid is different from currently associated AP, 759 * then no need to send action frame 760 */ 761 if (memcmp(bssid, sta_ctx->conn_info.bssid.bytes, 762 QDF_MAC_ADDR_SIZE)) { 763 hdd_warn("STA is not associated to this AP"); 764 ret = -EINVAL; 765 goto exit; 766 } 767 768 chan.center_freq = freq; 769 /* Check if it is specific action frame */ 770 if (vendor->category == 771 ACTION_CATEGORY_VENDOR_SPECIFIC) { 772 static const uint8_t oui[] = { 0x00, 0x00, 0xf0 }; 773 774 if (!qdf_mem_cmp(vendor->Oui, oui, 3)) { 775 /* 776 * if the freq number is different from operating 777 * freq then no need to send action frame 778 */ 779 if (freq) { 780 if (freq != sta_ctx->conn_info.chan_freq) { 781 hdd_warn("freq(%u) is different from operating freq(%u)", 782 freq, 783 sta_ctx->conn_info.chan_freq); 784 ret = -EINVAL; 785 goto exit; 786 } 787 /* 788 * If channel number is specified and same 789 * as home channel, ensure that action frame 790 * is sent immediately by cancelling 791 * roaming scans. Otherwise large dwell times 792 * may cause long delays in sending action 793 * frames. 794 */ 795 ucfg_cm_abort_roam_scan( 796 hdd_ctx->pdev, 797 adapter->deflink->vdev_id); 798 } else { 799 /* 800 * 0 is accepted as current home frequency, 801 * delayed transmission of action frame is ok. 802 */ 803 chan.center_freq = sta_ctx->conn_info.chan_freq; 804 } 805 } 806 } 807 if (chan.center_freq == 0) { 808 hdd_nofl_err("Invalid freq : %d", freq); 809 ret = -EINVAL; 810 goto exit; 811 } 812 813 frame_len = payload_len + 24; 814 frame = qdf_mem_malloc(frame_len); 815 if (!frame) { 816 ret = -ENOMEM; 817 goto exit; 818 } 819 820 hdr = (struct ieee80211_hdr_3addr *)frame; 821 hdr->frame_control = 822 cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); 823 qdf_mem_copy(hdr->addr1, bssid, QDF_MAC_ADDR_SIZE); 824 qdf_mem_copy(hdr->addr2, adapter->mac_addr.bytes, 825 QDF_MAC_ADDR_SIZE); 826 qdf_mem_copy(hdr->addr3, bssid, QDF_MAC_ADDR_SIZE); 827 qdf_mem_copy(hdr + 1, payload, payload_len); 828 829 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) 830 params.chan = &chan; 831 params.offchan = 0; 832 params.wait = dwell_time; 833 params.buf = frame; 834 params.len = frame_len; 835 params.no_cck = 1; 836 params.dont_wait_for_ack = 1; 837 ret = wlan_hdd_mgmt_tx(NULL, &adapter->wdev, ¶ms, &cookie); 838 #else 839 ret = wlan_hdd_mgmt_tx(NULL, 840 &(adapter->wdev), 841 &chan, 0, 842 843 dwell_time, frame, frame_len, 1, 1, &cookie); 844 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */ 845 846 qdf_mem_free(frame); 847 exit: 848 return ret; 849 } 850 851 /** 852 * hdd_parse_sendactionframe_v1() - parse version 1 of the 853 * SENDACTIONFRAME command 854 * @adapter: Adapter upon which the command was received 855 * @command: ASCII text command that was received 856 * 857 * This function parses the v1 SENDACTIONFRAME command with the format 858 * 859 * SENDACTIONFRAME xx:xx:xx:xx:xx:xx CH DW xxxxxx 860 * 861 * Where "xx:xx:xx:xx:xx:xx" is the Hex-ASCII representation of the 862 * BSSID, CH is the ASCII representation of the channel, DW is the 863 * ASCII representation of the dwell time, and xxxxxx is the Hex-ASCII 864 * payload. For example 865 * 866 * SENDACTIONFRAME 00:0a:0b:11:22:33 48 40 aabbccddee 867 * 868 * Return: 0 for success non-zero for failure 869 */ 870 static int 871 hdd_parse_sendactionframe_v1(struct hdd_adapter *adapter, const char *command) 872 { 873 qdf_freq_t freq = 0; 874 uint8_t dwell_time = 0; 875 uint8_t payload_len = 0; 876 uint8_t *payload = NULL; 877 tSirMacAddr bssid; 878 int ret; 879 struct wlan_objmgr_pdev *pdev; 880 881 pdev = wlan_vdev_get_pdev(adapter->deflink->vdev); 882 ret = hdd_parse_send_action_frame_v1_data(command, bssid, &freq, 883 &dwell_time, &payload, 884 &payload_len, pdev); 885 if (ret) { 886 hdd_nofl_err("Failed to parse send action frame data"); 887 } else { 888 ret = hdd_sendactionframe(adapter, bssid, freq, 889 dwell_time, payload_len, payload); 890 qdf_mem_free(payload); 891 } 892 893 return ret; 894 } 895 896 /** 897 * hdd_parse_sendactionframe_v2() - parse version 2 of the 898 * SENDACTIONFRAME command 899 * @adapter: Adapter upon which the command was received 900 * @command: Command that was received, ASCII command 901 * followed by binary data 902 * @total_len: Length of @command 903 * 904 * This function parses the v2 SENDACTIONFRAME command with the format 905 * 906 * SENDACTIONFRAME <android_wifi_af_params> 907 * 908 * Return: 0 for success non-zero for failure 909 */ 910 static int 911 hdd_parse_sendactionframe_v2(struct hdd_adapter *adapter, 912 const char *command, int total_len) 913 { 914 struct android_wifi_af_params *params; 915 tSirMacAddr bssid; 916 int ret; 917 int len_wo_payload = 0; 918 qdf_freq_t freq = 0; 919 struct wlan_objmgr_pdev *pdev; 920 921 pdev = wlan_vdev_get_pdev(adapter->deflink->vdev); 922 923 /* The params are located after "SENDACTIONFRAME " */ 924 total_len -= 16; 925 len_wo_payload = sizeof(*params) - ANDROID_WIFI_ACTION_FRAME_SIZE; 926 if (total_len <= len_wo_payload) { 927 hdd_err("Invalid command len"); 928 return -EINVAL; 929 } 930 931 params = (struct android_wifi_af_params *)(command + 16); 932 933 if (params->len <= 0 || params->len > ANDROID_WIFI_ACTION_FRAME_SIZE || 934 (params->len > (total_len - len_wo_payload))) { 935 hdd_err("Invalid payload length: %d", params->len); 936 return -EINVAL; 937 } 938 939 if (!mac_pton(params->bssid, (u8 *)&bssid)) { 940 hdd_err("MAC address parsing failed"); 941 return -EINVAL; 942 } 943 944 if (params->channel < 0 || 945 params->channel > WNI_CFG_CURRENT_CHANNEL_STAMAX) { 946 hdd_err("Invalid channel: %d", params->channel); 947 return -EINVAL; 948 } 949 950 if (params->dwell_time < 0) { 951 hdd_err("Invalid dwell_time: %d", params->dwell_time); 952 return -EINVAL; 953 } 954 955 if (!hdd_check_and_fill_freq(params->channel, &freq, pdev)) { 956 hdd_err("Invalid channel: %d", params->channel); 957 return -EINVAL; 958 } 959 960 ret = hdd_sendactionframe(adapter, bssid, freq, params->dwell_time, 961 params->len, params->data); 962 963 return ret; 964 } 965 966 /** 967 * hdd_parse_sendactionframe() - parse the SENDACTIONFRAME command 968 * @adapter: Adapter upon which the command was received 969 * @command: Command that was received 970 * @total_len: Length of @command 971 * 972 * There are two different versions of the SENDACTIONFRAME command. 973 * Version 1 of the command contains a parameter list that is ASCII 974 * characters whereas version 2 contains a combination of ASCII and 975 * binary payload. Determine if a version 1 or a version 2 command is 976 * being parsed by examining the parameters, and then dispatch the 977 * parser that is appropriate for the version of the command. 978 * 979 * Return: 0 for success non-zero for failure 980 */ 981 static int 982 hdd_parse_sendactionframe(struct hdd_adapter *adapter, const char *command, 983 int total_len) 984 { 985 int ret; 986 987 /* 988 * both versions start with "SENDACTIONFRAME " 989 * v1 has a bssid and other parameters as an ASCII string 990 * SENDACTIONFRAME xx:xx:xx:xx:xx:xx CH DWELL LEN FRAME 991 * v2 has a C struct 992 * SENDACTIONFRAME <binary c struct> 993 * 994 * The first field in the v2 struct is also the bssid in ASCII. 995 * But in the case of a v2 message the BSSID is NUL-terminated. 996 * Hence we can peek at that offset to see if this is V1 or V2 997 * SENDACTIONFRAME xx:xx:xx:xx:xx:xx* 998 * 111111111122222222223333 999 * 0123456789012345678901234567890123 1000 * For both the commands, a valid command must have atleast 1001 * first 34 length of data. 1002 */ 1003 if (total_len < 34) { 1004 hdd_err("Invalid command (total_len=%d)", total_len); 1005 return -EINVAL; 1006 } 1007 1008 if (command[33]) 1009 ret = hdd_parse_sendactionframe_v1(adapter, command); 1010 else 1011 ret = hdd_parse_sendactionframe_v2(adapter, command, total_len); 1012 1013 return ret; 1014 } 1015 1016 #ifdef FEATURE_WLAN_ESE 1017 /** 1018 * hdd_parse_channellist() - HDD Parse channel list 1019 * @hdd_ctx: hdd context 1020 * @command: Pointer to input channel list 1021 * @channel_freq_list: Pointer to local output array to record 1022 * channel list 1023 * @num_channels: Pointer to number of roam scan channels 1024 * 1025 * This function parses the channel list passed in the format 1026 * SETROAMSCANCHANNELS<space><Number of channels><space>Channel 1<space> 1027 * Channel 2<space>Channel N 1028 * if the Number of channels (N) does not match with the actual number 1029 * of channels passed then take the minimum of N and count of 1030 * (Ch1, Ch2, ...Ch M). For example, if SETROAMSCANCHANNELS 3 36 40 44 48, 1031 * only 36, 40 and 44 shall be taken. If SETROAMSCANCHANNELS 5 36 40 44 48, 1032 * ignore 5 and take 36, 40, 44 and 48. This function does not take care of 1033 * removing duplicate channels from the list 1034 * 1035 * Return: 0 for success non-zero for failure 1036 */ 1037 static int 1038 hdd_parse_channellist(struct hdd_context *hdd_ctx, 1039 const uint8_t *command, 1040 uint32_t *channel_freq_list, 1041 uint8_t *num_channels) 1042 { 1043 const uint8_t *in_ptr = command; 1044 int temp_int; 1045 int j = 0; 1046 int v = 0; 1047 char buf[32]; 1048 1049 in_ptr = strnchr(command, strlen(command), SPACE_ASCII_VALUE); 1050 /* no argument after the command */ 1051 if (!in_ptr) 1052 return -EINVAL; 1053 else if (SPACE_ASCII_VALUE != *in_ptr) /* no space after the command */ 1054 return -EINVAL; 1055 1056 /* remove empty spaces */ 1057 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) 1058 in_ptr++; 1059 1060 /* no argument followed by spaces */ 1061 if ('\0' == *in_ptr) 1062 return -EINVAL; 1063 1064 /* get the first argument ie the number of channels */ 1065 v = sscanf(in_ptr, "%31s ", buf); 1066 if (1 != v) 1067 return -EINVAL; 1068 1069 v = kstrtos32(buf, 10, &temp_int); 1070 if ((v < 0) || 1071 (temp_int <= 0) || (temp_int > CFG_VALID_CHANNEL_LIST_LEN)) 1072 return -EINVAL; 1073 1074 *num_channels = temp_int; 1075 1076 hdd_debug("Number of channels are: %d", *num_channels); 1077 1078 for (j = 0; j < (*num_channels); j++) { 1079 /* 1080 * in_ptr pointing to the beginning of first space after number 1081 * of channels 1082 */ 1083 in_ptr = strpbrk(in_ptr, " "); 1084 /* no channel list after the number of channels argument */ 1085 if (!in_ptr) { 1086 if ((j != 0) && (*num_channels == j)) 1087 return 0; 1088 else 1089 goto cnt_mismatch; 1090 } 1091 1092 /* remove empty space */ 1093 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) 1094 in_ptr++; 1095 1096 /* 1097 * no channel list after the number of channels 1098 * argument and spaces 1099 */ 1100 if ('\0' == *in_ptr) { 1101 if ((j != 0) && (*num_channels == j)) 1102 return 0; 1103 else 1104 goto cnt_mismatch; 1105 } 1106 1107 v = sscanf(in_ptr, "%31s ", buf); 1108 if (1 != v) 1109 return -EINVAL; 1110 1111 v = kstrtos32(buf, 10, &temp_int); 1112 if ((v < 0) || 1113 (temp_int <= 0) || 1114 (temp_int > WNI_CFG_CURRENT_CHANNEL_STAMAX)) { 1115 return -EINVAL; 1116 } 1117 channel_freq_list[j] = 1118 wlan_reg_legacy_chan_to_freq(hdd_ctx->pdev, temp_int); 1119 1120 hdd_debug("Channel %d added to preferred channel list", 1121 channel_freq_list[j]); 1122 } 1123 1124 return 0; 1125 1126 cnt_mismatch: 1127 hdd_debug("Mismatch in ch cnt: %d and num of ch: %d", *num_channels, j); 1128 *num_channels = 0; 1129 return -EINVAL; 1130 1131 } 1132 1133 /** 1134 * hdd_parse_plm_cmd() - HDD Parse Plm command 1135 * @command: Pointer to input data 1136 * @req: Pointer to output struct plm_req 1137 * 1138 * This function parses the plm command passed in the format 1139 * CCXPLMREQ<space><enable><space><dialog_token><space> 1140 * <meas_token><space><num_of_bursts><space><burst_int><space> 1141 * <measu duration><space><burst_len><space><desired_tx_pwr> 1142 * <space><multcast_addr><space><number_of_channels> 1143 * <space><channel_numbers> 1144 * 1145 * Return: 0 for success non-zero for failure 1146 */ 1147 static QDF_STATUS hdd_parse_plm_cmd(uint8_t *command, 1148 struct plm_req_params *req) 1149 { 1150 uint8_t *in_ptr = NULL; 1151 int count, content = 0, ret = 0; 1152 char buf[32]; 1153 1154 /* move to argument list */ 1155 in_ptr = strnchr(command, strlen(command), SPACE_ASCII_VALUE); 1156 if (!in_ptr) 1157 return QDF_STATUS_E_FAILURE; 1158 1159 /* no space after the command */ 1160 if (SPACE_ASCII_VALUE != *in_ptr) 1161 return QDF_STATUS_E_FAILURE; 1162 1163 /* remove empty spaces */ 1164 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) 1165 in_ptr++; 1166 1167 /* START/STOP PLM req */ 1168 ret = sscanf(in_ptr, "%31s ", buf); 1169 if (1 != ret) 1170 return QDF_STATUS_E_FAILURE; 1171 1172 ret = kstrtos32(buf, 10, &content); 1173 if (ret < 0) 1174 return QDF_STATUS_E_FAILURE; 1175 1176 req->enable = content; 1177 in_ptr = strpbrk(in_ptr, " "); 1178 1179 if (!in_ptr) 1180 return QDF_STATUS_E_FAILURE; 1181 1182 /* remove empty spaces */ 1183 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) 1184 in_ptr++; 1185 1186 /* Dialog token of radio meas req containing meas reqIE */ 1187 ret = sscanf(in_ptr, "%31s ", buf); 1188 if (1 != ret) 1189 return QDF_STATUS_E_FAILURE; 1190 1191 ret = kstrtos32(buf, 10, &content); 1192 if (ret < 0) 1193 return QDF_STATUS_E_FAILURE; 1194 1195 req->diag_token = content; 1196 hdd_debug("diag token %d", req->diag_token); 1197 in_ptr = strpbrk(in_ptr, " "); 1198 1199 if (!in_ptr) 1200 return QDF_STATUS_E_FAILURE; 1201 1202 /* remove empty spaces */ 1203 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) 1204 in_ptr++; 1205 1206 /* measurement token of meas req IE */ 1207 ret = sscanf(in_ptr, "%31s ", buf); 1208 if (1 != ret) 1209 return QDF_STATUS_E_FAILURE; 1210 1211 ret = kstrtos32(buf, 10, &content); 1212 if (ret < 0) 1213 return QDF_STATUS_E_FAILURE; 1214 1215 req->meas_token = content; 1216 hdd_debug("meas token %d", req->meas_token); 1217 1218 hdd_debug("PLM req %s", req->enable ? "START" : "STOP"); 1219 if (req->enable) { 1220 1221 in_ptr = strpbrk(in_ptr, " "); 1222 1223 if (!in_ptr) 1224 return QDF_STATUS_E_FAILURE; 1225 1226 /* remove empty spaces */ 1227 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) 1228 in_ptr++; 1229 1230 /* total number of bursts after which STA stops sending */ 1231 ret = sscanf(in_ptr, "%31s ", buf); 1232 if (1 != ret) 1233 return QDF_STATUS_E_FAILURE; 1234 1235 ret = kstrtos32(buf, 10, &content); 1236 if (ret < 0) 1237 return QDF_STATUS_E_FAILURE; 1238 1239 if (content < 0) 1240 return QDF_STATUS_E_FAILURE; 1241 1242 req->num_bursts = content; 1243 hdd_debug("num bursts %d", req->num_bursts); 1244 in_ptr = strpbrk(in_ptr, " "); 1245 1246 if (!in_ptr) 1247 return QDF_STATUS_E_FAILURE; 1248 1249 /* remove empty spaces */ 1250 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) 1251 in_ptr++; 1252 1253 /* burst interval in seconds */ 1254 ret = sscanf(in_ptr, "%31s ", buf); 1255 if (1 != ret) 1256 return QDF_STATUS_E_FAILURE; 1257 1258 ret = kstrtos32(buf, 10, &content); 1259 if (ret < 0) 1260 return QDF_STATUS_E_FAILURE; 1261 1262 if (content <= 0) 1263 return QDF_STATUS_E_FAILURE; 1264 1265 req->burst_int = content; 1266 hdd_debug("burst int %d", req->burst_int); 1267 in_ptr = strpbrk(in_ptr, " "); 1268 1269 if (!in_ptr) 1270 return QDF_STATUS_E_FAILURE; 1271 1272 /* remove empty spaces */ 1273 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) 1274 in_ptr++; 1275 1276 /* Meas dur in TU's,STA goes off-ch and transmit PLM bursts */ 1277 ret = sscanf(in_ptr, "%31s ", buf); 1278 if (1 != ret) 1279 return QDF_STATUS_E_FAILURE; 1280 1281 ret = kstrtos32(buf, 10, &content); 1282 if (ret < 0) 1283 return QDF_STATUS_E_FAILURE; 1284 1285 if (content <= 0) 1286 return QDF_STATUS_E_FAILURE; 1287 1288 req->meas_duration = content; 1289 hdd_debug("meas duration %d", req->meas_duration); 1290 in_ptr = strpbrk(in_ptr, " "); 1291 1292 if (!in_ptr) 1293 return QDF_STATUS_E_FAILURE; 1294 1295 /* remove empty spaces */ 1296 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) 1297 in_ptr++; 1298 1299 /* burst length of PLM bursts */ 1300 ret = sscanf(in_ptr, "%31s ", buf); 1301 if (1 != ret) 1302 return QDF_STATUS_E_FAILURE; 1303 1304 ret = kstrtos32(buf, 10, &content); 1305 if (ret < 0) 1306 return QDF_STATUS_E_FAILURE; 1307 1308 if (content <= 0) 1309 return QDF_STATUS_E_FAILURE; 1310 1311 req->burst_len = content; 1312 hdd_debug("burst len %d", req->burst_len); 1313 in_ptr = strpbrk(in_ptr, " "); 1314 1315 if (!in_ptr) 1316 return QDF_STATUS_E_FAILURE; 1317 1318 /* remove empty spaces */ 1319 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) 1320 in_ptr++; 1321 1322 /* desired tx power for transmission of PLM bursts */ 1323 ret = sscanf(in_ptr, "%31s ", buf); 1324 if (1 != ret) 1325 return QDF_STATUS_E_FAILURE; 1326 1327 ret = kstrtos32(buf, 10, &content); 1328 if (ret < 0) 1329 return QDF_STATUS_E_FAILURE; 1330 1331 if (content <= 0) 1332 return QDF_STATUS_E_FAILURE; 1333 1334 req->desired_tx_pwr = content; 1335 hdd_debug("desired tx pwr %d", req->desired_tx_pwr); 1336 1337 for (count = 0; count < QDF_MAC_ADDR_SIZE; count++) { 1338 in_ptr = strpbrk(in_ptr, " "); 1339 1340 if (!in_ptr) 1341 return QDF_STATUS_E_FAILURE; 1342 1343 /* remove empty spaces */ 1344 while ((SPACE_ASCII_VALUE == *in_ptr) 1345 && ('\0' != *in_ptr)) 1346 in_ptr++; 1347 1348 ret = sscanf(in_ptr, "%31s ", buf); 1349 if (1 != ret) 1350 return QDF_STATUS_E_FAILURE; 1351 1352 ret = kstrtos32(buf, 16, &content); 1353 if (ret < 0) 1354 return QDF_STATUS_E_FAILURE; 1355 1356 req->mac_addr.bytes[count] = content; 1357 } 1358 1359 hdd_debug("MAC addr " QDF_MAC_ADDR_FMT, 1360 QDF_MAC_ADDR_REF(req->mac_addr.bytes)); 1361 1362 in_ptr = strpbrk(in_ptr, " "); 1363 1364 if (!in_ptr) 1365 return QDF_STATUS_E_FAILURE; 1366 1367 /* remove empty spaces */ 1368 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) 1369 in_ptr++; 1370 1371 /* number of channels */ 1372 ret = sscanf(in_ptr, "%31s ", buf); 1373 if (1 != ret) 1374 return QDF_STATUS_E_FAILURE; 1375 1376 ret = kstrtos32(buf, 10, &content); 1377 if (ret < 0) 1378 return QDF_STATUS_E_FAILURE; 1379 1380 if (content < 0) 1381 return QDF_STATUS_E_FAILURE; 1382 1383 content = QDF_MIN(content, CFG_VALID_CHANNEL_LIST_LEN); 1384 req->plm_num_ch = content; 1385 hdd_debug("num ch: %d", req->plm_num_ch); 1386 1387 /* Channel numbers */ 1388 for (count = 0; count < req->plm_num_ch; count++) { 1389 in_ptr = strpbrk(in_ptr, " "); 1390 1391 if (!in_ptr) 1392 return QDF_STATUS_E_FAILURE; 1393 1394 /* remove empty spaces */ 1395 while ((SPACE_ASCII_VALUE == *in_ptr) 1396 && ('\0' != *in_ptr)) 1397 in_ptr++; 1398 1399 ret = sscanf(in_ptr, "%31s ", buf); 1400 if (1 != ret) 1401 return QDF_STATUS_E_FAILURE; 1402 1403 ret = kstrtos32(buf, 10, &content); 1404 if (ret < 0 || content <= 0 || 1405 content > WNI_CFG_CURRENT_CHANNEL_STAMAX) 1406 return QDF_STATUS_E_FAILURE; 1407 1408 req->plm_ch_freq_list[count] = 1409 cds_chan_to_freq(content); 1410 hdd_debug(" ch-freq- %d", req->plm_ch_freq_list[count]); 1411 } 1412 } 1413 /* If PLM START */ 1414 return QDF_STATUS_SUCCESS; 1415 } 1416 #endif 1417 1418 #ifdef WLAN_FEATURE_EXTWOW_SUPPORT 1419 /** 1420 * wlan_hdd_ready_to_extwow() - Callback function for enable ext wow 1421 * @cookie: callback context 1422 * @is_success: suspend status of ext wow 1423 * 1424 * Return: none 1425 */ 1426 static void wlan_hdd_ready_to_extwow(void *cookie, bool is_success) 1427 { 1428 struct osif_request *request = NULL; 1429 struct enable_ext_wow_priv *priv = NULL; 1430 1431 request = osif_request_get(cookie); 1432 if (!request) { 1433 hdd_err("obsolete request"); 1434 return; 1435 } 1436 priv = osif_request_priv(request); 1437 priv->ext_wow_should_suspend = is_success; 1438 1439 osif_request_complete(request); 1440 osif_request_put(request); 1441 } 1442 1443 static int hdd_enable_ext_wow(struct hdd_adapter *adapter, 1444 tpSirExtWoWParams arg_params) 1445 { 1446 tSirExtWoWParams params; 1447 QDF_STATUS status; 1448 struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); 1449 int rc; 1450 struct enable_ext_wow_priv *priv = NULL; 1451 struct osif_request *request = NULL; 1452 void *cookie = NULL; 1453 struct osif_request_params hdd_params = { 1454 .priv_size = sizeof(*priv), 1455 .timeout_ms = WLAN_WAIT_TIME_READY_TO_EXTWOW, 1456 }; 1457 1458 qdf_mem_copy(¶ms, arg_params, sizeof(params)); 1459 1460 request = osif_request_alloc(&hdd_params); 1461 if (!request) { 1462 hdd_err("Request Allocation Failure"); 1463 return -ENOMEM; 1464 } 1465 cookie = osif_request_cookie(request); 1466 1467 status = sme_configure_ext_wow(hdd_ctx->mac_handle, ¶ms, 1468 &wlan_hdd_ready_to_extwow, 1469 cookie); 1470 if (QDF_STATUS_SUCCESS != status) { 1471 hdd_err("sme_configure_ext_wow returned failure %d", 1472 status); 1473 rc = -EPERM; 1474 goto exit; 1475 } 1476 1477 rc = osif_request_wait_for_response(request); 1478 if (rc) { 1479 hdd_err("Failed to get ready to extwow"); 1480 rc = -EPERM; 1481 goto exit; 1482 } 1483 1484 priv = osif_request_priv(request); 1485 if (!priv->ext_wow_should_suspend) { 1486 hdd_err("Received ready to ExtWoW failure"); 1487 rc = -EPERM; 1488 goto exit; 1489 } 1490 1491 if (ucfg_pmo_extwow_is_goto_suspend_enabled(hdd_ctx->psoc)) { 1492 hdd_info("Received ready to ExtWoW. Going to suspend"); 1493 1494 rc = wlan_hdd_cfg80211_suspend_wlan(hdd_ctx->wiphy, NULL); 1495 if (rc < 0) { 1496 hdd_err("wlan_hdd_cfg80211_suspend_wlan failed, error = %d", 1497 rc); 1498 goto exit; 1499 } 1500 rc = wlan_hdd_bus_suspend(); 1501 if (rc) { 1502 hdd_err("wlan_hdd_bus_suspend failed, status = %d", 1503 rc); 1504 wlan_hdd_cfg80211_resume_wlan(hdd_ctx->wiphy); 1505 goto exit; 1506 } 1507 } 1508 1509 exit: 1510 osif_request_put(request); 1511 return rc; 1512 } 1513 1514 static int hdd_enable_ext_wow_parser(struct hdd_adapter *adapter, int vdev_id, 1515 int value) 1516 { 1517 tSirExtWoWParams params; 1518 struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); 1519 int rc; 1520 uint8_t pin1, pin2; 1521 1522 rc = wlan_hdd_validate_context(hdd_ctx); 1523 if (rc) 1524 return rc; 1525 1526 if (value < EXT_WOW_TYPE_APP_TYPE1 || 1527 value > EXT_WOW_TYPE_APP_TYPE1_2) { 1528 hdd_err("Invalid type: %d", value); 1529 return -EINVAL; 1530 } 1531 1532 if (value == EXT_WOW_TYPE_APP_TYPE1 && 1533 hdd_ctx->is_extwow_app_type1_param_set) 1534 params.type = value; 1535 else if (value == EXT_WOW_TYPE_APP_TYPE2 && 1536 hdd_ctx->is_extwow_app_type2_param_set) 1537 params.type = value; 1538 else if (value == EXT_WOW_TYPE_APP_TYPE1_2 && 1539 hdd_ctx->is_extwow_app_type1_param_set && 1540 hdd_ctx->is_extwow_app_type2_param_set) 1541 params.type = value; 1542 else { 1543 hdd_err("Set app params before enable it value %d", 1544 value); 1545 return -EINVAL; 1546 } 1547 1548 params.vdev_id = vdev_id; 1549 pin1 = ucfg_pmo_extwow_app1_wakeup_pin_num(hdd_ctx->psoc); 1550 pin2 = ucfg_pmo_extwow_app2_wakeup_pin_num(hdd_ctx->psoc); 1551 params.wakeup_pin_num = pin1 | (pin2 << 8); 1552 1553 return hdd_enable_ext_wow(adapter, ¶ms); 1554 } 1555 1556 static int hdd_set_app_type1_params(mac_handle_t mac_handle, 1557 tpSirAppType1Params arg_params) 1558 { 1559 tSirAppType1Params params; 1560 QDF_STATUS status; 1561 1562 qdf_mem_copy(¶ms, arg_params, sizeof(params)); 1563 1564 status = sme_configure_app_type1_params(mac_handle, ¶ms); 1565 if (QDF_STATUS_SUCCESS != status) { 1566 hdd_err("sme_configure_app_type1_params returned failure %d", 1567 status); 1568 return -EPERM; 1569 } 1570 1571 return 0; 1572 } 1573 1574 static int hdd_set_app_type1_parser(struct hdd_adapter *adapter, 1575 char *arg, int len) 1576 { 1577 struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); 1578 char id[20], password[20]; 1579 tSirAppType1Params params; 1580 int rc; 1581 1582 rc = wlan_hdd_validate_context(hdd_ctx); 1583 if (rc) 1584 return rc; 1585 1586 if (2 != sscanf(arg, "%8s %16s", id, password)) { 1587 hdd_err("Invalid Number of arguments"); 1588 return -EINVAL; 1589 } 1590 1591 memset(¶ms, 0, sizeof(tSirAppType1Params)); 1592 params.vdev_id = adapter->deflink->vdev_id; 1593 qdf_copy_macaddr(¶ms.wakee_mac_addr, &adapter->mac_addr); 1594 1595 params.id_length = strlen(id); 1596 qdf_mem_copy(params.identification_id, id, params.id_length); 1597 params.pass_length = strlen(password); 1598 qdf_mem_copy(params.password, password, params.pass_length); 1599 1600 hdd_debug("%d "QDF_MAC_ADDR_FMT" %.8s %u %.16s %u", 1601 params.vdev_id, 1602 QDF_MAC_ADDR_REF(params.wakee_mac_addr.bytes), 1603 params.identification_id, params.id_length, 1604 params.password, params.pass_length); 1605 1606 return hdd_set_app_type1_params(hdd_ctx->mac_handle, ¶ms); 1607 } 1608 1609 static int hdd_set_app_type2_params(mac_handle_t mac_handle, 1610 tpSirAppType2Params arg_params) 1611 { 1612 tSirAppType2Params params; 1613 QDF_STATUS status; 1614 1615 qdf_mem_copy(¶ms, arg_params, sizeof(params)); 1616 1617 status = sme_configure_app_type2_params(mac_handle, ¶ms); 1618 if (QDF_STATUS_SUCCESS != status) { 1619 hdd_err("sme_configure_app_type2_params returned failure %d", 1620 status); 1621 return -EPERM; 1622 } 1623 1624 return 0; 1625 } 1626 1627 static int hdd_set_app_type2_parser(struct hdd_adapter *adapter, 1628 char *arg, int len) 1629 { 1630 struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); 1631 char mac_addr[20], rc4_key[20]; 1632 unsigned int gateway_mac[QDF_MAC_ADDR_SIZE]; 1633 tSirAppType2Params params; 1634 int ret; 1635 1636 ret = wlan_hdd_validate_context(hdd_ctx); 1637 if (ret) 1638 return ret; 1639 1640 memset(¶ms, 0, sizeof(tSirAppType2Params)); 1641 1642 ret = sscanf(arg, "%17s %16s %x %x %x %u %u %hu %hu %u %u %u %u %u %u", 1643 mac_addr, rc4_key, (unsigned int *)¶ms.ip_id, 1644 (unsigned int *)¶ms.ip_device_ip, 1645 (unsigned int *)¶ms.ip_server_ip, 1646 (unsigned int *)¶ms.tcp_seq, 1647 (unsigned int *)¶ms.tcp_ack_seq, 1648 (uint16_t *)¶ms.tcp_src_port, 1649 (uint16_t *)¶ms.tcp_dst_port, 1650 (unsigned int *)¶ms.keepalive_init, 1651 (unsigned int *)¶ms.keepalive_min, 1652 (unsigned int *)¶ms.keepalive_max, 1653 (unsigned int *)¶ms.keepalive_inc, 1654 (unsigned int *)¶ms.tcp_tx_timeout_val, 1655 (unsigned int *)¶ms.tcp_rx_timeout_val); 1656 1657 if (ret != 15 && ret != 7) { 1658 hdd_err("Invalid Number of arguments"); 1659 return -EINVAL; 1660 } 1661 1662 if (6 != sscanf(mac_addr, "%02x:%02x:%02x:%02x:%02x:%02x", 1663 &gateway_mac[0], &gateway_mac[1], &gateway_mac[2], 1664 &gateway_mac[3], &gateway_mac[4], &gateway_mac[5])) { 1665 hdd_err("Invalid MacAddress Input %s", mac_addr); 1666 return -EINVAL; 1667 } 1668 1669 if (params.tcp_src_port > WLAN_HDD_MAX_TCP_PORT || 1670 params.tcp_dst_port > WLAN_HDD_MAX_TCP_PORT) { 1671 hdd_err("Invalid TCP Port Number"); 1672 return -EINVAL; 1673 } 1674 1675 qdf_mem_copy(¶ms.gateway_mac.bytes, (uint8_t *) &gateway_mac, 1676 QDF_MAC_ADDR_SIZE); 1677 1678 params.rc4_key_len = strlen(rc4_key); 1679 qdf_mem_copy(params.rc4_key, rc4_key, params.rc4_key_len); 1680 1681 params.vdev_id = adapter->deflink->vdev_id; 1682 1683 if (!params.tcp_src_port) 1684 params.tcp_src_port = 1685 ucfg_pmo_extwow_app2_tcp_src_port(hdd_ctx->psoc); 1686 1687 if (!params.tcp_dst_port) 1688 params.tcp_dst_port = 1689 ucfg_pmo_extwow_app2_tcp_dst_port(hdd_ctx->psoc); 1690 1691 if (!params.keepalive_init) 1692 params.keepalive_init = 1693 ucfg_pmo_extwow_app2_init_ping_interval(hdd_ctx->psoc); 1694 1695 if (!params.keepalive_min) 1696 params.keepalive_min = 1697 ucfg_pmo_extwow_app2_min_ping_interval(hdd_ctx->psoc); 1698 1699 if (!params.keepalive_max) 1700 params.keepalive_max = 1701 ucfg_pmo_extwow_app2_max_ping_interval(hdd_ctx->psoc); 1702 1703 if (!params.keepalive_inc) 1704 params.keepalive_inc = 1705 ucfg_pmo_extwow_app2_inc_ping_interval(hdd_ctx->psoc); 1706 1707 if (!params.tcp_tx_timeout_val) 1708 params.tcp_tx_timeout_val = 1709 ucfg_pmo_extwow_app2_tcp_tx_timeout(hdd_ctx->psoc); 1710 1711 if (!params.tcp_rx_timeout_val) 1712 params.tcp_rx_timeout_val = 1713 ucfg_pmo_extwow_app2_tcp_rx_timeout(hdd_ctx->psoc); 1714 1715 hdd_debug(QDF_MAC_ADDR_FMT" %.16s %u %u %u %u %u %u %u %u %u %u %u %u %u", 1716 QDF_MAC_ADDR_REF(gateway_mac), rc4_key, params.ip_id, 1717 params.ip_device_ip, params.ip_server_ip, params.tcp_seq, 1718 params.tcp_ack_seq, params.tcp_src_port, params.tcp_dst_port, 1719 params.keepalive_init, params.keepalive_min, 1720 params.keepalive_max, params.keepalive_inc, 1721 params.tcp_tx_timeout_val, params.tcp_rx_timeout_val); 1722 1723 return hdd_set_app_type2_params(hdd_ctx->mac_handle, ¶ms); 1724 } 1725 #endif /* WLAN_FEATURE_EXTWOW_SUPPORT */ 1726 1727 /** 1728 * hdd_parse_setmaxtxpower_command() - HDD Parse MAXTXPOWER command 1729 * @command: Pointer to MAXTXPOWER command 1730 * @tx_power: Pointer to tx power 1731 * 1732 * This function parses the MAXTXPOWER command passed in the format 1733 * MAXTXPOWER<space>X(Tx power in dbm) 1734 * 1735 * For example input commands: 1736 * 1) MAXTXPOWER -8 -> This is translated into set max TX power to -8 dbm 1737 * 2) MAXTXPOWER -23 -> This is translated into set max TX power to -23 dbm 1738 * 1739 * Return: 0 for success non-zero for failure 1740 */ 1741 static int hdd_parse_setmaxtxpower_command(uint8_t *command, int *tx_power) 1742 { 1743 uint8_t *in_ptr = command; 1744 int temp_int; 1745 int v = 0; 1746 *tx_power = 0; 1747 1748 in_ptr = strnchr(command, strlen(command), SPACE_ASCII_VALUE); 1749 /* no argument after the command */ 1750 if (!in_ptr) 1751 return -EINVAL; 1752 else if (SPACE_ASCII_VALUE != *in_ptr) /* no space after the command */ 1753 return -EINVAL; 1754 1755 /* remove empty spaces */ 1756 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) 1757 in_ptr++; 1758 1759 /* no argument followed by spaces */ 1760 if ('\0' == *in_ptr) 1761 return 0; 1762 1763 v = kstrtos32(in_ptr, 10, &temp_int); 1764 1765 /* Range checking for passed parameter */ 1766 if ((temp_int < HDD_MIN_TX_POWER) || (temp_int > HDD_MAX_TX_POWER)) 1767 return -EINVAL; 1768 1769 *tx_power = temp_int; 1770 1771 hdd_debug("SETMAXTXPOWER: %d", *tx_power); 1772 1773 return 0; 1774 } /* End of hdd_parse_setmaxtxpower_command */ 1775 1776 #ifdef CONFIG_BAND_6GHZ 1777 static int hdd_get_dwell_time_6g(struct wlan_objmgr_psoc *psoc, 1778 uint8_t *command, char *extra, uint8_t n, 1779 uint8_t *len) 1780 { 1781 uint32_t val = 0; 1782 QDF_STATUS status = QDF_STATUS_E_INVAL; 1783 1784 if (strncmp(command, "GETDWELLTIME 6G MAX", 19) == 0) { 1785 status = ucfg_scan_cfg_get_active_6g_dwelltime(psoc, &val); 1786 hdd_debug("active 6g dwelltime:%d", val); 1787 if (QDF_IS_STATUS_SUCCESS(status)) 1788 *len = scnprintf(extra, n, "GETDWELLTIME 6G MAX %u\n", 1789 val); 1790 } else if (strncmp(command, "GETDWELLTIME PASSIVE 6G MAX", 27) == 0) { 1791 status = ucfg_scan_cfg_get_passive_6g_dwelltime(psoc, &val); 1792 hdd_debug("passive 6g dwelltime:%d", val); 1793 if (QDF_IS_STATUS_SUCCESS(status)) 1794 *len = scnprintf(extra, n, 1795 "GETDWELLTIME PASSIVE 6G MAX %u\n", 1796 val); 1797 } 1798 1799 return qdf_status_to_os_return(status); 1800 } 1801 1802 static int hdd_set_dwell_time_6g(struct wlan_objmgr_psoc *psoc, 1803 uint8_t *command) 1804 { 1805 uint8_t *value = command; 1806 int temp = 0; 1807 uint32_t val = 0; 1808 QDF_STATUS status = QDF_STATUS_E_INVAL; 1809 1810 if (strncmp(command, "SETDWELLTIME 6G MAX", 19) == 0) { 1811 if (drv_cmd_validate(command, 19)) 1812 return -EINVAL; 1813 1814 value = value + 20; 1815 temp = kstrtou32(value, 10, &val); 1816 if (temp || !cfg_in_range(CFG_ACTIVE_MAX_6G_CHANNEL_TIME, 1817 val)) { 1818 hdd_err_rl("argument passed for SETDWELLTIME 6G MAX is incorrect"); 1819 return -EFAULT; 1820 } 1821 status = ucfg_scan_cfg_set_active_6g_dwelltime(psoc, val); 1822 } else if (strncmp(command, "SETDWELLTIME PASSIVE 6G MAX", 27) == 0) { 1823 if (drv_cmd_validate(command, 27)) 1824 return -EINVAL; 1825 1826 value = value + 28; 1827 temp = kstrtou32(value, 10, &val); 1828 if (temp || !cfg_in_range(CFG_PASSIVE_MAX_6G_CHANNEL_TIME, 1829 val)) { 1830 hdd_err_rl("argument passed for SETDWELLTIME PASSIVE 6G MAX is incorrect"); 1831 return -EFAULT; 1832 } 1833 status = ucfg_scan_cfg_set_passive_6g_dwelltime(psoc, val); 1834 } 1835 1836 return qdf_status_to_os_return(status); 1837 } 1838 #else 1839 static int hdd_get_dwell_time_6g(struct wlan_objmgr_psoc *psoc, 1840 uint8_t *command, char *extra, uint8_t n, 1841 uint8_t *len) 1842 { 1843 return -EINVAL; 1844 } 1845 1846 static int hdd_set_dwell_time_6g(struct wlan_objmgr_psoc *psoc, 1847 uint8_t *command) 1848 { 1849 return -EINVAL; 1850 } 1851 #endif 1852 1853 static int hdd_get_dwell_time(struct wlan_objmgr_psoc *psoc, uint8_t *command, 1854 char *extra, uint8_t n, uint8_t *len) 1855 { 1856 uint32_t val = 0; 1857 1858 if (!psoc || !command || !extra || !len) { 1859 hdd_err("argument passed for GETDWELLTIME is incorrect"); 1860 return -EINVAL; 1861 } 1862 1863 if (strncmp(command, "GETDWELLTIME ACTIVE MAX", 23) == 0) { 1864 ucfg_scan_cfg_get_active_dwelltime(psoc, &val); 1865 hdd_debug("active max dwelltime:%d", val); 1866 *len = scnprintf(extra, n, "GETDWELLTIME ACTIVE MAX %u\n", val); 1867 return 0; 1868 } 1869 if (strncmp(command, "GETDWELLTIME PASSIVE MAX", 24) == 0) { 1870 ucfg_scan_cfg_get_passive_dwelltime(psoc, &val); 1871 hdd_debug("passive dwelltime:%d", val); 1872 *len = scnprintf(extra, n, "GETDWELLTIME PASSIVE MAX %u\n", 1873 val); 1874 return 0; 1875 } 1876 if (strncmp(command, "GETDWELLTIME 2G MAX", 19) == 0) { 1877 ucfg_scan_cfg_get_active_2g_dwelltime(psoc, &val); 1878 hdd_debug("active 2g dwelltime:%d", val); 1879 *len = scnprintf(extra, n, "GETDWELLTIME 2G MAX %u\n", 1880 val); 1881 return 0; 1882 } 1883 if (strncmp(command, "GETDWELLTIME", 12) == 0) { 1884 ucfg_scan_cfg_get_active_dwelltime(psoc, &val); 1885 hdd_debug("active dwelltime:%d", val); 1886 *len = scnprintf(extra, n, "GETDWELLTIME %u\n", val); 1887 return 0; 1888 } 1889 1890 return hdd_get_dwell_time_6g(psoc, command, extra, n, len); 1891 } 1892 1893 static int hdd_set_dwell_time(struct wlan_objmgr_psoc *psoc, uint8_t *command) 1894 { 1895 uint8_t *value = command; 1896 int retval = 0, temp = 0; 1897 uint32_t val = 0; 1898 1899 if (!psoc) { 1900 hdd_err("psoc is null"); 1901 return -EINVAL; 1902 } 1903 1904 if (strncmp(command, "SETDWELLTIME ACTIVE MAX", 23) == 0) { 1905 if (drv_cmd_validate(command, 23)) 1906 return -EINVAL; 1907 1908 value = value + 24; 1909 temp = kstrtou32(value, 10, &val); 1910 if (temp || !cfg_in_range(CFG_ACTIVE_MAX_CHANNEL_TIME, val)) { 1911 hdd_err_rl("argument passed for SETDWELLTIME ACTIVE MAX is incorrect"); 1912 return -EFAULT; 1913 } 1914 ucfg_scan_cfg_set_active_dwelltime(psoc, val); 1915 } else if (strncmp(command, "SETDWELLTIME PASSIVE MAX", 24) == 0) { 1916 if (drv_cmd_validate(command, 24)) 1917 return -EINVAL; 1918 1919 value = value + 25; 1920 temp = kstrtou32(value, 10, &val); 1921 if (temp || !cfg_in_range(CFG_PASSIVE_MAX_CHANNEL_TIME, val)) { 1922 hdd_err_rl("argument passed for SETDWELLTIME PASSIVE MAX is incorrect"); 1923 return -EFAULT; 1924 } 1925 ucfg_scan_cfg_set_passive_dwelltime(psoc, val); 1926 } else if (strncmp(command, "SETDWELLTIME 2G MAX", 19) == 0) { 1927 if (drv_cmd_validate(command, 19)) 1928 return -EINVAL; 1929 1930 value = value + 20; 1931 temp = kstrtou32(value, 10, &val); 1932 if (temp || !cfg_in_range(CFG_ACTIVE_MAX_2G_CHANNEL_TIME, 1933 val)) { 1934 hdd_err_rl("argument passed for SETDWELLTIME 2G MAX is incorrect"); 1935 return -EFAULT; 1936 } 1937 ucfg_scan_cfg_set_active_2g_dwelltime(psoc, val); 1938 } else if (strncmp(command, "SETDWELLTIME", 12) == 0) { 1939 if (drv_cmd_validate(command, 12)) 1940 return -EINVAL; 1941 1942 value = value + 13; 1943 temp = kstrtou32(value, 10, &val); 1944 if (temp || !cfg_in_range(CFG_ACTIVE_MAX_CHANNEL_TIME, val)) { 1945 hdd_err_rl("argument passed for SETDWELLTIME is incorrect"); 1946 return -EFAULT; 1947 } 1948 ucfg_scan_cfg_set_active_dwelltime(psoc, val); 1949 } else { 1950 retval = hdd_set_dwell_time_6g(psoc, command); 1951 } 1952 1953 return retval; 1954 } 1955 1956 struct link_status_priv { 1957 uint8_t link_status; 1958 }; 1959 1960 /** 1961 * hdd_conc_set_dwell_time() - Set Concurrent dwell time parameters 1962 * @adapter: Adapter upon which the command was received 1963 * @command: ASCII text command that is received 1964 * 1965 * Driver commands: 1966 * wpa_cli DRIVER CONCSETDWELLTIME ACTIVE MAX <value> 1967 * wpa_cli DRIVER CONCSETDWELLTIME ACTIVE MIN <value> 1968 * wpa_cli DRIVER CONCSETDWELLTIME PASSIVE MAX <value> 1969 * wpa_cli DRIVER CONCSETDWELLTIME PASSIVE MIN <value> 1970 * 1971 * Return: 0 for success non-zero for failure 1972 */ 1973 static int hdd_conc_set_dwell_time(struct hdd_adapter *adapter, 1974 uint8_t *command) 1975 { 1976 u8 *value = command; 1977 int val = 0, temp = 0; 1978 int retval = 0; 1979 1980 if (strncmp(command, "CONCSETDWELLTIME ACTIVE MAX", 27) == 0) { 1981 if (drv_cmd_validate(command, 27)) { 1982 hdd_err("Invalid driver command"); 1983 return -EINVAL; 1984 } 1985 1986 value = value + 28; 1987 temp = kstrtou32(value, 10, &val); 1988 if (temp || 1989 !cfg_in_range(CFG_ACTIVE_MAX_CHANNEL_TIME_CONC, val)) { 1990 hdd_err("CONC ACTIVE MAX value %d incorrect", val); 1991 return -EFAULT; 1992 } 1993 ucfg_scan_cfg_set_conc_active_dwelltime( 1994 (WLAN_HDD_GET_CTX(adapter))->psoc, val); 1995 } else if (strncmp(command, "CONCSETDWELLTIME PASSIVE MAX", 28) == 0) { 1996 if (drv_cmd_validate(command, 28)) { 1997 hdd_err("Invalid driver command"); 1998 return -EINVAL; 1999 } 2000 2001 value = value + 29; 2002 temp = kstrtou32(value, 10, &val); 2003 if (temp || 2004 !cfg_in_range(CFG_PASSIVE_MAX_CHANNEL_TIME_CONC, val)) { 2005 hdd_err("CONC PASSIVE MAX val %d incorrect", val); 2006 return -EFAULT; 2007 } 2008 ucfg_scan_cfg_set_conc_passive_dwelltime( 2009 (WLAN_HDD_GET_CTX(adapter))->psoc, val); 2010 } else { 2011 retval = -EINVAL; 2012 } 2013 2014 return retval; 2015 } 2016 2017 static int hdd_enable_unit_test_commands(struct hdd_adapter *adapter, 2018 struct hdd_context *hdd_ctx) 2019 { 2020 enum pld_bus_type bus_type = pld_get_bus_type(hdd_ctx->parent_dev); 2021 u32 arg[3]; 2022 QDF_STATUS status; 2023 2024 if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE || 2025 hdd_get_conparam() == QDF_GLOBAL_MONITOR_MODE) 2026 return -EPERM; 2027 2028 if (adapter->deflink->vdev_id >= WLAN_MAX_VDEVS) { 2029 hdd_err_rl("Invalid vdev id"); 2030 return -EINVAL; 2031 } 2032 2033 if (bus_type == PLD_BUS_TYPE_PCIE) { 2034 arg[0] = 360; 2035 arg[1] = 1; 2036 2037 status = sme_send_unit_test_cmd(adapter->deflink->vdev_id, 2038 WLAN_MODULE_TX, 2039 2, 2040 arg); 2041 if (status != QDF_STATUS_SUCCESS) 2042 return qdf_status_to_os_return(status); 2043 2044 arg[0] = 361; 2045 arg[1] = 1; 2046 2047 status = sme_send_unit_test_cmd(adapter->deflink->vdev_id, 2048 WLAN_MODULE_TX, 2049 2, 2050 arg); 2051 if (status != QDF_STATUS_SUCCESS) 2052 return qdf_status_to_os_return(status); 2053 2054 arg[0] = 84; 2055 arg[1] = 1; 2056 arg[2] = 1; 2057 2058 status = sme_send_unit_test_cmd(adapter->deflink->vdev_id, 2059 WLAN_MODULE_TX, 2060 3, 2061 arg); 2062 if (status != QDF_STATUS_SUCCESS) 2063 return qdf_status_to_os_return(status); 2064 2065 if (hdd_ctx->target_type == TARGET_TYPE_QCA6390) { 2066 arg[0] = 37; 2067 arg[1] = 3000; 2068 2069 status = sme_send_unit_test_cmd( 2070 adapter->deflink->vdev_id, 2071 WLAN_MODULE_RX, 2, arg); 2072 if (status != QDF_STATUS_SUCCESS) 2073 return qdf_status_to_os_return(status); 2074 } 2075 2076 if (hdd_ctx->target_type == TARGET_TYPE_QCA6490) { 2077 arg[0] = 44; 2078 arg[1] = 3000; 2079 2080 status = sme_send_unit_test_cmd( 2081 adapter->deflink->vdev_id, 2082 WLAN_MODULE_RX, 2, arg); 2083 if (status != QDF_STATUS_SUCCESS) 2084 return qdf_status_to_os_return(status); 2085 } 2086 } else if (bus_type == PLD_BUS_TYPE_SNOC) { 2087 arg[0] = 7; 2088 arg[1] = 1; 2089 2090 status = sme_send_unit_test_cmd(adapter->deflink->vdev_id, 2091 0x44, 2092 2, 2093 arg); 2094 if (status != QDF_STATUS_SUCCESS) 2095 return qdf_status_to_os_return(status); 2096 } else { 2097 return -EINVAL; 2098 } 2099 return 0; 2100 } 2101 2102 static int hdd_disable_unit_test_commands(struct hdd_adapter *adapter, 2103 struct hdd_context *hdd_ctx) 2104 { 2105 enum pld_bus_type bus_type = pld_get_bus_type(hdd_ctx->parent_dev); 2106 u32 arg[2]; 2107 QDF_STATUS status; 2108 2109 if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE || 2110 hdd_get_conparam() == QDF_GLOBAL_MONITOR_MODE) 2111 return -EPERM; 2112 2113 if (adapter->deflink->vdev_id >= WLAN_MAX_VDEVS) { 2114 hdd_err_rl("Invalid vdev id"); 2115 return -EINVAL; 2116 } 2117 2118 if (bus_type == PLD_BUS_TYPE_PCIE) { 2119 arg[0] = 360; 2120 arg[1] = 0; 2121 2122 status = sme_send_unit_test_cmd(adapter->deflink->vdev_id, 2123 WLAN_MODULE_TX, 2124 2, 2125 arg); 2126 if (status != QDF_STATUS_SUCCESS) 2127 return qdf_status_to_os_return(status); 2128 2129 arg[0] = 361; 2130 arg[1] = 0; 2131 2132 status = sme_send_unit_test_cmd(adapter->deflink->vdev_id, 2133 WLAN_MODULE_TX, 2134 2, 2135 arg); 2136 if (status != QDF_STATUS_SUCCESS) 2137 return qdf_status_to_os_return(status); 2138 2139 arg[0] = 44; 2140 arg[1] = 0; 2141 2142 status = sme_send_unit_test_cmd(adapter->deflink->vdev_id, 2143 WLAN_MODULE_RX, 2144 2, 2145 arg); 2146 if (status != QDF_STATUS_SUCCESS) 2147 return qdf_status_to_os_return(status); 2148 2149 arg[0] = 84; 2150 arg[1] = 0; 2151 2152 status = sme_send_unit_test_cmd(adapter->deflink->vdev_id, 2153 WLAN_MODULE_RX, 2154 2, 2155 arg); 2156 if (status != QDF_STATUS_SUCCESS) 2157 return qdf_status_to_os_return(status); 2158 2159 } else if (bus_type == PLD_BUS_TYPE_SNOC) { 2160 arg[0] = 7; 2161 arg[1] = 0; 2162 2163 status = sme_send_unit_test_cmd(adapter->deflink->vdev_id, 2164 0x44, 2165 2, 2166 arg); 2167 if (status != QDF_STATUS_SUCCESS) 2168 return qdf_status_to_os_return(status); 2169 } else { 2170 return -EINVAL; 2171 } 2172 return 0; 2173 } 2174 2175 static void hdd_get_link_status_cb(uint8_t status, void *context) 2176 { 2177 struct osif_request *request; 2178 struct link_status_priv *priv; 2179 2180 request = osif_request_get(context); 2181 if (!request) { 2182 hdd_err("Obsolete request"); 2183 return; 2184 } 2185 2186 priv = osif_request_priv(request); 2187 priv->link_status = status; 2188 osif_request_complete(request); 2189 osif_request_put(request); 2190 } 2191 2192 /** 2193 * wlan_hdd_get_link_status() - get link status 2194 * @adapter: pointer to the adapter 2195 * 2196 * This function sends a request to query the link status and waits 2197 * on a timer to invoke the callback. if the callback is invoked then 2198 * latest link status shall be returned or otherwise cached value 2199 * will be returned. 2200 * 2201 * Return: On success, link status shall be returned. 2202 * On error or not associated, link status 0 will be returned. 2203 */ 2204 static int wlan_hdd_get_link_status(struct hdd_adapter *adapter) 2205 { 2206 struct hdd_station_ctx *sta_ctx; 2207 QDF_STATUS status; 2208 int ret; 2209 void *cookie; 2210 struct osif_request *request; 2211 struct link_status_priv *priv; 2212 static const struct osif_request_params params = { 2213 .priv_size = sizeof(*priv), 2214 .timeout_ms = WLAN_WAIT_TIME_LINK_STATUS, 2215 }; 2216 2217 if (cds_is_driver_recovering() || cds_is_driver_in_bad_state()) { 2218 hdd_warn("Recovery in Progress. State: 0x%x Ignore!!!", 2219 cds_get_driver_state()); 2220 return 0; 2221 } 2222 2223 if ((QDF_STA_MODE != adapter->device_mode) && 2224 (QDF_P2P_CLIENT_MODE != adapter->device_mode)) { 2225 hdd_warn("Unsupported in mode %s(%d)", 2226 qdf_opmode_str(adapter->device_mode), 2227 adapter->device_mode); 2228 return 0; 2229 } 2230 2231 sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter->deflink); 2232 if (!hdd_cm_is_vdev_associated(adapter->deflink)) { 2233 /* If not associated, then expected link status return 2234 * value is 0 2235 */ 2236 hdd_warn("Not associated!"); 2237 return 0; 2238 } 2239 2240 request = osif_request_alloc(¶ms); 2241 if (!request) { 2242 hdd_err("Request allocation failure"); 2243 return 0; 2244 } 2245 cookie = osif_request_cookie(request); 2246 2247 status = sme_get_link_status(adapter->hdd_ctx->mac_handle, 2248 hdd_get_link_status_cb, 2249 cookie, adapter->deflink->vdev_id); 2250 if (QDF_STATUS_SUCCESS != status) { 2251 hdd_err("Unable to retrieve link status"); 2252 /* return a cached value */ 2253 } else { 2254 /* request is sent -- wait for the response */ 2255 ret = osif_request_wait_for_response(request); 2256 if (ret) { 2257 hdd_err("SME timed out while retrieving link status"); 2258 /* return a cached value */ 2259 } else { 2260 /* update the adapter with the fresh results */ 2261 priv = osif_request_priv(request); 2262 adapter->link_status = priv->link_status; 2263 } 2264 } 2265 2266 /* 2267 * either we never sent a request, we sent a request and 2268 * received a response or we sent a request and timed out. 2269 * regardless we are done with the request. 2270 */ 2271 osif_request_put(request); 2272 2273 /* either callback updated adapter stats or it has cached data */ 2274 return adapter->link_status; 2275 } 2276 2277 #ifdef FEATURE_WLAN_ESE 2278 /** 2279 * hdd_parse_ese_beacon_req() - Parse ese beacon request 2280 * @pdev: pdev object 2281 * @command: Pointer to data 2282 * @req: Output pointer to store parsed ie information 2283 * 2284 * This function parses the ese beacon request passed in the format 2285 * CCXBEACONREQ<space><Number of fields><space><Measurement token> 2286 * <space>Channel 1<space>Scan Mode <space>Meas Duration<space>Channel N 2287 * <space>Scan Mode N<space>Meas Duration N 2288 * 2289 * If the Number of bcn req fields (N) does not match with the 2290 * actual number of fields passed then take N. 2291 * <Meas Token><Channel><Scan Mode> and <Meas Duration> are treated 2292 * as one pair. For example, CCXBEACONREQ 2 1 1 1 30 2 44 0 40. 2293 * This function does not take care of removing duplicate channels from the 2294 * list 2295 * 2296 * Return: 0 for success non-zero for failure 2297 */ 2298 static int hdd_parse_ese_beacon_req(struct wlan_objmgr_pdev *pdev, 2299 uint8_t *command, 2300 tCsrEseBeaconReq *req) 2301 { 2302 uint8_t *in_ptr = command; 2303 uint8_t input = 0; 2304 uint32_t temp_int = 0; 2305 int j = 0, i = 0, v = 0; 2306 char buf[32]; 2307 2308 in_ptr = strnchr(command, strlen(command), SPACE_ASCII_VALUE); 2309 if (!in_ptr) /* no argument after the command */ 2310 return -EINVAL; 2311 else if (SPACE_ASCII_VALUE != *in_ptr) /* no space after the command */ 2312 return -EINVAL; 2313 2314 /* remove empty spaces */ 2315 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) 2316 in_ptr++; 2317 2318 /* no argument followed by spaces */ 2319 if ('\0' == *in_ptr) 2320 return -EINVAL; 2321 2322 /* Getting the first argument ie Number of IE fields */ 2323 v = sscanf(in_ptr, "%31s ", buf); 2324 if (1 != v) 2325 return -EINVAL; 2326 2327 v = kstrtou8(buf, 10, &input); 2328 if (v < 0) 2329 return -EINVAL; 2330 2331 input = QDF_MIN(input, SIR_ESE_MAX_MEAS_IE_REQS); 2332 req->numBcnReqIe = input; 2333 2334 hdd_debug("Number of Bcn Req Ie fields: %d", req->numBcnReqIe); 2335 2336 for (j = 0; j < (req->numBcnReqIe); j++) { 2337 for (i = 0; i < 4; i++) { 2338 /* 2339 * in_ptr pointing to the beginning of 1st space 2340 * after number of ie fields 2341 */ 2342 in_ptr = strpbrk(in_ptr, " "); 2343 /* no ie data after the number of ie fields argument */ 2344 if (!in_ptr) 2345 return -EINVAL; 2346 2347 /* remove empty space */ 2348 while ((SPACE_ASCII_VALUE == *in_ptr) 2349 && ('\0' != *in_ptr)) 2350 in_ptr++; 2351 2352 /* 2353 * no ie data after the number of ie fields 2354 * argument and spaces 2355 */ 2356 if ('\0' == *in_ptr) 2357 return -EINVAL; 2358 2359 v = sscanf(in_ptr, "%31s ", buf); 2360 if (1 != v) 2361 return -EINVAL; 2362 2363 v = kstrtou32(buf, 10, &temp_int); 2364 if (v < 0) 2365 return -EINVAL; 2366 2367 switch (i) { 2368 case 0: /* Measurement token */ 2369 if (!temp_int) { 2370 hdd_err("Invalid Measurement Token: %u", 2371 temp_int); 2372 return -EINVAL; 2373 } 2374 req->bcnReq[j].measurementToken = 2375 temp_int; 2376 break; 2377 2378 case 1: /* Channel number */ 2379 if (!temp_int || 2380 (temp_int > 2381 WNI_CFG_CURRENT_CHANNEL_STAMAX)) { 2382 hdd_err("Invalid Channel Number: %u", 2383 temp_int); 2384 return -EINVAL; 2385 } 2386 req->bcnReq[j].ch_freq = 2387 wlan_reg_legacy_chan_to_freq(pdev, temp_int); 2388 break; 2389 2390 case 2: /* Scan mode */ 2391 if ((temp_int < eSIR_PASSIVE_SCAN) 2392 || (temp_int > eSIR_BEACON_TABLE)) { 2393 hdd_err("Invalid Scan Mode: %u Expected{0|1|2}", 2394 temp_int); 2395 return -EINVAL; 2396 } 2397 req->bcnReq[j].scanMode = temp_int; 2398 break; 2399 2400 case 3: /* Measurement duration */ 2401 if ((!temp_int 2402 && (req->bcnReq[j].scanMode != 2403 eSIR_BEACON_TABLE)) || 2404 (req->bcnReq[j].scanMode == 2405 eSIR_BEACON_TABLE)) { 2406 hdd_err("Invalid Measurement Duration: %u", 2407 temp_int); 2408 return -EINVAL; 2409 } 2410 req->bcnReq[j].measurementDuration = 2411 temp_int; 2412 break; 2413 } 2414 } 2415 } 2416 2417 for (j = 0; j < req->numBcnReqIe; j++) { 2418 hdd_debug("Index: %d Measurement Token: %u ch_freq: %u Scan Mode: %u Measurement Duration: %u", 2419 j, 2420 req->bcnReq[j].measurementToken, 2421 req->bcnReq[j].ch_freq, 2422 req->bcnReq[j].scanMode, 2423 req->bcnReq[j].measurementDuration); 2424 } 2425 2426 return 0; 2427 } 2428 2429 /** 2430 * hdd_parse_get_cckm_ie() - HDD Parse and fetch the CCKM IE 2431 * @command: Pointer to input data 2432 * @cckm_ie: Pointer to output cckm Ie 2433 * @cckm_ie_len: Pointer to output cckm ie length 2434 * 2435 * This function parses the SETCCKM IE command 2436 * SETCCKMIE<space><ie data> 2437 * 2438 * Return: 0 for success non-zero for failure 2439 */ 2440 static int hdd_parse_get_cckm_ie(uint8_t *command, uint8_t **cckm_ie, 2441 uint8_t *cckm_ie_len) 2442 { 2443 uint8_t *in_ptr = command; 2444 uint8_t *end_ptr; 2445 int j = 0; 2446 int i = 0; 2447 uint8_t temp_u8 = 0; 2448 2449 in_ptr = strnchr(command, strlen(command), SPACE_ASCII_VALUE); 2450 /* no argument after the command */ 2451 if (!in_ptr) 2452 return -EINVAL; 2453 else if (SPACE_ASCII_VALUE != *in_ptr) /* no space after the command */ 2454 return -EINVAL; 2455 2456 /* remove empty spaces */ 2457 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) 2458 in_ptr++; 2459 /* no argument followed by spaces */ 2460 if ('\0' == *in_ptr) 2461 return -EINVAL; 2462 2463 /* find the length of data */ 2464 end_ptr = in_ptr; 2465 while (('\0' != *end_ptr)) { 2466 end_ptr++; 2467 ++(*cckm_ie_len); 2468 } 2469 if (*cckm_ie_len <= 0) 2470 return -EINVAL; 2471 /* 2472 * Allocate the number of bytes based on the number of input characters 2473 * whether it is even or odd. 2474 * if the number of input characters are even, then we need N / 2 byte. 2475 * if the number of input characters are odd, then we need do 2476 * (N + 1) / 2 to compensate rounding off. 2477 * For example, if N = 18, then (18 + 1) / 2 = 9 bytes are enough. 2478 * If N = 19, then we need 10 bytes, hence (19 + 1) / 2 = 10 bytes 2479 */ 2480 *cckm_ie = qdf_mem_malloc((*cckm_ie_len + 1) / 2); 2481 if (!*cckm_ie) 2482 return -ENOMEM; 2483 2484 /* 2485 * the buffer received from the upper layer is character buffer, 2486 * we need to prepare the buffer taking 2 characters in to a U8 hex 2487 * decimal number for example 7f0000f0...form a buffer to contain 2488 * 7f in 0th location, 00 in 1st and f0 in 3rd location 2489 */ 2490 for (i = 0, j = 0; j < *cckm_ie_len; j += 2) { 2491 temp_u8 = (hex_to_bin(in_ptr[j]) << 4) | 2492 (hex_to_bin(in_ptr[j + 1])); 2493 (*cckm_ie)[i++] = temp_u8; 2494 } 2495 *cckm_ie_len = i; 2496 return 0; 2497 } 2498 #endif /* FEATURE_WLAN_ESE */ 2499 2500 int wlan_hdd_set_mc_rate(struct wlan_hdd_link_info *link_info, int target_rate) 2501 { 2502 tSirRateUpdateInd rate_update = {0}; 2503 QDF_STATUS status; 2504 struct hdd_adapter *adapter = link_info->adapter; 2505 struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); 2506 bool bval = false; 2507 2508 if (!hdd_ctx) { 2509 hdd_err("HDD context is null"); 2510 return -EINVAL; 2511 } 2512 if ((QDF_SAP_MODE != adapter->device_mode) && 2513 (QDF_STA_MODE != adapter->device_mode)) { 2514 hdd_err("Received SETMCRATE cmd in invalid mode %s(%d)", 2515 qdf_opmode_str(adapter->device_mode), 2516 adapter->device_mode); 2517 hdd_err("SETMCRATE cmd is allowed only in STA or SOFTAP mode"); 2518 return -EINVAL; 2519 } 2520 2521 status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, &bval); 2522 if (!QDF_IS_STATUS_SUCCESS(status)) { 2523 hdd_err("unable to get vht_enable2x2"); 2524 return -EINVAL; 2525 } 2526 rate_update.nss = (bval == 0) ? 0 : 1; 2527 2528 rate_update.dev_mode = adapter->device_mode; 2529 rate_update.mcastDataRate24GHz = target_rate; 2530 rate_update.mcastDataRate24GHzTxFlag = 1; 2531 rate_update.mcastDataRate5GHz = target_rate; 2532 rate_update.bcastDataRate = -1; 2533 qdf_copy_macaddr(&rate_update.bssid, &adapter->mac_addr); 2534 hdd_debug("MC Target rate %d, mac = "QDF_MAC_ADDR_FMT", dev_mode %s(%d)", 2535 rate_update.mcastDataRate24GHz, 2536 QDF_MAC_ADDR_REF(rate_update.bssid.bytes), 2537 qdf_opmode_str(adapter->device_mode), adapter->device_mode); 2538 status = sme_send_rate_update_ind(hdd_ctx->mac_handle, &rate_update); 2539 if (QDF_STATUS_SUCCESS != status) { 2540 hdd_err("SETMCRATE failed"); 2541 return -EFAULT; 2542 } 2543 return 0; 2544 } 2545 2546 static int drv_cmd_p2p_dev_addr(struct wlan_hdd_link_info *link_info, 2547 struct hdd_context *hdd_ctx, 2548 uint8_t *command, 2549 uint8_t command_len, 2550 struct hdd_priv_data *priv_data) 2551 { 2552 struct qdf_mac_addr *addr = &hdd_ctx->p2p_device_address; 2553 size_t user_size = qdf_min(sizeof(addr->bytes), 2554 (size_t)priv_data->total_len); 2555 2556 qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, 2557 TRACE_CODE_HDD_P2P_DEV_ADDR_IOCTL, 2558 link_info->vdev_id, 2559 (unsigned int)(*(addr->bytes + 2) << 24 | 2560 *(addr->bytes + 3) << 16 | 2561 *(addr->bytes + 4) << 8 | 2562 *(addr->bytes + 5))); 2563 2564 2565 if (copy_to_user(priv_data->buf, addr->bytes, user_size)) { 2566 hdd_err("failed to copy data to user buffer"); 2567 return -EFAULT; 2568 } 2569 2570 return 0; 2571 } 2572 2573 /** 2574 * drv_cmd_p2p_set_noa() - Handler for P2P_SET_NOA driver command 2575 * @link_info: Link info pointer in HDD adapter 2576 * @hdd_ctx: HDD global context 2577 * @command: Entire driver command received from userspace 2578 * @command_len: Length of @command 2579 * @priv_data: Pointer to ioctl private data structure 2580 * 2581 * This is a trivial command handler function which simply forwards the 2582 * command to the actual command processor within the P2P module. 2583 * 2584 * Return: 0 on success, non-zero on failure 2585 */ 2586 static int drv_cmd_p2p_set_noa(struct wlan_hdd_link_info *link_info, 2587 struct hdd_context *hdd_ctx, 2588 uint8_t *command, 2589 uint8_t command_len, 2590 struct hdd_priv_data *priv_data) 2591 { 2592 return hdd_set_p2p_noa(link_info->adapter->dev, command); 2593 } 2594 2595 /** 2596 * drv_cmd_p2p_set_ps() - Handler for P2P_SET_PS driver command 2597 * @link_info: Link info pointer in adapter 2598 * @hdd_ctx: HDD global context 2599 * @command: Entire driver command received from userspace 2600 * @command_len: Length of @command 2601 * @priv_data: Pointer to ioctl private data structure 2602 * 2603 * This is a trivial command handler function which simply forwards the 2604 * command to the actual command processor within the P2P module. 2605 * 2606 * Return: 0 on success, non-zero on failure 2607 */ 2608 static int drv_cmd_p2p_set_ps(struct wlan_hdd_link_info *link_info, 2609 struct hdd_context *hdd_ctx, 2610 uint8_t *command, 2611 uint8_t command_len, 2612 struct hdd_priv_data *priv_data) 2613 { 2614 return hdd_set_p2p_opps(link_info->adapter->dev, command); 2615 } 2616 2617 static int drv_cmd_set_band(struct wlan_hdd_link_info *link_info, 2618 struct hdd_context *hdd_ctx, 2619 uint8_t *command, 2620 uint8_t command_len, 2621 struct hdd_priv_data *priv_data) 2622 { 2623 int err; 2624 uint8_t band; 2625 uint32_t band_bitmap; 2626 2627 /* 2628 * Parse the band value passed from userspace. The first 8 bytes 2629 * should be "SETBAND " and the 9th byte should be a UI band value 2630 */ 2631 err = kstrtou8(command + command_len + 1, 10, &band); 2632 if (err) { 2633 hdd_err("error %d parsing userspace band parameter", err); 2634 return err; 2635 } 2636 2637 band_bitmap = hdd_reg_legacy_setband_to_reg_wifi_band_bitmap(band); 2638 2639 return hdd_reg_set_band(link_info->adapter->dev, band_bitmap); 2640 } 2641 2642 static int drv_cmd_set_wmmps(struct wlan_hdd_link_info *link_info, 2643 struct hdd_context *hdd_ctx, 2644 uint8_t *command, 2645 uint8_t command_len, 2646 struct hdd_priv_data *priv_data) 2647 { 2648 return hdd_wmmps_helper(link_info->adapter, command); 2649 } 2650 2651 #ifdef CONFIG_BAND_6GHZ 2652 /** 2653 * drv_cmd_get_wifi6e_channels() - Handler for GET_WIFI6E_CHANNELS driver 2654 * command 2655 * @link_info: Link info pointer in adapter 2656 * @hdd_ctx: pointer to hdd context 2657 * @command: command name 2658 * @command_len: command buffer length 2659 * @priv_data: output pointer to hold current country code 2660 * 2661 * Return: On success 0, negative value on error. 2662 */ 2663 static int drv_cmd_get_wifi6e_channels(struct wlan_hdd_link_info *link_info, 2664 struct hdd_context *hdd_ctx, 2665 uint8_t *command, 2666 uint8_t command_len, 2667 struct hdd_priv_data *priv_data) 2668 { 2669 uint8_t power_type; 2670 char extra[SIZE_OF_WIFI6E_CHAN_LIST] = {0}; 2671 int i, ret, copied_length = 0; 2672 enum channel_state state; 2673 struct regulatory_channel *chan_list; 2674 size_t max_buf_len = QDF_MIN(priv_data->total_len, 2675 SIZE_OF_WIFI6E_CHAN_LIST); 2676 QDF_STATUS status; 2677 2678 if (wlan_hdd_validate_context(hdd_ctx)) 2679 return -EINVAL; 2680 2681 ret = kstrtou8(command + command_len + 1, 10, &power_type); 2682 if (ret) { 2683 hdd_err("error %d parsing userspace 6 GHz power type parameter", 2684 ret); 2685 return -EINVAL; 2686 } 2687 2688 switch (power_type) { 2689 case 0: 2690 power_type = REG_CLI_DEF_LPI; 2691 break; 2692 case 1: 2693 power_type = REG_CLI_DEF_VLP; 2694 break; 2695 case 2: 2696 power_type = REG_CLI_DEF_SP; 2697 break; 2698 default: 2699 hdd_err("The power type : %u, is incorrect", power_type); 2700 return -EINVAL; 2701 } 2702 2703 chan_list = qdf_mem_malloc(NUM_CHANNELS * sizeof(*chan_list)); 2704 if (!chan_list) 2705 return -ENOMEM; 2706 2707 status = wlan_reg_get_pwrmode_chan_list(hdd_ctx->pdev, chan_list, 2708 power_type); 2709 if (QDF_IS_STATUS_ERROR(status)) { 2710 hdd_err("Failed to get wifi6e channel list for given power type %u", 2711 power_type); 2712 ret = qdf_status_to_os_return(status); 2713 goto free; 2714 } 2715 2716 for (i = 0; i < NUM_6GHZ_CHANNELS && copied_length < max_buf_len - 1; 2717 i++) { 2718 state = chan_list[i + MIN_6GHZ_CHANNEL].state; 2719 if (state == CHANNEL_STATE_INVALID || 2720 state == CHANNEL_STATE_DISABLE) 2721 continue; 2722 copied_length += scnprintf(extra + copied_length, 2723 max_buf_len - copied_length, "%u ", 2724 chan_list[i + MIN_6GHZ_CHANNEL].chan_num); 2725 } 2726 2727 if (copied_length == 0) { 2728 hdd_err("No Channel List found for given power type %u", 2729 power_type); 2730 ret = -EINVAL; 2731 goto free; 2732 } 2733 2734 if (copy_to_user(priv_data->buf, &extra, copied_length + 1)) { 2735 hdd_err("failed to copy data to user buffer"); 2736 ret = -EFAULT; 2737 goto free; 2738 } 2739 2740 hdd_debug("Power type = %u, Data = %s", power_type, extra); 2741 free: 2742 qdf_mem_free(chan_list); 2743 return ret; 2744 } 2745 #endif 2746 2747 static inline int __drv_cmd_country(struct wlan_hdd_link_info *link_info, 2748 struct hdd_context *hdd_ctx, 2749 uint8_t *command, 2750 uint8_t command_len, 2751 struct hdd_priv_data *priv_data) 2752 { 2753 char *country_code; 2754 2755 country_code = strnchr(command, strlen(command), ' '); 2756 /* no argument after the command */ 2757 if (!country_code) 2758 return -EINVAL; 2759 2760 /* no space after the command */ 2761 if (*country_code != SPACE_ASCII_VALUE) 2762 return -EINVAL; 2763 2764 country_code++; 2765 2766 /* removing empty spaces */ 2767 while ((*country_code == SPACE_ASCII_VALUE) && 2768 (*country_code != '\0')) 2769 country_code++; 2770 2771 /* no or less than 2 arguments followed by spaces */ 2772 if (*country_code == '\0' || *(country_code + 1) == '\0') 2773 return -EINVAL; 2774 2775 return hdd_reg_set_country(hdd_ctx, country_code); 2776 } 2777 2778 static inline int drv_cmd_country(struct wlan_hdd_link_info *link_info, 2779 struct hdd_context *hdd_ctx, 2780 uint8_t *command, 2781 uint8_t command_len, 2782 struct hdd_priv_data *priv_data) 2783 { 2784 struct osif_psoc_sync *psoc_sync; 2785 int errno; 2786 2787 errno = osif_psoc_sync_op_start(wiphy_dev(hdd_ctx->wiphy), &psoc_sync); 2788 if (errno) 2789 return errno; 2790 errno = __drv_cmd_country(link_info, hdd_ctx, command, command_len, 2791 priv_data); 2792 2793 osif_psoc_sync_op_stop(psoc_sync); 2794 2795 return errno; 2796 } 2797 2798 /** 2799 * drv_cmd_get_country() - Helper function to get current county code 2800 * @link_info: Link info pointer in HDD adapter 2801 * @hdd_ctx: pointer to hdd context 2802 * @command: command name 2803 * @command_len: command buffer length 2804 * @priv_data: output pointer to hold current country code 2805 * 2806 * Return: On success 0, negative value on error. 2807 */ 2808 static int drv_cmd_get_country(struct wlan_hdd_link_info *link_info, 2809 struct hdd_context *hdd_ctx, 2810 uint8_t *command, uint8_t command_len, 2811 struct hdd_priv_data *priv_data) 2812 { 2813 uint8_t buf[SIZE_OF_GETCOUNTRYREV_OUTPUT] = {0}; 2814 uint8_t cc[REG_ALPHA2_LEN + 1]; 2815 int ret = 0, len; 2816 2817 qdf_mem_copy(cc, hdd_ctx->reg.alpha2, REG_ALPHA2_LEN); 2818 cc[REG_ALPHA2_LEN] = '\0'; 2819 2820 len = scnprintf(buf, sizeof(buf), "%s %s", 2821 "GETCOUNTRYREV", cc); 2822 hdd_debug("buf = %s", buf); 2823 len = QDF_MIN(priv_data->total_len, len + 1); 2824 if (copy_to_user(priv_data->buf, buf, len)) { 2825 hdd_err("failed to copy data to user buffer"); 2826 ret = -EFAULT; 2827 } 2828 2829 return ret; 2830 } 2831 2832 static int drv_cmd_set_roam_trigger(struct wlan_hdd_link_info *link_info, 2833 struct hdd_context *hdd_ctx, 2834 uint8_t *command, 2835 uint8_t command_len, 2836 struct hdd_priv_data *priv_data) 2837 { 2838 int ret; 2839 uint8_t *value = command; 2840 int8_t rssi = 0; 2841 uint8_t lookup_threshold = cfg_default( 2842 CFG_LFR_NEIGHBOR_LOOKUP_RSSI_THRESHOLD); 2843 QDF_STATUS status = QDF_STATUS_SUCCESS; 2844 2845 /* Move pointer to ahead of SETROAMTRIGGER<delimiter> */ 2846 value = value + command_len + 1; 2847 2848 /* Convert the value from ascii to integer */ 2849 ret = kstrtos8(value, 10, &rssi); 2850 if (ret < 0) { 2851 /* 2852 * If the input value is greater than max value of datatype, 2853 * then also kstrtou8 fails 2854 */ 2855 hdd_err("kstrtou8 failed Input value may be out of range[%d - %d]", 2856 cfg_min(CFG_LFR_NEIGHBOR_LOOKUP_RSSI_THRESHOLD), 2857 cfg_max(CFG_LFR_NEIGHBOR_LOOKUP_RSSI_THRESHOLD)); 2858 ret = -EINVAL; 2859 goto exit; 2860 } 2861 2862 if (!cfg_in_range(CFG_LFR_NEIGHBOR_LOOKUP_RSSI_THRESHOLD, 2863 rssi)) { 2864 hdd_err("Neighbor lookup threshold value %d is out of range (Min: %d Max: %d)", 2865 lookup_threshold, 2866 cfg_min(CFG_LFR_NEIGHBOR_LOOKUP_RSSI_THRESHOLD), 2867 cfg_max(CFG_LFR_NEIGHBOR_LOOKUP_RSSI_THRESHOLD)); 2868 ret = -EINVAL; 2869 goto exit; 2870 } 2871 2872 lookup_threshold = abs(rssi); 2873 2874 qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, 2875 TRACE_CODE_HDD_SETROAMTRIGGER_IOCTL, 2876 link_info->vdev_id, lookup_threshold); 2877 2878 hdd_debug("Set Roam trigger: Neighbor lookup threshold = %d", 2879 lookup_threshold); 2880 2881 status = sme_set_neighbor_lookup_rssi_threshold( 2882 hdd_ctx->mac_handle, 2883 link_info->vdev_id, 2884 lookup_threshold); 2885 if (QDF_STATUS_SUCCESS != status) { 2886 hdd_err("Failed to set roam trigger, try again"); 2887 ret = -EPERM; 2888 goto exit; 2889 } 2890 2891 exit: 2892 return ret; 2893 } 2894 2895 static int drv_cmd_get_roam_trigger(struct wlan_hdd_link_info *link_info, 2896 struct hdd_context *hdd_ctx, 2897 uint8_t *command, 2898 uint8_t command_len, 2899 struct hdd_priv_data *priv_data) 2900 { 2901 int ret = 0; 2902 uint8_t lookup_threshold; 2903 int rssi; 2904 char extra[32]; 2905 uint8_t len = 0; 2906 QDF_STATUS status; 2907 2908 status = ucfg_cm_get_neighbor_lookup_rssi_threshold( 2909 hdd_ctx->psoc, 2910 link_info->vdev_id, 2911 &lookup_threshold); 2912 if (QDF_IS_STATUS_ERROR(status)) 2913 return qdf_status_to_os_return(status); 2914 2915 qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, 2916 TRACE_CODE_HDD_GETROAMTRIGGER_IOCTL, 2917 link_info->vdev_id, lookup_threshold); 2918 2919 hdd_debug("vdev_id: %u, lookup_threshold: %u", 2920 link_info->vdev_id, lookup_threshold); 2921 2922 rssi = (-1) * lookup_threshold; 2923 2924 len = scnprintf(extra, sizeof(extra), "%s %d", command, rssi); 2925 len = QDF_MIN(priv_data->total_len, len + 1); 2926 if (copy_to_user(priv_data->buf, &extra, len)) { 2927 hdd_err("failed to copy data to user buffer"); 2928 ret = -EFAULT; 2929 } 2930 2931 return ret; 2932 } 2933 2934 static int drv_cmd_set_roam_scan_period(struct wlan_hdd_link_info *link_info, 2935 struct hdd_context *hdd_ctx, 2936 uint8_t *command, 2937 uint8_t command_len, 2938 struct hdd_priv_data *priv_data) 2939 { 2940 int ret = 0; 2941 uint8_t *value = command; 2942 uint8_t roam_scan_period = 0; 2943 uint16_t empty_scan_refresh_period; 2944 bool flag = false, val = false; 2945 2946 ucfg_mlme_get_connection_roaming_ini_present(hdd_ctx->psoc, &val); 2947 if (val) { 2948 flag = true; 2949 empty_scan_refresh_period = 2950 cfg_default(CFG_ROAM_SCAN_FIRST_TIMER) * 1000; 2951 } else { 2952 empty_scan_refresh_period = 2953 cfg_default(CFG_LFR_EMPTY_SCAN_REFRESH_PERIOD); 2954 } 2955 2956 /* input refresh period is in terms of seconds */ 2957 2958 /* Move pointer to ahead of SETROAMSCANPERIOD<delimiter> */ 2959 value = value + command_len + 1; 2960 2961 /* Convert the value from ascii to integer */ 2962 ret = kstrtou8(value, 10, &roam_scan_period); 2963 if (ret < 0) { 2964 /* 2965 * If the input value is greater than max value of datatype, 2966 * then also kstrtou8 fails 2967 */ 2968 if (flag) 2969 hdd_err("kstrtou8 failed Input value may be out of range[%d - %d]", 2970 cfg_min(CFG_ROAM_SCAN_FIRST_TIMER), 2971 cfg_max(CFG_ROAM_SCAN_FIRST_TIMER)); 2972 else 2973 hdd_err("kstrtou8 failed Input value may be out of range[%d - %d]", 2974 (cfg_min(CFG_LFR_EMPTY_SCAN_REFRESH_PERIOD) / 1000), 2975 (cfg_max(CFG_LFR_EMPTY_SCAN_REFRESH_PERIOD) / 1000)); 2976 ret = -EINVAL; 2977 goto exit; 2978 } 2979 2980 if (!ucfg_mlme_validate_scan_period(hdd_ctx->psoc, 2981 roam_scan_period * 1000)) { 2982 ret = -EINVAL; 2983 goto exit; 2984 } 2985 2986 qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, 2987 TRACE_CODE_HDD_SETROAMSCANPERIOD_IOCTL, 2988 link_info->vdev_id, roam_scan_period); 2989 2990 empty_scan_refresh_period = roam_scan_period * 1000; 2991 2992 hdd_debug("Received Command to Set roam scan period (Empty Scan refresh period) = %d", 2993 roam_scan_period); 2994 2995 sme_update_empty_scan_refresh_period(hdd_ctx->mac_handle, 2996 link_info->vdev_id, 2997 empty_scan_refresh_period); 2998 2999 exit: 3000 return ret; 3001 } 3002 3003 static int drv_cmd_get_roam_scan_period(struct wlan_hdd_link_info *link_info, 3004 struct hdd_context *hdd_ctx, 3005 uint8_t *command, 3006 uint8_t command_len, 3007 struct hdd_priv_data *priv_data) 3008 { 3009 int ret = 0; 3010 uint16_t empty_scan_refresh_period; 3011 char extra[32]; 3012 uint8_t len; 3013 QDF_STATUS status; 3014 3015 status = ucfg_cm_get_empty_scan_refresh_period( 3016 hdd_ctx->psoc, 3017 link_info->vdev_id, 3018 &empty_scan_refresh_period); 3019 if (QDF_IS_STATUS_ERROR(status)) 3020 return qdf_status_to_os_return(status); 3021 3022 hdd_debug("vdev_id: %u, empty_scan_refresh_period: %u", 3023 link_info->vdev_id, empty_scan_refresh_period); 3024 3025 qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, 3026 TRACE_CODE_HDD_GETROAMSCANPERIOD_IOCTL, 3027 link_info->vdev_id, 3028 empty_scan_refresh_period); 3029 3030 len = scnprintf(extra, sizeof(extra), "%s %d", 3031 "GETROAMSCANPERIOD", 3032 (empty_scan_refresh_period / 1000)); 3033 /* Returned value is in units of seconds */ 3034 len = QDF_MIN(priv_data->total_len, len + 1); 3035 if (copy_to_user(priv_data->buf, &extra, len)) { 3036 hdd_err("failed to copy data to user buffer"); 3037 ret = -EFAULT; 3038 } 3039 3040 return ret; 3041 } 3042 3043 static int 3044 drv_cmd_set_roam_scan_refresh_period(struct wlan_hdd_link_info *link_info, 3045 struct hdd_context *hdd_ctx, 3046 uint8_t *command, uint8_t command_len, 3047 struct hdd_priv_data *priv_data) 3048 { 3049 int ret; 3050 uint8_t *value = command; 3051 uint8_t roam_scan_refresh_period = 0; 3052 uint16_t neighbor_scan_refresh_period = 3053 cfg_default(CFG_LFR_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD); 3054 3055 /* input refresh period is in terms of seconds */ 3056 /* Move pointer to ahead of SETROAMSCANREFRESHPERIOD<delimiter> */ 3057 value = value + command_len + 1; 3058 3059 /* Convert the value from ascii to integer */ 3060 ret = kstrtou8(value, 10, &roam_scan_refresh_period); 3061 if (ret < 0) { 3062 /* 3063 * If the input value is greater than max value of datatype, 3064 * then also kstrtou8 fails 3065 */ 3066 hdd_err("kstrtou8 failed Input value may be out of range[%d - %d]", 3067 (cfg_min(CFG_LFR_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD) 3068 / 1000), 3069 (cfg_max(CFG_LFR_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD) 3070 / 1000)); 3071 ret = -EINVAL; 3072 goto exit; 3073 } 3074 3075 if (!cfg_in_range(CFG_LFR_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD, 3076 roam_scan_refresh_period)) { 3077 hdd_err("Neighbor scan results refresh period value %d is out of range (Min: %d Max: %d)", 3078 roam_scan_refresh_period, 3079 (cfg_min(CFG_LFR_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD) 3080 / 1000), 3081 (cfg_max(CFG_LFR_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD) 3082 / 1000)); 3083 ret = -EINVAL; 3084 goto exit; 3085 } 3086 neighbor_scan_refresh_period = roam_scan_refresh_period * 1000; 3087 3088 hdd_debug("Received Command to Set roam scan refresh period (Scan refresh period) = %d", 3089 neighbor_scan_refresh_period); 3090 3091 sme_set_neighbor_scan_refresh_period(hdd_ctx->mac_handle, 3092 link_info->vdev_id, 3093 neighbor_scan_refresh_period); 3094 3095 exit: 3096 return ret; 3097 } 3098 3099 static int 3100 drv_cmd_get_roam_scan_refresh_period(struct wlan_hdd_link_info *link_info, 3101 struct hdd_context *hdd_ctx, 3102 uint8_t *command, uint8_t command_len, 3103 struct hdd_priv_data *priv_data) 3104 { 3105 int ret = 0; 3106 uint16_t value = 0; 3107 char extra[32]; 3108 uint8_t len; 3109 3110 ucfg_cm_get_neighbor_scan_refresh_period(hdd_ctx->psoc, &value); 3111 len = scnprintf(extra, sizeof(extra), "%s %d", 3112 "GETROAMSCANREFRESHPERIOD", 3113 (value / 1000)); 3114 /* Returned value is in units of seconds */ 3115 len = QDF_MIN(priv_data->total_len, len + 1); 3116 if (copy_to_user(priv_data->buf, &extra, len)) { 3117 hdd_err("failed to copy data to user buffer"); 3118 ret = -EFAULT; 3119 } 3120 3121 return ret; 3122 } 3123 3124 static int drv_cmd_set_roam_mode(struct wlan_hdd_link_info *link_info, 3125 struct hdd_context *hdd_ctx, 3126 uint8_t *command, 3127 uint8_t command_len, 3128 struct hdd_priv_data *priv_data) 3129 { 3130 mac_handle_t mac_handle; 3131 int ret; 3132 uint8_t *value = command; 3133 uint8_t roam_mode = cfg_default(CFG_LFR_FEATURE_ENABLED); 3134 3135 /* Move pointer to ahead of SETROAMMODE<delimiter> */ 3136 value = value + SIZE_OF_SETROAMMODE + 1; 3137 3138 /* Convert the value from ascii to integer */ 3139 ret = kstrtou8(value, 10, &roam_mode); 3140 if (ret < 0) { 3141 /* 3142 * If the input value is greater than max value of datatype, 3143 * then also kstrtou8 fails 3144 */ 3145 hdd_err("kstrtou8 failed range [%d - %d]", 3146 cfg_min(CFG_LFR_FEATURE_ENABLED), 3147 cfg_max(CFG_LFR_FEATURE_ENABLED)); 3148 ret = -EINVAL; 3149 goto exit; 3150 } 3151 3152 hdd_debug("Received Command to Set Roam Mode = %d", 3153 roam_mode); 3154 3155 if (sme_roaming_in_progress(hdd_ctx->mac_handle, 3156 link_info->vdev_id)) { 3157 hdd_err_rl("Roaming in progress for vdev %d", 3158 link_info->vdev_id); 3159 return -EAGAIN; 3160 } 3161 3162 /* 3163 * Note that 3164 * SETROAMMODE 0 is to enable LFR while 3165 * SETROAMMODE 1 is to disable LFR, but 3166 * notify_is_fast_roam_ini_feature_enabled 0/1 is to 3167 * enable/disable. So, we have to invert the value 3168 * to call sme_update_is_fast_roam_ini_feature_enabled. 3169 */ 3170 if (roam_mode) { 3171 /* Roam enable */ 3172 roam_mode = cfg_min(CFG_LFR_FEATURE_ENABLED); 3173 } else { 3174 /* Roam disable */ 3175 roam_mode = cfg_max(CFG_LFR_FEATURE_ENABLED); 3176 } 3177 3178 mac_handle = hdd_ctx->mac_handle; 3179 if (roam_mode) { 3180 ucfg_mlme_set_roam_scan_offload_enabled(hdd_ctx->psoc, 3181 (bool)roam_mode); 3182 sme_update_is_fast_roam_ini_feature_enabled( 3183 mac_handle, link_info->vdev_id, roam_mode); 3184 } else { 3185 sme_update_is_fast_roam_ini_feature_enabled( 3186 mac_handle, link_info->vdev_id, roam_mode); 3187 ucfg_mlme_set_roam_scan_offload_enabled(hdd_ctx->psoc, 3188 roam_mode); 3189 } 3190 3191 exit: 3192 return ret; 3193 } 3194 3195 static int drv_cmd_set_suspend_mode(struct wlan_hdd_link_info *link_info, 3196 struct hdd_context *hdd_ctx, 3197 uint8_t *command, 3198 uint8_t command_len, 3199 struct hdd_priv_data *priv_data) 3200 { 3201 struct hdd_adapter *adapter = link_info->adapter; 3202 int errno; 3203 uint8_t *value = command; 3204 QDF_STATUS status; 3205 uint8_t idle_monitor; 3206 3207 if (QDF_STA_MODE != adapter->device_mode) { 3208 hdd_debug("Non-STA interface"); 3209 return 0; 3210 } 3211 3212 /* Move pointer to ahead of SETSUSPENDMODE<delimiter> */ 3213 value = value + SIZE_OF_SETSUSPENDMODE + 1; 3214 3215 /* Convert the value from ascii to integer */ 3216 errno = kstrtou8(value, 10, &idle_monitor); 3217 if (errno < 0) { 3218 /* 3219 * If the input value is greater than max value of datatype, 3220 * then also kstrtou8 fails 3221 */ 3222 hdd_err("Range validation failed"); 3223 return -EINVAL; 3224 } 3225 3226 hdd_debug("idle_monitor:%d", idle_monitor); 3227 status = ucfg_pmo_tgt_psoc_send_idle_roam_suspend_mode(hdd_ctx->psoc, 3228 idle_monitor); 3229 if (QDF_IS_STATUS_ERROR(status)) { 3230 hdd_debug("Send suspend mode to fw failed"); 3231 return -EINVAL; 3232 } 3233 return 0; 3234 } 3235 3236 static int drv_cmd_get_roam_mode(struct wlan_hdd_link_info *link_info, 3237 struct hdd_context *hdd_ctx, 3238 uint8_t *command, 3239 uint8_t command_len, 3240 struct hdd_priv_data *priv_data) 3241 { 3242 int ret = 0; 3243 bool roam_mode = ucfg_cm_get_is_lfr_feature_enabled(hdd_ctx->psoc); 3244 char extra[32]; 3245 uint8_t len; 3246 3247 /* 3248 * roamMode value shall be inverted because the sementics is different. 3249 */ 3250 if (roam_mode) 3251 roam_mode = cfg_min(CFG_LFR_FEATURE_ENABLED); 3252 else 3253 roam_mode = cfg_max(CFG_LFR_FEATURE_ENABLED); 3254 3255 len = scnprintf(extra, sizeof(extra), "%s %d", command, roam_mode); 3256 len = QDF_MIN(priv_data->total_len, len + 1); 3257 if (copy_to_user(priv_data->buf, &extra, len)) { 3258 hdd_err("failed to copy data to user buffer"); 3259 ret = -EFAULT; 3260 } 3261 3262 return ret; 3263 } 3264 3265 static int drv_cmd_set_roam_delta(struct wlan_hdd_link_info *link_info, 3266 struct hdd_context *hdd_ctx, 3267 uint8_t *command, 3268 uint8_t command_len, 3269 struct hdd_priv_data *priv_data) 3270 { 3271 int ret; 3272 uint8_t *value = command; 3273 uint8_t roam_rssi_diff = cfg_default(CFG_LFR_ROAM_RSSI_DIFF); 3274 3275 /* Move pointer to ahead of SETROAMDELTA<delimiter> */ 3276 value = value + command_len + 1; 3277 3278 /* Convert the value from ascii to integer */ 3279 ret = kstrtou8(value, 10, &roam_rssi_diff); 3280 if (ret < 0) { 3281 /* 3282 * If the input value is greater than max value of datatype, 3283 * then also kstrtou8 fails 3284 */ 3285 hdd_err("kstrtou8 failed range [%d - %d]", 3286 cfg_min(CFG_LFR_ROAM_RSSI_DIFF), 3287 cfg_max(CFG_LFR_ROAM_RSSI_DIFF)); 3288 ret = -EINVAL; 3289 goto exit; 3290 } 3291 3292 if (!cfg_in_range(CFG_LFR_ROAM_RSSI_DIFF, roam_rssi_diff)) { 3293 hdd_err("Roam rssi diff value %d is out of range (Min: %d Max: %d)", 3294 roam_rssi_diff, 3295 cfg_min(CFG_LFR_ROAM_RSSI_DIFF), 3296 cfg_max(CFG_LFR_ROAM_RSSI_DIFF)); 3297 ret = -EINVAL; 3298 goto exit; 3299 } 3300 3301 hdd_debug("Received Command to Set roam rssi diff = %d", 3302 roam_rssi_diff); 3303 3304 sme_update_roam_rssi_diff(hdd_ctx->mac_handle, 3305 link_info->vdev_id, 3306 roam_rssi_diff); 3307 3308 exit: 3309 return ret; 3310 } 3311 3312 static int drv_cmd_get_roam_delta(struct wlan_hdd_link_info *link_info, 3313 struct hdd_context *hdd_ctx, 3314 uint8_t *command, 3315 uint8_t command_len, 3316 struct hdd_priv_data *priv_data) 3317 { 3318 int ret = 0; 3319 uint8_t rssi_diff; 3320 char extra[32]; 3321 uint8_t len; 3322 QDF_STATUS status; 3323 3324 status = ucfg_cm_get_roam_rssi_diff(hdd_ctx->psoc, 3325 link_info->vdev_id, 3326 &rssi_diff); 3327 if (QDF_IS_STATUS_ERROR(status)) 3328 return qdf_status_to_os_return(status); 3329 3330 hdd_debug("vdev_id: %u, rssi_diff: %u", 3331 link_info->vdev_id, rssi_diff); 3332 3333 qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, 3334 TRACE_CODE_HDD_GETROAMDELTA_IOCTL, 3335 link_info->vdev_id, rssi_diff); 3336 len = scnprintf(extra, sizeof(extra), "%s %d", 3337 command, rssi_diff); 3338 len = QDF_MIN(priv_data->total_len, len + 1); 3339 3340 if (copy_to_user(priv_data->buf, &extra, len)) { 3341 hdd_err("failed to copy data to user buffer"); 3342 ret = -EFAULT; 3343 } 3344 3345 return ret; 3346 } 3347 3348 static int drv_cmd_get_band(struct wlan_hdd_link_info *link_info, 3349 struct hdd_context *hdd_ctx, 3350 uint8_t *command, 3351 uint8_t command_len, 3352 struct hdd_priv_data *priv_data) 3353 { 3354 int ret = 0, band = -1; 3355 char extra[32]; 3356 uint8_t len = 0; 3357 3358 hdd_get_band_helper(hdd_ctx, &band); 3359 3360 qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, 3361 TRACE_CODE_HDD_GETBAND_IOCTL, 3362 link_info->vdev_id, band); 3363 3364 len = scnprintf(extra, sizeof(extra), "%s %d", command, band); 3365 len = QDF_MIN(priv_data->total_len, len + 1); 3366 3367 if (copy_to_user(priv_data->buf, &extra, len)) { 3368 hdd_err("failed to copy data to user buffer"); 3369 ret = -EFAULT; 3370 } 3371 3372 return ret; 3373 } 3374 3375 #ifdef WLAN_FEATURE_ROAM_OFFLOAD 3376 static bool is_roam_ch_from_fw_supported(struct hdd_context *hdd_ctx) 3377 { 3378 return hdd_ctx->roam_ch_from_fw_supported; 3379 } 3380 3381 struct roam_ch_priv { 3382 struct roam_scan_ch_resp roam_ch; 3383 }; 3384 3385 /** 3386 * hdd_dump_roam_scan_ch_list() - Function to dump roam scan chan list content 3387 * @chan_list: pointer to channel list received from FW via an event 3388 * WMI_ROAM_SCAN_CHANNEL_LIST_EVENTID 3389 * @num_channels: Number of channels 3390 * 3391 * Return: none 3392 */ 3393 static void 3394 hdd_dump_roam_scan_ch_list(uint32_t *chan_list, uint16_t num_channels) 3395 { 3396 uint8_t i, j; 3397 uint8_t ch_cache_str[128] = {0}; 3398 3399 /* print channel list after sorting in ascending order */ 3400 for (i = 0, j = 0; i < num_channels; i++) { 3401 if (j < sizeof(ch_cache_str)) 3402 j += snprintf(ch_cache_str + j, 3403 sizeof(ch_cache_str) - j, " %d", 3404 chan_list[i]); 3405 else 3406 break; 3407 } 3408 3409 hdd_debug("No of freq:%d, freq list : %s", num_channels, ch_cache_str); 3410 } 3411 3412 /** 3413 * hdd_sort_roam_scan_ch_list() - Function to sort roam scan channel list in 3414 * descending order before sending it to supplicant 3415 * @chan_list: pointer to channel list received from FW via an event 3416 * WMI_ROAM_SCAN_CHANNEL_LIST_EVENTID 3417 * @num_channels: Number of channels 3418 * 3419 * Return: none 3420 */ 3421 static void 3422 hdd_sort_roam_scan_ch_list(uint32_t *chan_list, uint16_t num_channels) 3423 { 3424 uint8_t i, j; 3425 uint32_t swap = 0; 3426 3427 for (i = 0; i < (num_channels - 1) && 3428 i < WNI_CFG_VALID_CHANNEL_LIST_LEN; i++) { 3429 for (j = 0; j < (num_channels - i - 1); j++) { 3430 if (chan_list[j] < chan_list[j + 1]) { 3431 swap = chan_list[j]; 3432 chan_list[j] = chan_list[j + 1]; 3433 chan_list[j + 1] = swap; 3434 } 3435 } 3436 } 3437 } 3438 3439 void hdd_get_roam_scan_ch_cb(hdd_handle_t hdd_handle, 3440 struct roam_scan_ch_resp *roam_ch, 3441 void *context) 3442 { 3443 struct osif_request *request; 3444 struct roam_ch_priv *priv; 3445 uint8_t *event = NULL, i = 0; 3446 uint32_t *freq = NULL, len; 3447 struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); 3448 3449 hdd_debug("roam scan ch list event received : vdev_id:%d command resp: %d", 3450 roam_ch->vdev_id, roam_ch->command_resp); 3451 /** 3452 * If command response is set in the response message, then it is 3453 * getroamscanchannels command response else this event is asynchronous 3454 * event raised by firmware. 3455 */ 3456 if (!roam_ch->command_resp) { 3457 /* 3458 * Maximum allowed channel count is 30 in supplicant vendor 3459 * event of RCL list. So if number of channels present in 3460 * channel list received from FW is more than 30 channels then 3461 * restrict it to 30 channels only. 3462 */ 3463 if (roam_ch->num_channels > MAX_RCL_CHANNEL_COUNT) 3464 roam_ch->num_channels = MAX_RCL_CHANNEL_COUNT; 3465 3466 len = roam_ch->num_channels * sizeof(roam_ch->chan_list[0]); 3467 if (!len) { 3468 hdd_err("Invalid len"); 3469 return; 3470 } 3471 event = (uint8_t *)qdf_mem_malloc(len); 3472 if (!event) 3473 return; 3474 3475 freq = (uint32_t *)event; 3476 hdd_sort_roam_scan_ch_list(roam_ch->chan_list, 3477 roam_ch->num_channels); 3478 hdd_dump_roam_scan_ch_list(roam_ch->chan_list, 3479 roam_ch->num_channels); 3480 for (i = 0; i < roam_ch->num_channels && 3481 i < WNI_CFG_VALID_CHANNEL_LIST_LEN; i++) { 3482 freq[i] = roam_ch->chan_list[i]; 3483 } 3484 3485 hdd_send_roam_scan_ch_list_event(hdd_ctx, roam_ch->vdev_id, 3486 len, event); 3487 qdf_mem_free(event); 3488 return; 3489 } 3490 3491 request = osif_request_get(context); 3492 if (!request) { 3493 hdd_err("Obsolete request"); 3494 return; 3495 } 3496 priv = osif_request_priv(request); 3497 3498 hdd_sort_roam_scan_ch_list(roam_ch->chan_list, roam_ch->num_channels); 3499 hdd_dump_roam_scan_ch_list(roam_ch->chan_list, roam_ch->num_channels); 3500 3501 priv->roam_ch.num_channels = roam_ch->num_channels; 3502 for (i = 0; i < priv->roam_ch.num_channels && 3503 i < WNI_CFG_VALID_CHANNEL_LIST_LEN; i++) 3504 priv->roam_ch.chan_list[i] = roam_ch->chan_list[i]; 3505 3506 osif_request_complete(request); 3507 osif_request_put(request); 3508 } 3509 3510 static uint32_t 3511 hdd_get_roam_chan_from_fw(struct hdd_adapter *adapter, uint32_t *chan_list, 3512 uint8_t *num_channels) 3513 { 3514 QDF_STATUS status = QDF_STATUS_E_INVAL; 3515 struct hdd_context *hdd_ctx; 3516 int ret, i; 3517 void *cookie; 3518 struct osif_request *request; 3519 struct roam_ch_priv *priv; 3520 struct roam_scan_ch_resp *p_roam_ch; 3521 static const struct osif_request_params params = { 3522 .priv_size = sizeof(*priv) + 3523 sizeof(priv->roam_ch.chan_list[0]) * 3524 WNI_CFG_VALID_CHANNEL_LIST_LEN, 3525 .timeout_ms = WLAN_WAIT_TIME_STATS, 3526 }; 3527 3528 hdd_ctx = WLAN_HDD_GET_CTX(adapter); 3529 request = osif_request_alloc(¶ms); 3530 if (!request) { 3531 hdd_err("Request allocation failure"); 3532 return -ENOMEM; 3533 } 3534 3535 priv = osif_request_priv(request); 3536 p_roam_ch = &priv->roam_ch; 3537 /** channel list starts after response structure*/ 3538 priv->roam_ch.chan_list = (uint32_t *)(p_roam_ch + 1); 3539 cookie = osif_request_cookie(request); 3540 status = sme_get_roam_scan_ch(hdd_ctx->mac_handle, 3541 adapter->deflink->vdev_id, cookie); 3542 3543 if (QDF_IS_STATUS_ERROR(status)) { 3544 hdd_err("Unable to retrieve roam channels"); 3545 ret = qdf_status_to_os_return(status); 3546 goto cleanup; 3547 } 3548 3549 ret = osif_request_wait_for_response(request); 3550 if (ret) { 3551 hdd_err("SME timed out while retrieving raom channels"); 3552 goto cleanup; 3553 } 3554 3555 priv = osif_request_priv(request); 3556 *num_channels = priv->roam_ch.num_channels; 3557 for (i = 0; i < *num_channels; i++) 3558 chan_list[i] = priv->roam_ch.chan_list[i]; 3559 3560 cleanup: 3561 osif_request_put(request); 3562 3563 return ret; 3564 } 3565 3566 int 3567 hdd_get_roam_scan_freq(struct hdd_adapter *adapter, mac_handle_t mac_handle, 3568 uint32_t *chan_list, uint8_t *num_channels) 3569 { 3570 int ret = 0; 3571 3572 if (!adapter || !mac_handle || !chan_list || !num_channels) { 3573 hdd_err("failed to get roam scan channel, invalid input"); 3574 return -EFAULT; 3575 } 3576 3577 if (is_roam_ch_from_fw_supported(adapter->hdd_ctx)) { 3578 ret = hdd_get_roam_chan_from_fw(adapter, chan_list, 3579 num_channels); 3580 if (ret != QDF_STATUS_SUCCESS) { 3581 hdd_err("failed to get roam scan channel list from FW"); 3582 return -EFAULT; 3583 } 3584 3585 return ret; 3586 } 3587 3588 if (sme_get_roam_scan_channel_list(mac_handle, chan_list, num_channels, 3589 adapter->deflink->vdev_id) != 3590 QDF_STATUS_SUCCESS) { 3591 hdd_err("failed to get roam scan channel list"); 3592 return -EFAULT; 3593 } 3594 3595 return ret; 3596 } 3597 #endif 3598 3599 enum host_target_comm_log { 3600 HTC_CREDIT_HISTORY_LOG = 0, 3601 COMMAND_LOG, 3602 COMMAND_TX_CMP_LOG, 3603 MGMT_COMMAND_LOG, 3604 MGMT_COMMAND_TX_CMP_LOG, 3605 EVENT_LOG, 3606 RX_EVENT_LOG, 3607 MGMT_EVENT_LOG 3608 }; 3609 3610 static int printk_adapter(void *priv, const char *fmt, ...) 3611 { 3612 int ret; 3613 va_list args; 3614 3615 va_start(args, fmt); 3616 ret = vprintk(fmt, args); 3617 ret += printk("\n"); 3618 va_end(args); 3619 3620 return ret; 3621 } 3622 3623 void hdd_ioctl_log_buffer(int log_id, uint32_t count, qdf_abstract_print 3624 *custom_print, 3625 void *print_ctx) 3626 { 3627 qdf_abstract_print *print; 3628 3629 if (custom_print) 3630 print = custom_print; 3631 else 3632 print = &printk_adapter; 3633 switch (log_id) { 3634 case HTC_CREDIT_HISTORY_LOG: 3635 cds_print_htc_credit_history(count, print, print_ctx); 3636 break; 3637 case COMMAND_LOG: 3638 wma_print_wmi_cmd_log(count, print, print_ctx); 3639 break; 3640 case COMMAND_TX_CMP_LOG: 3641 wma_print_wmi_cmd_tx_cmp_log(count, print, print_ctx); 3642 break; 3643 case MGMT_COMMAND_LOG: 3644 wma_print_wmi_mgmt_cmd_log(count, print, print_ctx); 3645 break; 3646 case MGMT_COMMAND_TX_CMP_LOG: 3647 wma_print_wmi_mgmt_cmd_tx_cmp_log(count, print, print_ctx); 3648 break; 3649 case EVENT_LOG: 3650 wma_print_wmi_event_log(count, print, print_ctx); 3651 break; 3652 case RX_EVENT_LOG: 3653 wma_print_wmi_rx_event_log(count, print, print_ctx); 3654 break; 3655 case MGMT_EVENT_LOG: 3656 wma_print_wmi_mgmt_event_log(count, print, print_ctx); 3657 break; 3658 default: 3659 print(print_ctx, "Invalid Log Id %d", log_id); 3660 break; 3661 } 3662 } 3663 3664 #ifdef WLAN_DUMP_LOG_BUF_CNT 3665 void hdd_dump_log_buffer(void *print_ctx, qdf_abstract_print *custom_print) 3666 { 3667 int i; 3668 3669 for (i = 0; i <= MGMT_EVENT_LOG; i++) 3670 hdd_ioctl_log_buffer(i, WLAN_DUMP_LOG_BUF_CNT, custom_print, 3671 print_ctx); 3672 } 3673 #endif 3674 3675 static int drv_cmd_get_ccx_mode(struct wlan_hdd_link_info *link_info, 3676 struct hdd_context *hdd_ctx, 3677 uint8_t *command, 3678 uint8_t command_len, 3679 struct hdd_priv_data *priv_data) 3680 { 3681 int ret = 0; 3682 bool ese_mode = ucfg_cm_get_is_ese_feature_enabled(hdd_ctx->psoc); 3683 char extra[32]; 3684 uint8_t len = 0; 3685 struct pmkid_mode_bits pmkid_modes; 3686 3687 hdd_get_pmkid_modes(hdd_ctx, &pmkid_modes); 3688 /* 3689 * Check if the features PMKID/ESE/11R are supported simultaneously, 3690 * then this operation is not permitted (return FAILURE) 3691 */ 3692 if (ese_mode && 3693 (pmkid_modes.fw_okc || pmkid_modes.fw_pmksa_cache) && 3694 ucfg_cm_get_is_ft_feature_enabled(hdd_ctx->psoc)) { 3695 hdd_warn("PMKID/ESE/11R are supported simultaneously hence this operation is not permitted!"); 3696 ret = -EPERM; 3697 goto exit; 3698 } 3699 3700 len = scnprintf(extra, sizeof(extra), "%s %d", 3701 "GETCCXMODE", ese_mode); 3702 len = QDF_MIN(priv_data->total_len, len + 1); 3703 if (copy_to_user(priv_data->buf, &extra, len)) { 3704 hdd_err("failed to copy data to user buffer"); 3705 ret = -EFAULT; 3706 goto exit; 3707 } 3708 3709 exit: 3710 return ret; 3711 } 3712 3713 static int drv_cmd_get_okc_mode(struct wlan_hdd_link_info *link_info, 3714 struct hdd_context *hdd_ctx, 3715 uint8_t *command, 3716 uint8_t command_len, 3717 struct hdd_priv_data *priv_data) 3718 { 3719 int ret = 0; 3720 struct pmkid_mode_bits pmkid_modes; 3721 char extra[32]; 3722 uint8_t len = 0; 3723 3724 hdd_get_pmkid_modes(hdd_ctx, &pmkid_modes); 3725 /* 3726 * Check if the features OKC/ESE/11R are supported simultaneously, 3727 * then this operation is not permitted (return FAILURE) 3728 */ 3729 if (pmkid_modes.fw_okc && 3730 ucfg_cm_get_is_ese_feature_enabled(hdd_ctx->psoc) && 3731 ucfg_cm_get_is_ft_feature_enabled(hdd_ctx->psoc)) { 3732 hdd_warn("PMKID/ESE/11R are supported simultaneously hence this operation is not permitted!"); 3733 ret = -EPERM; 3734 goto exit; 3735 } 3736 3737 len = scnprintf(extra, sizeof(extra), "%s %d", 3738 "GETOKCMODE", pmkid_modes.fw_okc); 3739 len = QDF_MIN(priv_data->total_len, len + 1); 3740 3741 if (copy_to_user(priv_data->buf, &extra, len)) { 3742 hdd_err("failed to copy data to user buffer"); 3743 ret = -EFAULT; 3744 goto exit; 3745 } 3746 3747 exit: 3748 return ret; 3749 } 3750 3751 static int drv_cmd_get_fast_roam(struct wlan_hdd_link_info *link_info, 3752 struct hdd_context *hdd_ctx, 3753 uint8_t *command, 3754 uint8_t command_len, 3755 struct hdd_priv_data *priv_data) 3756 { 3757 int ret = 0; 3758 bool lfr_mode = ucfg_cm_get_is_lfr_feature_enabled(hdd_ctx->psoc); 3759 char extra[32]; 3760 uint8_t len = 0; 3761 3762 len = scnprintf(extra, sizeof(extra), "%s %d", 3763 "GETFASTROAM", lfr_mode); 3764 len = QDF_MIN(priv_data->total_len, len + 1); 3765 3766 if (copy_to_user(priv_data->buf, &extra, len)) { 3767 hdd_err("failed to copy data to user buffer"); 3768 ret = -EFAULT; 3769 } 3770 3771 return ret; 3772 } 3773 3774 static int drv_cmd_get_fast_transition(struct wlan_hdd_link_info *link_info, 3775 struct hdd_context *hdd_ctx, 3776 uint8_t *command, 3777 uint8_t command_len, 3778 struct hdd_priv_data *priv_data) 3779 { 3780 int ret = 0; 3781 bool ft = ucfg_cm_get_is_ft_feature_enabled(hdd_ctx->psoc); 3782 char extra[32]; 3783 uint8_t len = 0; 3784 3785 len = scnprintf(extra, sizeof(extra), "%s %d", 3786 "GETFASTTRANSITION", ft); 3787 len = QDF_MIN(priv_data->total_len, len + 1); 3788 3789 if (copy_to_user(priv_data->buf, &extra, len)) { 3790 hdd_err("failed to copy data to user buffer"); 3791 ret = -EFAULT; 3792 } 3793 3794 return ret; 3795 } 3796 3797 static int 3798 drv_cmd_set_roam_scan_channel_min_time(struct wlan_hdd_link_info *link_info, 3799 struct hdd_context *hdd_ctx, 3800 uint8_t *command, uint8_t command_len, 3801 struct hdd_priv_data *priv_data) 3802 { 3803 int ret = 0; 3804 uint8_t *value = command; 3805 uint8_t min_time = cfg_default(CFG_LFR_NEIGHBOR_SCAN_MIN_CHAN_TIME); 3806 3807 /* Move pointer to ahead of SETROAMSCANCHANNELMINTIME<delimiter> */ 3808 value = value + command_len + 1; 3809 3810 /* Convert the value from ascii to integer */ 3811 ret = kstrtou8(value, 10, &min_time); 3812 if (ret < 0) { 3813 /* 3814 * If the input value is greater than max value of datatype, 3815 * then also kstrtou8 fails 3816 */ 3817 hdd_err("kstrtou8 failed range [%d - %d]", 3818 cfg_min(CFG_LFR_NEIGHBOR_SCAN_MIN_CHAN_TIME), 3819 cfg_max(CFG_LFR_NEIGHBOR_SCAN_MIN_CHAN_TIME)); 3820 ret = -EINVAL; 3821 goto exit; 3822 } 3823 3824 if (!cfg_in_range(CFG_LFR_NEIGHBOR_SCAN_MIN_CHAN_TIME, min_time)) { 3825 hdd_err("scan min channel time value %d is out of range (Min: %d Max: %d)", 3826 min_time, 3827 cfg_min(CFG_LFR_NEIGHBOR_SCAN_MIN_CHAN_TIME), 3828 cfg_max(CFG_LFR_NEIGHBOR_SCAN_MIN_CHAN_TIME)); 3829 ret = -EINVAL; 3830 goto exit; 3831 } 3832 3833 qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, 3834 TRACE_CODE_HDD_SETROAMSCANCHANNELMINTIME_IOCTL, 3835 link_info->vdev_id, min_time); 3836 3837 hdd_debug("Received Command to change channel min time = %d", 3838 min_time); 3839 3840 sme_set_neighbor_scan_min_chan_time(hdd_ctx->mac_handle, 3841 min_time, link_info->vdev_id); 3842 3843 exit: 3844 return ret; 3845 } 3846 3847 static int drv_cmd_send_action_frame(struct wlan_hdd_link_info *link_info, 3848 struct hdd_context *hdd_ctx, 3849 uint8_t *command, 3850 uint8_t command_len, 3851 struct hdd_priv_data *priv_data) 3852 { 3853 return hdd_parse_sendactionframe(link_info->adapter, command, 3854 priv_data->total_len); 3855 } 3856 3857 static int 3858 drv_cmd_get_roam_scan_channel_min_time(struct wlan_hdd_link_info *link_info, 3859 struct hdd_context *hdd_ctx, 3860 uint8_t *command, uint8_t command_len, 3861 struct hdd_priv_data *priv_data) 3862 { 3863 int ret = 0; 3864 uint16_t val; 3865 char extra[32]; 3866 uint8_t len = 0; 3867 3868 val = ucfg_cm_get_neighbor_scan_min_chan_time(hdd_ctx->psoc, 3869 link_info->vdev_id); 3870 /* value is interms of msec */ 3871 len = scnprintf(extra, sizeof(extra), "%s %d", 3872 "GETROAMSCANCHANNELMINTIME", val); 3873 len = QDF_MIN(priv_data->total_len, len + 1); 3874 3875 qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, 3876 TRACE_CODE_HDD_GETROAMSCANCHANNELMINTIME_IOCTL, 3877 link_info->vdev_id, val); 3878 3879 if (copy_to_user(priv_data->buf, &extra, len)) { 3880 hdd_err("failed to copy data to user buffer"); 3881 ret = -EFAULT; 3882 } 3883 3884 return ret; 3885 } 3886 3887 static int drv_cmd_set_scan_channel_time(struct wlan_hdd_link_info *link_info, 3888 struct hdd_context *hdd_ctx, 3889 uint8_t *command, 3890 uint8_t command_len, 3891 struct hdd_priv_data *priv_data) 3892 { 3893 int ret = 0; 3894 uint8_t *value = command; 3895 uint16_t max_time = cfg_default(CFG_LFR_NEIGHBOR_SCAN_MAX_CHAN_TIME); 3896 3897 /* Move pointer to ahead of SETSCANCHANNELTIME<delimiter> */ 3898 value = value + command_len + 1; 3899 3900 /* Convert the value from ascii to integer */ 3901 ret = kstrtou16(value, 10, &max_time); 3902 if (ret < 0) { 3903 /* 3904 * If the input value is greater than max value of datatype, 3905 * then also kstrtou8 fails 3906 */ 3907 hdd_err("kstrtou16 failed range [%d - %d]", 3908 cfg_min(CFG_LFR_NEIGHBOR_SCAN_MAX_CHAN_TIME), 3909 cfg_max(CFG_LFR_NEIGHBOR_SCAN_MAX_CHAN_TIME)); 3910 ret = -EINVAL; 3911 goto exit; 3912 } 3913 3914 if (!cfg_in_range(CFG_LFR_NEIGHBOR_SCAN_MAX_CHAN_TIME, max_time)) { 3915 hdd_err("lfr mode value %d is out of range (Min: %d Max: %d)", 3916 max_time, 3917 cfg_min(CFG_LFR_NEIGHBOR_SCAN_MAX_CHAN_TIME), 3918 cfg_max(CFG_LFR_NEIGHBOR_SCAN_MAX_CHAN_TIME)); 3919 ret = -EINVAL; 3920 goto exit; 3921 } 3922 3923 hdd_debug("Received Command to change channel max time = %d", 3924 max_time); 3925 3926 sme_set_neighbor_scan_max_chan_time(hdd_ctx->mac_handle, 3927 link_info->vdev_id, 3928 max_time); 3929 3930 exit: 3931 return ret; 3932 } 3933 3934 static int drv_cmd_get_scan_channel_time(struct wlan_hdd_link_info *link_info, 3935 struct hdd_context *hdd_ctx, 3936 uint8_t *command, 3937 uint8_t command_len, 3938 struct hdd_priv_data *priv_data) 3939 { 3940 int ret = 0; 3941 uint16_t val; 3942 char extra[32]; 3943 uint8_t len = 0; 3944 3945 val = ucfg_cm_get_neighbor_scan_max_chan_time(hdd_ctx->psoc, 3946 link_info->vdev_id); 3947 3948 hdd_debug("vdev_id: %u, scan channel time: %u", 3949 link_info->vdev_id, val); 3950 3951 /* value is interms of msec */ 3952 len = scnprintf(extra, sizeof(extra), "%s %d", 3953 "GETSCANCHANNELTIME", val); 3954 len = QDF_MIN(priv_data->total_len, len + 1); 3955 3956 if (copy_to_user(priv_data->buf, &extra, len)) { 3957 hdd_err("failed to copy data to user buffer"); 3958 ret = -EFAULT; 3959 } 3960 3961 return ret; 3962 } 3963 3964 static int drv_cmd_set_scan_home_time(struct wlan_hdd_link_info *link_info, 3965 struct hdd_context *hdd_ctx, 3966 uint8_t *command, 3967 uint8_t command_len, 3968 struct hdd_priv_data *priv_data) 3969 { 3970 int ret = 0; 3971 uint8_t *value = command; 3972 uint16_t val = cfg_default(CFG_LFR_NEIGHBOR_SCAN_TIMER_PERIOD); 3973 3974 /* Move pointer to ahead of SETSCANHOMETIME<delimiter> */ 3975 value = value + command_len + 1; 3976 3977 /* Convert the value from ascii to integer */ 3978 ret = kstrtou16(value, 10, &val); 3979 if (ret < 0) { 3980 /* 3981 * If the input value is greater than max value of datatype, 3982 * then also kstrtou8 fails 3983 */ 3984 hdd_err("kstrtou16 failed range [%d - %d]", 3985 cfg_min(CFG_LFR_NEIGHBOR_SCAN_TIMER_PERIOD), 3986 cfg_max(CFG_LFR_NEIGHBOR_SCAN_TIMER_PERIOD)); 3987 ret = -EINVAL; 3988 goto exit; 3989 } 3990 3991 if (!cfg_in_range(CFG_LFR_NEIGHBOR_SCAN_TIMER_PERIOD, val)) { 3992 hdd_err("scan home time value %d is out of range (Min: %d Max: %d)", 3993 val, 3994 cfg_min(CFG_LFR_NEIGHBOR_SCAN_TIMER_PERIOD), 3995 cfg_max(CFG_LFR_NEIGHBOR_SCAN_TIMER_PERIOD)); 3996 ret = -EINVAL; 3997 goto exit; 3998 } 3999 4000 hdd_debug("Received Command to change scan home time = %d", 4001 val); 4002 4003 sme_set_neighbor_scan_period(hdd_ctx->mac_handle, 4004 link_info->vdev_id, val); 4005 4006 exit: 4007 return ret; 4008 } 4009 4010 static int drv_cmd_get_scan_home_time(struct wlan_hdd_link_info *link_info, 4011 struct hdd_context *hdd_ctx, 4012 uint8_t *command, 4013 uint8_t command_len, 4014 struct hdd_priv_data *priv_data) 4015 { 4016 int ret = 0; 4017 uint16_t val; 4018 char extra[32]; 4019 uint8_t len = 0; 4020 4021 val = ucfg_cm_get_neighbor_scan_period(hdd_ctx->psoc, 4022 link_info->vdev_id); 4023 hdd_debug("vdev_id: %u, scan home time: %u", 4024 link_info->vdev_id, val); 4025 4026 /* value is interms of msec */ 4027 len = scnprintf(extra, sizeof(extra), "%s %d", 4028 "GETSCANHOMETIME", val); 4029 len = QDF_MIN(priv_data->total_len, len + 1); 4030 4031 if (copy_to_user(priv_data->buf, &extra, len)) { 4032 hdd_err("failed to copy data to user buffer"); 4033 ret = -EFAULT; 4034 } 4035 4036 return ret; 4037 } 4038 4039 static int drv_cmd_set_roam_intra_band(struct wlan_hdd_link_info *link_info, 4040 struct hdd_context *hdd_ctx, 4041 uint8_t *command, 4042 uint8_t command_len, 4043 struct hdd_priv_data *priv_data) 4044 { 4045 int ret = 0; 4046 uint8_t *value = command; 4047 uint8_t val = cfg_default(CFG_LFR_ROAM_INTRA_BAND); 4048 4049 /* Move pointer to ahead of SETROAMINTRABAND<delimiter> */ 4050 value = value + command_len + 1; 4051 4052 /* Convert the value from ascii to integer */ 4053 ret = kstrtou8(value, 10, &val); 4054 if (ret < 0) { 4055 /* 4056 * If the input value is greater than max value of datatype, 4057 * then also kstrtou8 fails 4058 */ 4059 hdd_err("kstrtou8 failed range [%d - %d]", 4060 cfg_min(CFG_LFR_ROAM_INTRA_BAND), 4061 cfg_max(CFG_LFR_ROAM_INTRA_BAND)); 4062 ret = -EINVAL; 4063 goto exit; 4064 } 4065 4066 hdd_debug("Received Command to change intra band = %d", 4067 val); 4068 4069 ucfg_mlme_set_roam_intra_band(hdd_ctx->psoc, (bool)val); 4070 4071 /* Disable roaming on Vdev before setting PCL */ 4072 sme_stop_roaming(hdd_ctx->mac_handle, link_info->vdev_id, 4073 REASON_DRIVER_DISABLED, RSO_SET_PCL); 4074 4075 policy_mgr_set_pcl_for_existing_combo(hdd_ctx->psoc, PM_STA_MODE, 4076 link_info->vdev_id); 4077 4078 /* Enable roaming once SET pcl is done */ 4079 sme_start_roaming(hdd_ctx->mac_handle, link_info->vdev_id, 4080 REASON_DRIVER_ENABLED, RSO_SET_PCL); 4081 4082 exit: 4083 return ret; 4084 } 4085 4086 static int drv_cmd_get_roam_intra_band(struct wlan_hdd_link_info *link_info, 4087 struct hdd_context *hdd_ctx, 4088 uint8_t *command, 4089 uint8_t command_len, 4090 struct hdd_priv_data *priv_data) 4091 { 4092 int ret = 0; 4093 uint16_t val = 0; 4094 char extra[32]; 4095 uint8_t len = 0; 4096 4097 ucfg_cm_get_roam_intra_band(hdd_ctx->psoc, &val); 4098 /* value is interms of msec */ 4099 len = scnprintf(extra, sizeof(extra), "%s %d", 4100 "GETROAMINTRABAND", val); 4101 len = QDF_MIN(priv_data->total_len, len + 1); 4102 if (copy_to_user(priv_data->buf, &extra, len)) { 4103 hdd_err("failed to copy data to user buffer"); 4104 ret = -EFAULT; 4105 } 4106 4107 return ret; 4108 } 4109 4110 static int drv_cmd_set_scan_n_probes(struct wlan_hdd_link_info *link_info, 4111 struct hdd_context *hdd_ctx, 4112 uint8_t *command, 4113 uint8_t command_len, 4114 struct hdd_priv_data *priv_data) 4115 { 4116 int ret = 0; 4117 uint8_t *value = command; 4118 uint8_t nprobes = cfg_default(CFG_LFR_ROAM_SCAN_N_PROBES); 4119 4120 /* Move pointer to ahead of SETSCANNPROBES<delimiter> */ 4121 value = value + command_len + 1; 4122 4123 /* Convert the value from ascii to integer */ 4124 ret = kstrtou8(value, 10, &nprobes); 4125 if (ret < 0) { 4126 /* 4127 * If the input value is greater than max value of datatype, 4128 * then also kstrtou8 fails 4129 */ 4130 hdd_err("kstrtou8 failed range [%d - %d]", 4131 cfg_min(CFG_LFR_ROAM_SCAN_N_PROBES), 4132 cfg_max(CFG_LFR_ROAM_SCAN_N_PROBES)); 4133 ret = -EINVAL; 4134 goto exit; 4135 } 4136 4137 if (!cfg_in_range(CFG_LFR_ROAM_SCAN_N_PROBES, nprobes)) { 4138 hdd_err("NProbes value %d is out of range (Min: %d Max: %d)", 4139 nprobes, 4140 cfg_min(CFG_LFR_ROAM_SCAN_N_PROBES), 4141 cfg_max(CFG_LFR_ROAM_SCAN_N_PROBES)); 4142 ret = -EINVAL; 4143 goto exit; 4144 } 4145 4146 hdd_debug("Received Command to Set nProbes = %d", 4147 nprobes); 4148 4149 sme_update_roam_scan_n_probes(hdd_ctx->mac_handle, 4150 link_info->vdev_id, nprobes); 4151 4152 exit: 4153 return ret; 4154 } 4155 4156 static int drv_cmd_get_scan_n_probes(struct wlan_hdd_link_info *link_info, 4157 struct hdd_context *hdd_ctx, 4158 uint8_t *command, 4159 uint8_t command_len, 4160 struct hdd_priv_data *priv_data) 4161 { 4162 int ret = 0; 4163 uint8_t val; 4164 char extra[32]; 4165 uint8_t len = 0; 4166 QDF_STATUS status; 4167 4168 status = sme_get_roam_scan_n_probes(hdd_ctx->mac_handle, 4169 link_info->vdev_id, &val); 4170 if (QDF_IS_STATUS_ERROR(status)) 4171 return qdf_status_to_os_return(status); 4172 4173 hdd_debug("vdev_id: %u, scan_n_probes: %u", 4174 link_info->vdev_id, val); 4175 4176 len = scnprintf(extra, sizeof(extra), "%s %d", command, val); 4177 len = QDF_MIN(priv_data->total_len, len + 1); 4178 if (copy_to_user(priv_data->buf, &extra, len)) { 4179 hdd_err("failed to copy data to user buffer"); 4180 ret = -EFAULT; 4181 } 4182 4183 return ret; 4184 } 4185 4186 static int drv_cmd_set_scan_home_away_time(struct wlan_hdd_link_info *link_info, 4187 struct hdd_context *hdd_ctx, 4188 uint8_t *command, 4189 uint8_t command_len, 4190 struct hdd_priv_data *priv_data) 4191 { 4192 int ret = 0; 4193 uint8_t *value = command; 4194 uint16_t home_away_time = cfg_default(CFG_LFR_ROAM_SCAN_HOME_AWAY_TIME); 4195 4196 /* input value is in units of msec */ 4197 4198 /* Move pointer to ahead of SETSCANHOMEAWAYTIME<delimiter> */ 4199 value = value + command_len + 1; 4200 4201 /* Convert the value from ascii to integer */ 4202 ret = kstrtou16(value, 10, &home_away_time); 4203 if (ret < 0) { 4204 /* 4205 * If the input value is greater than max value of datatype, 4206 * then also kstrtou8 fails 4207 */ 4208 hdd_err("kstrtou8 failed range [%d - %d]", 4209 cfg_min(CFG_LFR_ROAM_SCAN_HOME_AWAY_TIME), 4210 cfg_max(CFG_LFR_ROAM_SCAN_HOME_AWAY_TIME)); 4211 ret = -EINVAL; 4212 goto exit; 4213 } 4214 4215 if (!cfg_in_range(CFG_LFR_ROAM_SCAN_HOME_AWAY_TIME, home_away_time)) { 4216 hdd_err("home_away_time value %d is out of range (min: %d max: %d)", 4217 home_away_time, 4218 cfg_min(CFG_LFR_ROAM_SCAN_HOME_AWAY_TIME), 4219 cfg_max(CFG_LFR_ROAM_SCAN_HOME_AWAY_TIME)); 4220 ret = -EINVAL; 4221 goto exit; 4222 } 4223 4224 hdd_debug("Received Command to Set scan away time = %d", 4225 home_away_time); 4226 4227 sme_update_roam_scan_home_away_time(hdd_ctx->mac_handle, 4228 link_info->vdev_id, 4229 home_away_time, true); 4230 4231 exit: 4232 return ret; 4233 } 4234 4235 static int drv_cmd_get_scan_home_away_time(struct wlan_hdd_link_info *link_info, 4236 struct hdd_context *hdd_ctx, 4237 uint8_t *command, 4238 uint8_t command_len, 4239 struct hdd_priv_data *priv_data) 4240 { 4241 int ret = 0; 4242 uint16_t val; 4243 char extra[32] = {0}; 4244 uint8_t len = 0; 4245 QDF_STATUS status; 4246 4247 status = ucfg_cm_get_roam_scan_home_away_time(hdd_ctx->psoc, 4248 link_info->vdev_id, 4249 &val); 4250 if (QDF_IS_STATUS_ERROR(status)) 4251 return qdf_status_to_os_return(status); 4252 4253 hdd_debug("vdev_id: %u, scan home away time: %u", 4254 link_info->vdev_id, val); 4255 4256 len = scnprintf(extra, sizeof(extra), "%s %d", command, val); 4257 len = QDF_MIN(priv_data->total_len, len + 1); 4258 4259 if (copy_to_user(priv_data->buf, &extra, len)) { 4260 hdd_err("failed to copy data to user buffer"); 4261 ret = -EFAULT; 4262 } 4263 4264 return ret; 4265 } 4266 4267 static int drv_cmd_reassoc(struct wlan_hdd_link_info *link_info, 4268 struct hdd_context *hdd_ctx, 4269 uint8_t *command, 4270 uint8_t command_len, 4271 struct hdd_priv_data *priv_data) 4272 { 4273 return hdd_parse_reassoc(link_info->adapter, command, 4274 priv_data->total_len); 4275 } 4276 4277 static int drv_cmd_set_wes_mode(struct wlan_hdd_link_info *link_info, 4278 struct hdd_context *hdd_ctx, 4279 uint8_t *command, 4280 uint8_t command_len, 4281 struct hdd_priv_data *priv_data) 4282 { 4283 int ret = 0; 4284 uint8_t *value = command; 4285 uint8_t wes_mode = cfg_default(CFG_LFR_ENABLE_WES_MODE); 4286 4287 /* Move pointer to ahead of SETWESMODE<delimiter> */ 4288 value = value + command_len + 1; 4289 4290 /* Convert the value from ascii to integer */ 4291 ret = kstrtou8(value, 10, &wes_mode); 4292 if (ret < 0) { 4293 /* 4294 * If the input value is greater than max value of datatype, 4295 * then also kstrtou8 fails 4296 */ 4297 hdd_err("kstrtou8 failed range [%d - %d]", 4298 cfg_min(CFG_LFR_ENABLE_WES_MODE), 4299 cfg_max(CFG_LFR_ENABLE_WES_MODE)); 4300 ret = -EINVAL; 4301 goto exit; 4302 } 4303 4304 hdd_debug("Received Command to Set WES Mode rssi diff = %d", wes_mode); 4305 4306 sme_update_wes_mode(hdd_ctx->mac_handle, wes_mode, link_info->vdev_id); 4307 4308 exit: 4309 return ret; 4310 } 4311 4312 static int drv_cmd_get_wes_mode(struct wlan_hdd_link_info *link_info, 4313 struct hdd_context *hdd_ctx, 4314 uint8_t *command, 4315 uint8_t command_len, 4316 struct hdd_priv_data *priv_data) 4317 { 4318 int ret = 0; 4319 bool wes_mode = ucfg_cm_get_wes_mode(hdd_ctx->psoc); 4320 char extra[32]; 4321 uint8_t len = 0; 4322 4323 len = scnprintf(extra, sizeof(extra), "%s %d", command, wes_mode); 4324 len = QDF_MIN(priv_data->total_len, len + 1); 4325 if (copy_to_user(priv_data->buf, &extra, len)) { 4326 hdd_err("failed to copy data to user buffer"); 4327 ret = -EFAULT; 4328 } 4329 4330 return ret; 4331 } 4332 4333 static int 4334 drv_cmd_set_opportunistic_rssi_diff(struct wlan_hdd_link_info *link_info, 4335 struct hdd_context *hdd_ctx, 4336 uint8_t *command, uint8_t command_len, 4337 struct hdd_priv_data *priv_data) 4338 { 4339 int ret = 0; 4340 uint8_t *value = command; 4341 uint8_t diff = 4342 cfg_default(CFG_LFR_OPPORTUNISTIC_SCAN_THRESHOLD_DIFF); 4343 4344 /* Move pointer to ahead of SETOPPORTUNISTICRSSIDIFF<delimiter> */ 4345 value = value + command_len + 1; 4346 4347 /* Convert the value from ascii to integer */ 4348 ret = kstrtou8(value, 10, &diff); 4349 if (ret < 0) { 4350 /* 4351 * If the input value is greater than max value of datatype, 4352 * then also kstrtou8 fails 4353 */ 4354 hdd_err("kstrtou8 failed"); 4355 ret = -EINVAL; 4356 goto exit; 4357 } 4358 4359 hdd_debug("Received Command to Set Opportunistic Threshold diff = %d", 4360 diff); 4361 4362 sme_set_roam_opportunistic_scan_threshold_diff(hdd_ctx->mac_handle, 4363 link_info->vdev_id, 4364 diff); 4365 4366 exit: 4367 return ret; 4368 } 4369 4370 static int 4371 drv_cmd_get_opportunistic_rssi_diff(struct wlan_hdd_link_info *link_info, 4372 struct hdd_context *hdd_ctx, 4373 uint8_t *command, uint8_t command_len, 4374 struct hdd_priv_data *priv_data) 4375 { 4376 int ret = 0; 4377 int8_t val = 0; 4378 char extra[32]; 4379 uint8_t len = 0; 4380 4381 ucfg_cm_get_roam_opportunistic_scan_threshold_diff(hdd_ctx->psoc, &val); 4382 len = scnprintf(extra, sizeof(extra), "%s %d", command, val); 4383 len = QDF_MIN(priv_data->total_len, len + 1); 4384 if (copy_to_user(priv_data->buf, &extra, len)) { 4385 hdd_err("failed to copy data to user buffer"); 4386 ret = -EFAULT; 4387 } 4388 4389 return ret; 4390 } 4391 4392 static int 4393 drv_cmd_set_roam_rescan_rssi_diff(struct wlan_hdd_link_info *link_info, 4394 struct hdd_context *hdd_ctx, 4395 uint8_t *command, uint8_t command_len, 4396 struct hdd_priv_data *priv_data) 4397 { 4398 int ret = 0; 4399 uint8_t *value = command; 4400 uint8_t rescan_rssi_diff = cfg_default(CFG_LFR_ROAM_RESCAN_RSSI_DIFF); 4401 4402 /* Move pointer to ahead of SETROAMRESCANRSSIDIFF<delimiter> */ 4403 value = value + command_len + 1; 4404 4405 /* Convert the value from ascii to integer */ 4406 ret = kstrtou8(value, 10, &rescan_rssi_diff); 4407 if (ret < 0) { 4408 /* 4409 * If the input value is greater than max value of datatype, 4410 * then also kstrtou8 fails 4411 */ 4412 hdd_err("kstrtou8 failed"); 4413 ret = -EINVAL; 4414 goto exit; 4415 } 4416 4417 hdd_debug("Received Command to Set Roam Rescan RSSI Diff = %d", 4418 rescan_rssi_diff); 4419 4420 sme_set_roam_rescan_rssi_diff(hdd_ctx->mac_handle, 4421 link_info->vdev_id, rescan_rssi_diff); 4422 4423 exit: 4424 return ret; 4425 } 4426 4427 static int 4428 drv_cmd_get_roam_rescan_rssi_diff(struct wlan_hdd_link_info *link_info, 4429 struct hdd_context *hdd_ctx, 4430 uint8_t *command, uint8_t command_len, 4431 struct hdd_priv_data *priv_data) 4432 { 4433 int ret = 0; 4434 uint8_t val = 0; 4435 char extra[32]; 4436 uint8_t len = 0; 4437 4438 ucfg_cm_get_roam_rescan_rssi_diff(hdd_ctx->psoc, &val); 4439 len = scnprintf(extra, sizeof(extra), "%s %d", command, val); 4440 len = QDF_MIN(priv_data->total_len, len + 1); 4441 if (copy_to_user(priv_data->buf, &extra, len)) { 4442 hdd_err("failed to copy data to user buffer"); 4443 ret = -EFAULT; 4444 } 4445 4446 return ret; 4447 } 4448 4449 static int drv_cmd_set_fast_roam(struct wlan_hdd_link_info *link_info, 4450 struct hdd_context *hdd_ctx, 4451 uint8_t *command, 4452 uint8_t command_len, 4453 struct hdd_priv_data *priv_data) 4454 { 4455 int ret = 0; 4456 uint8_t *value = command; 4457 uint8_t lfr_mode = cfg_default(CFG_LFR_FEATURE_ENABLED); 4458 4459 /* Move pointer to ahead of SETFASTROAM<delimiter> */ 4460 value = value + command_len + 1; 4461 4462 /* Convert the value from ascii to integer */ 4463 ret = kstrtou8(value, 10, &lfr_mode); 4464 if (ret < 0) { 4465 /* 4466 * If the input value is greater than max value of datatype, 4467 * then also kstrtou8 fails 4468 */ 4469 hdd_err("kstrtou8 failed range [%d - %d]", 4470 cfg_min(CFG_LFR_FEATURE_ENABLED), 4471 cfg_max(CFG_LFR_FEATURE_ENABLED)); 4472 ret = -EINVAL; 4473 goto exit; 4474 } 4475 4476 hdd_debug("Received Command to change lfr mode = %d", 4477 lfr_mode); 4478 4479 if (sme_roaming_in_progress(hdd_ctx->mac_handle, 4480 link_info->vdev_id)) { 4481 hdd_err_rl("Roaming in progress for vdev %d", 4482 link_info->vdev_id); 4483 return -EAGAIN; 4484 } 4485 4486 ucfg_mlme_set_lfr_enabled(hdd_ctx->psoc, (bool)lfr_mode); 4487 sme_update_is_fast_roam_ini_feature_enabled(hdd_ctx->mac_handle, 4488 link_info->vdev_id, 4489 lfr_mode); 4490 4491 exit: 4492 return ret; 4493 } 4494 4495 static int drv_cmd_set_fast_transition(struct wlan_hdd_link_info *link_info, 4496 struct hdd_context *hdd_ctx, 4497 uint8_t *command, 4498 uint8_t command_len, 4499 struct hdd_priv_data *priv_data) 4500 { 4501 int ret = 0; 4502 uint8_t *value = command; 4503 uint8_t ft = cfg_default(CFG_LFR_FAST_TRANSITION_ENABLED); 4504 4505 /* Move pointer to ahead of SETFASTROAM<delimiter> */ 4506 value = value + command_len + 1; 4507 4508 /* Convert the value from ascii to integer */ 4509 ret = kstrtou8(value, 10, &ft); 4510 if (ret < 0) { 4511 /* 4512 * If the input value is greater than max value of datatype, 4513 * then also kstrtou8 fails 4514 */ 4515 hdd_err("kstrtou8 failed range [%d - %d]", 4516 cfg_min(CFG_LFR_FAST_TRANSITION_ENABLED), 4517 cfg_max(CFG_LFR_FAST_TRANSITION_ENABLED)); 4518 ret = -EINVAL; 4519 goto exit; 4520 } 4521 4522 hdd_debug("Received Command to change ft mode = %d", ft); 4523 4524 ucfg_mlme_set_fast_transition_enabled(hdd_ctx->psoc, (bool)ft); 4525 4526 exit: 4527 return ret; 4528 } 4529 4530 /** 4531 * drv_cmd_fast_reassoc() - Handler for FASTREASSOC driver command 4532 * @link_info: Carries link specific info, which contains adapter 4533 * @hdd_ctx: pointer to hdd context 4534 * @command: Buffer that carries actual command data, which can be parsed by 4535 * hdd_parse_reassoc_command_v1_data() 4536 * @command_len: Command length 4537 * @priv_data: to carry any priv data, FASTREASSOC doesn't have any priv 4538 * data for now. 4539 * 4540 * This function parses the reasoc command data passed in the format 4541 * FASTREASSOC<space><bssid><space><channel/frequency> 4542 * 4543 * If MAC from user space is broadcast MAC as: 4544 * "wpa_cli DRIVER FASTREASSOC ff:ff:ff:ff:ff:ff 0", 4545 * user space invoked roaming candidate selection will base on firmware score 4546 * algorithm, current connection will be kept if current AP has highest 4547 * score. It is requirement from customer which can avoid ping-pong roaming. 4548 * 4549 * If firmware fails to roam to new AP due to any reason, host to disconnect 4550 * from current AP as it's unable to roam. 4551 * 4552 * Return: 0 for success non-zero for failure 4553 */ 4554 static int drv_cmd_fast_reassoc(struct wlan_hdd_link_info *link_info, 4555 struct hdd_context *hdd_ctx, 4556 uint8_t *command, 4557 uint8_t command_len, 4558 struct hdd_priv_data *priv_data) 4559 { 4560 int ret = 0; 4561 uint8_t *value = command; 4562 qdf_freq_t freq = 0; 4563 tSirMacAddr bssid; 4564 struct qdf_mac_addr target_bssid; 4565 struct hdd_adapter *adapter = link_info->adapter; 4566 4567 if (QDF_STA_MODE != adapter->device_mode) { 4568 hdd_warn("Unsupported in mode %s(%d)", 4569 qdf_opmode_str(adapter->device_mode), 4570 adapter->device_mode); 4571 return -EINVAL; 4572 } 4573 4574 hdd_ctx = WLAN_HDD_GET_CTX(adapter); 4575 4576 /* if not associated, no need to proceed with reassoc */ 4577 if (!hdd_cm_is_vdev_associated(link_info)) { 4578 hdd_warn("Not associated!"); 4579 ret = -EINVAL; 4580 goto exit; 4581 } 4582 4583 ret = hdd_parse_reassoc_command_v1_data(value, bssid, 4584 &freq, hdd_ctx->pdev); 4585 if (ret) { 4586 hdd_err("Failed to parse reassoc command data"); 4587 goto exit; 4588 } 4589 4590 qdf_mem_copy(target_bssid.bytes, bssid, sizeof(tSirMacAddr)); 4591 ucfg_wlan_cm_roam_invoke(hdd_ctx->pdev, link_info->vdev_id, 4592 &target_bssid, freq, CM_ROAMING_HOST); 4593 4594 exit: 4595 return ret; 4596 } 4597 4598 static int drv_cmd_set_okc_mode(struct wlan_hdd_link_info *link_info, 4599 struct hdd_context *hdd_ctx, 4600 uint8_t *command, 4601 uint8_t command_len, 4602 struct hdd_priv_data *priv_data) 4603 { 4604 int ret = 0; 4605 uint8_t *value = command; 4606 uint32_t okc_mode; 4607 struct pmkid_mode_bits pmkid_modes; 4608 uint32_t cur_pmkid_modes; 4609 QDF_STATUS status; 4610 4611 hdd_get_pmkid_modes(hdd_ctx, &pmkid_modes); 4612 4613 /* 4614 * Check if the features PMKID/ESE/11R are supported simultaneously, 4615 * then this operation is not permitted (return FAILURE) 4616 */ 4617 if (ucfg_cm_get_is_ese_feature_enabled(hdd_ctx->psoc) && 4618 pmkid_modes.fw_okc && 4619 ucfg_cm_get_is_ft_feature_enabled(hdd_ctx->psoc)) { 4620 hdd_warn("PMKID/ESE/11R are supported simultaneously hence this operation is not permitted!"); 4621 ret = -EPERM; 4622 goto exit; 4623 } 4624 4625 /* Move pointer to ahead of SETOKCMODE<delimiter> */ 4626 value = value + command_len + 1; 4627 4628 /* get the current configured value */ 4629 status = ucfg_mlme_get_pmkid_modes(hdd_ctx->psoc, &cur_pmkid_modes); 4630 if (status != QDF_STATUS_SUCCESS) 4631 hdd_err("get pmkid modes failed"); 4632 4633 okc_mode = cur_pmkid_modes & CFG_PMKID_MODES_OKC; 4634 4635 /* Convert the value from ascii to integer */ 4636 ret = kstrtou32(value, 10, &okc_mode); 4637 if (ret < 0) { 4638 /* 4639 * If the input value is greater than max value of datatype, 4640 * then also kstrtou8 fails 4641 */ 4642 hdd_err("value out of range [0 - 1]"); 4643 ret = -EINVAL; 4644 goto exit; 4645 } 4646 4647 if ((okc_mode < 0) || 4648 (okc_mode > 1)) { 4649 hdd_err("Okc mode value %d is out of range (Min: 0 Max: 1)", 4650 okc_mode); 4651 ret = -EINVAL; 4652 goto exit; 4653 } 4654 hdd_debug("Received Command to change okc mode = %d", okc_mode); 4655 4656 if (okc_mode) 4657 cur_pmkid_modes |= CFG_PMKID_MODES_OKC; 4658 else 4659 cur_pmkid_modes &= ~CFG_PMKID_MODES_OKC; 4660 status = ucfg_mlme_set_pmkid_modes(hdd_ctx->psoc, 4661 cur_pmkid_modes); 4662 if (status != QDF_STATUS_SUCCESS) { 4663 ret = -EPERM; 4664 hdd_err("set pmkid modes failed"); 4665 } 4666 exit: 4667 return ret; 4668 } 4669 4670 static int drv_cmd_bt_coex_mode(struct wlan_hdd_link_info *link_info, 4671 struct hdd_context *hdd_ctx, 4672 uint8_t *command, 4673 uint8_t command_len, 4674 struct hdd_priv_data *priv_data) 4675 { 4676 int ret = 0; 4677 char *coex_mode; 4678 4679 coex_mode = command + 11; 4680 if ('1' == *coex_mode) { 4681 hdd_debug("BTCOEXMODE %d", *coex_mode); 4682 hdd_ctx->bt_coex_mode_set = true; 4683 ret = wlan_hdd_scan_abort(link_info); 4684 if (ret < 0) { 4685 hdd_err("Failed to abort existing scan status: %d", 4686 ret); 4687 } 4688 } else if ('2' == *coex_mode) { 4689 hdd_debug("BTCOEXMODE %d", *coex_mode); 4690 hdd_ctx->bt_coex_mode_set = false; 4691 } 4692 4693 return ret; 4694 } 4695 4696 static int drv_cmd_scan_active(struct wlan_hdd_link_info *link_info, 4697 struct hdd_context *hdd_ctx, 4698 uint8_t *command, 4699 uint8_t command_len, 4700 struct hdd_priv_data *priv_data) 4701 { 4702 hdd_ctx->ioctl_scan_mode = eSIR_ACTIVE_SCAN; 4703 return 0; 4704 } 4705 4706 static int drv_cmd_scan_passive(struct wlan_hdd_link_info *link_info, 4707 struct hdd_context *hdd_ctx, 4708 uint8_t *command, 4709 uint8_t command_len, 4710 struct hdd_priv_data *priv_data) 4711 { 4712 hdd_ctx->ioctl_scan_mode = eSIR_PASSIVE_SCAN; 4713 return 0; 4714 } 4715 4716 static int drv_cmd_get_dwell_time(struct wlan_hdd_link_info *link_info, 4717 struct hdd_context *hdd_ctx, 4718 uint8_t *command, 4719 uint8_t command_len, 4720 struct hdd_priv_data *priv_data) 4721 { 4722 int ret = 0; 4723 char extra[32]; 4724 uint8_t len = 0; 4725 4726 memset(extra, 0, sizeof(extra)); 4727 ret = hdd_get_dwell_time(hdd_ctx->psoc, command, extra, 4728 sizeof(extra), &len); 4729 len = QDF_MIN(priv_data->total_len, len + 1); 4730 if (ret != 0 || copy_to_user(priv_data->buf, &extra, len)) { 4731 hdd_err("failed to copy data to user buffer"); 4732 ret = -EFAULT; 4733 goto exit; 4734 } 4735 ret = len; 4736 exit: 4737 return ret; 4738 } 4739 4740 static int drv_cmd_set_dwell_time(struct wlan_hdd_link_info *link_info, 4741 struct hdd_context *hdd_ctx, 4742 uint8_t *command, 4743 uint8_t command_len, 4744 struct hdd_priv_data *priv_data) 4745 { 4746 return hdd_set_dwell_time(hdd_ctx->psoc, command); 4747 } 4748 4749 static int drv_cmd_conc_set_dwell_time(struct wlan_hdd_link_info *link_info, 4750 struct hdd_context *hdd_ctx, 4751 u8 *command, 4752 u8 command_len, 4753 struct hdd_priv_data *priv_data) 4754 { 4755 return hdd_conc_set_dwell_time(link_info->adapter, command); 4756 } 4757 4758 static int drv_cmd_miracast(struct wlan_hdd_link_info *link_info, 4759 struct hdd_context *hdd_ctx, 4760 uint8_t *command, 4761 uint8_t command_len, 4762 struct hdd_priv_data *priv_data) 4763 { 4764 QDF_STATUS ret_status; 4765 int ret = 0; 4766 uint8_t filter_type = 0; 4767 uint8_t *value; 4768 4769 if (wlan_hdd_validate_context(hdd_ctx)) 4770 return -EINVAL; 4771 4772 value = command + 9; 4773 4774 /* Convert the value from ascii to integer */ 4775 ret = kstrtou8(value, 10, &filter_type); 4776 if (ret < 0) { 4777 /* 4778 * If the input value is greater than max value of datatype, 4779 * then also kstrtou8 fails 4780 */ 4781 hdd_err("kstrtou8 failed range"); 4782 ret = -EINVAL; 4783 goto exit; 4784 } 4785 hdd_debug("filter_type %d", filter_type); 4786 4787 switch (filter_type) { 4788 case MIRACAST_DISABLED: 4789 case MIRACAST_SOURCE: 4790 case MIRACAST_SINK: 4791 break; 4792 case MIRACAST_CONN_OPT_ENABLED: 4793 case MIRACAST_CONN_OPT_DISABLED: 4794 { 4795 wma_cli_set_command( 4796 link_info->vdev_id, 4797 wmi_pdev_param_power_collapse_enable, 4798 (filter_type == MIRACAST_CONN_OPT_ENABLED ? 4799 0 : 1), PDEV_CMD); 4800 return 0; 4801 } 4802 default: 4803 hdd_err("accepted Values: 0-Disabled, 1-Source, 2-Sink, 128,129"); 4804 ret = -EINVAL; 4805 goto exit; 4806 } 4807 4808 /* Filtertype value should be either 0-Disabled, 1-Source, 2-sink */ 4809 hdd_ctx->miracast_value = filter_type; 4810 ucfg_mlme_set_vdev_traffic_low_latency(hdd_ctx->psoc, 4811 link_info->vdev_id, 4812 filter_type != 4813 MIRACAST_DISABLED); 4814 4815 ret_status = sme_set_miracast(hdd_ctx->mac_handle, filter_type); 4816 if (QDF_STATUS_SUCCESS != ret_status) { 4817 hdd_err("Failed to set miracast"); 4818 return -EBUSY; 4819 } 4820 ret_status = ucfg_scan_set_miracast(hdd_ctx->psoc, 4821 filter_type ? true : false); 4822 if (QDF_IS_STATUS_ERROR(ret_status)) { 4823 hdd_err("Failed to set miracastn scan"); 4824 return -EBUSY; 4825 } 4826 4827 if (policy_mgr_is_mcc_in_24G(hdd_ctx->psoc)) 4828 return wlan_hdd_set_mas(link_info->adapter, filter_type); 4829 4830 exit: 4831 return ret; 4832 } 4833 4834 static int drv_cmd_tput_debug_mode_enable(struct wlan_hdd_link_info *link_info, 4835 struct hdd_context *hdd_ctx, 4836 u8 *command, 4837 u8 command_len, 4838 struct hdd_priv_data *priv_data) 4839 { 4840 return hdd_enable_unit_test_commands(link_info->adapter, hdd_ctx); 4841 } 4842 4843 static int drv_cmd_tput_debug_mode_disable(struct wlan_hdd_link_info *link_info, 4844 struct hdd_context *hdd_ctx, 4845 u8 *command, 4846 u8 command_len, 4847 struct hdd_priv_data *priv_data) 4848 { 4849 return hdd_disable_unit_test_commands(link_info->adapter, hdd_ctx); 4850 } 4851 4852 #ifdef FEATURE_WLAN_ESE 4853 static int 4854 drv_cmd_set_ccx_roam_scan_channels(struct wlan_hdd_link_info *link_info, 4855 struct hdd_context *hdd_ctx, 4856 uint8_t *command, uint8_t command_len, 4857 struct hdd_priv_data *priv_data) 4858 { 4859 int ret = 0; 4860 uint8_t *value = command; 4861 uint32_t channel_freq_list[CFG_VALID_CHANNEL_LIST_LEN] = { 0 }; 4862 uint8_t num_channels = 0; 4863 QDF_STATUS status; 4864 mac_handle_t mac_handle; 4865 4866 if (!hdd_ctx) { 4867 hdd_err("invalid hdd ctx"); 4868 ret = -EINVAL; 4869 goto exit; 4870 } 4871 4872 ret = hdd_parse_channellist(hdd_ctx, value, channel_freq_list, 4873 &num_channels); 4874 if (ret) { 4875 hdd_err("Failed to parse channel list information"); 4876 goto exit; 4877 } 4878 if (num_channels > CFG_VALID_CHANNEL_LIST_LEN) { 4879 hdd_err("number of channels (%d) supported exceeded max (%d)", 4880 num_channels, CFG_VALID_CHANNEL_LIST_LEN); 4881 ret = -EINVAL; 4882 goto exit; 4883 } 4884 4885 mac_handle = hdd_ctx->mac_handle; 4886 if (!sme_validate_channel_list(mac_handle, channel_freq_list, 4887 num_channels)) { 4888 hdd_err("List contains invalid channel(s)"); 4889 ret = -EINVAL; 4890 goto exit; 4891 } 4892 4893 status = ucfg_cm_set_ese_roam_scan_channel_list(hdd_ctx->pdev, 4894 link_info->vdev_id, 4895 channel_freq_list, 4896 num_channels); 4897 if (QDF_STATUS_SUCCESS != status) { 4898 hdd_err("Failed to update channel list information"); 4899 ret = -EINVAL; 4900 goto exit; 4901 } 4902 4903 exit: 4904 return ret; 4905 } 4906 4907 static int drv_cmd_get_tsm_stats(struct wlan_hdd_link_info *link_info, 4908 struct hdd_context *hdd_ctx, 4909 uint8_t *command, 4910 uint8_t command_len, 4911 struct hdd_priv_data *priv_data) 4912 { 4913 int ret = 0; 4914 uint8_t *value = command; 4915 char extra[128] = { 0 }; 4916 int len = 0; 4917 uint8_t tid = 0; 4918 struct hdd_adapter *adapter = link_info->adapter; 4919 struct hdd_station_ctx *sta_ctx; 4920 tAniTrafStrmMetrics tsm_metrics = {0}; 4921 4922 if ((QDF_STA_MODE != adapter->device_mode) && 4923 (QDF_P2P_CLIENT_MODE != adapter->device_mode)) { 4924 hdd_warn("Unsupported in mode %s(%d)", 4925 qdf_opmode_str(adapter->device_mode), 4926 adapter->device_mode); 4927 return -EINVAL; 4928 } 4929 4930 sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info); 4931 4932 /* if not associated, return error */ 4933 if (!hdd_cm_is_vdev_associated(link_info)) { 4934 hdd_err("Not associated!"); 4935 ret = -EINVAL; 4936 goto exit; 4937 } 4938 4939 /* Move pointer to ahead of GETTSMSTATS<delimiter> */ 4940 value = value + command_len + 1; 4941 4942 /* Convert the value from ascii to integer */ 4943 ret = kstrtou8(value, 10, &tid); 4944 if (ret < 0) { 4945 /* 4946 * If the input value is greater than max value of datatype, 4947 * then also kstrtou8 fails 4948 */ 4949 hdd_err("kstrtou8 failed range [%d - %d]", 4950 TID_MIN_VALUE, 4951 TID_MAX_VALUE); 4952 ret = -EINVAL; 4953 goto exit; 4954 } 4955 if ((tid < TID_MIN_VALUE) || (tid > TID_MAX_VALUE)) { 4956 hdd_err("tid value %d is out of range (Min: %d Max: %d)", 4957 tid, TID_MIN_VALUE, TID_MAX_VALUE); 4958 ret = -EINVAL; 4959 goto exit; 4960 } 4961 hdd_debug("Received Command to get tsm stats tid = %d", 4962 tid); 4963 ret = hdd_get_tsm_stats(adapter, tid, &tsm_metrics); 4964 if (ret) { 4965 hdd_err("failed to get tsm stats"); 4966 goto exit; 4967 } 4968 hdd_debug( 4969 "UplinkPktQueueDly(%d) UplinkPktQueueDlyHist[0](%d) UplinkPktQueueDlyHist[1](%d) UplinkPktQueueDlyHist[2](%d) UplinkPktQueueDlyHist[3](%d) UplinkPktTxDly(%u) UplinkPktLoss(%d) UplinkPktCount(%d) RoamingCount(%d) RoamingDly(%d)", 4970 tsm_metrics.UplinkPktQueueDly, 4971 tsm_metrics.UplinkPktQueueDlyHist[0], 4972 tsm_metrics.UplinkPktQueueDlyHist[1], 4973 tsm_metrics.UplinkPktQueueDlyHist[2], 4974 tsm_metrics.UplinkPktQueueDlyHist[3], 4975 tsm_metrics.UplinkPktTxDly, 4976 tsm_metrics.UplinkPktLoss, 4977 tsm_metrics.UplinkPktCount, 4978 tsm_metrics.RoamingCount, 4979 tsm_metrics.RoamingDly); 4980 /* 4981 * Output TSM stats is of the format 4982 * GETTSMSTATS [PktQueueDly] 4983 * [PktQueueDlyHist[0]]:[PktQueueDlyHist[1]] ...[RoamingDly] 4984 * eg., GETTSMSTATS 10 1:0:0:161 20 1 17 8 39800 4985 */ 4986 len = scnprintf(extra, 4987 sizeof(extra), 4988 "%s %d %d:%d:%d:%d %u %d %d %d %d", 4989 command, 4990 tsm_metrics.UplinkPktQueueDly, 4991 tsm_metrics.UplinkPktQueueDlyHist[0], 4992 tsm_metrics.UplinkPktQueueDlyHist[1], 4993 tsm_metrics.UplinkPktQueueDlyHist[2], 4994 tsm_metrics.UplinkPktQueueDlyHist[3], 4995 tsm_metrics.UplinkPktTxDly, 4996 tsm_metrics.UplinkPktLoss, 4997 tsm_metrics.UplinkPktCount, 4998 tsm_metrics.RoamingCount, 4999 tsm_metrics.RoamingDly); 5000 len = QDF_MIN(priv_data->total_len, len + 1); 5001 if (copy_to_user(priv_data->buf, &extra, len)) { 5002 hdd_err("failed to copy data to user buffer"); 5003 ret = -EFAULT; 5004 goto exit; 5005 } 5006 5007 exit: 5008 return ret; 5009 } 5010 5011 static int drv_cmd_set_cckm_ie(struct wlan_hdd_link_info *link_info, 5012 struct hdd_context *hdd_ctx, 5013 uint8_t *command, 5014 uint8_t command_len, 5015 struct hdd_priv_data *priv_data) 5016 { 5017 int ret; 5018 uint8_t *value = command; 5019 uint8_t *cckm_ie = NULL; 5020 uint8_t cckm_ie_len = 0; 5021 5022 ret = hdd_parse_get_cckm_ie(value, &cckm_ie, &cckm_ie_len); 5023 if (ret) { 5024 hdd_err("Failed to parse cckm ie data"); 5025 goto exit; 5026 } 5027 5028 if (cckm_ie_len > DOT11F_IE_RSN_MAX_LEN) { 5029 hdd_err("CCKM Ie input length is more than max[%d]", 5030 DOT11F_IE_RSN_MAX_LEN); 5031 if (cckm_ie) { 5032 qdf_mem_free(cckm_ie); 5033 cckm_ie = NULL; 5034 } 5035 ret = -EINVAL; 5036 goto exit; 5037 } 5038 5039 ucfg_cm_set_cckm_ie(hdd_ctx->psoc, link_info->vdev_id, cckm_ie, 5040 cckm_ie_len); 5041 if (cckm_ie) { 5042 qdf_mem_free(cckm_ie); 5043 cckm_ie = NULL; 5044 } 5045 5046 exit: 5047 return ret; 5048 } 5049 5050 static int drv_cmd_ccx_beacon_req(struct wlan_hdd_link_info *link_info, 5051 struct hdd_context *hdd_ctx, 5052 uint8_t *command, 5053 uint8_t command_len, 5054 struct hdd_priv_data *priv_data) 5055 { 5056 int ret; 5057 uint8_t *value = command; 5058 tCsrEseBeaconReq req = {0}; 5059 QDF_STATUS status = QDF_STATUS_SUCCESS; 5060 struct hdd_adapter *adapter = link_info->adapter; 5061 5062 if (QDF_STA_MODE != adapter->device_mode) { 5063 hdd_warn("Unsupported in mode %s(%d)", 5064 qdf_opmode_str(adapter->device_mode), 5065 adapter->device_mode); 5066 return -EINVAL; 5067 } 5068 5069 ret = hdd_parse_ese_beacon_req(hdd_ctx->pdev, value, &req); 5070 if (ret) { 5071 hdd_err("Failed to parse ese beacon req"); 5072 goto exit; 5073 } 5074 5075 if (!hdd_cm_is_vdev_associated(link_info)) { 5076 hdd_debug("Not associated"); 5077 5078 if (!req.numBcnReqIe) 5079 return -EINVAL; 5080 5081 hdd_indicate_ese_bcn_report_no_results(adapter, 5082 req.bcnReq[0].measurementToken, 5083 0x02, /* BIT(1) set for measurement done */ 5084 0); /* no BSS */ 5085 goto exit; 5086 } 5087 5088 status = sme_set_ese_beacon_request(hdd_ctx->mac_handle, 5089 link_info->vdev_id, 5090 &req); 5091 5092 if (QDF_STATUS_E_RESOURCES == status) { 5093 hdd_err("sme_set_ese_beacon_request failed (%d), a request already in progress", 5094 status); 5095 ret = -EBUSY; 5096 goto exit; 5097 } else if (QDF_STATUS_SUCCESS != status) { 5098 hdd_err("sme_set_ese_beacon_request failed (%d)", 5099 status); 5100 ret = -EINVAL; 5101 goto exit; 5102 } 5103 5104 exit: 5105 return ret; 5106 } 5107 5108 /** 5109 * drv_cmd_ccx_plm_req() - Set ESE PLM request 5110 * @link_info: Link info pointer in HDD adapter 5111 * @hdd_ctx: Pointer to the HDD context 5112 * @command: Driver command string 5113 * @command_len: Driver command string length 5114 * @priv_data: Private data coming with the driver command. Unused here 5115 * 5116 * This function handles driver command that sets the ESE PLM request 5117 * 5118 * Return: 0 on success; negative errno otherwise 5119 */ 5120 static int drv_cmd_ccx_plm_req(struct wlan_hdd_link_info *link_info, 5121 struct hdd_context *hdd_ctx, 5122 uint8_t *command, 5123 uint8_t command_len, 5124 struct hdd_priv_data *priv_data) 5125 { 5126 QDF_STATUS status; 5127 struct plm_req_params *req; 5128 5129 req = qdf_mem_malloc(sizeof(*req)); 5130 if (!req) 5131 return -ENOMEM; 5132 5133 status = hdd_parse_plm_cmd(command, req); 5134 if (QDF_IS_STATUS_SUCCESS(status)) { 5135 req->vdev_id = link_info->vdev_id; 5136 status = sme_set_plm_request(hdd_ctx->mac_handle, req); 5137 } 5138 qdf_mem_free(req); 5139 5140 return qdf_status_to_os_return(status); 5141 } 5142 5143 /** 5144 * drv_cmd_set_ccx_mode() - Set ESE mode 5145 * @link_info: Link info pointer in HDD adapter 5146 * @hdd_ctx: Pointer to the HDD context 5147 * @command: Driver command string 5148 * @command_len: Driver command string length 5149 * @priv_data: Private data coming with the driver command. Unused here 5150 * 5151 * This function handles driver command that sets ESE mode 5152 * 5153 * Return: 0 on success; negative errno otherwise 5154 */ 5155 static int drv_cmd_set_ccx_mode(struct wlan_hdd_link_info *link_info, 5156 struct hdd_context *hdd_ctx, 5157 uint8_t *command, 5158 uint8_t command_len, 5159 struct hdd_priv_data *priv_data) 5160 { 5161 int ret = 0; 5162 uint8_t *value = command; 5163 uint8_t ese_mode = cfg_default(CFG_LFR_ESE_FEATURE_ENABLED); 5164 struct pmkid_mode_bits pmkid_modes; 5165 mac_handle_t mac_handle; 5166 5167 hdd_get_pmkid_modes(hdd_ctx, &pmkid_modes); 5168 mac_handle = hdd_ctx->mac_handle; 5169 /* 5170 * Check if the features OKC/ESE/11R are supported simultaneously, 5171 * then this operation is not permitted (return FAILURE) 5172 */ 5173 if (ucfg_cm_get_is_ese_feature_enabled(hdd_ctx->psoc) && 5174 pmkid_modes.fw_okc && 5175 ucfg_cm_get_is_ft_feature_enabled(hdd_ctx->psoc)) { 5176 hdd_warn("OKC/ESE/11R are supported simultaneously hence this operation is not permitted!"); 5177 ret = -EPERM; 5178 goto exit; 5179 } 5180 5181 /* Move pointer to ahead of SETCCXMODE<delimiter> */ 5182 value = value + command_len + 1; 5183 5184 /* Convert the value from ascii to integer */ 5185 ret = kstrtou8(value, 10, &ese_mode); 5186 if (ret < 0) { 5187 /* 5188 * If the input value is greater than max value of datatype, 5189 * then also kstrtou8 fails 5190 */ 5191 hdd_err("kstrtou8 failed range [%d - %d]", 5192 cfg_min(CFG_LFR_ESE_FEATURE_ENABLED), 5193 cfg_max(CFG_LFR_ESE_FEATURE_ENABLED)); 5194 ret = -EINVAL; 5195 goto exit; 5196 } 5197 5198 hdd_debug("Received Command to change ese mode = %d", ese_mode); 5199 5200 sme_update_is_ese_feature_enabled(mac_handle, 5201 link_info->vdev_id, 5202 ese_mode); 5203 5204 exit: 5205 return ret; 5206 } 5207 #endif /* FEATURE_WLAN_ESE */ 5208 5209 static int drv_cmd_set_mc_rate(struct wlan_hdd_link_info *link_info, 5210 struct hdd_context *hdd_ctx, 5211 uint8_t *command, uint8_t command_len, 5212 struct hdd_priv_data *priv_data) 5213 { 5214 int ret = 0; 5215 uint8_t *value = command; 5216 uint32_t target_rate = 0; 5217 5218 /* input value is in units of hundred kbps */ 5219 5220 /* Move pointer to ahead of SETMCRATE<delimiter> */ 5221 value = value + command_len + 1; 5222 5223 /* Convert the value from ascii to integer, decimal base */ 5224 ret = kstrtouint(value, 10, &target_rate); 5225 5226 ret = wlan_hdd_set_mc_rate(link_info, target_rate); 5227 return ret; 5228 } 5229 5230 static int drv_cmd_max_tx_power(struct wlan_hdd_link_info *link_info, 5231 struct hdd_context *hdd_ctx, 5232 uint8_t *command, 5233 uint8_t command_len, 5234 struct hdd_priv_data *priv_data) 5235 { 5236 int ret; 5237 int tx_power; 5238 QDF_STATUS status; 5239 uint8_t *value = command; 5240 struct qdf_mac_addr bssid = QDF_MAC_ADDR_BCAST_INIT; 5241 struct qdf_mac_addr selfmac = QDF_MAC_ADDR_BCAST_INIT; 5242 struct hdd_adapter *adapter = link_info->adapter, *next_adapter = NULL; 5243 wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_DRV_CMD_MAX_TX_POWER; 5244 5245 ret = hdd_parse_setmaxtxpower_command(value, &tx_power); 5246 if (ret) { 5247 hdd_err("Invalid MAXTXPOWER command"); 5248 return ret; 5249 } 5250 5251 hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, 5252 dbgid) { 5253 /* Assign correct self MAC address */ 5254 if (adapter->device_mode == QDF_SAP_MODE || 5255 adapter->device_mode == QDF_P2P_GO_MODE) { 5256 qdf_copy_macaddr(&bssid, &adapter->mac_addr); 5257 } else { 5258 struct hdd_station_ctx *sta_ctx = 5259 WLAN_HDD_GET_STATION_CTX_PTR(adapter->deflink); 5260 5261 if (hdd_cm_is_vdev_associated(adapter->deflink)) 5262 qdf_copy_macaddr(&bssid, 5263 &sta_ctx->conn_info.bssid); 5264 } 5265 5266 qdf_copy_macaddr(&selfmac, 5267 &adapter->mac_addr); 5268 5269 hdd_debug("Device mode %d max tx power %d selfMac: " 5270 QDF_MAC_ADDR_FMT " bssId: " QDF_MAC_ADDR_FMT, 5271 adapter->device_mode, tx_power, 5272 QDF_MAC_ADDR_REF(selfmac.bytes), 5273 QDF_MAC_ADDR_REF(bssid.bytes)); 5274 5275 status = sme_set_max_tx_power(hdd_ctx->mac_handle, 5276 bssid, selfmac, tx_power); 5277 if (QDF_STATUS_SUCCESS != status) { 5278 hdd_err("Set max tx power failed"); 5279 ret = -EINVAL; 5280 hdd_adapter_dev_put_debug(adapter, dbgid); 5281 if (next_adapter) 5282 hdd_adapter_dev_put_debug(next_adapter, 5283 dbgid); 5284 goto exit; 5285 } 5286 hdd_debug("Set max tx power success"); 5287 hdd_adapter_dev_put_debug(adapter, dbgid); 5288 } 5289 5290 exit: 5291 return ret; 5292 } 5293 5294 static int drv_cmd_set_dfs_scan_mode(struct wlan_hdd_link_info *link_info, 5295 struct hdd_context *hdd_ctx, 5296 uint8_t *command, uint8_t command_len, 5297 struct hdd_priv_data *priv_data) 5298 { 5299 int ret = 0; 5300 uint8_t *value = command; 5301 uint8_t dfs_scan_mode = cfg_default(CFG_LFR_ROAMING_DFS_CHANNEL); 5302 5303 /* Move pointer to ahead of SETDFSSCANMODE<delimiter> */ 5304 value = value + command_len + 1; 5305 5306 /* Convert the value from ascii to integer */ 5307 ret = kstrtou8(value, 10, &dfs_scan_mode); 5308 if (ret < 0) { 5309 /* 5310 * If the input value is greater than max value of datatype, 5311 * then also kstrtou8 fails 5312 */ 5313 hdd_err("kstrtou8 failed range [%d - %d]", 5314 cfg_min(CFG_LFR_ROAMING_DFS_CHANNEL), 5315 cfg_max(CFG_LFR_ROAMING_DFS_CHANNEL)); 5316 ret = -EINVAL; 5317 goto exit; 5318 } 5319 5320 if (!cfg_in_range(CFG_LFR_ROAMING_DFS_CHANNEL, dfs_scan_mode)) { 5321 hdd_err("dfs_scan_mode value %d is out of range (Min: %d Max: %d)", 5322 dfs_scan_mode, 5323 cfg_min(CFG_LFR_ROAMING_DFS_CHANNEL), 5324 cfg_max(CFG_LFR_ROAMING_DFS_CHANNEL)); 5325 ret = -EINVAL; 5326 goto exit; 5327 } 5328 5329 hdd_debug("Received Command to Set DFS Scan Mode = %d", 5330 dfs_scan_mode); 5331 5332 /* When DFS scanning is disabled, the DFS channels need to be 5333 * removed from the operation of device. 5334 */ 5335 ret = wlan_hdd_enable_dfs_chan_scan(hdd_ctx, 5336 dfs_scan_mode != ROAMING_DFS_CHANNEL_DISABLED); 5337 if (ret < 0) { 5338 /* Some conditions prevented it from disabling DFS channels */ 5339 hdd_err("disable/enable DFS channel request was denied"); 5340 goto exit; 5341 } 5342 5343 sme_update_dfs_scan_mode(hdd_ctx->mac_handle, link_info->vdev_id, 5344 dfs_scan_mode); 5345 5346 exit: 5347 return ret; 5348 } 5349 5350 static int drv_cmd_get_dfs_scan_mode(struct wlan_hdd_link_info *link_info, 5351 struct hdd_context *hdd_ctx, 5352 uint8_t *command, 5353 uint8_t command_len, 5354 struct hdd_priv_data *priv_data) 5355 { 5356 int ret = 0; 5357 uint8_t dfs_scan_mode = sme_get_dfs_scan_mode(hdd_ctx->mac_handle); 5358 char extra[32]; 5359 uint8_t len = 0; 5360 5361 len = scnprintf(extra, sizeof(extra), "%s %d", command, dfs_scan_mode); 5362 len = QDF_MIN(priv_data->total_len, len + 1); 5363 if (copy_to_user(priv_data->buf, &extra, len)) { 5364 hdd_err("failed to copy data to user buffer"); 5365 ret = -EFAULT; 5366 } 5367 5368 return ret; 5369 } 5370 5371 static int drv_cmd_get_link_status(struct wlan_hdd_link_info *link_info, 5372 struct hdd_context *hdd_ctx, 5373 uint8_t *command, 5374 uint8_t command_len, 5375 struct hdd_priv_data *priv_data) 5376 { 5377 int ret = 0; 5378 int value = wlan_hdd_get_link_status(link_info->adapter); 5379 char extra[32]; 5380 uint8_t len; 5381 5382 len = scnprintf(extra, sizeof(extra), "%s %d", command, value); 5383 len = QDF_MIN(priv_data->total_len, len + 1); 5384 if (copy_to_user(priv_data->buf, &extra, len)) { 5385 hdd_err("failed to copy data to user buffer"); 5386 ret = -EFAULT; 5387 } 5388 5389 return ret; 5390 } 5391 5392 #ifdef WLAN_FEATURE_EXTWOW_SUPPORT 5393 static int drv_cmd_enable_ext_wow(struct wlan_hdd_link_info *link_info, 5394 struct hdd_context *hdd_ctx, 5395 uint8_t *command, 5396 uint8_t command_len, 5397 struct hdd_priv_data *priv_data) 5398 { 5399 uint8_t *value = command; 5400 int set_value; 5401 5402 /* Move pointer to ahead of ENABLEEXTWOW */ 5403 value = value + command_len; 5404 5405 if (!(sscanf(value, "%d", &set_value))) { 5406 hdd_info("No input identified"); 5407 return -EINVAL; 5408 } 5409 5410 return hdd_enable_ext_wow_parser(link_info->adapter, 5411 link_info->vdev_id, set_value); 5412 } 5413 5414 static int drv_cmd_set_app1_params(struct wlan_hdd_link_info *link_info, 5415 struct hdd_context *hdd_ctx, 5416 uint8_t *command, 5417 uint8_t command_len, 5418 struct hdd_priv_data *priv_data) 5419 { 5420 int ret; 5421 uint8_t *value = command; 5422 5423 /* Move pointer to ahead of SETAPP1PARAMS */ 5424 value = value + command_len; 5425 5426 ret = hdd_set_app_type1_parser(link_info->adapter, 5427 value, strlen(value)); 5428 if (ret >= 0) 5429 hdd_ctx->is_extwow_app_type1_param_set = true; 5430 5431 return ret; 5432 } 5433 5434 static int drv_cmd_set_app2_params(struct wlan_hdd_link_info *link_info, 5435 struct hdd_context *hdd_ctx, 5436 uint8_t *command, 5437 uint8_t command_len, 5438 struct hdd_priv_data *priv_data) 5439 { 5440 int ret; 5441 uint8_t *value = command; 5442 5443 /* Move pointer to ahead of SETAPP2PARAMS */ 5444 value = value + command_len; 5445 5446 ret = hdd_set_app_type2_parser(link_info->adapter, 5447 value, strlen(value)); 5448 if (ret >= 0) 5449 hdd_ctx->is_extwow_app_type2_param_set = true; 5450 5451 return ret; 5452 } 5453 #endif /* WLAN_FEATURE_EXTWOW_SUPPORT */ 5454 5455 #ifdef FEATURE_WLAN_TDLS 5456 /** 5457 * drv_cmd_tdls_secondary_channel_offset() - secondary tdls off channel offset 5458 * @link_info: Link info pointer in HDD adapter 5459 * @hdd_ctx: Pointer to the HDD context 5460 * @command: Driver command string 5461 * @command_len: Driver command string length 5462 * @priv_data: Private data coming with the driver command. Unused here 5463 * 5464 * This function handles driver command that sets the secondary tdls off channel 5465 * offset 5466 * 5467 * Return: 0 on success; negative errno otherwise 5468 */ 5469 static int 5470 drv_cmd_tdls_secondary_channel_offset(struct wlan_hdd_link_info *link_info, 5471 struct hdd_context *hdd_ctx, 5472 uint8_t *command, uint8_t command_len, 5473 struct hdd_priv_data *priv_data) 5474 { 5475 int ret; 5476 uint8_t *value = command; 5477 int set_value; 5478 5479 /* Move pointer to point the string */ 5480 value += command_len; 5481 5482 ret = sscanf(value, "%d", &set_value); 5483 if (ret != 1) 5484 return -EINVAL; 5485 5486 hdd_debug("Tdls offchannel offset:%d", set_value); 5487 5488 ret = hdd_set_tdls_secoffchanneloffset(hdd_ctx, link_info->adapter, 5489 set_value); 5490 5491 return ret; 5492 } 5493 5494 /** 5495 * drv_cmd_tdls_off_channel_mode() - set tdls off channel mode 5496 * @link_info: Link info pointer in HDD adapter 5497 * @hdd_ctx: Pointer to the HDD context 5498 * @command: Driver command string 5499 * @command_len: Driver command string length 5500 * @priv_data: Private data coming with the driver command. Unused here 5501 * 5502 * This function handles driver command that sets tdls off channel mode 5503 * 5504 * Return: 0 on success; negative errno otherwise 5505 */ 5506 static int drv_cmd_tdls_off_channel_mode(struct wlan_hdd_link_info *link_info, 5507 struct hdd_context *hdd_ctx, 5508 uint8_t *command, 5509 uint8_t command_len, 5510 struct hdd_priv_data *priv_data) 5511 { 5512 int ret; 5513 uint8_t *value = command; 5514 int set_value; 5515 5516 /* Move pointer to point the string */ 5517 value += command_len; 5518 5519 ret = sscanf(value, "%d", &set_value); 5520 if (ret != 1) 5521 return -EINVAL; 5522 5523 hdd_debug("Tdls offchannel mode:%d", set_value); 5524 5525 ret = hdd_set_tdls_offchannelmode(hdd_ctx, 5526 link_info->adapter, set_value); 5527 5528 return ret; 5529 } 5530 5531 /** 5532 * drv_cmd_tdls_off_channel() - set tdls off channel number 5533 * @link_info: Link info pointer in HDD adapter 5534 * @hdd_ctx: Pointer to the HDD context 5535 * @command: Driver command string 5536 * @command_len: Driver command string length 5537 * @priv_data: Private data coming with the driver command. Unused here 5538 * 5539 * This function handles driver command that sets tdls off channel number 5540 * 5541 * Return: 0 on success; negative errno otherwise 5542 */ 5543 static int drv_cmd_tdls_off_channel(struct wlan_hdd_link_info *link_info, 5544 struct hdd_context *hdd_ctx, 5545 uint8_t *command, 5546 uint8_t command_len, 5547 struct hdd_priv_data *priv_data) 5548 { 5549 int ret; 5550 uint8_t *value = command; 5551 int channel; 5552 enum channel_state reg_state; 5553 qdf_freq_t ch_freq; 5554 5555 /* Move pointer to point the string */ 5556 value += command_len; 5557 5558 ret = sscanf(value, "%d", &channel); 5559 if (ret != 1) 5560 return -EINVAL; 5561 5562 ch_freq = wlan_reg_legacy_chan_to_freq(hdd_ctx->pdev, channel); 5563 reg_state = 5564 wlan_reg_get_channel_state_for_pwrmode(hdd_ctx->pdev, ch_freq, 5565 REG_CURRENT_PWR_MODE); 5566 5567 if (reg_state == CHANNEL_STATE_DFS || 5568 reg_state == CHANNEL_STATE_DISABLE || 5569 reg_state == CHANNEL_STATE_INVALID) { 5570 hdd_err("reg state of the channel %d is %d and not supported", 5571 channel, reg_state); 5572 return -EINVAL; 5573 } 5574 5575 hdd_debug("Tdls offchannel num: %d", channel); 5576 5577 ret = hdd_set_tdls_offchannel(hdd_ctx, link_info->adapter, channel); 5578 5579 return ret; 5580 } 5581 5582 /** 5583 * drv_cmd_tdls_scan() - set tdls scan type 5584 * @link_info: Link info pointer in HDD adapter 5585 * @hdd_ctx: Pointer to the HDD context 5586 * @command: Driver command string 5587 * @command_len: Driver command string length 5588 * @priv_data: Private data coming with the driver command. Unused here 5589 * 5590 * This function handles driver command that sets tdls scan type 5591 * 5592 * Return: 0 on success; negative errno otherwise 5593 */ 5594 static int drv_cmd_tdls_scan(struct wlan_hdd_link_info *link_info, 5595 struct hdd_context *hdd_ctx, 5596 uint8_t *command, uint8_t command_len, 5597 struct hdd_priv_data *priv_data) 5598 { 5599 int ret; 5600 uint8_t *value = command; 5601 int set_value; 5602 5603 /* Move pointer to point the string */ 5604 value += command_len; 5605 5606 ret = sscanf(value, "%d", &set_value); 5607 if (ret != 1) 5608 return -EINVAL; 5609 5610 hdd_debug("Tdls scan type val: %d", set_value); 5611 5612 ret = hdd_set_tdls_scan_type(hdd_ctx, set_value); 5613 5614 return ret; 5615 } 5616 #endif 5617 5618 static int drv_cmd_get_rssi(struct wlan_hdd_link_info *link_info, 5619 struct hdd_context *hdd_ctx, 5620 uint8_t *command, uint8_t command_len, 5621 struct hdd_priv_data *priv_data) 5622 { 5623 int ret = 0; 5624 int8_t rssi = 0; 5625 char extra[32]; 5626 5627 uint8_t len = 0; 5628 5629 wlan_hdd_get_rssi(link_info, &rssi); 5630 5631 len = scnprintf(extra, sizeof(extra), "%s %d", command, rssi); 5632 len = QDF_MIN(priv_data->total_len, len + 1); 5633 5634 if (copy_to_user(priv_data->buf, &extra, len)) { 5635 hdd_err("Failed to copy data to user buffer"); 5636 ret = -EFAULT; 5637 } 5638 5639 return ret; 5640 } 5641 5642 /** 5643 * drv_cmd_get_sap_go_linkspeed() - Driver command to get SAP/P2P Go peer link 5644 * speed 5645 * @link_info: Link info pointer in HDD adapter 5646 * @hdd_ctx: HDD context pointer 5647 * @command: Driver command string 5648 * @command_len: Driver command string length 5649 * @priv_data: Pointer to HDD private data 5650 * 5651 * Return: 0 if linkspeed data is available, negative errno otherwise 5652 */ 5653 static int drv_cmd_get_sap_go_linkspeed(struct wlan_hdd_link_info *link_info, 5654 struct hdd_context *hdd_ctx, 5655 uint8_t *command, 5656 uint8_t command_len, 5657 struct hdd_priv_data *priv_data) 5658 { 5659 int ret; 5660 uint32_t link_speed = 0; 5661 char extra[64]; 5662 uint8_t len = 0; 5663 struct hdd_adapter *adapter = link_info->adapter; 5664 5665 if (adapter->device_mode == QDF_P2P_GO_MODE || 5666 adapter->device_mode == QDF_SAP_MODE) { 5667 ret = wlan_hdd_get_sap_go_peer_linkspeed(link_info, 5668 &link_speed, 5669 command, 5670 command_len); 5671 } else { 5672 hdd_err("Link Speed is not allowed in Device mode %s(%d)", 5673 qdf_opmode_str(adapter->device_mode), 5674 adapter->device_mode); 5675 ret = -ENOTSUPP; 5676 } 5677 5678 if (0 != ret) 5679 return ret; 5680 5681 len = scnprintf(extra, sizeof(extra), "%s %d\n", 5682 "SOFT-AP LINKSPEED", link_speed); 5683 len = QDF_MIN(priv_data->total_len, len + 1); 5684 if (copy_to_user(priv_data->buf, &extra, len)) { 5685 hdd_err("Failed to copy data to user buffer"); 5686 ret = -EFAULT; 5687 } 5688 5689 return ret; 5690 } 5691 5692 static int drv_cmd_get_linkspeed(struct wlan_hdd_link_info *link_info, 5693 struct hdd_context *hdd_ctx, 5694 uint8_t *command, 5695 uint8_t command_len, 5696 struct hdd_priv_data *priv_data) 5697 { 5698 int ret; 5699 uint32_t link_speed = 0; 5700 char extra[32]; 5701 uint8_t len = 0; 5702 5703 ret = wlan_hdd_get_link_speed(link_info, &link_speed); 5704 if (0 != ret) 5705 return ret; 5706 5707 len = scnprintf(extra, sizeof(extra), "%s %d", command, link_speed); 5708 len = QDF_MIN(priv_data->total_len, len + 1); 5709 if (copy_to_user(priv_data->buf, &extra, len)) { 5710 hdd_err("Failed to copy data to user buffer"); 5711 ret = -EFAULT; 5712 } 5713 5714 return ret; 5715 } 5716 5717 #ifdef WLAN_FEATURE_PACKET_FILTERING 5718 /** 5719 * hdd_set_rx_filter() - set RX filter 5720 * @adapter: Pointer to adapter 5721 * @action: Filter action 5722 * @pattern: Address pattern 5723 * 5724 * Address pattern is most significant byte of address for example 5725 * 0x01 for IPV4 multicast address 5726 * 0x33 for IPV6 multicast address 5727 * 0xFF for broadcast address 5728 * 5729 * Return: 0 for success, non-zero for failure 5730 */ 5731 static int hdd_set_rx_filter(struct hdd_adapter *adapter, bool action, 5732 uint8_t pattern) 5733 { 5734 int ret; 5735 uint8_t i, j; 5736 tSirRcvFltMcAddrList *filter; 5737 struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); 5738 mac_handle_t mac_handle; 5739 5740 ret = wlan_hdd_validate_context(hdd_ctx); 5741 if (0 != ret) 5742 return ret; 5743 5744 mac_handle = hdd_ctx->mac_handle; 5745 if (!mac_handle) { 5746 hdd_err("MAC Handle is NULL"); 5747 return -EINVAL; 5748 } 5749 5750 if (!ucfg_pmo_is_mc_addr_list_enabled(hdd_ctx->psoc)) { 5751 hdd_warn("mc addr ini is disabled"); 5752 return -EINVAL; 5753 } 5754 5755 /* 5756 * If action is false it means start dropping packets 5757 * Set addr_filter_pattern which will be used when sending 5758 * MC/BC address list to target 5759 */ 5760 if (!action) 5761 adapter->addr_filter_pattern = pattern; 5762 else 5763 adapter->addr_filter_pattern = 0; 5764 5765 if (((adapter->device_mode == QDF_STA_MODE) || 5766 (adapter->device_mode == QDF_P2P_CLIENT_MODE)) && 5767 adapter->mc_addr_list.mc_cnt && 5768 hdd_cm_is_vdev_associated(adapter->deflink)) { 5769 5770 5771 filter = qdf_mem_malloc(sizeof(*filter)); 5772 if (!filter) 5773 return -ENOMEM; 5774 5775 filter->action = action; 5776 for (i = 0, j = 0; i < adapter->mc_addr_list.mc_cnt; i++) { 5777 if (!memcmp(adapter->mc_addr_list.addr[i], 5778 &pattern, 1)) { 5779 memcpy(filter->multicastAddr[j].bytes, 5780 adapter->mc_addr_list.addr[i], 5781 sizeof(adapter->mc_addr_list.addr[i])); 5782 5783 hdd_debug("%s RX filter : addr =" 5784 QDF_MAC_ADDR_FMT, 5785 action ? "setting" : "clearing", 5786 QDF_MAC_ADDR_REF(filter->multicastAddr[j].bytes)); 5787 j++; 5788 } 5789 if (j == SIR_MAX_NUM_MULTICAST_ADDRESS) 5790 break; 5791 } 5792 filter->ulMulticastAddrCnt = j; 5793 /* Set rx filter */ 5794 sme_8023_multicast_list(mac_handle, adapter->deflink->vdev_id, 5795 filter); 5796 qdf_mem_free(filter); 5797 } else { 5798 hdd_debug("mode %d mc_cnt %d", 5799 adapter->device_mode, adapter->mc_addr_list.mc_cnt); 5800 } 5801 5802 return 0; 5803 } 5804 5805 /** 5806 * hdd_driver_rxfilter_command_handler() - RXFILTER driver command handler 5807 * @command: Pointer to input string driver command 5808 * @adapter: Pointer to adapter 5809 * @action: Action to enable/disable filtering 5810 * 5811 * If action == false 5812 * Start filtering out data packets based on type 5813 * RXFILTER-REMOVE 0 -> Start filtering out unicast data packets 5814 * RXFILTER-REMOVE 1 -> Start filtering out broadcast data packets 5815 * RXFILTER-REMOVE 2 -> Start filtering out IPV4 mcast data packets 5816 * RXFILTER-REMOVE 3 -> Start filtering out IPV6 mcast data packets 5817 * 5818 * if action == true 5819 * Stop filtering data packets based on type 5820 * RXFILTER-ADD 0 -> Stop filtering unicast data packets 5821 * RXFILTER-ADD 1 -> Stop filtering broadcast data packets 5822 * RXFILTER-ADD 2 -> Stop filtering IPV4 mcast data packets 5823 * RXFILTER-ADD 3 -> Stop filtering IPV6 mcast data packets 5824 * 5825 * Current implementation only supports IPV4 address filtering by 5826 * selectively allowing IPV4 multicast data packest based on 5827 * address list received in .ndo_set_rx_mode 5828 * 5829 * Return: 0 for success, non-zero for failure 5830 */ 5831 static int hdd_driver_rxfilter_command_handler(uint8_t *command, 5832 struct hdd_adapter *adapter, 5833 bool action) 5834 { 5835 int ret = 0; 5836 uint8_t *value; 5837 uint8_t type; 5838 5839 value = command; 5840 /* Skip space after RXFILTER-REMOVE OR RXFILTER-ADD based on action */ 5841 if (!action) 5842 value = command + 16; 5843 else 5844 value = command + 13; 5845 ret = kstrtou8(value, 10, &type); 5846 if (ret < 0) { 5847 hdd_err("kstrtou8 failed invalid input value"); 5848 return -EINVAL; 5849 } 5850 5851 switch (type) { 5852 case 2: 5853 /* Set rx filter for IPV4 multicast data packets */ 5854 ret = hdd_set_rx_filter(adapter, action, 0x01); 5855 break; 5856 default: 5857 hdd_debug("Unsupported RXFILTER type %d", type); 5858 break; 5859 } 5860 5861 return ret; 5862 } 5863 5864 /** 5865 * drv_cmd_rx_filter_remove() - RXFILTER REMOVE driver command handler 5866 * @link_info: Link info pointer in HDD adapter 5867 * @hdd_ctx: Pointer to hdd context 5868 * @command: Pointer to input command 5869 * @command_len: Command length 5870 * @priv_data: Pointer to private data in command 5871 */ 5872 static int drv_cmd_rx_filter_remove(struct wlan_hdd_link_info *link_info, 5873 struct hdd_context *hdd_ctx, 5874 uint8_t *command, uint8_t command_len, 5875 struct hdd_priv_data *priv_data) 5876 { 5877 return hdd_driver_rxfilter_command_handler(command, 5878 link_info->adapter, false); 5879 } 5880 5881 /** 5882 * drv_cmd_rx_filter_add() - RXFILTER ADD driver command handler 5883 * @link_info: Link info pointer in HDD adapter 5884 * @hdd_ctx: Pointer to hdd context 5885 * @command: Pointer to input command 5886 * @command_len: Command length 5887 * @priv_data: Pointer to private data in command 5888 */ 5889 static int drv_cmd_rx_filter_add(struct wlan_hdd_link_info *link_info, 5890 struct hdd_context *hdd_ctx, 5891 uint8_t *command, uint8_t command_len, 5892 struct hdd_priv_data *priv_data) 5893 { 5894 return hdd_driver_rxfilter_command_handler(command, 5895 link_info->adapter, true); 5896 } 5897 #endif /* WLAN_FEATURE_PACKET_FILTERING */ 5898 5899 /** 5900 * hdd_parse_setantennamode_command() - HDD Parse SETANTENNAMODE 5901 * command 5902 * @value: Pointer to SETANTENNAMODE command 5903 * 5904 * This function parses the SETANTENNAMODE command passed in the format 5905 * SETANTENNAMODE<space>mode 5906 * 5907 * Return: parsed antenna mode 5908 */ 5909 static int hdd_parse_setantennamode_command(const uint8_t *value) 5910 { 5911 const uint8_t *in_ptr = value; 5912 int tmp, v; 5913 char arg1[32]; 5914 5915 in_ptr = strnchr(value, strlen(value), SPACE_ASCII_VALUE); 5916 5917 /* no argument after the command */ 5918 if (!in_ptr) { 5919 hdd_err("No argument after the command"); 5920 return -EINVAL; 5921 } 5922 5923 /* no space after the command */ 5924 if (SPACE_ASCII_VALUE != *in_ptr) { 5925 hdd_err("No space after the command"); 5926 return -EINVAL; 5927 } 5928 5929 /* remove empty spaces */ 5930 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) 5931 in_ptr++; 5932 5933 /* no argument followed by spaces */ 5934 if ('\0' == *in_ptr) { 5935 hdd_err("No argument followed by spaces"); 5936 return -EINVAL; 5937 } 5938 5939 /* get the argument i.e. antenna mode */ 5940 v = sscanf(in_ptr, "%31s ", arg1); 5941 if (1 != v) { 5942 hdd_err("argument retrieval from cmd string failed"); 5943 return -EINVAL; 5944 } 5945 5946 v = kstrtos32(arg1, 10, &tmp); 5947 if (v < 0) { 5948 hdd_err("argument string to int conversion failed"); 5949 return -EINVAL; 5950 } 5951 5952 return tmp; 5953 } 5954 5955 /** 5956 * hdd_is_supported_chain_mask_2x2() - Verify if supported chain 5957 * mask is 2x2 mode 5958 * @hdd_ctx: Pointer to hdd context 5959 * 5960 * Return: true if supported chain mask 2x2 else false 5961 */ 5962 static bool hdd_is_supported_chain_mask_2x2(struct hdd_context *hdd_ctx) 5963 { 5964 QDF_STATUS status; 5965 bool bval = false; 5966 5967 /* 5968 * Revisit and the update logic to determine the number 5969 * of TX/RX chains supported in the system when 5970 * antenna sharing per band chain mask support is 5971 * brought in 5972 */ 5973 status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, &bval); 5974 if (!QDF_IS_STATUS_SUCCESS(status)) 5975 hdd_err("unable to get vht_enable2x2"); 5976 5977 return (bval == 0x01) ? true : false; 5978 } 5979 5980 /** 5981 * hdd_is_supported_chain_mask_1x1() - Verify if the supported 5982 * chain mask is 1x1 5983 * @hdd_ctx: Pointer to hdd context 5984 * 5985 * Return: true if supported chain mask 1x1 else false 5986 */ 5987 static bool hdd_is_supported_chain_mask_1x1(struct hdd_context *hdd_ctx) 5988 { 5989 QDF_STATUS status; 5990 bool bval = false; 5991 5992 /* 5993 * Revisit and update the logic to determine the number 5994 * of TX/RX chains supported in the system when 5995 * antenna sharing per band chain mask support is 5996 * brought in 5997 */ 5998 status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, &bval); 5999 if (!QDF_IS_STATUS_SUCCESS(status)) 6000 hdd_err("unable to get vht_enable2x2"); 6001 6002 return (!bval) ? true : false; 6003 } 6004 6005 QDF_STATUS hdd_update_smps_antenna_mode(struct hdd_context *hdd_ctx, int mode) 6006 { 6007 mac_handle_t mac_handle = hdd_ctx->mac_handle; 6008 6009 hdd_ctx->current_antenna_mode = mode; 6010 /* 6011 * Update the user requested nss in the mac context. 6012 * This will be used in tdls protocol engine to form tdls 6013 * Management frames. 6014 */ 6015 sme_update_user_configured_nss(mac_handle, 6016 hdd_ctx->current_antenna_mode); 6017 6018 hdd_debug("Successfully switched to mode: %d x %d", 6019 hdd_ctx->current_antenna_mode, 6020 hdd_ctx->current_antenna_mode); 6021 6022 return QDF_STATUS_SUCCESS; 6023 } 6024 6025 /** 6026 * wlan_hdd_soc_set_antenna_mode_cb() - Callback for set antenna mode 6027 * @status: Status of set antenna mode 6028 * @context: callback context 6029 * 6030 * Callback on setting antenna mode 6031 * 6032 * Return: None 6033 */ 6034 static void 6035 wlan_hdd_soc_set_antenna_mode_cb(enum set_antenna_mode_status status, 6036 void *context) 6037 { 6038 struct osif_request *request = NULL; 6039 6040 hdd_debug("Status: %d", status); 6041 6042 request = osif_request_get(context); 6043 if (!request) { 6044 hdd_err("obsolete request"); 6045 return; 6046 } 6047 6048 /* Signal the completion of set dual mac config */ 6049 osif_request_complete(request); 6050 osif_request_put(request); 6051 } 6052 6053 int hdd_set_antenna_mode(struct wlan_hdd_link_info *link_info, int mode) 6054 { 6055 struct hdd_adapter *adapter = link_info->adapter; 6056 struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); 6057 struct sir_antenna_mode_param params; 6058 QDF_STATUS status; 6059 int ret = 0; 6060 struct osif_request *request = NULL; 6061 static const struct osif_request_params request_params = { 6062 .priv_size = 0, 6063 .timeout_ms = SME_POLICY_MGR_CMD_TIMEOUT, 6064 }; 6065 6066 switch (mode) { 6067 case HDD_ANTENNA_MODE_1X1: 6068 params.num_rx_chains = 1; 6069 params.num_tx_chains = 1; 6070 break; 6071 case HDD_ANTENNA_MODE_2X2: 6072 params.num_rx_chains = 2; 6073 params.num_tx_chains = 2; 6074 break; 6075 default: 6076 hdd_err("unsupported antenna mode"); 6077 ret = -EINVAL; 6078 goto exit; 6079 } 6080 6081 if (hdd_ctx->dynamic_nss_chains_support) 6082 return hdd_set_dynamic_antenna_mode(link_info, 6083 params.num_rx_chains, 6084 params.num_tx_chains); 6085 6086 if ((HDD_ANTENNA_MODE_2X2 == mode) && 6087 (!hdd_is_supported_chain_mask_2x2(hdd_ctx))) { 6088 hdd_err("System does not support 2x2 mode"); 6089 ret = -EPERM; 6090 goto exit; 6091 } 6092 6093 if ((HDD_ANTENNA_MODE_1X1 == mode) && 6094 hdd_is_supported_chain_mask_1x1(hdd_ctx)) { 6095 hdd_err("System only supports 1x1 mode"); 6096 goto exit; 6097 } 6098 6099 /* Check TDLS status and update antenna mode */ 6100 if ((QDF_STA_MODE == adapter->device_mode) && 6101 policy_mgr_is_sta_active_connection_exists(hdd_ctx->psoc)) { 6102 ret = wlan_hdd_tdls_antenna_switch(link_info, mode); 6103 if (0 != ret) 6104 goto exit; 6105 } 6106 6107 request = osif_request_alloc(&request_params); 6108 if (!request) { 6109 hdd_err("Request Allocation Failure"); 6110 ret = -ENOMEM; 6111 goto exit; 6112 } 6113 6114 params.set_antenna_mode_ctx = osif_request_cookie(request); 6115 params.set_antenna_mode_resp = (void *)wlan_hdd_soc_set_antenna_mode_cb; 6116 hdd_debug("Set antenna mode rx chains: %d tx chains: %d", 6117 params.num_rx_chains, 6118 params.num_tx_chains); 6119 6120 status = sme_soc_set_antenna_mode(hdd_ctx->mac_handle, ¶ms); 6121 if (QDF_IS_STATUS_ERROR(status)) { 6122 hdd_err("set antenna mode failed status : %d", status); 6123 ret = -EFAULT; 6124 goto request_put; 6125 } 6126 6127 ret = osif_request_wait_for_response(request); 6128 if (ret) { 6129 hdd_err("send set antenna mode timed out"); 6130 goto request_put; 6131 } 6132 6133 status = hdd_update_smps_antenna_mode(hdd_ctx, mode); 6134 if (QDF_STATUS_SUCCESS != status) { 6135 ret = -EFAULT; 6136 goto request_put; 6137 } 6138 ret = 0; 6139 request_put: 6140 osif_request_put(request); 6141 exit: 6142 hdd_debug("Set antenna status: %d current mode: %d", 6143 ret, hdd_ctx->current_antenna_mode); 6144 6145 return ret; 6146 } 6147 6148 /** 6149 * drv_cmd_set_antenna_mode() - SET ANTENNA MODE driver command 6150 * handler 6151 * @link_info: Link info pointer in HDD adapter 6152 * @hdd_ctx: Pointer to hdd context 6153 * @command: Pointer to input command 6154 * @command_len: Command length 6155 * @priv_data: Pointer to private data in command 6156 */ 6157 static int drv_cmd_set_antenna_mode(struct wlan_hdd_link_info *link_info, 6158 struct hdd_context *hdd_ctx, 6159 uint8_t *command, uint8_t command_len, 6160 struct hdd_priv_data *priv_data) 6161 { 6162 int mode; 6163 uint8_t *value = command; 6164 6165 mode = hdd_parse_setantennamode_command(value); 6166 if (mode < 0) { 6167 hdd_err("Invalid SETANTENNA command"); 6168 return mode; 6169 } 6170 6171 hdd_debug("Processing antenna mode switch to: %d", mode); 6172 6173 return hdd_set_antenna_mode(link_info, mode); 6174 } 6175 6176 /** 6177 * hdd_get_dynamic_antenna_mode() -get the dynamic antenna mode for vdev 6178 * @antenna_mode: pointer to antenna mode of vdev 6179 * @dynamic_nss_chains_support: feature support for dynamic nss chains update 6180 * @vdev: Pointer to vdev 6181 * 6182 * This API will check for the num of chains configured for the vdev, and fill 6183 * that info in the antenna mode if the dynamic chains per vdev are supported. 6184 * 6185 * Return: None 6186 */ 6187 static void 6188 hdd_get_dynamic_antenna_mode(uint32_t *antenna_mode, 6189 bool dynamic_nss_chains_support, 6190 struct wlan_objmgr_vdev *vdev) 6191 { 6192 struct wlan_mlme_nss_chains *nss_chains_dynamic_cfg; 6193 6194 if (!dynamic_nss_chains_support) 6195 return; 6196 6197 nss_chains_dynamic_cfg = ucfg_mlme_get_dynamic_vdev_config(vdev); 6198 if (!nss_chains_dynamic_cfg) { 6199 hdd_err("nss chain dynamic config NULL"); 6200 return; 6201 } 6202 /* 6203 * At present, this command doesn't include band, so by default 6204 * it will return the band 2ghz present rf chains. 6205 */ 6206 *antenna_mode = 6207 nss_chains_dynamic_cfg->num_rx_chains[NSS_CHAINS_BAND_2GHZ]; 6208 } 6209 6210 /** 6211 * drv_cmd_get_antenna_mode() - GET ANTENNA MODE driver command 6212 * handler 6213 * @link_info: Link info pointer in HDD adapter 6214 * @hdd_ctx: Pointer to hdd context 6215 * @command: Pointer to input command 6216 * @command_len: length of the command 6217 * @priv_data: private data coming with the driver command 6218 * 6219 * Return: 0 for success non-zero for failure 6220 */ 6221 static inline int drv_cmd_get_antenna_mode(struct wlan_hdd_link_info *link_info, 6222 struct hdd_context *hdd_ctx, 6223 uint8_t *command, 6224 uint8_t command_len, 6225 struct hdd_priv_data *priv_data) 6226 { 6227 uint32_t antenna_mode = 0; 6228 char extra[32]; 6229 uint8_t len = 0; 6230 struct wlan_objmgr_vdev *vdev; 6231 6232 vdev = hdd_objmgr_get_vdev_by_user(link_info, WLAN_OSIF_ID); 6233 if (!vdev) { 6234 hdd_err("vdev is NULL"); 6235 return -EINVAL; 6236 } 6237 6238 antenna_mode = hdd_ctx->current_antenna_mode; 6239 /* Overwrite this antenna mode if dynamic vdev chains are supported */ 6240 hdd_get_dynamic_antenna_mode(&antenna_mode, 6241 hdd_ctx->dynamic_nss_chains_support, vdev); 6242 hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID); 6243 len = scnprintf(extra, sizeof(extra), "%s %d", command, 6244 antenna_mode); 6245 len = QDF_MIN(priv_data->total_len, len + 1); 6246 if (copy_to_user(priv_data->buf, &extra, len)) { 6247 hdd_err("Failed to copy data to user buffer"); 6248 return -EFAULT; 6249 } 6250 6251 hdd_debug("Get antenna mode: %d", antenna_mode); 6252 6253 return 0; 6254 } 6255 6256 /* 6257 * dummy (no-op) hdd driver command handler 6258 */ 6259 static int drv_cmd_dummy(struct wlan_hdd_link_info *link_info, 6260 struct hdd_context *hdd_ctx, 6261 uint8_t *command, 6262 uint8_t command_len, 6263 struct hdd_priv_data *priv_data) 6264 { 6265 hdd_debug("%s: Ignoring driver command \"%s\"", 6266 link_info->adapter->dev->name, command); 6267 return 0; 6268 } 6269 6270 /* 6271 * handler for any unsupported wlan hdd driver command 6272 */ 6273 static int drv_cmd_invalid(struct hdd_adapter *adapter, 6274 struct hdd_context *hdd_ctx, 6275 uint8_t *command, 6276 uint8_t command_len, 6277 struct hdd_priv_data *priv_data) 6278 { 6279 qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, 6280 TRACE_CODE_HDD_UNSUPPORTED_IOCTL, 6281 adapter->deflink->vdev_id, 0); 6282 6283 hdd_debug("%s: Unsupported driver command \"%s\"", 6284 adapter->dev->name, command); 6285 6286 return -ENOTSUPP; 6287 } 6288 6289 /** 6290 * hdd_apply_fcc_constraint() - Set FCC constraint 6291 * @hdd_ctx: Pointer to hdd context 6292 * @fcc_constraint: Fcc constraint flag 6293 * 6294 * Return: Return 0 incase of success else return error number 6295 */ 6296 static int hdd_apply_fcc_constraint(struct hdd_context *hdd_ctx, 6297 bool fcc_constraint) 6298 { 6299 QDF_STATUS status; 6300 6301 status = ucfg_reg_set_fcc_constraint(hdd_ctx->pdev, 6302 fcc_constraint); 6303 if (QDF_IS_STATUS_ERROR(status)) { 6304 hdd_err("Failed to update tx power for channels 12/13"); 6305 return qdf_status_to_os_return(status); 6306 } 6307 6308 status = wlan_reg_recompute_current_chan_list(hdd_ctx->psoc, 6309 hdd_ctx->pdev); 6310 if (QDF_IS_STATUS_ERROR(status)) { 6311 hdd_err("Failed to update tx power for channels 12/13"); 6312 return qdf_status_to_os_return(status); 6313 } 6314 6315 return 0; 6316 } 6317 6318 /** 6319 * hdd_apply_fcc_constraint_update_band() - Set FCC constraint and update band 6320 * @link_info: Link info pointer in HDD adapter 6321 * @hdd_ctx: Pointer to hdd context 6322 * @fcc_constraint: FCC constraint flag 6323 * @dis_6g_keep_sta_cli_conn: Disable 6 GHz band and keep STA, P2P client 6324 * connection flag 6325 * @band_bitmap: Band bitmap 6326 * 6327 * Return: Return 0 incase of success else return error number 6328 */ 6329 static int 6330 hdd_apply_fcc_constraint_update_band(struct wlan_hdd_link_info *link_info, 6331 struct hdd_context *hdd_ctx, 6332 bool fcc_constraint, 6333 bool dis_6g_keep_sta_cli_conn, 6334 uint32_t band_bitmap) 6335 { 6336 QDF_STATUS status; 6337 6338 status = ucfg_reg_set_fcc_constraint(hdd_ctx->pdev, 6339 fcc_constraint); 6340 if (status) { 6341 hdd_err("Failed to update tx power for channels 12/13"); 6342 return qdf_status_to_os_return(status); 6343 } 6344 6345 status = ucfg_reg_set_keep_6ghz_sta_cli_connection(hdd_ctx->pdev, 6346 dis_6g_keep_sta_cli_conn); 6347 if (QDF_IS_STATUS_ERROR(status)) { 6348 hdd_err("Failed to update keep sta cli connection"); 6349 return qdf_status_to_os_return(status); 6350 } 6351 6352 return hdd_reg_set_band(link_info->adapter->dev, band_bitmap); 6353 } 6354 6355 /** 6356 * drv_cmd_set_fcc_channel() - Handle fcc constraint request 6357 * @link_info: Link info pointer in HDD adapter 6358 * @hdd_ctx: HDD context 6359 * @command: command ptr, SET_FCC_CHANNEL 0/1/2/-1 is the command 6360 * @command_len: command len 6361 * @priv_data: private data 6362 * 6363 * Return: status 6364 */ 6365 static int drv_cmd_set_fcc_channel(struct wlan_hdd_link_info *link_info, 6366 struct hdd_context *hdd_ctx, 6367 uint8_t *command, uint8_t command_len, 6368 struct hdd_priv_data *priv_data) 6369 { 6370 QDF_STATUS status; 6371 QDF_STATUS status_6G = QDF_STATUS_SUCCESS; 6372 int8_t input_value; 6373 int err; 6374 uint32_t band_bitmap = 0, curr_band_bitmap; 6375 bool rf_test_mode, fcc_constraint, dis_6g_keep_sta_cli_conn; 6376 bool modify_band = false; 6377 6378 /* 6379 * This command would be called by user-space when it detects WLAN 6380 * ON after airplane mode is set. When APM is set, WLAN turns off. 6381 * But it can be turned back on. Otherwise; when APM is turned back 6382 * off, WLAN would turn back on. So at that point the command is 6383 * expected to come down. 6384 * a) 0 means reduce power as per fcc constraint and disable 6 GHz band 6385 * but keep existing STA/P2P Client connections intact. 6386 * b) 1 means reduce power as per fcc constraint and enable 6 GHz band. 6387 * c) 2 means reset fcc constraint but disable 6 GHz band but keep 6388 * existing STA/P2P Client connections intact. 6389 * d) -1 means reset fcc constraint and enable 6 GHz band. 6390 */ 6391 6392 err = kstrtos8(command + command_len + 1, 10, &input_value); 6393 if (err) { 6394 hdd_err("error %d parsing userspace fcc parameter", err); 6395 return err; 6396 } 6397 6398 hdd_debug("input_value = %d", input_value); 6399 6400 switch (input_value) { 6401 case -1: 6402 fcc_constraint = false; 6403 dis_6g_keep_sta_cli_conn = false; 6404 break; 6405 case 0: 6406 fcc_constraint = true; 6407 dis_6g_keep_sta_cli_conn = true; 6408 break; 6409 case 1: 6410 fcc_constraint = true; 6411 dis_6g_keep_sta_cli_conn = false; 6412 break; 6413 case 2: 6414 fcc_constraint = false; 6415 dis_6g_keep_sta_cli_conn = true; 6416 break; 6417 default: 6418 hdd_err("Invalie input value"); 6419 return -EINVAL; 6420 } 6421 6422 status = ucfg_mlme_is_rf_test_mode_enabled(hdd_ctx->psoc, 6423 &rf_test_mode); 6424 if (!QDF_IS_STATUS_SUCCESS(status_6G)) { 6425 hdd_err("Get rf test mode failed"); 6426 return qdf_status_to_os_return(status); 6427 } 6428 6429 if (!rf_test_mode) { 6430 status = ucfg_reg_get_band(hdd_ctx->pdev, &curr_band_bitmap); 6431 if (!QDF_IS_STATUS_SUCCESS(status)) { 6432 hdd_err("failed to get band"); 6433 return qdf_status_to_os_return(status); 6434 } 6435 6436 if (dis_6g_keep_sta_cli_conn) { 6437 band_bitmap |= (BIT(REG_BAND_5G) | BIT(REG_BAND_2G)); 6438 } else { 6439 if (wlan_reg_is_6ghz_supported(hdd_ctx->psoc)) 6440 band_bitmap = REG_BAND_MASK_ALL; 6441 else 6442 band_bitmap = curr_band_bitmap; 6443 } 6444 6445 hdd_debug("Current band bitmap = %d, set band bitmap = %d", 6446 curr_band_bitmap, 6447 band_bitmap); 6448 6449 if (band_bitmap != curr_band_bitmap) 6450 modify_band = true; 6451 6452 if (ucfg_reg_is_fcc_constraint_set(hdd_ctx->pdev) == 6453 fcc_constraint && 6454 ucfg_reg_get_keep_6ghz_sta_cli_connection(hdd_ctx->pdev) == 6455 dis_6g_keep_sta_cli_conn) { 6456 hdd_debug("Same FCC constraint and band bitmap value"); 6457 return 0; 6458 } else if (modify_band) { 6459 return hdd_apply_fcc_constraint_update_band(link_info, 6460 hdd_ctx, 6461 fcc_constraint, 6462 dis_6g_keep_sta_cli_conn, 6463 band_bitmap); 6464 } 6465 } else { 6466 if (ucfg_reg_is_fcc_constraint_set(hdd_ctx->pdev) == 6467 fcc_constraint) { 6468 hdd_debug("Same FCC constraint value"); 6469 return 0; 6470 } 6471 } 6472 6473 return hdd_apply_fcc_constraint(hdd_ctx, fcc_constraint); 6474 } 6475 6476 /** 6477 * hdd_parse_set_channel_switch_command() - Parse and validate CHANNEL_SWITCH 6478 * command 6479 * @value: Pointer to the command 6480 * @chan_number: Pointer to the channel number 6481 * @chan_bw: Pointer to the channel bandwidth 6482 * 6483 * Parses and provides the channel number and channel width from the input 6484 * command which is expected to be of the format: CHANNEL_SWITCH <CH> <BW> 6485 * <CH> is channel number to move (where 1 = channel 1, 149 = channel 149, ...) 6486 * <BW> is bandwidth to move (where 20 = BW 20, 40 = BW 40, 80 = BW 80) 6487 * 6488 * Return: 0 for success, non-zero for failure 6489 */ 6490 static int hdd_parse_set_channel_switch_command(uint8_t *value, 6491 uint32_t *chan_number, 6492 uint32_t *chan_bw) 6493 { 6494 const uint8_t *in_ptr = value; 6495 int ret; 6496 6497 in_ptr = strnchr(value, strlen(value), SPACE_ASCII_VALUE); 6498 6499 /* no argument after the command */ 6500 if (!in_ptr) { 6501 hdd_err("No argument after the command"); 6502 return -EINVAL; 6503 } 6504 6505 /* no space after the command */ 6506 if (SPACE_ASCII_VALUE != *in_ptr) { 6507 hdd_err("No space after the command "); 6508 return -EINVAL; 6509 } 6510 6511 /* remove empty spaces and move the next argument */ 6512 while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) 6513 in_ptr++; 6514 6515 /* no argument followed by spaces */ 6516 if ('\0' == *in_ptr) { 6517 hdd_err("No argument followed by spaces"); 6518 return -EINVAL; 6519 } 6520 6521 /* get the two arguments: channel number and bandwidth */ 6522 ret = sscanf(in_ptr, "%u %u", chan_number, chan_bw); 6523 if (ret != 2) { 6524 hdd_err("Arguments retrieval from cmd string failed"); 6525 return -EINVAL; 6526 } 6527 6528 return 0; 6529 } 6530 6531 /** 6532 * drv_cmd_set_channel_switch() - Switch SAP/P2P-GO operating channel 6533 * @link_info: Link info pointer in HDD adapter 6534 * @hdd_ctx: HDD context 6535 * @command: Pointer to the input command CHANNEL_SWITCH 6536 * @command_len: Command len 6537 * @priv_data: Private data 6538 * 6539 * Handles private IOCTL CHANNEL_SWITCH command to switch the operating channel 6540 * of SAP/P2P-GO 6541 * 6542 * Return: 0 for success, non-zero for failure 6543 */ 6544 static int drv_cmd_set_channel_switch(struct wlan_hdd_link_info *link_info, 6545 struct hdd_context *hdd_ctx, 6546 uint8_t *command, uint8_t command_len, 6547 struct hdd_priv_data *priv_data) 6548 { 6549 struct hdd_adapter *adapter = link_info->adapter; 6550 struct net_device *dev = adapter->dev; 6551 int status; 6552 uint32_t chan_number = 0, chan_bw = 0; 6553 uint8_t *value = command; 6554 enum phy_ch_width width; 6555 6556 if ((adapter->device_mode != QDF_P2P_GO_MODE) && 6557 (adapter->device_mode != QDF_SAP_MODE)) { 6558 hdd_err("IOCTL CHANNEL_SWITCH not supported for mode %d", 6559 adapter->device_mode); 6560 return -EINVAL; 6561 } 6562 6563 if (!qdf_atomic_test_bit(SOFTAP_BSS_STARTED, &link_info->link_flags)) { 6564 hdd_err("SAP not started"); 6565 return -EINVAL; 6566 } 6567 6568 status = hdd_parse_set_channel_switch_command(value, 6569 &chan_number, &chan_bw); 6570 if (status) { 6571 hdd_err("Invalid CHANNEL_SWITCH command"); 6572 return status; 6573 } 6574 6575 if ((chan_bw != 20) && (chan_bw != 40) && (chan_bw != 80) && 6576 (chan_bw != 160)) { 6577 hdd_err("BW %d is not allowed for CHANNEL_SWITCH", chan_bw); 6578 return -EINVAL; 6579 } 6580 6581 if (chan_bw == 160) 6582 width = CH_WIDTH_160MHZ; 6583 else if (chan_bw == 80) 6584 width = CH_WIDTH_80MHZ; 6585 else if (chan_bw == 40) 6586 width = CH_WIDTH_40MHZ; 6587 else 6588 width = CH_WIDTH_20MHZ; 6589 6590 hdd_debug("CH:%d BW:%d", chan_number, chan_bw); 6591 6592 wlan_hdd_set_sap_csa_reason(hdd_ctx->psoc, link_info->vdev_id, 6593 CSA_REASON_USER_INITIATED); 6594 6595 if (chan_number <= wlan_reg_max_5ghz_ch_num()) 6596 chan_number = wlan_reg_legacy_chan_to_freq(hdd_ctx->pdev, 6597 chan_number); 6598 6599 status = hdd_softap_set_channel_change(dev, chan_number, width, false); 6600 if (status) { 6601 hdd_err("Set channel change fail"); 6602 return status; 6603 } 6604 6605 return 0; 6606 } 6607 6608 #ifdef DISABLE_CHANNEL_LIST 6609 void wlan_hdd_free_cache_channels(struct hdd_context *hdd_ctx) 6610 { 6611 hdd_enter(); 6612 6613 if (!hdd_ctx->original_channels) 6614 return; 6615 6616 qdf_mutex_acquire(&hdd_ctx->cache_channel_lock); 6617 hdd_ctx->original_channels->num_channels = 0; 6618 if (hdd_ctx->original_channels->channel_info) { 6619 qdf_mem_free(hdd_ctx->original_channels->channel_info); 6620 hdd_ctx->original_channels->channel_info = NULL; 6621 } 6622 qdf_mem_free(hdd_ctx->original_channels); 6623 hdd_ctx->original_channels = NULL; 6624 qdf_mutex_release(&hdd_ctx->cache_channel_lock); 6625 6626 hdd_exit(); 6627 } 6628 6629 /** 6630 * hdd_alloc_chan_cache() - Allocate the memory to cache the channel 6631 * info for the channels received in command SET_DISABLE_CHANNEL_LIST 6632 * @hdd_ctx: Pointer to HDD context 6633 * @num_chan: Number of channels for which memory needs to 6634 * be allocated 6635 * 6636 * Return: 0 on success and error code on failure 6637 */ 6638 static int hdd_alloc_chan_cache(struct hdd_context *hdd_ctx, int num_chan) 6639 { 6640 hdd_ctx->original_channels = 6641 qdf_mem_malloc(sizeof(struct hdd_cache_channels)); 6642 if (!hdd_ctx->original_channels) 6643 return -ENOMEM; 6644 6645 hdd_ctx->original_channels->num_channels = num_chan; 6646 hdd_ctx->original_channels->channel_info = 6647 qdf_mem_malloc(num_chan * 6648 sizeof(struct hdd_cache_channel_info)); 6649 if (!hdd_ctx->original_channels->channel_info) { 6650 hdd_ctx->original_channels->num_channels = 0; 6651 qdf_mem_free(hdd_ctx->original_channels); 6652 hdd_ctx->original_channels = NULL; 6653 return -ENOMEM; 6654 } 6655 return 0; 6656 } 6657 6658 /** 6659 * check_disable_channels() - Check for disable channel 6660 * @hdd_ctx: Pointer to hdd context 6661 * @operating_freq: Current operating frequency of adapter 6662 * 6663 * This function checks original_channels array for a specific channel 6664 * 6665 * Return: 0 if channel not found, 1 if channel found 6666 */ 6667 static bool check_disable_channels(struct hdd_context *hdd_ctx, 6668 qdf_freq_t operating_freq) 6669 { 6670 uint32_t num_channels; 6671 uint8_t i; 6672 if (!hdd_ctx || !hdd_ctx->original_channels || 6673 !hdd_ctx->original_channels->channel_info) 6674 return false; 6675 6676 num_channels = hdd_ctx->original_channels->num_channels; 6677 for (i = 0; i < num_channels; i++) { 6678 if (operating_freq == 6679 hdd_ctx->original_channels->channel_info[i].freq) 6680 return true; 6681 } 6682 6683 return false; 6684 } 6685 6686 /** 6687 * disconnect_sta_and_restart_sap() - Disconnect STA and restart SAP 6688 * 6689 * @hdd_ctx: Pointer to hdd context 6690 * @reason: Disconnect reason code as per @enum wlan_reason_code 6691 * 6692 * Disable channels provided by user and disconnect STA if it is 6693 * connected to any AP, restart SAP. 6694 * 6695 * Return: None 6696 */ 6697 static void disconnect_sta_and_restart_sap(struct hdd_context *hdd_ctx, 6698 enum wlan_reason_code reason) 6699 { 6700 struct hdd_adapter *adapter, *next = NULL; 6701 QDF_STATUS status; 6702 struct hdd_ap_ctx *ap_ctx; 6703 uint32_t ch_list[NUM_CHANNELS]; 6704 uint32_t ch_count = 0; 6705 bool is_valid_chan_present = true; 6706 6707 if (!hdd_ctx) 6708 return; 6709 6710 hdd_check_and_disconnect_sta_on_invalid_channel(hdd_ctx, reason); 6711 6712 status = policy_mgr_get_valid_chans(hdd_ctx->psoc, ch_list, &ch_count); 6713 if (QDF_IS_STATUS_ERROR(status) || !ch_count) { 6714 hdd_debug("No valid channels present, stop the SAPs"); 6715 is_valid_chan_present = false; 6716 } 6717 6718 status = hdd_get_front_adapter(hdd_ctx, &adapter); 6719 while (adapter && (status == QDF_STATUS_SUCCESS)) { 6720 if (hdd_validate_adapter(adapter) || 6721 adapter->device_mode != QDF_SAP_MODE) { 6722 goto next_adapter; 6723 } 6724 6725 ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter->deflink); 6726 if (!is_valid_chan_present) 6727 wlan_hdd_stop_sap(adapter); 6728 else if (check_disable_channels(hdd_ctx, 6729 ap_ctx->operating_chan_freq)) 6730 policy_mgr_check_sap_restart(hdd_ctx->psoc, 6731 adapter->deflink->vdev_id); 6732 next_adapter: 6733 status = hdd_get_next_adapter(hdd_ctx, adapter, &next); 6734 adapter = next; 6735 } 6736 } 6737 6738 /** 6739 * hdd_check_chan_and_fill_freq() - to validate chan and convert into freq 6740 * @pdev: The physical dev to cache the channels for 6741 * @in_chan: input as channel number or freq 6742 * @freq: frequency for input in_chan (output parameter) 6743 * 6744 * This function checks input "in_chan" is channel number, if yes then fills 6745 * appropriate frequency into "freq" out param. If the "in_param" is greater 6746 * than MAX_5GHZ_CHANNEL then gets the valid frequencies for legacy channels 6747 * else get the valid channel for 6Ghz frequency. 6748 * 6749 * Return: true if "in_chan" is valid channel/frequency; false otherwise 6750 */ 6751 static bool hdd_check_chan_and_fill_freq(struct wlan_objmgr_pdev *pdev, 6752 uint32_t *in_chan, qdf_freq_t *freq) 6753 { 6754 if (IS_CHANNEL_VALID(*in_chan)) { 6755 *freq = wlan_reg_legacy_chan_to_freq(pdev, *in_chan); 6756 } else if (WLAN_REG_IS_24GHZ_CH_FREQ(*in_chan) || 6757 WLAN_REG_IS_5GHZ_CH_FREQ(*in_chan) || 6758 WLAN_REG_IS_6GHZ_CHAN_FREQ(*in_chan)) { 6759 *freq = *in_chan; 6760 *in_chan = wlan_reg_freq_to_chan(pdev, *in_chan); 6761 } else { 6762 return false; 6763 } 6764 6765 return true; 6766 } 6767 6768 /** 6769 * hdd_parse_disable_chan_cmd() - Parse the channel list received 6770 * in command. 6771 * @adapter: pointer to hdd adapter 6772 * @ptr: Pointer to the command string 6773 * 6774 * This function parses the channel list/frequency received in the command. 6775 * command should be a string having format 6776 * SET_DISABLE_CHANNEL_LIST <num of channels> 6777 * <channels separated by spaces>/<frequency separated by spaces>. 6778 * If this command has frequency as input, this function first converts into 6779 * equivalent channel. 6780 * If the command comes multiple times then the channels received in the 6781 * command or channels converted from frequency will be compared with the 6782 * channels cached in the first command, if the channel list matches with 6783 * the cached channels, it returns success otherwise returns failure. 6784 * 6785 * Return: 0 on success, Error code on failure 6786 */ 6787 6788 static int hdd_parse_disable_chan_cmd(struct hdd_adapter *adapter, uint8_t *ptr) 6789 { 6790 struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); 6791 uint8_t *param; 6792 int j, i, temp_int, ret = 0, num_channels; 6793 qdf_freq_t *chan_freq_list = NULL; 6794 bool is_command_repeated = false; 6795 qdf_freq_t freq = 0; 6796 6797 if (!hdd_ctx) { 6798 hdd_err("HDD Context is NULL"); 6799 return -EINVAL; 6800 } 6801 6802 param = strnchr(ptr, strlen(ptr), ' '); 6803 /*no argument after the command*/ 6804 if (!param) 6805 return -EINVAL; 6806 6807 /*no space after the command*/ 6808 else if (SPACE_ASCII_VALUE != *param) 6809 return -EINVAL; 6810 6811 param++; 6812 6813 /*removing empty spaces*/ 6814 while ((SPACE_ASCII_VALUE == *param) && ('\0' != *param)) 6815 param++; 6816 6817 /*no argument followed by spaces*/ 6818 if ('\0' == *param) 6819 return -EINVAL; 6820 6821 /*getting the first argument ie the number of channels*/ 6822 if (sscanf(param, "%d ", &temp_int) != 1) { 6823 hdd_err("Cannot get number of channels from input"); 6824 return -EINVAL; 6825 } 6826 6827 if (temp_int < 0 || temp_int > NUM_CHANNELS) { 6828 hdd_err("Invalid Number of channel received"); 6829 return -EINVAL; 6830 } 6831 6832 hdd_debug("Number of channel to disable are: %d", temp_int); 6833 6834 if (!temp_int) { 6835 /* 6836 * Restore and Free the cache channels when the command is 6837 * received with num channels as 0 6838 */ 6839 wlan_hdd_restore_channels(hdd_ctx); 6840 return 0; 6841 } 6842 6843 qdf_mutex_acquire(&hdd_ctx->cache_channel_lock); 6844 6845 if (!hdd_ctx->original_channels) { 6846 if (hdd_alloc_chan_cache(hdd_ctx, temp_int)) { 6847 ret = -ENOMEM; 6848 goto mem_alloc_failed; 6849 } 6850 } else if (hdd_ctx->original_channels->num_channels != temp_int) { 6851 hdd_err("Invalid Number of channels"); 6852 ret = -EINVAL; 6853 is_command_repeated = true; 6854 goto parse_failed; 6855 } else { 6856 is_command_repeated = true; 6857 } 6858 num_channels = temp_int; 6859 6860 chan_freq_list = qdf_mem_malloc(num_channels * sizeof(qdf_freq_t)); 6861 if (!chan_freq_list) 6862 return -ENOMEM; 6863 6864 for (j = 0; j < num_channels; j++) { 6865 /* 6866 * param pointing to the beginning of first space 6867 * after number of channels 6868 */ 6869 param = strpbrk(param, " "); 6870 /*no channel list after the number of channels argument*/ 6871 if (!param) { 6872 hdd_err("Invalid No of channel provided in the list"); 6873 ret = -EINVAL; 6874 goto parse_failed; 6875 } 6876 6877 param++; 6878 6879 /*removing empty space*/ 6880 while ((SPACE_ASCII_VALUE == *param) && ('\0' != *param)) 6881 param++; 6882 6883 if ('\0' == *param) { 6884 hdd_err("No channel is provided in the list"); 6885 ret = -EINVAL; 6886 goto parse_failed; 6887 } 6888 6889 if (sscanf(param, "%d ", &temp_int) != 1) { 6890 hdd_err("Cannot read channel number"); 6891 ret = -EINVAL; 6892 goto parse_failed; 6893 } 6894 6895 if (!hdd_check_chan_and_fill_freq(hdd_ctx->pdev, &temp_int, 6896 &freq)) { 6897 hdd_err("Invalid channel number received"); 6898 ret = -EINVAL; 6899 goto parse_failed; 6900 } 6901 6902 hdd_debug("channel[%d] = %d Frequency[%d] = %d", j, temp_int, 6903 j, freq); 6904 chan_freq_list[j] = freq; 6905 } 6906 6907 /*extra arguments check*/ 6908 param = strpbrk(param, " "); 6909 if (param) { 6910 while ((SPACE_ASCII_VALUE == *param) && ('\0' != *param)) 6911 param++; 6912 6913 if ('\0' != *param) { 6914 hdd_err("Invalid argument received"); 6915 ret = -EINVAL; 6916 goto parse_failed; 6917 } 6918 } 6919 6920 /* 6921 * If command is received first time, cache the channels to 6922 * be disabled else compare the channels received in the 6923 * command with the cached channels, if channel list matches 6924 * return success otherwise return failure. 6925 */ 6926 if (!is_command_repeated) { 6927 for (j = 0; j < num_channels; j++) 6928 hdd_ctx->original_channels->channel_info[j].freq = 6929 chan_freq_list[j]; 6930 6931 /* Cache the channel list in regulatory also */ 6932 ucfg_reg_cache_channel_freq_state(hdd_ctx->pdev, 6933 chan_freq_list, 6934 num_channels); 6935 } else { 6936 for (i = 0; i < num_channels; i++) { 6937 for (j = 0; j < num_channels; j++) 6938 if (hdd_ctx->original_channels-> 6939 channel_info[i].freq == 6940 chan_freq_list[j]) 6941 break; 6942 if (j == num_channels) { 6943 ret = -EINVAL; 6944 goto parse_failed; 6945 } 6946 } 6947 ret = 0; 6948 } 6949 mem_alloc_failed: 6950 if (chan_freq_list) 6951 qdf_mem_free(chan_freq_list); 6952 qdf_mutex_release(&hdd_ctx->cache_channel_lock); 6953 /* Disable the channels received in command SET_DISABLE_CHANNEL_LIST */ 6954 if (!is_command_repeated && hdd_ctx->original_channels) { 6955 ret = wlan_hdd_disable_channels(hdd_ctx); 6956 if (ret) 6957 return ret; 6958 disconnect_sta_and_restart_sap( 6959 hdd_ctx, 6960 REASON_OPER_CHANNEL_BAND_CHANGE); 6961 } 6962 6963 hdd_exit(); 6964 6965 return ret; 6966 6967 parse_failed: 6968 if (!is_command_repeated) 6969 wlan_hdd_free_cache_channels(hdd_ctx); 6970 6971 if (chan_freq_list) 6972 qdf_mem_free(chan_freq_list); 6973 qdf_mutex_release(&hdd_ctx->cache_channel_lock); 6974 hdd_exit(); 6975 6976 return ret; 6977 } 6978 6979 static int drv_cmd_set_disable_chan_list(struct wlan_hdd_link_info *link_info, 6980 struct hdd_context *hdd_ctx, 6981 uint8_t *command, 6982 uint8_t command_len, 6983 struct hdd_priv_data *priv_data) 6984 { 6985 return hdd_parse_disable_chan_cmd(link_info->adapter, command); 6986 } 6987 6988 /** 6989 * hdd_get_disable_ch_list() - get disable channel list 6990 * @hdd_ctx: hdd context 6991 * @buf: buffer to hold disable channel list 6992 * @buf_len: buffer length 6993 * 6994 * Return: length of data copied to buf 6995 */ 6996 static int hdd_get_disable_ch_list(struct hdd_context *hdd_ctx, uint8_t *buf, 6997 uint32_t buf_len) 6998 { 6999 struct hdd_cache_channel_info *ch_list; 7000 unsigned char i, num_ch; 7001 int len = 0; 7002 7003 qdf_mutex_acquire(&hdd_ctx->cache_channel_lock); 7004 if (hdd_ctx->original_channels && 7005 hdd_ctx->original_channels->num_channels && 7006 hdd_ctx->original_channels->channel_info) { 7007 num_ch = hdd_ctx->original_channels->num_channels; 7008 7009 len = scnprintf(buf, buf_len, "%s %hhu", 7010 "GET_DISABLE_CHANNEL_LIST", num_ch); 7011 ch_list = hdd_ctx->original_channels->channel_info; 7012 for (i = 0; (i < num_ch) && (len < buf_len - 1); i++) { 7013 len += scnprintf(buf + len, buf_len - len, 7014 " %d", 7015 wlan_reg_freq_to_chan(hdd_ctx->pdev, 7016 ch_list[i].freq)); 7017 } 7018 } 7019 qdf_mutex_release(&hdd_ctx->cache_channel_lock); 7020 7021 return len; 7022 } 7023 7024 static int drv_cmd_get_disable_chan_list(struct wlan_hdd_link_info *link_info, 7025 struct hdd_context *hdd_ctx, 7026 uint8_t *command, uint8_t command_len, 7027 struct hdd_priv_data *priv_data) 7028 { 7029 char extra[512] = {0}; 7030 int max_len, copied_length; 7031 7032 hdd_debug("Received Command to get disable Channels list"); 7033 7034 max_len = QDF_MIN(priv_data->total_len, sizeof(extra)); 7035 copied_length = hdd_get_disable_ch_list(hdd_ctx, extra, max_len); 7036 if (copied_length == 0) { 7037 hdd_err("disable channel list is not yet programmed"); 7038 return -EINVAL; 7039 } 7040 7041 if (copy_to_user(priv_data->buf, &extra, copied_length + 1)) { 7042 hdd_err("failed to copy data to user buffer"); 7043 return -EFAULT; 7044 } 7045 7046 hdd_debug("data:%s", extra); 7047 return 0; 7048 } 7049 #else 7050 7051 static inline int 7052 drv_cmd_set_disable_chan_list(struct wlan_hdd_link_info *link_info, 7053 struct hdd_context *hdd_ctx, 7054 uint8_t *command, uint8_t command_len, 7055 struct hdd_priv_data *priv_data) 7056 { 7057 return 0; 7058 } 7059 7060 void wlan_hdd_free_cache_channels(struct hdd_context *hdd_ctx) 7061 { 7062 } 7063 7064 static inline int 7065 drv_cmd_get_disable_chan_list(struct wlan_hdd_link_info *link_info, 7066 struct hdd_context *hdd_ctx, 7067 uint8_t *command, uint8_t command_len, 7068 struct hdd_priv_data *priv_data) 7069 { 7070 return 0; 7071 } 7072 #endif 7073 7074 #ifdef FEATURE_ANI_LEVEL_REQUEST 7075 static int drv_cmd_get_ani_level(struct wlan_hdd_link_info *link_info, 7076 struct hdd_context *hdd_ctx, 7077 uint8_t *command, uint8_t command_len, 7078 struct hdd_priv_data *priv_data) 7079 { 7080 struct hdd_adapter *adapter = link_info->adapter; 7081 char *extra; 7082 int copied_length = 0, j, temp_int, ret = 0; 7083 uint8_t *param, num_freqs, num_recv_channels; 7084 uint32_t parsed_freqs[MAX_NUM_FREQS_FOR_ANI_LEVEL]; 7085 struct wmi_host_ani_level_event ani[MAX_NUM_FREQS_FOR_ANI_LEVEL]; 7086 size_t user_size = priv_data->total_len; 7087 7088 hdd_debug("Received Command to get ANI level"); 7089 7090 param = strnchr(command, strlen(command), ' '); 7091 7092 /* No argument after the command */ 7093 if (!param) 7094 return -EINVAL; 7095 7096 /* No space after the command */ 7097 else if (SPACE_ASCII_VALUE != *param) 7098 return -EINVAL; 7099 7100 param++; 7101 7102 /* Removing empty spaces*/ 7103 while ((SPACE_ASCII_VALUE == *param) && ('\0' != *param)) 7104 param++; 7105 7106 /*no argument followed by spaces */ 7107 if ('\0' == *param) 7108 return -EINVAL; 7109 7110 /* Getting the first argument ie the number of channels */ 7111 if (sscanf(param, "%d ", &temp_int) != 1) { 7112 hdd_err("Cannot get number of freq from input"); 7113 return -EINVAL; 7114 } 7115 7116 if (temp_int < 0 || temp_int > MAX_NUM_FREQS_FOR_ANI_LEVEL) { 7117 hdd_err("Invalid Number of channel received"); 7118 return -EINVAL; 7119 } 7120 7121 hdd_debug("Number of freq to fetch ANI level are: %d", temp_int); 7122 7123 if (!temp_int) 7124 return 0; 7125 7126 num_freqs = temp_int; 7127 7128 for (j = 0; j < num_freqs; j++) { 7129 /* 7130 * Param pointing to the beginning of first space 7131 * after number of channels. 7132 */ 7133 param = strpbrk(param, " "); 7134 /*no channel list after the number of channels argument*/ 7135 if (!param) { 7136 hdd_err("Invalid No of freq provided in the list"); 7137 ret = -EINVAL; 7138 goto parse_failed; 7139 } 7140 7141 param++; 7142 7143 /* Removing empty space */ 7144 while ((SPACE_ASCII_VALUE == *param) && ('\0' != *param)) 7145 param++; 7146 7147 if ('\0' == *param) { 7148 hdd_err("No freq is provided in the list"); 7149 ret = -EINVAL; 7150 goto parse_failed; 7151 } 7152 7153 if (sscanf(param, "%d ", &temp_int) != 1) { 7154 hdd_err("Cannot read freq number"); 7155 ret = -EINVAL; 7156 goto parse_failed; 7157 } 7158 7159 hdd_debug("channel_freq[%d] = %d", j, temp_int); 7160 parsed_freqs[j] = temp_int; 7161 } 7162 7163 /* Extra arguments check */ 7164 param = strpbrk(param, " "); 7165 if (param) { 7166 while ((SPACE_ASCII_VALUE == *param) && ('\0' != *param)) 7167 param++; 7168 7169 if ('\0' != *param) { 7170 hdd_err("Invalid argument received"); 7171 ret = -EINVAL; 7172 goto parse_failed; 7173 } 7174 } 7175 7176 qdf_mem_zero(ani, sizeof(ani)); 7177 hdd_debug("num_freq: %d", num_freqs); 7178 if (QDF_IS_STATUS_ERROR(wlan_hdd_get_ani_level(adapter, ani, 7179 parsed_freqs, 7180 num_freqs))) { 7181 hdd_err("Unable to retrieve ani level"); 7182 return -EINVAL; 7183 } 7184 7185 extra = qdf_mem_malloc(user_size); 7186 if (!extra) { 7187 ret = -ENOMEM; 7188 goto parse_failed; 7189 } 7190 7191 /* 7192 * Find the number of channels that are populated. If freq is not 7193 * filled then stop count there 7194 */ 7195 for (num_recv_channels = 0; 7196 (num_recv_channels < num_freqs && 7197 ani[num_recv_channels].chan_freq); num_recv_channels++) 7198 ; 7199 7200 for (j = 0; j < num_recv_channels; j++) { 7201 /* Sanity check for ANI level validity */ 7202 if (ani[j].ani_level > MAX_ANI_LEVEL) 7203 continue; 7204 7205 copied_length += scnprintf(extra + copied_length, 7206 user_size - copied_length, "%d:%d\n", 7207 ani[j].chan_freq, ani[j].ani_level); 7208 } 7209 7210 if (copied_length == 0) { 7211 hdd_err("ANI level not fetched"); 7212 ret = -EINVAL; 7213 goto free; 7214 } 7215 7216 hdd_debug("data: %s", extra); 7217 7218 if (copy_to_user(priv_data->buf, extra, copied_length + 1)) { 7219 hdd_err("failed to copy data to user buffer"); 7220 ret = -EFAULT; 7221 goto free; 7222 } 7223 7224 free: 7225 qdf_mem_free(extra); 7226 7227 parse_failed: 7228 return ret; 7229 } 7230 7231 #else 7232 static inline int drv_cmd_get_ani_level(struct wlan_hdd_link_info *link_info, 7233 struct hdd_context *hdd_ctx, 7234 uint8_t *command, uint8_t command_len, 7235 struct hdd_priv_data *priv_data) 7236 { 7237 return 0; 7238 } 7239 #endif 7240 7241 #ifdef FUNC_CALL_MAP 7242 static int drv_cmd_get_function_call_map(struct wlan_hdd_link_info *link_info, 7243 struct hdd_context *hdd_ctx, 7244 uint8_t *command, 7245 uint8_t command_len, 7246 struct hdd_priv_data *priv_data) 7247 { 7248 char *cc_buf = qdf_mem_malloc(QDF_FUNCTION_CALL_MAP_BUF_LEN); 7249 uint8_t *param; 7250 int temp_int; 7251 7252 param = strnchr(command, strlen(command), ' '); 7253 /*no argument after the command*/ 7254 if (NULL == param) 7255 return -EINVAL; 7256 7257 /*no space after the command*/ 7258 else if (SPACE_ASCII_VALUE != *param) 7259 return -EINVAL; 7260 7261 param++; 7262 7263 /*removing empty spaces*/ 7264 while ((SPACE_ASCII_VALUE == *param) && ('\0' != *param)) 7265 param++; 7266 7267 /*no argument followed by spaces*/ 7268 if ('\0' == *param) 7269 return -EINVAL; 7270 7271 /*getting the first argument */ 7272 if (sscanf(param, "%d ", &temp_int) != 1) { 7273 hdd_err("No option given"); 7274 return -EINVAL; 7275 } 7276 7277 if (temp_int < 0 || temp_int > 1) { 7278 hdd_err("Invalid option given"); 7279 return -EINVAL; 7280 } 7281 7282 /* Read the buffer */ 7283 if (temp_int) { 7284 /* 7285 * These logs are required as these indicates the start and end of the 7286 * dump for the auto script to parse 7287 */ 7288 hdd_info("Function call map dump start"); 7289 qdf_get_func_call_map(cc_buf); 7290 qdf_trace_hex_dump(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG, 7291 cc_buf, QDF_FUNCTION_CALL_MAP_BUF_LEN); 7292 hdd_info("Function call map dump end"); 7293 } else { 7294 qdf_clear_func_call_map(); 7295 hdd_info("Function call map clear"); 7296 } 7297 qdf_mem_free(cc_buf); 7298 7299 return 0; 7300 } 7301 #endif 7302 7303 /* 7304 * The following table contains all supported WLAN HDD 7305 * IOCTL driver commands and the handler for each of them. 7306 */ 7307 static const struct hdd_drv_cmd hdd_drv_cmds[] = { 7308 {"P2P_DEV_ADDR", drv_cmd_p2p_dev_addr, false}, 7309 {"P2P_SET_NOA", drv_cmd_p2p_set_noa, true}, 7310 {"P2P_SET_PS", drv_cmd_p2p_set_ps, true}, 7311 {"SETBAND", drv_cmd_set_band, true}, 7312 {"SETWMMPS", drv_cmd_set_wmmps, true}, 7313 {"COUNTRY", drv_cmd_country, true}, 7314 {"SETCOUNTRYREV", drv_cmd_country, true}, 7315 {"GETCOUNTRYREV", drv_cmd_get_country, false}, 7316 {"SETSUSPENDMODE", drv_cmd_set_suspend_mode, true}, 7317 {"SET_AP_WPS_P2P_IE", drv_cmd_dummy, false}, 7318 {"SETROAMTRIGGER", drv_cmd_set_roam_trigger, true}, 7319 {"GETROAMTRIGGER", drv_cmd_get_roam_trigger, false}, 7320 {"SETROAMSCANPERIOD", drv_cmd_set_roam_scan_period, true}, 7321 {"GETROAMSCANPERIOD", drv_cmd_get_roam_scan_period, false}, 7322 {"SETROAMSCANREFRESHPERIOD", drv_cmd_set_roam_scan_refresh_period, 7323 true}, 7324 {"GETROAMSCANREFRESHPERIOD", drv_cmd_get_roam_scan_refresh_period, 7325 false}, 7326 {"SETROAMMODE", drv_cmd_set_roam_mode, true}, 7327 {"GETROAMMODE", drv_cmd_get_roam_mode, false}, 7328 {"SETROAMDELTA", drv_cmd_set_roam_delta, true}, 7329 {"GETROAMDELTA", drv_cmd_get_roam_delta, false}, 7330 {"GETBAND", drv_cmd_get_band, false}, 7331 {"GETCCXMODE", drv_cmd_get_ccx_mode, false}, 7332 {"GETOKCMODE", drv_cmd_get_okc_mode, false}, 7333 {"GETFASTROAM", drv_cmd_get_fast_roam, false}, 7334 {"GETFASTTRANSITION", drv_cmd_get_fast_transition, false}, 7335 {"SETROAMSCANCHANNELMINTIME", drv_cmd_set_roam_scan_channel_min_time, 7336 true}, 7337 {"SENDACTIONFRAME", drv_cmd_send_action_frame, true}, 7338 {"GETROAMSCANCHANNELMINTIME", drv_cmd_get_roam_scan_channel_min_time, 7339 false}, 7340 {"SETSCANCHANNELTIME", drv_cmd_set_scan_channel_time, true}, 7341 {"GETSCANCHANNELTIME", drv_cmd_get_scan_channel_time, false}, 7342 {"SETSCANHOMETIME", drv_cmd_set_scan_home_time, true}, 7343 {"GETSCANHOMETIME", drv_cmd_get_scan_home_time, false}, 7344 {"SETROAMINTRABAND", drv_cmd_set_roam_intra_band, true}, 7345 {"GETROAMINTRABAND", drv_cmd_get_roam_intra_band, false}, 7346 {"SETSCANNPROBES", drv_cmd_set_scan_n_probes, true}, 7347 {"GETSCANNPROBES", drv_cmd_get_scan_n_probes, false}, 7348 {"SETSCANHOMEAWAYTIME", drv_cmd_set_scan_home_away_time, true}, 7349 {"GETSCANHOMEAWAYTIME", drv_cmd_get_scan_home_away_time, false}, 7350 {"REASSOC", drv_cmd_reassoc, true}, 7351 {"SETWESMODE", drv_cmd_set_wes_mode, true}, 7352 {"GETWESMODE", drv_cmd_get_wes_mode, false}, 7353 {"SETOPPORTUNISTICRSSIDIFF", drv_cmd_set_opportunistic_rssi_diff, 7354 true}, 7355 {"GETOPPORTUNISTICRSSIDIFF", drv_cmd_get_opportunistic_rssi_diff, 7356 false}, 7357 {"SETROAMRESCANRSSIDIFF", drv_cmd_set_roam_rescan_rssi_diff, true}, 7358 {"GETROAMRESCANRSSIDIFF", drv_cmd_get_roam_rescan_rssi_diff, false}, 7359 {"SETFASTROAM", drv_cmd_set_fast_roam, true}, 7360 {"SETFASTTRANSITION", drv_cmd_set_fast_transition, true}, 7361 {"FASTREASSOC", drv_cmd_fast_reassoc, true}, 7362 {"SETOKCMODE", drv_cmd_set_okc_mode, true}, 7363 {"BTCOEXMODE", drv_cmd_bt_coex_mode, true}, 7364 {"SCAN-ACTIVE", drv_cmd_scan_active, false}, 7365 {"SCAN-PASSIVE", drv_cmd_scan_passive, false}, 7366 {"CONCSETDWELLTIME", drv_cmd_conc_set_dwell_time, true}, 7367 {"GETDWELLTIME", drv_cmd_get_dwell_time, false}, 7368 {"SETDWELLTIME", drv_cmd_set_dwell_time, true}, 7369 {"MIRACAST", drv_cmd_miracast, true}, 7370 {"TPUT_DEBUG_MODE_ENABLE", drv_cmd_tput_debug_mode_enable, false}, 7371 {"TPUT_DEBUG_MODE_DISABLE", drv_cmd_tput_debug_mode_disable, false}, 7372 #ifdef FEATURE_WLAN_ESE 7373 {"SETCCXROAMSCANCHANNELS", drv_cmd_set_ccx_roam_scan_channels, true}, 7374 {"GETTSMSTATS", drv_cmd_get_tsm_stats, true}, 7375 {"SETCCKMIE", drv_cmd_set_cckm_ie, true}, 7376 {"CCXBEACONREQ", drv_cmd_ccx_beacon_req, true}, 7377 {"CCXPLMREQ", drv_cmd_ccx_plm_req, true}, 7378 {"SETCCXMODE", drv_cmd_set_ccx_mode, true}, 7379 #endif /* FEATURE_WLAN_ESE */ 7380 {"SETMCRATE", drv_cmd_set_mc_rate, true}, 7381 {"MAXTXPOWER", drv_cmd_max_tx_power, true}, 7382 {"SETDFSSCANMODE", drv_cmd_set_dfs_scan_mode, true}, 7383 {"GETDFSSCANMODE", drv_cmd_get_dfs_scan_mode, false}, 7384 {"GETLINKSTATUS", drv_cmd_get_link_status, false}, 7385 #ifdef WLAN_FEATURE_EXTWOW_SUPPORT 7386 {"ENABLEEXTWOW", drv_cmd_enable_ext_wow, true}, 7387 {"SETAPP1PARAMS", drv_cmd_set_app1_params, true}, 7388 {"SETAPP2PARAMS", drv_cmd_set_app2_params, true}, 7389 #endif 7390 #ifdef FEATURE_WLAN_TDLS 7391 {"TDLSSECONDARYCHANNELOFFSET", drv_cmd_tdls_secondary_channel_offset, 7392 true}, 7393 {"TDLSOFFCHANNELMODE", drv_cmd_tdls_off_channel_mode, true}, 7394 {"TDLSOFFCHANNEL", drv_cmd_tdls_off_channel, true}, 7395 {"TDLSSCAN", drv_cmd_tdls_scan, true}, 7396 #endif 7397 {"RSSI", drv_cmd_get_rssi, false}, 7398 {"LINKSPEED", drv_cmd_get_linkspeed, false}, 7399 #ifdef WLAN_FEATURE_PACKET_FILTERING 7400 {"RXFILTER-REMOVE", drv_cmd_rx_filter_remove, true}, 7401 {"RXFILTER-ADD", drv_cmd_rx_filter_add, true}, 7402 #endif 7403 {"SET_FCC_CHANNEL", drv_cmd_set_fcc_channel, true}, 7404 {"CHANNEL_SWITCH", drv_cmd_set_channel_switch, true}, 7405 {"SETANTENNAMODE", drv_cmd_set_antenna_mode, true}, 7406 {"GETANTENNAMODE", drv_cmd_get_antenna_mode, false}, 7407 {"SET_DISABLE_CHANNEL_LIST", drv_cmd_set_disable_chan_list, true}, 7408 {"GET_DISABLE_CHANNEL_LIST", drv_cmd_get_disable_chan_list, false}, 7409 {"GET_ANI_LEVEL", drv_cmd_get_ani_level, false}, 7410 #ifdef FUNC_CALL_MAP 7411 {"GET_FUNCTION_CALL_MAP", drv_cmd_get_function_call_map, true}, 7412 #endif 7413 {"STOP", drv_cmd_dummy, false}, 7414 /* Deprecated commands */ 7415 {"RXFILTER-START", drv_cmd_dummy, false}, 7416 {"RXFILTER-STOP", drv_cmd_dummy, false}, 7417 {"BTCOEXSCAN-START", drv_cmd_dummy, false}, 7418 {"BTCOEXSCAN-STOP", drv_cmd_dummy, false}, 7419 {"GET_SOFTAP_LINK_SPEED", drv_cmd_get_sap_go_linkspeed, true}, 7420 #ifdef CONFIG_BAND_6GHZ 7421 {"GET_WIFI6E_CHANNELS", drv_cmd_get_wifi6e_channels, true}, 7422 #endif 7423 }; 7424 7425 /** 7426 * hdd_drv_cmd_process() - chooses and runs the proper 7427 * handler based on the input command 7428 * @link_info: Link info pointer in adapter. 7429 * @cmd: Pointer to the driver command 7430 * @priv_data: Pointer to the data associated with the command 7431 * 7432 * This function parses the input hdd driver command and runs 7433 * the proper handler 7434 * 7435 * Return: 0 for success non-zero for failure 7436 */ 7437 static int hdd_drv_cmd_process(struct wlan_hdd_link_info *link_info, 7438 uint8_t *cmd, struct hdd_priv_data *priv_data) 7439 { 7440 struct hdd_adapter *adapter = link_info->adapter; 7441 struct hdd_context *hdd_ctx; 7442 int i; 7443 const int cmd_num_total = ARRAY_SIZE(hdd_drv_cmds); 7444 uint8_t *cmd_i = NULL; 7445 hdd_drv_cmd_handler_t handler = NULL; 7446 int len = 0, cmd_len = 0; 7447 uint8_t *ptr; 7448 bool args; 7449 7450 if (!cmd || !priv_data) { 7451 hdd_err("at least 1 param is NULL"); 7452 return -EINVAL; 7453 } 7454 7455 /* Calculate length of the first word */ 7456 ptr = strchrnul(cmd, ' '); 7457 cmd_len = ptr - cmd; 7458 7459 hdd_ctx = WLAN_HDD_GET_CTX(adapter); 7460 7461 for (i = 0; i < cmd_num_total; i++) { 7462 7463 cmd_i = (uint8_t *)hdd_drv_cmds[i].cmd; 7464 handler = hdd_drv_cmds[i].handler; 7465 len = strlen(cmd_i); 7466 args = hdd_drv_cmds[i].args; 7467 7468 if (!handler) { 7469 hdd_err("no. %d handler is NULL", i); 7470 return -EINVAL; 7471 } 7472 7473 if (len == cmd_len && strncasecmp(cmd, cmd_i, len) == 0) { 7474 if (args && drv_cmd_validate(cmd, len)) 7475 return -EINVAL; 7476 7477 return handler(link_info, hdd_ctx, 7478 cmd, len, priv_data); 7479 } 7480 } 7481 7482 return drv_cmd_invalid(adapter, hdd_ctx, cmd, len, priv_data); 7483 } 7484 7485 /** 7486 * hdd_driver_command() - top level wlan hdd driver command handler 7487 * @link_info: Link info pointer in HDD adapter 7488 * @priv_data: Pointer to the raw command data 7489 * 7490 * This function is the top level wlan hdd driver command handler. It 7491 * handles the command with the help of hdd_drv_cmd_process() 7492 * 7493 * Return: 0 for success non-zero for failure 7494 */ 7495 static int hdd_driver_command(struct wlan_hdd_link_info *link_info, 7496 struct hdd_priv_data *priv_data) 7497 { 7498 struct hdd_adapter *adapter = link_info->adapter; 7499 uint8_t *command = NULL; 7500 int ret = 0; 7501 struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); 7502 7503 hdd_enter(); 7504 7505 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { 7506 hdd_err("Command not allowed in FTM mode"); 7507 return -EINVAL; 7508 } 7509 7510 ret = wlan_hdd_validate_context(hdd_ctx); 7511 if (ret) 7512 return ret; 7513 7514 if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) { 7515 hdd_err("Driver module is closed; command can not be processed"); 7516 return -EINVAL; 7517 } 7518 7519 /* 7520 * Note that valid pointers are provided by caller 7521 */ 7522 7523 /* copy to local struct to avoid numerous changes to legacy code */ 7524 if (priv_data->total_len <= 0 || 7525 priv_data->total_len > WLAN_PRIV_DATA_MAX_LEN) { 7526 hdd_warn("Invalid priv_data.total_len: %d!!!", 7527 priv_data->total_len); 7528 ret = -EINVAL; 7529 goto exit; 7530 } 7531 7532 /* Allocate +1 for '\0' */ 7533 command = qdf_mem_malloc(priv_data->total_len + 1); 7534 if (!command) { 7535 ret = -ENOMEM; 7536 goto exit; 7537 } 7538 7539 if (copy_from_user(command, priv_data->buf, priv_data->total_len)) { 7540 ret = -EFAULT; 7541 goto exit; 7542 } 7543 7544 /* Make sure the command is NUL-terminated */ 7545 command[priv_data->total_len] = '\0'; 7546 7547 hdd_debug("%s: %s", adapter->dev->name, command); 7548 ret = hdd_drv_cmd_process(link_info, command, priv_data); 7549 7550 exit: 7551 if (command) 7552 qdf_mem_free(command); 7553 hdd_exit(); 7554 return ret; 7555 } 7556 7557 #ifdef CONFIG_COMPAT 7558 static int hdd_driver_compat_ioctl(struct wlan_hdd_link_info *link_info, 7559 void __user *data) 7560 { 7561 struct { 7562 compat_uptr_t buf; 7563 int used_len; 7564 int total_len; 7565 } compat_priv_data; 7566 struct hdd_priv_data priv_data; 7567 int ret = 0; 7568 7569 /* 7570 * Note that adapter and ifr have already been verified by caller, 7571 * and HDD context has also been validated 7572 */ 7573 if (copy_from_user(&compat_priv_data, data, 7574 sizeof(compat_priv_data))) { 7575 ret = -EFAULT; 7576 goto exit; 7577 } 7578 priv_data.buf = compat_ptr(compat_priv_data.buf); 7579 priv_data.used_len = compat_priv_data.used_len; 7580 priv_data.total_len = compat_priv_data.total_len; 7581 ret = hdd_driver_command(link_info, &priv_data); 7582 exit: 7583 return ret; 7584 } 7585 #else /* CONFIG_COMPAT */ 7586 static inline int hdd_driver_compat_ioctl(struct wlan_hdd_link_info *link_info, 7587 void __user *data) 7588 { 7589 /* will never be invoked */ 7590 return 0; 7591 } 7592 #endif /* CONFIG_COMPAT */ 7593 7594 static int hdd_driver_ioctl(struct wlan_hdd_link_info *link_info, 7595 void __user *data) 7596 { 7597 struct hdd_priv_data priv_data; 7598 int ret = 0; 7599 7600 /* 7601 * Note that adapter and ifr have already been verified by caller, 7602 * and HDD context has also been validated 7603 */ 7604 if (copy_from_user(&priv_data, data, sizeof(priv_data))) 7605 ret = -EFAULT; 7606 else 7607 ret = hdd_driver_command(link_info, &priv_data); 7608 7609 return ret; 7610 } 7611 7612 /** 7613 * __hdd_ioctl() - ioctl handler for wlan network interfaces 7614 * @dev: device upon which the ioctl was received 7615 * @data: pointer to the raw command data in the ioctl request 7616 * @cmd: ioctl command 7617 * 7618 * This function does initial processing of wlan device ioctls. 7619 * Currently two flavors of ioctls are supported. The primary ioctl 7620 * that is supported is the (SIOCDEVPRIVATE + 1) ioctl which is used 7621 * for Android "DRIVER" commands. The other ioctl that is 7622 * conditionally supported is the SIOCIOCTLTX99 ioctl which is used 7623 * for FTM on some platforms. This function simply verifies that the 7624 * driver is in a sane state, and that the ioctl is one of the 7625 * supported flavors, in which case flavor-specific handlers are 7626 * dispatched. 7627 * 7628 * Return: 0 on success, non-zero on error 7629 */ 7630 static int __hdd_ioctl(struct net_device *dev, void __user *data, int cmd) 7631 { 7632 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); 7633 struct hdd_context *hdd_ctx; 7634 int ret; 7635 7636 hdd_enter_dev(dev); 7637 7638 if (dev != adapter->dev) { 7639 hdd_err("HDD adapter/dev inconsistency"); 7640 ret = -ENODEV; 7641 goto exit; 7642 } 7643 7644 if (!data) { 7645 hdd_err("invalid data cmd: %d", cmd); 7646 ret = -EINVAL; 7647 goto exit; 7648 } 7649 #if defined(QCA_WIFI_FTM) && defined(LINUX_QCMBR) 7650 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { 7651 if (SIOCIOCTLTX99 == cmd) { 7652 ret = wlan_hdd_qcmbr_unified_ioctl(adapter, data); 7653 goto exit; 7654 } 7655 } 7656 #endif 7657 7658 hdd_ctx = WLAN_HDD_GET_CTX(adapter); 7659 ret = wlan_hdd_validate_context(hdd_ctx); 7660 if (ret) 7661 goto exit; 7662 7663 switch (cmd) { 7664 case (SIOCDEVPRIVATE + 1): 7665 if (in_compat_syscall()) 7666 ret = hdd_driver_compat_ioctl(adapter->deflink, data); 7667 else 7668 ret = hdd_driver_ioctl(adapter->deflink, data); 7669 break; 7670 default: 7671 hdd_warn("unknown ioctl %d", cmd); 7672 ret = -EINVAL; 7673 break; 7674 } 7675 exit: 7676 hdd_exit(); 7677 return ret; 7678 } 7679 7680 int hdd_ioctl(struct net_device *net_dev, struct ifreq *ifr, int cmd) 7681 { 7682 struct osif_vdev_sync *vdev_sync; 7683 int errno; 7684 7685 errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); 7686 if (errno) 7687 return errno; 7688 7689 errno = __hdd_ioctl(net_dev, ifr->ifr_data, cmd); 7690 7691 osif_vdev_sync_op_stop(vdev_sync); 7692 7693 return errno; 7694 } 7695 7696 int hdd_dev_private_ioctl(struct net_device *dev, struct ifreq *ifr, 7697 void __user *data, int cmd) 7698 { 7699 struct osif_vdev_sync *vdev_sync; 7700 int errno; 7701 7702 errno = osif_vdev_sync_op_start(dev, &vdev_sync); 7703 if (errno) 7704 return errno; 7705 7706 errno = __hdd_ioctl(dev, data, cmd); 7707 7708 osif_vdev_sync_op_stop(vdev_sync); 7709 7710 return errno; 7711 } 7712