2008-03-26 07:33:36 +08:00
|
|
|
/******************************************************************************
|
|
|
|
*
|
2010-01-16 05:43:41 +08:00
|
|
|
* Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved.
|
2008-03-26 07:33:36 +08:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of version 2 of the GNU General Public License as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
* more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along with
|
|
|
|
* this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
|
|
|
|
*
|
|
|
|
* The full GNU General Public License is included in this distribution in the
|
|
|
|
* file called LICENSE.
|
|
|
|
*
|
|
|
|
* Contact Information:
|
2008-12-10 03:28:58 +08:00
|
|
|
* Intel Linux Wireless <ilw@linux.intel.com>
|
2008-03-26 07:33:36 +08:00
|
|
|
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/pci.h>
|
|
|
|
#include <linux/dma-mapping.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
#include <linux/wireless.h>
|
|
|
|
#include <net/mac80211.h>
|
|
|
|
#include <linux/etherdevice.h>
|
|
|
|
#include <asm/unaligned.h>
|
|
|
|
|
2008-04-25 02:55:38 +08:00
|
|
|
#include "iwl-dev.h"
|
2008-03-26 07:33:36 +08:00
|
|
|
#include "iwl-core.h"
|
2008-04-04 07:05:21 +08:00
|
|
|
#include "iwl-io.h"
|
2008-03-26 07:33:36 +08:00
|
|
|
|
2009-09-18 01:43:54 +08:00
|
|
|
/* default: IWL_LED_BLINK(0) using blinking index table */
|
|
|
|
static int led_mode;
|
|
|
|
module_param(led_mode, int, S_IRUGO);
|
|
|
|
MODULE_PARM_DESC(led_mode, "led mode: 0=blinking, 1=On(RF On)/Off(RF Off), "
|
2010-03-19 06:37:49 +08:00
|
|
|
"(default 0)");
|
2009-09-18 01:43:54 +08:00
|
|
|
|
2008-07-11 11:53:36 +08:00
|
|
|
|
2008-03-26 07:33:36 +08:00
|
|
|
static const struct {
|
2009-07-18 00:30:20 +08:00
|
|
|
u16 tpt; /* Mb/s */
|
2008-03-26 07:33:36 +08:00
|
|
|
u8 on_time;
|
2008-07-11 11:53:36 +08:00
|
|
|
u8 off_time;
|
2008-03-26 07:33:36 +08:00
|
|
|
} blink_tbl[] =
|
|
|
|
{
|
|
|
|
{300, 25, 25},
|
|
|
|
{200, 40, 40},
|
|
|
|
{100, 55, 55},
|
|
|
|
{70, 65, 65},
|
|
|
|
{50, 75, 75},
|
|
|
|
{20, 85, 85},
|
2009-09-12 01:38:07 +08:00
|
|
|
{10, 95, 95},
|
|
|
|
{5, 110, 110},
|
|
|
|
{1, 130, 130},
|
2008-07-11 11:53:37 +08:00
|
|
|
{0, 167, 167},
|
2009-10-03 04:44:03 +08:00
|
|
|
/* SOLID_ON */
|
2008-07-11 11:53:37 +08:00
|
|
|
{-1, IWL_LED_SOLID, 0}
|
2008-03-26 07:33:36 +08:00
|
|
|
};
|
|
|
|
|
2008-07-11 11:53:37 +08:00
|
|
|
#define IWL_1MB_RATE (128 * 1024)
|
|
|
|
#define IWL_LED_THRESHOLD (16)
|
|
|
|
#define IWL_MAX_BLINK_TBL (ARRAY_SIZE(blink_tbl) - 1) /* exclude SOLID_ON */
|
|
|
|
#define IWL_SOLID_BLINK_IDX (ARRAY_SIZE(blink_tbl) - 1)
|
|
|
|
|
2009-09-12 01:38:14 +08:00
|
|
|
/*
|
|
|
|
* Adjust led blink rate to compensate on a MAC Clock difference on every HW
|
|
|
|
* Led blink rate analysis showed an average deviation of 0% on 3945,
|
|
|
|
* 5% on 4965 HW and 20% on 5000 series and up.
|
|
|
|
* Need to compensate on the led on/off time per HW according to the deviation
|
|
|
|
* to achieve the desired led frequency
|
|
|
|
* The calculation is: (100-averageDeviation)/100 * blinkTime
|
|
|
|
* For code efficiency the calculation will be:
|
|
|
|
* compensation = (100 - averageDeviation) * 64 / 100
|
|
|
|
* NewBlinkTime = (compensation * BlinkTime) / 64
|
|
|
|
*/
|
|
|
|
static inline u8 iwl_blink_compensation(struct iwl_priv *priv,
|
|
|
|
u8 time, u16 compensation)
|
|
|
|
{
|
|
|
|
if (!compensation) {
|
|
|
|
IWL_ERR(priv, "undefined blink compensation: "
|
|
|
|
"use pre-defined blinking time\n");
|
|
|
|
return time;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (u8)((time * compensation) >> 6);
|
|
|
|
}
|
|
|
|
|
2008-07-11 11:53:37 +08:00
|
|
|
/* Set led pattern command */
|
2009-10-03 04:44:03 +08:00
|
|
|
static int iwl_led_pattern(struct iwl_priv *priv, unsigned int idx)
|
2008-03-26 07:33:36 +08:00
|
|
|
{
|
2008-07-11 11:53:37 +08:00
|
|
|
struct iwl_led_cmd led_cmd = {
|
2009-10-03 04:44:03 +08:00
|
|
|
.id = IWL_LED_LINK,
|
2008-03-26 07:33:36 +08:00
|
|
|
.interval = IWL_DEF_LED_INTRVL
|
|
|
|
};
|
|
|
|
|
2008-07-11 11:53:37 +08:00
|
|
|
BUG_ON(idx > IWL_MAX_BLINK_TBL);
|
|
|
|
|
2009-09-12 01:38:14 +08:00
|
|
|
IWL_DEBUG_LED(priv, "Led blink time compensation= %u\n",
|
|
|
|
priv->cfg->led_compensation);
|
|
|
|
led_cmd.on =
|
|
|
|
iwl_blink_compensation(priv, blink_tbl[idx].on_time,
|
|
|
|
priv->cfg->led_compensation);
|
|
|
|
led_cmd.off =
|
|
|
|
iwl_blink_compensation(priv, blink_tbl[idx].off_time,
|
|
|
|
priv->cfg->led_compensation);
|
2008-07-11 11:53:37 +08:00
|
|
|
|
2009-10-03 04:44:03 +08:00
|
|
|
return priv->cfg->ops->led->cmd(priv, &led_cmd);
|
2008-03-26 07:33:36 +08:00
|
|
|
}
|
|
|
|
|
2009-10-03 04:44:03 +08:00
|
|
|
int iwl_led_start(struct iwl_priv *priv)
|
2008-03-26 07:33:36 +08:00
|
|
|
{
|
2009-10-03 04:44:03 +08:00
|
|
|
return priv->cfg->ops->led->on(priv);
|
2008-03-26 07:33:36 +08:00
|
|
|
}
|
2009-10-03 04:44:03 +08:00
|
|
|
EXPORT_SYMBOL(iwl_led_start);
|
2008-03-26 07:33:36 +08:00
|
|
|
|
2009-10-03 04:44:03 +08:00
|
|
|
int iwl_led_associate(struct iwl_priv *priv)
|
2008-07-18 13:53:07 +08:00
|
|
|
{
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_LED(priv, "Associated\n");
|
2009-09-18 01:43:54 +08:00
|
|
|
if (led_mode == IWL_LED_BLINK)
|
|
|
|
priv->allow_blinking = 1;
|
2009-10-03 04:44:03 +08:00
|
|
|
priv->last_blink_time = jiffies;
|
2008-03-26 07:33:36 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-10-03 04:44:03 +08:00
|
|
|
int iwl_led_disassociate(struct iwl_priv *priv)
|
2008-03-26 07:33:36 +08:00
|
|
|
{
|
2009-10-03 04:44:03 +08:00
|
|
|
priv->allow_blinking = 0;
|
2008-03-29 07:21:11 +08:00
|
|
|
|
2008-03-26 07:33:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-07-18 00:30:20 +08:00
|
|
|
* calculate blink rate according to last second Tx/Rx activities
|
2008-03-26 07:33:36 +08:00
|
|
|
*/
|
2008-07-11 11:53:37 +08:00
|
|
|
static int iwl_get_blink_rate(struct iwl_priv *priv)
|
2008-03-26 07:33:36 +08:00
|
|
|
{
|
|
|
|
int i;
|
2009-07-18 00:30:20 +08:00
|
|
|
/* count both tx and rx traffic to be able to
|
|
|
|
* handle traffic in either direction
|
|
|
|
*/
|
2009-08-08 06:41:40 +08:00
|
|
|
u64 current_tpt = priv->tx_stats.data_bytes +
|
|
|
|
priv->rx_stats.data_bytes;
|
2008-03-26 07:33:36 +08:00
|
|
|
s64 tpt = current_tpt - priv->led_tpt;
|
|
|
|
|
2008-10-24 14:48:56 +08:00
|
|
|
if (tpt < 0) /* wraparound */
|
2008-03-26 07:33:36 +08:00
|
|
|
tpt = -tpt;
|
|
|
|
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_LED(priv, "tpt %lld current_tpt %llu\n",
|
2008-07-23 14:50:04 +08:00
|
|
|
(long long)tpt,
|
|
|
|
(unsigned long long)current_tpt);
|
2008-03-26 07:33:36 +08:00
|
|
|
priv->led_tpt = current_tpt;
|
|
|
|
|
2008-07-11 11:53:37 +08:00
|
|
|
if (!priv->allow_blinking)
|
2008-03-26 07:33:36 +08:00
|
|
|
i = IWL_MAX_BLINK_TBL;
|
2008-07-11 11:53:37 +08:00
|
|
|
else
|
2008-03-26 07:33:36 +08:00
|
|
|
for (i = 0; i < IWL_MAX_BLINK_TBL; i++)
|
2009-10-03 04:44:03 +08:00
|
|
|
if (tpt > (blink_tbl[i].tpt * IWL_1MB_RATE))
|
2008-03-26 07:33:36 +08:00
|
|
|
break;
|
|
|
|
|
2009-01-28 06:27:56 +08:00
|
|
|
IWL_DEBUG_LED(priv, "LED BLINK IDX=%d\n", i);
|
2008-07-11 11:53:37 +08:00
|
|
|
return i;
|
2008-03-26 07:33:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* this function called from handler. Since setting Led command can
|
|
|
|
* happen very frequent we postpone led command to be called from
|
|
|
|
* REPLY handler so we know ucode is up
|
|
|
|
*/
|
|
|
|
void iwl_leds_background(struct iwl_priv *priv)
|
|
|
|
{
|
2008-07-11 11:53:37 +08:00
|
|
|
u8 blink_idx;
|
2008-03-26 07:33:36 +08:00
|
|
|
|
|
|
|
if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
|
|
|
|
priv->last_blink_time = 0;
|
|
|
|
return;
|
|
|
|
}
|
2008-07-18 13:53:07 +08:00
|
|
|
if (iwl_is_rfkill(priv)) {
|
2008-03-26 07:33:36 +08:00
|
|
|
priv->last_blink_time = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!priv->allow_blinking) {
|
|
|
|
priv->last_blink_time = 0;
|
2008-07-11 11:53:37 +08:00
|
|
|
if (priv->last_blink_rate != IWL_SOLID_BLINK_IDX) {
|
|
|
|
priv->last_blink_rate = IWL_SOLID_BLINK_IDX;
|
2009-10-03 04:44:03 +08:00
|
|
|
iwl_led_pattern(priv, IWL_SOLID_BLINK_IDX);
|
2008-03-26 07:33:36 +08:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!priv->last_blink_time ||
|
|
|
|
!time_after(jiffies, priv->last_blink_time +
|
|
|
|
msecs_to_jiffies(1000)))
|
|
|
|
return;
|
|
|
|
|
2008-07-11 11:53:37 +08:00
|
|
|
blink_idx = iwl_get_blink_rate(priv);
|
2008-03-26 07:33:36 +08:00
|
|
|
|
|
|
|
/* call only if blink rate change */
|
2008-07-11 11:53:37 +08:00
|
|
|
if (blink_idx != priv->last_blink_rate)
|
2009-10-03 04:44:03 +08:00
|
|
|
iwl_led_pattern(priv, blink_idx);
|
2008-03-26 07:33:36 +08:00
|
|
|
|
2008-07-11 11:53:36 +08:00
|
|
|
priv->last_blink_time = jiffies;
|
2008-07-11 11:53:37 +08:00
|
|
|
priv->last_blink_rate = blink_idx;
|
2008-03-26 07:33:36 +08:00
|
|
|
}
|
2009-10-03 04:44:03 +08:00
|
|
|
EXPORT_SYMBOL(iwl_leds_background);
|
2008-03-26 07:33:36 +08:00
|
|
|
|
2009-10-03 04:44:03 +08:00
|
|
|
void iwl_leds_init(struct iwl_priv *priv)
|
2008-03-26 07:33:36 +08:00
|
|
|
{
|
|
|
|
priv->last_blink_rate = 0;
|
|
|
|
priv->last_blink_time = 0;
|
|
|
|
priv->allow_blinking = 0;
|
|
|
|
}
|
2009-10-03 04:44:03 +08:00
|
|
|
EXPORT_SYMBOL(iwl_leds_init);
|