linux/drivers/net/wireless/broadcom/b43/leds.c
<<
>>
Prefs
   1/*
   2
   3  Broadcom B43 wireless driver
   4  LED control
   5
   6  Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
   7  Copyright (c) 2005 Stefano Brivio <stefano.brivio@polimi.it>
   8  Copyright (c) 2005-2007 Michael Buesch <m@bues.ch>
   9  Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org>
  10  Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
  11
  12  This program is free software; you can redistribute it and/or modify
  13  it under the terms of the GNU General Public License as published by
  14  the Free Software Foundation; either version 2 of the License, or
  15  (at your option) any later version.
  16
  17  This program is distributed in the hope that it will be useful,
  18  but WITHOUT ANY WARRANTY; without even the implied warranty of
  19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20  GNU General Public License for more details.
  21
  22  You should have received a copy of the GNU General Public License
  23  along with this program; see the file COPYING.  If not, write to
  24  the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
  25  Boston, MA 02110-1301, USA.
  26
  27*/
  28
  29#include "b43.h"
  30#include "leds.h"
  31#include "rfkill.h"
  32
  33
  34static void b43_led_turn_on(struct b43_wldev *dev, u8 led_index,
  35                            bool activelow)
  36{
  37        u16 ctl;
  38
  39        ctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL);
  40        if (activelow)
  41                ctl &= ~(1 << led_index);
  42        else
  43                ctl |= (1 << led_index);
  44        b43_write16(dev, B43_MMIO_GPIO_CONTROL, ctl);
  45}
  46
  47static void b43_led_turn_off(struct b43_wldev *dev, u8 led_index,
  48                             bool activelow)
  49{
  50        u16 ctl;
  51
  52        ctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL);
  53        if (activelow)
  54                ctl |= (1 << led_index);
  55        else
  56                ctl &= ~(1 << led_index);
  57        b43_write16(dev, B43_MMIO_GPIO_CONTROL, ctl);
  58}
  59
  60static void b43_led_update(struct b43_wldev *dev,
  61                           struct b43_led *led)
  62{
  63        bool radio_enabled;
  64        bool turn_on;
  65
  66        if (!led->wl)
  67                return;
  68
  69        radio_enabled = (dev->phy.radio_on && dev->radio_hw_enable);
  70
  71        /* The led->state read is racy, but we don't care. In case we raced
  72         * with the brightness_set handler, we will be called again soon
  73         * to fixup our state. */
  74        if (radio_enabled)
  75                turn_on = atomic_read(&led->state) != LED_OFF;
  76        else
  77                turn_on = false;
  78        if (turn_on == led->hw_state)
  79                return;
  80        led->hw_state = turn_on;
  81
  82        if (turn_on)
  83                b43_led_turn_on(dev, led->index, led->activelow);
  84        else
  85                b43_led_turn_off(dev, led->index, led->activelow);
  86}
  87
  88static void b43_leds_work(struct work_struct *work)
  89{
  90        struct b43_leds *leds = container_of(work, struct b43_leds, work);
  91        struct b43_wl *wl = container_of(leds, struct b43_wl, leds);
  92        struct b43_wldev *dev;
  93
  94        mutex_lock(&wl->mutex);
  95        dev = wl->current_dev;
  96        if (unlikely(!dev || b43_status(dev) < B43_STAT_STARTED))
  97                goto out_unlock;
  98
  99        b43_led_update(dev, &wl->leds.led_tx);
 100        b43_led_update(dev, &wl->leds.led_rx);
 101        b43_led_update(dev, &wl->leds.led_radio);
 102        b43_led_update(dev, &wl->leds.led_assoc);
 103
 104out_unlock:
 105        mutex_unlock(&wl->mutex);
 106}
 107
 108/* Callback from the LED subsystem. */
 109static void b43_led_brightness_set(struct led_classdev *led_dev,
 110                                   enum led_brightness brightness)
 111{
 112        struct b43_led *led = container_of(led_dev, struct b43_led, led_dev);
 113        struct b43_wl *wl = led->wl;
 114
 115        if (likely(!wl->leds.stop)) {
 116                atomic_set(&led->state, brightness);
 117                ieee80211_queue_work(wl->hw, &wl->leds.work);
 118        }
 119}
 120
 121static int b43_register_led(struct b43_wldev *dev, struct b43_led *led,
 122                            const char *name, const char *default_trigger,
 123                            u8 led_index, bool activelow)
 124{
 125        int err;
 126
 127        if (led->wl)
 128                return -EEXIST;
 129        if (!default_trigger)
 130                return -EINVAL;
 131        led->wl = dev->wl;
 132        led->index = led_index;
 133        led->activelow = activelow;
 134        strncpy(led->name, name, sizeof(led->name));
 135        atomic_set(&led->state, 0);
 136
 137        led->led_dev.name = led->name;
 138        led->led_dev.default_trigger = default_trigger;
 139        led->led_dev.brightness_set = b43_led_brightness_set;
 140
 141        err = led_classdev_register(dev->dev->dev, &led->led_dev);
 142        if (err) {
 143                b43warn(dev->wl, "LEDs: Failed to register %s\n", name);
 144                led->wl = NULL;
 145                return err;
 146        }
 147
 148        return 0;
 149}
 150
 151static void b43_unregister_led(struct b43_led *led)
 152{
 153        if (!led->wl)
 154                return;
 155        led_classdev_unregister(&led->led_dev);
 156        led->wl = NULL;
 157}
 158
 159static void b43_map_led(struct b43_wldev *dev,
 160                        u8 led_index,
 161                        enum b43_led_behaviour behaviour,
 162                        bool activelow)
 163{
 164        struct ieee80211_hw *hw = dev->wl->hw;
 165        char name[B43_LED_MAX_NAME_LEN + 1];
 166
 167        /* Map the b43 specific LED behaviour value to the
 168         * generic LED triggers. */
 169        switch (behaviour) {
 170        case B43_LED_INACTIVE:
 171        case B43_LED_OFF:
 172        case B43_LED_ON:
 173                break;
 174        case B43_LED_ACTIVITY:
 175        case B43_LED_TRANSFER:
 176        case B43_LED_APTRANSFER:
 177                snprintf(name, sizeof(name),
 178                         "b43-%s::tx", wiphy_name(hw->wiphy));
 179                b43_register_led(dev, &dev->wl->leds.led_tx, name,
 180                                 ieee80211_get_tx_led_name(hw),
 181                                 led_index, activelow);
 182                snprintf(name, sizeof(name),
 183                         "b43-%s::rx", wiphy_name(hw->wiphy));
 184                b43_register_led(dev, &dev->wl->leds.led_rx, name,
 185                                 ieee80211_get_rx_led_name(hw),
 186                                 led_index, activelow);
 187                break;
 188        case B43_LED_RADIO_ALL:
 189        case B43_LED_RADIO_A:
 190        case B43_LED_RADIO_B:
 191        case B43_LED_MODE_BG:
 192                snprintf(name, sizeof(name),
 193                         "b43-%s::radio", wiphy_name(hw->wiphy));
 194                b43_register_led(dev, &dev->wl->leds.led_radio, name,
 195                                 ieee80211_get_radio_led_name(hw),
 196                                 led_index, activelow);
 197                break;
 198        case B43_LED_WEIRD:
 199        case B43_LED_ASSOC:
 200                snprintf(name, sizeof(name),
 201                         "b43-%s::assoc", wiphy_name(hw->wiphy));
 202                b43_register_led(dev, &dev->wl->leds.led_assoc, name,
 203                                 ieee80211_get_assoc_led_name(hw),
 204                                 led_index, activelow);
 205                break;
 206        default:
 207                b43warn(dev->wl, "LEDs: Unknown behaviour 0x%02X\n",
 208                        behaviour);
 209                break;
 210        }
 211}
 212
 213static void b43_led_get_sprominfo(struct b43_wldev *dev,
 214                                  unsigned int led_index,
 215                                  enum b43_led_behaviour *behaviour,
 216                                  bool *activelow)
 217{
 218        u8 sprom[4];
 219
 220        sprom[0] = dev->dev->bus_sprom->gpio0;
 221        sprom[1] = dev->dev->bus_sprom->gpio1;
 222        sprom[2] = dev->dev->bus_sprom->gpio2;
 223        sprom[3] = dev->dev->bus_sprom->gpio3;
 224
 225        if ((sprom[0] & sprom[1] & sprom[2] & sprom[3]) == 0xff) {
 226                /* There is no LED information in the SPROM
 227                 * for this LED. Hardcode it here. */
 228                *activelow = false;
 229                switch (led_index) {
 230                case 0:
 231                        *behaviour = B43_LED_ACTIVITY;
 232                        *activelow = true;
 233                        if (dev->dev->board_vendor == PCI_VENDOR_ID_COMPAQ)
 234                                *behaviour = B43_LED_RADIO_ALL;
 235                        break;
 236                case 1:
 237                        *behaviour = B43_LED_RADIO_B;
 238                        if (dev->dev->board_vendor == PCI_VENDOR_ID_ASUSTEK)
 239                                *behaviour = B43_LED_ASSOC;
 240                        break;
 241                case 2:
 242                        *behaviour = B43_LED_RADIO_A;
 243                        break;
 244                case 3:
 245                        *behaviour = B43_LED_OFF;
 246                        break;
 247                default:
 248                        *behaviour = B43_LED_OFF;
 249                        B43_WARN_ON(1);
 250                        return;
 251                }
 252        } else {
 253                /* keep LED disabled if no mapping is defined */
 254                if (sprom[led_index] == 0xff)
 255                        *behaviour = B43_LED_OFF;
 256                else
 257                        *behaviour = sprom[led_index] & B43_LED_BEHAVIOUR;
 258                *activelow = !!(sprom[led_index] & B43_LED_ACTIVELOW);
 259        }
 260}
 261
 262void b43_leds_init(struct b43_wldev *dev)
 263{
 264        struct b43_led *led;
 265        unsigned int i;
 266        enum b43_led_behaviour behaviour;
 267        bool activelow;
 268
 269        /* Sync the RF-kill LED state (if we have one) with radio and switch states. */
 270        led = &dev->wl->leds.led_radio;
 271        if (led->wl) {
 272                if (dev->phy.radio_on && b43_is_hw_radio_enabled(dev)) {
 273                        b43_led_turn_on(dev, led->index, led->activelow);
 274                        led->hw_state = true;
 275                        atomic_set(&led->state, 1);
 276                } else {
 277                        b43_led_turn_off(dev, led->index, led->activelow);
 278                        led->hw_state = false;
 279                        atomic_set(&led->state, 0);
 280                }
 281        }
 282
 283        /* Initialize TX/RX/ASSOC leds */
 284        led = &dev->wl->leds.led_tx;
 285        if (led->wl) {
 286                b43_led_turn_off(dev, led->index, led->activelow);
 287                led->hw_state = false;
 288                atomic_set(&led->state, 0);
 289        }
 290        led = &dev->wl->leds.led_rx;
 291        if (led->wl) {
 292                b43_led_turn_off(dev, led->index, led->activelow);
 293                led->hw_state = false;
 294                atomic_set(&led->state, 0);
 295        }
 296        led = &dev->wl->leds.led_assoc;
 297        if (led->wl) {
 298                b43_led_turn_off(dev, led->index, led->activelow);
 299                led->hw_state = false;
 300                atomic_set(&led->state, 0);
 301        }
 302
 303        /* Initialize other LED states. */
 304        for (i = 0; i < B43_MAX_NR_LEDS; i++) {
 305                b43_led_get_sprominfo(dev, i, &behaviour, &activelow);
 306                switch (behaviour) {
 307                case B43_LED_OFF:
 308                        b43_led_turn_off(dev, i, activelow);
 309                        break;
 310                case B43_LED_ON:
 311                        b43_led_turn_on(dev, i, activelow);
 312                        break;
 313                default:
 314                        /* Leave others as-is. */
 315                        break;
 316                }
 317        }
 318
 319        dev->wl->leds.stop = 0;
 320}
 321
 322void b43_leds_exit(struct b43_wldev *dev)
 323{
 324        struct b43_leds *leds = &dev->wl->leds;
 325
 326        b43_led_turn_off(dev, leds->led_tx.index, leds->led_tx.activelow);
 327        b43_led_turn_off(dev, leds->led_rx.index, leds->led_rx.activelow);
 328        b43_led_turn_off(dev, leds->led_assoc.index, leds->led_assoc.activelow);
 329        b43_led_turn_off(dev, leds->led_radio.index, leds->led_radio.activelow);
 330}
 331
 332void b43_leds_stop(struct b43_wldev *dev)
 333{
 334        struct b43_leds *leds = &dev->wl->leds;
 335
 336        leds->stop = 1;
 337        cancel_work_sync(&leds->work);
 338}
 339
 340void b43_leds_register(struct b43_wldev *dev)
 341{
 342        unsigned int i;
 343        enum b43_led_behaviour behaviour;
 344        bool activelow;
 345
 346        INIT_WORK(&dev->wl->leds.work, b43_leds_work);
 347
 348        /* Register the LEDs to the LED subsystem. */
 349        for (i = 0; i < B43_MAX_NR_LEDS; i++) {
 350                b43_led_get_sprominfo(dev, i, &behaviour, &activelow);
 351                b43_map_led(dev, i, behaviour, activelow);
 352        }
 353}
 354
 355void b43_leds_unregister(struct b43_wl *wl)
 356{
 357        struct b43_leds *leds = &wl->leds;
 358
 359        b43_unregister_led(&leds->led_tx);
 360        b43_unregister_led(&leds->led_rx);
 361        b43_unregister_led(&leds->led_assoc);
 362        b43_unregister_led(&leds->led_radio);
 363}
 364