linux/drivers/net/wimax/i2400m/debugfs.c
<<
>>
Prefs
   1/*
   2 * Intel Wireless WiMAX Connection 2400m
   3 * Debugfs interfaces to manipulate driver and device information
   4 *
   5 *
   6 * Copyright (C) 2007 Intel Corporation <linux-wimax@intel.com>
   7 * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
   8 *
   9 * This program is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU General Public License version
  11 * 2 as published by the Free Software Foundation.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  21 * 02110-1301, USA.
  22 */
  23
  24#include <linux/debugfs.h>
  25#include <linux/netdevice.h>
  26#include <linux/etherdevice.h>
  27#include <linux/spinlock.h>
  28#include <linux/device.h>
  29#include "i2400m.h"
  30
  31
  32#define D_SUBMODULE debugfs
  33#include "debug-levels.h"
  34
  35static
  36int debugfs_netdev_queue_stopped_get(void *data, u64 *val)
  37{
  38        struct i2400m *i2400m = data;
  39        *val = netif_queue_stopped(i2400m->wimax_dev.net_dev);
  40        return 0;
  41}
  42DEFINE_SIMPLE_ATTRIBUTE(fops_netdev_queue_stopped,
  43                        debugfs_netdev_queue_stopped_get,
  44                        NULL, "%llu\n");
  45
  46
  47static
  48struct dentry *debugfs_create_netdev_queue_stopped(
  49        const char *name, struct dentry *parent, struct i2400m *i2400m)
  50{
  51        return debugfs_create_file(name, 0400, parent, i2400m,
  52                                   &fops_netdev_queue_stopped);
  53}
  54
  55
  56/*
  57 * inode->i_private has the @data argument to debugfs_create_file()
  58 */
  59static
  60int i2400m_stats_open(struct inode *inode, struct file *filp)
  61{
  62        filp->private_data = inode->i_private;
  63        return 0;
  64}
  65
  66/*
  67 * We don't allow partial reads of this file, as then the reader would
  68 * get weirdly confused data as it is updated.
  69 *
  70 * So or you read it all or nothing; if you try to read with an offset
  71 * != 0, we consider you are done reading.
  72 */
  73static
  74ssize_t i2400m_rx_stats_read(struct file *filp, char __user *buffer,
  75                             size_t count, loff_t *ppos)
  76{
  77        struct i2400m *i2400m = filp->private_data;
  78        char buf[128];
  79        unsigned long flags;
  80
  81        if (*ppos != 0)
  82                return 0;
  83        if (count < sizeof(buf))
  84                return -ENOSPC;
  85        spin_lock_irqsave(&i2400m->rx_lock, flags);
  86        snprintf(buf, sizeof(buf), "%u %u %u %u %u %u %u\n",
  87                 i2400m->rx_pl_num, i2400m->rx_pl_min,
  88                 i2400m->rx_pl_max, i2400m->rx_num,
  89                 i2400m->rx_size_acc,
  90                 i2400m->rx_size_min, i2400m->rx_size_max);
  91        spin_unlock_irqrestore(&i2400m->rx_lock, flags);
  92        return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
  93}
  94
  95
  96/* Any write clears the stats */
  97static
  98ssize_t i2400m_rx_stats_write(struct file *filp, const char __user *buffer,
  99                              size_t count, loff_t *ppos)
 100{
 101        struct i2400m *i2400m = filp->private_data;
 102        unsigned long flags;
 103
 104        spin_lock_irqsave(&i2400m->rx_lock, flags);
 105        i2400m->rx_pl_num = 0;
 106        i2400m->rx_pl_max = 0;
 107        i2400m->rx_pl_min = UINT_MAX;
 108        i2400m->rx_num = 0;
 109        i2400m->rx_size_acc = 0;
 110        i2400m->rx_size_min = UINT_MAX;
 111        i2400m->rx_size_max = 0;
 112        spin_unlock_irqrestore(&i2400m->rx_lock, flags);
 113        return count;
 114}
 115
 116static
 117const struct file_operations i2400m_rx_stats_fops = {
 118        .owner =        THIS_MODULE,
 119        .open =         i2400m_stats_open,
 120        .read =         i2400m_rx_stats_read,
 121        .write =        i2400m_rx_stats_write,
 122};
 123
 124
 125/* See i2400m_rx_stats_read() */
 126static
 127ssize_t i2400m_tx_stats_read(struct file *filp, char __user *buffer,
 128                             size_t count, loff_t *ppos)
 129{
 130        struct i2400m *i2400m = filp->private_data;
 131        char buf[128];
 132        unsigned long flags;
 133
 134        if (*ppos != 0)
 135                return 0;
 136        if (count < sizeof(buf))
 137                return -ENOSPC;
 138        spin_lock_irqsave(&i2400m->tx_lock, flags);
 139        snprintf(buf, sizeof(buf), "%u %u %u %u %u %u %u\n",
 140                 i2400m->tx_pl_num, i2400m->tx_pl_min,
 141                 i2400m->tx_pl_max, i2400m->tx_num,
 142                 i2400m->tx_size_acc,
 143                 i2400m->tx_size_min, i2400m->tx_size_max);
 144        spin_unlock_irqrestore(&i2400m->tx_lock, flags);
 145        return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
 146}
 147
 148/* Any write clears the stats */
 149static
 150ssize_t i2400m_tx_stats_write(struct file *filp, const char __user *buffer,
 151                              size_t count, loff_t *ppos)
 152{
 153        struct i2400m *i2400m = filp->private_data;
 154        unsigned long flags;
 155
 156        spin_lock_irqsave(&i2400m->tx_lock, flags);
 157        i2400m->tx_pl_num = 0;
 158        i2400m->tx_pl_max = 0;
 159        i2400m->tx_pl_min = UINT_MAX;
 160        i2400m->tx_num = 0;
 161        i2400m->tx_size_acc = 0;
 162        i2400m->tx_size_min = UINT_MAX;
 163        i2400m->tx_size_max = 0;
 164        spin_unlock_irqrestore(&i2400m->tx_lock, flags);
 165        return count;
 166}
 167
 168static
 169const struct file_operations i2400m_tx_stats_fops = {
 170        .owner =        THIS_MODULE,
 171        .open =         i2400m_stats_open,
 172        .read =         i2400m_tx_stats_read,
 173        .write =        i2400m_tx_stats_write,
 174};
 175
 176
 177/* Write 1 to ask the device to go into suspend */
 178static
 179int debugfs_i2400m_suspend_set(void *data, u64 val)
 180{
 181        int result;
 182        struct i2400m *i2400m = data;
 183        result = i2400m_cmd_enter_powersave(i2400m);
 184        if (result >= 0)
 185                result = 0;
 186        return result;
 187}
 188DEFINE_SIMPLE_ATTRIBUTE(fops_i2400m_suspend,
 189                        NULL, debugfs_i2400m_suspend_set,
 190                        "%llu\n");
 191
 192static
 193struct dentry *debugfs_create_i2400m_suspend(
 194        const char *name, struct dentry *parent, struct i2400m *i2400m)
 195{
 196        return debugfs_create_file(name, 0200, parent, i2400m,
 197                                   &fops_i2400m_suspend);
 198}
 199
 200
 201/*
 202 * Reset the device
 203 *
 204 * Write 0 to ask the device to soft reset, 1 to cold reset, 2 to bus
 205 * reset (as defined by enum i2400m_reset_type).
 206 */
 207static
 208int debugfs_i2400m_reset_set(void *data, u64 val)
 209{
 210        int result;
 211        struct i2400m *i2400m = data;
 212        enum i2400m_reset_type rt = val;
 213        switch(rt) {
 214        case I2400M_RT_WARM:
 215        case I2400M_RT_COLD:
 216        case I2400M_RT_BUS:
 217                result = i2400m->bus_reset(i2400m, rt);
 218                if (result >= 0)
 219                        result = 0;
 220        default:
 221                result = -EINVAL;
 222        }
 223        return result;
 224}
 225DEFINE_SIMPLE_ATTRIBUTE(fops_i2400m_reset,
 226                        NULL, debugfs_i2400m_reset_set,
 227                        "%llu\n");
 228
 229static
 230struct dentry *debugfs_create_i2400m_reset(
 231        const char *name, struct dentry *parent, struct i2400m *i2400m)
 232{
 233        return debugfs_create_file(name, 0200, parent, i2400m,
 234                                   &fops_i2400m_reset);
 235}
 236
 237
 238#define __debugfs_register(prefix, name, parent)                        \
 239do {                                                                    \
 240        result = d_level_register_debugfs(prefix, name, parent);        \
 241        if (result < 0)                                                 \
 242                goto error;                                             \
 243} while (0)
 244
 245
 246int i2400m_debugfs_add(struct i2400m *i2400m)
 247{
 248        int result;
 249        struct device *dev = i2400m_dev(i2400m);
 250        struct dentry *dentry = i2400m->wimax_dev.debugfs_dentry;
 251        struct dentry *fd;
 252
 253        dentry = debugfs_create_dir("i2400m", dentry);
 254        result = PTR_ERR(dentry);
 255        if (IS_ERR(dentry)) {
 256                if (result == -ENODEV)
 257                        result = 0;     /* No debugfs support */
 258                goto error;
 259        }
 260        i2400m->debugfs_dentry = dentry;
 261        __debugfs_register("dl_", control, dentry);
 262        __debugfs_register("dl_", driver, dentry);
 263        __debugfs_register("dl_", debugfs, dentry);
 264        __debugfs_register("dl_", fw, dentry);
 265        __debugfs_register("dl_", netdev, dentry);
 266        __debugfs_register("dl_", rfkill, dentry);
 267        __debugfs_register("dl_", rx, dentry);
 268        __debugfs_register("dl_", tx, dentry);
 269
 270        fd = debugfs_create_size_t("tx_in", 0400, dentry,
 271                                   &i2400m->tx_in);
 272        result = PTR_ERR(fd);
 273        if (IS_ERR(fd) && result != -ENODEV) {
 274                dev_err(dev, "Can't create debugfs entry "
 275                        "tx_in: %d\n", result);
 276                goto error;
 277        }
 278
 279        fd = debugfs_create_size_t("tx_out", 0400, dentry,
 280                                   &i2400m->tx_out);
 281        result = PTR_ERR(fd);
 282        if (IS_ERR(fd) && result != -ENODEV) {
 283                dev_err(dev, "Can't create debugfs entry "
 284                        "tx_out: %d\n", result);
 285                goto error;
 286        }
 287
 288        fd = debugfs_create_u32("state", 0600, dentry,
 289                                &i2400m->state);
 290        result = PTR_ERR(fd);
 291        if (IS_ERR(fd) && result != -ENODEV) {
 292                dev_err(dev, "Can't create debugfs entry "
 293                        "state: %d\n", result);
 294                goto error;
 295        }
 296
 297        /*
 298         * Trace received messages from user space
 299         *
 300         * In order to tap the bidirectional message stream in the
 301         * 'msg' pipe, user space can read from the 'msg' pipe;
 302         * however, due to limitations in libnl, we can't know what
 303         * the different applications are sending down to the kernel.
 304         *
 305         * So we have this hack where the driver will echo any message
 306         * received on the msg pipe from user space [through a call to
 307         * wimax_dev->op_msg_from_user() into
 308         * i2400m_op_msg_from_user()] into the 'trace' pipe that this
 309         * driver creates.
 310         *
 311         * So then, reading from both the 'trace' and 'msg' pipes in
 312         * user space will provide a full dump of the traffic.
 313         *
 314         * Write 1 to activate, 0 to clear.
 315         *
 316         * It is not really very atomic, but it is also not too
 317         * critical.
 318         */
 319        fd = debugfs_create_u8("trace_msg_from_user", 0600, dentry,
 320                               &i2400m->trace_msg_from_user);
 321        result = PTR_ERR(fd);
 322        if (IS_ERR(fd) && result != -ENODEV) {
 323                dev_err(dev, "Can't create debugfs entry "
 324                        "trace_msg_from_user: %d\n", result);
 325                goto error;
 326        }
 327
 328        fd = debugfs_create_netdev_queue_stopped("netdev_queue_stopped",
 329                                                 dentry, i2400m);
 330        result = PTR_ERR(fd);
 331        if (IS_ERR(fd) && result != -ENODEV) {
 332                dev_err(dev, "Can't create debugfs entry "
 333                        "netdev_queue_stopped: %d\n", result);
 334                goto error;
 335        }
 336
 337        fd = debugfs_create_file("rx_stats", 0600, dentry, i2400m,
 338                                 &i2400m_rx_stats_fops);
 339        result = PTR_ERR(fd);
 340        if (IS_ERR(fd) && result != -ENODEV) {
 341                dev_err(dev, "Can't create debugfs entry "
 342                        "rx_stats: %d\n", result);
 343                goto error;
 344        }
 345
 346        fd = debugfs_create_file("tx_stats", 0600, dentry, i2400m,
 347                                 &i2400m_tx_stats_fops);
 348        result = PTR_ERR(fd);
 349        if (IS_ERR(fd) && result != -ENODEV) {
 350                dev_err(dev, "Can't create debugfs entry "
 351                        "tx_stats: %d\n", result);
 352                goto error;
 353        }
 354
 355        fd = debugfs_create_i2400m_suspend("suspend", dentry, i2400m);
 356        result = PTR_ERR(fd);
 357        if (IS_ERR(fd) && result != -ENODEV) {
 358                dev_err(dev, "Can't create debugfs entry suspend: %d\n",
 359                        result);
 360                goto error;
 361        }
 362
 363        fd = debugfs_create_i2400m_reset("reset", dentry, i2400m);
 364        result = PTR_ERR(fd);
 365        if (IS_ERR(fd) && result != -ENODEV) {
 366                dev_err(dev, "Can't create debugfs entry reset: %d\n", result);
 367                goto error;
 368        }
 369
 370        result = 0;
 371error:
 372        return result;
 373}
 374
 375void i2400m_debugfs_rm(struct i2400m *i2400m)
 376{
 377        debugfs_remove_recursive(i2400m->debugfs_dentry);
 378}
 379