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