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 	u8	LedCfg;
36 	int res;
37 
38 	if (padapter->bDriverStopped)
39 		return;
40 
41 	res = rtw_read8(padapter, REG_LEDCFG2, &LedCfg);
42 	if (res)
43 		return;
44 
45 	rtw_write8(padapter, REG_LEDCFG2, (LedCfg & 0xf0) | BIT(5) | BIT(6)); /*  SW control led0 on. */
46 	pLed->bLedOn = true;
47 }
48 
SwLedOff(struct adapter * padapter,struct led_priv * pLed)49 static void SwLedOff(struct adapter *padapter, struct led_priv *pLed)
50 {
51 	u8	LedCfg;
52 	int res;
53 
54 	if (padapter->bDriverStopped)
55 		goto exit;
56 
57 	res = rtw_read8(padapter, REG_LEDCFG2, &LedCfg);/* 0x4E */
58 	if (res)
59 		goto exit;
60 
61 	LedCfg &= 0x90; /*  Set to software control. */
62 	rtw_write8(padapter, REG_LEDCFG2, (LedCfg | BIT(3)));
63 	res = rtw_read8(padapter, REG_MAC_PINMUX_CFG, &LedCfg);
64 	if (res)
65 		goto exit;
66 
67 	LedCfg &= 0xFE;
68 	rtw_write8(padapter, REG_MAC_PINMUX_CFG, LedCfg);
69 exit:
70 	pLed->bLedOn = false;
71 }
72 
blink_work(struct work_struct * work)73 static void blink_work(struct work_struct *work)
74 {
75 	struct delayed_work *dwork = to_delayed_work(work);
76 	struct led_priv *pLed = container_of(dwork, struct led_priv, blink_work);
77 	struct adapter *padapter = pLed->padapter;
78 	struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
79 
80 	if (padapter->pwrctrlpriv.rf_pwrstate != rf_on) {
81 		SwLedOff(padapter, pLed);
82 		ResetLedStatus(pLed);
83 		return;
84 	}
85 
86 	if (pLed->bLedOn)
87 		SwLedOff(padapter, pLed);
88 	else
89 		SwLedOn(padapter, pLed);
90 
91 	switch (pLed->CurrLedState) {
92 	case LED_BLINK_SLOWLY:
93 		schedule_delayed_work(&pLed->blink_work, LED_BLINK_NO_LINK_INTVL);
94 		break;
95 	case LED_BLINK_NORMAL:
96 		schedule_delayed_work(&pLed->blink_work, LED_BLINK_LINK_INTVL);
97 		break;
98 	case LED_BLINK_SCAN:
99 		pLed->BlinkTimes--;
100 		if (pLed->BlinkTimes == 0) {
101 			if (check_fwstate(pmlmepriv, _FW_LINKED)) {
102 				pLed->bLedLinkBlinkInProgress = true;
103 				pLed->CurrLedState = LED_BLINK_NORMAL;
104 				schedule_delayed_work(&pLed->blink_work, LED_BLINK_LINK_INTVL);
105 			} else {
106 				pLed->CurrLedState = LED_BLINK_SLOWLY;
107 				schedule_delayed_work(&pLed->blink_work, LED_BLINK_NO_LINK_INTVL);
108 			}
109 			pLed->bLedScanBlinkInProgress = false;
110 		} else {
111 			schedule_delayed_work(&pLed->blink_work, LED_BLINK_SCAN_INTVL);
112 		}
113 		break;
114 	case LED_BLINK_TXRX:
115 		pLed->BlinkTimes--;
116 		if (pLed->BlinkTimes == 0) {
117 			if (check_fwstate(pmlmepriv, _FW_LINKED)) {
118 				pLed->bLedLinkBlinkInProgress = true;
119 				pLed->CurrLedState = LED_BLINK_NORMAL;
120 				schedule_delayed_work(&pLed->blink_work, LED_BLINK_LINK_INTVL);
121 			} else {
122 				pLed->CurrLedState = LED_BLINK_SLOWLY;
123 				schedule_delayed_work(&pLed->blink_work, LED_BLINK_NO_LINK_INTVL);
124 			}
125 			pLed->bLedBlinkInProgress = false;
126 		} else {
127 			schedule_delayed_work(&pLed->blink_work, LED_BLINK_FASTER_INTVL);
128 		}
129 		break;
130 	case LED_BLINK_WPS:
131 		schedule_delayed_work(&pLed->blink_work, LED_BLINK_SCAN_INTVL);
132 		break;
133 	case LED_BLINK_WPS_STOP:	/* WPS success */
134 		if (!pLed->bLedOn) {
135 			pLed->bLedLinkBlinkInProgress = true;
136 			pLed->CurrLedState = LED_BLINK_NORMAL;
137 			schedule_delayed_work(&pLed->blink_work, LED_BLINK_LINK_INTVL);
138 
139 			pLed->bLedWPSBlinkInProgress = false;
140 		} else {
141 			schedule_delayed_work(&pLed->blink_work, LED_BLINK_WPS_SUCESS_INTVL);
142 		}
143 		break;
144 	default:
145 		break;
146 	}
147 }
148 
rtl8188eu_InitSwLeds(struct adapter * padapter)149 void rtl8188eu_InitSwLeds(struct adapter *padapter)
150 {
151 	struct led_priv *pledpriv = &padapter->ledpriv;
152 
153 	pledpriv->padapter = padapter;
154 	ResetLedStatus(pledpriv);
155 	INIT_DELAYED_WORK(&pledpriv->blink_work, blink_work);
156 }
157 
rtl8188eu_DeInitSwLeds(struct adapter * padapter)158 void rtl8188eu_DeInitSwLeds(struct adapter *padapter)
159 {
160 	struct led_priv	*ledpriv = &padapter->ledpriv;
161 
162 	cancel_delayed_work_sync(&ledpriv->blink_work);
163 	ResetLedStatus(ledpriv);
164 	SwLedOff(padapter, ledpriv);
165 }
166 
rtw_led_control(struct adapter * padapter,enum LED_CTL_MODE LedAction)167 void rtw_led_control(struct adapter *padapter, enum LED_CTL_MODE LedAction)
168 {
169 	struct led_priv *pLed = &padapter->ledpriv;
170 	struct registry_priv *registry_par;
171 	struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
172 
173 	if ((padapter->bSurpriseRemoved) || (padapter->bDriverStopped) ||
174 	    (!padapter->hw_init_completed))
175 		return;
176 
177 	if (!pLed->bRegUseLed)
178 		return;
179 
180 	registry_par = &padapter->registrypriv;
181 	if (!registry_par->led_enable)
182 		return;
183 
184 	switch (LedAction) {
185 	case LED_CTL_START_TO_LINK:
186 	case LED_CTL_NO_LINK:
187 		if (pLed->CurrLedState == LED_BLINK_SCAN || IS_LED_WPS_BLINKING(pLed))
188 			return;
189 
190 		cancel_delayed_work(&pLed->blink_work);
191 
192 		pLed->bLedLinkBlinkInProgress = false;
193 		pLed->bLedBlinkInProgress = false;
194 
195 		pLed->CurrLedState = LED_BLINK_SLOWLY;
196 		schedule_delayed_work(&pLed->blink_work, LED_BLINK_NO_LINK_INTVL);
197 		break;
198 	case LED_CTL_LINK:
199 		if (!pLed->bLedLinkBlinkInProgress)
200 			return;
201 
202 		if (pLed->CurrLedState == LED_BLINK_SCAN || IS_LED_WPS_BLINKING(pLed))
203 			return;
204 
205 		cancel_delayed_work(&pLed->blink_work);
206 
207 		pLed->bLedBlinkInProgress = false;
208 		pLed->bLedLinkBlinkInProgress = true;
209 
210 		pLed->CurrLedState = LED_BLINK_NORMAL;
211 		schedule_delayed_work(&pLed->blink_work, LED_BLINK_LINK_INTVL);
212 		break;
213 	case LED_CTL_SITE_SURVEY:
214 		if ((pmlmepriv->LinkDetectInfo.bBusyTraffic) && (check_fwstate(pmlmepriv, _FW_LINKED)))
215 			return;
216 
217 		if (pLed->bLedScanBlinkInProgress)
218 			return;
219 
220 		if (IS_LED_WPS_BLINKING(pLed))
221 			return;
222 
223 		cancel_delayed_work(&pLed->blink_work);
224 
225 		pLed->bLedLinkBlinkInProgress = false;
226 		pLed->bLedBlinkInProgress = false;
227 		pLed->bLedScanBlinkInProgress = true;
228 
229 		pLed->CurrLedState = LED_BLINK_SCAN;
230 		pLed->BlinkTimes = 24;
231 		schedule_delayed_work(&pLed->blink_work, LED_BLINK_SCAN_INTVL);
232 		break;
233 	case LED_CTL_TX:
234 	case LED_CTL_RX:
235 		if (pLed->bLedBlinkInProgress)
236 			return;
237 
238 		if (pLed->CurrLedState == LED_BLINK_SCAN || IS_LED_WPS_BLINKING(pLed))
239 			return;
240 
241 		cancel_delayed_work(&pLed->blink_work);
242 
243 		pLed->bLedLinkBlinkInProgress = false;
244 		pLed->bLedBlinkInProgress = true;
245 
246 		pLed->CurrLedState = LED_BLINK_TXRX;
247 		pLed->BlinkTimes = 2;
248 		schedule_delayed_work(&pLed->blink_work, LED_BLINK_FASTER_INTVL);
249 		break;
250 	case LED_CTL_START_WPS: /* wait until xinpin finish */
251 		if (pLed->bLedWPSBlinkInProgress)
252 			return;
253 
254 		cancel_delayed_work(&pLed->blink_work);
255 
256 		pLed->bLedLinkBlinkInProgress = false;
257 		pLed->bLedBlinkInProgress = false;
258 		pLed->bLedScanBlinkInProgress = false;
259 		pLed->bLedWPSBlinkInProgress = true;
260 		pLed->CurrLedState = LED_BLINK_WPS;
261 		schedule_delayed_work(&pLed->blink_work, LED_BLINK_SCAN_INTVL);
262 		break;
263 	case LED_CTL_STOP_WPS:
264 		cancel_delayed_work(&pLed->blink_work);
265 
266 		pLed->bLedLinkBlinkInProgress = false;
267 		pLed->bLedBlinkInProgress = false;
268 		pLed->bLedScanBlinkInProgress = false;
269 		pLed->bLedWPSBlinkInProgress = true;
270 
271 		pLed->CurrLedState = LED_BLINK_WPS_STOP;
272 		if (pLed->bLedOn) {
273 			schedule_delayed_work(&pLed->blink_work, LED_BLINK_WPS_SUCESS_INTVL);
274 		} else {
275 			schedule_delayed_work(&pLed->blink_work, 0);
276 		}
277 		break;
278 	case LED_CTL_STOP_WPS_FAIL:
279 		cancel_delayed_work(&pLed->blink_work);
280 		pLed->bLedWPSBlinkInProgress = false;
281 		pLed->CurrLedState = LED_BLINK_SLOWLY;
282 		schedule_delayed_work(&pLed->blink_work, LED_BLINK_NO_LINK_INTVL);
283 		break;
284 	case LED_CTL_POWER_OFF:
285 		pLed->CurrLedState = RTW_LED_OFF;
286 		pLed->bLedLinkBlinkInProgress = false;
287 		pLed->bLedBlinkInProgress = false;
288 		pLed->bLedWPSBlinkInProgress = false;
289 		pLed->bLedScanBlinkInProgress = false;
290 		cancel_delayed_work(&pLed->blink_work);
291 		SwLedOff(padapter, pLed);
292 		break;
293 	default:
294 		break;
295 	}
296 }
297