Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
leds.c
Go to the documentation of this file.
1 /*
2 
3  Broadcom B43 wireless driver
4  LED control
5 
6  Copyright (c) 2005 Martin Langer <[email protected]>,
7  Copyright (c) 2005 Stefano Brivio <[email protected]>
8  Copyright (c) 2005-2007 Michael Buesch <[email protected]>
9  Copyright (c) 2005 Danny van Dyk <[email protected]>
10  Copyright (c) 2005 Andreas Jaggi <[email protected]>
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 
34 static 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 
51 static 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. */
69 static void b43legacy_led_brightness_set(struct led_classdev *led_dev,
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 
88 static 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 
119 static 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 
128 static 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 
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:
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 
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 }