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