18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Intel Wireless WiMAX Connection 2400m 48c2ecf20Sopenharmony_ci * Debugfs interfaces to manipulate driver and device information 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2007 Intel Corporation <linux-wimax@intel.com> 78c2ecf20Sopenharmony_ci * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 118c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 128c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 138c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 148c2ecf20Sopenharmony_ci#include <linux/device.h> 158c2ecf20Sopenharmony_ci#include <linux/export.h> 168c2ecf20Sopenharmony_ci#include "i2400m.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define D_SUBMODULE debugfs 208c2ecf20Sopenharmony_ci#include "debug-levels.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic 238c2ecf20Sopenharmony_ciint debugfs_netdev_queue_stopped_get(void *data, u64 *val) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci struct i2400m *i2400m = data; 268c2ecf20Sopenharmony_ci *val = netif_queue_stopped(i2400m->wimax_dev.net_dev); 278c2ecf20Sopenharmony_ci return 0; 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_netdev_queue_stopped, 308c2ecf20Sopenharmony_ci debugfs_netdev_queue_stopped_get, 318c2ecf20Sopenharmony_ci NULL, "%llu\n"); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* 348c2ecf20Sopenharmony_ci * We don't allow partial reads of this file, as then the reader would 358c2ecf20Sopenharmony_ci * get weirdly confused data as it is updated. 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * So or you read it all or nothing; if you try to read with an offset 388c2ecf20Sopenharmony_ci * != 0, we consider you are done reading. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_cistatic 418c2ecf20Sopenharmony_cissize_t i2400m_rx_stats_read(struct file *filp, char __user *buffer, 428c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct i2400m *i2400m = filp->private_data; 458c2ecf20Sopenharmony_ci char buf[128]; 468c2ecf20Sopenharmony_ci unsigned long flags; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if (*ppos != 0) 498c2ecf20Sopenharmony_ci return 0; 508c2ecf20Sopenharmony_ci if (count < sizeof(buf)) 518c2ecf20Sopenharmony_ci return -ENOSPC; 528c2ecf20Sopenharmony_ci spin_lock_irqsave(&i2400m->rx_lock, flags); 538c2ecf20Sopenharmony_ci snprintf(buf, sizeof(buf), "%u %u %u %u %u %u %u\n", 548c2ecf20Sopenharmony_ci i2400m->rx_pl_num, i2400m->rx_pl_min, 558c2ecf20Sopenharmony_ci i2400m->rx_pl_max, i2400m->rx_num, 568c2ecf20Sopenharmony_ci i2400m->rx_size_acc, 578c2ecf20Sopenharmony_ci i2400m->rx_size_min, i2400m->rx_size_max); 588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i2400m->rx_lock, flags); 598c2ecf20Sopenharmony_ci return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* Any write clears the stats */ 648c2ecf20Sopenharmony_cistatic 658c2ecf20Sopenharmony_cissize_t i2400m_rx_stats_write(struct file *filp, const char __user *buffer, 668c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct i2400m *i2400m = filp->private_data; 698c2ecf20Sopenharmony_ci unsigned long flags; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci spin_lock_irqsave(&i2400m->rx_lock, flags); 728c2ecf20Sopenharmony_ci i2400m->rx_pl_num = 0; 738c2ecf20Sopenharmony_ci i2400m->rx_pl_max = 0; 748c2ecf20Sopenharmony_ci i2400m->rx_pl_min = UINT_MAX; 758c2ecf20Sopenharmony_ci i2400m->rx_num = 0; 768c2ecf20Sopenharmony_ci i2400m->rx_size_acc = 0; 778c2ecf20Sopenharmony_ci i2400m->rx_size_min = UINT_MAX; 788c2ecf20Sopenharmony_ci i2400m->rx_size_max = 0; 798c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i2400m->rx_lock, flags); 808c2ecf20Sopenharmony_ci return count; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic 848c2ecf20Sopenharmony_ciconst struct file_operations i2400m_rx_stats_fops = { 858c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 868c2ecf20Sopenharmony_ci .open = simple_open, 878c2ecf20Sopenharmony_ci .read = i2400m_rx_stats_read, 888c2ecf20Sopenharmony_ci .write = i2400m_rx_stats_write, 898c2ecf20Sopenharmony_ci .llseek = default_llseek, 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* See i2400m_rx_stats_read() */ 948c2ecf20Sopenharmony_cistatic 958c2ecf20Sopenharmony_cissize_t i2400m_tx_stats_read(struct file *filp, char __user *buffer, 968c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct i2400m *i2400m = filp->private_data; 998c2ecf20Sopenharmony_ci char buf[128]; 1008c2ecf20Sopenharmony_ci unsigned long flags; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (*ppos != 0) 1038c2ecf20Sopenharmony_ci return 0; 1048c2ecf20Sopenharmony_ci if (count < sizeof(buf)) 1058c2ecf20Sopenharmony_ci return -ENOSPC; 1068c2ecf20Sopenharmony_ci spin_lock_irqsave(&i2400m->tx_lock, flags); 1078c2ecf20Sopenharmony_ci snprintf(buf, sizeof(buf), "%u %u %u %u %u %u %u\n", 1088c2ecf20Sopenharmony_ci i2400m->tx_pl_num, i2400m->tx_pl_min, 1098c2ecf20Sopenharmony_ci i2400m->tx_pl_max, i2400m->tx_num, 1108c2ecf20Sopenharmony_ci i2400m->tx_size_acc, 1118c2ecf20Sopenharmony_ci i2400m->tx_size_min, i2400m->tx_size_max); 1128c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i2400m->tx_lock, flags); 1138c2ecf20Sopenharmony_ci return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* Any write clears the stats */ 1178c2ecf20Sopenharmony_cistatic 1188c2ecf20Sopenharmony_cissize_t i2400m_tx_stats_write(struct file *filp, const char __user *buffer, 1198c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct i2400m *i2400m = filp->private_data; 1228c2ecf20Sopenharmony_ci unsigned long flags; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci spin_lock_irqsave(&i2400m->tx_lock, flags); 1258c2ecf20Sopenharmony_ci i2400m->tx_pl_num = 0; 1268c2ecf20Sopenharmony_ci i2400m->tx_pl_max = 0; 1278c2ecf20Sopenharmony_ci i2400m->tx_pl_min = UINT_MAX; 1288c2ecf20Sopenharmony_ci i2400m->tx_num = 0; 1298c2ecf20Sopenharmony_ci i2400m->tx_size_acc = 0; 1308c2ecf20Sopenharmony_ci i2400m->tx_size_min = UINT_MAX; 1318c2ecf20Sopenharmony_ci i2400m->tx_size_max = 0; 1328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i2400m->tx_lock, flags); 1338c2ecf20Sopenharmony_ci return count; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic 1378c2ecf20Sopenharmony_ciconst struct file_operations i2400m_tx_stats_fops = { 1388c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1398c2ecf20Sopenharmony_ci .open = simple_open, 1408c2ecf20Sopenharmony_ci .read = i2400m_tx_stats_read, 1418c2ecf20Sopenharmony_ci .write = i2400m_tx_stats_write, 1428c2ecf20Sopenharmony_ci .llseek = default_llseek, 1438c2ecf20Sopenharmony_ci}; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci/* Write 1 to ask the device to go into suspend */ 1478c2ecf20Sopenharmony_cistatic 1488c2ecf20Sopenharmony_ciint debugfs_i2400m_suspend_set(void *data, u64 val) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci int result; 1518c2ecf20Sopenharmony_ci struct i2400m *i2400m = data; 1528c2ecf20Sopenharmony_ci result = i2400m_cmd_enter_powersave(i2400m); 1538c2ecf20Sopenharmony_ci if (result >= 0) 1548c2ecf20Sopenharmony_ci result = 0; 1558c2ecf20Sopenharmony_ci return result; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_i2400m_suspend, 1588c2ecf20Sopenharmony_ci NULL, debugfs_i2400m_suspend_set, 1598c2ecf20Sopenharmony_ci "%llu\n"); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci/* 1628c2ecf20Sopenharmony_ci * Reset the device 1638c2ecf20Sopenharmony_ci * 1648c2ecf20Sopenharmony_ci * Write 0 to ask the device to soft reset, 1 to cold reset, 2 to bus 1658c2ecf20Sopenharmony_ci * reset (as defined by enum i2400m_reset_type). 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_cistatic 1688c2ecf20Sopenharmony_ciint debugfs_i2400m_reset_set(void *data, u64 val) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci int result; 1718c2ecf20Sopenharmony_ci struct i2400m *i2400m = data; 1728c2ecf20Sopenharmony_ci enum i2400m_reset_type rt = val; 1738c2ecf20Sopenharmony_ci switch(rt) { 1748c2ecf20Sopenharmony_ci case I2400M_RT_WARM: 1758c2ecf20Sopenharmony_ci case I2400M_RT_COLD: 1768c2ecf20Sopenharmony_ci case I2400M_RT_BUS: 1778c2ecf20Sopenharmony_ci result = i2400m_reset(i2400m, rt); 1788c2ecf20Sopenharmony_ci if (result >= 0) 1798c2ecf20Sopenharmony_ci result = 0; 1808c2ecf20Sopenharmony_ci break; 1818c2ecf20Sopenharmony_ci default: 1828c2ecf20Sopenharmony_ci result = -EINVAL; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci return result; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_i2400m_reset, 1878c2ecf20Sopenharmony_ci NULL, debugfs_i2400m_reset_set, 1888c2ecf20Sopenharmony_ci "%llu\n"); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_civoid i2400m_debugfs_add(struct i2400m *i2400m) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct dentry *dentry = i2400m->wimax_dev.debugfs_dentry; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci dentry = debugfs_create_dir("i2400m", dentry); 1958c2ecf20Sopenharmony_ci i2400m->debugfs_dentry = dentry; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci d_level_register_debugfs("dl_", control, dentry); 1988c2ecf20Sopenharmony_ci d_level_register_debugfs("dl_", driver, dentry); 1998c2ecf20Sopenharmony_ci d_level_register_debugfs("dl_", debugfs, dentry); 2008c2ecf20Sopenharmony_ci d_level_register_debugfs("dl_", fw, dentry); 2018c2ecf20Sopenharmony_ci d_level_register_debugfs("dl_", netdev, dentry); 2028c2ecf20Sopenharmony_ci d_level_register_debugfs("dl_", rfkill, dentry); 2038c2ecf20Sopenharmony_ci d_level_register_debugfs("dl_", rx, dentry); 2048c2ecf20Sopenharmony_ci d_level_register_debugfs("dl_", tx, dentry); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci debugfs_create_size_t("tx_in", 0400, dentry, &i2400m->tx_in); 2078c2ecf20Sopenharmony_ci debugfs_create_size_t("tx_out", 0400, dentry, &i2400m->tx_out); 2088c2ecf20Sopenharmony_ci debugfs_create_u32("state", 0600, dentry, &i2400m->state); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* 2118c2ecf20Sopenharmony_ci * Trace received messages from user space 2128c2ecf20Sopenharmony_ci * 2138c2ecf20Sopenharmony_ci * In order to tap the bidirectional message stream in the 2148c2ecf20Sopenharmony_ci * 'msg' pipe, user space can read from the 'msg' pipe; 2158c2ecf20Sopenharmony_ci * however, due to limitations in libnl, we can't know what 2168c2ecf20Sopenharmony_ci * the different applications are sending down to the kernel. 2178c2ecf20Sopenharmony_ci * 2188c2ecf20Sopenharmony_ci * So we have this hack where the driver will echo any message 2198c2ecf20Sopenharmony_ci * received on the msg pipe from user space [through a call to 2208c2ecf20Sopenharmony_ci * wimax_dev->op_msg_from_user() into 2218c2ecf20Sopenharmony_ci * i2400m_op_msg_from_user()] into the 'trace' pipe that this 2228c2ecf20Sopenharmony_ci * driver creates. 2238c2ecf20Sopenharmony_ci * 2248c2ecf20Sopenharmony_ci * So then, reading from both the 'trace' and 'msg' pipes in 2258c2ecf20Sopenharmony_ci * user space will provide a full dump of the traffic. 2268c2ecf20Sopenharmony_ci * 2278c2ecf20Sopenharmony_ci * Write 1 to activate, 0 to clear. 2288c2ecf20Sopenharmony_ci * 2298c2ecf20Sopenharmony_ci * It is not really very atomic, but it is also not too 2308c2ecf20Sopenharmony_ci * critical. 2318c2ecf20Sopenharmony_ci */ 2328c2ecf20Sopenharmony_ci debugfs_create_u8("trace_msg_from_user", 0600, dentry, 2338c2ecf20Sopenharmony_ci &i2400m->trace_msg_from_user); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci debugfs_create_file("netdev_queue_stopped", 0400, dentry, i2400m, 2368c2ecf20Sopenharmony_ci &fops_netdev_queue_stopped); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci debugfs_create_file("rx_stats", 0600, dentry, i2400m, 2398c2ecf20Sopenharmony_ci &i2400m_rx_stats_fops); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci debugfs_create_file("tx_stats", 0600, dentry, i2400m, 2428c2ecf20Sopenharmony_ci &i2400m_tx_stats_fops); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci debugfs_create_file("suspend", 0200, dentry, i2400m, 2458c2ecf20Sopenharmony_ci &fops_i2400m_suspend); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci debugfs_create_file("reset", 0200, dentry, i2400m, &fops_i2400m_reset); 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_civoid i2400m_debugfs_rm(struct i2400m *i2400m) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci debugfs_remove_recursive(i2400m->debugfs_dentry); 2538c2ecf20Sopenharmony_ci} 254