aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Buesch <mbuesch@freenet.de>2006-02-28 15:32:19 +0100
committerJohn W. Linville <linville@tuxdriver.com>2006-03-27 11:18:40 -0500
commit367f899ac3b52cf4611cd291ec2bfbf774b15bc7 (patch)
tree99763a1b2c27e3371c575beab326147bdb675410
parentf398f02d12cdc372e16c5e86246b10acf7211abc (diff)
[PATCH] bcm43xx: Add sysfs attributes for device specific tunables.
Signed-off-by: Michael Buesch <mbuesch@freenet.de> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--drivers/net/wireless/bcm43xx/Makefile2
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx.h3
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_main.c4
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c323
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_sysfs.h25
5 files changed, 356 insertions, 1 deletions
diff --git a/drivers/net/wireless/bcm43xx/Makefile b/drivers/net/wireless/bcm43xx/Makefile
index 2962b5f9e3e..bb5220c629d 100644
--- a/drivers/net/wireless/bcm43xx/Makefile
+++ b/drivers/net/wireless/bcm43xx/Makefile
@@ -8,5 +8,5 @@ bcm43xx-objs := bcm43xx_main.o bcm43xx_ilt.o \
bcm43xx_radio.o bcm43xx_phy.o \
bcm43xx_power.o bcm43xx_wx.o \
bcm43xx_leds.o bcm43xx_ethtool.o \
- bcm43xx_xmit.o \
+ bcm43xx_xmit.o bcm43xx_sysfs.o \
$(bcm43xx-obj-y)
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx.h b/drivers/net/wireless/bcm43xx/bcm43xx.h
index 21c75cae14b..fd9754b5295 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx.h
+++ b/drivers/net/wireless/bcm43xx/bcm43xx.h
@@ -15,6 +15,7 @@
#include "bcm43xx_debugfs.h"
#include "bcm43xx_leds.h"
+#include "bcm43xx_sysfs.h"
#define PFX KBUILD_MODNAME ": "
@@ -606,6 +607,8 @@ struct bcm43xx_key {
};
struct bcm43xx_private {
+ struct bcm43xx_sysfs sysfs;
+
struct ieee80211_device *ieee;
struct ieee80211softmac_device *softmac;
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
index f1ac9940f14..1fe2fa9856e 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
@@ -3160,6 +3160,8 @@ static void bcm43xx_free_board(struct bcm43xx_private *bcm)
int i, err;
unsigned long flags;
+ bcm43xx_sysfs_unregister(bcm);
+
bcm43xx_periodic_tasks_delete(bcm);
spin_lock_irqsave(&bcm->lock, flags);
@@ -3270,6 +3272,8 @@ static int bcm43xx_init_board(struct bcm43xx_private *bcm)
spin_unlock_irqrestore(&bcm->lock, flags);
bcm43xx_periodic_tasks_setup(bcm);
+ bcm43xx_sysfs_register(bcm);
+ //FIXME: check for bcm43xx_sysfs_register failure. This function is a bit messy regarding unwinding, though...
assert(err == 0);
out:
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c b/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c
new file mode 100644
index 00000000000..2d31737372f
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c
@@ -0,0 +1,323 @@
+/*
+
+ Broadcom BCM43xx wireless driver
+
+ SYSFS support routines
+
+ Copyright (c) 2006 Michael Buesch <mbuesch@freenet.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#include "bcm43xx_sysfs.h"
+#include "bcm43xx.h"
+#include "bcm43xx_main.h"
+#include "bcm43xx_radio.h"
+
+#include <linux/capability.h>
+
+
+#define GENERIC_FILESIZE 64
+
+
+static int get_integer(const char *buf, size_t count)
+{
+ char tmp[10 + 1] = { 0 };
+ int ret = -EINVAL;
+
+ if (count == 0)
+ goto out;
+ count = min(count, (size_t)10);
+ memcpy(tmp, buf, count);
+ ret = simple_strtol(tmp, NULL, 10);
+out:
+ return ret;
+}
+
+static int get_boolean(const char *buf, size_t count)
+{
+ if (count != 0) {
+ if (buf[0] == '1')
+ return 1;
+ if (buf[0] == '0')
+ return 0;
+ if (count >= 4 && memcmp(buf, "true", 4) == 0)
+ return 1;
+ if (count >= 5 && memcmp(buf, "false", 5) == 0)
+ return 0;
+ if (count >= 3 && memcmp(buf, "yes", 3) == 0)
+ return 1;
+ if (count >= 2 && memcmp(buf, "no", 2) == 0)
+ return 0;
+ if (count >= 2 && memcmp(buf, "on", 2) == 0)
+ return 1;
+ if (count >= 3 && memcmp(buf, "off", 3) == 0)
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static ssize_t bcm43xx_attr_sprom_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_sprom);
+ u16 *sprom;
+ unsigned long flags;
+ int i, err;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ assert(BCM43xx_SPROM_SIZE * sizeof(u16) <= PAGE_SIZE);
+ sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
+ GFP_KERNEL);
+ if (!sprom)
+ return -ENOMEM;
+ spin_lock_irqsave(&bcm->lock, flags);
+ assert(bcm->initialized);
+ err = bcm43xx_sprom_read(bcm, sprom);
+ if (!err) {
+ for (i = 0; i < BCM43xx_SPROM_SIZE; i++) {
+ buf[i * 2] = sprom[i] & 0x00FF;
+ buf[i * 2 + 1] = (sprom[i] & 0xFF00) >> 8;
+ }
+ }
+ spin_unlock_irqrestore(&bcm->lock, flags);
+ kfree(sprom);
+
+ return err ? err : BCM43xx_SPROM_SIZE * sizeof(u16);
+}
+
+static ssize_t bcm43xx_attr_sprom_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_sprom);
+ u16 *sprom;
+ unsigned long flags;
+ int i, err;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (count != BCM43xx_SPROM_SIZE * sizeof(u16))
+ return -EINVAL;
+ sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
+ GFP_KERNEL);
+ if (!sprom)
+ return -ENOMEM;
+ for (i = 0; i < BCM43xx_SPROM_SIZE; i++) {
+ sprom[i] = buf[i * 2] & 0xFF;
+ sprom[i] |= ((u16)(buf[i * 2 + 1] & 0xFF)) << 8;
+ }
+ spin_lock_irqsave(&bcm->lock, flags);
+ assert(bcm->initialized);
+ err = bcm43xx_sprom_write(bcm, sprom);
+ spin_unlock_irqrestore(&bcm->lock, flags);
+ kfree(sprom);
+
+ return err ? err : count;
+
+}
+
+static ssize_t bcm43xx_attr_interfmode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_interfmode);
+ unsigned long flags;
+ int err;
+ ssize_t count = 0;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ spin_lock_irqsave(&bcm->lock, flags);
+ assert(bcm->initialized);
+
+ switch (bcm->current_core->radio->interfmode) {
+ case BCM43xx_RADIO_INTERFMODE_NONE:
+ count = snprintf(buf, PAGE_SIZE, "0 (No Interference Mitigation)\n");
+ break;
+ case BCM43xx_RADIO_INTERFMODE_NONWLAN:
+ count = snprintf(buf, PAGE_SIZE, "1 (Non-WLAN Interference Mitigation)\n");
+ break;
+ case BCM43xx_RADIO_INTERFMODE_MANUALWLAN:
+ count = snprintf(buf, PAGE_SIZE, "2 (WLAN Interference Mitigation)\n");
+ break;
+ default:
+ assert(0);
+ }
+ err = 0;
+
+ spin_unlock_irqrestore(&bcm->lock, flags);
+ return err ? err : count;
+
+}
+
+static ssize_t bcm43xx_attr_interfmode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_interfmode);
+ unsigned long flags;
+ int err;
+ int mode;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ mode = get_integer(buf, count);
+ switch (mode) {
+ case 0:
+ mode = BCM43xx_RADIO_INTERFMODE_NONE;
+ break;
+ case 1:
+ mode = BCM43xx_RADIO_INTERFMODE_NONWLAN;
+ break;
+ case 2:
+ mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN;
+ break;
+ case 3:
+ mode = BCM43xx_RADIO_INTERFMODE_AUTOWLAN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&bcm->lock, flags);
+ assert(bcm->initialized);
+
+ err = bcm43xx_radio_set_interference_mitigation(bcm, mode);
+ if (err) {
+ printk(KERN_ERR PFX "Interference Mitigation not "
+ "supported by device\n");
+ }
+
+ spin_unlock_irqrestore(&bcm->lock, flags);
+
+ return err ? err : count;
+}
+
+static ssize_t bcm43xx_attr_preamble_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_preamble);
+ unsigned long flags;
+ int err;
+ ssize_t count;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ spin_lock_irqsave(&bcm->lock, flags);
+ assert(bcm->initialized);
+
+ if (bcm->short_preamble)
+ count = snprintf(buf, PAGE_SIZE, "1 (Short Preamble enabled)\n");
+ else
+ count = snprintf(buf, PAGE_SIZE, "0 (Short Preamble disabled)\n");
+
+ err = 0;
+ spin_unlock_irqrestore(&bcm->lock, flags);
+
+ return err ? err : count;
+}
+
+static ssize_t bcm43xx_attr_preamble_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_preamble);
+ unsigned long flags;
+ int err;
+ int value;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ value = get_boolean(buf, count);
+ if (value < 0)
+ return value;
+ spin_lock_irqsave(&bcm->lock, flags);
+ assert(bcm->initialized);
+
+ bcm->short_preamble = !!value;
+
+ err = 0;
+ spin_unlock_irqrestore(&bcm->lock, flags);
+
+ return err ? err : count;
+}
+
+int bcm43xx_sysfs_register(struct bcm43xx_private *bcm)
+{
+ struct device *dev = &bcm->pci_dev->dev;
+ struct bcm43xx_sysfs *sysfs = &bcm->sysfs;
+ int err;
+
+ assert(bcm->initialized);
+
+ sysfs->attr_sprom.attr.name = "sprom";
+ sysfs->attr_sprom.attr.owner = THIS_MODULE;
+ sysfs->attr_sprom.attr.mode = 0600;
+ sysfs->attr_sprom.show = bcm43xx_attr_sprom_show;
+ sysfs->attr_sprom.store = bcm43xx_attr_sprom_store;
+ err = device_create_file(dev, &sysfs->attr_sprom);
+ if (err)
+ goto out;
+
+ sysfs->attr_interfmode.attr.name = "interference";
+ sysfs->attr_interfmode.attr.owner = THIS_MODULE;
+ sysfs->attr_interfmode.attr.mode = 0600;
+ sysfs->attr_interfmode.show = bcm43xx_attr_interfmode_show;
+ sysfs->attr_interfmode.store = bcm43xx_attr_interfmode_store;
+ err = device_create_file(dev, &sysfs->attr_interfmode);
+ if (err)
+ goto err_remove_sprom;
+
+ sysfs->attr_preamble.attr.name = "shortpreamble";
+ sysfs->attr_preamble.attr.owner = THIS_MODULE;
+ sysfs->attr_preamble.attr.mode = 0600;
+ sysfs->attr_preamble.show = bcm43xx_attr_preamble_show;
+ sysfs->attr_preamble.store = bcm43xx_attr_preamble_store;
+ err = device_create_file(dev, &sysfs->attr_preamble);
+ if (err)
+ goto err_remove_interfmode;
+
+out:
+ return err;
+err_remove_interfmode:
+ device_remove_file(dev, &sysfs->attr_interfmode);
+err_remove_sprom:
+ device_remove_file(dev, &sysfs->attr_sprom);
+ goto out;
+}
+
+void bcm43xx_sysfs_unregister(struct bcm43xx_private *bcm)
+{
+ struct device *dev = &bcm->pci_dev->dev;
+ struct bcm43xx_sysfs *sysfs = &bcm->sysfs;
+
+ device_remove_file(dev, &sysfs->attr_preamble);
+ device_remove_file(dev, &sysfs->attr_interfmode);
+ device_remove_file(dev, &sysfs->attr_sprom);
+}
+
+/* vim: set ts=8 sw=8 sts=8: */
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.h b/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.h
new file mode 100644
index 00000000000..57f14514e3e
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.h
@@ -0,0 +1,25 @@
+#ifndef BCM43xx_SYSFS_H_
+#define BCM43xx_SYSFS_H_
+
+#include <linux/device.h>
+
+
+struct bcm43xx_sysfs {
+ struct device_attribute attr_sprom;
+ struct device_attribute attr_interfmode;
+ struct device_attribute attr_preamble;
+};
+
+#define devattr_to_bcm(attr, attr_name) ({ \
+ struct bcm43xx_sysfs *__s; struct bcm43xx_private *__p; \
+ __s = container_of((attr), struct bcm43xx_sysfs, attr_name); \
+ __p = container_of(__s, struct bcm43xx_private, sysfs); \
+ __p; \
+ })
+
+struct bcm43xx_private;
+
+int bcm43xx_sysfs_register(struct bcm43xx_private *bcm);
+void bcm43xx_sysfs_unregister(struct bcm43xx_private *bcm);
+
+#endif /* BCM43xx_SYSFS_H_ */