iwlwifi: a few fixes in license
[deliverable/linux.git] / drivers / net / wireless / iwlwifi / mvm / debugfs.c
1 /******************************************************************************
2 *
3 * This file is provided under a dual BSD/GPLv2 license. When using or
4 * redistributing this file, you may do so under either license.
5 *
6 * GPL LICENSE SUMMARY
7 *
8 * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of version 2 of the GNU General Public License as
12 * published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
22 * USA
23 *
24 * The full GNU General Public License is included in this distribution
25 * in the file called COPYING.
26 *
27 * Contact Information:
28 * Intel Linux Wireless <ilw@linux.intel.com>
29 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30 *
31 * BSD LICENSE
32 *
33 * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 *
40 * * Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * * Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in
44 * the documentation and/or other materials provided with the
45 * distribution.
46 * * Neither the name Intel Corporation nor the names of its
47 * contributors may be used to endorse or promote products derived
48 * from this software without specific prior written permission.
49 *
50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
54 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
55 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
56 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
57 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
58 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
59 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
60 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61 *
62 *****************************************************************************/
63 #include "mvm.h"
64 #include "sta.h"
65 #include "iwl-io.h"
66
67 struct iwl_dbgfs_mvm_ctx {
68 struct iwl_mvm *mvm;
69 struct ieee80211_vif *vif;
70 };
71
72 static int iwl_dbgfs_open_file_generic(struct inode *inode, struct file *file)
73 {
74 file->private_data = inode->i_private;
75 return 0;
76 }
77
78 static ssize_t iwl_dbgfs_tx_flush_write(struct file *file,
79 const char __user *user_buf,
80 size_t count, loff_t *ppos)
81 {
82 struct iwl_mvm *mvm = file->private_data;
83
84 char buf[16];
85 int buf_size, ret;
86 u32 scd_q_msk;
87
88 if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR)
89 return -EIO;
90
91 memset(buf, 0, sizeof(buf));
92 buf_size = min(count, sizeof(buf) - 1);
93 if (copy_from_user(buf, user_buf, buf_size))
94 return -EFAULT;
95
96 if (sscanf(buf, "%x", &scd_q_msk) != 1)
97 return -EINVAL;
98
99 IWL_ERR(mvm, "FLUSHING queues: scd_q_msk = 0x%x\n", scd_q_msk);
100
101 mutex_lock(&mvm->mutex);
102 ret = iwl_mvm_flush_tx_path(mvm, scd_q_msk, true) ? : count;
103 mutex_unlock(&mvm->mutex);
104
105 return ret;
106 }
107
108 static ssize_t iwl_dbgfs_sta_drain_write(struct file *file,
109 const char __user *user_buf,
110 size_t count, loff_t *ppos)
111 {
112 struct iwl_mvm *mvm = file->private_data;
113 struct ieee80211_sta *sta;
114
115 char buf[8];
116 int buf_size, sta_id, drain, ret;
117
118 if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR)
119 return -EIO;
120
121 memset(buf, 0, sizeof(buf));
122 buf_size = min(count, sizeof(buf) - 1);
123 if (copy_from_user(buf, user_buf, buf_size))
124 return -EFAULT;
125
126 if (sscanf(buf, "%d %d", &sta_id, &drain) != 2)
127 return -EINVAL;
128
129 mutex_lock(&mvm->mutex);
130
131 sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
132 lockdep_is_held(&mvm->mutex));
133 if (IS_ERR_OR_NULL(sta))
134 ret = -ENOENT;
135 else
136 ret = iwl_mvm_drain_sta(mvm, (void *)sta->drv_priv, drain) ? :
137 count;
138
139 mutex_unlock(&mvm->mutex);
140
141 return ret;
142 }
143
144 static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf,
145 size_t count, loff_t *ppos)
146 {
147 struct iwl_mvm *mvm = file->private_data;
148 const struct fw_img *img;
149 int ofs, len, pos = 0;
150 size_t bufsz, ret;
151 char *buf;
152 u8 *ptr;
153
154 /* default is to dump the entire data segment */
155 if (!mvm->dbgfs_sram_offset && !mvm->dbgfs_sram_len) {
156 mvm->dbgfs_sram_offset = 0x800000;
157 if (!mvm->ucode_loaded)
158 return -EINVAL;
159 img = &mvm->fw->img[mvm->cur_ucode];
160 mvm->dbgfs_sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
161 }
162 len = mvm->dbgfs_sram_len;
163
164 bufsz = len * 4 + 256;
165 buf = kzalloc(bufsz, GFP_KERNEL);
166 if (!buf)
167 return -ENOMEM;
168
169 ptr = kzalloc(len, GFP_KERNEL);
170 if (!ptr) {
171 kfree(buf);
172 return -ENOMEM;
173 }
174
175 pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", len);
176 pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n",
177 mvm->dbgfs_sram_offset);
178
179 iwl_trans_read_mem_bytes(mvm->trans,
180 mvm->dbgfs_sram_offset,
181 ptr, len);
182 for (ofs = 0; ofs < len; ofs += 16) {
183 pos += scnprintf(buf + pos, bufsz - pos, "0x%.4x ", ofs);
184 hex_dump_to_buffer(ptr + ofs, 16, 16, 1, buf + pos,
185 bufsz - pos, false);
186 pos += strlen(buf + pos);
187 if (bufsz - pos > 0)
188 buf[pos++] = '\n';
189 }
190
191 ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
192
193 kfree(buf);
194 kfree(ptr);
195
196 return ret;
197 }
198
199 static ssize_t iwl_dbgfs_sram_write(struct file *file,
200 const char __user *user_buf, size_t count,
201 loff_t *ppos)
202 {
203 struct iwl_mvm *mvm = file->private_data;
204 char buf[64];
205 int buf_size;
206 u32 offset, len;
207
208 memset(buf, 0, sizeof(buf));
209 buf_size = min(count, sizeof(buf) - 1);
210 if (copy_from_user(buf, user_buf, buf_size))
211 return -EFAULT;
212
213 if (sscanf(buf, "%x,%x", &offset, &len) == 2) {
214 if ((offset & 0x3) || (len & 0x3))
215 return -EINVAL;
216 mvm->dbgfs_sram_offset = offset;
217 mvm->dbgfs_sram_len = len;
218 } else {
219 mvm->dbgfs_sram_offset = 0;
220 mvm->dbgfs_sram_len = 0;
221 }
222
223 return count;
224 }
225
226 static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
227 size_t count, loff_t *ppos)
228 {
229 struct iwl_mvm *mvm = file->private_data;
230 struct ieee80211_sta *sta;
231 char buf[400];
232 int i, pos = 0, bufsz = sizeof(buf);
233
234 mutex_lock(&mvm->mutex);
235
236 for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
237 pos += scnprintf(buf + pos, bufsz - pos, "%.2d: ", i);
238 sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
239 lockdep_is_held(&mvm->mutex));
240 if (!sta)
241 pos += scnprintf(buf + pos, bufsz - pos, "N/A\n");
242 else if (IS_ERR(sta))
243 pos += scnprintf(buf + pos, bufsz - pos, "%ld\n",
244 PTR_ERR(sta));
245 else
246 pos += scnprintf(buf + pos, bufsz - pos, "%pM\n",
247 sta->addr);
248 }
249
250 mutex_unlock(&mvm->mutex);
251
252 return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
253 }
254
255 static ssize_t iwl_dbgfs_power_down_allow_write(struct file *file,
256 const char __user *user_buf,
257 size_t count, loff_t *ppos)
258 {
259 struct iwl_mvm *mvm = file->private_data;
260 char buf[8] = {};
261 int allow;
262
263 if (!mvm->ucode_loaded)
264 return -EIO;
265
266 if (copy_from_user(buf, user_buf, sizeof(buf)))
267 return -EFAULT;
268
269 if (sscanf(buf, "%d", &allow) != 1)
270 return -EINVAL;
271
272 IWL_DEBUG_POWER(mvm, "%s device power down\n",
273 allow ? "allow" : "prevent");
274
275 /*
276 * TODO: Send REPLY_DEBUG_CMD (0xf0) when FW support it
277 */
278
279 return count;
280 }
281
282 static ssize_t iwl_dbgfs_power_down_d3_allow_write(struct file *file,
283 const char __user *user_buf,
284 size_t count, loff_t *ppos)
285 {
286 struct iwl_mvm *mvm = file->private_data;
287 char buf[8] = {};
288 int allow;
289
290 if (copy_from_user(buf, user_buf, sizeof(buf)))
291 return -EFAULT;
292
293 if (sscanf(buf, "%d", &allow) != 1)
294 return -EINVAL;
295
296 IWL_DEBUG_POWER(mvm, "%s device power down in d3\n",
297 allow ? "allow" : "prevent");
298
299 /*
300 * TODO: When WoWLAN FW alive notification happens, driver will send
301 * REPLY_DEBUG_CMD setting power_down_allow flag according to
302 * mvm->prevent_power_down_d3
303 */
304 mvm->prevent_power_down_d3 = !allow;
305
306 return count;
307 }
308
309 #define MVM_DEBUGFS_READ_FILE_OPS(name) \
310 static const struct file_operations iwl_dbgfs_##name##_ops = { \
311 .read = iwl_dbgfs_##name##_read, \
312 .open = iwl_dbgfs_open_file_generic, \
313 .llseek = generic_file_llseek, \
314 }
315
316 #define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name) \
317 static const struct file_operations iwl_dbgfs_##name##_ops = { \
318 .write = iwl_dbgfs_##name##_write, \
319 .read = iwl_dbgfs_##name##_read, \
320 .open = iwl_dbgfs_open_file_generic, \
321 .llseek = generic_file_llseek, \
322 };
323
324 #define MVM_DEBUGFS_WRITE_FILE_OPS(name) \
325 static const struct file_operations iwl_dbgfs_##name##_ops = { \
326 .write = iwl_dbgfs_##name##_write, \
327 .open = iwl_dbgfs_open_file_generic, \
328 .llseek = generic_file_llseek, \
329 };
330
331 #define MVM_DEBUGFS_ADD_FILE(name, parent, mode) do { \
332 if (!debugfs_create_file(#name, mode, parent, mvm, \
333 &iwl_dbgfs_##name##_ops)) \
334 goto err; \
335 } while (0)
336
337 #define MVM_DEBUGFS_ADD_FILE_VIF(name, parent, mode) do { \
338 if (!debugfs_create_file(#name, mode, parent, vif, \
339 &iwl_dbgfs_##name##_ops)) \
340 goto err; \
341 } while (0)
342
343 /* Device wide debugfs entries */
344 MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush);
345 MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain);
346 MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram);
347 MVM_DEBUGFS_READ_FILE_OPS(stations);
348 MVM_DEBUGFS_WRITE_FILE_OPS(power_down_allow);
349 MVM_DEBUGFS_WRITE_FILE_OPS(power_down_d3_allow);
350
351 int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
352 {
353 char buf[100];
354
355 mvm->debugfs_dir = dbgfs_dir;
356
357 MVM_DEBUGFS_ADD_FILE(tx_flush, mvm->debugfs_dir, S_IWUSR);
358 MVM_DEBUGFS_ADD_FILE(sta_drain, mvm->debugfs_dir, S_IWUSR);
359 MVM_DEBUGFS_ADD_FILE(sram, mvm->debugfs_dir, S_IWUSR | S_IRUSR);
360 MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR);
361 MVM_DEBUGFS_ADD_FILE(power_down_allow, mvm->debugfs_dir, S_IWUSR);
362 MVM_DEBUGFS_ADD_FILE(power_down_d3_allow, mvm->debugfs_dir, S_IWUSR);
363
364 /*
365 * Create a symlink with mac80211. It will be removed when mac80211
366 * exists (before the opmode exists which removes the target.)
367 */
368 snprintf(buf, 100, "../../%s/%s",
369 dbgfs_dir->d_parent->d_parent->d_name.name,
370 dbgfs_dir->d_parent->d_name.name);
371 if (!debugfs_create_symlink("iwlwifi", mvm->hw->wiphy->debugfsdir, buf))
372 goto err;
373
374 return 0;
375 err:
376 IWL_ERR(mvm, "Can't create the mvm debugfs directory\n");
377 return -ENOMEM;
378 }
This page took 0.047637 seconds and 5 git commands to generate.