Commit e932a609 authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville
Browse files

iwlwifi: LED cleanup



The iwlwifi drivers have LED blinking requirements that
mac80211 cannot fulfill due to the use of just a single
LED instead of different ones for TX, RX, radio etc.
Instead, the single LED blinks according to transfers
and is solid on the rest of the time. As such, having
LED class devices registered that mac80211 triggers are
connected to is pointless as we don't use the triggers
anyway.

Remove all the useless code and add hooks into the
driver itself. At the same time, make the LED code
abstracted so the core code that determines blink rate
etc. can be shared between 3945 and agn in iwlcore.

At the same time, the fact that we removed the use of
the mac80211 LED triggers means we can also remove the
IWLWIFI_LEDS Kconfig symbol since the LED support is
now self-contained.

Signed-off-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarReinette Chatre <reinette.chatre@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent be1a71a1
Loading
Loading
Loading
Loading
+0 −9
Original line number Diff line number Diff line
@@ -2,15 +2,6 @@ config IWLWIFI
	tristate "Intel Wireless Wifi"
	depends on PCI && MAC80211 && WLAN_80211 && EXPERIMENTAL
	select FW_LOADER
	select MAC80211_LEDS if IWLWIFI_LEDS
	select LEDS_CLASS if IWLWIFI_LEDS

config IWLWIFI_LEDS
	bool "Enable LED support in iwlagn and iwl3945 drivers"
	depends on IWLWIFI
	default y
	---help---
	  Select this if you want LED support.

config IWLWIFI_SPECTRUM_MEASUREMENT
	bool "Enable Spectrum Measurement in iwlagn driver"
+2 −3
Original line number Diff line number Diff line
obj-$(CONFIG_IWLWIFI)	+= iwlcore.o
iwlcore-objs 		:= iwl-core.o iwl-eeprom.o iwl-hcmd.o iwl-power.o
iwlcore-objs 		+= iwl-rx.o iwl-tx.o iwl-sta.o iwl-calib.o
iwlcore-objs 		+= iwl-scan.o
iwlcore-objs 		+= iwl-scan.o iwl-led.o
iwlcore-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o
iwlcore-$(CONFIG_IWLWIFI_LEDS) += iwl-led.o
iwlcore-$(CONFIG_IWLWIFI_SPECTRUM_MEASUREMENT) += iwl-spectrum.o
iwlcore-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o

@@ -11,7 +10,7 @@ CFLAGS_iwl-devtrace.o := -I$(src)

# AGN
obj-$(CONFIG_IWLAGN)	+= iwlagn.o
iwlagn-objs		:= iwl-agn.o iwl-agn-rs.o
iwlagn-objs		:= iwl-agn.o iwl-agn-rs.o iwl-agn-led.o

iwlagn-$(CONFIG_IWL4965) += iwl-4965.o
iwlagn-$(CONFIG_IWL5000) += iwl-5000.o
+2 −0
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@
#include "iwl-sta.h"
#include "iwl-helpers.h"
#include "iwl-5000-hw.h"
#include "iwl-agn-led.h"

/* Highest firmware API version supported */
#define IWL1000_UCODE_API_MAX 3
@@ -145,6 +146,7 @@ static struct iwl_ops iwl1000_ops = {
	.lib = &iwl1000_lib,
	.hcmd = &iwl5000_hcmd,
	.utils = &iwl5000_hcmd_utils,
	.led = &iwlagn_led_ops,
};

struct iwl_cfg iwl1000_bgn_cfg = {
+16 −355
Original line number Diff line number Diff line
@@ -24,8 +24,6 @@
 *
 *****************************************************************************/

#ifdef CONFIG_IWLWIFI_LEDS

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -43,55 +41,11 @@
#include "iwl-3945.h"
#include "iwl-core.h"
#include "iwl-dev.h"
#include "iwl-3945-led.h"

#ifdef CONFIG_IWLWIFI_DEBUG
static const char *led_type_str[] = {
	__stringify(IWL_LED_TRG_TX),
	__stringify(IWL_LED_TRG_RX),
	__stringify(IWL_LED_TRG_ASSOC),
	__stringify(IWL_LED_TRG_RADIO),
	NULL
};
#endif /* CONFIG_IWLWIFI_DEBUG */

static const struct {
	u16 brightness;
	u8 on_time;
	u8 off_time;
} blink_tbl[] =
{
	{300, 25, 25},
	{200, 40, 40},
	{100, 55, 55},
	{70, 65, 65},
	{50, 75, 75},
	{20, 85, 85},
	{15, 95, 95 },
	{10, 110, 110},
	{5, 130, 130},
	{0, 167, 167},
	/* SOLID_ON */
	{-1, IWL_LED_SOLID, 0}
};

#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)

static void iwl3945_led_cmd_callback(struct iwl_priv *priv,
				     struct iwl_device_cmd *cmd,
				     struct sk_buff *skb)
{
}

static inline int iwl3945_brightness_to_idx(enum led_brightness brightness)
{
	return fls(0x000000FF & (u32)brightness);
}

/* Send led command */
static int iwl_send_led_cmd(struct iwl_priv *priv,
static int iwl3945_send_led_cmd(struct iwl_priv *priv,
				struct iwl_led_cmd *led_cmd)
{
	struct iwl_host_cmd cmd = {
@@ -99,332 +53,39 @@ static int iwl_send_led_cmd(struct iwl_priv *priv,
		.len = sizeof(struct iwl_led_cmd),
		.data = led_cmd,
		.flags = CMD_ASYNC,
		.callback = iwl3945_led_cmd_callback,
		.callback = NULL,
	};

	return iwl_send_cmd(priv, &cmd);
}



/* Set led on command */
static int iwl3945_led_pattern(struct iwl_priv *priv, int led_id,
			       unsigned int idx)
static int iwl3945_led_on(struct iwl_priv *priv)
{
	struct iwl_led_cmd led_cmd = {
		.id = led_id,
		.interval = IWL_DEF_LED_INTRVL
	};

	BUG_ON(idx > IWL_MAX_BLINK_TBL);

	led_cmd.on = blink_tbl[idx].on_time;
	led_cmd.off = blink_tbl[idx].off_time;

	return iwl_send_led_cmd(priv, &led_cmd);
}


/* Set led on command */
static int iwl3945_led_on(struct iwl_priv *priv, int led_id)
{
	struct iwl_led_cmd led_cmd = {
		.id = led_id,
		.id = IWL_LED_LINK,
		.on = IWL_LED_SOLID,
		.off = 0,
		.interval = IWL_DEF_LED_INTRVL
	};
	return iwl_send_led_cmd(priv, &led_cmd);
	return iwl3945_send_led_cmd(priv, &led_cmd);
}

/* Set led off command */
static int iwl3945_led_off(struct iwl_priv *priv, int led_id)
static int iwl3945_led_off(struct iwl_priv *priv)
{
	struct iwl_led_cmd led_cmd = {
		.id = led_id,
		.id = IWL_LED_LINK,
		.on = 0,
		.off = 0,
		.interval = IWL_DEF_LED_INTRVL
	};
	IWL_DEBUG_LED(priv, "led off %d\n", led_id);
	return iwl_send_led_cmd(priv, &led_cmd);
}

/*
 *  Set led on in case of association
 *  */
static int iwl3945_led_associate(struct iwl_priv *priv, int led_id)
{
	IWL_DEBUG_LED(priv, "Associated\n");

	priv->allow_blinking = 1;
	return iwl3945_led_on(priv, led_id);
}
/* Set Led off in case of disassociation */
static int iwl3945_led_disassociate(struct iwl_priv *priv, int led_id)
{
	IWL_DEBUG_LED(priv, "Disassociated\n");

	priv->allow_blinking = 0;

	return 0;
}

/*
 * brightness call back function for Tx/Rx LED
 */
static int iwl3945_led_associated(struct iwl_priv *priv, int led_id)
{
	if (test_bit(STATUS_EXIT_PENDING, &priv->status) ||
	    !test_bit(STATUS_READY, &priv->status))
		return 0;


	/* start counting Tx/Rx bytes */
	if (!priv->last_blink_time && priv->allow_blinking)
		priv->last_blink_time = jiffies;
	return 0;
}

/*
 * brightness call back for association and radio
 */
static void iwl3945_led_brightness_set(struct led_classdev *led_cdev,
				enum led_brightness brightness)
{
	struct iwl_led *led = container_of(led_cdev,
					   struct iwl_led, led_dev);
	struct iwl_priv *priv = led->priv;

	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
		return;

	IWL_DEBUG_LED(priv, "Led type = %s brightness = %d\n",
			led_type_str[led->type], brightness);

	switch (brightness) {
	case LED_FULL:
		if (led->led_on)
			led->led_on(priv, IWL_LED_LINK);
		break;
	case LED_OFF:
		if (led->led_off)
			led->led_off(priv, IWL_LED_LINK);
		break;
	default:
		if (led->led_pattern) {
			int idx = iwl3945_brightness_to_idx(brightness);
			led->led_pattern(priv, IWL_LED_LINK, idx);
		}
		break;
	}
}

/*
 * Register led class with the system
 */
static int iwl3945_led_register_led(struct iwl_priv *priv,
				   struct iwl_led *led,
				   enum led_type type, u8 set_led,
				   char *trigger)
{
	struct device *device = wiphy_dev(priv->hw->wiphy);
	int ret;

	led->led_dev.name = led->name;
	led->led_dev.brightness_set = iwl3945_led_brightness_set;
	led->led_dev.default_trigger = trigger;

	led->priv = priv;
	led->type = type;

	ret = led_classdev_register(device, &led->led_dev);
	if (ret) {
		IWL_ERR(priv, "Error: failed to register led handler.\n");
		return ret;
	}

	led->registered = 1;

	if (set_led && led->led_on)
		led->led_on(priv, IWL_LED_LINK);
	return 0;
}


/*
 * calculate blink rate according to last 2 sec Tx/Rx activities
 */
static inline u8 get_blink_rate(struct iwl_priv *priv)
{
	int index;
	s64 tpt = priv->rxtxpackets;

	if (tpt < 0)
		tpt = -tpt;

	IWL_DEBUG_LED(priv, "tpt %lld \n", (long long)tpt);

	if (!priv->allow_blinking)
		index = IWL_MAX_BLINK_TBL;
	else
		for (index = 0; index < IWL_MAX_BLINK_TBL; index++)
			if (tpt > (blink_tbl[index].brightness * IWL_1MB_RATE))
				break;

	IWL_DEBUG_LED(priv, "LED BLINK IDX=%d\n", index);
	return index;
}

/*
 * 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 iwl3945_led_background(struct iwl_priv *priv)
{
	u8 blink_idx;

	if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
		priv->last_blink_time = 0;
		return;
	}
	if (iwl_is_rfkill(priv)) {
		priv->last_blink_time = 0;
		return;
	}

	if (!priv->allow_blinking) {
		priv->last_blink_time = 0;
		if (priv->last_blink_rate != IWL_SOLID_BLINK_IDX) {
			priv->last_blink_rate = IWL_SOLID_BLINK_IDX;
			iwl3945_led_pattern(priv, IWL_LED_LINK,
					    IWL_SOLID_BLINK_IDX);
		}
		return;
	}
	if (!priv->last_blink_time ||
	    !time_after(jiffies, priv->last_blink_time +
			msecs_to_jiffies(1000)))
		return;

	blink_idx = get_blink_rate(priv);

	/* call only if blink rate change */
	if (blink_idx != priv->last_blink_rate)
		iwl3945_led_pattern(priv, IWL_LED_LINK, blink_idx);

	priv->last_blink_time = jiffies;
	priv->last_blink_rate = blink_idx;
	priv->rxtxpackets = 0;
}


/* Register all led handler */
int iwl3945_led_register(struct iwl_priv *priv)
{
	char *trigger;
	int ret;

	priv->last_blink_rate = 0;
	priv->rxtxpackets = 0;
	priv->led_tpt = 0;
	priv->last_blink_time = 0;
	priv->allow_blinking = 0;

	trigger = ieee80211_get_radio_led_name(priv->hw);
	snprintf(priv->led[IWL_LED_TRG_RADIO].name,
		 sizeof(priv->led[IWL_LED_TRG_RADIO].name), "iwl-%s::radio",
		 wiphy_name(priv->hw->wiphy));

	priv->led[IWL_LED_TRG_RADIO].led_on = iwl3945_led_on;
	priv->led[IWL_LED_TRG_RADIO].led_off = iwl3945_led_off;
	priv->led[IWL_LED_TRG_RADIO].led_pattern = NULL;

	ret = iwl3945_led_register_led(priv,
				   &priv->led[IWL_LED_TRG_RADIO],
				   IWL_LED_TRG_RADIO, 1, trigger);

	if (ret)
		goto exit_fail;

	trigger = ieee80211_get_assoc_led_name(priv->hw);
	snprintf(priv->led[IWL_LED_TRG_ASSOC].name,
		 sizeof(priv->led[IWL_LED_TRG_ASSOC].name), "iwl-%s::assoc",
		 wiphy_name(priv->hw->wiphy));

	ret = iwl3945_led_register_led(priv,
				   &priv->led[IWL_LED_TRG_ASSOC],
				   IWL_LED_TRG_ASSOC, 0, trigger);

	/* for assoc always turn led on */
	priv->led[IWL_LED_TRG_ASSOC].led_on = iwl3945_led_associate;
	priv->led[IWL_LED_TRG_ASSOC].led_off = iwl3945_led_disassociate;
	priv->led[IWL_LED_TRG_ASSOC].led_pattern = NULL;

	if (ret)
		goto exit_fail;

	trigger = ieee80211_get_rx_led_name(priv->hw);
	snprintf(priv->led[IWL_LED_TRG_RX].name,
		 sizeof(priv->led[IWL_LED_TRG_RX].name), "iwl-%s::RX",
		 wiphy_name(priv->hw->wiphy));

	ret = iwl3945_led_register_led(priv,
				   &priv->led[IWL_LED_TRG_RX],
				   IWL_LED_TRG_RX, 0, trigger);

	priv->led[IWL_LED_TRG_RX].led_on = iwl3945_led_associated;
	priv->led[IWL_LED_TRG_RX].led_off = iwl3945_led_associated;
	priv->led[IWL_LED_TRG_RX].led_pattern = iwl3945_led_pattern;

	if (ret)
		goto exit_fail;

	trigger = ieee80211_get_tx_led_name(priv->hw);
	snprintf(priv->led[IWL_LED_TRG_TX].name,
		 sizeof(priv->led[IWL_LED_TRG_TX].name), "iwl-%s::TX",
		 wiphy_name(priv->hw->wiphy));

	ret = iwl3945_led_register_led(priv,
				   &priv->led[IWL_LED_TRG_TX],
				   IWL_LED_TRG_TX, 0, trigger);

	priv->led[IWL_LED_TRG_TX].led_on = iwl3945_led_associated;
	priv->led[IWL_LED_TRG_TX].led_off = iwl3945_led_associated;
	priv->led[IWL_LED_TRG_TX].led_pattern = iwl3945_led_pattern;

	if (ret)
		goto exit_fail;

	return 0;

exit_fail:
	iwl3945_led_unregister(priv);
	return ret;
	IWL_DEBUG_LED(priv, "led off\n");
	return iwl3945_send_led_cmd(priv, &led_cmd);
}


/* unregister led class */
static void iwl3945_led_unregister_led(struct iwl_led *led, u8 set_led)
{
	if (!led->registered)
		return;

	led_classdev_unregister(&led->led_dev);

	if (set_led)
		led->led_dev.brightness_set(&led->led_dev, LED_OFF);
	led->registered = 0;
}

/* Unregister all led handlers */
void iwl3945_led_unregister(struct iwl_priv *priv)
{
	iwl3945_led_unregister_led(&priv->led[IWL_LED_TRG_ASSOC], 0);
	iwl3945_led_unregister_led(&priv->led[IWL_LED_TRG_RX], 0);
	iwl3945_led_unregister_led(&priv->led[IWL_LED_TRG_TX], 0);
	iwl3945_led_unregister_led(&priv->led[IWL_LED_TRG_RADIO], 1);
}

#endif
const struct iwl_led_ops iwl3945_led_ops = {
	.cmd = iwl3945_send_led_cmd,
	.on = iwl3945_led_on,
	.off = iwl3945_led_off,
};
+4 −18
Original line number Diff line number Diff line
@@ -24,23 +24,9 @@
 *
 *****************************************************************************/

#ifndef IWL3945_LEDS_H
#define IWL3945_LEDS_H
#ifndef __iwl_3945_led_h__
#define __iwl_3945_led_h__

struct iwl_priv;
extern const struct iwl_led_ops iwl3945_led_ops;

#ifdef CONFIG_IWLWIFI_LEDS

#include "iwl-led.h"

extern int iwl3945_led_register(struct iwl_priv *priv);
extern void iwl3945_led_unregister(struct iwl_priv *priv);
extern void iwl3945_led_background(struct iwl_priv *priv);

#else
static inline int iwl3945_led_register(struct iwl_priv *priv) { return 0; }
static inline void iwl3945_led_unregister(struct iwl_priv *priv) {}
static inline void iwl3945_led_background(struct iwl_priv *priv) {}

#endif /* IWLWIFI_LEDS*/
#endif /* IWL3945_LEDS_H */
#endif /* __iwl_3945_led_h__ */
Loading