1 /*
2 * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved.
3 * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for
6 * any purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
8 * copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 /**
21 * DOC: This file contains main green ap function definitions
22 */
23
24 #include "wlan_green_ap_main_i.h"
25
26 /**
27 * wlan_green_ap_ant_ps_reset() - Reset function
28 * @green_ap_ctx: green ap context
29 *
30 * Reset function, so that Antenna Mask can come into effect.
31 * This applies for only few of the hardware chips
32 *
33 * Return: QDF_STATUS
34 */
wlan_green_ap_ant_ps_reset(struct wlan_pdev_green_ap_ctx * green_ap_ctx)35 static QDF_STATUS wlan_green_ap_ant_ps_reset
36 (struct wlan_pdev_green_ap_ctx *green_ap_ctx)
37 {
38 struct wlan_lmac_if_green_ap_tx_ops *green_ap_tx_ops;
39 struct wlan_objmgr_pdev *pdev;
40
41 if (!green_ap_ctx) {
42 green_ap_err("green ap context obtained is NULL");
43 return QDF_STATUS_E_FAILURE;
44 }
45 pdev = green_ap_ctx->pdev;
46
47 green_ap_tx_ops = wlan_psoc_get_green_ap_tx_ops(green_ap_ctx);
48 if (!green_ap_tx_ops) {
49 green_ap_err("green ap tx ops obtained are NULL");
50 return QDF_STATUS_E_FAILURE;
51 }
52
53 if (!green_ap_tx_ops->reset_dev)
54 return QDF_STATUS_SUCCESS;
55
56 /*
57 * Add protection against green AP enabling interrupts
58 * when not valid or no VAPs exist
59 */
60 if (wlan_util_is_vdev_active(pdev, WLAN_GREEN_AP_ID) ==
61 QDF_STATUS_SUCCESS)
62 green_ap_tx_ops->reset_dev(pdev);
63 else
64 green_ap_err("Green AP tried to enable IRQs when invalid");
65
66 return QDF_STATUS_SUCCESS;
67 }
68
69 struct wlan_lmac_if_green_ap_tx_ops *
wlan_psoc_get_green_ap_tx_ops(struct wlan_pdev_green_ap_ctx * green_ap_ctx)70 wlan_psoc_get_green_ap_tx_ops(struct wlan_pdev_green_ap_ctx *green_ap_ctx)
71 {
72 struct wlan_objmgr_psoc *psoc;
73 struct wlan_objmgr_pdev *pdev = green_ap_ctx->pdev;
74 struct wlan_lmac_if_tx_ops *tx_ops;
75
76 if (!pdev) {
77 green_ap_err("pdev context obtained is NULL");
78 return NULL;
79 }
80
81 psoc = wlan_pdev_get_psoc(pdev);
82 if (!psoc) {
83 green_ap_err("pdev context obtained is NULL");
84 return NULL;
85 }
86
87 tx_ops = wlan_psoc_get_lmac_if_txops(psoc);
88 if (!tx_ops) {
89 green_ap_err("tx_ops is NULL");
90 return NULL;
91 }
92
93 return &tx_ops->green_ap_tx_ops;
94 }
95
wlan_is_egap_enabled(struct wlan_pdev_green_ap_ctx * green_ap_ctx)96 bool wlan_is_egap_enabled(struct wlan_pdev_green_ap_ctx *green_ap_ctx)
97 {
98 struct wlan_green_ap_egap_params *egap_params;
99
100 if (!green_ap_ctx) {
101 green_ap_err("green ap context passed is NULL");
102 return QDF_STATUS_E_INVAL;
103 }
104 egap_params = &green_ap_ctx->egap_params;
105
106 if (egap_params->fw_egap_support &&
107 egap_params->host_enable_egap &&
108 egap_params->egap_feature_flags)
109 return true;
110 return false;
111 }
112 qdf_export_symbol(wlan_is_egap_enabled);
113
114 /**
115 * wlan_green_ap_ps_event_state_update() - Update PS state and event
116 * @green_ap_ctx: Green AP pdev context
117 * @state: ps state
118 * @event: ps event
119 *
120 * Return: Success or Failure
121 */
wlan_green_ap_ps_event_state_update(struct wlan_pdev_green_ap_ctx * green_ap_ctx,enum wlan_green_ap_ps_state state,enum wlan_green_ap_ps_event event)122 static QDF_STATUS wlan_green_ap_ps_event_state_update(
123 struct wlan_pdev_green_ap_ctx *green_ap_ctx,
124 enum wlan_green_ap_ps_state state,
125 enum wlan_green_ap_ps_event event)
126 {
127 if (!green_ap_ctx) {
128 green_ap_err("green ap context obtained is NULL");
129 return QDF_STATUS_E_FAILURE;
130 }
131
132 green_ap_ctx->ps_state = state;
133 green_ap_ctx->ps_event = event;
134
135 return QDF_STATUS_SUCCESS;
136 }
137
wlan_green_ap_state_mc(struct wlan_pdev_green_ap_ctx * green_ap_ctx,enum wlan_green_ap_ps_event event)138 QDF_STATUS wlan_green_ap_state_mc(struct wlan_pdev_green_ap_ctx *green_ap_ctx,
139 enum wlan_green_ap_ps_event event)
140 {
141 struct wlan_lmac_if_green_ap_tx_ops *green_ap_tx_ops;
142 uint8_t pdev_id;
143
144 /*
145 * Remove the assignments once channel info is available for
146 * converged component.
147 */
148 uint16_t channel = 1;
149 uint32_t channel_flags = 1;
150
151 if (!green_ap_ctx) {
152 green_ap_err("green ap context obtained is NULL");
153 return QDF_STATUS_E_FAILURE;
154 }
155
156 if (!green_ap_ctx->pdev) {
157 green_ap_err("pdev obtained is NULL");
158 return QDF_STATUS_E_FAILURE;
159 }
160 pdev_id = wlan_objmgr_pdev_get_pdev_id(green_ap_ctx->pdev);
161
162 green_ap_tx_ops = wlan_psoc_get_green_ap_tx_ops(green_ap_ctx);
163 if (!green_ap_tx_ops) {
164 green_ap_err("green ap tx ops obtained are NULL");
165 return QDF_STATUS_E_FAILURE;
166 }
167
168 if (!green_ap_tx_ops->ps_on_off_send) {
169 green_ap_err("tx op for sending enable/disable green ap is NULL");
170 return QDF_STATUS_E_FAILURE;
171 }
172
173 qdf_spin_lock_bh(&green_ap_ctx->lock);
174
175 if (green_ap_tx_ops->get_current_channel)
176 channel = green_ap_tx_ops->get_current_channel(
177 green_ap_ctx->pdev);
178
179 if (green_ap_tx_ops->get_current_channel_flags)
180 channel_flags = green_ap_tx_ops->get_current_channel_flags(
181 green_ap_ctx->pdev);
182
183 /* handle the green ap ps event */
184 switch (event) {
185 case WLAN_GREEN_AP_ADD_STA_EVENT:
186 green_ap_ctx->num_nodes++;
187 break;
188
189 case WLAN_GREEN_AP_DEL_STA_EVENT:
190 if (green_ap_ctx->num_nodes)
191 green_ap_ctx->num_nodes--;
192 break;
193
194 case WLAN_GREEN_AP_ADD_MULTISTREAM_STA_EVENT:
195 green_ap_ctx->num_nodes_multistream++;
196 break;
197
198 case WLAN_GREEN_AP_DEL_MULTISTREAM_STA_EVENT:
199 if (green_ap_ctx->num_nodes_multistream)
200 green_ap_ctx->num_nodes_multistream--;
201 break;
202
203 case WLAN_GREEN_AP_PS_START_EVENT:
204 case WLAN_GREEN_AP_PS_STOP_EVENT:
205 case WLAN_GREEN_AP_PS_ON_EVENT:
206 case WLAN_GREEN_AP_PS_WAIT_EVENT:
207 break;
208
209 default:
210 green_ap_err("Invalid event: %d", event);
211 break;
212 }
213
214 green_ap_debug("Green-AP event: %d, state: %d, num_nodes: %d",
215 event, green_ap_ctx->ps_state, green_ap_ctx->num_nodes);
216
217 /* Confirm that power save is enabled before doing state transitions */
218 if (!green_ap_ctx->ps_enable) {
219 green_ap_debug("Green-AP is disabled");
220 if (green_ap_ctx->ps_state == WLAN_GREEN_AP_PS_ON_STATE) {
221 if (green_ap_tx_ops->ps_on_off_send(green_ap_ctx->pdev,
222 false, pdev_id))
223 green_ap_err("failed to set green ap mode");
224 wlan_green_ap_ant_ps_reset(green_ap_ctx);
225 }
226 wlan_green_ap_ps_event_state_update(
227 green_ap_ctx,
228 WLAN_GREEN_AP_PS_IDLE_STATE,
229 WLAN_GREEN_AP_PS_WAIT_EVENT);
230 goto done;
231 }
232
233 /* handle the green ap ps state */
234 switch (green_ap_ctx->ps_state) {
235 case WLAN_GREEN_AP_PS_IDLE_STATE:
236 if ((green_ap_ctx->num_nodes &&
237 green_ap_ctx->ps_mode == WLAN_GREEN_AP_MODE_NO_STA) ||
238 (green_ap_ctx->num_nodes_multistream &&
239 green_ap_ctx->ps_mode == WLAN_GREEN_AP_MODE_NUM_STREAM)) {
240 /*
241 * Multistream nodes present, switchoff the power save
242 */
243 green_ap_info("Transition to OFF from IDLE");
244 wlan_green_ap_ps_event_state_update(
245 green_ap_ctx,
246 WLAN_GREEN_AP_PS_OFF_STATE,
247 WLAN_GREEN_AP_PS_WAIT_EVENT);
248 } else {
249 /* No Active nodes, get into power save */
250 green_ap_info("Transition to WAIT from IDLE");
251 wlan_green_ap_ps_event_state_update(
252 green_ap_ctx,
253 WLAN_GREEN_AP_PS_WAIT_STATE,
254 WLAN_GREEN_AP_PS_WAIT_EVENT);
255 qdf_timer_start(&green_ap_ctx->ps_timer,
256 green_ap_ctx->ps_trans_time * 1000);
257 }
258 break;
259
260 case WLAN_GREEN_AP_PS_OFF_STATE:
261 if ((!green_ap_ctx->num_nodes &&
262 green_ap_ctx->ps_mode == WLAN_GREEN_AP_MODE_NO_STA) ||
263 (!green_ap_ctx->num_nodes_multistream &&
264 green_ap_ctx->ps_mode == WLAN_GREEN_AP_MODE_NUM_STREAM)) {
265 green_ap_info("Transition to WAIT from OFF");
266 wlan_green_ap_ps_event_state_update(
267 green_ap_ctx,
268 WLAN_GREEN_AP_PS_WAIT_STATE,
269 WLAN_GREEN_AP_PS_WAIT_EVENT);
270 qdf_timer_start(&green_ap_ctx->ps_timer,
271 green_ap_ctx->ps_trans_time * 1000);
272 }
273 break;
274
275 case WLAN_GREEN_AP_PS_WAIT_STATE:
276 if ((!green_ap_ctx->num_nodes &&
277 green_ap_ctx->ps_mode == WLAN_GREEN_AP_MODE_NO_STA) ||
278 (!green_ap_ctx->num_nodes_multistream &&
279 green_ap_ctx->ps_mode == WLAN_GREEN_AP_MODE_NUM_STREAM)) {
280 if (event == WLAN_GREEN_AP_DEL_MULTISTREAM_STA_EVENT)
281 break;
282 if ((channel == 0) || (channel_flags == 0)) {
283 /*
284 * Stay in the current state and restart the
285 * timer to check later.
286 */
287 qdf_timer_start(&green_ap_ctx->ps_timer,
288 green_ap_ctx->ps_on_time * 1000);
289 } else {
290 wlan_green_ap_ps_event_state_update(
291 green_ap_ctx,
292 WLAN_GREEN_AP_PS_ON_STATE,
293 WLAN_GREEN_AP_PS_WAIT_EVENT);
294
295 green_ap_info("Transition to ON from WAIT");
296 green_ap_tx_ops->ps_on_off_send(
297 green_ap_ctx->pdev, true, pdev_id);
298 wlan_green_ap_ant_ps_reset(green_ap_ctx);
299
300 if (green_ap_ctx->ps_on_time)
301 qdf_timer_start(&green_ap_ctx->ps_timer,
302 green_ap_ctx->ps_on_time * 1000);
303 }
304 } else {
305 green_ap_info("Transition to OFF from WAIT");
306 qdf_timer_stop(&green_ap_ctx->ps_timer);
307 wlan_green_ap_ps_event_state_update(
308 green_ap_ctx,
309 WLAN_GREEN_AP_PS_OFF_STATE,
310 WLAN_GREEN_AP_PS_WAIT_EVENT);
311 }
312 break;
313
314 case WLAN_GREEN_AP_PS_ON_STATE:
315 if ((green_ap_ctx->num_nodes &&
316 green_ap_ctx->ps_mode == WLAN_GREEN_AP_MODE_NO_STA) ||
317 (green_ap_ctx->num_nodes_multistream &&
318 green_ap_ctx->ps_mode == WLAN_GREEN_AP_MODE_NUM_STREAM)) {
319 qdf_timer_stop(&green_ap_ctx->ps_timer);
320 if (green_ap_tx_ops->ps_on_off_send(
321 green_ap_ctx->pdev, false, pdev_id)) {
322 green_ap_err("Failed to set Green AP mode");
323 goto done;
324 }
325 wlan_green_ap_ant_ps_reset(green_ap_ctx);
326 green_ap_info("Transition to OFF from ON\n");
327 wlan_green_ap_ps_event_state_update(
328 green_ap_ctx,
329 WLAN_GREEN_AP_PS_OFF_STATE,
330 WLAN_GREEN_AP_PS_WAIT_EVENT);
331 } else if ((green_ap_ctx->ps_event ==
332 WLAN_GREEN_AP_PS_WAIT_EVENT) &&
333 (green_ap_ctx->ps_on_time)) {
334 /* ps_on_time timeout, switch to ps wait */
335 wlan_green_ap_ps_event_state_update(
336 green_ap_ctx,
337 WLAN_GREEN_AP_PS_WAIT_STATE,
338 WLAN_GREEN_AP_PS_ON_EVENT);
339
340 if (green_ap_tx_ops->ps_on_off_send(
341 green_ap_ctx->pdev, false, pdev_id)) {
342 green_ap_err("Failed to set Green AP mode");
343 goto done;
344 }
345
346 wlan_green_ap_ant_ps_reset(green_ap_ctx);
347 green_ap_info("Transition to WAIT from ON\n");
348 qdf_timer_start(&green_ap_ctx->ps_timer,
349 green_ap_ctx->ps_trans_time * 1000);
350 }
351 break;
352
353 default:
354 green_ap_err("invalid state %d", green_ap_ctx->ps_state);
355 wlan_green_ap_ps_event_state_update(
356 green_ap_ctx,
357 WLAN_GREEN_AP_PS_OFF_STATE,
358 WLAN_GREEN_AP_PS_WAIT_EVENT);
359 break;
360 }
361
362 done:
363 qdf_spin_unlock_bh(&green_ap_ctx->lock);
364 return QDF_STATUS_SUCCESS;
365 }
366
wlan_green_ap_timer_fn(void * pdev)367 void wlan_green_ap_timer_fn(void *pdev)
368 {
369 struct wlan_pdev_green_ap_ctx *green_ap_ctx;
370 struct wlan_objmgr_pdev *pdev_ctx = (struct wlan_objmgr_pdev *)pdev;
371
372 if (!pdev_ctx) {
373 green_ap_err("pdev context passed is NULL");
374 return;
375 }
376
377 green_ap_ctx = wlan_objmgr_pdev_get_comp_private_obj(
378 pdev_ctx, WLAN_UMAC_COMP_GREEN_AP);
379 if (!green_ap_ctx) {
380 green_ap_err("green ap context obtained is NULL");
381 return;
382 }
383 wlan_green_ap_state_mc(green_ap_ctx, green_ap_ctx->ps_event);
384 }
385
wlan_green_ap_check_mode(struct wlan_objmgr_pdev * pdev,void * object,void * arg)386 void wlan_green_ap_check_mode(struct wlan_objmgr_pdev *pdev,
387 void *object,
388 void *arg)
389 {
390 struct wlan_objmgr_vdev *vdev = (struct wlan_objmgr_vdev *)object;
391 uint8_t *flag = (uint8_t *)arg;
392 enum QDF_OPMODE mode = QDF_MAX_NO_OF_MODE;
393
394 wlan_vdev_obj_lock(vdev);
395 mode = wlan_vdev_mlme_get_opmode(vdev);
396
397 if (mode != QDF_SAP_MODE && mode != QDF_STA_MODE)
398 *flag = 1;
399
400 wlan_vdev_obj_unlock(vdev);
401 }
402
403 #ifdef WLAN_SUPPORT_GAP_LL_PS_MODE
404 #define LOW_LATENCY_MAX_CMDID 0x00000080
405
wlan_green_ap_get_cookie_id(struct wlan_pdev_green_ap_ctx * green_ap_ctx,enum wlan_green_ap_ll_ps_state state)406 uint32_t wlan_green_ap_get_cookie_id(struct wlan_pdev_green_ap_ctx *green_ap_ctx,
407 enum wlan_green_ap_ll_ps_state state)
408 {
409 uint32_t id;
410 qdf_atomic_t *cmd_cnt;
411
412 qdf_spin_lock_bh(&green_ap_ctx->lock);
413
414 if (!state)
415 cmd_cnt = &green_ap_ctx->ps_dis_cmd_cnt;
416 else
417 cmd_cnt = &green_ap_ctx->ps_en_cmd_cnt;
418
419 id = qdf_atomic_read(cmd_cnt);
420
421 if (id >= LOW_LATENCY_MAX_CMDID) {
422 green_ap_debug("Cookie id Max limit reached");
423
424 if (!state)
425 qdf_atomic_set(cmd_cnt, 0);
426 else
427 qdf_atomic_set(cmd_cnt, 1);
428
429 id = qdf_atomic_read(cmd_cnt);
430 }
431
432 qdf_atomic_add(2, cmd_cnt);
433 qdf_spin_unlock_bh(&green_ap_ctx->lock);
434
435 green_ap_debug("Cookie id : %x", id);
436
437 return id;
438 }
439
440 QDF_STATUS
wlan_green_ap_send_ll_ps_event_params(struct wlan_objmgr_pdev * pdev,struct wlan_green_ap_ll_ps_event_param * event_param)441 wlan_green_ap_send_ll_ps_event_params(struct wlan_objmgr_pdev *pdev,
442 struct wlan_green_ap_ll_ps_event_param *event_param)
443 {
444 QDF_STATUS status;
445 struct wlan_pdev_green_ap_ctx *green_ap_ctx;
446
447 green_ap_ctx = wlan_objmgr_pdev_get_comp_private_obj(
448 pdev, WLAN_UMAC_COMP_GREEN_AP);
449
450 if (!green_ap_ctx) {
451 green_ap_err("green ap context obtained is NULL");
452 return QDF_STATUS_E_FAILURE;
453 }
454
455 status = green_ap_ctx->hdd_cback.send_event(green_ap_ctx->vdev, event_param);
456
457 return status;
458 }
459 #endif
460