OSDN Git Service

iwl3945: improve 3945 leds
[uclinux-h8/linux.git] / 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         if (iwl_is_rfkill(priv))
171                 iwl3945_led_off(priv, led_id);
172         else
173                 iwl3945_led_on(priv, led_id);
174
175         return 0;
176 }
177
178 /*
179  * brightness call back function for Tx/Rx LED
180  */
181 static int iwl3945_led_associated(struct iwl_priv *priv, int led_id)
182 {
183         if (test_bit(STATUS_EXIT_PENDING, &priv->status) ||
184             !test_bit(STATUS_READY, &priv->status))
185                 return 0;
186
187
188         /* start counting Tx/Rx bytes */
189         if (!priv->last_blink_time && priv->allow_blinking)
190                 priv->last_blink_time = jiffies;
191         return 0;
192 }
193
194 /*
195  * brightness call back for association and radio
196  */
197 static void iwl3945_led_brightness_set(struct led_classdev *led_cdev,
198                                 enum led_brightness brightness)
199 {
200         struct iwl_led *led = container_of(led_cdev,
201                                            struct iwl_led, led_dev);
202         struct iwl_priv *priv = led->priv;
203
204         if (test_bit(STATUS_EXIT_PENDING, &priv->status))
205                 return;
206
207         IWL_DEBUG_LED(priv, "Led type = %s brightness = %d\n",
208                         led_type_str[led->type], brightness);
209
210         switch (brightness) {
211         case LED_FULL:
212                 if (led->led_on)
213                         led->led_on(priv, IWL_LED_LINK);
214                 break;
215         case LED_OFF:
216                 if (led->led_off)
217                         led->led_off(priv, IWL_LED_LINK);
218                 break;
219         default:
220                 if (led->led_pattern) {
221                         int idx = iwl3945_brightness_to_idx(brightness);
222                         led->led_pattern(priv, IWL_LED_LINK, idx);
223                 }
224                 break;
225         }
226 }
227
228 /*
229  * Register led class with the system
230  */
231 static int iwl3945_led_register_led(struct iwl_priv *priv,
232                                    struct iwl_led *led,
233                                    enum led_type type, u8 set_led,
234                                    char *trigger)
235 {
236         struct device *device = wiphy_dev(priv->hw->wiphy);
237         int ret;
238
239         led->led_dev.name = led->name;
240         led->led_dev.brightness_set = iwl3945_led_brightness_set;
241         led->led_dev.default_trigger = trigger;
242
243         led->priv = priv;
244         led->type = type;
245
246         ret = led_classdev_register(device, &led->led_dev);
247         if (ret) {
248                 IWL_ERR(priv, "Error: failed to register led handler.\n");
249                 return ret;
250         }
251
252         led->registered = 1;
253
254         if (set_led && led->led_on)
255                 led->led_on(priv, IWL_LED_LINK);
256         return 0;
257 }
258
259
260 /*
261  * calculate blink rate according to last 2 sec Tx/Rx activities
262  */
263 static inline u8 get_blink_rate(struct iwl_priv *priv)
264 {
265         int index;
266         s64 tpt = priv->rxtxpackets;
267
268         if (tpt < 0)
269                 tpt = -tpt;
270
271         IWL_DEBUG_LED(priv, "tpt %lld \n", (long long)tpt);
272
273         if (!priv->allow_blinking)
274                 index = IWL_MAX_BLINK_TBL;
275         else
276                 for (index = 0; index < IWL_MAX_BLINK_TBL; index++)
277                         if (tpt > (blink_tbl[index].brightness * IWL_1MB_RATE))
278                                 break;
279
280         IWL_DEBUG_LED(priv, "LED BLINK IDX=%d\n", index);
281         return index;
282 }
283
284 /*
285  * this function called from handler. Since setting Led command can
286  * happen very frequent we postpone led command to be called from
287  * REPLY handler so we know ucode is up
288  */
289 void iwl3945_led_background(struct iwl_priv *priv)
290 {
291         u8 blink_idx;
292
293         if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
294                 priv->last_blink_time = 0;
295                 return;
296         }
297         if (iwl_is_rfkill(priv)) {
298                 priv->last_blink_time = 0;
299                 return;
300         }
301
302         if (!priv->allow_blinking) {
303                 priv->last_blink_time = 0;
304                 if (priv->last_blink_rate != IWL_SOLID_BLINK_IDX) {
305                         priv->last_blink_rate = IWL_SOLID_BLINK_IDX;
306                         iwl3945_led_pattern(priv, IWL_LED_LINK,
307                                             IWL_SOLID_BLINK_IDX);
308                 }
309                 return;
310         }
311         if (!priv->last_blink_time ||
312             !time_after(jiffies, priv->last_blink_time +
313                         msecs_to_jiffies(1000)))
314                 return;
315
316         blink_idx = get_blink_rate(priv);
317
318         /* call only if blink rate change */
319         if (blink_idx != priv->last_blink_rate)
320                 iwl3945_led_pattern(priv, IWL_LED_LINK, blink_idx);
321
322         priv->last_blink_time = jiffies;
323         priv->last_blink_rate = blink_idx;
324         priv->rxtxpackets = 0;
325 }
326
327
328 /* Register all led handler */
329 int iwl3945_led_register(struct iwl_priv *priv)
330 {
331         char *trigger;
332         int ret;
333
334         priv->last_blink_rate = 0;
335         priv->rxtxpackets = 0;
336         priv->led_tpt = 0;
337         priv->last_blink_time = 0;
338         priv->allow_blinking = 0;
339
340         trigger = ieee80211_get_radio_led_name(priv->hw);
341         snprintf(priv->led[IWL_LED_TRG_RADIO].name,
342                  sizeof(priv->led[IWL_LED_TRG_RADIO].name), "iwl-%s::radio",
343                  wiphy_name(priv->hw->wiphy));
344
345         priv->led[IWL_LED_TRG_RADIO].led_on = iwl3945_led_on;
346         priv->led[IWL_LED_TRG_RADIO].led_off = iwl3945_led_off;
347         priv->led[IWL_LED_TRG_RADIO].led_pattern = NULL;
348
349         ret = iwl3945_led_register_led(priv,
350                                    &priv->led[IWL_LED_TRG_RADIO],
351                                    IWL_LED_TRG_RADIO, 1, trigger);
352
353         if (ret)
354                 goto exit_fail;
355
356         trigger = ieee80211_get_assoc_led_name(priv->hw);
357         snprintf(priv->led[IWL_LED_TRG_ASSOC].name,
358                  sizeof(priv->led[IWL_LED_TRG_ASSOC].name), "iwl-%s::assoc",
359                  wiphy_name(priv->hw->wiphy));
360
361         ret = iwl3945_led_register_led(priv,
362                                    &priv->led[IWL_LED_TRG_ASSOC],
363                                    IWL_LED_TRG_ASSOC, 0, trigger);
364
365         /* for assoc always turn led on */
366         priv->led[IWL_LED_TRG_ASSOC].led_on = iwl3945_led_associate;
367         priv->led[IWL_LED_TRG_ASSOC].led_off = iwl3945_led_disassociate;
368         priv->led[IWL_LED_TRG_ASSOC].led_pattern = NULL;
369
370         if (ret)
371                 goto exit_fail;
372
373         trigger = ieee80211_get_rx_led_name(priv->hw);
374         snprintf(priv->led[IWL_LED_TRG_RX].name,
375                  sizeof(priv->led[IWL_LED_TRG_RX].name), "iwl-%s::RX",
376                  wiphy_name(priv->hw->wiphy));
377
378         ret = iwl3945_led_register_led(priv,
379                                    &priv->led[IWL_LED_TRG_RX],
380                                    IWL_LED_TRG_RX, 0, trigger);
381
382         priv->led[IWL_LED_TRG_RX].led_on = iwl3945_led_associated;
383         priv->led[IWL_LED_TRG_RX].led_off = iwl3945_led_associated;
384         priv->led[IWL_LED_TRG_RX].led_pattern = iwl3945_led_pattern;
385
386         if (ret)
387                 goto exit_fail;
388
389         trigger = ieee80211_get_tx_led_name(priv->hw);
390         snprintf(priv->led[IWL_LED_TRG_TX].name,
391                  sizeof(priv->led[IWL_LED_TRG_TX].name), "iwl-%s::TX",
392                  wiphy_name(priv->hw->wiphy));
393
394         ret = iwl3945_led_register_led(priv,
395                                    &priv->led[IWL_LED_TRG_TX],
396                                    IWL_LED_TRG_TX, 0, trigger);
397
398         priv->led[IWL_LED_TRG_TX].led_on = iwl3945_led_associated;
399         priv->led[IWL_LED_TRG_TX].led_off = iwl3945_led_associated;
400         priv->led[IWL_LED_TRG_TX].led_pattern = iwl3945_led_pattern;
401
402         if (ret)
403                 goto exit_fail;
404
405         return 0;
406
407 exit_fail:
408         iwl3945_led_unregister(priv);
409         return ret;
410 }
411
412
413 /* unregister led class */
414 static void iwl3945_led_unregister_led(struct iwl_led *led, u8 set_led)
415 {
416         if (!led->registered)
417                 return;
418
419         led_classdev_unregister(&led->led_dev);
420
421         if (set_led)
422                 led->led_dev.brightness_set(&led->led_dev, LED_OFF);
423         led->registered = 0;
424 }
425
426 /* Unregister all led handlers */
427 void iwl3945_led_unregister(struct iwl_priv *priv)
428 {
429         iwl3945_led_unregister_led(&priv->led[IWL_LED_TRG_ASSOC], 0);
430         iwl3945_led_unregister_led(&priv->led[IWL_LED_TRG_RX], 0);
431         iwl3945_led_unregister_led(&priv->led[IWL_LED_TRG_TX], 0);
432         iwl3945_led_unregister_led(&priv->led[IWL_LED_TRG_RADIO], 1);
433 }
434
435 #endif