1 /* 2 * Copyright (c) 2013-2021 The Linux Foundation. All rights reserved. 3 * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for 6 * any purpose with or without fee is hereby granted, provided that the 7 * above copyright notice and this permission notice appear in all 8 * copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 /** 21 * DOC: wlan_hdd_debugfs.c 22 * 23 * This driver currently supports the following debugfs files: 24 * wlan_wcnss/wow_enable to enable/disable WoWL. 25 * wlan_wcnss/wow_pattern to configure WoWL patterns. 26 * wlan_wcnss/pattern_gen to configure periodic TX patterns. 27 */ 28 29 #include "osif_sync.h" 30 #include <wlan_hdd_includes.h> 31 #include <wlan_hdd_debugfs.h> 32 #include <wlan_osif_request_manager.h> 33 #include <wlan_hdd_wowl.h> 34 #include <cds_sched.h> 35 #include <wlan_hdd_debugfs_llstat.h> 36 #include <wlan_hdd_debugfs_mibstat.h> 37 #include "wlan_hdd_debugfs_unit_test.h" 38 39 40 #define MAX_USER_COMMAND_SIZE_WOWL_ENABLE 8 41 #define MAX_USER_COMMAND_SIZE_WOWL_PATTERN 512 42 #define MAX_USER_COMMAND_SIZE_FRAME 4096 43 44 #define MAX_DEBUGFS_WAIT_ITERATIONS 20 45 #define DEBUGFS_WAIT_SLEEP_TIME 100 46 47 static bool hdd_periodic_pattern_map[MAXNUM_PERIODIC_TX_PTRNS]; 48 49 static qdf_atomic_t debugfs_thread_count; 50 hdd_debugfs_thread_increment(void)51 void hdd_debugfs_thread_increment(void) 52 { 53 qdf_atomic_inc(&debugfs_thread_count); 54 } 55 hdd_debugfs_thread_decrement(void)56 void hdd_debugfs_thread_decrement(void) 57 { 58 qdf_atomic_dec(&debugfs_thread_count); 59 } 60 hdd_return_debugfs_threads_count(void)61 int hdd_return_debugfs_threads_count(void) 62 { 63 return qdf_atomic_read(&debugfs_thread_count); 64 } 65 hdd_wait_for_debugfs_threads_completion(void)66 bool hdd_wait_for_debugfs_threads_completion(void) 67 { 68 int count = MAX_DEBUGFS_WAIT_ITERATIONS; 69 int r; 70 71 while (count) { 72 r = hdd_return_debugfs_threads_count(); 73 if (!r) 74 break; 75 76 if (--count) { 77 hdd_debug("Waiting for %d debugfs threads to exit", r); 78 qdf_sleep(DEBUGFS_WAIT_SLEEP_TIME); 79 } 80 } 81 82 /* at least one debugfs thread is executing */ 83 if (!count) { 84 hdd_err("Timed-out waiting for debugfs threads"); 85 return false; 86 } 87 88 return true; 89 } 90 91 /** 92 * __wcnss_wowpattern_write() - wow_pattern debugfs handler 93 * @net_dev: net_device context used to register the debugfs file 94 * @buf: text being written to the debugfs 95 * @count: size of @buf 96 * @ppos: (unused) offset into the virtual file system 97 * 98 * Return: number of bytes processed 99 */ __wcnss_wowpattern_write(struct net_device * net_dev,const char __user * buf,size_t count,loff_t * ppos)100 static ssize_t __wcnss_wowpattern_write(struct net_device *net_dev, 101 const char __user *buf, size_t count, 102 loff_t *ppos) 103 { 104 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev); 105 struct hdd_context *hdd_ctx; 106 char cmd[MAX_USER_COMMAND_SIZE_WOWL_PATTERN + 1]; 107 char *sptr, *token; 108 uint8_t pattern_idx = 0; 109 uint8_t pattern_offset = 0; 110 char *pattern_buf; 111 char *pattern_mask; 112 int ret; 113 114 hdd_enter(); 115 116 if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) { 117 hdd_err("Invalid adapter or adapter has invalid magic"); 118 return -EINVAL; 119 } 120 121 hdd_ctx = WLAN_HDD_GET_CTX(adapter); 122 ret = wlan_hdd_validate_context(hdd_ctx); 123 if (0 != ret) 124 return ret; 125 126 if (!wlan_hdd_validate_modules_state(hdd_ctx)) 127 return -EINVAL; 128 129 if (!sme_is_feature_supported_by_fw(WOW)) { 130 hdd_err("Wake-on-Wireless feature is not supported in firmware!"); 131 return -EINVAL; 132 } 133 134 if (count > MAX_USER_COMMAND_SIZE_WOWL_PATTERN) { 135 hdd_err("Command length is larger than %d bytes", 136 MAX_USER_COMMAND_SIZE_WOWL_PATTERN); 137 return -EINVAL; 138 } 139 140 /* Get command from user */ 141 if (copy_from_user(cmd, buf, count)) 142 return -EFAULT; 143 cmd[count] = '\0'; 144 sptr = cmd; 145 146 /* Get pattern idx */ 147 token = strsep(&sptr, " "); 148 if (!token) 149 return -EINVAL; 150 151 if (kstrtou8(token, 0, &pattern_idx)) 152 return -EINVAL; 153 154 /* Get pattern offset */ 155 token = strsep(&sptr, " "); 156 157 /* Delete pattern if no further argument */ 158 if (!token) { 159 hdd_del_wowl_ptrn_debugfs(adapter, pattern_idx); 160 161 return count; 162 } 163 164 if (kstrtou8(token, 0, &pattern_offset)) 165 return -EINVAL; 166 167 /* Get pattern */ 168 token = strsep(&sptr, " "); 169 if (!token) 170 return -EINVAL; 171 172 pattern_buf = token; 173 174 /* Get pattern mask */ 175 token = strsep(&sptr, " "); 176 if (!token) 177 return -EINVAL; 178 179 pattern_mask = token; 180 pattern_mask[strlen(pattern_mask) - 1] = '\0'; 181 182 hdd_add_wowl_ptrn_debugfs(adapter, pattern_idx, pattern_offset, 183 pattern_buf, pattern_mask); 184 hdd_exit(); 185 return count; 186 } 187 188 /** 189 * wcnss_wowpattern_write() - SSR wrapper for __wcnss_wowpattern_write 190 * @file: file pointer 191 * @buf: buffer 192 * @count: count 193 * @ppos: position pointer 194 * 195 * Return: 0 on success, error number otherwise 196 */ wcnss_wowpattern_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)197 static ssize_t wcnss_wowpattern_write(struct file *file, 198 const char __user *buf, 199 size_t count, loff_t *ppos) 200 { 201 struct net_device *net_dev = file_inode(file)->i_private; 202 struct osif_vdev_sync *vdev_sync; 203 ssize_t err_size; 204 205 err_size = osif_vdev_sync_op_start(net_dev, &vdev_sync); 206 if (err_size) 207 return err_size; 208 209 err_size = __wcnss_wowpattern_write(net_dev, buf, count, ppos); 210 211 osif_vdev_sync_op_stop(vdev_sync); 212 213 return err_size; 214 } 215 216 /** 217 * __wcnss_patterngen_write() - pattern_gen debugfs handler 218 * @net_dev: net_device context used to register the debugfs file 219 * @buf: text being written to the debugfs 220 * @count: size of @buf 221 * @ppos: (unused) offset into the virtual file system 222 * 223 * Return: number of bytes processed 224 */ __wcnss_patterngen_write(struct net_device * net_dev,const char __user * buf,size_t count,loff_t * ppos)225 static ssize_t __wcnss_patterngen_write(struct net_device *net_dev, 226 const char __user *buf, size_t count, 227 loff_t *ppos) 228 { 229 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev); 230 struct hdd_context *hdd_ctx; 231 tSirAddPeriodicTxPtrn *addPeriodicTxPtrnParams; 232 tSirDelPeriodicTxPtrn *delPeriodicTxPtrnParams; 233 234 char *cmd, *sptr, *token; 235 uint8_t pattern_idx = 0; 236 uint8_t pattern_duration = 0; 237 char *pattern_buf; 238 uint16_t pattern_len = 0; 239 uint16_t i = 0; 240 QDF_STATUS status; 241 int ret; 242 243 hdd_enter(); 244 245 if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) { 246 hdd_err("Invalid adapter or adapter has invalid magic"); 247 return -EINVAL; 248 } 249 250 hdd_ctx = WLAN_HDD_GET_CTX(adapter); 251 ret = wlan_hdd_validate_context(hdd_ctx); 252 if (0 != ret) 253 return ret; 254 255 if (!wlan_hdd_validate_modules_state(hdd_ctx)) 256 return -EINVAL; 257 258 if (!sme_is_feature_supported_by_fw(WLAN_PERIODIC_TX_PTRN)) { 259 hdd_err("Periodic Tx Pattern Offload feature is not supported in firmware!"); 260 return -EINVAL; 261 } 262 263 /* Get command from user */ 264 if (count <= MAX_USER_COMMAND_SIZE_FRAME) 265 cmd = qdf_mem_malloc(count + 1); 266 else { 267 hdd_err("Command length is larger than %d bytes", 268 MAX_USER_COMMAND_SIZE_FRAME); 269 270 return -EINVAL; 271 } 272 273 if (!cmd) { 274 hdd_err("Memory allocation for cmd failed!"); 275 return -ENOMEM; 276 } 277 278 if (copy_from_user(cmd, buf, count)) { 279 qdf_mem_free(cmd); 280 return -EFAULT; 281 } 282 cmd[count] = '\0'; 283 sptr = cmd; 284 285 /* Get pattern idx */ 286 token = strsep(&sptr, " "); 287 if (!token) 288 goto failure; 289 if (kstrtou8(token, 0, &pattern_idx)) 290 goto failure; 291 292 if (pattern_idx > (MAXNUM_PERIODIC_TX_PTRNS - 1)) { 293 hdd_err("Pattern index: %d is not in the range (0 ~ %d)", 294 pattern_idx, MAXNUM_PERIODIC_TX_PTRNS - 1); 295 296 goto failure; 297 } 298 299 /* Get pattern duration */ 300 token = strsep(&sptr, " "); 301 if (!token) 302 goto failure; 303 if (kstrtou8(token, 0, &pattern_duration)) 304 goto failure; 305 306 /* Delete pattern using index if duration is 0 */ 307 if (!pattern_duration) { 308 if (!hdd_periodic_pattern_map[pattern_idx]) { 309 hdd_debug_rl("WoW pattern %d is not in the table.", 310 pattern_idx); 311 312 qdf_mem_free(cmd); 313 return -EINVAL; 314 } 315 316 delPeriodicTxPtrnParams = 317 qdf_mem_malloc(sizeof(tSirDelPeriodicTxPtrn)); 318 if (!delPeriodicTxPtrnParams) { 319 qdf_mem_free(cmd); 320 return -ENOMEM; 321 } 322 323 delPeriodicTxPtrnParams->ucPtrnId = pattern_idx; 324 qdf_copy_macaddr(&delPeriodicTxPtrnParams->mac_address, 325 &adapter->mac_addr); 326 327 /* Delete pattern */ 328 status = sme_del_periodic_tx_ptrn(hdd_ctx->mac_handle, 329 delPeriodicTxPtrnParams); 330 if (QDF_STATUS_SUCCESS != status) { 331 hdd_err("sme_del_periodic_tx_ptrn() failed!"); 332 333 qdf_mem_free(delPeriodicTxPtrnParams); 334 goto failure; 335 } 336 337 hdd_periodic_pattern_map[pattern_idx] = false; 338 339 qdf_mem_free(cmd); 340 qdf_mem_free(delPeriodicTxPtrnParams); 341 return count; 342 } 343 344 /* 345 * In SAP mode allow configuration without any connection check 346 * In STA mode check if it's in connected state before adding 347 * patterns 348 */ 349 hdd_debug("device mode %d", adapter->device_mode); 350 if ((QDF_STA_MODE == adapter->device_mode) && 351 (!hdd_cm_is_vdev_associated(adapter->deflink))) { 352 hdd_err("Not in Connected state!"); 353 goto failure; 354 } 355 356 /* Get pattern */ 357 token = strsep(&sptr, " "); 358 if (!token) 359 goto failure; 360 361 pattern_buf = token; 362 pattern_buf[strlen(pattern_buf) - 1] = '\0'; 363 pattern_len = strlen(pattern_buf); 364 365 /* Since the pattern is a hex string, 2 characters represent 1 byte. */ 366 if (pattern_len % 2) { 367 hdd_err("Malformed pattern!"); 368 369 goto failure; 370 } else 371 pattern_len >>= 1; 372 373 if (pattern_len < 14 || pattern_len > PERIODIC_TX_PTRN_MAX_SIZE) { 374 hdd_err("Not an 802.3 frame!"); 375 376 goto failure; 377 } 378 379 addPeriodicTxPtrnParams = qdf_mem_malloc(sizeof(tSirAddPeriodicTxPtrn)); 380 if (!addPeriodicTxPtrnParams) { 381 qdf_mem_free(cmd); 382 return -ENOMEM; 383 } 384 385 addPeriodicTxPtrnParams->ucPtrnId = pattern_idx; 386 addPeriodicTxPtrnParams->usPtrnIntervalMs = pattern_duration * 500; 387 addPeriodicTxPtrnParams->ucPtrnSize = pattern_len; 388 qdf_copy_macaddr(&addPeriodicTxPtrnParams->mac_address, 389 &adapter->mac_addr); 390 391 /* Extract the pattern */ 392 for (i = 0; i < addPeriodicTxPtrnParams->ucPtrnSize; i++) { 393 addPeriodicTxPtrnParams->ucPattern[i] = 394 (hex_to_bin(pattern_buf[0]) << 4) + 395 hex_to_bin(pattern_buf[1]); 396 397 /* Skip to next byte */ 398 pattern_buf += 2; 399 } 400 401 /* Add pattern */ 402 status = sme_add_periodic_tx_ptrn(hdd_ctx->mac_handle, 403 addPeriodicTxPtrnParams); 404 if (QDF_STATUS_SUCCESS != status) { 405 hdd_err("sme_add_periodic_tx_ptrn() failed!"); 406 407 qdf_mem_free(addPeriodicTxPtrnParams); 408 goto failure; 409 } 410 411 if (!hdd_periodic_pattern_map[pattern_idx]) 412 hdd_periodic_pattern_map[pattern_idx] = true; 413 414 qdf_mem_free(cmd); 415 qdf_mem_free(addPeriodicTxPtrnParams); 416 hdd_exit(); 417 return count; 418 419 failure: 420 hdd_err("Invalid input. Input format is: ptrn_idx duration pattern"); 421 qdf_mem_free(cmd); 422 return -EINVAL; 423 } 424 425 /** 426 * wcnss_patterngen_write() - SSR wrapper for __wcnss_patterngen_write 427 * @file: file pointer 428 * @buf: buffer 429 * @count: count 430 * @ppos: position pointer 431 * 432 * Return: 0 on success, error number otherwise 433 */ wcnss_patterngen_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)434 static ssize_t wcnss_patterngen_write(struct file *file, 435 const char __user *buf, 436 size_t count, loff_t *ppos) 437 { 438 struct net_device *net_dev = file_inode(file)->i_private; 439 struct osif_vdev_sync *vdev_sync; 440 ssize_t err_size; 441 442 err_size = osif_vdev_sync_op_start(net_dev, &vdev_sync); 443 if (err_size) 444 return err_size; 445 446 err_size = __wcnss_patterngen_write(net_dev, buf, count, ppos); 447 448 osif_vdev_sync_op_stop(vdev_sync); 449 450 return err_size; 451 } 452 453 /** 454 * __wcnss_debugfs_open() - Generic debugfs open() handler 455 * @net_dev: net_device context used to register the debugfs file 456 * 457 * Return: Errno 458 */ __wcnss_debugfs_open(struct net_device * net_dev)459 static int __wcnss_debugfs_open(struct net_device *net_dev) 460 { 461 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev); 462 struct hdd_context *hdd_ctx; 463 int errno; 464 465 hdd_enter(); 466 467 if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) { 468 hdd_err("Invalid adapter or adapter has invalid magic"); 469 return -EINVAL; 470 } 471 472 hdd_ctx = WLAN_HDD_GET_CTX(adapter); 473 errno = wlan_hdd_validate_context(hdd_ctx); 474 475 hdd_exit(); 476 477 return errno; 478 } 479 480 /** 481 * wcnss_debugfs_open() - SSR wrapper for __wcnss_debugfs_open 482 * @inode: inode pointer 483 * @file: file pointer 484 * 485 * Return: 0 on success, error number otherwise 486 */ wcnss_debugfs_open(struct inode * inode,struct file * file)487 static int wcnss_debugfs_open(struct inode *inode, struct file *file) 488 { 489 struct net_device *net_dev = inode->i_private; 490 struct osif_vdev_sync *vdev_sync; 491 int errno; 492 493 errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); 494 if (errno) 495 return errno; 496 497 errno = __wcnss_debugfs_open(net_dev); 498 499 osif_vdev_sync_op_stop(vdev_sync); 500 501 return errno; 502 } 503 504 static const struct file_operations fops_wowpattern = { 505 .write = wcnss_wowpattern_write, 506 .open = wcnss_debugfs_open, 507 .owner = THIS_MODULE, 508 .llseek = default_llseek, 509 }; 510 511 static const struct file_operations fops_patterngen = { 512 .write = wcnss_patterngen_write, 513 .open = wcnss_debugfs_open, 514 .owner = THIS_MODULE, 515 .llseek = default_llseek, 516 }; 517 518 /** 519 * hdd_debugfs_init() - Initialize debugfs interface 520 * @adapter: interface adapter pointer 521 * 522 * Register support for the debugfs files supported by the driver. 523 * 524 * NB: The current implementation only supports debugfs operations 525 * on the primary interface, i.e. wlan0 526 * 527 * Return: QDF_STATUS_SUCCESS if all files registered, 528 * QDF_STATUS_E_FAILURE on failure 529 */ hdd_debugfs_init(struct hdd_adapter * adapter)530 QDF_STATUS hdd_debugfs_init(struct hdd_adapter *adapter) 531 { 532 struct net_device *net_dev = adapter->dev; 533 534 adapter->debugfs_phy = debugfs_create_dir(net_dev->name, 0); 535 536 if (!adapter->debugfs_phy) { 537 hdd_err("debugfs: create folder %s fail", net_dev->name); 538 return QDF_STATUS_E_FAILURE; 539 } 540 541 if (!debugfs_create_file("wow_pattern", 00400 | 00200, 542 adapter->debugfs_phy, net_dev, 543 &fops_wowpattern)) 544 return QDF_STATUS_E_FAILURE; 545 546 if (!debugfs_create_file("pattern_gen", 00400 | 00200, 547 adapter->debugfs_phy, net_dev, 548 &fops_patterngen)) 549 return QDF_STATUS_E_FAILURE; 550 551 if (wlan_hdd_create_mib_stats_file(adapter)) 552 return QDF_STATUS_E_FAILURE; 553 554 if (wlan_hdd_create_ll_stats_file(adapter)) 555 return QDF_STATUS_E_FAILURE; 556 557 return QDF_STATUS_SUCCESS; 558 } 559 560 /** 561 * hdd_debugfs_exit() - Shutdown debugfs interface 562 * @adapter: interface adapter pointer 563 * 564 * Unregister support for the debugfs files supported by the driver. 565 * 566 * Return: None 567 */ hdd_debugfs_exit(struct hdd_adapter * adapter)568 void hdd_debugfs_exit(struct hdd_adapter *adapter) 569 { 570 debugfs_remove_recursive(adapter->debugfs_phy); 571 } 572