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 "b43.h"
30 #include "leds.h"
31 #include "rfkill.h"
32 
33 
34 static 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 
47 static 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 
60 static 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 
88 static 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 
104 out_unlock:
105  mutex_unlock(&wl->mutex);
106 }
107 
108 /* Callback from the LED subsystem. */
109 static void b43_led_brightness_set(struct led_classdev *led_dev,
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 
121 static 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 
151 static 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 
159 static 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 
213 static 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[led_index] == 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  *behaviour = sprom[led_index] & B43_LED_BEHAVIOUR;
254  *activelow = !!(sprom[led_index] & B43_LED_ACTIVELOW);
255  }
256 }
257 
258 void b43_leds_init(struct b43_wldev *dev)
259 {
260  struct b43_led *led;
261  unsigned int i;
262  enum b43_led_behaviour behaviour;
263  bool activelow;
264 
265  /* Sync the RF-kill LED state (if we have one) with radio and switch states. */
266  led = &dev->wl->leds.led_radio;
267  if (led->wl) {
268  if (dev->phy.radio_on && b43_is_hw_radio_enabled(dev)) {
269  b43_led_turn_on(dev, led->index, led->activelow);
270  led->hw_state = true;
271  atomic_set(&led->state, 1);
272  } else {
273  b43_led_turn_off(dev, led->index, led->activelow);
274  led->hw_state = false;
275  atomic_set(&led->state, 0);
276  }
277  }
278 
279  /* Initialize TX/RX/ASSOC leds */
280  led = &dev->wl->leds.led_tx;
281  if (led->wl) {
282  b43_led_turn_off(dev, led->index, led->activelow);
283  led->hw_state = false;
284  atomic_set(&led->state, 0);
285  }
286  led = &dev->wl->leds.led_rx;
287  if (led->wl) {
288  b43_led_turn_off(dev, led->index, led->activelow);
289  led->hw_state = false;
290  atomic_set(&led->state, 0);
291  }
292  led = &dev->wl->leds.led_assoc;
293  if (led->wl) {
294  b43_led_turn_off(dev, led->index, led->activelow);
295  led->hw_state = false;
296  atomic_set(&led->state, 0);
297  }
298 
299  /* Initialize other LED states. */
300  for (i = 0; i < B43_MAX_NR_LEDS; i++) {
301  b43_led_get_sprominfo(dev, i, &behaviour, &activelow);
302  switch (behaviour) {
303  case B43_LED_OFF:
304  b43_led_turn_off(dev, i, activelow);
305  break;
306  case B43_LED_ON:
307  b43_led_turn_on(dev, i, activelow);
308  break;
309  default:
310  /* Leave others as-is. */
311  break;
312  }
313  }
314 
315  dev->wl->leds.stop = 0;
316 }
317 
318 void b43_leds_exit(struct b43_wldev *dev)
319 {
320  struct b43_leds *leds = &dev->wl->leds;
321 
322  b43_led_turn_off(dev, leds->led_tx.index, leds->led_tx.activelow);
323  b43_led_turn_off(dev, leds->led_rx.index, leds->led_rx.activelow);
324  b43_led_turn_off(dev, leds->led_assoc.index, leds->led_assoc.activelow);
325  b43_led_turn_off(dev, leds->led_radio.index, leds->led_radio.activelow);
326 }
327 
328 void b43_leds_stop(struct b43_wldev *dev)
329 {
330  struct b43_leds *leds = &dev->wl->leds;
331 
332  leds->stop = 1;
333  cancel_work_sync(&leds->work);
334 }
335 
336 void b43_leds_register(struct b43_wldev *dev)
337 {
338  unsigned int i;
339  enum b43_led_behaviour behaviour;
340  bool activelow;
341 
342  INIT_WORK(&dev->wl->leds.work, b43_leds_work);
343 
344  /* Register the LEDs to the LED subsystem. */
345  for (i = 0; i < B43_MAX_NR_LEDS; i++) {
346  b43_led_get_sprominfo(dev, i, &behaviour, &activelow);
347  b43_map_led(dev, i, behaviour, activelow);
348  }
349 }
350 
351 void b43_leds_unregister(struct b43_wl *wl)
352 {
353  struct b43_leds *leds = &wl->leds;
354 
355  b43_unregister_led(&leds->led_tx);
356  b43_unregister_led(&leds->led_rx);
357  b43_unregister_led(&leds->led_assoc);
358  b43_unregister_led(&leds->led_radio);
359 }