Linux 3.9-rc8
[firefly-linux-kernel-4.4.55.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 LICENSE.GPL.
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 }