root/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. _iwl_dbgfs_release
  2. iwl_fw_send_timestamp_marker_cmd
  3. iwl_fw_timestamp_marker_wk
  4. iwl_fw_trigger_timestamp
  5. iwl_dbgfs_timestamp_marker_write
  6. iwl_dbgfs_timestamp_marker_read
  7. iwl_dbgfs_send_hcmd_write
  8. iwl_fwrt_dbgfs_register

   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 - 2014 Intel Corporation. All rights reserved.
   9  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  10  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
  11  * Copyright (C) 2018 Intel Corporation
  12  *
  13  * This program is free software; you can redistribute it and/or modify
  14  * it under the terms of version 2 of the GNU General Public License as
  15  * published by the Free Software Foundation.
  16  *
  17  * This program is distributed in the hope that it will be useful, but
  18  * WITHOUT ANY WARRANTY; without even the implied warranty of
  19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  20  * General Public License for more details.
  21  *
  22  * The full GNU General Public License is included in this distribution
  23  * in the file called COPYING.
  24  *
  25  * Contact Information:
  26  *  Intel Linux Wireless <linuxwifi@intel.com>
  27  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
  28  *
  29  * BSD LICENSE
  30  *
  31  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  32  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  33  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
  34  * Copyright (C) 2018 Intel Corporation
  35  * All rights reserved.
  36  *
  37  * Redistribution and use in source and binary forms, with or without
  38  * modification, are permitted provided that the following conditions
  39  * are met:
  40  *
  41  *  * Redistributions of source code must retain the above copyright
  42  *    notice, this list of conditions and the following disclaimer.
  43  *  * Redistributions in binary form must reproduce the above copyright
  44  *    notice, this list of conditions and the following disclaimer in
  45  *    the documentation and/or other materials provided with the
  46  *    distribution.
  47  *  * Neither the name Intel Corporation nor the names of its
  48  *    contributors may be used to endorse or promote products derived
  49  *    from this software without specific prior written permission.
  50  *
  51  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  52  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  53  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  54  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  55  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  56  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  57  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  58  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  59  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  60  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  61  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  62  *
  63  *****************************************************************************/
  64 #include "api/commands.h"
  65 #include "debugfs.h"
  66 #include "dbg.h"
  67 
  68 #define FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype)                \
  69 struct dbgfs_##name##_data {                                            \
  70         argtype *arg;                                                   \
  71         bool read_done;                                                 \
  72         ssize_t rlen;                                                   \
  73         char rbuf[buflen];                                              \
  74 };                                                                      \
  75 static int _iwl_dbgfs_##name##_open(struct inode *inode,                \
  76                                     struct file *file)                  \
  77 {                                                                       \
  78         struct dbgfs_##name##_data *data;                               \
  79                                                                         \
  80         data = kzalloc(sizeof(*data), GFP_KERNEL);                      \
  81         if (!data)                                                      \
  82                 return -ENOMEM;                                         \
  83                                                                         \
  84         data->read_done = false;                                        \
  85         data->arg = inode->i_private;                                   \
  86         file->private_data = data;                                      \
  87                                                                         \
  88         return 0;                                                       \
  89 }
  90 
  91 #define FWRT_DEBUGFS_READ_WRAPPER(name)                                 \
  92 static ssize_t _iwl_dbgfs_##name##_read(struct file *file,              \
  93                                         char __user *user_buf,          \
  94                                         size_t count, loff_t *ppos)     \
  95 {                                                                       \
  96         struct dbgfs_##name##_data *data = file->private_data;          \
  97                                                                         \
  98         if (!data->read_done) {                                         \
  99                 data->read_done = true;                                 \
 100                 data->rlen = iwl_dbgfs_##name##_read(data->arg,         \
 101                                                      sizeof(data->rbuf),\
 102                                                      data->rbuf);       \
 103         }                                                               \
 104                                                                         \
 105         if (data->rlen < 0)                                             \
 106                 return data->rlen;                                      \
 107         return simple_read_from_buffer(user_buf, count, ppos,           \
 108                                        data->rbuf, data->rlen);         \
 109 }
 110 
 111 static int _iwl_dbgfs_release(struct inode *inode, struct file *file)
 112 {
 113         kfree(file->private_data);
 114 
 115         return 0;
 116 }
 117 
 118 #define _FWRT_DEBUGFS_READ_FILE_OPS(name, buflen, argtype)              \
 119 FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype)                        \
 120 FWRT_DEBUGFS_READ_WRAPPER(name)                                         \
 121 static const struct file_operations iwl_dbgfs_##name##_ops = {          \
 122         .read = _iwl_dbgfs_##name##_read,                               \
 123         .open = _iwl_dbgfs_##name##_open,                               \
 124         .llseek = generic_file_llseek,                                  \
 125         .release = _iwl_dbgfs_release,                                  \
 126 }
 127 
 128 #define FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype)               \
 129 static ssize_t _iwl_dbgfs_##name##_write(struct file *file,             \
 130                                          const char __user *user_buf,   \
 131                                          size_t count, loff_t *ppos)    \
 132 {                                                                       \
 133         argtype *arg =                                                  \
 134                 ((struct dbgfs_##name##_data *)file->private_data)->arg;\
 135         char buf[buflen] = {};                                          \
 136         size_t buf_size = min(count, sizeof(buf) -  1);                 \
 137                                                                         \
 138         if (copy_from_user(buf, user_buf, buf_size))                    \
 139                 return -EFAULT;                                         \
 140                                                                         \
 141         return iwl_dbgfs_##name##_write(arg, buf, buf_size);            \
 142 }
 143 
 144 #define _FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, buflen, argtype)        \
 145 FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype)                        \
 146 FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype)                       \
 147 FWRT_DEBUGFS_READ_WRAPPER(name)                                         \
 148 static const struct file_operations iwl_dbgfs_##name##_ops = {          \
 149         .write = _iwl_dbgfs_##name##_write,                             \
 150         .read = _iwl_dbgfs_##name##_read,                               \
 151         .open = _iwl_dbgfs_##name##_open,                               \
 152         .llseek = generic_file_llseek,                                  \
 153         .release = _iwl_dbgfs_release,                                  \
 154 }
 155 
 156 #define _FWRT_DEBUGFS_WRITE_FILE_OPS(name, buflen, argtype)             \
 157 FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype)                        \
 158 FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype)                       \
 159 static const struct file_operations iwl_dbgfs_##name##_ops = {          \
 160         .write = _iwl_dbgfs_##name##_write,                             \
 161         .open = _iwl_dbgfs_##name##_open,                               \
 162         .llseek = generic_file_llseek,                                  \
 163         .release = _iwl_dbgfs_release,                                  \
 164 }
 165 
 166 #define FWRT_DEBUGFS_READ_FILE_OPS(name, bufsz)                         \
 167         _FWRT_DEBUGFS_READ_FILE_OPS(name, bufsz, struct iwl_fw_runtime)
 168 
 169 #define FWRT_DEBUGFS_WRITE_FILE_OPS(name, bufsz)                        \
 170         _FWRT_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct iwl_fw_runtime)
 171 
 172 #define FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz)                   \
 173         _FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_fw_runtime)
 174 
 175 #define FWRT_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) do {     \
 176         debugfs_create_file(alias, mode, parent, fwrt,                  \
 177                             &iwl_dbgfs_##name##_ops);                   \
 178         } while (0)
 179 #define FWRT_DEBUGFS_ADD_FILE(name, parent, mode) \
 180         FWRT_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
 181 
 182 static int iwl_fw_send_timestamp_marker_cmd(struct iwl_fw_runtime *fwrt)
 183 {
 184         struct iwl_mvm_marker marker = {
 185                 .dw_len = sizeof(struct iwl_mvm_marker) / 4,
 186                 .marker_id = MARKER_ID_SYNC_CLOCK,
 187 
 188                 /* the real timestamp is taken from the ftrace clock
 189                  * this is for finding the match between fw and kernel logs
 190                  */
 191                 .timestamp = cpu_to_le64(fwrt->timestamp.seq++),
 192         };
 193 
 194         struct iwl_host_cmd hcmd = {
 195                 .id = MARKER_CMD,
 196                 .flags = CMD_ASYNC,
 197                 .data[0] = &marker,
 198                 .len[0] = sizeof(marker),
 199         };
 200 
 201         return iwl_trans_send_cmd(fwrt->trans, &hcmd);
 202 }
 203 
 204 static void iwl_fw_timestamp_marker_wk(struct work_struct *work)
 205 {
 206         int ret;
 207         struct iwl_fw_runtime *fwrt =
 208                 container_of(work, struct iwl_fw_runtime, timestamp.wk.work);
 209         unsigned long delay = fwrt->timestamp.delay;
 210 
 211         ret = iwl_fw_send_timestamp_marker_cmd(fwrt);
 212         if (!ret && delay)
 213                 schedule_delayed_work(&fwrt->timestamp.wk,
 214                                       round_jiffies_relative(delay));
 215         else
 216                 IWL_INFO(fwrt,
 217                          "stopping timestamp_marker, ret: %d, delay: %u\n",
 218                          ret, jiffies_to_msecs(delay) / 1000);
 219 }
 220 
 221 void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt, u32 delay)
 222 {
 223         IWL_INFO(fwrt,
 224                  "starting timestamp_marker trigger with delay: %us\n",
 225                  delay);
 226 
 227         iwl_fw_cancel_timestamp(fwrt);
 228 
 229         fwrt->timestamp.delay = msecs_to_jiffies(delay * 1000);
 230 
 231         schedule_delayed_work(&fwrt->timestamp.wk,
 232                               round_jiffies_relative(fwrt->timestamp.delay));
 233 }
 234 
 235 static ssize_t iwl_dbgfs_timestamp_marker_write(struct iwl_fw_runtime *fwrt,
 236                                                 char *buf, size_t count)
 237 {
 238         int ret;
 239         u32 delay;
 240 
 241         ret = kstrtou32(buf, 10, &delay);
 242         if (ret < 0)
 243                 return ret;
 244 
 245         iwl_fw_trigger_timestamp(fwrt, delay);
 246 
 247         return count;
 248 }
 249 
 250 static ssize_t iwl_dbgfs_timestamp_marker_read(struct iwl_fw_runtime *fwrt,
 251                                                size_t size, char *buf)
 252 {
 253         u32 delay_secs = jiffies_to_msecs(fwrt->timestamp.delay) / 1000;
 254 
 255         return scnprintf(buf, size, "%d\n", delay_secs);
 256 }
 257 
 258 FWRT_DEBUGFS_READ_WRITE_FILE_OPS(timestamp_marker, 16);
 259 
 260 struct hcmd_write_data {
 261         __be32 cmd_id;
 262         __be32 flags;
 263         __be16 length;
 264         u8 data[0];
 265 } __packed;
 266 
 267 static ssize_t iwl_dbgfs_send_hcmd_write(struct iwl_fw_runtime *fwrt, char *buf,
 268                                          size_t count)
 269 {
 270         size_t header_size = (sizeof(u32) * 2 + sizeof(u16)) * 2;
 271         size_t data_size = (count - 1) / 2;
 272         int ret;
 273         struct hcmd_write_data *data;
 274         struct iwl_host_cmd hcmd = {
 275                 .len = { 0, },
 276                 .data = { NULL, },
 277         };
 278 
 279         if (fwrt->ops && fwrt->ops->fw_running &&
 280             !fwrt->ops->fw_running(fwrt->ops_ctx))
 281                 return -EIO;
 282 
 283         if (count < header_size + 1 || count > 1024 * 4)
 284                 return -EINVAL;
 285 
 286         data = kmalloc(data_size, GFP_KERNEL);
 287         if (!data)
 288                 return -ENOMEM;
 289 
 290         ret = hex2bin((u8 *)data, buf, data_size);
 291         if (ret)
 292                 goto out;
 293 
 294         hcmd.id = be32_to_cpu(data->cmd_id);
 295         hcmd.flags = be32_to_cpu(data->flags);
 296         hcmd.len[0] = be16_to_cpu(data->length);
 297         hcmd.data[0] = data->data;
 298 
 299         if (count != header_size + hcmd.len[0] * 2 + 1) {
 300                 IWL_ERR(fwrt,
 301                         "host command data size does not match header length\n");
 302                 ret = -EINVAL;
 303                 goto out;
 304         }
 305 
 306         if (fwrt->ops && fwrt->ops->send_hcmd)
 307                 ret = fwrt->ops->send_hcmd(fwrt->ops_ctx, &hcmd);
 308         else
 309                 ret = -EPERM;
 310 
 311         if (ret < 0)
 312                 goto out;
 313 
 314         if (hcmd.flags & CMD_WANT_SKB)
 315                 iwl_free_resp(&hcmd);
 316 out:
 317         kfree(data);
 318         return ret ?: count;
 319 }
 320 
 321 FWRT_DEBUGFS_WRITE_FILE_OPS(send_hcmd, 512);
 322 
 323 void iwl_fwrt_dbgfs_register(struct iwl_fw_runtime *fwrt,
 324                             struct dentry *dbgfs_dir)
 325 {
 326         INIT_DELAYED_WORK(&fwrt->timestamp.wk, iwl_fw_timestamp_marker_wk);
 327         FWRT_DEBUGFS_ADD_FILE(timestamp_marker, dbgfs_dir, 0200);
 328         FWRT_DEBUGFS_ADD_FILE(send_hcmd, dbgfs_dir, 0200);
 329 }

/* [<][>][^][v][top][bottom][index][help] */