1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * nxpwifi: debugfs 4 * 5 * Copyright 2011-2024 NXP 6 */ 7 8 #include <linux/debugfs.h> 9 #include <linux/string_choices.h> 10 11 #include "main.h" 12 #include "cmdevt.h" 13 #include "11n.h" 14 15 static struct dentry *nxpwifi_dfs_dir; 16 17 static char *bss_modes[] = { 18 "UNSPECIFIED", 19 "ADHOC", 20 "STATION", 21 "AP", 22 "AP_VLAN", 23 "WDS", 24 "MONITOR", 25 "MESH_POINT", 26 "P2P_CLIENT", 27 "P2P_GO", 28 "P2P_DEVICE", 29 }; 30 31 /* 32 * debugfs "info" read handler: dump driver name/version, interface, BSS mode, 33 * link state, MAC, counters; STA adds SSID/BSSID/channel/country/region and 34 * multicast list. 35 */ 36 static ssize_t 37 nxpwifi_info_read(struct file *file, char __user *ubuf, 38 size_t count, loff_t *ppos) 39 { 40 struct nxpwifi_private *priv = 41 (struct nxpwifi_private *)file->private_data; 42 struct net_device *netdev = priv->netdev; 43 struct netdev_hw_addr *ha; 44 struct netdev_queue *txq; 45 unsigned long page = get_zeroed_page(GFP_KERNEL); 46 char *p = (char *)page, fmt[64]; 47 struct nxpwifi_bss_info info; 48 ssize_t ret; 49 int i = 0; 50 51 if (!p) 52 return -ENOMEM; 53 54 memset(&info, 0, sizeof(info)); 55 ret = nxpwifi_get_bss_info(priv, &info); 56 if (ret) 57 goto free_and_exit; 58 59 nxpwifi_drv_get_driver_version(priv->adapter, fmt, sizeof(fmt) - 1); 60 61 nxpwifi_get_ver_ext(priv, 0); 62 63 p += sprintf(p, "driver_name = "); 64 p += sprintf(p, "\"nxpwifi\"\n"); 65 p += sprintf(p, "driver_version = %s", fmt); 66 p += sprintf(p, "\nverext = %s", priv->version_str); 67 p += sprintf(p, "\ninterface_name=\"%s\"\n", netdev->name); 68 69 if (info.bss_mode >= ARRAY_SIZE(bss_modes)) 70 p += sprintf(p, "bss_mode=\"%d\"\n", info.bss_mode); 71 else 72 p += sprintf(p, "bss_mode=\"%s\"\n", bss_modes[info.bss_mode]); 73 74 p += sprintf(p, "media_state=\"%s\"\n", 75 (!priv->media_connected ? "Disconnected" : "Connected")); 76 p += sprintf(p, "mac_address=\"%pM\"\n", netdev->dev_addr); 77 78 if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) { 79 p += sprintf(p, "multicast_count=\"%d\"\n", 80 netdev_mc_count(netdev)); 81 p += sprintf(p, "essid=\"%.*s\"\n", info.ssid.ssid_len, 82 info.ssid.ssid); 83 p += sprintf(p, "bssid=\"%pM\"\n", info.bssid); 84 p += sprintf(p, "channel=\"%d\"\n", (int)info.bss_chan); 85 p += sprintf(p, "country_code = \"%s\"\n", info.country_code); 86 p += sprintf(p, "region_code=\"0x%x\"\n", 87 priv->adapter->region_code); 88 89 netdev_for_each_mc_addr(ha, netdev) 90 p += sprintf(p, "multicast_address[%d]=\"%pM\"\n", 91 i++, ha->addr); 92 } 93 94 p += sprintf(p, "num_tx_bytes = %lu\n", priv->stats.tx_bytes); 95 p += sprintf(p, "num_rx_bytes = %lu\n", priv->stats.rx_bytes); 96 p += sprintf(p, "num_tx_pkts = %lu\n", priv->stats.tx_packets); 97 p += sprintf(p, "num_rx_pkts = %lu\n", priv->stats.rx_packets); 98 p += sprintf(p, "num_tx_pkts_dropped = %lu\n", priv->stats.tx_dropped); 99 p += sprintf(p, "num_rx_pkts_dropped = %lu\n", priv->stats.rx_dropped); 100 p += sprintf(p, "num_tx_pkts_err = %lu\n", priv->stats.tx_errors); 101 p += sprintf(p, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors); 102 p += sprintf(p, "carrier %s\n", 103 str_on_off(netif_carrier_ok(priv->netdev))); 104 p += sprintf(p, "tx queue"); 105 for (i = 0; i < netdev->num_tx_queues; i++) { 106 txq = netdev_get_tx_queue(netdev, i); 107 p += sprintf(p, " %d:%s", i, netif_tx_queue_stopped(txq) ? 108 "stopped" : "started"); 109 } 110 p += sprintf(p, "\n"); 111 112 ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page, 113 (unsigned long)p - page); 114 115 free_and_exit: 116 free_page(page); 117 return ret; 118 } 119 120 /* 121 * debugfs "getlog" read handler: dump firmware/802.11 counters (retry, RTS/ACK, dup, 122 * frag, mcast, FCS, beacon stats). 123 */ 124 static ssize_t 125 nxpwifi_getlog_read(struct file *file, char __user *ubuf, 126 size_t count, loff_t *ppos) 127 { 128 struct nxpwifi_private *priv = 129 (struct nxpwifi_private *)file->private_data; 130 unsigned long page = get_zeroed_page(GFP_KERNEL); 131 char *p = (char *)page; 132 ssize_t ret; 133 struct nxpwifi_ds_get_stats stats; 134 135 if (!p) 136 return -ENOMEM; 137 138 memset(&stats, 0, sizeof(stats)); 139 ret = nxpwifi_get_stats_info(priv, &stats); 140 if (ret) 141 goto free_and_exit; 142 143 p += sprintf(p, "\n" 144 "mcasttxframe %u\n" 145 "failed %u\n" 146 "retry %u\n" 147 "multiretry %u\n" 148 "framedup %u\n" 149 "rtssuccess %u\n" 150 "rtsfailure %u\n" 151 "ackfailure %u\n" 152 "rxfrag %u\n" 153 "mcastrxframe %u\n" 154 "fcserror %u\n" 155 "txframe %u\n" 156 "wepicverrcnt-1 %u\n" 157 "wepicverrcnt-2 %u\n" 158 "wepicverrcnt-3 %u\n" 159 "wepicverrcnt-4 %u\n" 160 "bcn_rcv_cnt %u\n" 161 "bcn_miss_cnt %u\n", 162 stats.mcast_tx_frame, 163 stats.failed, 164 stats.retry, 165 stats.multi_retry, 166 stats.frame_dup, 167 stats.rts_success, 168 stats.rts_failure, 169 stats.ack_failure, 170 stats.rx_frag, 171 stats.mcast_rx_frame, 172 stats.fcs_error, 173 stats.tx_frame, 174 stats.wep_icv_error[0], 175 stats.wep_icv_error[1], 176 stats.wep_icv_error[2], 177 stats.wep_icv_error[3], 178 stats.bcn_rcv_cnt, 179 stats.bcn_miss_cnt); 180 181 ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page, 182 (unsigned long)p - page); 183 184 free_and_exit: 185 free_page(page); 186 return ret; 187 } 188 189 /* 190 * debugfs "histogram" read handler: report sample count and per-rate/SNR/noise 191 * floor/signal strength histograms. 192 */ 193 static ssize_t 194 nxpwifi_histogram_read(struct file *file, char __user *ubuf, 195 size_t count, loff_t *ppos) 196 { 197 struct nxpwifi_private *priv = 198 (struct nxpwifi_private *)file->private_data; 199 ssize_t ret; 200 struct nxpwifi_histogram_data *phist_data; 201 int i, value; 202 unsigned long page = get_zeroed_page(GFP_KERNEL); 203 char *p = (char *)page; 204 205 if (!p) 206 return -ENOMEM; 207 208 if (!priv || !priv->hist_data) { 209 ret = -EFAULT; 210 goto free_and_exit; 211 } 212 213 phist_data = priv->hist_data; 214 215 p += scnprintf(p, PAGE_SIZE - (p - (char *)page), "\n" 216 "total samples = %d\n", 217 atomic_read(&phist_data->num_samples)); 218 219 p += scnprintf(p, PAGE_SIZE - (p - (char *)page), 220 "rx rates (in Mbps): 0=1M 1=2M 2=5.5M 3=11M 4=6M 5=9M 6=12M\n" 221 "7=18M 8=24M 9=36M 10=48M 11=54M 12-27=MCS0-15(BW20) 28-43=MCS0-15(BW40)\n"); 222 223 if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) { 224 p += scnprintf(p, PAGE_SIZE - (p - (char *)page), 225 "44-53=MCS0-9(VHT:BW20) 54-63=MCS0-9(VHT:BW40) 64-73=MCS0-9(VHT:BW80)\n\n"); 226 } else { 227 p += scnprintf(p, PAGE_SIZE - (p - (char *)page), "\n"); 228 } 229 230 for (i = 0; i < NXPWIFI_MAX_RX_RATES; i++) { 231 value = atomic_read(&phist_data->rx_rate[i]); 232 if (value) 233 p += scnprintf(p, PAGE_SIZE - (p - (char *)page), 234 "rx_rate[%02d] = %d\n", i, value); 235 } 236 237 if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) { 238 for (i = NXPWIFI_MAX_RX_RATES; i < NXPWIFI_MAX_AC_RX_RATES; 239 i++) { 240 value = atomic_read(&phist_data->rx_rate[i]); 241 if (value) 242 p += scnprintf(p, PAGE_SIZE - (p - (char *)page), 243 "rx_rate[%02d] = %d\n", 244 i, value); 245 } 246 } 247 248 for (i = 0; i < NXPWIFI_MAX_SNR; i++) { 249 value = atomic_read(&phist_data->snr[i]); 250 if (value) 251 p += scnprintf(p, PAGE_SIZE - (p - (char *)page), 252 "snr[%02ddB] = %d\n", i, value); 253 } 254 for (i = 0; i < NXPWIFI_MAX_NOISE_FLR; i++) { 255 value = atomic_read(&phist_data->noise_flr[i]); 256 if (value) 257 p += scnprintf(p, PAGE_SIZE - (p - (char *)page), 258 "noise_flr[%02ddBm] = %d\n", 259 (int)(i - 128), value); 260 } 261 for (i = 0; i < NXPWIFI_MAX_SIG_STRENGTH; i++) { 262 value = atomic_read(&phist_data->sig_str[i]); 263 if (value) 264 p += scnprintf(p, PAGE_SIZE - (p - (char *)page), 265 "sig_strength[-%02ddBm] = %d\n", 266 i, value); 267 } 268 269 ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page, 270 (unsigned long)p - page); 271 272 free_and_exit: 273 free_page(page); 274 return ret; 275 } 276 277 static ssize_t 278 nxpwifi_histogram_write(struct file *file, const char __user *ubuf, 279 size_t count, loff_t *ppos) 280 { 281 struct nxpwifi_private *priv = (void *)file->private_data; 282 283 if (priv && priv->hist_data) 284 nxpwifi_hist_data_reset(priv); 285 return 0; 286 } 287 288 static struct nxpwifi_debug_info info; 289 290 /* debugfs "debug" read handler: dump adapter debug info and BA/reorder tables. */ 291 static ssize_t 292 nxpwifi_debug_read(struct file *file, char __user *ubuf, 293 size_t count, loff_t *ppos) 294 { 295 struct nxpwifi_private *priv = 296 (struct nxpwifi_private *)file->private_data; 297 unsigned long page = get_zeroed_page(GFP_KERNEL); 298 char *p = (char *)page; 299 ssize_t ret; 300 301 if (!p) 302 return -ENOMEM; 303 304 ret = nxpwifi_get_debug_info(priv, &info); 305 if (ret) 306 goto free_and_exit; 307 308 p += nxpwifi_debug_info_to_buffer(priv, p, PAGE_SIZE, &info); 309 310 ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page, 311 (unsigned long)p - page); 312 313 free_and_exit: 314 free_page(page); 315 return ret; 316 } 317 318 static u32 saved_reg_type, saved_reg_offset, saved_reg_value; 319 320 /* 321 * debugfs "regrdwr" write handler: parse <type offset value> and store for 322 * readback/IO. 323 */ 324 static ssize_t 325 nxpwifi_regrdwr_write(struct file *file, 326 const char __user *ubuf, size_t count, loff_t *ppos) 327 { 328 char *buf; 329 int ret; 330 u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX; 331 int rv; 332 333 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); 334 if (IS_ERR(buf)) 335 return PTR_ERR(buf); 336 337 rv = sscanf(buf, "%u %x %x", ®_type, ®_offset, ®_value); 338 339 if (rv != 3) { 340 ret = -EINVAL; 341 goto done; 342 } 343 344 if (reg_type == 0 || reg_offset == 0) { 345 ret = -EINVAL; 346 goto done; 347 } else { 348 saved_reg_type = reg_type; 349 saved_reg_offset = reg_offset; 350 saved_reg_value = reg_value; 351 ret = count; 352 } 353 done: 354 kfree(buf); 355 return ret; 356 } 357 358 /* 359 * debugfs "regrdwr" read handler: perform pending register read/write and return 360 * <type offset value>. 361 */ 362 static ssize_t 363 nxpwifi_regrdwr_read(struct file *file, char __user *ubuf, 364 size_t count, loff_t *ppos) 365 { 366 struct nxpwifi_private *priv = 367 (struct nxpwifi_private *)file->private_data; 368 unsigned long addr = get_zeroed_page(GFP_KERNEL); 369 char *buf = (char *)addr; 370 int pos = 0, ret = 0; 371 u32 reg_value; 372 373 if (!buf) 374 return -ENOMEM; 375 376 if (!saved_reg_type) { 377 /* No command has been given */ 378 pos += snprintf(buf, PAGE_SIZE, "0"); 379 goto done; 380 } 381 /* Set command has been given */ 382 if (saved_reg_value != UINT_MAX) { 383 ret = nxpwifi_reg_write(priv, saved_reg_type, saved_reg_offset, 384 saved_reg_value); 385 386 pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", 387 saved_reg_type, saved_reg_offset, 388 saved_reg_value); 389 390 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); 391 392 goto done; 393 } 394 /* Get command has been given */ 395 ret = nxpwifi_reg_read(priv, saved_reg_type, 396 saved_reg_offset, ®_value); 397 if (ret) { 398 ret = -EINVAL; 399 goto done; 400 } 401 402 pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", saved_reg_type, 403 saved_reg_offset, reg_value); 404 405 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); 406 407 done: 408 free_page(addr); 409 return ret; 410 } 411 412 /* debugfs "debug_mask" read handler: show driver debug mask. */ 413 414 static ssize_t 415 nxpwifi_debug_mask_read(struct file *file, char __user *ubuf, 416 size_t count, loff_t *ppos) 417 { 418 struct nxpwifi_private *priv = 419 (struct nxpwifi_private *)file->private_data; 420 unsigned long page = get_zeroed_page(GFP_KERNEL); 421 char *buf = (char *)page; 422 size_t ret = 0; 423 int pos = 0; 424 425 if (!buf) 426 return -ENOMEM; 427 428 pos += snprintf(buf, PAGE_SIZE, "debug mask=0x%08x\n", 429 priv->adapter->debug_mask); 430 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); 431 432 free_page(page); 433 return ret; 434 } 435 436 /* debugfs "debug_mask" write handler: set driver debug mask. */ 437 438 static ssize_t 439 nxpwifi_debug_mask_write(struct file *file, const char __user *ubuf, 440 size_t count, loff_t *ppos) 441 { 442 int ret; 443 unsigned long debug_mask; 444 struct nxpwifi_private *priv = (void *)file->private_data; 445 char *buf; 446 447 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); 448 if (IS_ERR(buf)) 449 return PTR_ERR(buf); 450 451 if (kstrtoul(buf, 0, &debug_mask)) { 452 ret = -EINVAL; 453 goto done; 454 } 455 456 priv->adapter->debug_mask = debug_mask; 457 ret = count; 458 done: 459 kfree(buf); 460 return ret; 461 } 462 463 /* debugfs "verext" write handler: select extended version string. */ 464 static ssize_t 465 nxpwifi_verext_write(struct file *file, const char __user *ubuf, 466 size_t count, loff_t *ppos) 467 { 468 int ret; 469 u32 versionstrsel; 470 struct nxpwifi_private *priv = (void *)file->private_data; 471 472 ret = kstrtou32_from_user(ubuf, count, 10, &versionstrsel); 473 if (ret) 474 return ret; 475 476 priv->versionstrsel = versionstrsel; 477 478 return count; 479 } 480 481 /* debugfs "verext" read handler: show extended version string. */ 482 static ssize_t 483 nxpwifi_verext_read(struct file *file, char __user *ubuf, 484 size_t count, loff_t *ppos) 485 { 486 struct nxpwifi_private *priv = 487 (struct nxpwifi_private *)file->private_data; 488 char buf[256]; 489 int ret; 490 491 nxpwifi_get_ver_ext(priv, priv->versionstrsel); 492 ret = snprintf(buf, sizeof(buf), "version string: %s\n", 493 priv->version_str); 494 495 return simple_read_from_buffer(ubuf, count, ppos, buf, ret); 496 } 497 498 /* debugfs "memrw" write handler: read/write firmware memory (addr, value). */ 499 static ssize_t 500 nxpwifi_memrw_write(struct file *file, const char __user *ubuf, size_t count, 501 loff_t *ppos) 502 { 503 int ret; 504 char cmd; 505 struct nxpwifi_ds_mem_rw mem_rw; 506 u16 cmd_action; 507 struct nxpwifi_private *priv = (void *)file->private_data; 508 char *buf; 509 510 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); 511 if (IS_ERR(buf)) 512 return PTR_ERR(buf); 513 514 ret = sscanf(buf, "%c %x %x", &cmd, &mem_rw.addr, &mem_rw.value); 515 if (ret != 3) { 516 ret = -EINVAL; 517 goto done; 518 } 519 520 if ((cmd == 'r') || (cmd == 'R')) { 521 cmd_action = HOST_ACT_GEN_GET; 522 mem_rw.value = 0; 523 } else if ((cmd == 'w') || (cmd == 'W')) { 524 cmd_action = HOST_ACT_GEN_SET; 525 } else { 526 ret = -EINVAL; 527 goto done; 528 } 529 530 memcpy(&priv->mem_rw, &mem_rw, sizeof(mem_rw)); 531 ret = nxpwifi_send_cmd(priv, HOST_CMD_MEM_ACCESS, cmd_action, 0, 532 &mem_rw, true); 533 if (!ret) 534 ret = count; 535 536 done: 537 kfree(buf); 538 return ret; 539 } 540 541 /* debugfs "memrw" read handler: show last memory access result. */ 542 static ssize_t 543 nxpwifi_memrw_read(struct file *file, char __user *ubuf, 544 size_t count, loff_t *ppos) 545 { 546 struct nxpwifi_private *priv = (void *)file->private_data; 547 unsigned long addr = get_zeroed_page(GFP_KERNEL); 548 char *buf = (char *)addr; 549 int ret, pos = 0; 550 551 if (!buf) 552 return -ENOMEM; 553 554 pos += snprintf(buf, PAGE_SIZE, "0x%x 0x%x\n", priv->mem_rw.addr, 555 priv->mem_rw.value); 556 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); 557 558 free_page(addr); 559 return ret; 560 } 561 562 static u32 saved_offset = -1, saved_bytes = -1; 563 564 /* debugfs "rdeeprom" write handler: set EEPROM offset/length to read. */ 565 static ssize_t 566 nxpwifi_rdeeprom_write(struct file *file, 567 const char __user *ubuf, size_t count, loff_t *ppos) 568 { 569 char *buf; 570 int ret = 0; 571 int offset = -1, bytes = -1; 572 int rv; 573 574 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); 575 if (IS_ERR(buf)) 576 return PTR_ERR(buf); 577 578 rv = sscanf(buf, "%d %d", &offset, &bytes); 579 580 if (rv != 2) { 581 ret = -EINVAL; 582 goto done; 583 } 584 585 if (offset == -1 || bytes == -1) { 586 ret = -EINVAL; 587 goto done; 588 } else { 589 saved_offset = offset; 590 saved_bytes = bytes; 591 ret = count; 592 } 593 done: 594 kfree(buf); 595 return ret; 596 } 597 598 /* debugfs "rdeeprom" read handler: dump EEPROM bytes from saved offset/length. */ 599 static ssize_t 600 nxpwifi_rdeeprom_read(struct file *file, char __user *ubuf, 601 size_t count, loff_t *ppos) 602 { 603 struct nxpwifi_private *priv = 604 (struct nxpwifi_private *)file->private_data; 605 unsigned long addr = get_zeroed_page(GFP_KERNEL); 606 char *buf = (char *)addr; 607 int pos, ret, i; 608 u8 value[MAX_EEPROM_DATA]; 609 610 if (!buf) 611 return -ENOMEM; 612 613 if (saved_offset == -1) { 614 /* No command has been given */ 615 pos = snprintf(buf, PAGE_SIZE, "0"); 616 goto done; 617 } 618 619 /* Get command has been given */ 620 ret = nxpwifi_eeprom_read(priv, (u16)saved_offset, 621 (u16)saved_bytes, value); 622 if (ret) { 623 ret = -EINVAL; 624 goto out_free; 625 } 626 627 pos = snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes); 628 629 for (i = 0; i < saved_bytes; i++) 630 pos += scnprintf(buf + pos, PAGE_SIZE - pos, "%d ", value[i]); 631 632 done: 633 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); 634 out_free: 635 free_page(addr); 636 return ret; 637 } 638 639 /* 640 * debugfs "hscfg" write handler: configure host-sleep (conditions/gpio/gap) or 641 * cancel. 642 */ 643 static ssize_t 644 nxpwifi_hscfg_write(struct file *file, const char __user *ubuf, 645 size_t count, loff_t *ppos) 646 { 647 struct nxpwifi_private *priv = (void *)file->private_data; 648 char *buf; 649 int ret, arg_num; 650 struct nxpwifi_ds_hs_cfg hscfg; 651 int conditions = HS_CFG_COND_DEF; 652 u32 gpio = HS_CFG_GPIO_DEF, gap = HS_CFG_GAP_DEF; 653 654 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); 655 if (IS_ERR(buf)) 656 return PTR_ERR(buf); 657 658 arg_num = sscanf(buf, "%d %x %x", &conditions, &gpio, &gap); 659 660 memset(&hscfg, 0, sizeof(struct nxpwifi_ds_hs_cfg)); 661 662 if (arg_num > 3) { 663 nxpwifi_dbg(priv->adapter, ERROR, 664 "Too many arguments\n"); 665 ret = -EINVAL; 666 goto done; 667 } 668 669 if (arg_num >= 1 && arg_num < 3) 670 nxpwifi_set_hs_params(priv, HOST_ACT_GEN_GET, 671 NXPWIFI_SYNC_CMD, &hscfg); 672 673 if (arg_num) { 674 if (conditions == HS_CFG_CANCEL) { 675 nxpwifi_cancel_hs(priv, NXPWIFI_ASYNC_CMD); 676 ret = count; 677 goto done; 678 } 679 hscfg.conditions = conditions; 680 } 681 if (arg_num >= 2) 682 hscfg.gpio = gpio; 683 if (arg_num == 3) 684 hscfg.gap = gap; 685 686 hscfg.is_invoke_hostcmd = false; 687 nxpwifi_set_hs_params(priv, HOST_ACT_GEN_SET, 688 NXPWIFI_SYNC_CMD, &hscfg); 689 690 nxpwifi_enable_hs(priv->adapter); 691 clear_bit(NXPWIFI_IS_HS_ENABLING, &priv->adapter->work_flags); 692 ret = count; 693 done: 694 kfree(buf); 695 return ret; 696 } 697 698 /* debugfs "hscfg" read handler: show current host-sleep configuration. */ 699 static ssize_t 700 nxpwifi_hscfg_read(struct file *file, char __user *ubuf, 701 size_t count, loff_t *ppos) 702 { 703 struct nxpwifi_private *priv = (void *)file->private_data; 704 unsigned long addr = get_zeroed_page(GFP_KERNEL); 705 char *buf = (char *)addr; 706 int pos, ret; 707 struct nxpwifi_ds_hs_cfg hscfg; 708 709 if (!buf) 710 return -ENOMEM; 711 712 nxpwifi_set_hs_params(priv, HOST_ACT_GEN_GET, 713 NXPWIFI_SYNC_CMD, &hscfg); 714 715 pos = snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", hscfg.conditions, 716 hscfg.gpio, hscfg.gap); 717 718 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); 719 720 free_page(addr); 721 return ret; 722 } 723 724 static ssize_t 725 nxpwifi_timeshare_coex_read(struct file *file, char __user *ubuf, 726 size_t count, loff_t *ppos) 727 { 728 struct nxpwifi_private *priv = file->private_data; 729 char buf[3]; 730 bool timeshare_coex; 731 int ret; 732 unsigned int len; 733 734 if (priv->adapter->fw_api_ver != NXPWIFI_FW_V15) 735 return -EOPNOTSUPP; 736 737 ret = nxpwifi_send_cmd(priv, HOST_CMD_ROBUST_COEX, 738 HOST_ACT_GEN_GET, 0, ×hare_coex, true); 739 if (ret) 740 return ret; 741 742 len = sprintf(buf, "%d\n", timeshare_coex); 743 return simple_read_from_buffer(ubuf, count, ppos, buf, len); 744 } 745 746 static ssize_t 747 nxpwifi_timeshare_coex_write(struct file *file, const char __user *ubuf, 748 size_t count, loff_t *ppos) 749 { 750 bool timeshare_coex; 751 struct nxpwifi_private *priv = file->private_data; 752 int ret; 753 754 if (priv->adapter->fw_api_ver != NXPWIFI_FW_V15) 755 return -EOPNOTSUPP; 756 757 ret = kstrtobool_from_user(ubuf, count, ×hare_coex); 758 if (ret) 759 return ret; 760 761 ret = nxpwifi_send_cmd(priv, HOST_CMD_ROBUST_COEX, 762 HOST_ACT_GEN_SET, 0, ×hare_coex, true); 763 if (ret) 764 return ret; 765 else 766 return count; 767 } 768 769 static ssize_t 770 nxpwifi_reset_write(struct file *file, 771 const char __user *ubuf, size_t count, loff_t *ppos) 772 { 773 struct nxpwifi_private *priv = file->private_data; 774 struct nxpwifi_adapter *adapter = priv->adapter; 775 bool result; 776 int rc; 777 778 rc = kstrtobool_from_user(ubuf, count, &result); 779 if (rc) 780 return rc; 781 782 if (!result) 783 return -EINVAL; 784 785 if (adapter->if_ops.card_reset) { 786 nxpwifi_dbg(adapter, INFO, "Resetting per request\n"); 787 adapter->if_ops.card_reset(adapter); 788 } 789 790 return count; 791 } 792 793 static ssize_t 794 nxpwifi_fake_radar_detect_write(struct file *file, 795 const char __user *ubuf, 796 size_t count, loff_t *ppos) 797 { 798 struct nxpwifi_private *priv = file->private_data; 799 struct nxpwifi_adapter *adapter = priv->adapter; 800 bool result; 801 int rc; 802 803 rc = kstrtobool_from_user(ubuf, count, &result); 804 if (rc) 805 return rc; 806 807 if (!result) 808 return -EINVAL; 809 810 if (priv->wdev.links[0].cac_started) { 811 nxpwifi_dbg(adapter, MSG, 812 "Generate fake radar detected during CAC\n"); 813 if (nxpwifi_stop_radar_detection(priv, &priv->dfs_chandef)) 814 nxpwifi_dbg(adapter, ERROR, 815 "Failed to stop CAC in FW\n"); 816 wiphy_delayed_work_cancel(priv->adapter->wiphy, &priv->dfs_cac_work); 817 cfg80211_cac_event(priv->netdev, &priv->dfs_chandef, 818 NL80211_RADAR_CAC_ABORTED, GFP_KERNEL, 0); 819 cfg80211_radar_event(adapter->wiphy, &priv->dfs_chandef, 820 GFP_KERNEL); 821 } else { 822 if (priv->bss_chandef.chan->dfs_cac_ms) { 823 nxpwifi_dbg(adapter, MSG, 824 "Generate fake radar detected\n"); 825 cfg80211_radar_event(adapter->wiphy, 826 &priv->dfs_chandef, 827 GFP_KERNEL); 828 } 829 } 830 831 return count; 832 } 833 834 static ssize_t 835 nxpwifi_netmon_write(struct file *file, const char __user *ubuf, 836 size_t count, loff_t *ppos) 837 { 838 int ret; 839 struct nxpwifi_802_11_net_monitor netmon_cfg; 840 struct nxpwifi_private *priv = (void *)file->private_data; 841 char *buf; 842 843 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); 844 if (IS_ERR(buf)) 845 return PTR_ERR(buf); 846 memset(&netmon_cfg, 0, sizeof(struct nxpwifi_802_11_net_monitor)); 847 ret = sscanf(buf, "%u %u %u %u %u", 848 &netmon_cfg.enable_net_mon, 849 &netmon_cfg.filter_flag, 850 &netmon_cfg.band, 851 &netmon_cfg.channel, 852 &netmon_cfg.chan_bandwidth); 853 854 ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_NET_MONITOR, 855 HOST_ACT_GEN_SET, 0, &netmon_cfg, true); 856 857 if (!ret) 858 ret = count; 859 860 kfree(buf); 861 return ret; 862 } 863 864 static ssize_t 865 nxpwifi_twt_setup_write(struct file *file, const char __user *ubuf, 866 size_t count, loff_t *ppos) 867 { 868 int ret; 869 struct nxpwifi_twt_cfg twt_cfg; 870 struct nxpwifi_private *priv = (void *)file->private_data; 871 char *buf; 872 u16 twt_mantissa, bcn_miss_threshold; 873 874 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); 875 if (IS_ERR(buf)) 876 return PTR_ERR(buf); 877 878 ret = sscanf(buf, "%hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hu %hhu %hu", 879 &twt_cfg.param.twt_setup.implicit, 880 &twt_cfg.param.twt_setup.announced, 881 &twt_cfg.param.twt_setup.trigger_enabled, 882 &twt_cfg.param.twt_setup.twt_info_disabled, 883 &twt_cfg.param.twt_setup.negotiation_type, 884 &twt_cfg.param.twt_setup.twt_wakeup_duration, 885 &twt_cfg.param.twt_setup.flow_identifier, 886 &twt_cfg.param.twt_setup.hard_constraint, 887 &twt_cfg.param.twt_setup.twt_exponent, 888 &twt_mantissa, 889 &twt_cfg.param.twt_setup.twt_request, 890 &bcn_miss_threshold); 891 892 twt_cfg.param.twt_setup.twt_mantissa = cpu_to_le16(twt_mantissa); 893 twt_cfg.param.twt_setup.bcn_miss_threshold = cpu_to_le16(bcn_miss_threshold); 894 twt_cfg.sub_id = NXPWIFI_11AX_TWT_SETUP_SUBID; 895 ret = nxpwifi_send_cmd(priv, HOST_CMD_TWT_CFG, HOST_ACT_GEN_SET, 0, 896 &twt_cfg, true); 897 if (!ret) 898 ret = count; 899 900 kfree(buf); 901 return ret; 902 } 903 904 static ssize_t 905 nxpwifi_twt_teardown_write(struct file *file, const char __user *ubuf, 906 size_t count, loff_t *ppos) 907 { 908 int ret; 909 struct nxpwifi_twt_cfg twt_cfg; 910 struct nxpwifi_private *priv = (void *)file->private_data; 911 char *buf; 912 913 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); 914 if (IS_ERR(buf)) 915 return PTR_ERR(buf); 916 917 ret = sscanf(buf, "%hhu %hhu %hhu", 918 &twt_cfg.param.twt_teardown.flow_identifier, 919 &twt_cfg.param.twt_teardown.negotiation_type, 920 &twt_cfg.param.twt_teardown.teardown_all_twt); 921 922 twt_cfg.sub_id = NXPWIFI_11AX_TWT_TEARDOWN_SUBID; 923 ret = nxpwifi_send_cmd(priv, HOST_CMD_TWT_CFG, HOST_ACT_GEN_SET, 0, 924 &twt_cfg, true); 925 926 if (!ret) 927 ret = count; 928 929 kfree(buf); 930 return ret; 931 } 932 933 static ssize_t 934 nxpwifi_twt_report_read(struct file *file, char __user *ubuf, 935 size_t count, loff_t *ppos) 936 { 937 struct nxpwifi_private *priv = 938 (struct nxpwifi_private *)file->private_data; 939 unsigned long page = get_zeroed_page(GFP_KERNEL); 940 char *p = (char *)page; 941 ssize_t ret; 942 struct nxpwifi_twt_cfg twt_cfg; 943 u8 num, i, j; 944 945 if (!p) 946 return -ENOMEM; 947 948 twt_cfg.sub_id = NXPWIFI_11AX_TWT_REPORT_SUBID; 949 ret = nxpwifi_send_cmd(priv, HOST_CMD_TWT_CFG, HOST_ACT_GEN_GET, 0, 950 &twt_cfg, true); 951 if (ret) 952 goto done; 953 num = twt_cfg.param.twt_report.length / NXPWIFI_BTWT_REPORT_LEN; 954 num = num <= NXPWIFI_BTWT_REPORT_MAX_NUM ? num : NXPWIFI_BTWT_REPORT_MAX_NUM; 955 p += sprintf(p, "\ntwt_report len %hhu, num %hhu, twt_report_info:\n", 956 twt_cfg.param.twt_report.length, num); 957 for (i = 0; i < num; i++) { 958 p += sprintf(p, "id[%hu]:\r\n", i); 959 for (j = 0; j < NXPWIFI_BTWT_REPORT_LEN; j++) { 960 p += sprintf(p, 961 " 0x%02x", 962 twt_cfg.param.twt_report.data[i * NXPWIFI_BTWT_REPORT_LEN + j]); 963 } 964 p += sprintf(p, "\r\n"); 965 } 966 967 ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page, 968 (unsigned long)p - page); 969 970 done: 971 free_page(page); 972 return ret; 973 } 974 975 static ssize_t 976 nxpwifi_twt_information_write(struct file *file, const char __user *ubuf, 977 size_t count, loff_t *ppos) 978 { 979 int ret; 980 struct nxpwifi_twt_cfg twt_cfg; 981 struct nxpwifi_private *priv = (void *)file->private_data; 982 char *buf; 983 u32 suspend_duration; 984 985 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); 986 if (IS_ERR(buf)) 987 return PTR_ERR(buf); 988 989 ret = sscanf(buf, "%hhu %u", 990 &twt_cfg.param.twt_information.flow_identifier, &suspend_duration); 991 twt_cfg.param.twt_information.suspend_duration = cpu_to_le32(suspend_duration); 992 993 twt_cfg.sub_id = NXPWIFI_11AX_TWT_INFORMATION_SUBID; 994 ret = nxpwifi_send_cmd(priv, HOST_CMD_TWT_CFG, HOST_ACT_GEN_SET, 0, 995 &twt_cfg, true); 996 997 if (!ret) 998 ret = count; 999 1000 kfree(buf); 1001 return ret; 1002 } 1003 1004 #define NXPWIFI_DFS_ADD_FILE(name) debugfs_create_file(#name, 0644, \ 1005 priv->dfs_dev_dir, priv, \ 1006 &nxpwifi_dfs_##name##_fops) 1007 1008 #define NXPWIFI_DFS_FILE_OPS(name) \ 1009 static const struct file_operations nxpwifi_dfs_##name##_fops = { \ 1010 .read = nxpwifi_##name##_read, \ 1011 .write = nxpwifi_##name##_write, \ 1012 .open = simple_open, \ 1013 } 1014 1015 #define NXPWIFI_DFS_FILE_READ_OPS(name) \ 1016 static const struct file_operations nxpwifi_dfs_##name##_fops = { \ 1017 .read = nxpwifi_##name##_read, \ 1018 .open = simple_open, \ 1019 } 1020 1021 #define NXPWIFI_DFS_FILE_WRITE_OPS(name) \ 1022 static const struct file_operations nxpwifi_dfs_##name##_fops = { \ 1023 .write = nxpwifi_##name##_write, \ 1024 .open = simple_open, \ 1025 } 1026 1027 NXPWIFI_DFS_FILE_READ_OPS(info); 1028 NXPWIFI_DFS_FILE_READ_OPS(debug); 1029 NXPWIFI_DFS_FILE_READ_OPS(getlog); 1030 NXPWIFI_DFS_FILE_OPS(regrdwr); 1031 NXPWIFI_DFS_FILE_OPS(rdeeprom); 1032 NXPWIFI_DFS_FILE_OPS(memrw); 1033 NXPWIFI_DFS_FILE_OPS(hscfg); 1034 NXPWIFI_DFS_FILE_OPS(histogram); 1035 NXPWIFI_DFS_FILE_OPS(debug_mask); 1036 NXPWIFI_DFS_FILE_OPS(timeshare_coex); 1037 NXPWIFI_DFS_FILE_WRITE_OPS(reset); 1038 NXPWIFI_DFS_FILE_WRITE_OPS(fake_radar_detect); 1039 NXPWIFI_DFS_FILE_OPS(verext); 1040 NXPWIFI_DFS_FILE_WRITE_OPS(netmon); 1041 NXPWIFI_DFS_FILE_WRITE_OPS(twt_setup); 1042 NXPWIFI_DFS_FILE_WRITE_OPS(twt_teardown); 1043 NXPWIFI_DFS_FILE_READ_OPS(twt_report); 1044 NXPWIFI_DFS_FILE_WRITE_OPS(twt_information); 1045 1046 /* Create per-netdev debugfs directory and files. */ 1047 void 1048 nxpwifi_dev_debugfs_init(struct nxpwifi_private *priv) 1049 { 1050 if (!nxpwifi_dfs_dir || !priv) 1051 return; 1052 1053 priv->dfs_dev_dir = debugfs_create_dir(priv->netdev->name, 1054 nxpwifi_dfs_dir); 1055 1056 NXPWIFI_DFS_ADD_FILE(info); 1057 NXPWIFI_DFS_ADD_FILE(debug); 1058 NXPWIFI_DFS_ADD_FILE(getlog); 1059 NXPWIFI_DFS_ADD_FILE(regrdwr); 1060 NXPWIFI_DFS_ADD_FILE(rdeeprom); 1061 1062 NXPWIFI_DFS_ADD_FILE(memrw); 1063 NXPWIFI_DFS_ADD_FILE(hscfg); 1064 NXPWIFI_DFS_ADD_FILE(histogram); 1065 NXPWIFI_DFS_ADD_FILE(debug_mask); 1066 NXPWIFI_DFS_ADD_FILE(timeshare_coex); 1067 NXPWIFI_DFS_ADD_FILE(reset); 1068 NXPWIFI_DFS_ADD_FILE(fake_radar_detect); 1069 NXPWIFI_DFS_ADD_FILE(verext); 1070 NXPWIFI_DFS_ADD_FILE(netmon); 1071 NXPWIFI_DFS_ADD_FILE(twt_setup); 1072 NXPWIFI_DFS_ADD_FILE(twt_teardown); 1073 NXPWIFI_DFS_ADD_FILE(twt_report); 1074 NXPWIFI_DFS_ADD_FILE(twt_information); 1075 } 1076 1077 /* Remove per-netdev debugfs directory and files. */ 1078 void 1079 nxpwifi_dev_debugfs_remove(struct nxpwifi_private *priv) 1080 { 1081 if (!priv) 1082 return; 1083 1084 debugfs_remove_recursive(priv->dfs_dev_dir); 1085 } 1086 1087 /* Create top-level debugfs directory. */ 1088 void 1089 nxpwifi_debugfs_init(void) 1090 { 1091 if (!nxpwifi_dfs_dir) 1092 nxpwifi_dfs_dir = debugfs_create_dir("nxpwifi", NULL); 1093 } 1094 1095 /* Remove top-level debugfs directory. */ 1096 void 1097 nxpwifi_debugfs_remove(void) 1098 { 1099 debugfs_remove(nxpwifi_dfs_dir); 1100 } 1101