blob: 48c5db69929c32ecea8c5f96b12bf9e6c5131ef3 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2007 - 2012 Realtek Corporation. */
#include "../include/drv_types.h"
#include "../include/rtw_led.h"
#include "../include/rtl8188e_spec.h"
#define LED_BLINK_NO_LINK_INTVL msecs_to_jiffies(1000)
#define LED_BLINK_LINK_INTVL msecs_to_jiffies(500)
#define LED_BLINK_SCAN_INTVL msecs_to_jiffies(180)
#define LED_BLINK_FASTER_INTVL msecs_to_jiffies(50)
#define LED_BLINK_WPS_SUCESS_INTVL msecs_to_jiffies(5000)
#define IS_LED_WPS_BLINKING(l) \
((l)->CurrLedState == LED_BLINK_WPS || \
(l)->CurrLedState == LED_BLINK_WPS_STOP || \
(l)->bLedWPSBlinkInProgress)
static void ResetLedStatus(struct led_priv *pLed)
{
pLed->CurrLedState = RTW_LED_OFF; /* Current LED state. */
pLed->bLedOn = false; /* true if LED is ON, false if LED is OFF. */
pLed->bLedBlinkInProgress = false; /* true if it is blinking, false o.w.. */
pLed->bLedWPSBlinkInProgress = false;
pLed->BlinkTimes = 0; /* Number of times to toggle led state for blinking. */
pLed->bLedLinkBlinkInProgress = false;
pLed->bLedScanBlinkInProgress = false;
}
static void SwLedOn(struct adapter *padapter, struct led_priv *pLed)
{
if (padapter->bDriverStopped)
return;
rtw_write8(padapter, REG_LEDCFG2, BIT(5)); /* SW control led0 on. */
pLed->bLedOn = true;
}
static void SwLedOff(struct adapter *padapter, struct led_priv *pLed)
{
if (padapter->bDriverStopped)
goto exit;
rtw_write8(padapter, REG_LEDCFG2, BIT(5) | BIT(3));
exit:
pLed->bLedOn = false;
}
static void blink_work(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
struct led_priv *pLed = container_of(dwork, struct led_priv, blink_work);
struct adapter *padapter = pLed->padapter;
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
if (padapter->pwrctrlpriv.rf_pwrstate != rf_on) {
SwLedOff(padapter, pLed);
ResetLedStatus(pLed);
return;
}
if (pLed->bLedOn)
SwLedOff(padapter, pLed);
else
SwLedOn(padapter, pLed);
switch (pLed->CurrLedState) {
case LED_BLINK_SLOWLY:
schedule_delayed_work(&pLed->blink_work, LED_BLINK_NO_LINK_INTVL);
break;
case LED_BLINK_NORMAL:
schedule_delayed_work(&pLed->blink_work, LED_BLINK_LINK_INTVL);
break;
case LED_BLINK_SCAN:
pLed->BlinkTimes--;
if (pLed->BlinkTimes == 0) {
if (check_fwstate(pmlmepriv, _FW_LINKED)) {
pLed->bLedLinkBlinkInProgress = true;
pLed->CurrLedState = LED_BLINK_NORMAL;
schedule_delayed_work(&pLed->blink_work, LED_BLINK_LINK_INTVL);
} else {
pLed->CurrLedState = LED_BLINK_SLOWLY;
schedule_delayed_work(&pLed->blink_work, LED_BLINK_NO_LINK_INTVL);
}
pLed->bLedScanBlinkInProgress = false;
} else {
schedule_delayed_work(&pLed->blink_work, LED_BLINK_SCAN_INTVL);
}
break;
case LED_BLINK_TXRX:
pLed->BlinkTimes--;
if (pLed->BlinkTimes == 0) {
if (check_fwstate(pmlmepriv, _FW_LINKED)) {
pLed->bLedLinkBlinkInProgress = true;
pLed->CurrLedState = LED_BLINK_NORMAL;
schedule_delayed_work(&pLed->blink_work, LED_BLINK_LINK_INTVL);
} else {
pLed->CurrLedState = LED_BLINK_SLOWLY;
schedule_delayed_work(&pLed->blink_work, LED_BLINK_NO_LINK_INTVL);
}
pLed->bLedBlinkInProgress = false;
} else {
schedule_delayed_work(&pLed->blink_work, LED_BLINK_FASTER_INTVL);
}
break;
case LED_BLINK_WPS:
schedule_delayed_work(&pLed->blink_work, LED_BLINK_SCAN_INTVL);
break;
case LED_BLINK_WPS_STOP: /* WPS success */
if (!pLed->bLedOn) {
pLed->bLedLinkBlinkInProgress = true;
pLed->CurrLedState = LED_BLINK_NORMAL;
schedule_delayed_work(&pLed->blink_work, LED_BLINK_LINK_INTVL);
pLed->bLedWPSBlinkInProgress = false;
} else {
schedule_delayed_work(&pLed->blink_work, LED_BLINK_WPS_SUCESS_INTVL);
}
break;
default:
break;
}
}
void rtl8188eu_InitSwLeds(struct adapter *padapter)
{
struct led_priv *pledpriv = &padapter->ledpriv;
pledpriv->padapter = padapter;
ResetLedStatus(pledpriv);
INIT_DELAYED_WORK(&pledpriv->blink_work, blink_work);
}
void rtl8188eu_DeInitSwLeds(struct adapter *padapter)
{
struct led_priv *ledpriv = &padapter->ledpriv;
cancel_delayed_work_sync(&ledpriv->blink_work);
ResetLedStatus(ledpriv);
SwLedOff(padapter, ledpriv);
}
void rtw_led_control(struct adapter *padapter, enum LED_CTL_MODE LedAction)
{
struct led_priv *pLed = &padapter->ledpriv;
struct registry_priv *registry_par;
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
if ((padapter->bSurpriseRemoved) || (padapter->bDriverStopped) ||
(!padapter->hw_init_completed))
return;
if (!pLed->bRegUseLed)
return;
registry_par = &padapter->registrypriv;
if (!registry_par->led_enable)
return;
switch (LedAction) {
case LED_CTL_START_TO_LINK:
case LED_CTL_NO_LINK:
if (pLed->CurrLedState == LED_BLINK_SCAN || IS_LED_WPS_BLINKING(pLed))
return;
cancel_delayed_work(&pLed->blink_work);
pLed->bLedLinkBlinkInProgress = false;
pLed->bLedBlinkInProgress = false;
pLed->CurrLedState = LED_BLINK_SLOWLY;
schedule_delayed_work(&pLed->blink_work, LED_BLINK_NO_LINK_INTVL);
break;
case LED_CTL_LINK:
if (!pLed->bLedLinkBlinkInProgress)
return;
if (pLed->CurrLedState == LED_BLINK_SCAN || IS_LED_WPS_BLINKING(pLed))
return;
cancel_delayed_work(&pLed->blink_work);
pLed->bLedBlinkInProgress = false;
pLed->bLedLinkBlinkInProgress = true;
pLed->CurrLedState = LED_BLINK_NORMAL;
schedule_delayed_work(&pLed->blink_work, LED_BLINK_LINK_INTVL);
break;
case LED_CTL_SITE_SURVEY:
if ((pmlmepriv->LinkDetectInfo.bBusyTraffic) && (check_fwstate(pmlmepriv, _FW_LINKED)))
return;
if (pLed->bLedScanBlinkInProgress)
return;
if (IS_LED_WPS_BLINKING(pLed))
return;
cancel_delayed_work(&pLed->blink_work);
pLed->bLedLinkBlinkInProgress = false;
pLed->bLedBlinkInProgress = false;
pLed->bLedScanBlinkInProgress = true;
pLed->CurrLedState = LED_BLINK_SCAN;
pLed->BlinkTimes = 24;
schedule_delayed_work(&pLed->blink_work, LED_BLINK_SCAN_INTVL);
break;
case LED_CTL_TX:
case LED_CTL_RX:
if (pLed->bLedBlinkInProgress)
return;
if (pLed->CurrLedState == LED_BLINK_SCAN || IS_LED_WPS_BLINKING(pLed))
return;
cancel_delayed_work(&pLed->blink_work);
pLed->bLedLinkBlinkInProgress = false;
pLed->bLedBlinkInProgress = true;
pLed->CurrLedState = LED_BLINK_TXRX;
pLed->BlinkTimes = 2;
schedule_delayed_work(&pLed->blink_work, LED_BLINK_FASTER_INTVL);
break;
case LED_CTL_START_WPS: /* wait until xinpin finish */
if (pLed->bLedWPSBlinkInProgress)
return;
cancel_delayed_work(&pLed->blink_work);
pLed->bLedLinkBlinkInProgress = false;
pLed->bLedBlinkInProgress = false;
pLed->bLedScanBlinkInProgress = false;
pLed->bLedWPSBlinkInProgress = true;
pLed->CurrLedState = LED_BLINK_WPS;
schedule_delayed_work(&pLed->blink_work, LED_BLINK_SCAN_INTVL);
break;
case LED_CTL_STOP_WPS:
cancel_delayed_work(&pLed->blink_work);
pLed->bLedLinkBlinkInProgress = false;
pLed->bLedBlinkInProgress = false;
pLed->bLedScanBlinkInProgress = false;
pLed->bLedWPSBlinkInProgress = true;
pLed->CurrLedState = LED_BLINK_WPS_STOP;
if (pLed->bLedOn) {
schedule_delayed_work(&pLed->blink_work, LED_BLINK_WPS_SUCESS_INTVL);
} else {
schedule_delayed_work(&pLed->blink_work, 0);
}
break;
case LED_CTL_STOP_WPS_FAIL:
cancel_delayed_work(&pLed->blink_work);
pLed->bLedWPSBlinkInProgress = false;
pLed->CurrLedState = LED_BLINK_SLOWLY;
schedule_delayed_work(&pLed->blink_work, LED_BLINK_NO_LINK_INTVL);
break;
case LED_CTL_POWER_OFF:
pLed->CurrLedState = RTW_LED_OFF;
pLed->bLedLinkBlinkInProgress = false;
pLed->bLedBlinkInProgress = false;
pLed->bLedWPSBlinkInProgress = false;
pLed->bLedScanBlinkInProgress = false;
cancel_delayed_work(&pLed->blink_work);
SwLedOff(padapter, pLed);
break;
default:
break;
}
}