linux/drivers/net/wireless/iwlwifi/iwl-led.c
<<
>>
Prefs
   1/******************************************************************************
   2 *
   3 * Copyright(c) 2003 - 2010 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/* default: IWL_LED_BLINK(0) using blinking index table */
  46static int led_mode;
  47module_param(led_mode, int, S_IRUGO);
  48MODULE_PARM_DESC(led_mode, "0=system default, "
  49                "1=On(RF On)/Off(RF Off), 2=blinking");
  50
  51static const struct {
  52        u16 tpt;        /* Mb/s */
  53        u8 on_time;
  54        u8 off_time;
  55} blink_tbl[] =
  56{
  57        {300, 25, 25},
  58        {200, 40, 40},
  59        {100, 55, 55},
  60        {70, 65, 65},
  61        {50, 75, 75},
  62        {20, 85, 85},
  63        {10, 95, 95},
  64        {5, 110, 110},
  65        {1, 130, 130},
  66        {0, 167, 167},
  67        /* SOLID_ON */
  68        {-1, IWL_LED_SOLID, 0}
  69};
  70
  71#define IWL_1MB_RATE (128 * 1024)
  72#define IWL_LED_THRESHOLD (16)
  73#define IWL_MAX_BLINK_TBL (ARRAY_SIZE(blink_tbl) - 1) /* exclude SOLID_ON */
  74#define IWL_SOLID_BLINK_IDX (ARRAY_SIZE(blink_tbl) - 1)
  75
  76/*
  77 * Adjust led blink rate to compensate on a MAC Clock difference on every HW
  78 * Led blink rate analysis showed an average deviation of 0% on 3945,
  79 * 5% on 4965 HW and 20% on 5000 series and up.
  80 * Need to compensate on the led on/off time per HW according to the deviation
  81 * to achieve the desired led frequency
  82 * The calculation is: (100-averageDeviation)/100 * blinkTime
  83 * For code efficiency the calculation will be:
  84 *     compensation = (100 - averageDeviation) * 64 / 100
  85 *     NewBlinkTime = (compensation * BlinkTime) / 64
  86 */
  87static inline u8 iwl_blink_compensation(struct iwl_priv *priv,
  88                                    u8 time, u16 compensation)
  89{
  90        if (!compensation) {
  91                IWL_ERR(priv, "undefined blink compensation: "
  92                        "use pre-defined blinking time\n");
  93                return time;
  94        }
  95
  96        return (u8)((time * compensation) >> 6);
  97}
  98
  99/* Set led pattern command */
 100static int iwl_led_pattern(struct iwl_priv *priv, unsigned int idx)
 101{
 102        struct iwl_led_cmd led_cmd = {
 103                .id = IWL_LED_LINK,
 104                .interval = IWL_DEF_LED_INTRVL
 105        };
 106
 107        BUG_ON(idx > IWL_MAX_BLINK_TBL);
 108
 109        IWL_DEBUG_LED(priv, "Led blink time compensation= %u\n",
 110                        priv->cfg->base_params->led_compensation);
 111        led_cmd.on =
 112                iwl_blink_compensation(priv, blink_tbl[idx].on_time,
 113                                priv->cfg->base_params->led_compensation);
 114        led_cmd.off =
 115                iwl_blink_compensation(priv, blink_tbl[idx].off_time,
 116                                priv->cfg->base_params->led_compensation);
 117
 118        return priv->cfg->ops->led->cmd(priv, &led_cmd);
 119}
 120
 121int iwl_led_start(struct iwl_priv *priv)
 122{
 123        return priv->cfg->ops->led->on(priv);
 124}
 125EXPORT_SYMBOL(iwl_led_start);
 126
 127int iwl_led_associate(struct iwl_priv *priv)
 128{
 129        IWL_DEBUG_LED(priv, "Associated\n");
 130        if (priv->cfg->led_mode == IWL_LED_BLINK)
 131                priv->allow_blinking = 1;
 132        priv->last_blink_time = jiffies;
 133
 134        return 0;
 135}
 136EXPORT_SYMBOL(iwl_led_associate);
 137
 138int iwl_led_disassociate(struct iwl_priv *priv)
 139{
 140        priv->allow_blinking = 0;
 141
 142        return 0;
 143}
 144EXPORT_SYMBOL(iwl_led_disassociate);
 145
 146/*
 147 * calculate blink rate according to last second Tx/Rx activities
 148 */
 149static int iwl_get_blink_rate(struct iwl_priv *priv)
 150{
 151        int i;
 152        /* count both tx and rx traffic to be able to
 153         * handle traffic in either direction
 154         */
 155        u64 current_tpt = priv->tx_stats.data_bytes +
 156                          priv->rx_stats.data_bytes;
 157        s64 tpt = current_tpt - priv->led_tpt;
 158
 159        if (tpt < 0) /* wraparound */
 160                tpt = -tpt;
 161
 162        IWL_DEBUG_LED(priv, "tpt %lld current_tpt %llu\n",
 163                (long long)tpt,
 164                (unsigned long long)current_tpt);
 165        priv->led_tpt = current_tpt;
 166
 167        if (!priv->allow_blinking)
 168                i = IWL_MAX_BLINK_TBL;
 169        else
 170                for (i = 0; i < IWL_MAX_BLINK_TBL; i++)
 171                        if (tpt > (blink_tbl[i].tpt * IWL_1MB_RATE))
 172                                break;
 173
 174        IWL_DEBUG_LED(priv, "LED BLINK IDX=%d\n", i);
 175        return i;
 176}
 177
 178/*
 179 * this function called from handler. Since setting Led command can
 180 * happen very frequent we postpone led command to be called from
 181 * REPLY handler so we know ucode is up
 182 */
 183void iwl_leds_background(struct iwl_priv *priv)
 184{
 185        u8 blink_idx;
 186
 187        if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
 188                priv->last_blink_time = 0;
 189                return;
 190        }
 191        if (iwl_is_rfkill(priv)) {
 192                priv->last_blink_time = 0;
 193                return;
 194        }
 195
 196        if (!priv->allow_blinking) {
 197                priv->last_blink_time = 0;
 198                if (priv->last_blink_rate != IWL_SOLID_BLINK_IDX) {
 199                        priv->last_blink_rate = IWL_SOLID_BLINK_IDX;
 200                        iwl_led_pattern(priv, IWL_SOLID_BLINK_IDX);
 201                }
 202                return;
 203        }
 204        if (!priv->last_blink_time ||
 205            !time_after(jiffies, priv->last_blink_time +
 206                        msecs_to_jiffies(1000)))
 207                return;
 208
 209        blink_idx = iwl_get_blink_rate(priv);
 210
 211        /* call only if blink rate change */
 212        if (blink_idx != priv->last_blink_rate)
 213                iwl_led_pattern(priv, blink_idx);
 214
 215        priv->last_blink_time = jiffies;
 216        priv->last_blink_rate = blink_idx;
 217}
 218EXPORT_SYMBOL(iwl_leds_background);
 219
 220void iwl_leds_init(struct iwl_priv *priv)
 221{
 222        priv->last_blink_rate = 0;
 223        priv->last_blink_time = 0;
 224        priv->allow_blinking = 0;
 225        if (led_mode != IWL_LED_DEFAULT &&
 226            led_mode != priv->cfg->led_mode)
 227                priv->cfg->led_mode = led_mode;
 228}
 229EXPORT_SYMBOL(iwl_leds_init);
 230