/******************************************************************************
 * rtl8712_led.c
 *
 * Copyright(c) 2007 - 2010  Realtek Corporation. All rights reserved.
 * Linux device driver for RTL8192SU
 *
 * 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
 *
 * Modifications for inclusion into the Linux staging tree are
 * Copyright(c) 2010 Larry Finger. All rights reserved.
 *
 * Contact information:
 * WLAN FAE <wlanfae@realtek.com>
 * Larry Finger <Larry.Finger@lwfinger.net>
 *
 ******************************************************************************/

#include "drv_types.h"

/*===========================================================================
 *	Constant.
 *===========================================================================

 *
 * Default LED behavior.
 */
#define LED_BLINK_NORMAL_INTERVAL	100
#define LED_BLINK_SLOWLY_INTERVAL	200
#define LED_BLINK_LONG_INTERVAL	400

#define LED_BLINK_NO_LINK_INTERVAL_ALPHA	1000
#define LED_BLINK_LINK_INTERVAL_ALPHA		500
#define LED_BLINK_SCAN_INTERVAL_ALPHA		180
#define LED_BLINK_FASTER_INTERVAL_ALPHA		50
#define LED_BLINK_WPS_SUCESS_INTERVAL_ALPHA	5000

/*===========================================================================
 * LED object.
 *===========================================================================
 */
enum _LED_STATE_871x{
	LED_UNKNOWN = 0,
	LED_ON = 1,
	LED_OFF = 2,
	LED_BLINK_NORMAL = 3,
	LED_BLINK_SLOWLY = 4,
	LED_POWER_ON_BLINK = 5,
	LED_SCAN_BLINK = 6, /* LED is blinking during scanning period,
			     * the # of times to blink is depend on time
			     * for scanning. */
	LED_NO_LINK_BLINK = 7, /* LED is blinking during no link state. */
	LED_BLINK_StartToBlink = 8,/* Customzied for Sercomm Printer
				    * Server case */
	LED_BLINK_WPS = 9,	/* LED is blinkg during WPS communication */
	LED_TXRX_BLINK = 10,
	LED_BLINK_WPS_STOP = 11,	/*for ALPHA */
	LED_BLINK_WPS_STOP_OVERLAP = 12,	/*for BELKIN */
};

/*===========================================================================
 *	Prototype of protected function.
 *===========================================================================
 */
static void BlinkTimerCallback(unsigned long data);

static void BlinkWorkItemCallback(struct work_struct *work);
/*===========================================================================
 * LED_819xUsb routines.
 *===========================================================================
 *
 *
 *
 *	Description:
 *		Initialize an LED_871x object.
 */
static void InitLed871x(struct _adapter *padapter, struct LED_871x *pLed,
		 enum LED_PIN_871x	LedPin)
{
	struct  net_device *nic;

	nic = padapter->pnetdev;
	pLed->padapter = padapter;
	pLed->LedPin = LedPin;
	pLed->CurrLedState = LED_OFF;
	pLed->bLedOn = false;
	pLed->bLedBlinkInProgress = false;
	pLed->BlinkTimes = 0;
	pLed->BlinkingLedState = LED_UNKNOWN;
	_init_timer(&(pLed->BlinkTimer), nic, BlinkTimerCallback, pLed);
	_init_workitem(&(pLed->BlinkWorkItem), BlinkWorkItemCallback, pLed);
}

/*
 *	Description:
 *		DeInitialize an LED_871x object.
 */
static void DeInitLed871x(struct LED_871x *pLed)
{
	_cancel_timer_ex(&(pLed->BlinkTimer));
	/* We should reset bLedBlinkInProgress if we cancel
	 * the LedControlTimer, */
	pLed->bLedBlinkInProgress = false;
}

/*
 *	Description:
 *		Turn on LED according to LedPin specified.
 */
static void SwLedOn(struct _adapter *padapter, struct LED_871x *pLed)
{
	u8	LedCfg;

	if ((padapter->bSurpriseRemoved == true) ||
	    (padapter->bDriverStopped == true))
		return;
	LedCfg = r8712_read8(padapter, LEDCFG);
	switch (pLed->LedPin) {
	case LED_PIN_GPIO0:
		break;
	case LED_PIN_LED0:
		/* SW control led0 on.*/
		r8712_write8(padapter, LEDCFG, LedCfg&0xf0);
		break;
	case LED_PIN_LED1:
		/* SW control led1 on.*/
		r8712_write8(padapter, LEDCFG, LedCfg&0x0f);
		break;
	default:
		break;
	}
	pLed->bLedOn = true;
}

/*
 *	Description:
 *		Turn off LED according to LedPin specified.
 */
static void SwLedOff(struct _adapter *padapter, struct LED_871x *pLed)
{
	u8	LedCfg;

	if ((padapter->bSurpriseRemoved == true) ||
	    (padapter->bDriverStopped == true))
		return;
	LedCfg = r8712_read8(padapter, LEDCFG);
	switch (pLed->LedPin) {
	case LED_PIN_GPIO0:
		break;
	case LED_PIN_LED0:
		LedCfg &= 0xf0; /* Set to software control.*/
		r8712_write8(padapter, LEDCFG, (LedCfg|BIT(3)));
		break;
	case LED_PIN_LED1:
		LedCfg &= 0x0f; /* Set to software control.*/
		r8712_write8(padapter, LEDCFG, (LedCfg|BIT(7)));
		break;
	default:
		break;
	}
	pLed->bLedOn = false;
}

/*===========================================================================
 * Interface to manipulate LED objects.
 *===========================================================================
 *
 *	Description:
 *		Initialize all LED_871x objects.
 */
void r8712_InitSwLeds(struct _adapter *padapter)
{
	struct led_priv	*pledpriv = &(padapter->ledpriv);

	pledpriv->LedControlHandler = LedControl871x;
	InitLed871x(padapter, &(pledpriv->SwLed0), LED_PIN_LED0);
	InitLed871x(padapter, &(pledpriv->SwLed1), LED_PIN_LED1);
}

/*	Description:
 *		DeInitialize all LED_819xUsb objects.
 */
void r8712_DeInitSwLeds(struct _adapter *padapter)
{
	struct led_priv	*ledpriv = &(padapter->ledpriv);

	DeInitLed871x(&(ledpriv->SwLed0));
	DeInitLed871x(&(ledpriv->SwLed1));
}

/*	Description:
 *		Implementation of LED blinking behavior.
 *		It toggle off LED and schedule corresponding timer if necessary.
 */
static void SwLedBlink(struct LED_871x *pLed)
{
	struct _adapter *padapter = pLed->padapter;
	struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
	u8 bStopBlinking = false;

	/* Change LED according to BlinkingLedState specified. */
	if (pLed->BlinkingLedState == LED_ON)
		SwLedOn(padapter, pLed);
	else
		SwLedOff(padapter, pLed);
	/* Determine if we shall change LED state again. */
	pLed->BlinkTimes--;
	switch (pLed->CurrLedState) {
	case LED_BLINK_NORMAL:
		if (pLed->BlinkTimes == 0)
			bStopBlinking = true;
		break;
	case LED_BLINK_StartToBlink:
		if ((check_fwstate(pmlmepriv, _FW_LINKED) == true) &&
		    (pmlmepriv->fw_state & WIFI_STATION_STATE))
			bStopBlinking = true;
		if ((check_fwstate(pmlmepriv, _FW_LINKED) == true) &&
		   ((pmlmepriv->fw_state & WIFI_ADHOC_STATE) ||
		    (pmlmepriv->fw_state & WIFI_ADHOC_MASTER_STATE)))
			bStopBlinking = true;
		else if (pLed->BlinkTimes == 0)
			bStopBlinking = true;
		break;
	case LED_BLINK_WPS:
		if (pLed->BlinkTimes == 0)
			bStopBlinking = true;
		break;
	default:
		bStopBlinking = true;
		break;
	}
	if (bStopBlinking) {
		if ((check_fwstate(pmlmepriv, _FW_LINKED) == true) &&
		    (pLed->bLedOn == false))
			SwLedOn(padapter, pLed);
		else if ((check_fwstate(pmlmepriv, _FW_LINKED) ==
			 true) &&  pLed->bLedOn == true)
			SwLedOff(padapter, pLed);
		pLed->BlinkTimes = 0;
		pLed->bLedBlinkInProgress = false;
	} else {
		/* Assign LED state to toggle. */
		if (pLed->BlinkingLedState == LED_ON)
			pLed->BlinkingLedState = LED_OFF;
		else
			pLed->BlinkingLedState = LED_ON;

		/* Schedule a timer to toggle LED state. */
		switch (pLed->CurrLedState) {
		case LED_BLINK_NORMAL:
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_NORMAL_INTERVAL);
			break;
		case LED_BLINK_SLOWLY:
		case LED_BLINK_StartToBlink:
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_SLOWLY_INTERVAL);
			break;
		case LED_BLINK_WPS:
			if (pLed->BlinkingLedState == LED_ON)
				_set_timer(&(pLed->BlinkTimer),
					   LED_BLINK_LONG_INTERVAL);
			else
				_set_timer(&(pLed->BlinkTimer),
					   LED_BLINK_LONG_INTERVAL);
			break;
		default:
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_SLOWLY_INTERVAL);
			break;
		}
	}
}

static void SwLedBlink1(struct LED_871x *pLed)
{
	struct _adapter *padapter = pLed->padapter;
	struct led_priv *ledpriv = &(padapter->ledpriv);
	struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
	struct eeprom_priv *peeprompriv = &(padapter->eeprompriv);
	struct LED_871x *pLed1 = &(ledpriv->SwLed1);
	u8 bStopBlinking = false;

	if (peeprompriv->CustomerID == RT_CID_819x_CAMEO)
		pLed = &(ledpriv->SwLed1);
	/* Change LED according to BlinkingLedState specified. */
	if (pLed->BlinkingLedState == LED_ON)
		SwLedOn(padapter, pLed);
	else
		SwLedOff(padapter, pLed);
	if (peeprompriv->CustomerID == RT_CID_DEFAULT) {
		if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
			if (!pLed1->bSWLedCtrl) {
				SwLedOn(padapter, pLed1);
				pLed1->bSWLedCtrl = true;
			} else if (!pLed1->bLedOn)
				SwLedOn(padapter, pLed1);
		} else {
			if (!pLed1->bSWLedCtrl) {
				SwLedOff(padapter, pLed1);
				pLed1->bSWLedCtrl = true;
			} else if (pLed1->bLedOn)
				SwLedOff(padapter, pLed1);
		}
	}
	switch (pLed->CurrLedState) {
	case LED_BLINK_SLOWLY:
		if (pLed->bLedOn)
			pLed->BlinkingLedState = LED_OFF;
		else
			pLed->BlinkingLedState = LED_ON;
		_set_timer(&(pLed->BlinkTimer),
			   LED_BLINK_NO_LINK_INTERVAL_ALPHA);
		break;
	case LED_BLINK_NORMAL:
		if (pLed->bLedOn)
			pLed->BlinkingLedState = LED_OFF;
		else
			pLed->BlinkingLedState = LED_ON;
		_set_timer(&(pLed->BlinkTimer),
			   LED_BLINK_LINK_INTERVAL_ALPHA);
		break;
	case LED_SCAN_BLINK:
		pLed->BlinkTimes--;
		if (pLed->BlinkTimes == 0)
			bStopBlinking = true;
		if (bStopBlinking) {
			if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
				pLed->bLedLinkBlinkInProgress = true;
				pLed->CurrLedState = LED_BLINK_NORMAL;
				if (pLed->bLedOn)
					pLed->BlinkingLedState = LED_OFF;
				else
					pLed->BlinkingLedState = LED_ON;
				_set_timer(&(pLed->BlinkTimer),
					   LED_BLINK_LINK_INTERVAL_ALPHA);
			} else if (!check_fwstate(pmlmepriv, _FW_LINKED)) {
				pLed->bLedNoLinkBlinkInProgress = true;
				pLed->CurrLedState = LED_BLINK_SLOWLY;
				if (pLed->bLedOn)
					pLed->BlinkingLedState = LED_OFF;
				else
					pLed->BlinkingLedState = LED_ON;
				_set_timer(&(pLed->BlinkTimer),
					   LED_BLINK_NO_LINK_INTERVAL_ALPHA);
			}
			pLed->bLedScanBlinkInProgress = false;
		} else {
			 if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_SCAN_INTERVAL_ALPHA);
		}
		break;
	case LED_TXRX_BLINK:
		pLed->BlinkTimes--;
		if (pLed->BlinkTimes == 0)
			bStopBlinking = true;
		if (bStopBlinking) {
			if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
				pLed->bLedLinkBlinkInProgress = true;
				pLed->CurrLedState = LED_BLINK_NORMAL;
				if (pLed->bLedOn)
					pLed->BlinkingLedState = LED_OFF;
				else
					pLed->BlinkingLedState = LED_ON;
				_set_timer(&(pLed->BlinkTimer),
					   LED_BLINK_LINK_INTERVAL_ALPHA);
			} else if (!check_fwstate(pmlmepriv, _FW_LINKED)) {
				pLed->bLedNoLinkBlinkInProgress = true;
				pLed->CurrLedState = LED_BLINK_SLOWLY;
				if (pLed->bLedOn)
					pLed->BlinkingLedState = LED_OFF;
				else
					pLed->BlinkingLedState = LED_ON;
				_set_timer(&(pLed->BlinkTimer),
					   LED_BLINK_NO_LINK_INTERVAL_ALPHA);
			}
			pLed->BlinkTimes = 0;
			pLed->bLedBlinkInProgress = false;
		} else {
			 if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_FASTER_INTERVAL_ALPHA);
		}
		break;
	case LED_BLINK_WPS:
		if (pLed->bLedOn)
			pLed->BlinkingLedState = LED_OFF;
		else
			pLed->BlinkingLedState = LED_ON;
		_set_timer(&(pLed->BlinkTimer),
			   LED_BLINK_SCAN_INTERVAL_ALPHA);
		break;
	case LED_BLINK_WPS_STOP:	/* WPS success */
		if (pLed->BlinkingLedState == LED_ON) {
			pLed->BlinkingLedState = LED_OFF;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_WPS_SUCESS_INTERVAL_ALPHA);
			bStopBlinking = false;
		} else
			bStopBlinking = true;
		if (bStopBlinking) {
			pLed->bLedLinkBlinkInProgress = true;
			pLed->CurrLedState = LED_BLINK_NORMAL;
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_LINK_INTERVAL_ALPHA);
		}
		pLed->bLedWPSBlinkInProgress = false;
		break;
	default:
		break;
	}
}

static void SwLedBlink2(struct LED_871x *pLed)
{
	struct _adapter *padapter = pLed->padapter;
	struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
	u8 bStopBlinking = false;

	/* Change LED according to BlinkingLedState specified. */
	if (pLed->BlinkingLedState == LED_ON)
		SwLedOn(padapter, pLed);
	else
		SwLedOff(padapter, pLed);
	switch (pLed->CurrLedState) {
	case LED_SCAN_BLINK:
		pLed->BlinkTimes--;
		if (pLed->BlinkTimes == 0)
			bStopBlinking = true;
		if (bStopBlinking) {
			if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
				pLed->CurrLedState = LED_ON;
				pLed->BlinkingLedState = LED_ON;
				SwLedOn(padapter, pLed);
			} else if (!check_fwstate(pmlmepriv, _FW_LINKED)) {
				pLed->CurrLedState = LED_OFF;
				pLed->BlinkingLedState = LED_OFF;
				SwLedOff(padapter, pLed);
			}
			pLed->bLedScanBlinkInProgress = false;
		} else {
			 if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_SCAN_INTERVAL_ALPHA);
		}
		break;
	case LED_TXRX_BLINK:
		pLed->BlinkTimes--;
		if (pLed->BlinkTimes == 0)
			bStopBlinking = true;
		if (bStopBlinking) {
			if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
				pLed->CurrLedState = LED_ON;
				pLed->BlinkingLedState = LED_ON;
				SwLedOn(padapter, pLed);
			} else if (!check_fwstate(pmlmepriv, _FW_LINKED)) {
				pLed->CurrLedState = LED_OFF;
				pLed->BlinkingLedState = LED_OFF;
				SwLedOff(padapter, pLed);
			}
			pLed->bLedBlinkInProgress = false;
		} else {
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_FASTER_INTERVAL_ALPHA);
		}
		break;
	default:
		break;
	}
}

static void SwLedBlink3(struct LED_871x *pLed)
{
	struct _adapter *padapter = pLed->padapter;
	struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
	u8 bStopBlinking = false;

	/* Change LED according to BlinkingLedState specified. */
	if (pLed->BlinkingLedState == LED_ON)
		SwLedOn(padapter, pLed);
	else
		if (pLed->CurrLedState != LED_BLINK_WPS_STOP)
			SwLedOff(padapter, pLed);
	switch (pLed->CurrLedState) {
	case LED_SCAN_BLINK:
		pLed->BlinkTimes--;
		if (pLed->BlinkTimes == 0)
			bStopBlinking = true;
		if (bStopBlinking) {
			if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
				pLed->CurrLedState = LED_ON;
				pLed->BlinkingLedState = LED_ON;
				if (!pLed->bLedOn)
					SwLedOn(padapter, pLed);
			} else if (!check_fwstate(pmlmepriv, _FW_LINKED)) {
				pLed->CurrLedState = LED_OFF;
				pLed->BlinkingLedState = LED_OFF;
				if (pLed->bLedOn)
					SwLedOff(padapter, pLed);
			}
			pLed->bLedScanBlinkInProgress = false;
		} else {
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_SCAN_INTERVAL_ALPHA);
		}
		break;
	case LED_TXRX_BLINK:
		pLed->BlinkTimes--;
		if (pLed->BlinkTimes == 0)
			bStopBlinking = true;
		if (bStopBlinking) {
			if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
				pLed->CurrLedState = LED_ON;
				pLed->BlinkingLedState = LED_ON;
				if (!pLed->bLedOn)
					SwLedOn(padapter, pLed);
			} else if (!check_fwstate(pmlmepriv, _FW_LINKED)) {
				pLed->CurrLedState = LED_OFF;
				pLed->BlinkingLedState = LED_OFF;
				if (pLed->bLedOn)
					SwLedOff(padapter, pLed);
			}
			pLed->bLedBlinkInProgress = false;
		} else {
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_FASTER_INTERVAL_ALPHA);
		}
		break;
	case LED_BLINK_WPS:
		if (pLed->bLedOn)
			pLed->BlinkingLedState = LED_OFF;
		else
			pLed->BlinkingLedState = LED_ON;
		_set_timer(&(pLed->BlinkTimer),
			   LED_BLINK_SCAN_INTERVAL_ALPHA);
		break;
	case LED_BLINK_WPS_STOP:	/*WPS success*/
		if (pLed->BlinkingLedState == LED_ON) {
			pLed->BlinkingLedState = LED_OFF;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_WPS_SUCESS_INTERVAL_ALPHA);
			bStopBlinking = false;
		} else
			bStopBlinking = true;
		if (bStopBlinking) {
			pLed->CurrLedState = LED_ON;
			pLed->BlinkingLedState = LED_ON;
			SwLedOn(padapter, pLed);
			pLed->bLedWPSBlinkInProgress = false;
		}
		break;
	default:
		break;
	}
}

static void SwLedBlink4(struct LED_871x *pLed)
{
	struct _adapter *padapter = pLed->padapter;
	struct led_priv	*ledpriv = &(padapter->ledpriv);
	struct LED_871x *pLed1 = &(ledpriv->SwLed1);
	u8 bStopBlinking = false;

	/* Change LED according to BlinkingLedState specified. */
	if (pLed->BlinkingLedState == LED_ON)
		SwLedOn(padapter, pLed);
	else
		SwLedOff(padapter, pLed);
	if (!pLed1->bLedWPSBlinkInProgress &&
	    pLed1->BlinkingLedState == LED_UNKNOWN) {
		pLed1->BlinkingLedState = LED_OFF;
		pLed1->CurrLedState = LED_OFF;
		SwLedOff(padapter, pLed1);
	}
	switch (pLed->CurrLedState) {
	case LED_BLINK_SLOWLY:
		if (pLed->bLedOn)
			pLed->BlinkingLedState = LED_OFF;
		else
			pLed->BlinkingLedState = LED_ON;
		_set_timer(&(pLed->BlinkTimer),
			   LED_BLINK_NO_LINK_INTERVAL_ALPHA);
		break;
	case LED_BLINK_StartToBlink:
		if (pLed->bLedOn) {
			pLed->BlinkingLedState = LED_OFF;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_SLOWLY_INTERVAL);
		} else {
			pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_NORMAL_INTERVAL);
		}
		break;
	case LED_SCAN_BLINK:
		pLed->BlinkTimes--;
		if (pLed->BlinkTimes == 0)
			bStopBlinking = true;
		if (bStopBlinking) {
			pLed->bLedNoLinkBlinkInProgress = true;
			pLed->CurrLedState = LED_BLINK_SLOWLY;
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_NO_LINK_INTERVAL_ALPHA);
			pLed->bLedScanBlinkInProgress = false;
		} else {
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_SCAN_INTERVAL_ALPHA);
		}
		break;
	case LED_TXRX_BLINK:
		pLed->BlinkTimes--;
		if (pLed->BlinkTimes == 0)
			bStopBlinking = true;
		if (bStopBlinking) {
			pLed->bLedNoLinkBlinkInProgress = true;
			pLed->CurrLedState = LED_BLINK_SLOWLY;
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_NO_LINK_INTERVAL_ALPHA);
			pLed->bLedBlinkInProgress = false;
		} else {
			 if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_FASTER_INTERVAL_ALPHA);
		}
		break;
	case LED_BLINK_WPS:
		if (pLed->bLedOn) {
			pLed->BlinkingLedState = LED_OFF;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_SLOWLY_INTERVAL);
		} else {
			pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_NORMAL_INTERVAL);
		}
		break;
	case LED_BLINK_WPS_STOP:	/*WPS authentication fail*/
		if (pLed->bLedOn)
			pLed->BlinkingLedState = LED_OFF;
		else
			pLed->BlinkingLedState = LED_ON;
		_set_timer(&(pLed->BlinkTimer), LED_BLINK_NORMAL_INTERVAL);
		break;
	case LED_BLINK_WPS_STOP_OVERLAP:	/*WPS session overlap */
		pLed->BlinkTimes--;
		if (pLed->BlinkTimes == 0) {
			if (pLed->bLedOn)
				pLed->BlinkTimes = 1;
			else
				bStopBlinking = true;
		}
		if (bStopBlinking) {
			pLed->BlinkTimes = 10;
			pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_LINK_INTERVAL_ALPHA);
		} else {
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_NORMAL_INTERVAL);
		}
		break;
	default:
		break;
	}
}

static void SwLedBlink5(struct LED_871x *pLed)
{
	struct _adapter *padapter = pLed->padapter;
	u8 bStopBlinking = false;

	/* Change LED according to BlinkingLedState specified. */
	if (pLed->BlinkingLedState == LED_ON)
		SwLedOn(padapter, pLed);
	else
		SwLedOff(padapter, pLed);
	switch (pLed->CurrLedState) {
	case LED_SCAN_BLINK:
		pLed->BlinkTimes--;
		if (pLed->BlinkTimes == 0)
			bStopBlinking = true;
		if (bStopBlinking) {
			pLed->CurrLedState = LED_ON;
			pLed->BlinkingLedState = LED_ON;
			if (!pLed->bLedOn)
				_set_timer(&(pLed->BlinkTimer),
					   LED_BLINK_FASTER_INTERVAL_ALPHA);
			pLed->bLedScanBlinkInProgress = false;
		} else {
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_SCAN_INTERVAL_ALPHA);
		}
		break;
	case LED_TXRX_BLINK:
		pLed->BlinkTimes--;
		if (pLed->BlinkTimes == 0)
			bStopBlinking = true;
		if (bStopBlinking) {
			pLed->CurrLedState = LED_ON;
			pLed->BlinkingLedState = LED_ON;
			if (!pLed->bLedOn)
				_set_timer(&(pLed->BlinkTimer),
					   LED_BLINK_FASTER_INTERVAL_ALPHA);
			pLed->bLedBlinkInProgress = false;
		} else {
			 if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_FASTER_INTERVAL_ALPHA);
		}
		break;
	default:
		break;
	}
}

static void SwLedBlink6(struct LED_871x *pLed)
{
	struct _adapter *padapter = pLed->padapter;
	u8 bStopBlinking = false;

	/* Change LED according to BlinkingLedState specified. */
	if (pLed->BlinkingLedState == LED_ON)
		SwLedOn(padapter, pLed);
	else
		SwLedOff(padapter, pLed);
	switch (pLed->CurrLedState) {
	case LED_TXRX_BLINK:
		pLed->BlinkTimes--;
		if (pLed->BlinkTimes == 0)
			bStopBlinking = true;
		if (bStopBlinking) {
			pLed->CurrLedState = LED_ON;
			pLed->BlinkingLedState = LED_ON;
			if (!pLed->bLedOn)
				SwLedOn(padapter, pLed);
			pLed->bLedBlinkInProgress = false;
		} else {
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_FASTER_INTERVAL_ALPHA);
		}
		break;
	case LED_BLINK_WPS:
		if (pLed->bLedOn)
			pLed->BlinkingLedState = LED_OFF;
		else
			pLed->BlinkingLedState = LED_ON;
		_set_timer(&(pLed->BlinkTimer), LED_BLINK_SCAN_INTERVAL_ALPHA);
		break;

	default:
		break;
	}
}

/*	Description:
 *		Callback function of LED BlinkTimer,
 *		it just schedules to corresponding BlinkWorkItem.
 */
static void BlinkTimerCallback(unsigned long data)
{
	struct LED_871x  *pLed = (struct LED_871x *)data;

	/* This fixed the crash problem on Fedora 12 when trying to do thei
	 * insmod;ifconfig up;rmmod commands. */
	if ((pLed->padapter->bSurpriseRemoved == true) ||
	    (pLed->padapter->bDriverStopped == true))
		return;
	_set_workitem(&(pLed->BlinkWorkItem));
}

/*	Description:
 *		Callback function of LED BlinkWorkItem.
 *		We dispatch acture LED blink action according to LedStrategy.
 */
static void BlinkWorkItemCallback(struct work_struct *work)
{
	struct LED_871x *pLed = container_of(work, struct LED_871x,
				BlinkWorkItem);
	struct led_priv	*ledpriv = &(pLed->padapter->ledpriv);

	switch (ledpriv->LedStrategy) {
	case SW_LED_MODE0:
		SwLedBlink(pLed);
		break;
	case SW_LED_MODE1:
		SwLedBlink1(pLed);
		break;
	case SW_LED_MODE2:
		SwLedBlink2(pLed);
		break;
	case SW_LED_MODE3:
		SwLedBlink3(pLed);
		break;
	case SW_LED_MODE4:
		SwLedBlink4(pLed);
		break;
	case SW_LED_MODE5:
		SwLedBlink5(pLed);
		break;
	case SW_LED_MODE6:
		SwLedBlink6(pLed);
		break;
	default:
		SwLedBlink(pLed);
		break;
	}
}

/*============================================================================
 * Default LED behavior.
 *============================================================================
 *
 *	Description:
 *		Implement each led action for SW_LED_MODE0.
 *		This is default strategy.
 */

static void SwLedControlMode1(struct _adapter *padapter,
			      enum LED_CTL_MODE LedAction)
{
	struct led_priv *ledpriv = &(padapter->ledpriv);
	struct LED_871x *pLed = &(ledpriv->SwLed0);
	struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
	struct sitesurvey_ctrl *psitesurveyctrl = &(pmlmepriv->sitesurveyctrl);

	if (padapter->eeprompriv.CustomerID == RT_CID_819x_CAMEO)
		pLed = &(ledpriv->SwLed1);
	switch (LedAction) {
	case LED_CTL_START_TO_LINK:
	case LED_CTL_NO_LINK:
		if (pLed->bLedNoLinkBlinkInProgress == false) {
			if (pLed->CurrLedState == LED_SCAN_BLINK ||
			  IS_LED_WPS_BLINKING(pLed))
				return;
			if (pLed->bLedLinkBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedLinkBlinkInProgress = false;
			}
			if (pLed->bLedBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedBlinkInProgress = false;
			}
			pLed->bLedNoLinkBlinkInProgress = true;
			pLed->CurrLedState = LED_BLINK_SLOWLY;
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_NO_LINK_INTERVAL_ALPHA);
		}
		break;
	case LED_CTL_LINK:
		if (pLed->bLedLinkBlinkInProgress == false) {
			if (pLed->CurrLedState == LED_SCAN_BLINK ||
			    IS_LED_WPS_BLINKING(pLed))
				return;
			if (pLed->bLedNoLinkBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedNoLinkBlinkInProgress = false;
			}
			if (pLed->bLedBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedBlinkInProgress = false;
			}
			pLed->bLedLinkBlinkInProgress = true;
			pLed->CurrLedState = LED_BLINK_NORMAL;
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_LINK_INTERVAL_ALPHA);
		}
		break;
	case LED_CTL_SITE_SURVEY:
		if ((psitesurveyctrl->traffic_busy) &&
		    (check_fwstate(pmlmepriv, _FW_LINKED) == true))
			; /* dummy branch */
		 else if (pLed->bLedScanBlinkInProgress == false) {
			if (IS_LED_WPS_BLINKING(pLed))
				return;
			if (pLed->bLedNoLinkBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedNoLinkBlinkInProgress = false;
			}
			if (pLed->bLedLinkBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				 pLed->bLedLinkBlinkInProgress = false;
			}
			if (pLed->bLedBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedBlinkInProgress = false;
			}
			pLed->bLedScanBlinkInProgress = true;
			pLed->CurrLedState = LED_SCAN_BLINK;
			pLed->BlinkTimes = 24;
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_SCAN_INTERVAL_ALPHA);
		 }
		break;
	case LED_CTL_TX:
	case LED_CTL_RX:
		if (pLed->bLedBlinkInProgress == false) {
			if (pLed->CurrLedState == LED_SCAN_BLINK ||
			    IS_LED_WPS_BLINKING(pLed))
				return;
			if (pLed->bLedNoLinkBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedNoLinkBlinkInProgress = false;
			}
			if (pLed->bLedLinkBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedLinkBlinkInProgress = false;
			}
			pLed->bLedBlinkInProgress = true;
			pLed->CurrLedState = LED_TXRX_BLINK;
			pLed->BlinkTimes = 2;
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_FASTER_INTERVAL_ALPHA);
		}
		break;

	case LED_CTL_START_WPS: /*wait until xinpin finish */
	case LED_CTL_START_WPS_BOTTON:
		 if (pLed->bLedWPSBlinkInProgress == false) {
			if (pLed->bLedNoLinkBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedNoLinkBlinkInProgress = false;
			}
			if (pLed->bLedLinkBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				 pLed->bLedLinkBlinkInProgress = false;
			}
			if (pLed->bLedBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedBlinkInProgress = false;
			}
			if (pLed->bLedScanBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedScanBlinkInProgress = false;
			}
			pLed->bLedWPSBlinkInProgress = true;
			pLed->CurrLedState = LED_BLINK_WPS;
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_SCAN_INTERVAL_ALPHA);
		}
		break;
	case LED_CTL_STOP_WPS:
		if (pLed->bLedNoLinkBlinkInProgress == true) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedNoLinkBlinkInProgress = false;
		}
		if (pLed->bLedLinkBlinkInProgress == true) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			 pLed->bLedLinkBlinkInProgress = false;
		}
		if (pLed->bLedBlinkInProgress == true) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedBlinkInProgress = false;
		}
		if (pLed->bLedScanBlinkInProgress == true) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedScanBlinkInProgress = false;
		}
		if (pLed->bLedWPSBlinkInProgress)
			_cancel_timer_ex(&(pLed->BlinkTimer));
		else
			pLed->bLedWPSBlinkInProgress = true;
		pLed->CurrLedState = LED_BLINK_WPS_STOP;
		if (pLed->bLedOn) {
			pLed->BlinkingLedState = LED_OFF;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_WPS_SUCESS_INTERVAL_ALPHA);
		} else {
			pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer), 0);
		}
		break;
	case LED_CTL_STOP_WPS_FAIL:
		if (pLed->bLedWPSBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedWPSBlinkInProgress = false;
		}
		pLed->bLedNoLinkBlinkInProgress = true;
		pLed->CurrLedState = LED_BLINK_SLOWLY;
		if (pLed->bLedOn)
			pLed->BlinkingLedState = LED_OFF;
		else
			pLed->BlinkingLedState = LED_ON;
		_set_timer(&(pLed->BlinkTimer),
			   LED_BLINK_NO_LINK_INTERVAL_ALPHA);
		break;
	case LED_CTL_POWER_OFF:
		pLed->CurrLedState = LED_OFF;
		pLed->BlinkingLedState = LED_OFF;
		if (pLed->bLedNoLinkBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedNoLinkBlinkInProgress = false;
		}
		if (pLed->bLedLinkBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedLinkBlinkInProgress = false;
		}
		if (pLed->bLedBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedBlinkInProgress = false;
		}
		if (pLed->bLedWPSBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedWPSBlinkInProgress = false;
		}
		if (pLed->bLedScanBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedScanBlinkInProgress = false;
		}
		_set_timer(&(pLed->BlinkTimer), 0);
		break;
	default:
		break;
	}
}

static void SwLedControlMode2(struct _adapter *padapter,
			      enum LED_CTL_MODE LedAction)
{
	struct led_priv	 *ledpriv = &(padapter->ledpriv);
	struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
	struct LED_871x *pLed = &(ledpriv->SwLed0);

	switch (LedAction) {
	case LED_CTL_SITE_SURVEY:
		 if (pmlmepriv->sitesurveyctrl.traffic_busy)
			; /* dummy branch */
		 else if (pLed->bLedScanBlinkInProgress == false) {
			if (IS_LED_WPS_BLINKING(pLed))
				return;

			if (pLed->bLedBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedBlinkInProgress = false;
			}
			pLed->bLedScanBlinkInProgress = true;
			pLed->CurrLedState = LED_SCAN_BLINK;
			pLed->BlinkTimes = 24;
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_SCAN_INTERVAL_ALPHA);
		 }
		break;

	case LED_CTL_TX:
	case LED_CTL_RX:
		if ((pLed->bLedBlinkInProgress == false) &&
		   (check_fwstate(pmlmepriv, _FW_LINKED) == true)) {
			if (pLed->CurrLedState == LED_SCAN_BLINK ||
			   IS_LED_WPS_BLINKING(pLed))
				return;
			pLed->bLedBlinkInProgress = true;
			pLed->CurrLedState = LED_TXRX_BLINK;
			pLed->BlinkTimes = 2;
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_FASTER_INTERVAL_ALPHA);
		}
		break;

	case LED_CTL_LINK:
		pLed->CurrLedState = LED_ON;
		pLed->BlinkingLedState = LED_ON;
		if (pLed->bLedBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedBlinkInProgress = false;
		}
		if (pLed->bLedScanBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedScanBlinkInProgress = false;
		}

		_set_timer(&(pLed->BlinkTimer), 0);
		break;

	case LED_CTL_START_WPS: /*wait until xinpin finish*/
	case LED_CTL_START_WPS_BOTTON:
		if (pLed->bLedWPSBlinkInProgress == false) {
			if (pLed->bLedBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedBlinkInProgress = false;
			}
			if (pLed->bLedScanBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedScanBlinkInProgress = false;
			}
			pLed->bLedWPSBlinkInProgress = true;
			pLed->CurrLedState = LED_ON;
			pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer), 0);
		 }
		break;

	case LED_CTL_STOP_WPS:
		pLed->bLedWPSBlinkInProgress = false;
		pLed->CurrLedState = LED_ON;
		pLed->BlinkingLedState = LED_ON;
		_set_timer(&(pLed->BlinkTimer), 0);
		break;

	case LED_CTL_STOP_WPS_FAIL:
		pLed->bLedWPSBlinkInProgress = false;
		pLed->CurrLedState = LED_OFF;
		pLed->BlinkingLedState = LED_OFF;
		_set_timer(&(pLed->BlinkTimer), 0);
		break;

	case LED_CTL_START_TO_LINK:
	case LED_CTL_NO_LINK:
		if (!IS_LED_BLINKING(pLed)) {
			pLed->CurrLedState = LED_OFF;
			pLed->BlinkingLedState = LED_OFF;
			_set_timer(&(pLed->BlinkTimer), 0);
		}
		break;
	case LED_CTL_POWER_OFF:
		pLed->CurrLedState = LED_OFF;
		pLed->BlinkingLedState = LED_OFF;
		if (pLed->bLedBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedBlinkInProgress = false;
		}
		if (pLed->bLedScanBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedScanBlinkInProgress = false;
		}
		if (pLed->bLedWPSBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedWPSBlinkInProgress = false;
		}
		_set_timer(&(pLed->BlinkTimer), 0);
		break;
	default:
		break;
	}
}

static void SwLedControlMode3(struct _adapter *padapter,
			      enum LED_CTL_MODE LedAction)
{
	struct led_priv	*ledpriv = &(padapter->ledpriv);
	struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
	struct LED_871x *pLed = &(ledpriv->SwLed0);

	switch (LedAction) {
	case LED_CTL_SITE_SURVEY:
		if (pmlmepriv->sitesurveyctrl.traffic_busy)
			; /* dummy branch */
		else if (pLed->bLedScanBlinkInProgress == false) {
			if (IS_LED_WPS_BLINKING(pLed))
				return;
			if (pLed->bLedBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedBlinkInProgress = false;
			}
			pLed->bLedScanBlinkInProgress = true;
			pLed->CurrLedState = LED_SCAN_BLINK;
			pLed->BlinkTimes = 24;
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_SCAN_INTERVAL_ALPHA);
		}
		break;
	case LED_CTL_TX:
	case LED_CTL_RX:
		if ((pLed->bLedBlinkInProgress == false) &&
		    (check_fwstate(pmlmepriv, _FW_LINKED) == true)) {
			if (pLed->CurrLedState == LED_SCAN_BLINK ||
			    IS_LED_WPS_BLINKING(pLed))
				return;
			pLed->bLedBlinkInProgress = true;
			pLed->CurrLedState = LED_TXRX_BLINK;
			pLed->BlinkTimes = 2;
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_FASTER_INTERVAL_ALPHA);
		}
		break;
	case LED_CTL_LINK:
		if (IS_LED_WPS_BLINKING(pLed))
			return;
		pLed->CurrLedState = LED_ON;
		pLed->BlinkingLedState = LED_ON;
		if (pLed->bLedBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedBlinkInProgress = false;
		}
		if (pLed->bLedScanBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedScanBlinkInProgress = false;
		}
		_set_timer(&(pLed->BlinkTimer), 0);
		break;
	case LED_CTL_START_WPS: /* wait until xinpin finish */
	case LED_CTL_START_WPS_BOTTON:
		if (pLed->bLedWPSBlinkInProgress == false) {
			if (pLed->bLedBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedBlinkInProgress = false;
			}
			if (pLed->bLedScanBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedScanBlinkInProgress = false;
			}
			pLed->bLedWPSBlinkInProgress = true;
			pLed->CurrLedState = LED_BLINK_WPS;
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_SCAN_INTERVAL_ALPHA);
		}
		break;
	case LED_CTL_STOP_WPS:
		if (pLed->bLedWPSBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedWPSBlinkInProgress = false;
		} else
			pLed->bLedWPSBlinkInProgress = true;
		pLed->CurrLedState = LED_BLINK_WPS_STOP;
		if (pLed->bLedOn) {
			pLed->BlinkingLedState = LED_OFF;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_WPS_SUCESS_INTERVAL_ALPHA);
		} else {
			pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer), 0);
		}
		break;
	case LED_CTL_STOP_WPS_FAIL:
		if (pLed->bLedWPSBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedWPSBlinkInProgress = false;
		}
		pLed->CurrLedState = LED_OFF;
		pLed->BlinkingLedState = LED_OFF;
		_set_timer(&(pLed->BlinkTimer), 0);
		break;
	case LED_CTL_START_TO_LINK:
	case LED_CTL_NO_LINK:
		if (!IS_LED_BLINKING(pLed)) {
			pLed->CurrLedState = LED_OFF;
			pLed->BlinkingLedState = LED_OFF;
			_set_timer(&(pLed->BlinkTimer), 0);
		}
		break;
	case LED_CTL_POWER_OFF:
		pLed->CurrLedState = LED_OFF;
		pLed->BlinkingLedState = LED_OFF;
		if (pLed->bLedBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedBlinkInProgress = false;
		}
		if (pLed->bLedScanBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedScanBlinkInProgress = false;
		}
		if (pLed->bLedWPSBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedWPSBlinkInProgress = false;
		}
		_set_timer(&(pLed->BlinkTimer), 0);
		break;
	default:
		break;
	}
}

static void SwLedControlMode4(struct _adapter *padapter,
			      enum LED_CTL_MODE LedAction)
{
	struct led_priv	*ledpriv = &(padapter->ledpriv);
	struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
	struct LED_871x *pLed = &(ledpriv->SwLed0);
	struct LED_871x *pLed1 = &(ledpriv->SwLed1);

	switch (LedAction) {
	case LED_CTL_START_TO_LINK:
		if (pLed1->bLedWPSBlinkInProgress) {
			pLed1->bLedWPSBlinkInProgress = false;
			_cancel_timer_ex(&(pLed1->BlinkTimer));
			pLed1->BlinkingLedState = LED_OFF;
			pLed1->CurrLedState = LED_OFF;
			if (pLed1->bLedOn)
				_set_timer(&(pLed->BlinkTimer), 0);
		}
		if (pLed->bLedStartToLinkBlinkInProgress == false) {
			if (pLed->CurrLedState == LED_SCAN_BLINK ||
			    IS_LED_WPS_BLINKING(pLed))
				return;
			if (pLed->bLedBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedBlinkInProgress = false;
			}
			if (pLed->bLedNoLinkBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedNoLinkBlinkInProgress = false;
			}
			pLed->bLedStartToLinkBlinkInProgress = true;
			pLed->CurrLedState = LED_BLINK_StartToBlink;
			if (pLed->bLedOn) {
				pLed->BlinkingLedState = LED_OFF;
				_set_timer(&(pLed->BlinkTimer),
					   LED_BLINK_SLOWLY_INTERVAL);
			} else {
				pLed->BlinkingLedState = LED_ON;
				_set_timer(&(pLed->BlinkTimer),
					   LED_BLINK_NORMAL_INTERVAL);
			}
		}
		break;
	case LED_CTL_LINK:
	case LED_CTL_NO_LINK:
		/*LED1 settings*/
		if (LedAction == LED_CTL_LINK) {
			if (pLed1->bLedWPSBlinkInProgress) {
				pLed1->bLedWPSBlinkInProgress = false;
				_cancel_timer_ex(&(pLed1->BlinkTimer));
				pLed1->BlinkingLedState = LED_OFF;
				pLed1->CurrLedState = LED_OFF;
				if (pLed1->bLedOn)
					_set_timer(&(pLed->BlinkTimer), 0);
			}
		}
		if (pLed->bLedNoLinkBlinkInProgress == false) {
			if (pLed->CurrLedState == LED_SCAN_BLINK ||
			    IS_LED_WPS_BLINKING(pLed))
				return;
			if (pLed->bLedBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedBlinkInProgress = false;
			}
			pLed->bLedNoLinkBlinkInProgress = true;
			pLed->CurrLedState = LED_BLINK_SLOWLY;
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_NO_LINK_INTERVAL_ALPHA);
		}
		break;
	case LED_CTL_SITE_SURVEY:
		if ((pmlmepriv->sitesurveyctrl.traffic_busy) &&
		    (check_fwstate(pmlmepriv, _FW_LINKED) == true))
			;
		else if (pLed->bLedScanBlinkInProgress == false) {
			if (IS_LED_WPS_BLINKING(pLed))
				return;
			if (pLed->bLedNoLinkBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedNoLinkBlinkInProgress = false;
			}
			if (pLed->bLedBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedBlinkInProgress = false;
			}
			pLed->bLedScanBlinkInProgress = true;
			pLed->CurrLedState = LED_SCAN_BLINK;
			pLed->BlinkTimes = 24;
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_SCAN_INTERVAL_ALPHA);
		}
		break;
	case LED_CTL_TX:
	case LED_CTL_RX:
		if (pLed->bLedBlinkInProgress == false) {
			if (pLed->CurrLedState == LED_SCAN_BLINK ||
			    IS_LED_WPS_BLINKING(pLed))
				return;
			if (pLed->bLedNoLinkBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedNoLinkBlinkInProgress = false;
			}
			pLed->bLedBlinkInProgress = true;
			pLed->CurrLedState = LED_TXRX_BLINK;
			pLed->BlinkTimes = 2;
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_FASTER_INTERVAL_ALPHA);
		}
		break;
	case LED_CTL_START_WPS: /*wait until xinpin finish*/
	case LED_CTL_START_WPS_BOTTON:
		if (pLed1->bLedWPSBlinkInProgress) {
			pLed1->bLedWPSBlinkInProgress = false;
			_cancel_timer_ex(&(pLed1->BlinkTimer));
			pLed1->BlinkingLedState = LED_OFF;
			pLed1->CurrLedState = LED_OFF;
			if (pLed1->bLedOn)
				_set_timer(&(pLed->BlinkTimer), 0);
		}
		if (pLed->bLedWPSBlinkInProgress == false) {
			if (pLed->bLedNoLinkBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedNoLinkBlinkInProgress = false;
			}
			if (pLed->bLedBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedBlinkInProgress = false;
			}
			if (pLed->bLedScanBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedScanBlinkInProgress = false;
			}
			pLed->bLedWPSBlinkInProgress = true;
			pLed->CurrLedState = LED_BLINK_WPS;
			if (pLed->bLedOn) {
				pLed->BlinkingLedState = LED_OFF;
				_set_timer(&(pLed->BlinkTimer),
					   LED_BLINK_SLOWLY_INTERVAL);
			} else {
				pLed->BlinkingLedState = LED_ON;
				_set_timer(&(pLed->BlinkTimer),
					   LED_BLINK_NORMAL_INTERVAL);
			}
		}
		break;
	case LED_CTL_STOP_WPS:	/*WPS connect success*/
		if (pLed->bLedWPSBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedWPSBlinkInProgress = false;
		}
		pLed->bLedNoLinkBlinkInProgress = true;
		pLed->CurrLedState = LED_BLINK_SLOWLY;
		if (pLed->bLedOn)
			pLed->BlinkingLedState = LED_OFF;
		else
			pLed->BlinkingLedState = LED_ON;
		_set_timer(&(pLed->BlinkTimer),
			   LED_BLINK_NO_LINK_INTERVAL_ALPHA);
		break;
	case LED_CTL_STOP_WPS_FAIL:	/*WPS authentication fail*/
		if (pLed->bLedWPSBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedWPSBlinkInProgress = false;
		}
		pLed->bLedNoLinkBlinkInProgress = true;
		pLed->CurrLedState = LED_BLINK_SLOWLY;
		if (pLed->bLedOn)
			pLed->BlinkingLedState = LED_OFF;
		else
			pLed->BlinkingLedState = LED_ON;
		_set_timer(&(pLed->BlinkTimer),
			   LED_BLINK_NO_LINK_INTERVAL_ALPHA);
		/*LED1 settings*/
		if (pLed1->bLedWPSBlinkInProgress)
			_cancel_timer_ex(&(pLed1->BlinkTimer));
		else
			pLed1->bLedWPSBlinkInProgress = true;
		pLed1->CurrLedState = LED_BLINK_WPS_STOP;
		if (pLed1->bLedOn)
			pLed1->BlinkingLedState = LED_OFF;
		else
			pLed1->BlinkingLedState = LED_ON;
		_set_timer(&(pLed->BlinkTimer), LED_BLINK_NORMAL_INTERVAL);
		break;
	case LED_CTL_STOP_WPS_FAIL_OVERLAP:	/*WPS session overlap*/
		if (pLed->bLedWPSBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedWPSBlinkInProgress = false;
		}
		pLed->bLedNoLinkBlinkInProgress = true;
		pLed->CurrLedState = LED_BLINK_SLOWLY;
		if (pLed->bLedOn)
			pLed->BlinkingLedState = LED_OFF;
		else
			pLed->BlinkingLedState = LED_ON;
		_set_timer(&(pLed->BlinkTimer),
			   LED_BLINK_NO_LINK_INTERVAL_ALPHA);
		/*LED1 settings*/
		if (pLed1->bLedWPSBlinkInProgress)
			_cancel_timer_ex(&(pLed1->BlinkTimer));
		else
			pLed1->bLedWPSBlinkInProgress = true;
		pLed1->CurrLedState = LED_BLINK_WPS_STOP_OVERLAP;
		pLed1->BlinkTimes = 10;
		if (pLed1->bLedOn)
			pLed1->BlinkingLedState = LED_OFF;
		else
			pLed1->BlinkingLedState = LED_ON;
		_set_timer(&(pLed->BlinkTimer), LED_BLINK_NORMAL_INTERVAL);
		break;
	case LED_CTL_POWER_OFF:
		pLed->CurrLedState = LED_OFF;
		pLed->BlinkingLedState = LED_OFF;
		if (pLed->bLedNoLinkBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedNoLinkBlinkInProgress = false;
		}
		if (pLed->bLedLinkBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedLinkBlinkInProgress = false;
		}
		if (pLed->bLedBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedBlinkInProgress = false;
		}
		if (pLed->bLedWPSBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedWPSBlinkInProgress = false;
		}
		if (pLed->bLedScanBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedScanBlinkInProgress = false;
		}
		if (pLed->bLedStartToLinkBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedStartToLinkBlinkInProgress = false;
		}
		if (pLed1->bLedWPSBlinkInProgress) {
			_cancel_timer_ex(&(pLed1->BlinkTimer));
			pLed1->bLedWPSBlinkInProgress = false;
		}
		pLed1->BlinkingLedState = LED_UNKNOWN;
		SwLedOff(padapter, pLed);
		SwLedOff(padapter, pLed1);
		break;
	default:
		break;
	}
}

static void SwLedControlMode5(struct _adapter *padapter,
			      enum LED_CTL_MODE LedAction)
{
	struct led_priv	*ledpriv = &(padapter->ledpriv);
	struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
	struct LED_871x *pLed = &(ledpriv->SwLed0);

	if (padapter->eeprompriv.CustomerID == RT_CID_819x_CAMEO)
		pLed = &(ledpriv->SwLed1);

	switch (LedAction) {
	case LED_CTL_POWER_ON:
	case LED_CTL_NO_LINK:
	case LED_CTL_LINK:	/* solid blue */
		if (pLed->CurrLedState == LED_SCAN_BLINK)
			return;
		pLed->CurrLedState = LED_ON;
		pLed->BlinkingLedState = LED_ON;
		pLed->bLedBlinkInProgress = false;
		_set_timer(&(pLed->BlinkTimer), 0);
		break;
	case LED_CTL_SITE_SURVEY:
		if ((pmlmepriv->sitesurveyctrl.traffic_busy) &&
		    (check_fwstate(pmlmepriv, _FW_LINKED) == true))
			; /* dummy branch */
		else if (pLed->bLedScanBlinkInProgress == false) {
			if (pLed->bLedBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedBlinkInProgress = false;
			}
			pLed->bLedScanBlinkInProgress = true;
			pLed->CurrLedState = LED_SCAN_BLINK;
			pLed->BlinkTimes = 24;
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_SCAN_INTERVAL_ALPHA);
		}
		break;
	case LED_CTL_TX:
	case LED_CTL_RX:
		if (pLed->bLedBlinkInProgress == false) {
			if (pLed->CurrLedState == LED_SCAN_BLINK)
				return;
			pLed->bLedBlinkInProgress = true;
			pLed->CurrLedState = LED_TXRX_BLINK;
			pLed->BlinkTimes = 2;
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_FASTER_INTERVAL_ALPHA);
		}
		break;
	case LED_CTL_POWER_OFF:
		pLed->CurrLedState = LED_OFF;
		pLed->BlinkingLedState = LED_OFF;
		if (pLed->bLedBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedBlinkInProgress = false;
		}
		SwLedOff(padapter, pLed);
		break;
	default:
		break;
	}
}


static void SwLedControlMode6(struct _adapter *padapter,
			      enum LED_CTL_MODE LedAction)
{
	struct led_priv	*ledpriv = &(padapter->ledpriv);
	struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
	struct LED_871x *pLed = &(ledpriv->SwLed0);

	switch (LedAction) {
	case LED_CTL_POWER_ON:
	case LED_CTL_NO_LINK:
	case LED_CTL_LINK:	/*solid blue*/
	case LED_CTL_SITE_SURVEY:
		if (IS_LED_WPS_BLINKING(pLed))
				return;
		pLed->CurrLedState = LED_ON;
		pLed->BlinkingLedState = LED_ON;
		pLed->bLedBlinkInProgress = false;
		_set_timer(&(pLed->BlinkTimer), 0);
		break;
	case LED_CTL_TX:
	case LED_CTL_RX:
		if (pLed->bLedBlinkInProgress == false &&
		   (check_fwstate(pmlmepriv, _FW_LINKED) == true)) {
			if (IS_LED_WPS_BLINKING(pLed))
				return;
			pLed->bLedBlinkInProgress = true;
			pLed->CurrLedState = LED_TXRX_BLINK;
			pLed->BlinkTimes = 2;
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_FASTER_INTERVAL_ALPHA);
		}
		break;
	case LED_CTL_START_WPS: /*wait until xinpin finish*/
	case LED_CTL_START_WPS_BOTTON:
		if (pLed->bLedWPSBlinkInProgress == false) {
			if (pLed->bLedBlinkInProgress == true) {
				_cancel_timer_ex(&(pLed->BlinkTimer));
				pLed->bLedBlinkInProgress = false;
			}
			pLed->bLedWPSBlinkInProgress = true;
			pLed->CurrLedState = LED_BLINK_WPS;
			if (pLed->bLedOn)
				pLed->BlinkingLedState = LED_OFF;
			else
				pLed->BlinkingLedState = LED_ON;
			_set_timer(&(pLed->BlinkTimer),
				   LED_BLINK_SCAN_INTERVAL_ALPHA);
		}
		break;
	case LED_CTL_STOP_WPS_FAIL:
	case LED_CTL_STOP_WPS:
		if (pLed->bLedWPSBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedWPSBlinkInProgress = false;
		}
		pLed->CurrLedState = LED_ON;
		pLed->BlinkingLedState = LED_ON;
		_set_timer(&(pLed->BlinkTimer), 0);
		break;
	case LED_CTL_POWER_OFF:
		pLed->CurrLedState = LED_OFF;
		pLed->BlinkingLedState = LED_OFF;
		if (pLed->bLedBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedBlinkInProgress = false;
		}
		if (pLed->bLedWPSBlinkInProgress) {
			_cancel_timer_ex(&(pLed->BlinkTimer));
			pLed->bLedWPSBlinkInProgress = false;
		}
		SwLedOff(padapter, pLed);
		break;
	default:
		break;
	}
}

/*	Description:
 *		Dispatch LED action according to pHalData->LedStrategy.
 */
void LedControl871x(struct _adapter *padapter, enum LED_CTL_MODE LedAction)
{
	struct led_priv	*ledpriv = &(padapter->ledpriv);

	if (ledpriv == NULL || ledpriv->bRegUseLed == false)
		return;
	switch (ledpriv->LedStrategy) {
	case SW_LED_MODE0:
		break;
	case SW_LED_MODE1:
		SwLedControlMode1(padapter, LedAction);
		break;
	case SW_LED_MODE2:
		SwLedControlMode2(padapter, LedAction);
		break;
	case SW_LED_MODE3:
		SwLedControlMode3(padapter, LedAction);
		break;
	case SW_LED_MODE4:
		SwLedControlMode4(padapter, LedAction);
		break;
	case SW_LED_MODE5:
		SwLedControlMode5(padapter, LedAction);
		break;
	case SW_LED_MODE6:
		SwLedControlMode6(padapter, LedAction);
		break;
	default:
		break;
	}
}