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 any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 18 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_DEBUGFS 27 28 static 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 else 42 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 52 static 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 93 static 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 99 static 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 135 static const struct file_operations fops_wcn36xx_dump = { 136 .open = simple_open, 137 .write = write_file_dump, 138 }; 139 140 static 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 171 static 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 190 void 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 207 void 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