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