aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wimax
diff options
context:
space:
mode:
authorInaky Perez-Gonzalez <inaky@linux.intel.com>2009-02-28 18:42:50 -0500
committerDavid S. Miller <davem@davemloft.net>2009-03-02 06:10:25 -0500
commit8987691a4aa6622a1b58bb12c56abaf3d2098fad (patch)
tree92da0abdf6a29aa60ec5aff6250b1937d4c264d6 /drivers/net/wimax
parent6a0f7ab8305cb60a43a6c4a548f57adab784e6cd (diff)
wimax/i2400m: allow control of the base-station idle mode timeout
For power saving reasons, WiMAX links can be put in idle mode while connected after a certain time of the link not being used for tx or rx. In this mode, the device pages the base-station regularly and when data is ready to be transmitted, the link is revived. This patch allows the user to control the time the device has to be idle before it decides to go to idle mode from a sysfs interace. It also updates the initialization code to acknowledge the module variable 'idle_mode_disabled' when the firmware is a newer version (upcoming 1.4 vs 2.6.29's v1.3). The method for setting the idle mode timeout in the older firmwares is much more limited and can be only done at initialization time. Thus, the sysfs file will return -ENOSYS on older ones. Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/wimax')
-rw-r--r--drivers/net/wimax/i2400m/Makefile1
-rw-r--r--drivers/net/wimax/i2400m/control.c98
-rw-r--r--drivers/net/wimax/i2400m/debug-levels.h1
-rw-r--r--drivers/net/wimax/i2400m/driver.c10
-rw-r--r--drivers/net/wimax/i2400m/i2400m.h29
-rw-r--r--drivers/net/wimax/i2400m/sysfs.c80
6 files changed, 211 insertions, 8 deletions
diff --git a/drivers/net/wimax/i2400m/Makefile b/drivers/net/wimax/i2400m/Makefile
index 1696e936cf5a..5d9e018d31af 100644
--- a/drivers/net/wimax/i2400m/Makefile
+++ b/drivers/net/wimax/i2400m/Makefile
@@ -8,6 +8,7 @@ i2400m-y := \
8 driver.o \ 8 driver.o \
9 fw.o \ 9 fw.o \
10 op-rfkill.o \ 10 op-rfkill.o \
11 sysfs.o \
11 netdev.o \ 12 netdev.o \
12 tx.o \ 13 tx.o \
13 rx.o 14 rx.o
diff --git a/drivers/net/wimax/i2400m/control.c b/drivers/net/wimax/i2400m/control.c
index c8b3a68b72b8..c3968b240d69 100644
--- a/drivers/net/wimax/i2400m/control.c
+++ b/drivers/net/wimax/i2400m/control.c
@@ -1222,6 +1222,77 @@ EXPORT_SYMBOL_GPL(i2400m_set_init_config);
1222 1222
1223 1223
1224/** 1224/**
1225 * i2400m_set_idle_timeout - Set the device's idle mode timeout
1226 *
1227 * @i2400m: i2400m device descriptor
1228 *
1229 * @msecs: milliseconds for the timeout to enter idle mode. Between
1230 * 100 to 300000 (5m); 0 to disable. In increments of 100.
1231 *
1232 * After this @msecs of the link being idle (no data being sent or
1233 * received), the device will negotiate with the basestation entering
1234 * idle mode for saving power. The connection is maintained, but
1235 * getting out of it (done in tx.c) will require some negotiation,
1236 * possible crypto re-handshake and a possible DHCP re-lease.
1237 *
1238 * Only available if fw_version >= 0x00090002.
1239 *
1240 * Returns: 0 if ok, < 0 errno code on error.
1241 */
1242int i2400m_set_idle_timeout(struct i2400m *i2400m, unsigned msecs)
1243{
1244 int result;
1245 struct device *dev = i2400m_dev(i2400m);
1246 struct sk_buff *ack_skb;
1247 struct {
1248 struct i2400m_l3l4_hdr hdr;
1249 struct i2400m_tlv_config_idle_timeout cit;
1250 } *cmd;
1251 const struct i2400m_l3l4_hdr *ack;
1252 size_t ack_len;
1253 char strerr[32];
1254
1255 result = -ENOSYS;
1256 if (i2400m_le_v1_3(i2400m))
1257 goto error_alloc;
1258 result = -ENOMEM;
1259 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
1260 if (cmd == NULL)
1261 goto error_alloc;
1262 cmd->hdr.type = cpu_to_le16(I2400M_MT_GET_STATE);
1263 cmd->hdr.length = cpu_to_le16(sizeof(*cmd) - sizeof(cmd->hdr));
1264 cmd->hdr.version = cpu_to_le16(I2400M_L3L4_VERSION);
1265
1266 cmd->cit.hdr.type =
1267 cpu_to_le16(I2400M_TLV_CONFIG_IDLE_TIMEOUT);
1268 cmd->cit.hdr.length = cpu_to_le16(sizeof(cmd->cit.timeout));
1269 cmd->cit.timeout = cpu_to_le32(msecs);
1270
1271 ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd));
1272 if (IS_ERR(ack_skb)) {
1273 dev_err(dev, "Failed to issue 'set idle timeout' command: "
1274 "%ld\n", PTR_ERR(ack_skb));
1275 result = PTR_ERR(ack_skb);
1276 goto error_msg_to_dev;
1277 }
1278 ack = wimax_msg_data_len(ack_skb, &ack_len);
1279 result = i2400m_msg_check_status(ack, strerr, sizeof(strerr));
1280 if (result < 0) {
1281 dev_err(dev, "'set idle timeout' (0x%04x) command failed: "
1282 "%d - %s\n", I2400M_MT_GET_STATE, result, strerr);
1283 goto error_cmd_failed;
1284 }
1285 result = 0;
1286 kfree_skb(ack_skb);
1287error_cmd_failed:
1288error_msg_to_dev:
1289 kfree(cmd);
1290error_alloc:
1291 return result;
1292}
1293
1294
1295/**
1225 * i2400m_dev_initialize - Initialize the device once communications are ready 1296 * i2400m_dev_initialize - Initialize the device once communications are ready
1226 * 1297 *
1227 * @i2400m: device descriptor 1298 * @i2400m: device descriptor
@@ -1239,19 +1310,28 @@ int i2400m_dev_initialize(struct i2400m *i2400m)
1239 int result; 1310 int result;
1240 struct device *dev = i2400m_dev(i2400m); 1311 struct device *dev = i2400m_dev(i2400m);
1241 struct i2400m_tlv_config_idle_parameters idle_params; 1312 struct i2400m_tlv_config_idle_parameters idle_params;
1313 struct i2400m_tlv_config_idle_timeout idle_timeout;
1242 const struct i2400m_tlv_hdr *args[9]; 1314 const struct i2400m_tlv_hdr *args[9];
1243 unsigned argc = 0; 1315 unsigned argc = 0;
1244 1316
1245 d_fnstart(3, dev, "(i2400m %p)\n", i2400m); 1317 d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
1246 /* Useless for now...might change */
1247 if (i2400m_idle_mode_disabled) { 1318 if (i2400m_idle_mode_disabled) {
1248 idle_params.hdr.type = 1319 if (i2400m_le_v1_3(i2400m)) {
1249 cpu_to_le16(I2400M_TLV_CONFIG_IDLE_PARAMETERS); 1320 idle_params.hdr.type =
1250 idle_params.hdr.length = cpu_to_le16( 1321 cpu_to_le16(I2400M_TLV_CONFIG_IDLE_PARAMETERS);
1251 sizeof(idle_params) - sizeof(idle_params.hdr)); 1322 idle_params.hdr.length = cpu_to_le16(
1252 idle_params.idle_timeout = 0; 1323 sizeof(idle_params) - sizeof(idle_params.hdr));
1253 idle_params.idle_paging_interval = 0; 1324 idle_params.idle_timeout = 0;
1254 args[argc++] = &idle_params.hdr; 1325 idle_params.idle_paging_interval = 0;
1326 args[argc++] = &idle_params.hdr;
1327 } else {
1328 idle_timeout.hdr.type =
1329 cpu_to_le16(I2400M_TLV_CONFIG_IDLE_TIMEOUT);
1330 idle_timeout.hdr.length = cpu_to_le16(
1331 sizeof(idle_timeout) - sizeof(idle_timeout.hdr));
1332 idle_timeout.timeout = 0;
1333 args[argc++] = &idle_timeout.hdr;
1334 }
1255 } 1335 }
1256 result = i2400m_set_init_config(i2400m, args, argc); 1336 result = i2400m_set_init_config(i2400m, args, argc);
1257 if (result < 0) 1337 if (result < 0)
@@ -1264,6 +1344,8 @@ int i2400m_dev_initialize(struct i2400m *i2400m)
1264 */ 1344 */
1265 result = i2400m_cmd_get_state(i2400m); 1345 result = i2400m_cmd_get_state(i2400m);
1266error: 1346error:
1347 if (result < 0)
1348 dev_err(dev, "failed to initialize the device: %d\n", result);
1267 d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); 1349 d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result);
1268 return result; 1350 return result;
1269} 1351}
diff --git a/drivers/net/wimax/i2400m/debug-levels.h b/drivers/net/wimax/i2400m/debug-levels.h
index 3183baa16a52..48fbfaa0d403 100644
--- a/drivers/net/wimax/i2400m/debug-levels.h
+++ b/drivers/net/wimax/i2400m/debug-levels.h
@@ -38,6 +38,7 @@ enum d_module {
38 D_SUBMODULE_DECLARE(netdev), 38 D_SUBMODULE_DECLARE(netdev),
39 D_SUBMODULE_DECLARE(rfkill), 39 D_SUBMODULE_DECLARE(rfkill),
40 D_SUBMODULE_DECLARE(rx), 40 D_SUBMODULE_DECLARE(rx),
41 D_SUBMODULE_DECLARE(sysfs),
41 D_SUBMODULE_DECLARE(tx), 42 D_SUBMODULE_DECLARE(tx),
42}; 43};
43 44
diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c
index 69a816e7c5db..f988771bfae0 100644
--- a/drivers/net/wimax/i2400m/driver.c
+++ b/drivers/net/wimax/i2400m/driver.c
@@ -662,6 +662,11 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags)
662 wimax_state_change(wimax_dev, WIMAX_ST_UNINITIALIZED); 662 wimax_state_change(wimax_dev, WIMAX_ST_UNINITIALIZED);
663 663
664 /* Now setup all that requires a registered net and wimax device. */ 664 /* Now setup all that requires a registered net and wimax device. */
665 result = sysfs_create_group(&net_dev->dev.kobj, &i2400m_dev_attr_group);
666 if (result < 0) {
667 dev_err(dev, "cannot setup i2400m's sysfs: %d\n", result);
668 goto error_sysfs_setup;
669 }
665 result = i2400m_debugfs_add(i2400m); 670 result = i2400m_debugfs_add(i2400m);
666 if (result < 0) { 671 if (result < 0) {
667 dev_err(dev, "cannot setup i2400m's debugfs: %d\n", result); 672 dev_err(dev, "cannot setup i2400m's debugfs: %d\n", result);
@@ -671,6 +676,9 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags)
671 return result; 676 return result;
672 677
673error_debugfs_setup: 678error_debugfs_setup:
679 sysfs_remove_group(&i2400m->wimax_dev.net_dev->dev.kobj,
680 &i2400m_dev_attr_group);
681error_sysfs_setup:
674 wimax_dev_rm(&i2400m->wimax_dev); 682 wimax_dev_rm(&i2400m->wimax_dev);
675error_wimax_dev_add: 683error_wimax_dev_add:
676 i2400m_dev_stop(i2400m); 684 i2400m_dev_stop(i2400m);
@@ -702,6 +710,8 @@ void i2400m_release(struct i2400m *i2400m)
702 netif_stop_queue(i2400m->wimax_dev.net_dev); 710 netif_stop_queue(i2400m->wimax_dev.net_dev);
703 711
704 i2400m_debugfs_rm(i2400m); 712 i2400m_debugfs_rm(i2400m);
713 sysfs_remove_group(&i2400m->wimax_dev.net_dev->dev.kobj,
714 &i2400m_dev_attr_group);
705 wimax_dev_rm(&i2400m->wimax_dev); 715 wimax_dev_rm(&i2400m->wimax_dev);
706 i2400m_dev_stop(i2400m); 716 i2400m_dev_stop(i2400m);
707 unregister_netdev(i2400m->wimax_dev.net_dev); 717 unregister_netdev(i2400m->wimax_dev.net_dev);
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h
index 5008cdb12b42..0c60d5c43007 100644
--- a/drivers/net/wimax/i2400m/i2400m.h
+++ b/drivers/net/wimax/i2400m/i2400m.h
@@ -585,6 +585,8 @@ unsigned i2400m_brh_get_signature(const struct i2400m_bootrom_header *hdr)
585 * Driver / device setup and internal functions 585 * Driver / device setup and internal functions
586 */ 586 */
587extern void i2400m_netdev_setup(struct net_device *net_dev); 587extern void i2400m_netdev_setup(struct net_device *net_dev);
588extern int i2400m_sysfs_setup(struct device_driver *);
589extern void i2400m_sysfs_release(struct device_driver *);
588extern int i2400m_tx_setup(struct i2400m *); 590extern int i2400m_tx_setup(struct i2400m *);
589extern void i2400m_wake_tx_work(struct work_struct *); 591extern void i2400m_wake_tx_work(struct work_struct *);
590extern void i2400m_tx_release(struct i2400m *); 592extern void i2400m_tx_release(struct i2400m *);
@@ -728,6 +730,7 @@ extern struct sk_buff *i2400m_get_device_info(struct i2400m *);
728extern int i2400m_firmware_check(struct i2400m *); 730extern int i2400m_firmware_check(struct i2400m *);
729extern int i2400m_set_init_config(struct i2400m *, 731extern int i2400m_set_init_config(struct i2400m *,
730 const struct i2400m_tlv_hdr **, size_t); 732 const struct i2400m_tlv_hdr **, size_t);
733extern int i2400m_set_idle_timeout(struct i2400m *, unsigned);
731 734
732static inline 735static inline
733struct usb_endpoint_descriptor *usb_get_epd(struct usb_interface *iface, int ep) 736struct usb_endpoint_descriptor *usb_get_epd(struct usb_interface *iface, int ep)
@@ -740,6 +743,32 @@ extern int i2400m_op_rfkill_sw_toggle(struct wimax_dev *,
740extern void i2400m_report_tlv_rf_switches_status( 743extern void i2400m_report_tlv_rf_switches_status(
741 struct i2400m *, const struct i2400m_tlv_rf_switches_status *); 744 struct i2400m *, const struct i2400m_tlv_rf_switches_status *);
742 745
746/*
747 * Helpers for firmware backwards compability
748 *
749 * As we aim to support at least the firmware version that was
750 * released with the previous kernel/driver release, some code will be
751 * conditionally executed depending on the firmware version. On each
752 * release, the code to support fw releases past the last two ones
753 * will be purged.
754 *
755 * By making it depend on this macros, it is easier to keep it a tab
756 * on what has to go and what not.
757 */
758static inline
759unsigned i2400m_le_v1_3(struct i2400m *i2400m)
760{
761 /* running fw is lower or v1.3 */
762 return i2400m->fw_version <= 0x00090001;
763}
764
765static inline
766unsigned i2400m_ge_v1_4(struct i2400m *i2400m)
767{
768 /* running fw is higher or v1.4 */
769 return i2400m->fw_version >= 0x00090002;
770}
771
743 772
744/* 773/*
745 * Do a millisecond-sleep for allowing wireshark to dump all the data 774 * Do a millisecond-sleep for allowing wireshark to dump all the data
diff --git a/drivers/net/wimax/i2400m/sysfs.c b/drivers/net/wimax/i2400m/sysfs.c
new file mode 100644
index 000000000000..1237109f251a
--- /dev/null
+++ b/drivers/net/wimax/i2400m/sysfs.c
@@ -0,0 +1,80 @@
1/*
2 * Intel Wireless WiMAX Connection 2400m
3 * Sysfs interfaces to show 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/netdevice.h>
25#include <linux/etherdevice.h>
26#include <linux/spinlock.h>
27#include <linux/device.h>
28#include "i2400m.h"
29
30
31#define D_SUBMODULE sysfs
32#include "debug-levels.h"
33
34
35/*
36 * Set the idle timeout (msecs)
37 *
38 * FIXME: eventually this should be a common WiMAX stack method, but
39 * would like to wait to see how other devices manage it.
40 */
41static
42ssize_t i2400m_idle_timeout_store(struct device *dev,
43 struct device_attribute *attr,
44 const char *buf, size_t size)
45{
46 ssize_t result;
47 struct i2400m *i2400m = net_dev_to_i2400m(to_net_dev(dev));
48 unsigned val;
49
50 result = -EINVAL;
51 if (sscanf(buf, "%u\n", &val) != 1)
52 goto error_no_unsigned;
53 if (val != 0 && (val < 100 || val > 300000 || val % 100 != 0)) {
54 dev_err(dev, "idle_timeout: %u: invalid msecs specification; "
55 "valid values are 0, 100-300000 in 100 increments\n",
56 val);
57 goto error_bad_value;
58 }
59 result = i2400m_set_idle_timeout(i2400m, val);
60 if (result >= 0)
61 result = size;
62error_no_unsigned:
63error_bad_value:
64 return result;
65}
66
67static
68DEVICE_ATTR(i2400m_idle_timeout, S_IWUSR,
69 NULL, i2400m_idle_timeout_store);
70
71static
72struct attribute *i2400m_dev_attrs[] = {
73 &dev_attr_i2400m_idle_timeout.attr,
74 NULL,
75};
76
77struct attribute_group i2400m_dev_attr_group = {
78 .name = NULL, /* we want them in the same directory */
79 .attrs = i2400m_dev_attrs,
80};