1/* 2 * NXP Wireless LAN device driver: debugfs 3 * 4 * Copyright 2011-2020 NXP 5 * 6 * This software file (the "File") is distributed by NXP 7 * under the terms of the GNU General Public License Version 2, June 1991 8 * (the "License"). You may use, redistribute and/or modify this File in 9 * accordance with the terms and conditions of the License, a copy of which 10 * is available by writing to the Free Software Foundation, Inc., 11 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the 12 * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 13 * 14 * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE 16 * ARE EXPRESSLY DISCLAIMED. The License provides additional details about 17 * this warranty disclaimer. 18 */ 19 20#include <linux/debugfs.h> 21 22#include "main.h" 23#include "11n.h" 24 25 26static struct dentry *mwifiex_dfs_dir; 27 28static char *bss_modes[] = { 29 "UNSPECIFIED", 30 "ADHOC", 31 "STATION", 32 "AP", 33 "AP_VLAN", 34 "WDS", 35 "MONITOR", 36 "MESH_POINT", 37 "P2P_CLIENT", 38 "P2P_GO", 39 "P2P_DEVICE", 40}; 41 42/* 43 * Proc info file read handler. 44 * 45 * This function is called when the 'info' file is opened for reading. 46 * It prints the following driver related information - 47 * - Driver name 48 * - Driver version 49 * - Driver extended version 50 * - Interface name 51 * - BSS mode 52 * - Media state (connected or disconnected) 53 * - MAC address 54 * - Total number of Tx bytes 55 * - Total number of Rx bytes 56 * - Total number of Tx packets 57 * - Total number of Rx packets 58 * - Total number of dropped Tx packets 59 * - Total number of dropped Rx packets 60 * - Total number of corrupted Tx packets 61 * - Total number of corrupted Rx packets 62 * - Carrier status (on or off) 63 * - Tx queue status (started or stopped) 64 * 65 * For STA mode drivers, it also prints the following extra - 66 * - ESSID 67 * - BSSID 68 * - Channel 69 * - Region code 70 * - Multicast count 71 * - Multicast addresses 72 */ 73static ssize_t 74mwifiex_info_read(struct file *file, char __user *ubuf, 75 size_t count, loff_t *ppos) 76{ 77 struct mwifiex_private *priv = 78 (struct mwifiex_private *) file->private_data; 79 struct net_device *netdev = priv->netdev; 80 struct netdev_hw_addr *ha; 81 struct netdev_queue *txq; 82 unsigned long page = get_zeroed_page(GFP_KERNEL); 83 char *p = (char *) page, fmt[64]; 84 struct mwifiex_bss_info info; 85 ssize_t ret; 86 int i = 0; 87 88 if (!p) 89 return -ENOMEM; 90 91 memset(&info, 0, sizeof(info)); 92 ret = mwifiex_get_bss_info(priv, &info); 93 if (ret) 94 goto free_and_exit; 95 96 mwifiex_drv_get_driver_version(priv->adapter, fmt, sizeof(fmt) - 1); 97 98 mwifiex_get_ver_ext(priv, 0); 99 100 p += sprintf(p, "driver_name = " "\"mwifiex\"\n"); 101 p += sprintf(p, "driver_version = %s", fmt); 102 p += sprintf(p, "\nverext = %s", priv->version_str); 103 p += sprintf(p, "\ninterface_name=\"%s\"\n", netdev->name); 104 105 if (info.bss_mode >= ARRAY_SIZE(bss_modes)) 106 p += sprintf(p, "bss_mode=\"%d\"\n", info.bss_mode); 107 else 108 p += sprintf(p, "bss_mode=\"%s\"\n", bss_modes[info.bss_mode]); 109 110 p += sprintf(p, "media_state=\"%s\"\n", 111 (!priv->media_connected ? "Disconnected" : "Connected")); 112 p += sprintf(p, "mac_address=\"%pM\"\n", netdev->dev_addr); 113 114 if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) { 115 p += sprintf(p, "multicast_count=\"%d\"\n", 116 netdev_mc_count(netdev)); 117 p += sprintf(p, "essid=\"%.*s\"\n", info.ssid.ssid_len, 118 info.ssid.ssid); 119 p += sprintf(p, "bssid=\"%pM\"\n", info.bssid); 120 p += sprintf(p, "channel=\"%d\"\n", (int) info.bss_chan); 121 p += sprintf(p, "country_code = \"%s\"\n", info.country_code); 122 p += sprintf(p, "region_code=\"0x%x\"\n", 123 priv->adapter->region_code); 124 125 netdev_for_each_mc_addr(ha, netdev) 126 p += sprintf(p, "multicast_address[%d]=\"%pM\"\n", 127 i++, ha->addr); 128 } 129 130 p += sprintf(p, "num_tx_bytes = %lu\n", priv->stats.tx_bytes); 131 p += sprintf(p, "num_rx_bytes = %lu\n", priv->stats.rx_bytes); 132 p += sprintf(p, "num_tx_pkts = %lu\n", priv->stats.tx_packets); 133 p += sprintf(p, "num_rx_pkts = %lu\n", priv->stats.rx_packets); 134 p += sprintf(p, "num_tx_pkts_dropped = %lu\n", priv->stats.tx_dropped); 135 p += sprintf(p, "num_rx_pkts_dropped = %lu\n", priv->stats.rx_dropped); 136 p += sprintf(p, "num_tx_pkts_err = %lu\n", priv->stats.tx_errors); 137 p += sprintf(p, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors); 138 p += sprintf(p, "carrier %s\n", ((netif_carrier_ok(priv->netdev)) 139 ? "on" : "off")); 140 p += sprintf(p, "tx queue"); 141 for (i = 0; i < netdev->num_tx_queues; i++) { 142 txq = netdev_get_tx_queue(netdev, i); 143 p += sprintf(p, " %d:%s", i, netif_tx_queue_stopped(txq) ? 144 "stopped" : "started"); 145 } 146 p += sprintf(p, "\n"); 147 148 ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, 149 (unsigned long) p - page); 150 151free_and_exit: 152 free_page(page); 153 return ret; 154} 155 156/* 157 * Proc getlog file read handler. 158 * 159 * This function is called when the 'getlog' file is opened for reading 160 * It prints the following log information - 161 * - Number of multicast Tx frames 162 * - Number of failed packets 163 * - Number of Tx retries 164 * - Number of multicast Tx retries 165 * - Number of duplicate frames 166 * - Number of RTS successes 167 * - Number of RTS failures 168 * - Number of ACK failures 169 * - Number of fragmented Rx frames 170 * - Number of multicast Rx frames 171 * - Number of FCS errors 172 * - Number of Tx frames 173 * - WEP ICV error counts 174 * - Number of received beacons 175 * - Number of missed beacons 176 */ 177static ssize_t 178mwifiex_getlog_read(struct file *file, char __user *ubuf, 179 size_t count, loff_t *ppos) 180{ 181 struct mwifiex_private *priv = 182 (struct mwifiex_private *) file->private_data; 183 unsigned long page = get_zeroed_page(GFP_KERNEL); 184 char *p = (char *) page; 185 ssize_t ret; 186 struct mwifiex_ds_get_stats stats; 187 188 if (!p) 189 return -ENOMEM; 190 191 memset(&stats, 0, sizeof(stats)); 192 ret = mwifiex_get_stats_info(priv, &stats); 193 if (ret) 194 goto free_and_exit; 195 196 p += sprintf(p, "\n" 197 "mcasttxframe %u\n" 198 "failed %u\n" 199 "retry %u\n" 200 "multiretry %u\n" 201 "framedup %u\n" 202 "rtssuccess %u\n" 203 "rtsfailure %u\n" 204 "ackfailure %u\n" 205 "rxfrag %u\n" 206 "mcastrxframe %u\n" 207 "fcserror %u\n" 208 "txframe %u\n" 209 "wepicverrcnt-1 %u\n" 210 "wepicverrcnt-2 %u\n" 211 "wepicverrcnt-3 %u\n" 212 "wepicverrcnt-4 %u\n" 213 "bcn_rcv_cnt %u\n" 214 "bcn_miss_cnt %u\n", 215 stats.mcast_tx_frame, 216 stats.failed, 217 stats.retry, 218 stats.multi_retry, 219 stats.frame_dup, 220 stats.rts_success, 221 stats.rts_failure, 222 stats.ack_failure, 223 stats.rx_frag, 224 stats.mcast_rx_frame, 225 stats.fcs_error, 226 stats.tx_frame, 227 stats.wep_icv_error[0], 228 stats.wep_icv_error[1], 229 stats.wep_icv_error[2], 230 stats.wep_icv_error[3], 231 stats.bcn_rcv_cnt, 232 stats.bcn_miss_cnt); 233 234 235 ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, 236 (unsigned long) p - page); 237 238free_and_exit: 239 free_page(page); 240 return ret; 241} 242 243/* Sysfs histogram file read handler. 244 * 245 * This function is called when the 'histogram' file is opened for reading 246 * It prints the following histogram information - 247 * - Number of histogram samples 248 * - Receive packet number of each rx_rate 249 * - Receive packet number of each snr 250 * - Receive packet number of each nosie_flr 251 * - Receive packet number of each signal streath 252 */ 253static ssize_t 254mwifiex_histogram_read(struct file *file, char __user *ubuf, 255 size_t count, loff_t *ppos) 256{ 257 struct mwifiex_private *priv = 258 (struct mwifiex_private *)file->private_data; 259 ssize_t ret; 260 struct mwifiex_histogram_data *phist_data; 261 int i, value; 262 unsigned long page = get_zeroed_page(GFP_KERNEL); 263 char *p = (char *)page; 264 265 if (!p) 266 return -ENOMEM; 267 268 if (!priv || !priv->hist_data) { 269 ret = -EFAULT; 270 goto free_and_exit; 271 } 272 273 phist_data = priv->hist_data; 274 275 p += sprintf(p, "\n" 276 "total samples = %d\n", 277 atomic_read(&phist_data->num_samples)); 278 279 p += sprintf(p, 280 "rx rates (in Mbps): 0=1M 1=2M 2=5.5M 3=11M 4=6M 5=9M 6=12M\n" 281 "7=18M 8=24M 9=36M 10=48M 11=54M 12-27=MCS0-15(BW20) 28-43=MCS0-15(BW40)\n"); 282 283 if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) { 284 p += sprintf(p, 285 "44-53=MCS0-9(VHT:BW20) 54-63=MCS0-9(VHT:BW40) 64-73=MCS0-9(VHT:BW80)\n\n"); 286 } else { 287 p += sprintf(p, "\n"); 288 } 289 290 for (i = 0; i < MWIFIEX_MAX_RX_RATES; i++) { 291 value = atomic_read(&phist_data->rx_rate[i]); 292 if (value) 293 p += sprintf(p, "rx_rate[%02d] = %d\n", i, value); 294 } 295 296 if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) { 297 for (i = MWIFIEX_MAX_RX_RATES; i < MWIFIEX_MAX_AC_RX_RATES; 298 i++) { 299 value = atomic_read(&phist_data->rx_rate[i]); 300 if (value) 301 p += sprintf(p, "rx_rate[%02d] = %d\n", 302 i, value); 303 } 304 } 305 306 for (i = 0; i < MWIFIEX_MAX_SNR; i++) { 307 value = atomic_read(&phist_data->snr[i]); 308 if (value) 309 p += sprintf(p, "snr[%02ddB] = %d\n", i, value); 310 } 311 for (i = 0; i < MWIFIEX_MAX_NOISE_FLR; i++) { 312 value = atomic_read(&phist_data->noise_flr[i]); 313 if (value) 314 p += sprintf(p, "noise_flr[%02ddBm] = %d\n", 315 (int)(i-128), value); 316 } 317 for (i = 0; i < MWIFIEX_MAX_SIG_STRENGTH; i++) { 318 value = atomic_read(&phist_data->sig_str[i]); 319 if (value) 320 p += sprintf(p, "sig_strength[-%02ddBm] = %d\n", 321 i, value); 322 } 323 324 ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page, 325 (unsigned long)p - page); 326 327free_and_exit: 328 free_page(page); 329 return ret; 330} 331 332static ssize_t 333mwifiex_histogram_write(struct file *file, const char __user *ubuf, 334 size_t count, loff_t *ppos) 335{ 336 struct mwifiex_private *priv = (void *)file->private_data; 337 338 if (priv && priv->hist_data) 339 mwifiex_hist_data_reset(priv); 340 return 0; 341} 342 343static struct mwifiex_debug_info info; 344 345/* 346 * Proc debug file read handler. 347 * 348 * This function is called when the 'debug' file is opened for reading 349 * It prints the following log information - 350 * - Interrupt count 351 * - WMM AC VO packets count 352 * - WMM AC VI packets count 353 * - WMM AC BE packets count 354 * - WMM AC BK packets count 355 * - Maximum Tx buffer size 356 * - Tx buffer size 357 * - Current Tx buffer size 358 * - Power Save mode 359 * - Power Save state 360 * - Deep Sleep status 361 * - Device wakeup required status 362 * - Number of wakeup tries 363 * - Host Sleep configured status 364 * - Host Sleep activated status 365 * - Number of Tx timeouts 366 * - Number of command timeouts 367 * - Last timed out command ID 368 * - Last timed out command action 369 * - Last command ID 370 * - Last command action 371 * - Last command index 372 * - Last command response ID 373 * - Last command response index 374 * - Last event 375 * - Last event index 376 * - Number of host to card command failures 377 * - Number of sleep confirm command failures 378 * - Number of host to card data failure 379 * - Number of deauthentication events 380 * - Number of disassociation events 381 * - Number of link lost events 382 * - Number of deauthentication commands 383 * - Number of association success commands 384 * - Number of association failure commands 385 * - Number of commands sent 386 * - Number of data packets sent 387 * - Number of command responses received 388 * - Number of events received 389 * - Tx BA stream table (TID, RA) 390 * - Rx reorder table (TID, TA, Start window, Window size, Buffer) 391 */ 392static ssize_t 393mwifiex_debug_read(struct file *file, char __user *ubuf, 394 size_t count, loff_t *ppos) 395{ 396 struct mwifiex_private *priv = 397 (struct mwifiex_private *) file->private_data; 398 unsigned long page = get_zeroed_page(GFP_KERNEL); 399 char *p = (char *) page; 400 ssize_t ret; 401 402 if (!p) 403 return -ENOMEM; 404 405 ret = mwifiex_get_debug_info(priv, &info); 406 if (ret) 407 goto free_and_exit; 408 409 p += mwifiex_debug_info_to_buffer(priv, p, &info); 410 411 ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, 412 (unsigned long) p - page); 413 414free_and_exit: 415 free_page(page); 416 return ret; 417} 418 419static u32 saved_reg_type, saved_reg_offset, saved_reg_value; 420 421/* 422 * Proc regrdwr file write handler. 423 * 424 * This function is called when the 'regrdwr' file is opened for writing 425 * 426 * This function can be used to write to a register. 427 */ 428static ssize_t 429mwifiex_regrdwr_write(struct file *file, 430 const char __user *ubuf, size_t count, loff_t *ppos) 431{ 432 char *buf; 433 int ret; 434 u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX; 435 436 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); 437 if (IS_ERR(buf)) 438 return PTR_ERR(buf); 439 440 sscanf(buf, "%u %x %x", ®_type, ®_offset, ®_value); 441 442 if (reg_type == 0 || reg_offset == 0) { 443 ret = -EINVAL; 444 goto done; 445 } else { 446 saved_reg_type = reg_type; 447 saved_reg_offset = reg_offset; 448 saved_reg_value = reg_value; 449 ret = count; 450 } 451done: 452 kfree(buf); 453 return ret; 454} 455 456/* 457 * Proc regrdwr file read handler. 458 * 459 * This function is called when the 'regrdwr' file is opened for reading 460 * 461 * This function can be used to read from a register. 462 */ 463static ssize_t 464mwifiex_regrdwr_read(struct file *file, char __user *ubuf, 465 size_t count, loff_t *ppos) 466{ 467 struct mwifiex_private *priv = 468 (struct mwifiex_private *) file->private_data; 469 unsigned long addr = get_zeroed_page(GFP_KERNEL); 470 char *buf = (char *) addr; 471 int pos = 0, ret = 0; 472 u32 reg_value; 473 474 if (!buf) 475 return -ENOMEM; 476 477 if (!saved_reg_type) { 478 /* No command has been given */ 479 pos += snprintf(buf, PAGE_SIZE, "0"); 480 goto done; 481 } 482 /* Set command has been given */ 483 if (saved_reg_value != UINT_MAX) { 484 ret = mwifiex_reg_write(priv, saved_reg_type, saved_reg_offset, 485 saved_reg_value); 486 487 pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", 488 saved_reg_type, saved_reg_offset, 489 saved_reg_value); 490 491 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); 492 493 goto done; 494 } 495 /* Get command has been given */ 496 ret = mwifiex_reg_read(priv, saved_reg_type, 497 saved_reg_offset, ®_value); 498 if (ret) { 499 ret = -EINVAL; 500 goto done; 501 } 502 503 pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", saved_reg_type, 504 saved_reg_offset, reg_value); 505 506 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); 507 508done: 509 free_page(addr); 510 return ret; 511} 512 513/* Proc debug_mask file read handler. 514 * This function is called when the 'debug_mask' file is opened for reading 515 * This function can be used read driver debugging mask value. 516 */ 517static ssize_t 518mwifiex_debug_mask_read(struct file *file, char __user *ubuf, 519 size_t count, loff_t *ppos) 520{ 521 struct mwifiex_private *priv = 522 (struct mwifiex_private *)file->private_data; 523 unsigned long page = get_zeroed_page(GFP_KERNEL); 524 char *buf = (char *)page; 525 size_t ret = 0; 526 int pos = 0; 527 528 if (!buf) 529 return -ENOMEM; 530 531 pos += snprintf(buf, PAGE_SIZE, "debug mask=0x%08x\n", 532 priv->adapter->debug_mask); 533 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); 534 535 free_page(page); 536 return ret; 537} 538 539/* Proc debug_mask file read handler. 540 * This function is called when the 'debug_mask' file is opened for reading 541 * This function can be used read driver debugging mask value. 542 */ 543static ssize_t 544mwifiex_debug_mask_write(struct file *file, const char __user *ubuf, 545 size_t count, loff_t *ppos) 546{ 547 int ret; 548 unsigned long debug_mask; 549 struct mwifiex_private *priv = (void *)file->private_data; 550 char *buf; 551 552 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); 553 if (IS_ERR(buf)) 554 return PTR_ERR(buf); 555 556 if (kstrtoul(buf, 0, &debug_mask)) { 557 ret = -EINVAL; 558 goto done; 559 } 560 561 priv->adapter->debug_mask = debug_mask; 562 ret = count; 563done: 564 kfree(buf); 565 return ret; 566} 567 568/* debugfs verext file write handler. 569 * This function is called when the 'verext' file is opened for write 570 */ 571static ssize_t 572mwifiex_verext_write(struct file *file, const char __user *ubuf, 573 size_t count, loff_t *ppos) 574{ 575 int ret; 576 u32 versionstrsel; 577 struct mwifiex_private *priv = (void *)file->private_data; 578 char buf[16]; 579 580 memset(buf, 0, sizeof(buf)); 581 582 if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) 583 return -EFAULT; 584 585 ret = kstrtou32(buf, 10, &versionstrsel); 586 if (ret) 587 return ret; 588 589 priv->versionstrsel = versionstrsel; 590 591 return count; 592} 593 594/* Proc verext file read handler. 595 * This function is called when the 'verext' file is opened for reading 596 * This function can be used read driver exteneed verion string. 597 */ 598static ssize_t 599mwifiex_verext_read(struct file *file, char __user *ubuf, 600 size_t count, loff_t *ppos) 601{ 602 struct mwifiex_private *priv = 603 (struct mwifiex_private *)file->private_data; 604 char buf[256]; 605 int ret; 606 607 mwifiex_get_ver_ext(priv, priv->versionstrsel); 608 ret = snprintf(buf, sizeof(buf), "version string: %s\n", 609 priv->version_str); 610 611 return simple_read_from_buffer(ubuf, count, ppos, buf, ret); 612} 613 614/* Proc memrw file write handler. 615 * This function is called when the 'memrw' file is opened for writing 616 * This function can be used to write to a memory location. 617 */ 618static ssize_t 619mwifiex_memrw_write(struct file *file, const char __user *ubuf, size_t count, 620 loff_t *ppos) 621{ 622 int ret; 623 char cmd; 624 struct mwifiex_ds_mem_rw mem_rw; 625 u16 cmd_action; 626 struct mwifiex_private *priv = (void *)file->private_data; 627 char *buf; 628 629 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); 630 if (IS_ERR(buf)) 631 return PTR_ERR(buf); 632 633 ret = sscanf(buf, "%c %x %x", &cmd, &mem_rw.addr, &mem_rw.value); 634 if (ret != 3) { 635 ret = -EINVAL; 636 goto done; 637 } 638 639 if ((cmd == 'r') || (cmd == 'R')) { 640 cmd_action = HostCmd_ACT_GEN_GET; 641 mem_rw.value = 0; 642 } else if ((cmd == 'w') || (cmd == 'W')) { 643 cmd_action = HostCmd_ACT_GEN_SET; 644 } else { 645 ret = -EINVAL; 646 goto done; 647 } 648 649 memcpy(&priv->mem_rw, &mem_rw, sizeof(mem_rw)); 650 if (mwifiex_send_cmd(priv, HostCmd_CMD_MEM_ACCESS, cmd_action, 0, 651 &mem_rw, true)) 652 ret = -1; 653 else 654 ret = count; 655 656done: 657 kfree(buf); 658 return ret; 659} 660 661/* Proc memrw file read handler. 662 * This function is called when the 'memrw' file is opened for reading 663 * This function can be used to read from a memory location. 664 */ 665static ssize_t 666mwifiex_memrw_read(struct file *file, char __user *ubuf, 667 size_t count, loff_t *ppos) 668{ 669 struct mwifiex_private *priv = (void *)file->private_data; 670 unsigned long addr = get_zeroed_page(GFP_KERNEL); 671 char *buf = (char *)addr; 672 int ret, pos = 0; 673 674 if (!buf) 675 return -ENOMEM; 676 677 pos += snprintf(buf, PAGE_SIZE, "0x%x 0x%x\n", priv->mem_rw.addr, 678 priv->mem_rw.value); 679 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); 680 681 free_page(addr); 682 return ret; 683} 684 685static u32 saved_offset = -1, saved_bytes = -1; 686 687/* 688 * Proc rdeeprom file write handler. 689 * 690 * This function is called when the 'rdeeprom' file is opened for writing 691 * 692 * This function can be used to write to a RDEEPROM location. 693 */ 694static ssize_t 695mwifiex_rdeeprom_write(struct file *file, 696 const char __user *ubuf, size_t count, loff_t *ppos) 697{ 698 char *buf; 699 int ret = 0; 700 int offset = -1, bytes = -1; 701 702 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); 703 if (IS_ERR(buf)) 704 return PTR_ERR(buf); 705 706 sscanf(buf, "%d %d", &offset, &bytes); 707 708 if (offset == -1 || bytes == -1) { 709 ret = -EINVAL; 710 goto done; 711 } else { 712 saved_offset = offset; 713 saved_bytes = bytes; 714 ret = count; 715 } 716done: 717 kfree(buf); 718 return ret; 719} 720 721/* 722 * Proc rdeeprom read write handler. 723 * 724 * This function is called when the 'rdeeprom' file is opened for reading 725 * 726 * This function can be used to read from a RDEEPROM location. 727 */ 728static ssize_t 729mwifiex_rdeeprom_read(struct file *file, char __user *ubuf, 730 size_t count, loff_t *ppos) 731{ 732 struct mwifiex_private *priv = 733 (struct mwifiex_private *) file->private_data; 734 unsigned long addr = get_zeroed_page(GFP_KERNEL); 735 char *buf = (char *) addr; 736 int pos, ret, i; 737 u8 value[MAX_EEPROM_DATA]; 738 739 if (!buf) 740 return -ENOMEM; 741 742 if (saved_offset == -1) { 743 /* No command has been given */ 744 pos = snprintf(buf, PAGE_SIZE, "0"); 745 goto done; 746 } 747 748 /* Get command has been given */ 749 ret = mwifiex_eeprom_read(priv, (u16) saved_offset, 750 (u16) saved_bytes, value); 751 if (ret) { 752 ret = -EINVAL; 753 goto out_free; 754 } 755 756 pos = snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes); 757 758 for (i = 0; i < saved_bytes; i++) 759 pos += scnprintf(buf + pos, PAGE_SIZE - pos, "%d ", value[i]); 760 761done: 762 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); 763out_free: 764 free_page(addr); 765 return ret; 766} 767 768/* Proc hscfg file write handler 769 * This function can be used to configure the host sleep parameters. 770 */ 771static ssize_t 772mwifiex_hscfg_write(struct file *file, const char __user *ubuf, 773 size_t count, loff_t *ppos) 774{ 775 struct mwifiex_private *priv = (void *)file->private_data; 776 char *buf; 777 int ret, arg_num; 778 struct mwifiex_ds_hs_cfg hscfg; 779 int conditions = HS_CFG_COND_DEF; 780 u32 gpio = HS_CFG_GPIO_DEF, gap = HS_CFG_GAP_DEF; 781 782 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); 783 if (IS_ERR(buf)) 784 return PTR_ERR(buf); 785 786 arg_num = sscanf(buf, "%d %x %x", &conditions, &gpio, &gap); 787 788 memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg)); 789 790 if (arg_num > 3) { 791 mwifiex_dbg(priv->adapter, ERROR, 792 "Too many arguments\n"); 793 ret = -EINVAL; 794 goto done; 795 } 796 797 if (arg_num >= 1 && arg_num < 3) 798 mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET, 799 MWIFIEX_SYNC_CMD, &hscfg); 800 801 if (arg_num) { 802 if (conditions == HS_CFG_CANCEL) { 803 mwifiex_cancel_hs(priv, MWIFIEX_ASYNC_CMD); 804 ret = count; 805 goto done; 806 } 807 hscfg.conditions = conditions; 808 } 809 if (arg_num >= 2) 810 hscfg.gpio = gpio; 811 if (arg_num == 3) 812 hscfg.gap = gap; 813 814 hscfg.is_invoke_hostcmd = false; 815 mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, 816 MWIFIEX_SYNC_CMD, &hscfg); 817 818 mwifiex_enable_hs(priv->adapter); 819 clear_bit(MWIFIEX_IS_HS_ENABLING, &priv->adapter->work_flags); 820 ret = count; 821done: 822 kfree(buf); 823 return ret; 824} 825 826/* Proc hscfg file read handler 827 * This function can be used to read host sleep configuration 828 * parameters from driver. 829 */ 830static ssize_t 831mwifiex_hscfg_read(struct file *file, char __user *ubuf, 832 size_t count, loff_t *ppos) 833{ 834 struct mwifiex_private *priv = (void *)file->private_data; 835 unsigned long addr = get_zeroed_page(GFP_KERNEL); 836 char *buf = (char *)addr; 837 int pos, ret; 838 struct mwifiex_ds_hs_cfg hscfg; 839 840 if (!buf) 841 return -ENOMEM; 842 843 mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET, 844 MWIFIEX_SYNC_CMD, &hscfg); 845 846 pos = snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", hscfg.conditions, 847 hscfg.gpio, hscfg.gap); 848 849 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); 850 851 free_page(addr); 852 return ret; 853} 854 855static ssize_t 856mwifiex_timeshare_coex_read(struct file *file, char __user *ubuf, 857 size_t count, loff_t *ppos) 858{ 859 struct mwifiex_private *priv = file->private_data; 860 char buf[3]; 861 bool timeshare_coex; 862 int ret; 863 unsigned int len; 864 865 if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15) 866 return -EOPNOTSUPP; 867 868 ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX, 869 HostCmd_ACT_GEN_GET, 0, ×hare_coex, true); 870 if (ret) 871 return ret; 872 873 len = sprintf(buf, "%d\n", timeshare_coex); 874 return simple_read_from_buffer(ubuf, count, ppos, buf, len); 875} 876 877static ssize_t 878mwifiex_timeshare_coex_write(struct file *file, const char __user *ubuf, 879 size_t count, loff_t *ppos) 880{ 881 bool timeshare_coex; 882 struct mwifiex_private *priv = file->private_data; 883 char kbuf[16]; 884 int ret; 885 886 if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15) 887 return -EOPNOTSUPP; 888 889 memset(kbuf, 0, sizeof(kbuf)); 890 891 if (copy_from_user(&kbuf, ubuf, min_t(size_t, sizeof(kbuf) - 1, count))) 892 return -EFAULT; 893 894 if (strtobool(kbuf, ×hare_coex)) 895 return -EINVAL; 896 897 ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX, 898 HostCmd_ACT_GEN_SET, 0, ×hare_coex, true); 899 if (ret) 900 return ret; 901 else 902 return count; 903} 904 905static ssize_t 906mwifiex_reset_write(struct file *file, 907 const char __user *ubuf, size_t count, loff_t *ppos) 908{ 909 struct mwifiex_private *priv = file->private_data; 910 struct mwifiex_adapter *adapter = priv->adapter; 911 bool result; 912 int rc; 913 914 rc = kstrtobool_from_user(ubuf, count, &result); 915 if (rc) 916 return rc; 917 918 if (!result) 919 return -EINVAL; 920 921 if (adapter->if_ops.card_reset) { 922 dev_info(adapter->dev, "Resetting per request\n"); 923 adapter->if_ops.card_reset(adapter); 924 } 925 926 return count; 927} 928 929#define MWIFIEX_DFS_ADD_FILE(name) do { \ 930 debugfs_create_file(#name, 0644, priv->dfs_dev_dir, priv, \ 931 &mwifiex_dfs_##name##_fops); \ 932} while (0); 933 934#define MWIFIEX_DFS_FILE_OPS(name) \ 935static const struct file_operations mwifiex_dfs_##name##_fops = { \ 936 .read = mwifiex_##name##_read, \ 937 .write = mwifiex_##name##_write, \ 938 .open = simple_open, \ 939}; 940 941#define MWIFIEX_DFS_FILE_READ_OPS(name) \ 942static const struct file_operations mwifiex_dfs_##name##_fops = { \ 943 .read = mwifiex_##name##_read, \ 944 .open = simple_open, \ 945}; 946 947#define MWIFIEX_DFS_FILE_WRITE_OPS(name) \ 948static const struct file_operations mwifiex_dfs_##name##_fops = { \ 949 .write = mwifiex_##name##_write, \ 950 .open = simple_open, \ 951}; 952 953 954MWIFIEX_DFS_FILE_READ_OPS(info); 955MWIFIEX_DFS_FILE_READ_OPS(debug); 956MWIFIEX_DFS_FILE_READ_OPS(getlog); 957MWIFIEX_DFS_FILE_OPS(regrdwr); 958MWIFIEX_DFS_FILE_OPS(rdeeprom); 959MWIFIEX_DFS_FILE_OPS(memrw); 960MWIFIEX_DFS_FILE_OPS(hscfg); 961MWIFIEX_DFS_FILE_OPS(histogram); 962MWIFIEX_DFS_FILE_OPS(debug_mask); 963MWIFIEX_DFS_FILE_OPS(timeshare_coex); 964MWIFIEX_DFS_FILE_WRITE_OPS(reset); 965MWIFIEX_DFS_FILE_OPS(verext); 966 967/* 968 * This function creates the debug FS directory structure and the files. 969 */ 970void 971mwifiex_dev_debugfs_init(struct mwifiex_private *priv) 972{ 973 if (!mwifiex_dfs_dir || !priv) 974 return; 975 976 priv->dfs_dev_dir = debugfs_create_dir(priv->netdev->name, 977 mwifiex_dfs_dir); 978 979 if (!priv->dfs_dev_dir) 980 return; 981 982 MWIFIEX_DFS_ADD_FILE(info); 983 MWIFIEX_DFS_ADD_FILE(debug); 984 MWIFIEX_DFS_ADD_FILE(getlog); 985 MWIFIEX_DFS_ADD_FILE(regrdwr); 986 MWIFIEX_DFS_ADD_FILE(rdeeprom); 987 988 MWIFIEX_DFS_ADD_FILE(memrw); 989 MWIFIEX_DFS_ADD_FILE(hscfg); 990 MWIFIEX_DFS_ADD_FILE(histogram); 991 MWIFIEX_DFS_ADD_FILE(debug_mask); 992 MWIFIEX_DFS_ADD_FILE(timeshare_coex); 993 MWIFIEX_DFS_ADD_FILE(reset); 994 MWIFIEX_DFS_ADD_FILE(verext); 995} 996 997/* 998 * This function removes the debug FS directory structure and the files. 999 */ 1000void 1001mwifiex_dev_debugfs_remove(struct mwifiex_private *priv) 1002{ 1003 if (!priv) 1004 return; 1005 1006 debugfs_remove_recursive(priv->dfs_dev_dir); 1007} 1008 1009/* 1010 * This function creates the top level proc directory. 1011 */ 1012void 1013mwifiex_debugfs_init(void) 1014{ 1015 if (!mwifiex_dfs_dir) 1016 mwifiex_dfs_dir = debugfs_create_dir("mwifiex", NULL); 1017} 1018 1019/* 1020 * This function removes the top level proc directory. 1021 */ 1022void 1023mwifiex_debugfs_remove(void) 1024{ 1025 debugfs_remove(mwifiex_dfs_dir); 1026} 1027