aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/b43/leds.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/b43/leds.c')
-rw-r--r--drivers/net/wireless/b43/leds.c266
1 files changed, 191 insertions, 75 deletions
diff --git a/drivers/net/wireless/b43/leds.c b/drivers/net/wireless/b43/leds.c
index c8b317094c3..fbe3d4f62ce 100644
--- a/drivers/net/wireless/b43/leds.c
+++ b/drivers/net/wireless/b43/leds.c
@@ -34,57 +34,88 @@
34static void b43_led_turn_on(struct b43_wldev *dev, u8 led_index, 34static void b43_led_turn_on(struct b43_wldev *dev, u8 led_index,
35 bool activelow) 35 bool activelow)
36{ 36{
37 struct b43_wl *wl = dev->wl;
38 unsigned long flags;
39 u16 ctl; 37 u16 ctl;
40 38
41 spin_lock_irqsave(&wl->leds_lock, flags);
42 ctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL); 39 ctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL);
43 if (activelow) 40 if (activelow)
44 ctl &= ~(1 << led_index); 41 ctl &= ~(1 << led_index);
45 else 42 else
46 ctl |= (1 << led_index); 43 ctl |= (1 << led_index);
47 b43_write16(dev, B43_MMIO_GPIO_CONTROL, ctl); 44 b43_write16(dev, B43_MMIO_GPIO_CONTROL, ctl);
48 spin_unlock_irqrestore(&wl->leds_lock, flags);
49} 45}
50 46
51static void b43_led_turn_off(struct b43_wldev *dev, u8 led_index, 47static void b43_led_turn_off(struct b43_wldev *dev, u8 led_index,
52 bool activelow) 48 bool activelow)
53{ 49{
54 struct b43_wl *wl = dev->wl;
55 unsigned long flags;
56 u16 ctl; 50 u16 ctl;
57 51
58 spin_lock_irqsave(&wl->leds_lock, flags);
59 ctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL); 52 ctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL);
60 if (activelow) 53 if (activelow)
61 ctl |= (1 << led_index); 54 ctl |= (1 << led_index);
62 else 55 else
63 ctl &= ~(1 << led_index); 56 ctl &= ~(1 << led_index);
64 b43_write16(dev, B43_MMIO_GPIO_CONTROL, ctl); 57 b43_write16(dev, B43_MMIO_GPIO_CONTROL, ctl);
65 spin_unlock_irqrestore(&wl->leds_lock, flags);
66} 58}
67 59
68/* Callback from the LED subsystem. */ 60static void b43_led_update(struct b43_wldev *dev,
69static void b43_led_brightness_set(struct led_classdev *led_dev, 61 struct b43_led *led)
70 enum led_brightness brightness)
71{ 62{
72 struct b43_led *led = container_of(led_dev, struct b43_led, led_dev);
73 struct b43_wldev *dev = led->dev;
74 bool radio_enabled; 63 bool radio_enabled;
64 bool turn_on;
75 65
76 if (unlikely(b43_status(dev) < B43_STAT_INITIALIZED)) 66 if (!led->wl)
77 return; 67 return;
78 68
79 /* Checking the radio-enabled status here is slightly racy,
80 * but we want to avoid the locking overhead and we don't care
81 * whether the LED has the wrong state for a second. */
82 radio_enabled = (dev->phy.radio_on && dev->radio_hw_enable); 69 radio_enabled = (dev->phy.radio_on && dev->radio_hw_enable);
83 70
84 if (brightness == LED_OFF || !radio_enabled) 71 /* The led->state read is racy, but we don't care. In case we raced
85 b43_led_turn_off(dev, led->index, led->activelow); 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;
86 else 76 else
77 turn_on = 0;
78 if (turn_on == led->hw_state)
79 return;
80 led->hw_state = turn_on;
81
82 if (turn_on)
87 b43_led_turn_on(dev, led->index, led->activelow); 83 b43_led_turn_on(dev, led->index, led->activelow);
84 else
85 b43_led_turn_off(dev, led->index, led->activelow);
86}
87
88static 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
104out_unlock:
105 mutex_unlock(&wl->mutex);
106}
107
108/* Callback from the LED subsystem. */
109static void b43_led_brightness_set(struct led_classdev *led_dev,
110 enum led_brightness brightness)
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 }
88} 119}
89 120
90static int b43_register_led(struct b43_wldev *dev, struct b43_led *led, 121static int b43_register_led(struct b43_wldev *dev, struct b43_led *led,
@@ -93,15 +124,15 @@ static int b43_register_led(struct b43_wldev *dev, struct b43_led *led,
93{ 124{
94 int err; 125 int err;
95 126
96 b43_led_turn_off(dev, led_index, activelow); 127 if (led->wl)
97 if (led->dev)
98 return -EEXIST; 128 return -EEXIST;
99 if (!default_trigger) 129 if (!default_trigger)
100 return -EINVAL; 130 return -EINVAL;
101 led->dev = dev; 131 led->wl = dev->wl;
102 led->index = led_index; 132 led->index = led_index;
103 led->activelow = activelow; 133 led->activelow = activelow;
104 strncpy(led->name, name, sizeof(led->name)); 134 strncpy(led->name, name, sizeof(led->name));
135 atomic_set(&led->state, 0);
105 136
106 led->led_dev.name = led->name; 137 led->led_dev.name = led->name;
107 led->led_dev.default_trigger = default_trigger; 138 led->led_dev.default_trigger = default_trigger;
@@ -110,19 +141,19 @@ static int b43_register_led(struct b43_wldev *dev, struct b43_led *led,
110 err = led_classdev_register(dev->dev->dev, &led->led_dev); 141 err = led_classdev_register(dev->dev->dev, &led->led_dev);
111 if (err) { 142 if (err) {
112 b43warn(dev->wl, "LEDs: Failed to register %s\n", name); 143 b43warn(dev->wl, "LEDs: Failed to register %s\n", name);
113 led->dev = NULL; 144 led->wl = NULL;
114 return err; 145 return err;
115 } 146 }
147
116 return 0; 148 return 0;
117} 149}
118 150
119static void b43_unregister_led(struct b43_led *led) 151static void b43_unregister_led(struct b43_led *led)
120{ 152{
121 if (!led->dev) 153 if (!led->wl)
122 return; 154 return;
123 led_classdev_unregister(&led->led_dev); 155 led_classdev_unregister(&led->led_dev);
124 b43_led_turn_off(led->dev, led->index, led->activelow); 156 led->wl = NULL;
125 led->dev = NULL;
126} 157}
127 158
128static void b43_map_led(struct b43_wldev *dev, 159static void b43_map_led(struct b43_wldev *dev,
@@ -137,24 +168,20 @@ static void b43_map_led(struct b43_wldev *dev,
137 * generic LED triggers. */ 168 * generic LED triggers. */
138 switch (behaviour) { 169 switch (behaviour) {
139 case B43_LED_INACTIVE: 170 case B43_LED_INACTIVE:
140 break;
141 case B43_LED_OFF: 171 case B43_LED_OFF:
142 b43_led_turn_off(dev, led_index, activelow);
143 break;
144 case B43_LED_ON: 172 case B43_LED_ON:
145 b43_led_turn_on(dev, led_index, activelow);
146 break; 173 break;
147 case B43_LED_ACTIVITY: 174 case B43_LED_ACTIVITY:
148 case B43_LED_TRANSFER: 175 case B43_LED_TRANSFER:
149 case B43_LED_APTRANSFER: 176 case B43_LED_APTRANSFER:
150 snprintf(name, sizeof(name), 177 snprintf(name, sizeof(name),
151 "b43-%s::tx", wiphy_name(hw->wiphy)); 178 "b43-%s::tx", wiphy_name(hw->wiphy));
152 b43_register_led(dev, &dev->led_tx, name, 179 b43_register_led(dev, &dev->wl->leds.led_tx, name,
153 ieee80211_get_tx_led_name(hw), 180 ieee80211_get_tx_led_name(hw),
154 led_index, activelow); 181 led_index, activelow);
155 snprintf(name, sizeof(name), 182 snprintf(name, sizeof(name),
156 "b43-%s::rx", wiphy_name(hw->wiphy)); 183 "b43-%s::rx", wiphy_name(hw->wiphy));
157 b43_register_led(dev, &dev->led_rx, name, 184 b43_register_led(dev, &dev->wl->leds.led_rx, name,
158 ieee80211_get_rx_led_name(hw), 185 ieee80211_get_rx_led_name(hw),
159 led_index, activelow); 186 led_index, activelow);
160 break; 187 break;
@@ -164,18 +191,15 @@ static void b43_map_led(struct b43_wldev *dev,
164 case B43_LED_MODE_BG: 191 case B43_LED_MODE_BG:
165 snprintf(name, sizeof(name), 192 snprintf(name, sizeof(name),
166 "b43-%s::radio", wiphy_name(hw->wiphy)); 193 "b43-%s::radio", wiphy_name(hw->wiphy));
167 b43_register_led(dev, &dev->led_radio, name, 194 b43_register_led(dev, &dev->wl->leds.led_radio, name,
168 ieee80211_get_radio_led_name(hw), 195 ieee80211_get_radio_led_name(hw),
169 led_index, activelow); 196 led_index, activelow);
170 /* Sync the RF-kill LED state with radio and switch states. */
171 if (dev->phy.radio_on && b43_is_hw_radio_enabled(dev))
172 b43_led_turn_on(dev, led_index, activelow);
173 break; 197 break;
174 case B43_LED_WEIRD: 198 case B43_LED_WEIRD:
175 case B43_LED_ASSOC: 199 case B43_LED_ASSOC:
176 snprintf(name, sizeof(name), 200 snprintf(name, sizeof(name),
177 "b43-%s::assoc", wiphy_name(hw->wiphy)); 201 "b43-%s::assoc", wiphy_name(hw->wiphy));
178 b43_register_led(dev, &dev->led_assoc, name, 202 b43_register_led(dev, &dev->wl->leds.led_assoc, name,
179 ieee80211_get_assoc_led_name(hw), 203 ieee80211_get_assoc_led_name(hw),
180 led_index, activelow); 204 led_index, activelow);
181 break; 205 break;
@@ -186,58 +210,150 @@ static void b43_map_led(struct b43_wldev *dev,
186 } 210 }
187} 211}
188 212
189void b43_leds_init(struct b43_wldev *dev) 213static void b43_led_get_sprominfo(struct b43_wldev *dev,
214 unsigned int led_index,
215 enum b43_led_behaviour *behaviour,
216 bool *activelow)
190{ 217{
191 struct ssb_bus *bus = dev->dev->bus; 218 struct ssb_bus *bus = dev->dev->bus;
192 u8 sprom[4]; 219 u8 sprom[4];
193 int i;
194 enum b43_led_behaviour behaviour;
195 bool activelow;
196 220
197 sprom[0] = bus->sprom.gpio0; 221 sprom[0] = bus->sprom.gpio0;
198 sprom[1] = bus->sprom.gpio1; 222 sprom[1] = bus->sprom.gpio1;
199 sprom[2] = bus->sprom.gpio2; 223 sprom[2] = bus->sprom.gpio2;
200 sprom[3] = bus->sprom.gpio3; 224 sprom[3] = bus->sprom.gpio3;
201 225
202 for (i = 0; i < 4; i++) { 226 if (sprom[led_index] == 0xFF) {
203 if (sprom[i] == 0xFF) { 227 /* There is no LED information in the SPROM
204 /* There is no LED information in the SPROM 228 * for this LED. Hardcode it here. */
205 * for this LED. Hardcode it here. */ 229 *activelow = 0;
206 activelow = 0; 230 switch (led_index) {
207 switch (i) { 231 case 0:
208 case 0: 232 *behaviour = B43_LED_ACTIVITY;
209 behaviour = B43_LED_ACTIVITY; 233 *activelow = 1;
210 activelow = 1; 234 if (bus->boardinfo.vendor == PCI_VENDOR_ID_COMPAQ)
211 if (bus->boardinfo.vendor == PCI_VENDOR_ID_COMPAQ) 235 *behaviour = B43_LED_RADIO_ALL;
212 behaviour = B43_LED_RADIO_ALL; 236 break;
213 break; 237 case 1:
214 case 1: 238 *behaviour = B43_LED_RADIO_B;
215 behaviour = B43_LED_RADIO_B; 239 if (bus->boardinfo.vendor == PCI_VENDOR_ID_ASUSTEK)
216 if (bus->boardinfo.vendor == PCI_VENDOR_ID_ASUSTEK) 240 *behaviour = B43_LED_ASSOC;
217 behaviour = B43_LED_ASSOC; 241 break;
218 break; 242 case 2:
219 case 2: 243 *behaviour = B43_LED_RADIO_A;
220 behaviour = B43_LED_RADIO_A; 244 break;
221 break; 245 case 3:
222 case 3: 246 *behaviour = B43_LED_OFF;
223 behaviour = B43_LED_OFF; 247 break;
224 break; 248 default:
225 default: 249 B43_WARN_ON(1);
226 B43_WARN_ON(1); 250 return;
227 return; 251 }
228 } 252 } else {
253 *behaviour = sprom[led_index] & B43_LED_BEHAVIOUR;
254 *activelow = !!(sprom[led_index] & B43_LED_ACTIVELOW);
255 }
256}
257
258void 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 = 1;
271 atomic_set(&led->state, 1);
229 } else { 272 } else {
230 behaviour = sprom[i] & B43_LED_BEHAVIOUR; 273 b43_led_turn_off(dev, led->index, led->activelow);
231 activelow = !!(sprom[i] & B43_LED_ACTIVELOW); 274 led->hw_state = 0;
275 atomic_set(&led->state, 0);
232 } 276 }
233 b43_map_led(dev, i, behaviour, activelow);
234 } 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 = 0;
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 = 0;
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 = 0;
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;
235} 316}
236 317
237void b43_leds_exit(struct b43_wldev *dev) 318void b43_leds_exit(struct b43_wldev *dev)
238{ 319{
239 b43_unregister_led(&dev->led_tx); 320 struct b43_leds *leds = &dev->wl->leds;
240 b43_unregister_led(&dev->led_rx); 321
241 b43_unregister_led(&dev->led_assoc); 322 b43_led_turn_off(dev, leds->led_tx.index, leds->led_tx.activelow);
242 b43_unregister_led(&dev->led_radio); 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
328void 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
336void 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
351void b43_leds_unregister(struct b43_wldev *dev)
352{
353 struct b43_leds *leds = &dev->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);
243} 359}