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