1 /* 2 * Marvell Wireless LAN device driver: debugfs 3 * 4 * Copyright (C) 2011-2014, Marvell International Ltd. 5 * 6 * This software file (the "File") is distributed by Marvell International 7 * Ltd. 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 26 static struct dentry *mwifiex_dfs_dir; 27 28 static 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 */ 73 static ssize_t 74 mwifiex_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 151 free_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 */ 177 static ssize_t 178 mwifiex_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 238 free_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 */ 253 static ssize_t 254 mwifiex_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 return -EFAULT; 270 phist_data = priv->hist_data; 271 272 p += sprintf(p, "\n" 273 "total samples = %d\n", 274 atomic_read(&phist_data->num_samples)); 275 276 p += sprintf(p, "rx rates (in Mbps): 0=1M 1=2M"); 277 p += sprintf(p, "2=5.5M 3=11M 4=6M 5=9M 6=12M\n"); 278 p += sprintf(p, "7=18M 8=24M 9=36M 10=48M 11=54M"); 279 p += sprintf(p, "12-27=MCS0-15(BW20) 28-43=MCS0-15(BW40)\n"); 280 281 if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) { 282 p += sprintf(p, "44-53=MCS0-9(VHT:BW20)"); 283 p += sprintf(p, "54-63=MCS0-9(VHT:BW40)"); 284 p += sprintf(p, "64-73=MCS0-9(VHT:BW80)\n\n"); 285 } else { 286 p += sprintf(p, "\n"); 287 } 288 289 for (i = 0; i < MWIFIEX_MAX_RX_RATES; i++) { 290 value = atomic_read(&phist_data->rx_rate[i]); 291 if (value) 292 p += sprintf(p, "rx_rate[%02d] = %d\n", i, value); 293 } 294 295 if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) { 296 for (i = MWIFIEX_MAX_RX_RATES; i < MWIFIEX_MAX_AC_RX_RATES; 297 i++) { 298 value = atomic_read(&phist_data->rx_rate[i]); 299 if (value) 300 p += sprintf(p, "rx_rate[%02d] = %d\n", 301 i, value); 302 } 303 } 304 305 for (i = 0; i < MWIFIEX_MAX_SNR; i++) { 306 value = atomic_read(&phist_data->snr[i]); 307 if (value) 308 p += sprintf(p, "snr[%02ddB] = %d\n", i, value); 309 } 310 for (i = 0; i < MWIFIEX_MAX_NOISE_FLR; i++) { 311 value = atomic_read(&phist_data->noise_flr[i]); 312 if (value) 313 p += sprintf(p, "noise_flr[-%02ddBm] = %d\n", 314 (int)(i-128), value); 315 } 316 for (i = 0; i < MWIFIEX_MAX_SIG_STRENGTH; i++) { 317 value = atomic_read(&phist_data->sig_str[i]); 318 if (value) 319 p += sprintf(p, "sig_strength[-%02ddBm] = %d\n", 320 i, value); 321 } 322 323 ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page, 324 (unsigned long)p - page); 325 326 return ret; 327 } 328 329 static ssize_t 330 mwifiex_histogram_write(struct file *file, const char __user *ubuf, 331 size_t count, loff_t *ppos) 332 { 333 struct mwifiex_private *priv = (void *)file->private_data; 334 335 if (priv && priv->hist_data) 336 mwifiex_hist_data_reset(priv); 337 return 0; 338 } 339 340 static struct mwifiex_debug_info info; 341 342 /* 343 * Proc debug file read handler. 344 * 345 * This function is called when the 'debug' file is opened for reading 346 * It prints the following log information - 347 * - Interrupt count 348 * - WMM AC VO packets count 349 * - WMM AC VI packets count 350 * - WMM AC BE packets count 351 * - WMM AC BK packets count 352 * - Maximum Tx buffer size 353 * - Tx buffer size 354 * - Current Tx buffer size 355 * - Power Save mode 356 * - Power Save state 357 * - Deep Sleep status 358 * - Device wakeup required status 359 * - Number of wakeup tries 360 * - Host Sleep configured status 361 * - Host Sleep activated status 362 * - Number of Tx timeouts 363 * - Number of command timeouts 364 * - Last timed out command ID 365 * - Last timed out command action 366 * - Last command ID 367 * - Last command action 368 * - Last command index 369 * - Last command response ID 370 * - Last command response index 371 * - Last event 372 * - Last event index 373 * - Number of host to card command failures 374 * - Number of sleep confirm command failures 375 * - Number of host to card data failure 376 * - Number of deauthentication events 377 * - Number of disassociation events 378 * - Number of link lost events 379 * - Number of deauthentication commands 380 * - Number of association success commands 381 * - Number of association failure commands 382 * - Number of commands sent 383 * - Number of data packets sent 384 * - Number of command responses received 385 * - Number of events received 386 * - Tx BA stream table (TID, RA) 387 * - Rx reorder table (TID, TA, Start window, Window size, Buffer) 388 */ 389 static ssize_t 390 mwifiex_debug_read(struct file *file, char __user *ubuf, 391 size_t count, loff_t *ppos) 392 { 393 struct mwifiex_private *priv = 394 (struct mwifiex_private *) file->private_data; 395 unsigned long page = get_zeroed_page(GFP_KERNEL); 396 char *p = (char *) page; 397 ssize_t ret; 398 399 if (!p) 400 return -ENOMEM; 401 402 ret = mwifiex_get_debug_info(priv, &info); 403 if (ret) 404 goto free_and_exit; 405 406 p += mwifiex_debug_info_to_buffer(priv, p, &info); 407 408 ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, 409 (unsigned long) p - page); 410 411 free_and_exit: 412 free_page(page); 413 return ret; 414 } 415 416 static u32 saved_reg_type, saved_reg_offset, saved_reg_value; 417 418 /* 419 * Proc regrdwr file write handler. 420 * 421 * This function is called when the 'regrdwr' file is opened for writing 422 * 423 * This function can be used to write to a register. 424 */ 425 static ssize_t 426 mwifiex_regrdwr_write(struct file *file, 427 const char __user *ubuf, size_t count, loff_t *ppos) 428 { 429 char *buf; 430 int ret; 431 u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX; 432 433 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); 434 if (IS_ERR(buf)) 435 return PTR_ERR(buf); 436 437 sscanf(buf, "%u %x %x", ®_type, ®_offset, ®_value); 438 439 if (reg_type == 0 || reg_offset == 0) { 440 ret = -EINVAL; 441 goto done; 442 } else { 443 saved_reg_type = reg_type; 444 saved_reg_offset = reg_offset; 445 saved_reg_value = reg_value; 446 ret = count; 447 } 448 done: 449 kfree(buf); 450 return ret; 451 } 452 453 /* 454 * Proc regrdwr file read handler. 455 * 456 * This function is called when the 'regrdwr' file is opened for reading 457 * 458 * This function can be used to read from a register. 459 */ 460 static ssize_t 461 mwifiex_regrdwr_read(struct file *file, char __user *ubuf, 462 size_t count, loff_t *ppos) 463 { 464 struct mwifiex_private *priv = 465 (struct mwifiex_private *) file->private_data; 466 unsigned long addr = get_zeroed_page(GFP_KERNEL); 467 char *buf = (char *) addr; 468 int pos = 0, ret = 0; 469 u32 reg_value; 470 471 if (!buf) 472 return -ENOMEM; 473 474 if (!saved_reg_type) { 475 /* No command has been given */ 476 pos += snprintf(buf, PAGE_SIZE, "0"); 477 goto done; 478 } 479 /* Set command has been given */ 480 if (saved_reg_value != UINT_MAX) { 481 ret = mwifiex_reg_write(priv, saved_reg_type, saved_reg_offset, 482 saved_reg_value); 483 484 pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", 485 saved_reg_type, saved_reg_offset, 486 saved_reg_value); 487 488 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); 489 490 goto done; 491 } 492 /* Get command has been given */ 493 ret = mwifiex_reg_read(priv, saved_reg_type, 494 saved_reg_offset, ®_value); 495 if (ret) { 496 ret = -EINVAL; 497 goto done; 498 } 499 500 pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", saved_reg_type, 501 saved_reg_offset, reg_value); 502 503 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); 504 505 done: 506 free_page(addr); 507 return ret; 508 } 509 510 /* Proc debug_mask file read handler. 511 * This function is called when the 'debug_mask' file is opened for reading 512 * This function can be used read driver debugging mask value. 513 */ 514 static ssize_t 515 mwifiex_debug_mask_read(struct file *file, char __user *ubuf, 516 size_t count, loff_t *ppos) 517 { 518 struct mwifiex_private *priv = 519 (struct mwifiex_private *)file->private_data; 520 unsigned long page = get_zeroed_page(GFP_KERNEL); 521 char *buf = (char *)page; 522 size_t ret = 0; 523 int pos = 0; 524 525 if (!buf) 526 return -ENOMEM; 527 528 pos += snprintf(buf, PAGE_SIZE, "debug mask=0x%08x\n", 529 priv->adapter->debug_mask); 530 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); 531 532 free_page(page); 533 return ret; 534 } 535 536 /* Proc debug_mask file read handler. 537 * This function is called when the 'debug_mask' file is opened for reading 538 * This function can be used read driver debugging mask value. 539 */ 540 static ssize_t 541 mwifiex_debug_mask_write(struct file *file, const char __user *ubuf, 542 size_t count, loff_t *ppos) 543 { 544 int ret; 545 unsigned long debug_mask; 546 struct mwifiex_private *priv = (void *)file->private_data; 547 char *buf; 548 549 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); 550 if (IS_ERR(buf)) 551 return PTR_ERR(buf); 552 553 if (kstrtoul(buf, 0, &debug_mask)) { 554 ret = -EINVAL; 555 goto done; 556 } 557 558 priv->adapter->debug_mask = debug_mask; 559 ret = count; 560 done: 561 kfree(buf); 562 return ret; 563 } 564 565 /* debugfs verext file write handler. 566 * This function is called when the 'verext' file is opened for write 567 */ 568 static ssize_t 569 mwifiex_verext_write(struct file *file, const char __user *ubuf, 570 size_t count, loff_t *ppos) 571 { 572 int ret; 573 u32 versionstrsel; 574 struct mwifiex_private *priv = (void *)file->private_data; 575 char buf[16]; 576 577 memset(buf, 0, sizeof(buf)); 578 579 if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) 580 return -EFAULT; 581 582 ret = kstrtou32(buf, 10, &versionstrsel); 583 if (ret) 584 return ret; 585 586 priv->versionstrsel = versionstrsel; 587 588 return count; 589 } 590 591 /* Proc verext file read handler. 592 * This function is called when the 'verext' file is opened for reading 593 * This function can be used read driver exteneed verion string. 594 */ 595 static ssize_t 596 mwifiex_verext_read(struct file *file, char __user *ubuf, 597 size_t count, loff_t *ppos) 598 { 599 struct mwifiex_private *priv = 600 (struct mwifiex_private *)file->private_data; 601 char buf[256]; 602 int ret; 603 604 mwifiex_get_ver_ext(priv, priv->versionstrsel); 605 ret = snprintf(buf, sizeof(buf), "version string: %s\n", 606 priv->version_str); 607 608 return simple_read_from_buffer(ubuf, count, ppos, buf, ret); 609 } 610 611 /* Proc memrw file write handler. 612 * This function is called when the 'memrw' file is opened for writing 613 * This function can be used to write to a memory location. 614 */ 615 static ssize_t 616 mwifiex_memrw_write(struct file *file, const char __user *ubuf, size_t count, 617 loff_t *ppos) 618 { 619 int ret; 620 char cmd; 621 struct mwifiex_ds_mem_rw mem_rw; 622 u16 cmd_action; 623 struct mwifiex_private *priv = (void *)file->private_data; 624 char *buf; 625 626 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); 627 if (IS_ERR(buf)) 628 return PTR_ERR(buf); 629 630 ret = sscanf(buf, "%c %x %x", &cmd, &mem_rw.addr, &mem_rw.value); 631 if (ret != 3) { 632 ret = -EINVAL; 633 goto done; 634 } 635 636 if ((cmd == 'r') || (cmd == 'R')) { 637 cmd_action = HostCmd_ACT_GEN_GET; 638 mem_rw.value = 0; 639 } else if ((cmd == 'w') || (cmd == 'W')) { 640 cmd_action = HostCmd_ACT_GEN_SET; 641 } else { 642 ret = -EINVAL; 643 goto done; 644 } 645 646 memcpy(&priv->mem_rw, &mem_rw, sizeof(mem_rw)); 647 if (mwifiex_send_cmd(priv, HostCmd_CMD_MEM_ACCESS, cmd_action, 0, 648 &mem_rw, true)) 649 ret = -1; 650 else 651 ret = count; 652 653 done: 654 kfree(buf); 655 return ret; 656 } 657 658 /* Proc memrw file read handler. 659 * This function is called when the 'memrw' file is opened for reading 660 * This function can be used to read from a memory location. 661 */ 662 static ssize_t 663 mwifiex_memrw_read(struct file *file, char __user *ubuf, 664 size_t count, loff_t *ppos) 665 { 666 struct mwifiex_private *priv = (void *)file->private_data; 667 unsigned long addr = get_zeroed_page(GFP_KERNEL); 668 char *buf = (char *)addr; 669 int ret, pos = 0; 670 671 if (!buf) 672 return -ENOMEM; 673 674 pos += snprintf(buf, PAGE_SIZE, "0x%x 0x%x\n", priv->mem_rw.addr, 675 priv->mem_rw.value); 676 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); 677 678 free_page(addr); 679 return ret; 680 } 681 682 static u32 saved_offset = -1, saved_bytes = -1; 683 684 /* 685 * Proc rdeeprom file write handler. 686 * 687 * This function is called when the 'rdeeprom' file is opened for writing 688 * 689 * This function can be used to write to a RDEEPROM location. 690 */ 691 static ssize_t 692 mwifiex_rdeeprom_write(struct file *file, 693 const char __user *ubuf, size_t count, loff_t *ppos) 694 { 695 char *buf; 696 int ret = 0; 697 int offset = -1, bytes = -1; 698 699 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); 700 if (IS_ERR(buf)) 701 return PTR_ERR(buf); 702 703 sscanf(buf, "%d %d", &offset, &bytes); 704 705 if (offset == -1 || bytes == -1) { 706 ret = -EINVAL; 707 goto done; 708 } else { 709 saved_offset = offset; 710 saved_bytes = bytes; 711 ret = count; 712 } 713 done: 714 kfree(buf); 715 return ret; 716 } 717 718 /* 719 * Proc rdeeprom read write handler. 720 * 721 * This function is called when the 'rdeeprom' file is opened for reading 722 * 723 * This function can be used to read from a RDEEPROM location. 724 */ 725 static ssize_t 726 mwifiex_rdeeprom_read(struct file *file, char __user *ubuf, 727 size_t count, loff_t *ppos) 728 { 729 struct mwifiex_private *priv = 730 (struct mwifiex_private *) file->private_data; 731 unsigned long addr = get_zeroed_page(GFP_KERNEL); 732 char *buf = (char *) addr; 733 int pos, ret, i; 734 u8 value[MAX_EEPROM_DATA]; 735 736 if (!buf) 737 return -ENOMEM; 738 739 if (saved_offset == -1) { 740 /* No command has been given */ 741 pos = snprintf(buf, PAGE_SIZE, "0"); 742 goto done; 743 } 744 745 /* Get command has been given */ 746 ret = mwifiex_eeprom_read(priv, (u16) saved_offset, 747 (u16) saved_bytes, value); 748 if (ret) { 749 ret = -EINVAL; 750 goto out_free; 751 } 752 753 pos = snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes); 754 755 for (i = 0; i < saved_bytes; i++) 756 pos += scnprintf(buf + pos, PAGE_SIZE - pos, "%d ", value[i]); 757 758 done: 759 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); 760 out_free: 761 free_page(addr); 762 return ret; 763 } 764 765 /* Proc hscfg file write handler 766 * This function can be used to configure the host sleep parameters. 767 */ 768 static ssize_t 769 mwifiex_hscfg_write(struct file *file, const char __user *ubuf, 770 size_t count, loff_t *ppos) 771 { 772 struct mwifiex_private *priv = (void *)file->private_data; 773 char *buf; 774 int ret, arg_num; 775 struct mwifiex_ds_hs_cfg hscfg; 776 int conditions = HS_CFG_COND_DEF; 777 u32 gpio = HS_CFG_GPIO_DEF, gap = HS_CFG_GAP_DEF; 778 779 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); 780 if (IS_ERR(buf)) 781 return PTR_ERR(buf); 782 783 arg_num = sscanf(buf, "%d %x %x", &conditions, &gpio, &gap); 784 785 memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg)); 786 787 if (arg_num > 3) { 788 mwifiex_dbg(priv->adapter, ERROR, 789 "Too many arguments\n"); 790 ret = -EINVAL; 791 goto done; 792 } 793 794 if (arg_num >= 1 && arg_num < 3) 795 mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET, 796 MWIFIEX_SYNC_CMD, &hscfg); 797 798 if (arg_num) { 799 if (conditions == HS_CFG_CANCEL) { 800 mwifiex_cancel_hs(priv, MWIFIEX_ASYNC_CMD); 801 ret = count; 802 goto done; 803 } 804 hscfg.conditions = conditions; 805 } 806 if (arg_num >= 2) 807 hscfg.gpio = gpio; 808 if (arg_num == 3) 809 hscfg.gap = gap; 810 811 hscfg.is_invoke_hostcmd = false; 812 mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, 813 MWIFIEX_SYNC_CMD, &hscfg); 814 815 mwifiex_enable_hs(priv->adapter); 816 clear_bit(MWIFIEX_IS_HS_ENABLING, &priv->adapter->work_flags); 817 ret = count; 818 done: 819 kfree(buf); 820 return ret; 821 } 822 823 /* Proc hscfg file read handler 824 * This function can be used to read host sleep configuration 825 * parameters from driver. 826 */ 827 static ssize_t 828 mwifiex_hscfg_read(struct file *file, char __user *ubuf, 829 size_t count, loff_t *ppos) 830 { 831 struct mwifiex_private *priv = (void *)file->private_data; 832 unsigned long addr = get_zeroed_page(GFP_KERNEL); 833 char *buf = (char *)addr; 834 int pos, ret; 835 struct mwifiex_ds_hs_cfg hscfg; 836 837 if (!buf) 838 return -ENOMEM; 839 840 mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET, 841 MWIFIEX_SYNC_CMD, &hscfg); 842 843 pos = snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", hscfg.conditions, 844 hscfg.gpio, hscfg.gap); 845 846 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); 847 848 free_page(addr); 849 return ret; 850 } 851 852 static ssize_t 853 mwifiex_timeshare_coex_read(struct file *file, char __user *ubuf, 854 size_t count, loff_t *ppos) 855 { 856 struct mwifiex_private *priv = file->private_data; 857 char buf[3]; 858 bool timeshare_coex; 859 int ret; 860 unsigned int len; 861 862 if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15) 863 return -EOPNOTSUPP; 864 865 ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX, 866 HostCmd_ACT_GEN_GET, 0, ×hare_coex, true); 867 if (ret) 868 return ret; 869 870 len = sprintf(buf, "%d\n", timeshare_coex); 871 return simple_read_from_buffer(ubuf, count, ppos, buf, len); 872 } 873 874 static ssize_t 875 mwifiex_timeshare_coex_write(struct file *file, const char __user *ubuf, 876 size_t count, loff_t *ppos) 877 { 878 bool timeshare_coex; 879 struct mwifiex_private *priv = file->private_data; 880 char kbuf[16]; 881 int ret; 882 883 if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15) 884 return -EOPNOTSUPP; 885 886 memset(kbuf, 0, sizeof(kbuf)); 887 888 if (copy_from_user(&kbuf, ubuf, min_t(size_t, sizeof(kbuf) - 1, count))) 889 return -EFAULT; 890 891 if (strtobool(kbuf, ×hare_coex)) 892 return -EINVAL; 893 894 ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX, 895 HostCmd_ACT_GEN_SET, 0, ×hare_coex, true); 896 if (ret) 897 return ret; 898 else 899 return count; 900 } 901 902 static ssize_t 903 mwifiex_reset_write(struct file *file, 904 const char __user *ubuf, size_t count, loff_t *ppos) 905 { 906 struct mwifiex_private *priv = file->private_data; 907 struct mwifiex_adapter *adapter = priv->adapter; 908 bool result; 909 int rc; 910 911 rc = kstrtobool_from_user(ubuf, count, &result); 912 if (rc) 913 return rc; 914 915 if (!result) 916 return -EINVAL; 917 918 if (adapter->if_ops.card_reset) { 919 dev_info(adapter->dev, "Resetting per request\n"); 920 adapter->if_ops.card_reset(adapter); 921 } 922 923 return count; 924 } 925 926 #define MWIFIEX_DFS_ADD_FILE(name) do { \ 927 if (!debugfs_create_file(#name, 0644, priv->dfs_dev_dir, \ 928 priv, &mwifiex_dfs_##name##_fops)) \ 929 return; \ 930 } while (0); 931 932 #define MWIFIEX_DFS_FILE_OPS(name) \ 933 static const struct file_operations mwifiex_dfs_##name##_fops = { \ 934 .read = mwifiex_##name##_read, \ 935 .write = mwifiex_##name##_write, \ 936 .open = simple_open, \ 937 }; 938 939 #define MWIFIEX_DFS_FILE_READ_OPS(name) \ 940 static const struct file_operations mwifiex_dfs_##name##_fops = { \ 941 .read = mwifiex_##name##_read, \ 942 .open = simple_open, \ 943 }; 944 945 #define MWIFIEX_DFS_FILE_WRITE_OPS(name) \ 946 static const struct file_operations mwifiex_dfs_##name##_fops = { \ 947 .write = mwifiex_##name##_write, \ 948 .open = simple_open, \ 949 }; 950 951 952 MWIFIEX_DFS_FILE_READ_OPS(info); 953 MWIFIEX_DFS_FILE_READ_OPS(debug); 954 MWIFIEX_DFS_FILE_READ_OPS(getlog); 955 MWIFIEX_DFS_FILE_OPS(regrdwr); 956 MWIFIEX_DFS_FILE_OPS(rdeeprom); 957 MWIFIEX_DFS_FILE_OPS(memrw); 958 MWIFIEX_DFS_FILE_OPS(hscfg); 959 MWIFIEX_DFS_FILE_OPS(histogram); 960 MWIFIEX_DFS_FILE_OPS(debug_mask); 961 MWIFIEX_DFS_FILE_OPS(timeshare_coex); 962 MWIFIEX_DFS_FILE_WRITE_OPS(reset); 963 MWIFIEX_DFS_FILE_OPS(verext); 964 965 /* 966 * This function creates the debug FS directory structure and the files. 967 */ 968 void 969 mwifiex_dev_debugfs_init(struct mwifiex_private *priv) 970 { 971 if (!mwifiex_dfs_dir || !priv) 972 return; 973 974 priv->dfs_dev_dir = debugfs_create_dir(priv->netdev->name, 975 mwifiex_dfs_dir); 976 977 if (!priv->dfs_dev_dir) 978 return; 979 980 MWIFIEX_DFS_ADD_FILE(info); 981 MWIFIEX_DFS_ADD_FILE(debug); 982 MWIFIEX_DFS_ADD_FILE(getlog); 983 MWIFIEX_DFS_ADD_FILE(regrdwr); 984 MWIFIEX_DFS_ADD_FILE(rdeeprom); 985 986 MWIFIEX_DFS_ADD_FILE(memrw); 987 MWIFIEX_DFS_ADD_FILE(hscfg); 988 MWIFIEX_DFS_ADD_FILE(histogram); 989 MWIFIEX_DFS_ADD_FILE(debug_mask); 990 MWIFIEX_DFS_ADD_FILE(timeshare_coex); 991 MWIFIEX_DFS_ADD_FILE(reset); 992 MWIFIEX_DFS_ADD_FILE(verext); 993 } 994 995 /* 996 * This function removes the debug FS directory structure and the files. 997 */ 998 void 999 mwifiex_dev_debugfs_remove(struct mwifiex_private *priv) 1000 { 1001 if (!priv) 1002 return; 1003 1004 debugfs_remove_recursive(priv->dfs_dev_dir); 1005 } 1006 1007 /* 1008 * This function creates the top level proc directory. 1009 */ 1010 void 1011 mwifiex_debugfs_init(void) 1012 { 1013 if (!mwifiex_dfs_dir) 1014 mwifiex_dfs_dir = debugfs_create_dir("mwifiex", NULL); 1015 } 1016 1017 /* 1018 * This function removes the top level proc directory. 1019 */ 1020 void 1021 mwifiex_debugfs_remove(void) 1022 { 1023 debugfs_remove(mwifiex_dfs_dir); 1024 } 1025