1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright(c) 2007 - 2012 Realtek Corporation. */
3 
4 #include "../include/drv_types.h"
5 #include "../include/rtw_led.h"
6 #include "../include/rtl8188e_spec.h"
7 
8 #define LED_BLINK_NO_LINK_INTVL			msecs_to_jiffies(1000)
9 #define LED_BLINK_LINK_INTVL			msecs_to_jiffies(500)
10 #define LED_BLINK_SCAN_INTVL			msecs_to_jiffies(180)
11 #define LED_BLINK_FASTER_INTVL			msecs_to_jiffies(50)
12 #define LED_BLINK_WPS_SUCESS_INTVL		msecs_to_jiffies(5000)
13 
14 #define IS_LED_WPS_BLINKING(l) \
15 	((l)->CurrLedState == LED_BLINK_WPS || \
16 	(l)->CurrLedState == LED_BLINK_WPS_STOP || \
17 	(l)->bLedWPSBlinkInProgress)
18 
ResetLedStatus(struct led_priv * pLed)19 static void ResetLedStatus(struct led_priv *pLed)
20 {
21 	pLed->CurrLedState = RTW_LED_OFF; /*  Current LED state. */
22 	pLed->bLedOn = false; /*  true if LED is ON, false if LED is OFF. */
23 
24 	pLed->bLedBlinkInProgress = false; /*  true if it is blinking, false o.w.. */
25 	pLed->bLedWPSBlinkInProgress = false;
26 
27 	pLed->BlinkTimes = 0; /*  Number of times to toggle led state for blinking. */
28 
29 	pLed->bLedLinkBlinkInProgress = false;
30 	pLed->bLedScanBlinkInProgress = false;
31 }
32 
SwLedOn(struct adapter * padapter,struct led_priv * pLed)33 static void SwLedOn(struct adapter *padapter, struct led_priv *pLed)
34 {
35 	if (padapter->bDriverStopped)
36 		return;
37 
38 	rtw_write8(padapter, REG_LEDCFG2, BIT(5)); /*  SW control led0 on. */
39 	pLed->bLedOn = true;
40 }
41 
SwLedOff(struct adapter * padapter,struct led_priv * pLed)42 static void SwLedOff(struct adapter *padapter, struct led_priv *pLed)
43 {
44 	if (padapter->bDriverStopped)
45 		goto exit;
46 
47 	rtw_write8(padapter, REG_LEDCFG2, BIT(5) | BIT(3));
48 exit:
49 	pLed->bLedOn = false;
50 }
51 
blink_work(struct work_struct * work)52 static void blink_work(struct work_struct *work)
53 {
54 	struct delayed_work *dwork = to_delayed_work(work);
55 	struct led_priv *pLed = container_of(dwork, struct led_priv, blink_work);
56 	struct adapter *padapter = pLed->padapter;
57 	struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
58 
59 	if (padapter->pwrctrlpriv.rf_pwrstate != rf_on) {
60 		SwLedOff(padapter, pLed);
61 		ResetLedStatus(pLed);
62 		return;
63 	}
64 
65 	if (pLed->bLedOn)
66 		SwLedOff(padapter, pLed);
67 	else
68 		SwLedOn(padapter, pLed);
69 
70 	switch (pLed->CurrLedState) {
71 	case LED_BLINK_SLOWLY:
72 		schedule_delayed_work(&pLed->blink_work, LED_BLINK_NO_LINK_INTVL);
73 		break;
74 	case LED_BLINK_NORMAL:
75 		schedule_delayed_work(&pLed->blink_work, LED_BLINK_LINK_INTVL);
76 		break;
77 	case LED_BLINK_SCAN:
78 		pLed->BlinkTimes--;
79 		if (pLed->BlinkTimes == 0) {
80 			if (check_fwstate(pmlmepriv, _FW_LINKED)) {
81 				pLed->bLedLinkBlinkInProgress = true;
82 				pLed->CurrLedState = LED_BLINK_NORMAL;
83 				schedule_delayed_work(&pLed->blink_work, LED_BLINK_LINK_INTVL);
84 			} else {
85 				pLed->CurrLedState = LED_BLINK_SLOWLY;
86 				schedule_delayed_work(&pLed->blink_work, LED_BLINK_NO_LINK_INTVL);
87 			}
88 			pLed->bLedScanBlinkInProgress = false;
89 		} else {
90 			schedule_delayed_work(&pLed->blink_work, LED_BLINK_SCAN_INTVL);
91 		}
92 		break;
93 	case LED_BLINK_TXRX:
94 		pLed->BlinkTimes--;
95 		if (pLed->BlinkTimes == 0) {
96 			if (check_fwstate(pmlmepriv, _FW_LINKED)) {
97 				pLed->bLedLinkBlinkInProgress = true;
98 				pLed->CurrLedState = LED_BLINK_NORMAL;
99 				schedule_delayed_work(&pLed->blink_work, LED_BLINK_LINK_INTVL);
100 			} else {
101 				pLed->CurrLedState = LED_BLINK_SLOWLY;
102 				schedule_delayed_work(&pLed->blink_work, LED_BLINK_NO_LINK_INTVL);
103 			}
104 			pLed->bLedBlinkInProgress = false;
105 		} else {
106 			schedule_delayed_work(&pLed->blink_work, LED_BLINK_FASTER_INTVL);
107 		}
108 		break;
109 	case LED_BLINK_WPS:
110 		schedule_delayed_work(&pLed->blink_work, LED_BLINK_SCAN_INTVL);
111 		break;
112 	case LED_BLINK_WPS_STOP:	/* WPS success */
113 		if (!pLed->bLedOn) {
114 			pLed->bLedLinkBlinkInProgress = true;
115 			pLed->CurrLedState = LED_BLINK_NORMAL;
116 			schedule_delayed_work(&pLed->blink_work, LED_BLINK_LINK_INTVL);
117 
118 			pLed->bLedWPSBlinkInProgress = false;
119 		} else {
120 			schedule_delayed_work(&pLed->blink_work, LED_BLINK_WPS_SUCESS_INTVL);
121 		}
122 		break;
123 	default:
124 		break;
125 	}
126 }
127 
rtl8188eu_InitSwLeds(struct adapter * padapter)128 void rtl8188eu_InitSwLeds(struct adapter *padapter)
129 {
130 	struct led_priv *pledpriv = &padapter->ledpriv;
131 
132 	pledpriv->padapter = padapter;
133 	ResetLedStatus(pledpriv);
134 	INIT_DELAYED_WORK(&pledpriv->blink_work, blink_work);
135 }
136 
rtl8188eu_DeInitSwLeds(struct adapter * padapter)137 void rtl8188eu_DeInitSwLeds(struct adapter *padapter)
138 {
139 	struct led_priv	*ledpriv = &padapter->ledpriv;
140 
141 	cancel_delayed_work_sync(&ledpriv->blink_work);
142 	ResetLedStatus(ledpriv);
143 	SwLedOff(padapter, ledpriv);
144 }
145 
rtw_led_control(struct adapter * padapter,enum LED_CTL_MODE LedAction)146 void rtw_led_control(struct adapter *padapter, enum LED_CTL_MODE LedAction)
147 {
148 	struct led_priv *pLed = &padapter->ledpriv;
149 	struct registry_priv *registry_par;
150 	struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
151 
152 	if ((padapter->bSurpriseRemoved) || (padapter->bDriverStopped) ||
153 	    (!padapter->hw_init_completed))
154 		return;
155 
156 	if (!pLed->bRegUseLed)
157 		return;
158 
159 	registry_par = &padapter->registrypriv;
160 	if (!registry_par->led_enable)
161 		return;
162 
163 	switch (LedAction) {
164 	case LED_CTL_START_TO_LINK:
165 	case LED_CTL_NO_LINK:
166 		if (pLed->CurrLedState == LED_BLINK_SCAN || IS_LED_WPS_BLINKING(pLed))
167 			return;
168 
169 		cancel_delayed_work(&pLed->blink_work);
170 
171 		pLed->bLedLinkBlinkInProgress = false;
172 		pLed->bLedBlinkInProgress = false;
173 
174 		pLed->CurrLedState = LED_BLINK_SLOWLY;
175 		schedule_delayed_work(&pLed->blink_work, LED_BLINK_NO_LINK_INTVL);
176 		break;
177 	case LED_CTL_LINK:
178 		if (!pLed->bLedLinkBlinkInProgress)
179 			return;
180 
181 		if (pLed->CurrLedState == LED_BLINK_SCAN || IS_LED_WPS_BLINKING(pLed))
182 			return;
183 
184 		cancel_delayed_work(&pLed->blink_work);
185 
186 		pLed->bLedBlinkInProgress = false;
187 		pLed->bLedLinkBlinkInProgress = true;
188 
189 		pLed->CurrLedState = LED_BLINK_NORMAL;
190 		schedule_delayed_work(&pLed->blink_work, LED_BLINK_LINK_INTVL);
191 		break;
192 	case LED_CTL_SITE_SURVEY:
193 		if ((pmlmepriv->LinkDetectInfo.bBusyTraffic) && (check_fwstate(pmlmepriv, _FW_LINKED)))
194 			return;
195 
196 		if (pLed->bLedScanBlinkInProgress)
197 			return;
198 
199 		if (IS_LED_WPS_BLINKING(pLed))
200 			return;
201 
202 		cancel_delayed_work(&pLed->blink_work);
203 
204 		pLed->bLedLinkBlinkInProgress = false;
205 		pLed->bLedBlinkInProgress = false;
206 		pLed->bLedScanBlinkInProgress = true;
207 
208 		pLed->CurrLedState = LED_BLINK_SCAN;
209 		pLed->BlinkTimes = 24;
210 		schedule_delayed_work(&pLed->blink_work, LED_BLINK_SCAN_INTVL);
211 		break;
212 	case LED_CTL_TX:
213 	case LED_CTL_RX:
214 		if (pLed->bLedBlinkInProgress)
215 			return;
216 
217 		if (pLed->CurrLedState == LED_BLINK_SCAN || IS_LED_WPS_BLINKING(pLed))
218 			return;
219 
220 		cancel_delayed_work(&pLed->blink_work);
221 
222 		pLed->bLedLinkBlinkInProgress = false;
223 		pLed->bLedBlinkInProgress = true;
224 
225 		pLed->CurrLedState = LED_BLINK_TXRX;
226 		pLed->BlinkTimes = 2;
227 		schedule_delayed_work(&pLed->blink_work, LED_BLINK_FASTER_INTVL);
228 		break;
229 	case LED_CTL_START_WPS: /* wait until xinpin finish */
230 		if (pLed->bLedWPSBlinkInProgress)
231 			return;
232 
233 		cancel_delayed_work(&pLed->blink_work);
234 
235 		pLed->bLedLinkBlinkInProgress = false;
236 		pLed->bLedBlinkInProgress = false;
237 		pLed->bLedScanBlinkInProgress = false;
238 		pLed->bLedWPSBlinkInProgress = true;
239 		pLed->CurrLedState = LED_BLINK_WPS;
240 		schedule_delayed_work(&pLed->blink_work, LED_BLINK_SCAN_INTVL);
241 		break;
242 	case LED_CTL_STOP_WPS:
243 		cancel_delayed_work(&pLed->blink_work);
244 
245 		pLed->bLedLinkBlinkInProgress = false;
246 		pLed->bLedBlinkInProgress = false;
247 		pLed->bLedScanBlinkInProgress = false;
248 		pLed->bLedWPSBlinkInProgress = true;
249 
250 		pLed->CurrLedState = LED_BLINK_WPS_STOP;
251 		if (pLed->bLedOn) {
252 			schedule_delayed_work(&pLed->blink_work, LED_BLINK_WPS_SUCESS_INTVL);
253 		} else {
254 			schedule_delayed_work(&pLed->blink_work, 0);
255 		}
256 		break;
257 	case LED_CTL_STOP_WPS_FAIL:
258 		cancel_delayed_work(&pLed->blink_work);
259 		pLed->bLedWPSBlinkInProgress = false;
260 		pLed->CurrLedState = LED_BLINK_SLOWLY;
261 		schedule_delayed_work(&pLed->blink_work, LED_BLINK_NO_LINK_INTVL);
262 		break;
263 	case LED_CTL_POWER_OFF:
264 		pLed->CurrLedState = RTW_LED_OFF;
265 		pLed->bLedLinkBlinkInProgress = false;
266 		pLed->bLedBlinkInProgress = false;
267 		pLed->bLedWPSBlinkInProgress = false;
268 		pLed->bLedScanBlinkInProgress = false;
269 		cancel_delayed_work(&pLed->blink_work);
270 		SwLedOff(padapter, pLed);
271 		break;
272 	default:
273 		break;
274 	}
275 }
276