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