1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Intel Wireless WiMAX Connection 2400m 4 * Debugfs interfaces to manipulate driver and device information 5 * 6 * Copyright (C) 2007 Intel Corporation <linux-wimax@intel.com> 7 * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> 8 */ 9 10#include <linux/debugfs.h> 11#include <linux/netdevice.h> 12#include <linux/etherdevice.h> 13#include <linux/spinlock.h> 14#include <linux/device.h> 15#include <linux/export.h> 16#include "i2400m.h" 17 18 19#define D_SUBMODULE debugfs 20#include "debug-levels.h" 21 22static 23int debugfs_netdev_queue_stopped_get(void *data, u64 *val) 24{ 25 struct i2400m *i2400m = data; 26 *val = netif_queue_stopped(i2400m->wimax_dev.net_dev); 27 return 0; 28} 29DEFINE_DEBUGFS_ATTRIBUTE(fops_netdev_queue_stopped, 30 debugfs_netdev_queue_stopped_get, 31 NULL, "%llu\n"); 32 33/* 34 * We don't allow partial reads of this file, as then the reader would 35 * get weirdly confused data as it is updated. 36 * 37 * So or you read it all or nothing; if you try to read with an offset 38 * != 0, we consider you are done reading. 39 */ 40static 41ssize_t i2400m_rx_stats_read(struct file *filp, char __user *buffer, 42 size_t count, loff_t *ppos) 43{ 44 struct i2400m *i2400m = filp->private_data; 45 char buf[128]; 46 unsigned long flags; 47 48 if (*ppos != 0) 49 return 0; 50 if (count < sizeof(buf)) 51 return -ENOSPC; 52 spin_lock_irqsave(&i2400m->rx_lock, flags); 53 snprintf(buf, sizeof(buf), "%u %u %u %u %u %u %u\n", 54 i2400m->rx_pl_num, i2400m->rx_pl_min, 55 i2400m->rx_pl_max, i2400m->rx_num, 56 i2400m->rx_size_acc, 57 i2400m->rx_size_min, i2400m->rx_size_max); 58 spin_unlock_irqrestore(&i2400m->rx_lock, flags); 59 return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); 60} 61 62 63/* Any write clears the stats */ 64static 65ssize_t i2400m_rx_stats_write(struct file *filp, const char __user *buffer, 66 size_t count, loff_t *ppos) 67{ 68 struct i2400m *i2400m = filp->private_data; 69 unsigned long flags; 70 71 spin_lock_irqsave(&i2400m->rx_lock, flags); 72 i2400m->rx_pl_num = 0; 73 i2400m->rx_pl_max = 0; 74 i2400m->rx_pl_min = UINT_MAX; 75 i2400m->rx_num = 0; 76 i2400m->rx_size_acc = 0; 77 i2400m->rx_size_min = UINT_MAX; 78 i2400m->rx_size_max = 0; 79 spin_unlock_irqrestore(&i2400m->rx_lock, flags); 80 return count; 81} 82 83static 84const struct file_operations i2400m_rx_stats_fops = { 85 .owner = THIS_MODULE, 86 .open = simple_open, 87 .read = i2400m_rx_stats_read, 88 .write = i2400m_rx_stats_write, 89 .llseek = default_llseek, 90}; 91 92 93/* See i2400m_rx_stats_read() */ 94static 95ssize_t i2400m_tx_stats_read(struct file *filp, char __user *buffer, 96 size_t count, loff_t *ppos) 97{ 98 struct i2400m *i2400m = filp->private_data; 99 char buf[128]; 100 unsigned long flags; 101 102 if (*ppos != 0) 103 return 0; 104 if (count < sizeof(buf)) 105 return -ENOSPC; 106 spin_lock_irqsave(&i2400m->tx_lock, flags); 107 snprintf(buf, sizeof(buf), "%u %u %u %u %u %u %u\n", 108 i2400m->tx_pl_num, i2400m->tx_pl_min, 109 i2400m->tx_pl_max, i2400m->tx_num, 110 i2400m->tx_size_acc, 111 i2400m->tx_size_min, i2400m->tx_size_max); 112 spin_unlock_irqrestore(&i2400m->tx_lock, flags); 113 return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); 114} 115 116/* Any write clears the stats */ 117static 118ssize_t i2400m_tx_stats_write(struct file *filp, const char __user *buffer, 119 size_t count, loff_t *ppos) 120{ 121 struct i2400m *i2400m = filp->private_data; 122 unsigned long flags; 123 124 spin_lock_irqsave(&i2400m->tx_lock, flags); 125 i2400m->tx_pl_num = 0; 126 i2400m->tx_pl_max = 0; 127 i2400m->tx_pl_min = UINT_MAX; 128 i2400m->tx_num = 0; 129 i2400m->tx_size_acc = 0; 130 i2400m->tx_size_min = UINT_MAX; 131 i2400m->tx_size_max = 0; 132 spin_unlock_irqrestore(&i2400m->tx_lock, flags); 133 return count; 134} 135 136static 137const struct file_operations i2400m_tx_stats_fops = { 138 .owner = THIS_MODULE, 139 .open = simple_open, 140 .read = i2400m_tx_stats_read, 141 .write = i2400m_tx_stats_write, 142 .llseek = default_llseek, 143}; 144 145 146/* Write 1 to ask the device to go into suspend */ 147static 148int debugfs_i2400m_suspend_set(void *data, u64 val) 149{ 150 int result; 151 struct i2400m *i2400m = data; 152 result = i2400m_cmd_enter_powersave(i2400m); 153 if (result >= 0) 154 result = 0; 155 return result; 156} 157DEFINE_DEBUGFS_ATTRIBUTE(fops_i2400m_suspend, 158 NULL, debugfs_i2400m_suspend_set, 159 "%llu\n"); 160 161/* 162 * Reset the device 163 * 164 * Write 0 to ask the device to soft reset, 1 to cold reset, 2 to bus 165 * reset (as defined by enum i2400m_reset_type). 166 */ 167static 168int debugfs_i2400m_reset_set(void *data, u64 val) 169{ 170 int result; 171 struct i2400m *i2400m = data; 172 enum i2400m_reset_type rt = val; 173 switch(rt) { 174 case I2400M_RT_WARM: 175 case I2400M_RT_COLD: 176 case I2400M_RT_BUS: 177 result = i2400m_reset(i2400m, rt); 178 if (result >= 0) 179 result = 0; 180 break; 181 default: 182 result = -EINVAL; 183 } 184 return result; 185} 186DEFINE_DEBUGFS_ATTRIBUTE(fops_i2400m_reset, 187 NULL, debugfs_i2400m_reset_set, 188 "%llu\n"); 189 190void i2400m_debugfs_add(struct i2400m *i2400m) 191{ 192 struct dentry *dentry = i2400m->wimax_dev.debugfs_dentry; 193 194 dentry = debugfs_create_dir("i2400m", dentry); 195 i2400m->debugfs_dentry = dentry; 196 197 d_level_register_debugfs("dl_", control, dentry); 198 d_level_register_debugfs("dl_", driver, dentry); 199 d_level_register_debugfs("dl_", debugfs, dentry); 200 d_level_register_debugfs("dl_", fw, dentry); 201 d_level_register_debugfs("dl_", netdev, dentry); 202 d_level_register_debugfs("dl_", rfkill, dentry); 203 d_level_register_debugfs("dl_", rx, dentry); 204 d_level_register_debugfs("dl_", tx, dentry); 205 206 debugfs_create_size_t("tx_in", 0400, dentry, &i2400m->tx_in); 207 debugfs_create_size_t("tx_out", 0400, dentry, &i2400m->tx_out); 208 debugfs_create_u32("state", 0600, dentry, &i2400m->state); 209 210 /* 211 * Trace received messages from user space 212 * 213 * In order to tap the bidirectional message stream in the 214 * 'msg' pipe, user space can read from the 'msg' pipe; 215 * however, due to limitations in libnl, we can't know what 216 * the different applications are sending down to the kernel. 217 * 218 * So we have this hack where the driver will echo any message 219 * received on the msg pipe from user space [through a call to 220 * wimax_dev->op_msg_from_user() into 221 * i2400m_op_msg_from_user()] into the 'trace' pipe that this 222 * driver creates. 223 * 224 * So then, reading from both the 'trace' and 'msg' pipes in 225 * user space will provide a full dump of the traffic. 226 * 227 * Write 1 to activate, 0 to clear. 228 * 229 * It is not really very atomic, but it is also not too 230 * critical. 231 */ 232 debugfs_create_u8("trace_msg_from_user", 0600, dentry, 233 &i2400m->trace_msg_from_user); 234 235 debugfs_create_file("netdev_queue_stopped", 0400, dentry, i2400m, 236 &fops_netdev_queue_stopped); 237 238 debugfs_create_file("rx_stats", 0600, dentry, i2400m, 239 &i2400m_rx_stats_fops); 240 241 debugfs_create_file("tx_stats", 0600, dentry, i2400m, 242 &i2400m_tx_stats_fops); 243 244 debugfs_create_file("suspend", 0200, dentry, i2400m, 245 &fops_i2400m_suspend); 246 247 debugfs_create_file("reset", 0200, dentry, i2400m, &fops_i2400m_reset); 248} 249 250void i2400m_debugfs_rm(struct i2400m *i2400m) 251{ 252 debugfs_remove_recursive(i2400m->debugfs_dentry); 253} 254