1 /******************************************************************************
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.
8 * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
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.
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.
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,
24 * The full GNU General Public License is included in this distribution
25 * in the file called LICENSE.GPL.
27 * Contact Information:
28 * Intel Linux Wireless <ilw@linux.intel.com>
29 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
33 * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
34 * All rights reserved.
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
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
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.
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.
62 *****************************************************************************/
67 struct iwl_dbgfs_mvm_ctx {
69 struct ieee80211_vif *vif;
72 static int iwl_dbgfs_open_file_generic(struct inode *inode, struct file *file)
74 file->private_data = inode->i_private;
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)
82 struct iwl_mvm *mvm = file->private_data;
88 if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR)
91 memset(buf, 0, sizeof(buf));
92 buf_size = min(count, sizeof(buf) - 1);
93 if (copy_from_user(buf, user_buf, buf_size))
96 if (sscanf(buf, "%x", &scd_q_msk) != 1)
99 IWL_ERR(mvm, "FLUSHING queues: scd_q_msk = 0x%x\n", scd_q_msk);
101 mutex_lock(&mvm->mutex);
102 ret = iwl_mvm_flush_tx_path(mvm, scd_q_msk, true) ? : count;
103 mutex_unlock(&mvm->mutex);
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)
112 struct iwl_mvm *mvm = file->private_data;
113 struct ieee80211_sta *sta;
116 int buf_size, sta_id, drain, ret;
118 if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR)
121 memset(buf, 0, sizeof(buf));
122 buf_size = min(count, sizeof(buf) - 1);
123 if (copy_from_user(buf, user_buf, buf_size))
126 if (sscanf(buf, "%d %d", &sta_id, &drain) != 2)
129 mutex_lock(&mvm->mutex);
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))
136 ret = iwl_mvm_drain_sta(mvm, (void *)sta->drv_priv, drain) ? :
139 mutex_unlock(&mvm->mutex);
144 static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf,
145 size_t count, loff_t *ppos)
147 struct iwl_mvm *mvm = file->private_data;
148 const struct fw_img *img;
149 int ofs, len, pos = 0;
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)
159 img = &mvm->fw->img[mvm->cur_ucode];
160 mvm->dbgfs_sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
162 len = mvm->dbgfs_sram_len;
164 bufsz = len * 4 + 256;
165 buf = kzalloc(bufsz, GFP_KERNEL);
169 ptr = kzalloc(len, GFP_KERNEL);
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);
179 iwl_trans_read_mem_bytes(mvm->trans,
180 mvm->dbgfs_sram_offset,
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,
186 pos += strlen(buf + pos);
191 ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
199 static ssize_t iwl_dbgfs_sram_write(struct file *file,
200 const char __user *user_buf, size_t count,
203 struct iwl_mvm *mvm = file->private_data;
208 memset(buf, 0, sizeof(buf));
209 buf_size = min(count, sizeof(buf) - 1);
210 if (copy_from_user(buf, user_buf, buf_size))
213 if (sscanf(buf, "%x,%x", &offset, &len) == 2) {
214 if ((offset & 0x3) || (len & 0x3))
216 mvm->dbgfs_sram_offset = offset;
217 mvm->dbgfs_sram_len = len;
219 mvm->dbgfs_sram_offset = 0;
220 mvm->dbgfs_sram_len = 0;
226 static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
227 size_t count, loff_t *ppos)
229 struct iwl_mvm *mvm = file->private_data;
230 struct ieee80211_sta *sta;
232 int i, pos = 0, bufsz = sizeof(buf);
234 mutex_lock(&mvm->mutex);
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));
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",
246 pos += scnprintf(buf + pos, bufsz - pos, "%pM\n",
250 mutex_unlock(&mvm->mutex);
252 return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
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)
259 struct iwl_mvm *mvm = file->private_data;
263 if (!mvm->ucode_loaded)
266 if (copy_from_user(buf, user_buf, sizeof(buf)))
269 if (sscanf(buf, "%d", &allow) != 1)
272 IWL_DEBUG_POWER(mvm, "%s device power down\n",
273 allow ? "allow" : "prevent");
276 * TODO: Send REPLY_DEBUG_CMD (0xf0) when FW support it
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)
286 struct iwl_mvm *mvm = file->private_data;
290 if (copy_from_user(buf, user_buf, sizeof(buf)))
293 if (sscanf(buf, "%d", &allow) != 1)
296 IWL_DEBUG_POWER(mvm, "%s device power down in d3\n",
297 allow ? "allow" : "prevent");
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
304 mvm->prevent_power_down_d3 = !allow;
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, \
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, \
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, \
331 #define MVM_DEBUGFS_ADD_FILE(name, parent, mode) do { \
332 if (!debugfs_create_file(#name, mode, parent, mvm, \
333 &iwl_dbgfs_##name##_ops)) \
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)) \
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);
351 int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
355 mvm->debugfs_dir = dbgfs_dir;
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);
365 * Create a symlink with mac80211. It will be removed when mac80211
366 * exists (before the opmode exists which removes the target.)
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))
376 IWL_ERR(mvm, "Can't create the mvm debugfs directory\n");