linux/drivers/net/wireless/broadcom/b43legacy/leds.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3
   4  Broadcom B43 wireless driver
   5  LED control
   6
   7  Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
   8  Copyright (c) 2005 Stefano Brivio <stefano.brivio@polimi.it>
   9  Copyright (c) 2005-2007 Michael Buesch <m@bues.ch>
  10  Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org>
  11  Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
  12
  13
  14*/
  15
  16#include "b43legacy.h"
  17#include "leds.h"
  18#include "rfkill.h"
  19
  20
  21static void b43legacy_led_turn_on(struct b43legacy_wldev *dev, u8 led_index,
  22                            bool activelow)
  23{
  24        struct b43legacy_wl *wl = dev->wl;
  25        unsigned long flags;
  26        u16 ctl;
  27
  28        spin_lock_irqsave(&wl->leds_lock, flags);
  29        ctl = b43legacy_read16(dev, B43legacy_MMIO_GPIO_CONTROL);
  30        if (activelow)
  31                ctl &= ~(1 << led_index);
  32        else
  33                ctl |= (1 << led_index);
  34        b43legacy_write16(dev, B43legacy_MMIO_GPIO_CONTROL, ctl);
  35        spin_unlock_irqrestore(&wl->leds_lock, flags);
  36}
  37
  38static void b43legacy_led_turn_off(struct b43legacy_wldev *dev, u8 led_index,
  39                             bool activelow)
  40{
  41        struct b43legacy_wl *wl = dev->wl;
  42        unsigned long flags;
  43        u16 ctl;
  44
  45        spin_lock_irqsave(&wl->leds_lock, flags);
  46        ctl = b43legacy_read16(dev, B43legacy_MMIO_GPIO_CONTROL);
  47        if (activelow)
  48                ctl |= (1 << led_index);
  49        else
  50                ctl &= ~(1 << led_index);
  51        b43legacy_write16(dev, B43legacy_MMIO_GPIO_CONTROL, ctl);
  52        spin_unlock_irqrestore(&wl->leds_lock, flags);
  53}
  54
  55/* Callback from the LED subsystem. */
  56static void b43legacy_led_brightness_set(struct led_classdev *led_dev,
  57                                   enum led_brightness brightness)
  58{
  59        struct b43legacy_led *led = container_of(led_dev, struct b43legacy_led,
  60                                    led_dev);
  61        struct b43legacy_wldev *dev = led->dev;
  62        bool radio_enabled;
  63
  64        /* Checking the radio-enabled status here is slightly racy,
  65         * but we want to avoid the locking overhead and we don't care
  66         * whether the LED has the wrong state for a second. */
  67        radio_enabled = (dev->phy.radio_on && dev->radio_hw_enable);
  68
  69        if (brightness == LED_OFF || !radio_enabled)
  70                b43legacy_led_turn_off(dev, led->index, led->activelow);
  71        else
  72                b43legacy_led_turn_on(dev, led->index, led->activelow);
  73}
  74
  75static int b43legacy_register_led(struct b43legacy_wldev *dev,
  76                                  struct b43legacy_led *led,
  77                                  const char *name,
  78                                  const char *default_trigger,
  79                                  u8 led_index, bool activelow)
  80{
  81        int err;
  82
  83        b43legacy_led_turn_off(dev, led_index, activelow);
  84        if (led->dev)
  85                return -EEXIST;
  86        if (!default_trigger)
  87                return -EINVAL;
  88        led->dev = dev;
  89        led->index = led_index;
  90        led->activelow = activelow;
  91        strlcpy(led->name, name, sizeof(led->name));
  92
  93        led->led_dev.name = led->name;
  94        led->led_dev.default_trigger = default_trigger;
  95        led->led_dev.brightness_set = b43legacy_led_brightness_set;
  96
  97        err = led_classdev_register(dev->dev->dev, &led->led_dev);
  98        if (err) {
  99                b43legacywarn(dev->wl, "LEDs: Failed to register %s\n", name);
 100                led->dev = NULL;
 101                return err;
 102        }
 103        return 0;
 104}
 105
 106static void b43legacy_unregister_led(struct b43legacy_led *led)
 107{
 108        if (!led->dev)
 109                return;
 110        led_classdev_unregister(&led->led_dev);
 111        b43legacy_led_turn_off(led->dev, led->index, led->activelow);
 112        led->dev = NULL;
 113}
 114
 115static void b43legacy_map_led(struct b43legacy_wldev *dev,
 116                        u8 led_index,
 117                        enum b43legacy_led_behaviour behaviour,
 118                        bool activelow)
 119{
 120        struct ieee80211_hw *hw = dev->wl->hw;
 121        char name[B43legacy_LED_MAX_NAME_LEN + 1];
 122
 123        /* Map the b43 specific LED behaviour value to the
 124         * generic LED triggers. */
 125        switch (behaviour) {
 126        case B43legacy_LED_INACTIVE:
 127                break;
 128        case B43legacy_LED_OFF:
 129                b43legacy_led_turn_off(dev, led_index, activelow);
 130                break;
 131        case B43legacy_LED_ON:
 132                b43legacy_led_turn_on(dev, led_index, activelow);
 133                break;
 134        case B43legacy_LED_ACTIVITY:
 135        case B43legacy_LED_TRANSFER:
 136        case B43legacy_LED_APTRANSFER:
 137                snprintf(name, sizeof(name),
 138                         "b43legacy-%s::tx", wiphy_name(hw->wiphy));
 139                b43legacy_register_led(dev, &dev->led_tx, name,
 140                                 ieee80211_get_tx_led_name(hw),
 141                                 led_index, activelow);
 142                snprintf(name, sizeof(name),
 143                         "b43legacy-%s::rx", wiphy_name(hw->wiphy));
 144                b43legacy_register_led(dev, &dev->led_rx, name,
 145                                 ieee80211_get_rx_led_name(hw),
 146                                 led_index, activelow);
 147                break;
 148        case B43legacy_LED_RADIO_ALL:
 149        case B43legacy_LED_RADIO_A:
 150        case B43legacy_LED_RADIO_B:
 151        case B43legacy_LED_MODE_BG:
 152                snprintf(name, sizeof(name),
 153                         "b43legacy-%s::radio", wiphy_name(hw->wiphy));
 154                b43legacy_register_led(dev, &dev->led_radio, name,
 155                                 ieee80211_get_radio_led_name(hw),
 156                                 led_index, activelow);
 157                /* Sync the RF-kill LED state with radio and switch states. */
 158                if (dev->phy.radio_on && b43legacy_is_hw_radio_enabled(dev))
 159                        b43legacy_led_turn_on(dev, led_index, activelow);
 160                break;
 161        case B43legacy_LED_WEIRD:
 162        case B43legacy_LED_ASSOC:
 163                snprintf(name, sizeof(name),
 164                         "b43legacy-%s::assoc", wiphy_name(hw->wiphy));
 165                b43legacy_register_led(dev, &dev->led_assoc, name,
 166                                 ieee80211_get_assoc_led_name(hw),
 167                                 led_index, activelow);
 168                break;
 169        default:
 170                b43legacywarn(dev->wl, "LEDs: Unknown behaviour 0x%02X\n",
 171                        behaviour);
 172                break;
 173        }
 174}
 175
 176void b43legacy_leds_init(struct b43legacy_wldev *dev)
 177{
 178        struct ssb_bus *bus = dev->dev->bus;
 179        u8 sprom[4];
 180        int i;
 181        enum b43legacy_led_behaviour behaviour;
 182        bool activelow;
 183
 184        sprom[0] = bus->sprom.gpio0;
 185        sprom[1] = bus->sprom.gpio1;
 186        sprom[2] = bus->sprom.gpio2;
 187        sprom[3] = bus->sprom.gpio3;
 188
 189        for (i = 0; i < 4; i++) {
 190                if (sprom[i] == 0xFF) {
 191                        /* There is no LED information in the SPROM
 192                         * for this LED. Hardcode it here. */
 193                        activelow = false;
 194                        switch (i) {
 195                        case 0:
 196                                behaviour = B43legacy_LED_ACTIVITY;
 197                                activelow = true;
 198                                if (bus->boardinfo.vendor == PCI_VENDOR_ID_COMPAQ)
 199                                        behaviour = B43legacy_LED_RADIO_ALL;
 200                                break;
 201                        case 1:
 202                                behaviour = B43legacy_LED_RADIO_B;
 203                                if (bus->boardinfo.vendor == PCI_VENDOR_ID_ASUSTEK)
 204                                        behaviour = B43legacy_LED_ASSOC;
 205                                break;
 206                        case 2:
 207                                behaviour = B43legacy_LED_RADIO_A;
 208                                break;
 209                        case 3:
 210                                behaviour = B43legacy_LED_OFF;
 211                                break;
 212                        default:
 213                                B43legacy_WARN_ON(1);
 214                                return;
 215                        }
 216                } else {
 217                        behaviour = sprom[i] & B43legacy_LED_BEHAVIOUR;
 218                        activelow = !!(sprom[i] & B43legacy_LED_ACTIVELOW);
 219                }
 220                b43legacy_map_led(dev, i, behaviour, activelow);
 221        }
 222}
 223
 224void b43legacy_leds_exit(struct b43legacy_wldev *dev)
 225{
 226        b43legacy_unregister_led(&dev->led_tx);
 227        b43legacy_unregister_led(&dev->led_rx);
 228        b43legacy_unregister_led(&dev->led_assoc);
 229        b43legacy_unregister_led(&dev->led_radio);
 230}
 231