linux/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c
<<
>>
Prefs
   1/*
   2
   3  Broadcom BCM43xx wireless driver
   4
   5  SYSFS support routines
   6
   7  Copyright (c) 2006 Michael Buesch <mbuesch@freenet.de>
   8
   9  This program is free software; you can redistribute it and/or modify
  10  it under the terms of the GNU General Public License as published by
  11  the Free Software Foundation; either version 2 of the License, or
  12  (at your option) any later version.
  13
  14  This program is distributed in the hope that it will be useful,
  15  but WITHOUT ANY WARRANTY; without even the implied warranty of
  16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17  GNU General Public License for more details.
  18
  19  You should have received a copy of the GNU General Public License
  20  along with this program; see the file COPYING.  If not, write to
  21  the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
  22  Boston, MA 02110-1301, USA.
  23
  24*/
  25
  26#include "bcm43xx_sysfs.h"
  27#include "bcm43xx.h"
  28#include "bcm43xx_main.h"
  29#include "bcm43xx_radio.h"
  30
  31#include <linux/capability.h>
  32
  33
  34#define GENERIC_FILESIZE        64
  35
  36
  37static int get_integer(const char *buf, size_t count)
  38{
  39        char tmp[10 + 1] = { 0 };
  40        int ret = -EINVAL;
  41
  42        if (count == 0)
  43                goto out;
  44        count = min(count, (size_t)10);
  45        memcpy(tmp, buf, count);
  46        ret = simple_strtol(tmp, NULL, 10);
  47out:
  48        return ret;
  49}
  50
  51static int get_boolean(const char *buf, size_t count)
  52{
  53        if (count != 0) {
  54                if (buf[0] == '1')
  55                        return 1;
  56                if (buf[0] == '0')
  57                        return 0;
  58                if (count >= 4 && memcmp(buf, "true", 4) == 0)
  59                        return 1;
  60                if (count >= 5 && memcmp(buf, "false", 5) == 0)
  61                        return 0;
  62                if (count >= 3 && memcmp(buf, "yes", 3) == 0)
  63                        return 1;
  64                if (count >= 2 && memcmp(buf, "no", 2) == 0)
  65                        return 0;
  66                if (count >= 2 && memcmp(buf, "on", 2) == 0)
  67                        return 1;
  68                if (count >= 3 && memcmp(buf, "off", 3) == 0)
  69                        return 0;
  70        }
  71        return -EINVAL;
  72}
  73
  74static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len)
  75{
  76        int i, pos = 0;
  77
  78        for (i = 0; i < BCM43xx_SPROM_SIZE; i++) {
  79                pos += snprintf(buf + pos, buf_len - pos - 1,
  80                                "%04X", swab16(sprom[i]) & 0xFFFF);
  81        }
  82        pos += snprintf(buf + pos, buf_len - pos - 1, "\n");
  83
  84        return pos + 1;
  85}
  86
  87static int hex2sprom(u16 *sprom, const char *dump, size_t len)
  88{
  89        char tmp[5] = { 0 };
  90        int cnt = 0;
  91        unsigned long parsed;
  92
  93        if (len < BCM43xx_SPROM_SIZE * sizeof(u16) * 2)
  94                return -EINVAL;
  95
  96        while (cnt < BCM43xx_SPROM_SIZE) {
  97                memcpy(tmp, dump, 4);
  98                dump += 4;
  99                parsed = simple_strtoul(tmp, NULL, 16);
 100                sprom[cnt++] = swab16((u16)parsed);
 101        }
 102
 103        return 0;
 104}
 105
 106static ssize_t bcm43xx_attr_sprom_show(struct device *dev,
 107                                       struct device_attribute *attr,
 108                                       char *buf)
 109{
 110        struct bcm43xx_private *bcm = dev_to_bcm(dev);
 111        u16 *sprom;
 112        unsigned long flags;
 113        int err;
 114
 115        if (!capable(CAP_NET_ADMIN))
 116                return -EPERM;
 117
 118        assert(BCM43xx_SPROM_SIZE * sizeof(u16) <= PAGE_SIZE);
 119        sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
 120                        GFP_KERNEL);
 121        if (!sprom)
 122                return -ENOMEM;
 123        mutex_lock(&bcm->mutex);
 124        spin_lock_irqsave(&bcm->irq_lock, flags);
 125        err = bcm43xx_sprom_read(bcm, sprom);
 126        if (!err)
 127                err = sprom2hex(sprom, buf, PAGE_SIZE);
 128        mmiowb();
 129        spin_unlock_irqrestore(&bcm->irq_lock, flags);
 130        mutex_unlock(&bcm->mutex);
 131        kfree(sprom);
 132
 133        return err;
 134}
 135
 136static ssize_t bcm43xx_attr_sprom_store(struct device *dev,
 137                                        struct device_attribute *attr,
 138                                        const char *buf, size_t count)
 139{
 140        struct bcm43xx_private *bcm = dev_to_bcm(dev);
 141        u16 *sprom;
 142        unsigned long flags;
 143        int err;
 144
 145        if (!capable(CAP_NET_ADMIN))
 146                return -EPERM;
 147
 148        sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
 149                        GFP_KERNEL);
 150        if (!sprom)
 151                return -ENOMEM;
 152        err = hex2sprom(sprom, buf, count);
 153        if (err)
 154                goto out_kfree;
 155        mutex_lock(&bcm->mutex);
 156        spin_lock_irqsave(&bcm->irq_lock, flags);
 157        spin_lock(&bcm->leds_lock);
 158        err = bcm43xx_sprom_write(bcm, sprom);
 159        mmiowb();
 160        spin_unlock(&bcm->leds_lock);
 161        spin_unlock_irqrestore(&bcm->irq_lock, flags);
 162        mutex_unlock(&bcm->mutex);
 163out_kfree:
 164        kfree(sprom);
 165
 166        return err ? err : count;
 167
 168}
 169
 170static DEVICE_ATTR(sprom, 0600,
 171                   bcm43xx_attr_sprom_show,
 172                   bcm43xx_attr_sprom_store);
 173
 174static ssize_t bcm43xx_attr_interfmode_show(struct device *dev,
 175                                            struct device_attribute *attr,
 176                                            char *buf)
 177{
 178        struct bcm43xx_private *bcm = dev_to_bcm(dev);
 179        ssize_t count = 0;
 180
 181        if (!capable(CAP_NET_ADMIN))
 182                return -EPERM;
 183
 184        mutex_lock(&bcm->mutex);
 185
 186        switch (bcm43xx_current_radio(bcm)->interfmode) {
 187        case BCM43xx_RADIO_INTERFMODE_NONE:
 188                count = snprintf(buf, PAGE_SIZE, "0 (No Interference Mitigation)\n");
 189                break;
 190        case BCM43xx_RADIO_INTERFMODE_NONWLAN:
 191                count = snprintf(buf, PAGE_SIZE, "1 (Non-WLAN Interference Mitigation)\n");
 192                break;
 193        case BCM43xx_RADIO_INTERFMODE_MANUALWLAN:
 194                count = snprintf(buf, PAGE_SIZE, "2 (WLAN Interference Mitigation)\n");
 195                break;
 196        default:
 197                assert(0);
 198        }
 199
 200        mutex_unlock(&bcm->mutex);
 201
 202        return count;
 203
 204}
 205
 206static ssize_t bcm43xx_attr_interfmode_store(struct device *dev,
 207                                             struct device_attribute *attr,
 208                                             const char *buf, size_t count)
 209{
 210        struct bcm43xx_private *bcm = dev_to_bcm(dev);
 211        unsigned long flags;
 212        int err;
 213        int mode;
 214
 215        if (!capable(CAP_NET_ADMIN))
 216                return -EPERM;
 217
 218        mode = get_integer(buf, count);
 219        switch (mode) {
 220        case 0:
 221                mode = BCM43xx_RADIO_INTERFMODE_NONE;
 222                break;
 223        case 1:
 224                mode = BCM43xx_RADIO_INTERFMODE_NONWLAN;
 225                break;
 226        case 2:
 227                mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN;
 228                break;
 229        case 3:
 230                mode = BCM43xx_RADIO_INTERFMODE_AUTOWLAN;
 231                break;
 232        default:
 233                return -EINVAL;
 234        }
 235
 236        mutex_lock(&bcm->mutex);
 237        spin_lock_irqsave(&bcm->irq_lock, flags);
 238
 239        err = bcm43xx_radio_set_interference_mitigation(bcm, mode);
 240        if (err) {
 241                printk(KERN_ERR PFX "Interference Mitigation not "
 242                                    "supported by device\n");
 243        }
 244        mmiowb();
 245        spin_unlock_irqrestore(&bcm->irq_lock, flags);
 246        mutex_unlock(&bcm->mutex);
 247
 248        return err ? err : count;
 249}
 250
 251static DEVICE_ATTR(interference, 0644,
 252                   bcm43xx_attr_interfmode_show,
 253                   bcm43xx_attr_interfmode_store);
 254
 255static ssize_t bcm43xx_attr_preamble_show(struct device *dev,
 256                                          struct device_attribute *attr,
 257                                          char *buf)
 258{
 259        struct bcm43xx_private *bcm = dev_to_bcm(dev);
 260        ssize_t count;
 261
 262        if (!capable(CAP_NET_ADMIN))
 263                return -EPERM;
 264
 265        mutex_lock(&bcm->mutex);
 266
 267        if (bcm->short_preamble)
 268                count = snprintf(buf, PAGE_SIZE, "1 (Short Preamble enabled)\n");
 269        else
 270                count = snprintf(buf, PAGE_SIZE, "0 (Short Preamble disabled)\n");
 271
 272        mutex_unlock(&bcm->mutex);
 273
 274        return count;
 275}
 276
 277static ssize_t bcm43xx_attr_preamble_store(struct device *dev,
 278                                           struct device_attribute *attr,
 279                                           const char *buf, size_t count)
 280{
 281        struct bcm43xx_private *bcm = dev_to_bcm(dev);
 282        unsigned long flags;
 283        int value;
 284
 285        if (!capable(CAP_NET_ADMIN))
 286                return -EPERM;
 287
 288        value = get_boolean(buf, count);
 289        if (value < 0)
 290                return value;
 291        mutex_lock(&bcm->mutex);
 292        spin_lock_irqsave(&bcm->irq_lock, flags);
 293
 294        bcm->short_preamble = !!value;
 295
 296        spin_unlock_irqrestore(&bcm->irq_lock, flags);
 297        mutex_unlock(&bcm->mutex);
 298
 299        return count;
 300}
 301
 302static DEVICE_ATTR(shortpreamble, 0644,
 303                   bcm43xx_attr_preamble_show,
 304                   bcm43xx_attr_preamble_store);
 305
 306static ssize_t bcm43xx_attr_phymode_store(struct device *dev,
 307                                          struct device_attribute *attr,
 308                                          const char *buf, size_t count)
 309{
 310        struct bcm43xx_private *bcm = dev_to_bcm(dev);
 311        int phytype;
 312        int err = -EINVAL;
 313
 314        if (count < 1)
 315                goto out;
 316        switch (buf[0]) {
 317        case 'a':  case 'A':
 318                phytype = BCM43xx_PHYTYPE_A;
 319                break;
 320        case 'b':  case 'B':
 321                phytype = BCM43xx_PHYTYPE_B;
 322                break;
 323        case 'g':  case 'G':
 324                phytype = BCM43xx_PHYTYPE_G;
 325                break;
 326        default:
 327                goto out;
 328        }
 329
 330        bcm43xx_cancel_work(bcm);
 331        mutex_lock(&(bcm)->mutex);
 332        err = bcm43xx_select_wireless_core(bcm, phytype);
 333        if (!err)
 334                bcm43xx_periodic_tasks_setup(bcm);
 335        mutex_unlock(&(bcm)->mutex);
 336        if (err == -ESRCH)
 337                err = -ENODEV;
 338
 339out:
 340        return err ? err : count;
 341}
 342
 343static ssize_t bcm43xx_attr_phymode_show(struct device *dev,
 344                                         struct device_attribute *attr,
 345                                         char *buf)
 346{
 347        struct bcm43xx_private *bcm = dev_to_bcm(dev);
 348        ssize_t count = 0;
 349
 350        mutex_lock(&(bcm)->mutex);
 351        switch (bcm43xx_current_phy(bcm)->type) {
 352        case BCM43xx_PHYTYPE_A:
 353                snprintf(buf, PAGE_SIZE, "A");
 354                break;
 355        case BCM43xx_PHYTYPE_B:
 356                snprintf(buf, PAGE_SIZE, "B");
 357                break;
 358        case BCM43xx_PHYTYPE_G:
 359                snprintf(buf, PAGE_SIZE, "G");
 360                break;
 361        default:
 362                assert(0);
 363        }
 364        mutex_unlock(&(bcm)->mutex);
 365
 366        return count;
 367}
 368
 369static DEVICE_ATTR(phymode, 0644,
 370                   bcm43xx_attr_phymode_show,
 371                   bcm43xx_attr_phymode_store);
 372
 373static ssize_t bcm43xx_attr_microcode_show(struct device *dev,
 374                                           struct device_attribute *attr,
 375                                           char *buf)
 376{
 377        unsigned long flags;
 378        struct bcm43xx_private *bcm = dev_to_bcm(dev);
 379        ssize_t count = 0;
 380        u16 status;
 381
 382        if (!capable(CAP_NET_ADMIN))
 383                return -EPERM;
 384
 385        mutex_lock(&(bcm)->mutex);
 386        spin_lock_irqsave(&bcm->irq_lock, flags);
 387        status = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED,
 388                                    BCM43xx_UCODE_STATUS);
 389
 390        spin_unlock_irqrestore(&bcm->irq_lock, flags);
 391        mutex_unlock(&(bcm)->mutex);
 392        switch (status) {
 393        case 0x0000:
 394                count = snprintf(buf, PAGE_SIZE, "0x%.4x (invalid)\n",
 395                                 status);
 396                break;
 397        case 0x0001:
 398                count = snprintf(buf, PAGE_SIZE, "0x%.4x (init)\n",
 399                                 status);
 400                break;
 401        case 0x0002:
 402                count = snprintf(buf, PAGE_SIZE, "0x%.4x (active)\n",
 403                                 status);
 404                break;
 405        case 0x0003:
 406                count = snprintf(buf, PAGE_SIZE, "0x%.4x (suspended)\n",
 407                                 status);
 408                break;
 409        case 0x0004:
 410                count = snprintf(buf, PAGE_SIZE, "0x%.4x (asleep)\n",
 411                                 status);
 412                break;
 413        default:
 414                count = snprintf(buf, PAGE_SIZE, "0x%.4x (unknown)\n",
 415                                 status);
 416                break;
 417        }
 418
 419        return count;
 420}
 421
 422static DEVICE_ATTR(microcodestatus, 0444,
 423                   bcm43xx_attr_microcode_show,
 424                   NULL);
 425
 426int bcm43xx_sysfs_register(struct bcm43xx_private *bcm)
 427{
 428        struct device *dev = &bcm->pci_dev->dev;
 429        int err;
 430
 431        assert(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED);
 432
 433        err = device_create_file(dev, &dev_attr_sprom);
 434        if (err)
 435                goto out;
 436        err = device_create_file(dev, &dev_attr_interference);
 437        if (err)
 438                goto err_remove_sprom;
 439        err = device_create_file(dev, &dev_attr_shortpreamble);
 440        if (err)
 441                goto err_remove_interfmode;
 442        err = device_create_file(dev, &dev_attr_phymode);
 443        if (err)
 444                goto err_remove_shortpreamble;
 445        err = device_create_file(dev, &dev_attr_microcodestatus);
 446        if (err)
 447                goto err_remove_phymode;
 448
 449out:
 450        return err;
 451err_remove_phymode:
 452        device_remove_file(dev, &dev_attr_phymode);
 453err_remove_shortpreamble:
 454        device_remove_file(dev, &dev_attr_shortpreamble);
 455err_remove_interfmode:
 456        device_remove_file(dev, &dev_attr_interference);
 457err_remove_sprom:
 458        device_remove_file(dev, &dev_attr_sprom);
 459        goto out;
 460}
 461
 462void bcm43xx_sysfs_unregister(struct bcm43xx_private *bcm)
 463{
 464        struct device *dev = &bcm->pci_dev->dev;
 465
 466        device_remove_file(dev, &dev_attr_microcodestatus);
 467        device_remove_file(dev, &dev_attr_phymode);
 468        device_remove_file(dev, &dev_attr_shortpreamble);
 469        device_remove_file(dev, &dev_attr_interference);
 470        device_remove_file(dev, &dev_attr_sprom);
 471}
 472