Expose knobs to control the device (induce reset, power saving,
querying tx or rx stats, internal debug information and debug level
manipulation).

Signed-off-by: Inaky Perez-Gonzalez <[EMAIL PROTECTED]>
---
 drivers/net/wimax/i2400m/sysfs.c |  458 ++++++++++++++++++++++++++++++++++++++
 1 files changed, 458 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/wimax/i2400m/sysfs.c

diff --git a/drivers/net/wimax/i2400m/sysfs.c b/drivers/net/wimax/i2400m/sysfs.c
new file mode 100644
index 0000000..3640719
--- /dev/null
+++ b/drivers/net/wimax/i2400m/sysfs.c
@@ -0,0 +1,458 @@
+/*
+ * Intel Wireless WiMAX Connection 2400m
+ * Sysfs interfaces to show driver and device information
+ *
+ *
+ * Copyright (C) 2007 Intel Corporation <[EMAIL PROTECTED]>
+ * Inaky Perez-Gonzalez <[EMAIL PROTECTED]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include "i2400m.h"
+
+
+#define D_SUBMODULE sysfs
+#include "debug-levels.h"
+
+/*
+ * Cold reset the device (deferred work routine)
+ *
+ * Need to use a workstruct because when done from sysfs, the device
+ * lock is taken, so after a reset, the new device "instance" is
+ * connected before we have a chance to disconnect the current
+ * instance. This creates problems for upper layers, as for example
+ * the management daemon for a while could think we have two wimax
+ * connections in the system.
+ *
+ * Note calling _put before _reset_cold is ok because _put uses
+ * netdev's dev_put(), which won't free anything.
+ *
+ * In any case, it has to be before, as if not we enter a race
+ * coindition calling reset_cold(); it would try to unregister the
+ * device, but it will keep the reference count and because reset had
+ * a device lock...well, big mess.
+ */
+static
+void __i2400m_reset_cold_work(struct work_struct *ws)
+{
+       struct i2400m_work *iw =
+               container_of(ws, struct i2400m_work, ws);
+       i2400m_put(iw->i2400m);
+       iw->i2400m->bus_reset(iw->i2400m, I2400M_RT_COLD);
+       kfree(iw);
+}
+
+static
+ssize_t i2400m_reset_cold_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t size)
+{
+       ssize_t result;
+       struct i2400m *i2400m = net_dev_to_i2400m(to_net_dev(dev));
+       unsigned val;
+
+       result = -EINVAL;
+       if (sscanf(buf, "%u\n", &val) != 1)
+               goto error_no_unsigned;
+       if (val != 1)
+               goto error_bad_value;
+       i2400m_schedule_work(i2400m, __i2400m_reset_cold_work, GFP_KERNEL);
+       if (result >= 1)
+               result = size;
+error_no_unsigned:
+error_bad_value:
+       return result;
+}
+
+static
+DEVICE_ATTR(i2400m_reset_cold, S_IRUGO | S_IWUSR,
+           NULL, i2400m_reset_cold_store);
+
+
+/*
+ * Warm reset the device
+ *
+ * We just warm reset the device; no need to defer, as the device will
+ * not disconnect.
+ */
+static
+ssize_t i2400m_reset_warm_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t size)
+{
+       ssize_t result;
+       struct i2400m *i2400m = net_dev_to_i2400m(to_net_dev(dev));
+       unsigned val;
+
+       result = -EINVAL;
+       if (sscanf(buf, "%u\n", &val) != 1)
+               goto error_no_unsigned;
+       if (val != 1)
+               goto error_bad_value;
+       result = i2400m->bus_reset(i2400m, I2400M_RT_WARM);
+       if (result >= 0)
+               result = size;
+error_no_unsigned:
+error_bad_value:
+       return result;
+}
+
+static
+DEVICE_ATTR(i2400m_reset_warm, S_IRUGO | S_IWUSR,
+           NULL, i2400m_reset_warm_store);
+
+
+/*
+ * Show RX statistics
+ *
+ * Total #payloads | min #payloads in a RX | max #payloads in a RX
+ * Total #RXs | Total bytes | min #bytes in a RX | max #bytes in a RX
+ *
+ * Write 1 to clear.
+ */
+static
+ssize_t i2400m_rx_stats_show(struct device *dev,
+                            struct device_attribute *attr,
+                            char *buf)
+{
+       ssize_t result;
+       struct i2400m *i2400m = net_dev_to_i2400m(to_net_dev(dev));
+       unsigned long flags;
+
+       spin_lock_irqsave(&i2400m->rx_lock, flags);
+       result = snprintf(buf, PAGE_SIZE, "%u %u %u %u %u %u %u\n",
+                         i2400m->rx_pl_num, i2400m->rx_pl_min,
+                         i2400m->rx_pl_max, i2400m->rx_num,
+                         i2400m->rx_size_acc,
+                         i2400m->rx_size_min, i2400m->rx_size_max);
+       spin_unlock_irqrestore(&i2400m->rx_lock, flags);
+       return result;
+}
+
+static
+ssize_t i2400m_rx_stats_store(struct device *dev,
+                             struct device_attribute *attr,
+                             const char *buf, size_t size)
+{
+       ssize_t result;
+       struct i2400m *i2400m = net_dev_to_i2400m(to_net_dev(dev));
+       unsigned val;
+       unsigned long flags;
+
+       result = -EINVAL;
+       if (sscanf(buf, "%u\n", &val) != 1)
+               goto error_no_unsigned;
+       if (val != 1)
+               goto error_bad_value;
+       spin_lock_irqsave(&i2400m->rx_lock, flags);
+       i2400m->rx_pl_num = 0;
+       i2400m->rx_pl_max = 0;
+       i2400m->rx_pl_min = UINT_MAX;
+       i2400m->rx_num = 0;
+       i2400m->rx_size_acc = 0;
+       i2400m->rx_size_min = UINT_MAX;
+       i2400m->rx_size_max = 0;
+       spin_unlock_irqrestore(&i2400m->rx_lock, flags);
+       result = size;
+error_no_unsigned:
+error_bad_value:
+       return result;
+}
+
+static
+DEVICE_ATTR(i2400m_rx_stats, S_IRUGO | S_IWUSR,
+           i2400m_rx_stats_show, i2400m_rx_stats_store);
+
+
+/*
+ * Show TX statistics
+ *
+ * Total #payloads | min #payloads in a TX | max #payloads in a TX
+ * Total #TXs | Total bytes | min #bytes in a TX | max #bytes in a TX
+ *
+ * Write 1 to clear.
+ */
+static
+ssize_t i2400m_tx_stats_show(struct device *dev,
+                            struct device_attribute *attr,
+                            char *buf)
+{
+       ssize_t result;
+       struct i2400m *i2400m = net_dev_to_i2400m(to_net_dev(dev));
+       unsigned long flags;
+
+       spin_lock_irqsave(&i2400m->tx_lock, flags);
+       result = snprintf(buf, PAGE_SIZE, "%u %u %u %u %u %u %u\n",
+                         i2400m->tx_pl_num, i2400m->tx_pl_min,
+                         i2400m->tx_pl_max, i2400m->tx_num,
+                         i2400m->tx_size_acc,
+                         i2400m->tx_size_min, i2400m->tx_size_max);
+       spin_unlock_irqrestore(&i2400m->tx_lock, flags);
+       return result;
+}
+
+static
+ssize_t i2400m_tx_stats_store(struct device *dev,
+                             struct device_attribute *attr,
+                             const char *buf, size_t size)
+{
+       ssize_t result;
+       struct i2400m *i2400m = net_dev_to_i2400m(to_net_dev(dev));
+       unsigned val;
+       unsigned long flags;
+
+       result = -EINVAL;
+       if (sscanf(buf, "%u\n", &val) != 1)
+               goto error_no_unsigned;
+       if (val != 1)
+               goto error_bad_value;
+       spin_lock_irqsave(&i2400m->tx_lock, flags);
+       i2400m->tx_pl_num = 0;
+       i2400m->tx_pl_max = 0;
+       i2400m->tx_pl_min = UINT_MAX;
+       i2400m->tx_num = 0;
+       i2400m->tx_size_acc = 0;
+       i2400m->tx_size_min = UINT_MAX;
+       i2400m->tx_size_max = 0;
+       spin_unlock_irqrestore(&i2400m->tx_lock, flags);
+       result = size;
+error_no_unsigned:
+error_bad_value:
+       return result;
+}
+
+static
+DEVICE_ATTR(i2400m_tx_stats, S_IRUGO | S_IWUSR,
+           i2400m_tx_stats_show, i2400m_tx_stats_store);
+
+/*
+ * Show debug stuff
+ *
+ * Don't poke with this unless you know what you are doing.
+ */
+static
+ssize_t i2400m_debug_store(struct device *dev,
+                          struct device_attribute *attr,
+                          const char *buf, size_t size)
+{
+       ssize_t result = 0;
+       struct i2400m *i2400m = net_dev_to_i2400m(to_net_dev(dev));
+       char var[256], val[256];
+
+       result = -EINVAL;
+       if (sscanf(buf, "%254s %254s\n", var, val) != 2) {
+               dev_err(dev, "debug: bad format, expected VARIABLE VALUE\n");
+               goto error;
+       }
+
+       if (!strcmp(var, "state")) {
+               enum i2400m_system_state state;
+               if (sscanf(val, "%u", &state) != 1) {
+                       dev_err(dev, "debug/state: can't parse unsigned %s\n",
+                               val);
+                       goto error;
+               }
+               if (state < I2400M_SS_UNINITIALIZED || state >= I2400M_SS_MAX) {
+                       dev_err(dev, "debug/state: %u is out of range\n",
+                               state);
+                       goto error;
+               }
+               i2400m->state = state;
+               result = size;
+       } else
+               dev_err(dev, "debug: unknown variable %s\n", var);
+error:
+       return result;
+}
+
+/*
+ * Show debug stuff
+ */
+static
+ssize_t i2400m_debug_show(struct device *dev,
+                         struct device_attribute *attr,
+                         char *buf)
+{
+       ssize_t result = 0;
+       struct i2400m *i2400m = net_dev_to_i2400m(to_net_dev(dev));
+       unsigned long flags;
+
+       result += scnprintf(
+               buf, PAGE_SIZE,
+               "Don't poke with this unless you know what you are doing. \n"
+               "It provides means to modify internal settings in the \n"
+               "driver that can be used to exercise error paths.\n"
+               "\n"
+               "Format for setting them is 'echo FIELD VALUE' (fields \n"
+               "marked ! can't be set)\n"
+               "\n");
+
+       result += scnprintf(buf + result, PAGE_SIZE - result,
+                           "!queue: %s\n",
+                           netif_queue_stopped(to_net_dev(dev)) ?
+                           "stopped" : "running");
+
+       spin_lock_irqsave(&i2400m->tx_lock, flags);
+       result += scnprintf(
+               buf + result, PAGE_SIZE - result,
+               "!TX FIFO in: %zu\n"
+               "!TX FIFO out: %zu (%zu used)\n"
+               "!TX FIFO msg: @%zd\n",
+               i2400m->tx_in, i2400m->tx_out,
+               i2400m->tx_out - i2400m->tx_in,
+               (size_t) (i2400m->tx_msg ?
+                         (void *) i2400m->tx_msg - i2400m->tx_buf : -1));
+       spin_unlock_irqrestore(&i2400m->tx_lock, flags);
+
+       result += scnprintf(
+               buf + result, PAGE_SIZE - result,
+               "state: %u\n", i2400m->state);
+       return result;
+}
+static
+DEVICE_ATTR(i2400m_debug, S_IRUGO | S_IWUSR,
+           i2400m_debug_show, i2400m_debug_store);
+
+
+/*
+ * Trace received messages from user space
+ *
+ * In order to tap the bidirectional message stream in the 'msg' pipe,
+ * user space can read from the 'msg' pipe; however, due to
+ * limitations in libnl, we can't know what the different applications
+ * are sending down to the kernel.
+ *
+ * So we have this hack where the driver will echo any message
+ * received on the msg pipe from user space [through a call to
+ * wimax_dev->op_msg_from_user() into i2400m_op_msg_from_user()] into
+ * the 'trace' pipe that this driver creates.
+ *
+ * So then, reading from both the 'trace' and 'msg' pipes in user spce
+ * will provide a full dump of the traffic.
+ *
+ * Write 1 to activate, 0 to clear.
+ *
+ * It is not really very atomic, but it is also not too critical.
+ */
+static
+ssize_t i2400m_trace_msg_from_user_show(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       ssize_t result;
+       struct i2400m *i2400m = net_dev_to_i2400m(to_net_dev(dev));
+
+       result = snprintf(buf, PAGE_SIZE, "%u\n",
+                         i2400m->trace_msg_from_user);
+       return result;
+}
+
+static
+ssize_t i2400m_trace_msg_from_user_store(struct device *dev,
+                                        struct device_attribute *attr,
+                                        const char *buf, size_t size)
+{
+       ssize_t result;
+       struct i2400m *i2400m = net_dev_to_i2400m(to_net_dev(dev));
+       unsigned val;
+
+       result = -EINVAL;
+       if (sscanf(buf, "%u\n", &val) == 1) {
+               i2400m->trace_msg_from_user = val ? 1 : 0;
+               result = size;
+       }
+       return result;
+}
+
+static
+DEVICE_ATTR(i2400m_trace_msg_from_user, S_IRUGO | S_IWUSR,
+           i2400m_trace_msg_from_user_show, i2400m_trace_msg_from_user_store);
+
+
+/*
+ * Ask the device to enter power saving mode.
+ *
+ * This is not really selective suspend mode, but asking the device to
+ * enter selective suspend on its own.
+ */
+static
+ssize_t i2400m_suspend_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t size)
+{
+       ssize_t result;
+       struct i2400m *i2400m = net_dev_to_i2400m(to_net_dev(dev));
+       unsigned val;
+
+       result = -EINVAL;
+       if (sscanf(buf, "%u\n", &val) != 1)
+               goto error_no_unsigned;
+       if (val != 1)
+               goto error_bad_value;
+       result = i2400m_cmd_enter_powersave(i2400m);
+       if (result >= 0)
+               result = size;
+error_no_unsigned:
+error_bad_value:
+       return result;
+}
+
+static
+DEVICE_ATTR(i2400m_suspend, S_IRUGO | S_IWUSR,
+           NULL, i2400m_suspend_store);
+
+
+/*
+ * Debug levels control; see debug.h
+ */
+struct d_level D_LEVEL[] = {
+       D_SUBMODULE_DEFINE(control),
+       D_SUBMODULE_DEFINE(driver),
+       D_SUBMODULE_DEFINE(fw),
+       D_SUBMODULE_DEFINE(netdev),
+       D_SUBMODULE_DEFINE(rfkill),
+       D_SUBMODULE_DEFINE(rx),
+       D_SUBMODULE_DEFINE(sysfs),
+       D_SUBMODULE_DEFINE(tx),
+};
+size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL);
+
+static
+DEVICE_ATTR(i2400m_debug_levels, S_IRUGO | S_IWUSR,
+           d_level_show, d_level_store);
+
+
+static
+struct attribute *i2400m_dev_attrs[] = {
+       &dev_attr_i2400m_reset_cold.attr,
+       &dev_attr_i2400m_reset_warm.attr,
+       &dev_attr_i2400m_suspend.attr,
+       &dev_attr_i2400m_rx_stats.attr,
+       &dev_attr_i2400m_tx_stats.attr,
+       &dev_attr_i2400m_debug.attr,
+       &dev_attr_i2400m_debug_levels.attr,
+       &dev_attr_i2400m_trace_msg_from_user.attr,
+       NULL,
+};
+
+struct attribute_group i2400m_dev_attr_group = {
+       .name = NULL,           /* we want them in the same directory */
+       .attrs = i2400m_dev_attrs,
+};
-- 
1.5.6.5

_______________________________________________
wimax mailing list
[email protected]
http://www.linuxwimax.org/mailman/listinfo/wimax

Reply via email to