linux/drivers/net/wireless/iwlwifi/iwl-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
  28#include <linux/kernel.h>
  29#include <linux/module.h>
  30#include <linux/init.h>
  31#include <linux/pci.h>
  32#include <linux/dma-mapping.h>
  33#include <linux/delay.h>
  34#include <linux/skbuff.h>
  35#include <linux/netdevice.h>
  36#include <linux/wireless.h>
  37#include <net/mac80211.h>
  38#include <linux/etherdevice.h>
  39#include <asm/unaligned.h>
  40
  41#include "iwl-dev.h"
  42#include "iwl-core.h"
  43#include "iwl-io.h"
  44
  45#ifdef CONFIG_IWLWIFI_DEBUG
  46static const char *led_type_str[] = {
  47        __stringify(IWL_LED_TRG_TX),
  48        __stringify(IWL_LED_TRG_RX),
  49        __stringify(IWL_LED_TRG_ASSOC),
  50        __stringify(IWL_LED_TRG_RADIO),
  51        NULL
  52};
  53#endif /* CONFIG_IWLWIFI_DEBUG */
  54
  55
  56static const struct {
  57        u16 tpt;        /* Mb/s */
  58        u8 on_time;
  59        u8 off_time;
  60} blink_tbl[] =
  61{
  62        {300, 25, 25},
  63        {200, 40, 40},
  64        {100, 55, 55},
  65        {70, 65, 65},
  66        {50, 75, 75},
  67        {20, 85, 85},
  68        {15, 95, 95 },
  69        {10, 110, 110},
  70        {5, 130, 130},
  71        {0, 167, 167},
  72/* SOLID_ON */
  73        {-1, IWL_LED_SOLID, 0}
  74};
  75
  76#define IWL_1MB_RATE (128 * 1024)
  77#define IWL_LED_THRESHOLD (16)
  78#define IWL_MAX_BLINK_TBL (ARRAY_SIZE(blink_tbl) - 1) /* exclude SOLID_ON */
  79#define IWL_SOLID_BLINK_IDX (ARRAY_SIZE(blink_tbl) - 1)
  80
  81/*  [0-256] -> [0..8] FIXME: we need [0..10] */
  82static inline int iwl_brightness_to_idx(enum led_brightness brightness)
  83{
  84        return fls(0x000000FF & (u32)brightness);
  85}
  86
  87/* Send led command */
  88static int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd)
  89{
  90        struct iwl_host_cmd cmd = {
  91                .id = REPLY_LEDS_CMD,
  92                .len = sizeof(struct iwl_led_cmd),
  93                .data = led_cmd,
  94                .flags = CMD_ASYNC,
  95                .callback = NULL,
  96        };
  97        u32 reg;
  98
  99        reg = iwl_read32(priv, CSR_LED_REG);
 100        if (reg != (reg & CSR_LED_BSM_CTRL_MSK))
 101                iwl_write32(priv, CSR_LED_REG, reg & CSR_LED_BSM_CTRL_MSK);
 102
 103        return iwl_send_cmd(priv, &cmd);
 104}
 105
 106/* Set led pattern command */
 107static int iwl_led_pattern(struct iwl_priv *priv, int led_id,
 108                               unsigned int idx)
 109{
 110        struct iwl_led_cmd led_cmd = {
 111                .id = led_id,
 112                .interval = IWL_DEF_LED_INTRVL
 113        };
 114
 115        BUG_ON(idx > IWL_MAX_BLINK_TBL);
 116
 117        led_cmd.on = blink_tbl[idx].on_time;
 118        led_cmd.off = blink_tbl[idx].off_time;
 119
 120        return iwl_send_led_cmd(priv, &led_cmd);
 121}
 122
 123/* Set led register off */
 124static int iwl_led_on_reg(struct iwl_priv *priv, int led_id)
 125{
 126        IWL_DEBUG_LED(priv, "led on %d\n", led_id);
 127        iwl_write32(priv, CSR_LED_REG, CSR_LED_REG_TRUN_ON);
 128        return 0;
 129}
 130
 131#if 0
 132/* Set led on command */
 133static int iwl_led_on(struct iwl_priv *priv, int led_id)
 134{
 135        struct iwl_led_cmd led_cmd = {
 136                .id = led_id,
 137                .on = IWL_LED_SOLID,
 138                .off = 0,
 139                .interval = IWL_DEF_LED_INTRVL
 140        };
 141        return iwl_send_led_cmd(priv, &led_cmd);
 142}
 143
 144/* Set led off command */
 145int iwl_led_off(struct iwl_priv *priv, int led_id)
 146{
 147        struct iwl_led_cmd led_cmd = {
 148                .id = led_id,
 149                .on = 0,
 150                .off = 0,
 151                .interval = IWL_DEF_LED_INTRVL
 152        };
 153        IWL_DEBUG_LED(priv, "led off %d\n", led_id);
 154        return iwl_send_led_cmd(priv, &led_cmd);
 155}
 156#endif
 157
 158
 159/* Set led register off */
 160static int iwl_led_off_reg(struct iwl_priv *priv, int led_id)
 161{
 162        IWL_DEBUG_LED(priv, "LED Reg off\n");
 163        iwl_write32(priv, CSR_LED_REG, CSR_LED_REG_TRUN_OFF);
 164        return 0;
 165}
 166
 167/*
 168 * Set led register in case of disassociation according to rfkill state
 169 */
 170static int iwl_led_associate(struct iwl_priv *priv, int led_id)
 171{
 172        IWL_DEBUG_LED(priv, "Associated\n");
 173        priv->allow_blinking = 1;
 174        return iwl_led_on_reg(priv, led_id);
 175}
 176static int iwl_led_disassociate(struct iwl_priv *priv, int led_id)
 177{
 178        priv->allow_blinking = 0;
 179
 180        return 0;
 181}
 182
 183/*
 184 * brightness call back function for Tx/Rx LED
 185 */
 186static int iwl_led_associated(struct iwl_priv *priv, int led_id)
 187{
 188        if (test_bit(STATUS_EXIT_PENDING, &priv->status) ||
 189            !test_bit(STATUS_READY, &priv->status))
 190                return 0;
 191
 192
 193        /* start counting Tx/Rx bytes */
 194        if (!priv->last_blink_time && priv->allow_blinking)
 195                priv->last_blink_time = jiffies;
 196        return 0;
 197}
 198
 199/*
 200 * brightness call back for association and radio
 201 */
 202static void iwl_led_brightness_set(struct led_classdev *led_cdev,
 203                                       enum led_brightness brightness)
 204{
 205        struct iwl_led *led = container_of(led_cdev, struct iwl_led, led_dev);
 206        struct iwl_priv *priv = led->priv;
 207
 208        if (test_bit(STATUS_EXIT_PENDING, &priv->status))
 209                return;
 210
 211
 212        IWL_DEBUG_LED(priv, "Led type = %s brightness = %d\n",
 213                        led_type_str[led->type], brightness);
 214        switch (brightness) {
 215        case LED_FULL:
 216                if (led->led_on)
 217                        led->led_on(priv, IWL_LED_LINK);
 218                break;
 219        case LED_OFF:
 220                if (led->led_off)
 221                        led->led_off(priv, IWL_LED_LINK);
 222                break;
 223        default:
 224                if (led->led_pattern) {
 225                        int idx = iwl_brightness_to_idx(brightness);
 226                        led->led_pattern(priv, IWL_LED_LINK, idx);
 227                }
 228                break;
 229        }
 230}
 231
 232
 233
 234/*
 235 * Register led class with the system
 236 */
 237static int iwl_leds_register_led(struct iwl_priv *priv, struct iwl_led *led,
 238                                   enum led_type type, u8 set_led,
 239                                   char *trigger)
 240{
 241        struct device *device = wiphy_dev(priv->hw->wiphy);
 242        int ret;
 243
 244        led->led_dev.name = led->name;
 245        led->led_dev.brightness_set = iwl_led_brightness_set;
 246        led->led_dev.default_trigger = trigger;
 247
 248        led->priv = priv;
 249        led->type = type;
 250
 251        ret = led_classdev_register(device, &led->led_dev);
 252        if (ret) {
 253                IWL_ERR(priv, "Error: failed to register led handler.\n");
 254                return ret;
 255        }
 256
 257        led->registered = 1;
 258
 259        if (set_led && led->led_on)
 260                led->led_on(priv, IWL_LED_LINK);
 261
 262        return 0;
 263}
 264
 265
 266/*
 267 * calculate blink rate according to last second Tx/Rx activities
 268 */
 269static int iwl_get_blink_rate(struct iwl_priv *priv)
 270{
 271        int i;
 272        /* count both tx and rx traffic to be able to
 273         * handle traffic in either direction
 274         */
 275        u64 current_tpt = priv->tx_stats.data_bytes +
 276                          priv->rx_stats.data_bytes;
 277        s64 tpt = current_tpt - priv->led_tpt;
 278
 279        if (tpt < 0) /* wraparound */
 280                tpt = -tpt;
 281
 282        IWL_DEBUG_LED(priv, "tpt %lld current_tpt %llu\n",
 283                (long long)tpt,
 284                (unsigned long long)current_tpt);
 285        priv->led_tpt = current_tpt;
 286
 287        if (!priv->allow_blinking)
 288                i = IWL_MAX_BLINK_TBL;
 289        else
 290                for (i = 0; i < IWL_MAX_BLINK_TBL; i++)
 291                        if (tpt  > (blink_tbl[i].tpt * IWL_1MB_RATE))
 292                                break;
 293
 294        IWL_DEBUG_LED(priv, "LED BLINK IDX=%d\n", i);
 295        return i;
 296}
 297
 298/*
 299 * this function called from handler. Since setting Led command can
 300 * happen very frequent we postpone led command to be called from
 301 * REPLY handler so we know ucode is up
 302 */
 303void iwl_leds_background(struct iwl_priv *priv)
 304{
 305        u8 blink_idx;
 306
 307        if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
 308                priv->last_blink_time = 0;
 309                return;
 310        }
 311        if (iwl_is_rfkill(priv)) {
 312                priv->last_blink_time = 0;
 313                return;
 314        }
 315
 316        if (!priv->allow_blinking) {
 317                priv->last_blink_time = 0;
 318                if (priv->last_blink_rate != IWL_SOLID_BLINK_IDX) {
 319                        priv->last_blink_rate = IWL_SOLID_BLINK_IDX;
 320                        iwl_led_pattern(priv, IWL_LED_LINK,
 321                                            IWL_SOLID_BLINK_IDX);
 322                }
 323                return;
 324        }
 325        if (!priv->last_blink_time ||
 326            !time_after(jiffies, priv->last_blink_time +
 327                        msecs_to_jiffies(1000)))
 328                return;
 329
 330        blink_idx = iwl_get_blink_rate(priv);
 331
 332        /* call only if blink rate change */
 333        if (blink_idx != priv->last_blink_rate)
 334                iwl_led_pattern(priv, IWL_LED_LINK, blink_idx);
 335
 336        priv->last_blink_time = jiffies;
 337        priv->last_blink_rate = blink_idx;
 338}
 339
 340/* Register all led handler */
 341int iwl_leds_register(struct iwl_priv *priv)
 342{
 343        char *trigger;
 344        int ret;
 345
 346        priv->last_blink_rate = 0;
 347        priv->led_tpt = 0;
 348        priv->last_blink_time = 0;
 349        priv->allow_blinking = 0;
 350
 351        trigger = ieee80211_get_radio_led_name(priv->hw);
 352        snprintf(priv->led[IWL_LED_TRG_RADIO].name,
 353                 sizeof(priv->led[IWL_LED_TRG_RADIO].name), "iwl-%s::radio",
 354                 wiphy_name(priv->hw->wiphy));
 355
 356        priv->led[IWL_LED_TRG_RADIO].led_on = iwl_led_on_reg;
 357        priv->led[IWL_LED_TRG_RADIO].led_off = iwl_led_off_reg;
 358        priv->led[IWL_LED_TRG_RADIO].led_pattern = NULL;
 359
 360        ret = iwl_leds_register_led(priv, &priv->led[IWL_LED_TRG_RADIO],
 361                                   IWL_LED_TRG_RADIO, 1, trigger);
 362        if (ret)
 363                goto exit_fail;
 364
 365        trigger = ieee80211_get_assoc_led_name(priv->hw);
 366        snprintf(priv->led[IWL_LED_TRG_ASSOC].name,
 367                 sizeof(priv->led[IWL_LED_TRG_ASSOC].name), "iwl-%s::assoc",
 368                 wiphy_name(priv->hw->wiphy));
 369
 370        ret = iwl_leds_register_led(priv, &priv->led[IWL_LED_TRG_ASSOC],
 371                                   IWL_LED_TRG_ASSOC, 0, trigger);
 372
 373        /* for assoc always turn led on */
 374        priv->led[IWL_LED_TRG_ASSOC].led_on = iwl_led_associate;
 375        priv->led[IWL_LED_TRG_ASSOC].led_off = iwl_led_disassociate;
 376        priv->led[IWL_LED_TRG_ASSOC].led_pattern = NULL;
 377
 378        if (ret)
 379                goto exit_fail;
 380
 381        trigger = ieee80211_get_rx_led_name(priv->hw);
 382        snprintf(priv->led[IWL_LED_TRG_RX].name,
 383                 sizeof(priv->led[IWL_LED_TRG_RX].name), "iwl-%s::RX",
 384                 wiphy_name(priv->hw->wiphy));
 385
 386        ret = iwl_leds_register_led(priv, &priv->led[IWL_LED_TRG_RX],
 387                                   IWL_LED_TRG_RX, 0, trigger);
 388
 389        priv->led[IWL_LED_TRG_RX].led_on = iwl_led_associated;
 390        priv->led[IWL_LED_TRG_RX].led_off = iwl_led_associated;
 391        priv->led[IWL_LED_TRG_RX].led_pattern = iwl_led_pattern;
 392
 393        if (ret)
 394                goto exit_fail;
 395
 396        trigger = ieee80211_get_tx_led_name(priv->hw);
 397        snprintf(priv->led[IWL_LED_TRG_TX].name,
 398                 sizeof(priv->led[IWL_LED_TRG_TX].name), "iwl-%s::TX",
 399                 wiphy_name(priv->hw->wiphy));
 400
 401        ret = iwl_leds_register_led(priv, &priv->led[IWL_LED_TRG_TX],
 402                                   IWL_LED_TRG_TX, 0, trigger);
 403
 404        priv->led[IWL_LED_TRG_TX].led_on = iwl_led_associated;
 405        priv->led[IWL_LED_TRG_TX].led_off = iwl_led_associated;
 406        priv->led[IWL_LED_TRG_TX].led_pattern = iwl_led_pattern;
 407
 408        if (ret)
 409                goto exit_fail;
 410
 411        return 0;
 412
 413exit_fail:
 414        iwl_leds_unregister(priv);
 415        return ret;
 416}
 417EXPORT_SYMBOL(iwl_leds_register);
 418
 419/* unregister led class */
 420static void iwl_leds_unregister_led(struct iwl_led *led, u8 set_led)
 421{
 422        if (!led->registered)
 423                return;
 424
 425        led_classdev_unregister(&led->led_dev);
 426
 427        if (set_led)
 428                led->led_dev.brightness_set(&led->led_dev, LED_OFF);
 429        led->registered = 0;
 430}
 431
 432/* Unregister all led handlers */
 433void iwl_leds_unregister(struct iwl_priv *priv)
 434{
 435        iwl_leds_unregister_led(&priv->led[IWL_LED_TRG_ASSOC], 0);
 436        iwl_leds_unregister_led(&priv->led[IWL_LED_TRG_RX], 0);
 437        iwl_leds_unregister_led(&priv->led[IWL_LED_TRG_TX], 0);
 438        iwl_leds_unregister_led(&priv->led[IWL_LED_TRG_RADIO], 1);
 439}
 440EXPORT_SYMBOL(iwl_leds_unregister);
 441
 442