aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi/iwl-led.c
diff options
context:
space:
mode:
authorMohamed Abbas <mabbas@linux.intel.com>2008-03-25 19:33:36 -0400
committerJohn W. Linville <linville@tuxdriver.com>2008-03-27 16:03:16 -0400
commitab53d8af6772b22d4d68b1bcd74f7a5dba693983 (patch)
tree599c6682d75e15b30af9bf994595e7d5eed5c3ac /drivers/net/wireless/iwlwifi/iwl-led.c
parente0e0a67e44ce13e34f553b6ab6377560fa9813f1 (diff)
iwlwifi: Add led support
This patch add LEDS support to 3965 and 4965 drivers. It is based on led trigger and class. For our drivers we needed to avoid two things. 1- We receive led trigger on/off on each Rx\Tx frame. In our driver we can not call led command like that. In this driver once driver receive a start of traffic it call the led command to start blinking then we count all bytes of Tx and Rx frame, after two second we count the blink rate of last two second then id blink rate changed we call the led commands 2- Since we can call led command very often, we make sure we call the led command after we receive the statistics notification so we don't need to wake up the ucode id it is in sleep state. This patch was tested with 4965 and 3945. Signed-off-by: Mohamed Abbas <mabbas@linux.intel.com> Signed-off-by: Ian Schram<ischram@telenet.be> Signed-off-by: Tomas Winkler <tomas.winkler@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-led.c')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-led.c446
1 files changed, 446 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-led.c b/drivers/net/wireless/iwlwifi/iwl-led.c
new file mode 100644
index 000000000000..6f5424bd365b
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-led.c
@@ -0,0 +1,446 @@
1/******************************************************************************
2 *
3 * Copyright(c) 2003 - 2008 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
17 *
18 * The full GNU General Public License is included in this distribution in the
19 * file called LICENSE.
20 *
21 * Contact Information:
22 * James P. Ketrenos <ipw2100-admin@linux.intel.com>
23 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
24 *
25 *****************************************************************************/
26
27
28#include <linux/kernel.h>
29#include <linux/module.h>
30#include <linux/version.h>
31#include <linux/init.h>
32#include <linux/pci.h>
33#include <linux/dma-mapping.h>
34#include <linux/delay.h>
35#include <linux/skbuff.h>
36#include <linux/netdevice.h>
37#include <linux/wireless.h>
38#include <net/mac80211.h>
39#include <linux/etherdevice.h>
40#include <asm/unaligned.h>
41
42#include "iwl-4965.h"
43#include "iwl-core.h"
44#include "iwl-helpers.h"
45
46#define IWL_1MB_RATE (128 * 1024)
47#define IWL_LED_THRESHOLD (16)
48#define IWL_MAX_BLINK_TBL (10)
49
50static const struct {
51 u16 tpt;
52 u8 on_time;
53 u8 of_time;
54} blink_tbl[] =
55{
56 {300, 25, 25},
57 {200, 40, 40},
58 {100, 55, 55},
59 {70, 65, 65},
60 {50, 75, 75},
61 {20, 85, 85},
62 {15, 95, 95 },
63 {10, 110, 110},
64 {5, 130, 130},
65 {0, 167, 167}
66};
67
68static int iwl_led_cmd_callback(struct iwl_priv *priv,
69 struct iwl_cmd *cmd, struct sk_buff *skb)
70{
71 return 1;
72}
73
74
75/* Send led command */
76static int iwl_send_led_cmd(struct iwl_priv *priv,
77 struct iwl4965_led_cmd *led_cmd)
78{
79 struct iwl_host_cmd cmd = {
80 .id = REPLY_LEDS_CMD,
81 .len = sizeof(struct iwl4965_led_cmd),
82 .data = led_cmd,
83 .meta.flags = CMD_ASYNC,
84 .meta.u.callback = iwl_led_cmd_callback
85 };
86 u32 reg;
87
88 reg = iwl4965_read32(priv, CSR_LED_REG);
89 if (reg != (reg & CSR_LED_BSM_CTRL_MSK))
90 iwl4965_write32(priv, CSR_LED_REG, reg & CSR_LED_BSM_CTRL_MSK);
91
92 return iwl_send_cmd(priv, &cmd);
93}
94
95
96/* Set led on command */
97static int iwl4965_led_on(struct iwl_priv *priv, int led_id)
98{
99 struct iwl4965_led_cmd led_cmd = {
100 .id = led_id,
101 .on = IWL_LED_SOLID,
102 .off = 0,
103 .interval = IWL_DEF_LED_INTRVL
104 };
105 return iwl_send_led_cmd(priv, &led_cmd);
106}
107
108/* Set led on command */
109static int iwl4965_led_pattern(struct iwl_priv *priv, int led_id,
110 enum led_brightness brightness)
111{
112 struct iwl4965_led_cmd led_cmd = {
113 .id = led_id,
114 .on = brightness,
115 .off = brightness,
116 .interval = IWL_DEF_LED_INTRVL
117 };
118 if (brightness == LED_FULL) {
119 led_cmd.on = IWL_LED_SOLID;
120 led_cmd.off = 0;
121 }
122 return iwl_send_led_cmd(priv, &led_cmd);
123}
124
125/* Set led register off */
126static int iwl4965_led_on_reg(struct iwl_priv *priv, int led_id)
127{
128 IWL_DEBUG_LED("led on %d\n", led_id);
129 iwl4965_write32(priv, CSR_LED_REG, CSR_LED_REG_TRUN_ON);
130 return 0;
131}
132
133#if 0
134/* Set led off command */
135int iwl4965_led_off(struct iwl_priv *priv, int led_id)
136{
137 struct iwl4965_led_cmd led_cmd = {
138 .id = led_id,
139 .on = 0,
140 .off = 0,
141 .interval = IWL_DEF_LED_INTRVL
142 };
143 IWL_DEBUG_LED("led off %d\n", led_id);
144 return iwl_send_led_cmd(priv, &led_cmd);
145}
146#endif
147
148
149/* Set led register off */
150static int iwl4965_led_off_reg(struct iwl_priv *priv, int led_id)
151{
152 IWL_DEBUG_LED("radio off\n");
153 iwl4965_write32(priv, CSR_LED_REG, CSR_LED_REG_TRUN_OFF);
154 return 0;
155}
156
157/* Set led blink command */
158static int iwl4965_led_not_solid(struct iwl_priv *priv, int led_id,
159 u8 brightness)
160{
161 struct iwl4965_led_cmd led_cmd = {
162 .id = led_id,
163 .on = brightness,
164 .off = brightness,
165 .interval = IWL_DEF_LED_INTRVL
166 };
167
168 return iwl_send_led_cmd(priv, &led_cmd);
169}
170
171
172/*
173 * brightness call back function for Tx/Rx LED
174 */
175static int iwl4965_led_associated(struct iwl_priv *priv, int led_id)
176{
177 if (test_bit(STATUS_EXIT_PENDING, &priv->status) ||
178 !test_bit(STATUS_READY, &priv->status))
179 return 0;
180
181
182 /* start counting Tx/Rx bytes */
183 if (!priv->last_blink_time && priv->allow_blinking)
184 priv->last_blink_time = jiffies;
185 return 0;
186}
187
188/*
189 * brightness call back for association and radio
190 */
191static void iwl4965_led_brightness_set(struct led_classdev *led_cdev,
192 enum led_brightness brightness)
193{
194 struct iwl4965_led *led = container_of(led_cdev,
195 struct iwl4965_led, led_dev);
196 struct iwl_priv *priv = led->priv;
197
198 if (test_bit(STATUS_EXIT_PENDING, &priv->status))
199 return;
200
201 switch (brightness) {
202 case LED_FULL:
203 if (led->type == IWL_LED_TRG_ASSOC)
204 priv->allow_blinking = 1;
205
206 if (led->led_on)
207 led->led_on(priv, IWL_LED_LINK);
208 break;
209 case LED_OFF:
210 if (led->type == IWL_LED_TRG_ASSOC)
211 priv->allow_blinking = 0;
212
213 if (led->led_off)
214 led->led_off(priv, IWL_LED_LINK);
215 break;
216 default:
217 if (led->led_pattern)
218 led->led_pattern(priv, IWL_LED_LINK, brightness);
219 break;
220 }
221}
222
223
224
225/*
226 * Register led class with the system
227 */
228static int iwl_leds_register_led(struct iwl_priv *priv,
229 struct iwl4965_led *led,
230 enum led_type type, u8 set_led,
231 const char *name, char *trigger)
232{
233 struct device *device = wiphy_dev(priv->hw->wiphy);
234 int ret;
235
236 led->led_dev.name = name;
237 led->led_dev.brightness_set = iwl4965_led_brightness_set;
238 led->led_dev.default_trigger = trigger;
239
240 ret = led_classdev_register(device, &led->led_dev);
241 if (ret) {
242 IWL_ERROR("Error: failed to register led handler.\n");
243 return ret;
244 }
245
246 led->priv = priv;
247 led->type = type;
248 led->registered = 1;
249
250 if (set_led && led->led_on)
251 led->led_on(priv, IWL_LED_LINK);
252 return 0;
253}
254
255
256/*
257 * calculate blink rate according to last 2 sec Tx/Rx activities
258 */
259static inline u8 get_blink_rate(struct iwl_priv *priv)
260{
261 int i;
262 u8 blink_rate;
263 u64 current_tpt = priv->tx_stats[2].bytes + priv->rx_stats[2].bytes;
264 s64 tpt = current_tpt - priv->led_tpt;
265
266 if (tpt < 0) /* wrapparound */
267 tpt = -tpt;
268
269 priv->led_tpt = current_tpt;
270
271 if (tpt < IWL_LED_THRESHOLD) {
272 i = IWL_MAX_BLINK_TBL;
273 } else {
274 for (i = 0; i < IWL_MAX_BLINK_TBL; i++)
275 if (tpt > (blink_tbl[i].tpt * IWL_1MB_RATE))
276 break;
277 }
278 /* if 0 frame is transfered */
279 if ((i == IWL_MAX_BLINK_TBL) || !priv->allow_blinking)
280 blink_rate = IWL_LED_SOLID;
281 else
282 blink_rate = blink_tbl[i].on_time;
283
284 return blink_rate;
285}
286
287static inline int is_rf_kill(struct iwl_priv *priv)
288{
289 return test_bit(STATUS_RF_KILL_HW, &priv->status) ||
290 test_bit(STATUS_RF_KILL_SW, &priv->status);
291}
292
293/*
294 * this function called from handler. Since setting Led command can
295 * happen very frequent we postpone led command to be called from
296 * REPLY handler so we know ucode is up
297 */
298void iwl_leds_background(struct iwl_priv *priv)
299{
300 u8 blink_rate;
301
302 if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
303 priv->last_blink_time = 0;
304 return;
305 }
306 if (is_rf_kill(priv)) {
307 priv->last_blink_time = 0;
308 return;
309 }
310
311 if (!priv->allow_blinking) {
312 priv->last_blink_time = 0;
313 if (priv->last_blink_rate != IWL_LED_SOLID) {
314 priv->last_blink_rate = IWL_LED_SOLID;
315 iwl4965_led_on(priv, IWL_LED_LINK);
316 }
317 return;
318 }
319 if (!priv->last_blink_time ||
320 !time_after(jiffies, priv->last_blink_time +
321 msecs_to_jiffies(1000)))
322 return;
323
324 blink_rate = get_blink_rate(priv);
325
326 /* call only if blink rate change */
327 if (blink_rate != priv->last_blink_rate) {
328 if (blink_rate != IWL_LED_SOLID) {
329 priv->last_blink_time = jiffies +
330 msecs_to_jiffies(1000);
331 iwl4965_led_not_solid(priv, IWL_LED_LINK, blink_rate);
332 } else {
333 priv->last_blink_time = 0;
334 iwl4965_led_on(priv, IWL_LED_LINK);
335 }
336 }
337
338 priv->last_blink_rate = blink_rate;
339}
340EXPORT_SYMBOL(iwl_leds_background);
341
342/* Register all led handler */
343int iwl_leds_register(struct iwl_priv *priv)
344{
345 char *trigger;
346 char name[32];
347 int ret;
348
349 priv->last_blink_rate = 0;
350 priv->led_tpt = 0;
351 priv->last_blink_time = 0;
352 priv->allow_blinking = 0;
353
354 trigger = ieee80211_get_radio_led_name(priv->hw);
355 snprintf(name, sizeof(name), "iwl-%s:radio",
356 wiphy_name(priv->hw->wiphy));
357
358 priv->led[IWL_LED_TRG_RADIO].led_on = iwl4965_led_on_reg;
359 priv->led[IWL_LED_TRG_RADIO].led_off = iwl4965_led_off_reg;
360 priv->led[IWL_LED_TRG_RADIO].led_pattern = NULL;
361
362 ret = iwl_leds_register_led(priv,
363 &priv->led[IWL_LED_TRG_RADIO],
364 IWL_LED_TRG_RADIO, 1,
365 name, trigger);
366 if (ret)
367 goto exit_fail;
368
369 trigger = ieee80211_get_assoc_led_name(priv->hw);
370 snprintf(name, sizeof(name), "iwl-%s:assoc",
371 wiphy_name(priv->hw->wiphy));
372
373 ret = iwl_leds_register_led(priv,
374 &priv->led[IWL_LED_TRG_ASSOC],
375 IWL_LED_TRG_ASSOC, 0,
376 name, trigger);
377 /* for assoc always turn led on */
378 priv->led[IWL_LED_TRG_ASSOC].led_on = iwl4965_led_on_reg;
379 priv->led[IWL_LED_TRG_ASSOC].led_off = iwl4965_led_on_reg;
380 priv->led[IWL_LED_TRG_ASSOC].led_pattern = NULL;
381
382 if (ret)
383 goto exit_fail;
384
385 trigger = ieee80211_get_rx_led_name(priv->hw);
386 snprintf(name, sizeof(name), "iwl-%s:RX",
387 wiphy_name(priv->hw->wiphy));
388
389
390 ret = iwl_leds_register_led(priv,
391 &priv->led[IWL_LED_TRG_RX],
392 IWL_LED_TRG_RX, 0,
393 name, trigger);
394
395 priv->led[IWL_LED_TRG_RX].led_on = iwl4965_led_associated;
396 priv->led[IWL_LED_TRG_RX].led_off = iwl4965_led_associated;
397 priv->led[IWL_LED_TRG_RX].led_pattern = iwl4965_led_pattern;
398
399 if (ret)
400 goto exit_fail;
401
402 trigger = ieee80211_get_tx_led_name(priv->hw);
403 snprintf(name, sizeof(name), "iwl-%s:TX",
404 wiphy_name(priv->hw->wiphy));
405 ret = iwl_leds_register_led(priv,
406 &priv->led[IWL_LED_TRG_TX],
407 IWL_LED_TRG_TX, 0,
408 name, trigger);
409 priv->led[IWL_LED_TRG_TX].led_on = iwl4965_led_associated;
410 priv->led[IWL_LED_TRG_TX].led_off = iwl4965_led_associated;
411 priv->led[IWL_LED_TRG_TX].led_pattern = iwl4965_led_pattern;
412
413 if (ret)
414 goto exit_fail;
415
416 return 0;
417
418exit_fail:
419 iwl_leds_unregister(priv);
420 return ret;
421}
422EXPORT_SYMBOL(iwl_leds_register);
423
424/* unregister led class */
425static void iwl_leds_unregister_led(struct iwl4965_led *led, u8 set_led)
426{
427 if (!led->registered)
428 return;
429
430 led_classdev_unregister(&led->led_dev);
431
432 if (set_led)
433 led->led_dev.brightness_set(&led->led_dev, LED_OFF);
434 led->registered = 0;
435}
436
437/* Unregister all led handlers */
438void iwl_leds_unregister(struct iwl_priv *priv)
439{
440 iwl_leds_unregister_led(&priv->led[IWL_LED_TRG_ASSOC], 0);
441 iwl_leds_unregister_led(&priv->led[IWL_LED_TRG_RX], 0);
442 iwl_leds_unregister_led(&priv->led[IWL_LED_TRG_TX], 0);
443 iwl_leds_unregister_led(&priv->led[IWL_LED_TRG_RADIO], 1);
444}
445EXPORT_SYMBOL(iwl_leds_unregister);
446