leds.c 6.4 KB
Newer Older
1 2 3
/*

  Broadcom B43 wireless driver
M
Michael Buesch 已提交
4
  LED control
5 6

  Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
7
  Copyright (c) 2005 Stefano Brivio <stefano.brivio@polimi.it>
M
Michael Buesch 已提交
8 9 10
  Copyright (c) 2005-2007 Michael Buesch <mb@bu3sch.de>
  Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org>
  Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

  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 "b43.h"
#include "leds.h"


M
Michael Buesch 已提交
33 34
static void b43_led_turn_on(struct b43_wldev *dev, u8 led_index,
			    bool activelow)
35
{
M
Michael Buesch 已提交
36
	struct b43_wl *wl = dev->wl;
37
	unsigned long flags;
M
Michael Buesch 已提交
38
	u16 ctl;
39

M
Michael Buesch 已提交
40 41 42 43 44 45 46 47
	spin_lock_irqsave(&wl->leds_lock, flags);
	ctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL);
	if (activelow)
		ctl &= ~(1 << led_index);
	else
		ctl |= (1 << led_index);
	b43_write16(dev, B43_MMIO_GPIO_CONTROL, ctl);
	spin_unlock_irqrestore(&wl->leds_lock, flags);
48 49
}

M
Michael Buesch 已提交
50 51
static void b43_led_turn_off(struct b43_wldev *dev, u8 led_index,
			     bool activelow)
52
{
M
Michael Buesch 已提交
53 54 55 56 57 58 59 60 61 62 63 64
	struct b43_wl *wl = dev->wl;
	unsigned long flags;
	u16 ctl;

	spin_lock_irqsave(&wl->leds_lock, flags);
	ctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL);
	if (activelow)
		ctl |= (1 << led_index);
	else
		ctl &= ~(1 << led_index);
	b43_write16(dev, B43_MMIO_GPIO_CONTROL, ctl);
	spin_unlock_irqrestore(&wl->leds_lock, flags);
65 66
}

M
Michael Buesch 已提交
67 68 69
/* Callback from the LED subsystem. */
static void b43_led_brightness_set(struct led_classdev *led_dev,
				   enum led_brightness brightness)
70
{
M
Michael Buesch 已提交
71
	struct b43_led *led = container_of(led_dev, struct b43_led, led_dev);
72
	struct b43_wldev *dev = led->dev;
M
Michael Buesch 已提交
73
	bool radio_enabled;
74

75 76 77
	if (unlikely(b43_status(dev) < B43_STAT_INITIALIZED))
		return;

M
Michael Buesch 已提交
78 79 80 81
	/* Checking the radio-enabled status here is slightly racy,
	 * but we want to avoid the locking overhead and we don't care
	 * whether the LED has the wrong state for a second. */
	radio_enabled = (dev->phy.radio_on && dev->radio_hw_enable);
82

M
Michael Buesch 已提交
83 84
	if (brightness == LED_OFF || !radio_enabled)
		b43_led_turn_off(dev, led->index, led->activelow);
85
	else
M
Michael Buesch 已提交
86
		b43_led_turn_on(dev, led->index, led->activelow);
87 88
}

M
Michael Buesch 已提交
89
static int b43_register_led(struct b43_wldev *dev, struct b43_led *led,
J
Johannes Berg 已提交
90
			    const char *name, const char *default_trigger,
M
Michael Buesch 已提交
91
			    u8 led_index, bool activelow)
92
{
M
Michael Buesch 已提交
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
	int err;

	b43_led_turn_off(dev, led_index, activelow);
	if (led->dev)
		return -EEXIST;
	if (!default_trigger)
		return -EINVAL;
	led->dev = dev;
	led->index = led_index;
	led->activelow = activelow;
	strncpy(led->name, name, sizeof(led->name));

	led->led_dev.name = led->name;
	led->led_dev.default_trigger = default_trigger;
	led->led_dev.brightness_set = b43_led_brightness_set;

	err = led_classdev_register(dev->dev->dev, &led->led_dev);
	if (err) {
		b43warn(dev->wl, "LEDs: Failed to register %s\n", name);
		led->dev = NULL;
		return err;
	}
	return 0;
}

static void b43_unregister_led(struct b43_led *led)
{
	if (!led->dev)
		return;
122
	led_classdev_unregister(&led->led_dev);
M
Michael Buesch 已提交
123 124 125
	b43_led_turn_off(led->dev, led->index, led->activelow);
	led->dev = NULL;
}
126

M
Michael Buesch 已提交
127 128 129 130 131 132 133
static void b43_map_led(struct b43_wldev *dev,
			u8 led_index,
			enum b43_led_behaviour behaviour,
			bool activelow)
{
	struct ieee80211_hw *hw = dev->wl->hw;
	char name[B43_LED_MAX_NAME_LEN + 1];
134

M
Michael Buesch 已提交
135 136 137 138 139 140 141
	/* Map the b43 specific LED behaviour value to the
	 * generic LED triggers. */
	switch (behaviour) {
	case B43_LED_INACTIVE:
		break;
	case B43_LED_OFF:
		b43_led_turn_off(dev, led_index, activelow);
142
		break;
M
Michael Buesch 已提交
143 144
	case B43_LED_ON:
		b43_led_turn_on(dev, led_index, activelow);
145
		break;
M
Michael Buesch 已提交
146 147 148 149
	case B43_LED_ACTIVITY:
	case B43_LED_TRANSFER:
	case B43_LED_APTRANSFER:
		snprintf(name, sizeof(name),
150
			 "b43-%s::tx", wiphy_name(hw->wiphy));
M
Michael Buesch 已提交
151 152 153 154
		b43_register_led(dev, &dev->led_tx, name,
				 ieee80211_get_tx_led_name(hw),
				 led_index, activelow);
		snprintf(name, sizeof(name),
155
			 "b43-%s::rx", wiphy_name(hw->wiphy));
M
Michael Buesch 已提交
156 157 158
		b43_register_led(dev, &dev->led_rx, name,
				 ieee80211_get_rx_led_name(hw),
				 led_index, activelow);
159
		break;
M
Michael Buesch 已提交
160 161 162 163
	case B43_LED_RADIO_ALL:
	case B43_LED_RADIO_A:
	case B43_LED_RADIO_B:
	case B43_LED_MODE_BG:
M
Michael Buesch 已提交
164
		snprintf(name, sizeof(name),
165
			 "b43-%s::radio", wiphy_name(hw->wiphy));
M
Michael Buesch 已提交
166 167 168
		b43_register_led(dev, &dev->led_radio, name,
				 b43_rfkill_led_name(dev),
				 led_index, activelow);
L
Larry Finger 已提交
169 170 171
		/* Sync the RF-kill LED state with the switch state. */
		if (dev->radio_hw_enable)
			b43_led_turn_on(dev, led_index, activelow);
M
Michael Buesch 已提交
172
		break;
M
Michael Buesch 已提交
173 174 175
	case B43_LED_WEIRD:
	case B43_LED_ASSOC:
		snprintf(name, sizeof(name),
176
			 "b43-%s::assoc", wiphy_name(hw->wiphy));
M
Michael Buesch 已提交
177 178 179
		b43_register_led(dev, &dev->led_assoc, name,
				 ieee80211_get_assoc_led_name(hw),
				 led_index, activelow);
180 181
		break;
	default:
M
Michael Buesch 已提交
182 183 184
		b43warn(dev->wl, "LEDs: Unknown behaviour 0x%02X\n",
			behaviour);
		break;
185 186 187
	}
}

M
Michael Buesch 已提交
188
void b43_leds_init(struct b43_wldev *dev)
189
{
M
Michael Buesch 已提交
190
	struct ssb_bus *bus = dev->dev->bus;
191 192
	u8 sprom[4];
	int i;
M
Michael Buesch 已提交
193 194
	enum b43_led_behaviour behaviour;
	bool activelow;
195

196 197 198 199
	sprom[0] = bus->sprom.gpio0;
	sprom[1] = bus->sprom.gpio1;
	sprom[2] = bus->sprom.gpio2;
	sprom[3] = bus->sprom.gpio3;
200

M
Michael Buesch 已提交
201
	for (i = 0; i < 4; i++) {
202
		if (sprom[i] == 0xFF) {
M
Michael Buesch 已提交
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
			/* There is no LED information in the SPROM
			 * for this LED. Hardcode it here. */
			activelow = 0;
			switch (i) {
			case 0:
				behaviour = B43_LED_ACTIVITY;
				activelow = 1;
				if (bus->boardinfo.vendor == PCI_VENDOR_ID_COMPAQ)
					behaviour = B43_LED_RADIO_ALL;
				break;
			case 1:
				behaviour = B43_LED_RADIO_B;
				if (bus->boardinfo.vendor == PCI_VENDOR_ID_ASUSTEK)
					behaviour = B43_LED_ASSOC;
				break;
			case 2:
				behaviour = B43_LED_RADIO_A;
				break;
			case 3:
				behaviour = B43_LED_OFF;
				break;
			default:
				B43_WARN_ON(1);
				return;
			}
228
		} else {
M
Michael Buesch 已提交
229 230
			behaviour = sprom[i] & B43_LED_BEHAVIOUR;
			activelow = !!(sprom[i] & B43_LED_ACTIVELOW);
231
		}
M
Michael Buesch 已提交
232
		b43_map_led(dev, i, behaviour, activelow);
233 234 235 236 237
	}
}

void b43_leds_exit(struct b43_wldev *dev)
{
M
Michael Buesch 已提交
238 239 240
	b43_unregister_led(&dev->led_tx);
	b43_unregister_led(&dev->led_rx);
	b43_unregister_led(&dev->led_assoc);
L
Larry Finger 已提交
241
	b43_unregister_led(&dev->led_radio);
242
}