Merge branch 'timers-for-linus-clocksource' of git://git.kernel.org/pub/scm/linux...
[linux-2.6] / drivers / net / wireless / iwlwifi / iwl-3945-led.c
1 /******************************************************************************
2  *
3  * Copyright(c) 2003 - 2009 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  *  Intel Linux Wireless <ilw@linux.intel.com>
23  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
24  *
25  *****************************************************************************/
26
27 #ifdef CONFIG_IWLWIFI_LEDS
28
29 #include <linux/kernel.h>
30 #include <linux/module.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-commands.h"
43 #include "iwl-3945.h"
44 #include "iwl-core.h"
45 #include "iwl-dev.h"
46
47 #ifdef CONFIG_IWLWIFI_DEBUG
48 static const char *led_type_str[] = {
49         __stringify(IWL_LED_TRG_TX),
50         __stringify(IWL_LED_TRG_RX),
51         __stringify(IWL_LED_TRG_ASSOC),
52         __stringify(IWL_LED_TRG_RADIO),
53         NULL
54 };
55 #endif /* CONFIG_IWLWIFI_DEBUG */
56
57 static const struct {
58         u16 brightness;
59         u8 on_time;
60         u8 off_time;
61 } blink_tbl[] =
62 {
63         {300, 25, 25},
64         {200, 40, 40},
65         {100, 55, 55},
66         {70, 65, 65},
67         {50, 75, 75},
68         {20, 85, 85},
69         {15, 95, 95 },
70         {10, 110, 110},
71         {5, 130, 130},
72         {0, 167, 167},
73         /* SOLID_ON */
74         {-1, IWL_LED_SOLID, 0}
75 };
76
77 #define IWL_1MB_RATE (128 * 1024)
78 #define IWL_LED_THRESHOLD (16)
79 #define IWL_MAX_BLINK_TBL (ARRAY_SIZE(blink_tbl) - 1) /*Exclude Solid on*/
80 #define IWL_SOLID_BLINK_IDX (ARRAY_SIZE(blink_tbl) - 1)
81
82 static int iwl3945_led_cmd_callback(struct iwl_priv *priv,
83                                     struct iwl_cmd *cmd,
84                                     struct sk_buff *skb)
85 {
86         return 1;
87 }
88
89 static inline int iwl3945_brightness_to_idx(enum led_brightness brightness)
90 {
91         return fls(0x000000FF & (u32)brightness);
92 }
93
94 /* Send led command */
95 static int iwl_send_led_cmd(struct iwl_priv *priv,
96                             struct iwl_led_cmd *led_cmd)
97 {
98         struct iwl_host_cmd cmd = {
99                 .id = REPLY_LEDS_CMD,
100                 .len = sizeof(struct iwl_led_cmd),
101                 .data = led_cmd,
102                 .meta.flags = CMD_ASYNC,
103                 .meta.u.callback = iwl3945_led_cmd_callback,
104         };
105
106         return iwl_send_cmd(priv, &cmd);
107 }
108
109
110
111 /* Set led on command */
112 static int iwl3945_led_pattern(struct iwl_priv *priv, int led_id,
113                                unsigned int idx)
114 {
115         struct iwl_led_cmd led_cmd = {
116                 .id = led_id,
117                 .interval = IWL_DEF_LED_INTRVL
118         };
119
120         BUG_ON(idx > IWL_MAX_BLINK_TBL);
121
122         led_cmd.on = blink_tbl[idx].on_time;
123         led_cmd.off = blink_tbl[idx].off_time;
124
125         return iwl_send_led_cmd(priv, &led_cmd);
126 }
127
128
129 /* Set led on command */
130 static int iwl3945_led_on(struct iwl_priv *priv, int led_id)
131 {
132         struct iwl_led_cmd led_cmd = {
133                 .id = led_id,
134                 .on = IWL_LED_SOLID,
135                 .off = 0,
136                 .interval = IWL_DEF_LED_INTRVL
137         };
138         return iwl_send_led_cmd(priv, &led_cmd);
139 }
140
141 /* Set led off command */
142 static int iwl3945_led_off(struct iwl_priv *priv, int led_id)
143 {
144         struct iwl_led_cmd led_cmd = {
145                 .id = led_id,
146                 .on = 0,
147                 .off = 0,
148                 .interval = IWL_DEF_LED_INTRVL
149         };
150         IWL_DEBUG_LED(priv, "led off %d\n", led_id);
151         return iwl_send_led_cmd(priv, &led_cmd);
152 }
153
154 /*
155  *  Set led on in case of association
156  *  */
157 static int iwl3945_led_associate(struct iwl_priv *priv, int led_id)
158 {
159         IWL_DEBUG_LED(priv, "Associated\n");
160
161         priv->allow_blinking = 1;
162         return iwl3945_led_on(priv, led_id);
163 }
164 /* Set Led off in case of disassociation */
165 static int iwl3945_led_disassociate(struct iwl_priv *priv, int led_id)
166 {
167         IWL_DEBUG_LED(priv, "Disassociated\n");
168
169         priv->allow_blinking = 0;
170
171         return 0;
172 }
173
174 /*
175  * brightness call back function for Tx/Rx LED
176  */
177 static int iwl3945_led_associated(struct iwl_priv *priv, int led_id)
178 {
179         if (test_bit(STATUS_EXIT_PENDING, &priv->status) ||
180             !test_bit(STATUS_READY, &priv->status))
181                 return 0;
182
183
184         /* start counting Tx/Rx bytes */
185         if (!priv->last_blink_time && priv->allow_blinking)
186                 priv->last_blink_time = jiffies;
187         return 0;
188 }
189
190 /*
191  * brightness call back for association and radio
192  */
193 static void iwl3945_led_brightness_set(struct led_classdev *led_cdev,
194                                 enum led_brightness brightness)
195 {
196         struct iwl_led *led = container_of(led_cdev,
197                                            struct iwl_led, led_dev);
198         struct iwl_priv *priv = led->priv;
199
200         if (test_bit(STATUS_EXIT_PENDING, &priv->status))
201                 return;
202
203         IWL_DEBUG_LED(priv, "Led type = %s brightness = %d\n",
204                         led_type_str[led->type], brightness);
205
206         switch (brightness) {
207         case LED_FULL:
208                 if (led->led_on)
209                         led->led_on(priv, IWL_LED_LINK);
210                 break;
211         case LED_OFF:
212                 if (led->led_off)
213                         led->led_off(priv, IWL_LED_LINK);
214                 break;
215         default:
216                 if (led->led_pattern) {
217                         int idx = iwl3945_brightness_to_idx(brightness);
218                         led->led_pattern(priv, IWL_LED_LINK, idx);
219                 }
220                 break;
221         }
222 }
223
224 /*
225  * Register led class with the system
226  */
227 static int iwl3945_led_register_led(struct iwl_priv *priv,
228                                    struct iwl_led *led,
229                                    enum led_type type, u8 set_led,
230                                    char *trigger)
231 {
232         struct device *device = wiphy_dev(priv->hw->wiphy);
233         int ret;
234
235         led->led_dev.name = led->name;
236         led->led_dev.brightness_set = iwl3945_led_brightness_set;
237         led->led_dev.default_trigger = trigger;
238
239         led->priv = priv;
240         led->type = type;
241
242         ret = led_classdev_register(device, &led->led_dev);
243         if (ret) {
244                 IWL_ERR(priv, "Error: failed to register led handler.\n");
245                 return ret;
246         }
247
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  */
259 static inline u8 get_blink_rate(struct iwl_priv *priv)
260 {
261         int index;
262         s64 tpt = priv->rxtxpackets;
263
264         if (tpt < 0)
265                 tpt = -tpt;
266
267         IWL_DEBUG_LED(priv, "tpt %lld \n", (long long)tpt);
268
269         if (!priv->allow_blinking)
270                 index = IWL_MAX_BLINK_TBL;
271         else
272                 for (index = 0; index < IWL_MAX_BLINK_TBL; index++)
273                         if (tpt > (blink_tbl[index].brightness * IWL_1MB_RATE))
274                                 break;
275
276         IWL_DEBUG_LED(priv, "LED BLINK IDX=%d\n", index);
277         return index;
278 }
279
280 /*
281  * this function called from handler. Since setting Led command can
282  * happen very frequent we postpone led command to be called from
283  * REPLY handler so we know ucode is up
284  */
285 void iwl3945_led_background(struct iwl_priv *priv)
286 {
287         u8 blink_idx;
288
289         if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
290                 priv->last_blink_time = 0;
291                 return;
292         }
293         if (iwl_is_rfkill(priv)) {
294                 priv->last_blink_time = 0;
295                 return;
296         }
297
298         if (!priv->allow_blinking) {
299                 priv->last_blink_time = 0;
300                 if (priv->last_blink_rate != IWL_SOLID_BLINK_IDX) {
301                         priv->last_blink_rate = IWL_SOLID_BLINK_IDX;
302                         iwl3945_led_pattern(priv, IWL_LED_LINK,
303                                             IWL_SOLID_BLINK_IDX);
304                 }
305                 return;
306         }
307         if (!priv->last_blink_time ||
308             !time_after(jiffies, priv->last_blink_time +
309                         msecs_to_jiffies(1000)))
310                 return;
311
312         blink_idx = get_blink_rate(priv);
313
314         /* call only if blink rate change */
315         if (blink_idx != priv->last_blink_rate)
316                 iwl3945_led_pattern(priv, IWL_LED_LINK, blink_idx);
317
318         priv->last_blink_time = jiffies;
319         priv->last_blink_rate = blink_idx;
320         priv->rxtxpackets = 0;
321 }
322
323
324 /* Register all led handler */
325 int iwl3945_led_register(struct iwl_priv *priv)
326 {
327         char *trigger;
328         int ret;
329
330         priv->last_blink_rate = 0;
331         priv->rxtxpackets = 0;
332         priv->led_tpt = 0;
333         priv->last_blink_time = 0;
334         priv->allow_blinking = 0;
335
336         trigger = ieee80211_get_radio_led_name(priv->hw);
337         snprintf(priv->led[IWL_LED_TRG_RADIO].name,
338                  sizeof(priv->led[IWL_LED_TRG_RADIO].name), "iwl-%s::radio",
339                  wiphy_name(priv->hw->wiphy));
340
341         priv->led[IWL_LED_TRG_RADIO].led_on = iwl3945_led_on;
342         priv->led[IWL_LED_TRG_RADIO].led_off = iwl3945_led_off;
343         priv->led[IWL_LED_TRG_RADIO].led_pattern = NULL;
344
345         ret = iwl3945_led_register_led(priv,
346                                    &priv->led[IWL_LED_TRG_RADIO],
347                                    IWL_LED_TRG_RADIO, 1, trigger);
348
349         if (ret)
350                 goto exit_fail;
351
352         trigger = ieee80211_get_assoc_led_name(priv->hw);
353         snprintf(priv->led[IWL_LED_TRG_ASSOC].name,
354                  sizeof(priv->led[IWL_LED_TRG_ASSOC].name), "iwl-%s::assoc",
355                  wiphy_name(priv->hw->wiphy));
356
357         ret = iwl3945_led_register_led(priv,
358                                    &priv->led[IWL_LED_TRG_ASSOC],
359                                    IWL_LED_TRG_ASSOC, 0, trigger);
360
361         /* for assoc always turn led on */
362         priv->led[IWL_LED_TRG_ASSOC].led_on = iwl3945_led_associate;
363         priv->led[IWL_LED_TRG_ASSOC].led_off = iwl3945_led_disassociate;
364         priv->led[IWL_LED_TRG_ASSOC].led_pattern = NULL;
365
366         if (ret)
367                 goto exit_fail;
368
369         trigger = ieee80211_get_rx_led_name(priv->hw);
370         snprintf(priv->led[IWL_LED_TRG_RX].name,
371                  sizeof(priv->led[IWL_LED_TRG_RX].name), "iwl-%s::RX",
372                  wiphy_name(priv->hw->wiphy));
373
374         ret = iwl3945_led_register_led(priv,
375                                    &priv->led[IWL_LED_TRG_RX],
376                                    IWL_LED_TRG_RX, 0, trigger);
377
378         priv->led[IWL_LED_TRG_RX].led_on = iwl3945_led_associated;
379         priv->led[IWL_LED_TRG_RX].led_off = iwl3945_led_associated;
380         priv->led[IWL_LED_TRG_RX].led_pattern = iwl3945_led_pattern;
381
382         if (ret)
383                 goto exit_fail;
384
385         trigger = ieee80211_get_tx_led_name(priv->hw);
386         snprintf(priv->led[IWL_LED_TRG_TX].name,
387                  sizeof(priv->led[IWL_LED_TRG_TX].name), "iwl-%s::TX",
388                  wiphy_name(priv->hw->wiphy));
389
390         ret = iwl3945_led_register_led(priv,
391                                    &priv->led[IWL_LED_TRG_TX],
392                                    IWL_LED_TRG_TX, 0, trigger);
393
394         priv->led[IWL_LED_TRG_TX].led_on = iwl3945_led_associated;
395         priv->led[IWL_LED_TRG_TX].led_off = iwl3945_led_associated;
396         priv->led[IWL_LED_TRG_TX].led_pattern = iwl3945_led_pattern;
397
398         if (ret)
399                 goto exit_fail;
400
401         return 0;
402
403 exit_fail:
404         iwl3945_led_unregister(priv);
405         return ret;
406 }
407
408
409 /* unregister led class */
410 static void iwl3945_led_unregister_led(struct iwl_led *led, u8 set_led)
411 {
412         if (!led->registered)
413                 return;
414
415         led_classdev_unregister(&led->led_dev);
416
417         if (set_led)
418                 led->led_dev.brightness_set(&led->led_dev, LED_OFF);
419         led->registered = 0;
420 }
421
422 /* Unregister all led handlers */
423 void iwl3945_led_unregister(struct iwl_priv *priv)
424 {
425         iwl3945_led_unregister_led(&priv->led[IWL_LED_TRG_ASSOC], 0);
426         iwl3945_led_unregister_led(&priv->led[IWL_LED_TRG_RX], 0);
427         iwl3945_led_unregister_led(&priv->led[IWL_LED_TRG_TX], 0);
428         iwl3945_led_unregister_led(&priv->led[IWL_LED_TRG_RADIO], 1);
429 }
430
431 #endif