214 lines · c
1/*2 * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>3 *4 * Permission to use, copy, modify, and/or distribute this software for any5 * purpose with or without fee is hereby granted, provided that the above6 * copyright notice and this permission notice appear in all copies.7 *8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.15 */16 17#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt18 19#include <linux/debugfs.h>20#include <linux/uaccess.h>21#include "wcn36xx.h"22#include "debug.h"23#include "pmc.h"24#include "firmware.h"25 26#ifdef CONFIG_WCN36XX_DEBUGFS27 28static ssize_t read_file_bool_bmps(struct file *file, char __user *user_buf,29 size_t count, loff_t *ppos)30{31 struct wcn36xx *wcn = file->private_data;32 struct wcn36xx_vif *vif_priv = NULL;33 struct ieee80211_vif *vif = NULL;34 char buf[3];35 36 list_for_each_entry(vif_priv, &wcn->vif_list, list) {37 vif = wcn36xx_priv_to_vif(vif_priv);38 if (NL80211_IFTYPE_STATION == vif->type) {39 if (vif_priv->pw_state == WCN36XX_BMPS)40 buf[0] = '1';41 else42 buf[0] = '0';43 break;44 }45 }46 buf[1] = '\n';47 buf[2] = 0x00;48 49 return simple_read_from_buffer(user_buf, count, ppos, buf, 2);50}51 52static ssize_t write_file_bool_bmps(struct file *file,53 const char __user *user_buf,54 size_t count, loff_t *ppos)55{56 struct wcn36xx *wcn = file->private_data;57 struct wcn36xx_vif *vif_priv = NULL;58 struct ieee80211_vif *vif = NULL;59 60 char buf[32];61 int buf_size;62 63 buf_size = min(count, (sizeof(buf)-1));64 if (copy_from_user(buf, user_buf, buf_size))65 return -EFAULT;66 67 switch (buf[0]) {68 case 'y':69 case 'Y':70 case '1':71 list_for_each_entry(vif_priv, &wcn->vif_list, list) {72 vif = wcn36xx_priv_to_vif(vif_priv);73 if (NL80211_IFTYPE_STATION == vif->type) {74 wcn36xx_enable_keep_alive_null_packet(wcn, vif);75 wcn36xx_pmc_enter_bmps_state(wcn, vif);76 }77 }78 break;79 case 'n':80 case 'N':81 case '0':82 list_for_each_entry(vif_priv, &wcn->vif_list, list) {83 vif = wcn36xx_priv_to_vif(vif_priv);84 if (NL80211_IFTYPE_STATION == vif->type)85 wcn36xx_pmc_exit_bmps_state(wcn, vif);86 }87 break;88 }89 90 return count;91}92 93static const struct file_operations fops_wcn36xx_bmps = {94 .open = simple_open,95 .read = read_file_bool_bmps,96 .write = write_file_bool_bmps,97};98 99static ssize_t write_file_dump(struct file *file,100 const char __user *user_buf,101 size_t count, loff_t *ppos)102{103 struct wcn36xx *wcn = file->private_data;104 char buf[255], *tmp;105 int buf_size;106 u32 arg[WCN36xx_MAX_DUMP_ARGS];107 int i;108 109 memset(buf, 0, sizeof(buf));110 memset(arg, 0, sizeof(arg));111 112 buf_size = min(count, (sizeof(buf) - 1));113 if (copy_from_user(buf, user_buf, buf_size))114 return -EFAULT;115 116 tmp = buf;117 118 for (i = 0; i < WCN36xx_MAX_DUMP_ARGS; i++) {119 char *begin;120 begin = strsep(&tmp, " ");121 if (begin == NULL)122 break;123 124 if (kstrtos32(begin, 0, &arg[i]) != 0)125 break;126 }127 128 wcn36xx_info("DUMP args is %d %d %d %d %d\n", arg[0], arg[1], arg[2],129 arg[3], arg[4]);130 wcn36xx_smd_dump_cmd_req(wcn, arg[0], arg[1], arg[2], arg[3], arg[4]);131 132 return count;133}134 135static const struct file_operations fops_wcn36xx_dump = {136 .open = simple_open,137 .write = write_file_dump,138};139 140static ssize_t read_file_firmware_feature_caps(struct file *file,141 char __user *user_buf,142 size_t count, loff_t *ppos)143{144 struct wcn36xx *wcn = file->private_data;145 size_t len = 0, buf_len = 2048;146 char *buf;147 int i;148 int ret;149 150 buf = kzalloc(buf_len, GFP_KERNEL);151 if (!buf)152 return -ENOMEM;153 154 mutex_lock(&wcn->hal_mutex);155 for (i = 0; i < MAX_FEATURE_SUPPORTED; i++) {156 if (wcn36xx_firmware_get_feat_caps(wcn->fw_feat_caps, i)) {157 len += scnprintf(buf + len, buf_len - len, "%s\n",158 wcn36xx_firmware_get_cap_name(i));159 }160 if (len >= buf_len)161 break;162 }163 mutex_unlock(&wcn->hal_mutex);164 165 ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);166 kfree(buf);167 168 return ret;169}170 171static const struct file_operations fops_wcn36xx_firmware_feat_caps = {172 .open = simple_open,173 .read = read_file_firmware_feature_caps,174};175 176#define ADD_FILE(name, mode, fop, priv_data) \177 do { \178 struct dentry *d; \179 d = debugfs_create_file(__stringify(name), \180 mode, dfs->rootdir, \181 priv_data, fop); \182 dfs->file_##name.dentry = d; \183 if (IS_ERR(d)) { \184 wcn36xx_warn("Create the debugfs entry failed");\185 dfs->file_##name.dentry = NULL; \186 } \187 } while (0)188 189 190void wcn36xx_debugfs_init(struct wcn36xx *wcn)191{192 struct wcn36xx_dfs_entry *dfs = &wcn->dfs;193 194 dfs->rootdir = debugfs_create_dir(KBUILD_MODNAME,195 wcn->hw->wiphy->debugfsdir);196 if (IS_ERR(dfs->rootdir)) {197 wcn36xx_warn("Create the debugfs failed\n");198 dfs->rootdir = NULL;199 }200 201 ADD_FILE(bmps_switcher, 0600, &fops_wcn36xx_bmps, wcn);202 ADD_FILE(dump, 0200, &fops_wcn36xx_dump, wcn);203 ADD_FILE(firmware_feat_caps, 0200,204 &fops_wcn36xx_firmware_feat_caps, wcn);205}206 207void wcn36xx_debugfs_exit(struct wcn36xx *wcn)208{209 struct wcn36xx_dfs_entry *dfs = &wcn->dfs;210 debugfs_remove_recursive(dfs->rootdir);211}212 213#endif /* CONFIG_WCN36XX_DEBUGFS */214