1007 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * NXP Wireless LAN device driver: debugfs4 *5 * Copyright 2011-2020 NXP6 */7 8#include <linux/debugfs.h>9 10#include "main.h"11#include "11n.h"12 13 14static struct dentry *mwifiex_dfs_dir;15 16static char *bss_modes[] = {17 "UNSPECIFIED",18 "ADHOC",19 "STATION",20 "AP",21 "AP_VLAN",22 "WDS",23 "MONITOR",24 "MESH_POINT",25 "P2P_CLIENT",26 "P2P_GO",27 "P2P_DEVICE",28};29 30/*31 * Proc info file read handler.32 *33 * This function is called when the 'info' file is opened for reading.34 * It prints the following driver related information -35 * - Driver name36 * - Driver version37 * - Driver extended version38 * - Interface name39 * - BSS mode40 * - Media state (connected or disconnected)41 * - MAC address42 * - Total number of Tx bytes43 * - Total number of Rx bytes44 * - Total number of Tx packets45 * - Total number of Rx packets46 * - Total number of dropped Tx packets47 * - Total number of dropped Rx packets48 * - Total number of corrupted Tx packets49 * - Total number of corrupted Rx packets50 * - Carrier status (on or off)51 * - Tx queue status (started or stopped)52 *53 * For STA mode drivers, it also prints the following extra -54 * - ESSID55 * - BSSID56 * - Channel57 * - Region code58 * - Multicast count59 * - Multicast addresses60 */61static ssize_t62mwifiex_info_read(struct file *file, char __user *ubuf,63 size_t count, loff_t *ppos)64{65 struct mwifiex_private *priv =66 (struct mwifiex_private *) file->private_data;67 struct net_device *netdev = priv->netdev;68 struct netdev_hw_addr *ha;69 struct netdev_queue *txq;70 unsigned long page = get_zeroed_page(GFP_KERNEL);71 char *p = (char *) page, fmt[64];72 struct mwifiex_bss_info info;73 ssize_t ret;74 int i = 0;75 76 if (!p)77 return -ENOMEM;78 79 memset(&info, 0, sizeof(info));80 ret = mwifiex_get_bss_info(priv, &info);81 if (ret)82 goto free_and_exit;83 84 mwifiex_drv_get_driver_version(priv->adapter, fmt, sizeof(fmt) - 1);85 86 mwifiex_get_ver_ext(priv, 0);87 88 p += sprintf(p, "driver_name = " "\"mwifiex\"\n");89 p += sprintf(p, "driver_version = %s", fmt);90 p += sprintf(p, "\nverext = %s", priv->version_str);91 p += sprintf(p, "\ninterface_name=\"%s\"\n", netdev->name);92 93 if (info.bss_mode >= ARRAY_SIZE(bss_modes))94 p += sprintf(p, "bss_mode=\"%d\"\n", info.bss_mode);95 else96 p += sprintf(p, "bss_mode=\"%s\"\n", bss_modes[info.bss_mode]);97 98 p += sprintf(p, "media_state=\"%s\"\n",99 (!priv->media_connected ? "Disconnected" : "Connected"));100 p += sprintf(p, "mac_address=\"%pM\"\n", netdev->dev_addr);101 102 if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) {103 p += sprintf(p, "multicast_count=\"%d\"\n",104 netdev_mc_count(netdev));105 p += sprintf(p, "essid=\"%.*s\"\n", info.ssid.ssid_len,106 info.ssid.ssid);107 p += sprintf(p, "bssid=\"%pM\"\n", info.bssid);108 p += sprintf(p, "channel=\"%d\"\n", (int) info.bss_chan);109 p += sprintf(p, "country_code = \"%s\"\n", info.country_code);110 p += sprintf(p, "region_code=\"0x%x\"\n",111 priv->adapter->region_code);112 113 netdev_for_each_mc_addr(ha, netdev)114 p += sprintf(p, "multicast_address[%d]=\"%pM\"\n",115 i++, ha->addr);116 }117 118 p += sprintf(p, "num_tx_bytes = %lu\n", priv->stats.tx_bytes);119 p += sprintf(p, "num_rx_bytes = %lu\n", priv->stats.rx_bytes);120 p += sprintf(p, "num_tx_pkts = %lu\n", priv->stats.tx_packets);121 p += sprintf(p, "num_rx_pkts = %lu\n", priv->stats.rx_packets);122 p += sprintf(p, "num_tx_pkts_dropped = %lu\n", priv->stats.tx_dropped);123 p += sprintf(p, "num_rx_pkts_dropped = %lu\n", priv->stats.rx_dropped);124 p += sprintf(p, "num_tx_pkts_err = %lu\n", priv->stats.tx_errors);125 p += sprintf(p, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors);126 p += sprintf(p, "carrier %s\n", ((netif_carrier_ok(priv->netdev))127 ? "on" : "off"));128 p += sprintf(p, "tx queue");129 for (i = 0; i < netdev->num_tx_queues; i++) {130 txq = netdev_get_tx_queue(netdev, i);131 p += sprintf(p, " %d:%s", i, netif_tx_queue_stopped(txq) ?132 "stopped" : "started");133 }134 p += sprintf(p, "\n");135 136 ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page,137 (unsigned long) p - page);138 139free_and_exit:140 free_page(page);141 return ret;142}143 144/*145 * Proc getlog file read handler.146 *147 * This function is called when the 'getlog' file is opened for reading148 * It prints the following log information -149 * - Number of multicast Tx frames150 * - Number of failed packets151 * - Number of Tx retries152 * - Number of multicast Tx retries153 * - Number of duplicate frames154 * - Number of RTS successes155 * - Number of RTS failures156 * - Number of ACK failures157 * - Number of fragmented Rx frames158 * - Number of multicast Rx frames159 * - Number of FCS errors160 * - Number of Tx frames161 * - WEP ICV error counts162 * - Number of received beacons163 * - Number of missed beacons164 */165static ssize_t166mwifiex_getlog_read(struct file *file, char __user *ubuf,167 size_t count, loff_t *ppos)168{169 struct mwifiex_private *priv =170 (struct mwifiex_private *) file->private_data;171 unsigned long page = get_zeroed_page(GFP_KERNEL);172 char *p = (char *) page;173 ssize_t ret;174 struct mwifiex_ds_get_stats stats;175 176 if (!p)177 return -ENOMEM;178 179 memset(&stats, 0, sizeof(stats));180 ret = mwifiex_get_stats_info(priv, &stats);181 if (ret)182 goto free_and_exit;183 184 p += sprintf(p, "\n"185 "mcasttxframe %u\n"186 "failed %u\n"187 "retry %u\n"188 "multiretry %u\n"189 "framedup %u\n"190 "rtssuccess %u\n"191 "rtsfailure %u\n"192 "ackfailure %u\n"193 "rxfrag %u\n"194 "mcastrxframe %u\n"195 "fcserror %u\n"196 "txframe %u\n"197 "wepicverrcnt-1 %u\n"198 "wepicverrcnt-2 %u\n"199 "wepicverrcnt-3 %u\n"200 "wepicverrcnt-4 %u\n"201 "bcn_rcv_cnt %u\n"202 "bcn_miss_cnt %u\n",203 stats.mcast_tx_frame,204 stats.failed,205 stats.retry,206 stats.multi_retry,207 stats.frame_dup,208 stats.rts_success,209 stats.rts_failure,210 stats.ack_failure,211 stats.rx_frag,212 stats.mcast_rx_frame,213 stats.fcs_error,214 stats.tx_frame,215 stats.wep_icv_error[0],216 stats.wep_icv_error[1],217 stats.wep_icv_error[2],218 stats.wep_icv_error[3],219 stats.bcn_rcv_cnt,220 stats.bcn_miss_cnt);221 222 223 ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page,224 (unsigned long) p - page);225 226free_and_exit:227 free_page(page);228 return ret;229}230 231/* Sysfs histogram file read handler.232 *233 * This function is called when the 'histogram' file is opened for reading234 * It prints the following histogram information -235 * - Number of histogram samples236 * - Receive packet number of each rx_rate237 * - Receive packet number of each snr238 * - Receive packet number of each nosie_flr239 * - Receive packet number of each signal streath240 */241static ssize_t242mwifiex_histogram_read(struct file *file, char __user *ubuf,243 size_t count, loff_t *ppos)244{245 struct mwifiex_private *priv =246 (struct mwifiex_private *)file->private_data;247 ssize_t ret;248 struct mwifiex_histogram_data *phist_data;249 int i, value;250 unsigned long page = get_zeroed_page(GFP_KERNEL);251 char *p = (char *)page;252 253 if (!p)254 return -ENOMEM;255 256 if (!priv || !priv->hist_data) {257 ret = -EFAULT;258 goto free_and_exit;259 }260 261 phist_data = priv->hist_data;262 263 p += sprintf(p, "\n"264 "total samples = %d\n",265 atomic_read(&phist_data->num_samples));266 267 p += sprintf(p,268 "rx rates (in Mbps): 0=1M 1=2M 2=5.5M 3=11M 4=6M 5=9M 6=12M\n"269 "7=18M 8=24M 9=36M 10=48M 11=54M 12-27=MCS0-15(BW20) 28-43=MCS0-15(BW40)\n");270 271 if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) {272 p += sprintf(p,273 "44-53=MCS0-9(VHT:BW20) 54-63=MCS0-9(VHT:BW40) 64-73=MCS0-9(VHT:BW80)\n\n");274 } else {275 p += sprintf(p, "\n");276 }277 278 for (i = 0; i < MWIFIEX_MAX_RX_RATES; i++) {279 value = atomic_read(&phist_data->rx_rate[i]);280 if (value)281 p += sprintf(p, "rx_rate[%02d] = %d\n", i, value);282 }283 284 if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) {285 for (i = MWIFIEX_MAX_RX_RATES; i < MWIFIEX_MAX_AC_RX_RATES;286 i++) {287 value = atomic_read(&phist_data->rx_rate[i]);288 if (value)289 p += sprintf(p, "rx_rate[%02d] = %d\n",290 i, value);291 }292 }293 294 for (i = 0; i < MWIFIEX_MAX_SNR; i++) {295 value = atomic_read(&phist_data->snr[i]);296 if (value)297 p += sprintf(p, "snr[%02ddB] = %d\n", i, value);298 }299 for (i = 0; i < MWIFIEX_MAX_NOISE_FLR; i++) {300 value = atomic_read(&phist_data->noise_flr[i]);301 if (value)302 p += sprintf(p, "noise_flr[%02ddBm] = %d\n",303 (int)(i-128), value);304 }305 for (i = 0; i < MWIFIEX_MAX_SIG_STRENGTH; i++) {306 value = atomic_read(&phist_data->sig_str[i]);307 if (value)308 p += sprintf(p, "sig_strength[-%02ddBm] = %d\n",309 i, value);310 }311 312 ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,313 (unsigned long)p - page);314 315free_and_exit:316 free_page(page);317 return ret;318}319 320static ssize_t321mwifiex_histogram_write(struct file *file, const char __user *ubuf,322 size_t count, loff_t *ppos)323{324 struct mwifiex_private *priv = (void *)file->private_data;325 326 if (priv && priv->hist_data)327 mwifiex_hist_data_reset(priv);328 return 0;329}330 331static struct mwifiex_debug_info info;332 333/*334 * Proc debug file read handler.335 *336 * This function is called when the 'debug' file is opened for reading337 * It prints the following log information -338 * - Interrupt count339 * - WMM AC VO packets count340 * - WMM AC VI packets count341 * - WMM AC BE packets count342 * - WMM AC BK packets count343 * - Maximum Tx buffer size344 * - Tx buffer size345 * - Current Tx buffer size346 * - Power Save mode347 * - Power Save state348 * - Deep Sleep status349 * - Device wakeup required status350 * - Number of wakeup tries351 * - Host Sleep configured status352 * - Host Sleep activated status353 * - Number of Tx timeouts354 * - Number of command timeouts355 * - Last timed out command ID356 * - Last timed out command action357 * - Last command ID358 * - Last command action359 * - Last command index360 * - Last command response ID361 * - Last command response index362 * - Last event363 * - Last event index364 * - Number of host to card command failures365 * - Number of sleep confirm command failures366 * - Number of host to card data failure367 * - Number of deauthentication events368 * - Number of disassociation events369 * - Number of link lost events370 * - Number of deauthentication commands371 * - Number of association success commands372 * - Number of association failure commands373 * - Number of commands sent374 * - Number of data packets sent375 * - Number of command responses received376 * - Number of events received377 * - Tx BA stream table (TID, RA)378 * - Rx reorder table (TID, TA, Start window, Window size, Buffer)379 */380static ssize_t381mwifiex_debug_read(struct file *file, char __user *ubuf,382 size_t count, loff_t *ppos)383{384 struct mwifiex_private *priv =385 (struct mwifiex_private *) file->private_data;386 unsigned long page = get_zeroed_page(GFP_KERNEL);387 char *p = (char *) page;388 ssize_t ret;389 390 if (!p)391 return -ENOMEM;392 393 ret = mwifiex_get_debug_info(priv, &info);394 if (ret)395 goto free_and_exit;396 397 p += mwifiex_debug_info_to_buffer(priv, p, &info);398 399 ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page,400 (unsigned long) p - page);401 402free_and_exit:403 free_page(page);404 return ret;405}406 407static u32 saved_reg_type, saved_reg_offset, saved_reg_value;408 409/*410 * Proc regrdwr file write handler.411 *412 * This function is called when the 'regrdwr' file is opened for writing413 *414 * This function can be used to write to a register.415 */416static ssize_t417mwifiex_regrdwr_write(struct file *file,418 const char __user *ubuf, size_t count, loff_t *ppos)419{420 char *buf;421 int ret;422 u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX;423 424 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));425 if (IS_ERR(buf))426 return PTR_ERR(buf);427 428 if (sscanf(buf, "%u %x %x", ®_type, ®_offset, ®_value) != 3) {429 ret = -EINVAL;430 goto done;431 }432 433 if (reg_type == 0 || reg_offset == 0) {434 ret = -EINVAL;435 goto done;436 } else {437 saved_reg_type = reg_type;438 saved_reg_offset = reg_offset;439 saved_reg_value = reg_value;440 ret = count;441 }442done:443 kfree(buf);444 return ret;445}446 447/*448 * Proc regrdwr file read handler.449 *450 * This function is called when the 'regrdwr' file is opened for reading451 *452 * This function can be used to read from a register.453 */454static ssize_t455mwifiex_regrdwr_read(struct file *file, char __user *ubuf,456 size_t count, loff_t *ppos)457{458 struct mwifiex_private *priv =459 (struct mwifiex_private *) file->private_data;460 unsigned long addr = get_zeroed_page(GFP_KERNEL);461 char *buf = (char *) addr;462 int pos = 0, ret = 0;463 u32 reg_value;464 465 if (!buf)466 return -ENOMEM;467 468 if (!saved_reg_type) {469 /* No command has been given */470 pos += snprintf(buf, PAGE_SIZE, "0");471 goto done;472 }473 /* Set command has been given */474 if (saved_reg_value != UINT_MAX) {475 ret = mwifiex_reg_write(priv, saved_reg_type, saved_reg_offset,476 saved_reg_value);477 478 pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n",479 saved_reg_type, saved_reg_offset,480 saved_reg_value);481 482 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);483 484 goto done;485 }486 /* Get command has been given */487 ret = mwifiex_reg_read(priv, saved_reg_type,488 saved_reg_offset, ®_value);489 if (ret) {490 ret = -EINVAL;491 goto done;492 }493 494 pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", saved_reg_type,495 saved_reg_offset, reg_value);496 497 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);498 499done:500 free_page(addr);501 return ret;502}503 504/* Proc debug_mask file read handler.505 * This function is called when the 'debug_mask' file is opened for reading506 * This function can be used read driver debugging mask value.507 */508static ssize_t509mwifiex_debug_mask_read(struct file *file, char __user *ubuf,510 size_t count, loff_t *ppos)511{512 struct mwifiex_private *priv =513 (struct mwifiex_private *)file->private_data;514 unsigned long page = get_zeroed_page(GFP_KERNEL);515 char *buf = (char *)page;516 size_t ret = 0;517 int pos = 0;518 519 if (!buf)520 return -ENOMEM;521 522 pos += snprintf(buf, PAGE_SIZE, "debug mask=0x%08x\n",523 priv->adapter->debug_mask);524 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);525 526 free_page(page);527 return ret;528}529 530/* Proc debug_mask file read handler.531 * This function is called when the 'debug_mask' file is opened for reading532 * This function can be used read driver debugging mask value.533 */534static ssize_t535mwifiex_debug_mask_write(struct file *file, const char __user *ubuf,536 size_t count, loff_t *ppos)537{538 int ret;539 unsigned long debug_mask;540 struct mwifiex_private *priv = (void *)file->private_data;541 char *buf;542 543 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));544 if (IS_ERR(buf))545 return PTR_ERR(buf);546 547 if (kstrtoul(buf, 0, &debug_mask)) {548 ret = -EINVAL;549 goto done;550 }551 552 priv->adapter->debug_mask = debug_mask;553 ret = count;554done:555 kfree(buf);556 return ret;557}558 559/* debugfs verext file write handler.560 * This function is called when the 'verext' file is opened for write561 */562static ssize_t563mwifiex_verext_write(struct file *file, const char __user *ubuf,564 size_t count, loff_t *ppos)565{566 int ret;567 u32 versionstrsel;568 struct mwifiex_private *priv = (void *)file->private_data;569 570 ret = kstrtou32_from_user(ubuf, count, 10, &versionstrsel);571 if (ret)572 return ret;573 574 priv->versionstrsel = versionstrsel;575 576 return count;577}578 579/* Proc verext file read handler.580 * This function is called when the 'verext' file is opened for reading581 * This function can be used read driver exteneed verion string.582 */583static ssize_t584mwifiex_verext_read(struct file *file, char __user *ubuf,585 size_t count, loff_t *ppos)586{587 struct mwifiex_private *priv =588 (struct mwifiex_private *)file->private_data;589 char buf[256];590 int ret;591 592 mwifiex_get_ver_ext(priv, priv->versionstrsel);593 ret = snprintf(buf, sizeof(buf), "version string: %s\n",594 priv->version_str);595 596 return simple_read_from_buffer(ubuf, count, ppos, buf, ret);597}598 599/* Proc memrw file write handler.600 * This function is called when the 'memrw' file is opened for writing601 * This function can be used to write to a memory location.602 */603static ssize_t604mwifiex_memrw_write(struct file *file, const char __user *ubuf, size_t count,605 loff_t *ppos)606{607 int ret;608 char cmd;609 struct mwifiex_ds_mem_rw mem_rw;610 u16 cmd_action;611 struct mwifiex_private *priv = (void *)file->private_data;612 char *buf;613 614 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));615 if (IS_ERR(buf))616 return PTR_ERR(buf);617 618 ret = sscanf(buf, "%c %x %x", &cmd, &mem_rw.addr, &mem_rw.value);619 if (ret != 3) {620 ret = -EINVAL;621 goto done;622 }623 624 if ((cmd == 'r') || (cmd == 'R')) {625 cmd_action = HostCmd_ACT_GEN_GET;626 mem_rw.value = 0;627 } else if ((cmd == 'w') || (cmd == 'W')) {628 cmd_action = HostCmd_ACT_GEN_SET;629 } else {630 ret = -EINVAL;631 goto done;632 }633 634 memcpy(&priv->mem_rw, &mem_rw, sizeof(mem_rw));635 if (mwifiex_send_cmd(priv, HostCmd_CMD_MEM_ACCESS, cmd_action, 0,636 &mem_rw, true))637 ret = -1;638 else639 ret = count;640 641done:642 kfree(buf);643 return ret;644}645 646/* Proc memrw file read handler.647 * This function is called when the 'memrw' file is opened for reading648 * This function can be used to read from a memory location.649 */650static ssize_t651mwifiex_memrw_read(struct file *file, char __user *ubuf,652 size_t count, loff_t *ppos)653{654 struct mwifiex_private *priv = (void *)file->private_data;655 unsigned long addr = get_zeroed_page(GFP_KERNEL);656 char *buf = (char *)addr;657 int ret, pos = 0;658 659 if (!buf)660 return -ENOMEM;661 662 pos += snprintf(buf, PAGE_SIZE, "0x%x 0x%x\n", priv->mem_rw.addr,663 priv->mem_rw.value);664 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);665 666 free_page(addr);667 return ret;668}669 670static u32 saved_offset = -1, saved_bytes = -1;671 672/*673 * Proc rdeeprom file write handler.674 *675 * This function is called when the 'rdeeprom' file is opened for writing676 *677 * This function can be used to write to a RDEEPROM location.678 */679static ssize_t680mwifiex_rdeeprom_write(struct file *file,681 const char __user *ubuf, size_t count, loff_t *ppos)682{683 char *buf;684 int ret = 0;685 int offset = -1, bytes = -1;686 687 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));688 if (IS_ERR(buf))689 return PTR_ERR(buf);690 691 if (sscanf(buf, "%d %d", &offset, &bytes) != 2) {692 ret = -EINVAL;693 goto done;694 }695 696 if (offset == -1 || bytes == -1) {697 ret = -EINVAL;698 goto done;699 } else {700 saved_offset = offset;701 saved_bytes = bytes;702 ret = count;703 }704done:705 kfree(buf);706 return ret;707}708 709/*710 * Proc rdeeprom read write handler.711 *712 * This function is called when the 'rdeeprom' file is opened for reading713 *714 * This function can be used to read from a RDEEPROM location.715 */716static ssize_t717mwifiex_rdeeprom_read(struct file *file, char __user *ubuf,718 size_t count, loff_t *ppos)719{720 struct mwifiex_private *priv =721 (struct mwifiex_private *) file->private_data;722 unsigned long addr = get_zeroed_page(GFP_KERNEL);723 char *buf = (char *) addr;724 int pos, ret, i;725 u8 value[MAX_EEPROM_DATA];726 727 if (!buf)728 return -ENOMEM;729 730 if (saved_offset == -1) {731 /* No command has been given */732 pos = snprintf(buf, PAGE_SIZE, "0");733 goto done;734 }735 736 /* Get command has been given */737 ret = mwifiex_eeprom_read(priv, (u16) saved_offset,738 (u16) saved_bytes, value);739 if (ret) {740 ret = -EINVAL;741 goto out_free;742 }743 744 pos = snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes);745 746 for (i = 0; i < saved_bytes; i++)747 pos += scnprintf(buf + pos, PAGE_SIZE - pos, "%d ", value[i]);748 749done:750 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);751out_free:752 free_page(addr);753 return ret;754}755 756/* Proc hscfg file write handler757 * This function can be used to configure the host sleep parameters.758 */759static ssize_t760mwifiex_hscfg_write(struct file *file, const char __user *ubuf,761 size_t count, loff_t *ppos)762{763 struct mwifiex_private *priv = (void *)file->private_data;764 char *buf;765 int ret, arg_num;766 struct mwifiex_ds_hs_cfg hscfg;767 int conditions = HS_CFG_COND_DEF;768 u32 gpio = HS_CFG_GPIO_DEF, gap = HS_CFG_GAP_DEF;769 770 buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));771 if (IS_ERR(buf))772 return PTR_ERR(buf);773 774 arg_num = sscanf(buf, "%d %x %x", &conditions, &gpio, &gap);775 776 memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg));777 778 if (arg_num > 3) {779 mwifiex_dbg(priv->adapter, ERROR,780 "Too many arguments\n");781 ret = -EINVAL;782 goto done;783 }784 785 if (arg_num >= 1 && arg_num < 3)786 mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET,787 MWIFIEX_SYNC_CMD, &hscfg);788 789 if (arg_num) {790 if (conditions == HS_CFG_CANCEL) {791 mwifiex_cancel_hs(priv, MWIFIEX_ASYNC_CMD);792 ret = count;793 goto done;794 }795 hscfg.conditions = conditions;796 }797 if (arg_num >= 2)798 hscfg.gpio = gpio;799 if (arg_num == 3)800 hscfg.gap = gap;801 802 hscfg.is_invoke_hostcmd = false;803 mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET,804 MWIFIEX_SYNC_CMD, &hscfg);805 806 mwifiex_enable_hs(priv->adapter);807 clear_bit(MWIFIEX_IS_HS_ENABLING, &priv->adapter->work_flags);808 ret = count;809done:810 kfree(buf);811 return ret;812}813 814/* Proc hscfg file read handler815 * This function can be used to read host sleep configuration816 * parameters from driver.817 */818static ssize_t819mwifiex_hscfg_read(struct file *file, char __user *ubuf,820 size_t count, loff_t *ppos)821{822 struct mwifiex_private *priv = (void *)file->private_data;823 unsigned long addr = get_zeroed_page(GFP_KERNEL);824 char *buf = (char *)addr;825 int pos, ret;826 struct mwifiex_ds_hs_cfg hscfg;827 828 if (!buf)829 return -ENOMEM;830 831 mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET,832 MWIFIEX_SYNC_CMD, &hscfg);833 834 pos = snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", hscfg.conditions,835 hscfg.gpio, hscfg.gap);836 837 ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);838 839 free_page(addr);840 return ret;841}842 843static ssize_t844mwifiex_timeshare_coex_read(struct file *file, char __user *ubuf,845 size_t count, loff_t *ppos)846{847 struct mwifiex_private *priv = file->private_data;848 char buf[3];849 bool timeshare_coex;850 int ret;851 unsigned int len;852 853 if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15)854 return -EOPNOTSUPP;855 856 ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX,857 HostCmd_ACT_GEN_GET, 0, ×hare_coex, true);858 if (ret)859 return ret;860 861 len = sprintf(buf, "%d\n", timeshare_coex);862 return simple_read_from_buffer(ubuf, count, ppos, buf, len);863}864 865static ssize_t866mwifiex_timeshare_coex_write(struct file *file, const char __user *ubuf,867 size_t count, loff_t *ppos)868{869 bool timeshare_coex;870 struct mwifiex_private *priv = file->private_data;871 int ret;872 873 if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15)874 return -EOPNOTSUPP;875 876 ret = kstrtobool_from_user(ubuf, count, ×hare_coex);877 if (ret)878 return ret;879 880 ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX,881 HostCmd_ACT_GEN_SET, 0, ×hare_coex, true);882 if (ret)883 return ret;884 else885 return count;886}887 888static ssize_t889mwifiex_reset_write(struct file *file,890 const char __user *ubuf, size_t count, loff_t *ppos)891{892 struct mwifiex_private *priv = file->private_data;893 struct mwifiex_adapter *adapter = priv->adapter;894 bool result;895 int rc;896 897 rc = kstrtobool_from_user(ubuf, count, &result);898 if (rc)899 return rc;900 901 if (!result)902 return -EINVAL;903 904 if (adapter->if_ops.card_reset) {905 dev_info(adapter->dev, "Resetting per request\n");906 adapter->if_ops.card_reset(adapter);907 }908 909 return count;910}911 912#define MWIFIEX_DFS_ADD_FILE(name) do { \913 debugfs_create_file(#name, 0644, priv->dfs_dev_dir, priv, \914 &mwifiex_dfs_##name##_fops); \915} while (0);916 917#define MWIFIEX_DFS_FILE_OPS(name) \918static const struct file_operations mwifiex_dfs_##name##_fops = { \919 .read = mwifiex_##name##_read, \920 .write = mwifiex_##name##_write, \921 .open = simple_open, \922};923 924#define MWIFIEX_DFS_FILE_READ_OPS(name) \925static const struct file_operations mwifiex_dfs_##name##_fops = { \926 .read = mwifiex_##name##_read, \927 .open = simple_open, \928};929 930#define MWIFIEX_DFS_FILE_WRITE_OPS(name) \931static const struct file_operations mwifiex_dfs_##name##_fops = { \932 .write = mwifiex_##name##_write, \933 .open = simple_open, \934};935 936 937MWIFIEX_DFS_FILE_READ_OPS(info);938MWIFIEX_DFS_FILE_READ_OPS(debug);939MWIFIEX_DFS_FILE_READ_OPS(getlog);940MWIFIEX_DFS_FILE_OPS(regrdwr);941MWIFIEX_DFS_FILE_OPS(rdeeprom);942MWIFIEX_DFS_FILE_OPS(memrw);943MWIFIEX_DFS_FILE_OPS(hscfg);944MWIFIEX_DFS_FILE_OPS(histogram);945MWIFIEX_DFS_FILE_OPS(debug_mask);946MWIFIEX_DFS_FILE_OPS(timeshare_coex);947MWIFIEX_DFS_FILE_WRITE_OPS(reset);948MWIFIEX_DFS_FILE_OPS(verext);949 950/*951 * This function creates the debug FS directory structure and the files.952 */953void954mwifiex_dev_debugfs_init(struct mwifiex_private *priv)955{956 if (!mwifiex_dfs_dir || !priv)957 return;958 959 priv->dfs_dev_dir = debugfs_create_dir(priv->netdev->name,960 mwifiex_dfs_dir);961 962 MWIFIEX_DFS_ADD_FILE(info);963 MWIFIEX_DFS_ADD_FILE(debug);964 MWIFIEX_DFS_ADD_FILE(getlog);965 MWIFIEX_DFS_ADD_FILE(regrdwr);966 MWIFIEX_DFS_ADD_FILE(rdeeprom);967 968 MWIFIEX_DFS_ADD_FILE(memrw);969 MWIFIEX_DFS_ADD_FILE(hscfg);970 MWIFIEX_DFS_ADD_FILE(histogram);971 MWIFIEX_DFS_ADD_FILE(debug_mask);972 MWIFIEX_DFS_ADD_FILE(timeshare_coex);973 MWIFIEX_DFS_ADD_FILE(reset);974 MWIFIEX_DFS_ADD_FILE(verext);975}976 977/*978 * This function removes the debug FS directory structure and the files.979 */980void981mwifiex_dev_debugfs_remove(struct mwifiex_private *priv)982{983 if (!priv)984 return;985 986 debugfs_remove_recursive(priv->dfs_dev_dir);987}988 989/*990 * This function creates the top level proc directory.991 */992void993mwifiex_debugfs_init(void)994{995 if (!mwifiex_dfs_dir)996 mwifiex_dfs_dir = debugfs_create_dir("mwifiex", NULL);997}998 999/*1000 * This function removes the top level proc directory.1001 */1002void1003mwifiex_debugfs_remove(void)1004{1005 debugfs_remove(mwifiex_dfs_dir);1006}1007