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(to: user_buf, count, ppos, from: buf, available: 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(to: buf, from: user_buf, n: 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(to: buf, from: user_buf, n: 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(s: begin, base: 0, res: &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, arg1: arg[0], arg2: arg[1], arg3: arg[2], arg4: arg[3], arg5: 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(size: 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(bitmap: wcn->fw_feat_caps, cap: i)) { |
157 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "%s\n" , |
158 | wcn36xx_firmware_get_cap_name(x: i)); |
159 | } |
160 | if (len >= buf_len) |
161 | break; |
162 | } |
163 | mutex_unlock(lock: &wcn->hal_mutex); |
164 | |
165 | ret = simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
166 | kfree(objp: 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 | parent: wcn->hw->wiphy->debugfsdir); |
196 | if (IS_ERR(ptr: 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(dentry: dfs->rootdir); |
211 | } |
212 | |
213 | #endif /* CONFIG_WCN36XX_DEBUGFS */ |
214 | |