linux/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c
<<
>>
Prefs
   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)                \
  69struct dbgfs_##name##_data {                                            \
  70        argtype *arg;                                                   \
  71        bool read_done;                                                 \
  72        ssize_t rlen;                                                   \
  73        char rbuf[buflen];                                              \
  74};                                                                      \
  75static 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)                                 \
  92static 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
 111static 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)              \
 119FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype)                        \
 120FWRT_DEBUGFS_READ_WRAPPER(name)                                         \
 121static 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)               \
 129static 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)        \
 145FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype)                        \
 146FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype)                       \
 147FWRT_DEBUGFS_READ_WRAPPER(name)                                         \
 148static 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)             \
 157FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype)                        \
 158FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype)                       \
 159static 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        if (!debugfs_create_file(alias, mode, parent, fwrt,             \
 177                                 &iwl_dbgfs_##name##_ops))              \
 178                goto err;                                               \
 179        } while (0)
 180#define FWRT_DEBUGFS_ADD_FILE(name, parent, mode) \
 181        FWRT_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
 182
 183static int iwl_fw_send_timestamp_marker_cmd(struct iwl_fw_runtime *fwrt)
 184{
 185        struct iwl_mvm_marker marker = {
 186                .dw_len = sizeof(struct iwl_mvm_marker) / 4,
 187                .marker_id = MARKER_ID_SYNC_CLOCK,
 188
 189                /* the real timestamp is taken from the ftrace clock
 190                 * this is for finding the match between fw and kernel logs
 191                 */
 192                .timestamp = cpu_to_le64(fwrt->timestamp.seq++),
 193        };
 194
 195        struct iwl_host_cmd hcmd = {
 196                .id = MARKER_CMD,
 197                .flags = CMD_ASYNC,
 198                .data[0] = &marker,
 199                .len[0] = sizeof(marker),
 200        };
 201
 202        return iwl_trans_send_cmd(fwrt->trans, &hcmd);
 203}
 204
 205static void iwl_fw_timestamp_marker_wk(struct work_struct *work)
 206{
 207        int ret;
 208        struct iwl_fw_runtime *fwrt =
 209                container_of(work, struct iwl_fw_runtime, timestamp.wk.work);
 210        unsigned long delay = fwrt->timestamp.delay;
 211
 212        ret = iwl_fw_send_timestamp_marker_cmd(fwrt);
 213        if (!ret && delay)
 214                schedule_delayed_work(&fwrt->timestamp.wk,
 215                                      round_jiffies_relative(delay));
 216        else
 217                IWL_INFO(fwrt,
 218                         "stopping timestamp_marker, ret: %d, delay: %u\n",
 219                         ret, jiffies_to_msecs(delay) / 1000);
 220}
 221
 222void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt, u32 delay)
 223{
 224        IWL_INFO(fwrt,
 225                 "starting timestamp_marker trigger with delay: %us\n",
 226                 delay);
 227
 228        iwl_fw_cancel_timestamp(fwrt);
 229
 230        fwrt->timestamp.delay = msecs_to_jiffies(delay * 1000);
 231
 232        schedule_delayed_work(&fwrt->timestamp.wk,
 233                              round_jiffies_relative(fwrt->timestamp.delay));
 234}
 235
 236static ssize_t iwl_dbgfs_timestamp_marker_write(struct iwl_fw_runtime *fwrt,
 237                                                char *buf, size_t count)
 238{
 239        int ret;
 240        u32 delay;
 241
 242        ret = kstrtou32(buf, 10, &delay);
 243        if (ret < 0)
 244                return ret;
 245
 246        iwl_fw_trigger_timestamp(fwrt, delay);
 247
 248        return count;
 249}
 250
 251static ssize_t iwl_dbgfs_timestamp_marker_read(struct iwl_fw_runtime *fwrt,
 252                                               size_t size, char *buf)
 253{
 254        u32 delay_secs = jiffies_to_msecs(fwrt->timestamp.delay) / 1000;
 255
 256        return scnprintf(buf, size, "%d\n", delay_secs);
 257}
 258
 259FWRT_DEBUGFS_READ_WRITE_FILE_OPS(timestamp_marker, 16);
 260
 261struct hcmd_write_data {
 262        __be32 cmd_id;
 263        __be32 flags;
 264        __be16 length;
 265        u8 data[0];
 266} __packed;
 267
 268static ssize_t iwl_dbgfs_send_hcmd_write(struct iwl_fw_runtime *fwrt, char *buf,
 269                                         size_t count)
 270{
 271        size_t header_size = (sizeof(u32) * 2 + sizeof(u16)) * 2;
 272        size_t data_size = (count - 1) / 2;
 273        int ret;
 274        struct hcmd_write_data *data;
 275        struct iwl_host_cmd hcmd = {
 276                .len = { 0, },
 277                .data = { NULL, },
 278        };
 279
 280        if (fwrt->ops && fwrt->ops->fw_running &&
 281            !fwrt->ops->fw_running(fwrt->ops_ctx))
 282                return -EIO;
 283
 284        if (count < header_size + 1 || count > 1024 * 4)
 285                return -EINVAL;
 286
 287        data = kmalloc(data_size, GFP_KERNEL);
 288        if (!data)
 289                return -ENOMEM;
 290
 291        ret = hex2bin((u8 *)data, buf, data_size);
 292        if (ret)
 293                goto out;
 294
 295        hcmd.id = be32_to_cpu(data->cmd_id);
 296        hcmd.flags = be32_to_cpu(data->flags);
 297        hcmd.len[0] = be16_to_cpu(data->length);
 298        hcmd.data[0] = data->data;
 299
 300        if (count != header_size + hcmd.len[0] * 2 + 1) {
 301                IWL_ERR(fwrt,
 302                        "host command data size does not match header length\n");
 303                ret = -EINVAL;
 304                goto out;
 305        }
 306
 307        if (fwrt->ops && fwrt->ops->send_hcmd)
 308                ret = fwrt->ops->send_hcmd(fwrt->ops_ctx, &hcmd);
 309        else
 310                ret = -EPERM;
 311
 312        if (ret < 0)
 313                goto out;
 314
 315        if (hcmd.flags & CMD_WANT_SKB)
 316                iwl_free_resp(&hcmd);
 317out:
 318        kfree(data);
 319        return ret ?: count;
 320}
 321
 322FWRT_DEBUGFS_WRITE_FILE_OPS(send_hcmd, 512);
 323
 324int iwl_fwrt_dbgfs_register(struct iwl_fw_runtime *fwrt,
 325                            struct dentry *dbgfs_dir)
 326{
 327        INIT_DELAYED_WORK(&fwrt->timestamp.wk, iwl_fw_timestamp_marker_wk);
 328        FWRT_DEBUGFS_ADD_FILE(timestamp_marker, dbgfs_dir, 0200);
 329        FWRT_DEBUGFS_ADD_FILE(send_hcmd, dbgfs_dir, 0200);
 330        return 0;
 331err:
 332        IWL_ERR(fwrt, "Can't create the fwrt debugfs directory\n");
 333        return -ENOMEM;
 334}
 335