diff options
Diffstat (limited to 'drivers/net/wireless/b43/leds.c')
-rw-r--r-- | drivers/net/wireless/b43/leds.c | 266 |
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 @@ | |||
34 | static void b43_led_turn_on(struct b43_wldev *dev, u8 led_index, | 34 | static 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 | ||
51 | static void b43_led_turn_off(struct b43_wldev *dev, u8 led_index, | 47 | static 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. */ | 60 | static void b43_led_update(struct b43_wldev *dev, |
69 | static 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 | |||
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, | ||
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 | ||
90 | static int b43_register_led(struct b43_wldev *dev, struct b43_led *led, | 121 | static 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 | ||
119 | static void b43_unregister_led(struct b43_led *led) | 151 | static 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 | ||
128 | static void b43_map_led(struct b43_wldev *dev, | 159 | static 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 | ||
189 | void b43_leds_init(struct b43_wldev *dev) | 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) | ||
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 | |||
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 = 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 | ||
237 | void b43_leds_exit(struct b43_wldev *dev) | 318 | void 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 | |||
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_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 | } |