1 /*
2 * Copyright (c) 2015-2021 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 #include "sme_power_save.h"
21 #include "sme_power_save_api.h"
22 #include <sir_common.h>
23 #include <ani_global.h>
24 #include <utils_api.h>
25 #include "sme_trace.h"
26 #include "qdf_mem.h"
27 #include "qdf_types.h"
28 #include "wma.h"
29 #include "wma_internal.h"
30 #include "wmm_apsd.h"
31 #include "csr_inside_api.h"
32
33 /**
34 * sme_post_ps_msg_to_wma(): post message to WMA.
35 * @type: type
36 * @body: body pointer
37 *
38 * Return: QDF_STATUS
39 */
sme_post_ps_msg_to_wma(uint16_t type,void * body)40 static QDF_STATUS sme_post_ps_msg_to_wma(uint16_t type, void *body)
41 {
42 struct scheduler_msg msg = {0};
43
44 msg.type = type;
45 msg.reserved = 0;
46 msg.bodyptr = body;
47 msg.bodyval = 0;
48
49 if (QDF_STATUS_SUCCESS != scheduler_post_message(QDF_MODULE_ID_SME,
50 QDF_MODULE_ID_WMA,
51 QDF_MODULE_ID_WMA,
52 &msg)) {
53 sme_err("Posting message %d failed", type);
54 qdf_mem_free(body);
55 return QDF_STATUS_E_FAILURE;
56 }
57 return QDF_STATUS_SUCCESS;
58 }
59
60 /**
61 * sme_ps_enable_uapsd_req_params(): enables UASPD req params
62 * @mac_ctx: global mac context
63 * @session_id: session id
64 *
65 * Return: QDF_STATUS
66 */
sme_ps_fill_uapsd_req_params(struct mac_context * mac_ctx,tUapsd_Params * uapsdParams,uint32_t session_id,enum ps_state * ps_state)67 static void sme_ps_fill_uapsd_req_params(struct mac_context *mac_ctx,
68 tUapsd_Params *uapsdParams, uint32_t session_id,
69 enum ps_state *ps_state)
70 {
71
72 uint8_t uapsd_delivery_mask = 0;
73 uint8_t uapsd_trigger_mask = 0;
74 struct ps_global_info *ps_global_info = &mac_ctx->sme.ps_global_info;
75 struct ps_params *ps_param = &ps_global_info->ps_params[session_id];
76
77 uapsd_delivery_mask =
78 ps_param->uapsd_per_ac_bit_mask |
79 ps_param->uapsd_per_ac_delivery_enable_mask;
80
81 uapsd_trigger_mask =
82 ps_param->uapsd_per_ac_bit_mask |
83 ps_param->uapsd_per_ac_trigger_enable_mask;
84
85 uapsdParams->bkDeliveryEnabled =
86 LIM_UAPSD_GET(ACBK, uapsd_delivery_mask);
87
88 uapsdParams->beDeliveryEnabled =
89 LIM_UAPSD_GET(ACBE, uapsd_delivery_mask);
90
91 uapsdParams->viDeliveryEnabled =
92 LIM_UAPSD_GET(ACVI, uapsd_delivery_mask);
93
94 uapsdParams->voDeliveryEnabled =
95 LIM_UAPSD_GET(ACVO, uapsd_delivery_mask);
96
97 uapsdParams->bkTriggerEnabled =
98 LIM_UAPSD_GET(ACBK, uapsd_trigger_mask);
99
100 uapsdParams->beTriggerEnabled =
101 LIM_UAPSD_GET(ACBE, uapsd_trigger_mask);
102
103 uapsdParams->viTriggerEnabled =
104 LIM_UAPSD_GET(ACVI, uapsd_trigger_mask);
105
106 uapsdParams->voTriggerEnabled =
107 LIM_UAPSD_GET(ACVO, uapsd_trigger_mask);
108 if (ps_param->ps_state != FULL_POWER_MODE) {
109 uapsdParams->enable_ps = true;
110 *ps_state = UAPSD_MODE;
111 } else {
112 uapsdParams->enable_ps = false;
113 *ps_state = FULL_POWER_MODE;
114 }
115 }
116
sme_set_ps_state(struct mac_context * mac_ctx,uint32_t session_id,enum ps_state ps_state)117 static void sme_set_ps_state(struct mac_context *mac_ctx,
118 uint32_t session_id, enum ps_state ps_state)
119 {
120 struct ps_global_info *ps_global_info = &mac_ctx->sme.ps_global_info;
121 struct ps_params *ps_param = &ps_global_info->ps_params[session_id];
122
123 ps_param->ps_state = ps_state;
124 }
125
sme_get_ps_state(struct mac_context * mac_ctx,uint32_t session_id,enum ps_state * ps_state)126 static void sme_get_ps_state(struct mac_context *mac_ctx,
127 uint32_t session_id, enum ps_state *ps_state)
128 {
129 struct ps_global_info *ps_global_info = &mac_ctx->sme.ps_global_info;
130 struct ps_params *ps_param = &ps_global_info->ps_params[session_id];
131 *ps_state = ps_param->ps_state;
132 }
133 /**
134 * sme_ps_enable_ps_req_params(): enables power save req params
135 * @mac_ctx: global mac context
136 * @session_id: session id
137 *
138 * Return: QDF_STATUS
139 */
140 static QDF_STATUS
sme_ps_enable_ps_req_params(struct mac_context * mac_ctx,uint32_t vdev_id)141 sme_ps_enable_ps_req_params(struct mac_context *mac_ctx, uint32_t vdev_id)
142 {
143 struct sEnablePsParams *enable_ps_req_params;
144 struct ps_global_info *ps_global_info = &mac_ctx->sme.ps_global_info;
145 struct ps_params *ps_param = &ps_global_info->ps_params[vdev_id];
146 enum ps_state ps_state;
147
148 enable_ps_req_params = qdf_mem_malloc(sizeof(*enable_ps_req_params));
149 if (!enable_ps_req_params)
150 return QDF_STATUS_E_NOMEM;
151
152 if (ps_param->uapsd_per_ac_bit_mask) {
153 enable_ps_req_params->psSetting = eSIR_ADDON_ENABLE_UAPSD;
154 sme_ps_fill_uapsd_req_params(mac_ctx,
155 &enable_ps_req_params->uapsdParams,
156 vdev_id, &ps_state);
157 ps_state = UAPSD_MODE;
158 enable_ps_req_params->uapsdParams.enable_ps = true;
159 } else {
160 enable_ps_req_params->psSetting = eSIR_ADDON_NOTHING;
161 ps_state = LEGACY_POWER_SAVE_MODE;
162 }
163 enable_ps_req_params->sessionid = vdev_id;
164
165 wma_enable_sta_ps_mode(enable_ps_req_params);
166
167 qdf_mem_free(enable_ps_req_params);
168
169 sme_debug("Powersave Enable sent to FW");
170 ps_param->ps_state = ps_state;
171
172 return QDF_STATUS_SUCCESS;
173 }
174
175 /**
176 * sme_ps_disable_ps_req_params(): Disable power save req params
177 * @mac_ctx: global mac context
178 * @session_id: session id
179 *
180 * Return: QDF_STATUS
181 */
sme_ps_disable_ps_req_params(struct mac_context * mac_ctx,uint32_t vdev_id)182 static QDF_STATUS sme_ps_disable_ps_req_params(struct mac_context *mac_ctx,
183 uint32_t vdev_id)
184 {
185 struct sDisablePsParams *disable_ps_req_params;
186
187 disable_ps_req_params = qdf_mem_malloc(sizeof(*disable_ps_req_params));
188 if (!disable_ps_req_params)
189 return QDF_STATUS_E_NOMEM;
190
191 disable_ps_req_params->psSetting = eSIR_ADDON_NOTHING;
192 disable_ps_req_params->sessionid = vdev_id;
193
194 wma_disable_sta_ps_mode(disable_ps_req_params);
195 qdf_mem_free(disable_ps_req_params);
196
197 sme_debug("Powersave disable sent to FW");
198 sme_set_ps_state(mac_ctx, vdev_id, FULL_POWER_MODE);
199
200 return QDF_STATUS_SUCCESS;
201 }
202
203 /**
204 * sme_ps_enable_uapsd_req_params(): enables UASPD req params
205 * @mac_ctx: global mac context
206 * @session_id: session id
207 *
208 * Return: QDF_STATUS
209 */
sme_ps_enable_uapsd_req_params(struct mac_context * mac_ctx,uint32_t session_id)210 static QDF_STATUS sme_ps_enable_uapsd_req_params(struct mac_context *mac_ctx,
211 uint32_t session_id)
212 {
213
214 struct sEnableUapsdParams *enable_uapsd_req_params;
215 QDF_STATUS status = QDF_STATUS_SUCCESS;
216 enum ps_state ps_state;
217
218 enable_uapsd_req_params =
219 qdf_mem_malloc(sizeof(*enable_uapsd_req_params));
220 if (!enable_uapsd_req_params)
221 return QDF_STATUS_E_NOMEM;
222
223 sme_ps_fill_uapsd_req_params(mac_ctx,
224 &enable_uapsd_req_params->uapsdParams,
225 session_id, &ps_state);
226 enable_uapsd_req_params->sessionid = session_id;
227
228 status = sme_post_ps_msg_to_wma(WMA_ENABLE_UAPSD_REQ,
229 enable_uapsd_req_params);
230 if (!QDF_IS_STATUS_SUCCESS(status))
231 return QDF_STATUS_E_FAILURE;
232
233 sme_debug("Msg WMA_ENABLE_UAPSD_REQ Successfully sent to WMA");
234 sme_set_ps_state(mac_ctx, session_id, ps_state);
235 return QDF_STATUS_SUCCESS;
236 }
237
238 /**
239 * sme_ps_disable_uapsd_req_params(): disables UASPD req params
240 * @mac_ctx: global mac context
241 * @session_id: session id
242 *
243 * Return: QDF_STATUS
244 */
sme_ps_disable_uapsd_req_params(struct mac_context * mac_ctx,uint32_t session_id)245 static QDF_STATUS sme_ps_disable_uapsd_req_params(struct mac_context *mac_ctx,
246 uint32_t session_id)
247 {
248 struct sDisableUapsdParams *disable_uapsd_req_params;
249 QDF_STATUS status = QDF_STATUS_SUCCESS;
250 enum ps_state ps_state;
251
252 sme_get_ps_state(mac_ctx, session_id, &ps_state);
253 if (ps_state != UAPSD_MODE) {
254 sme_err("UAPSD is already disabled");
255 return QDF_STATUS_SUCCESS;
256 }
257 disable_uapsd_req_params =
258 qdf_mem_malloc(sizeof(*disable_uapsd_req_params));
259 if (!disable_uapsd_req_params)
260 return QDF_STATUS_E_NOMEM;
261
262 disable_uapsd_req_params->sessionid = session_id;
263 status = sme_post_ps_msg_to_wma(WMA_DISABLE_UAPSD_REQ,
264 disable_uapsd_req_params);
265 if (!QDF_IS_STATUS_SUCCESS(status))
266 return QDF_STATUS_E_FAILURE;
267
268 sme_debug("Message WMA_DISABLE_UAPSD_REQ Successfully sent to WMA");
269 sme_set_ps_state(mac_ctx, session_id, LEGACY_POWER_SAVE_MODE);
270 return QDF_STATUS_SUCCESS;
271 }
272
273 /**
274 * sme_ps_process_command(): Sme process power save messages
275 * and pass messages to WMA.
276 * @mac_ctx: global mac context
277 * @session_id: session id
278 * sme_ps_cmd: power save message
279 *
280 * Return: QDF_STATUS
281 */
sme_ps_process_command(struct mac_context * mac_ctx,uint32_t session_id,enum sme_ps_cmd command)282 QDF_STATUS sme_ps_process_command(struct mac_context *mac_ctx, uint32_t session_id,
283 enum sme_ps_cmd command)
284 {
285 QDF_STATUS status = QDF_STATUS_SUCCESS;
286
287 if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) {
288 sme_err("Invalid Session_id: %d", session_id);
289 return QDF_STATUS_E_INVAL;
290 }
291 sme_debug("Vdev id %d, Power Save command %d", session_id, command);
292 switch (command) {
293 case SME_PS_ENABLE:
294 status = sme_ps_enable_ps_req_params(mac_ctx, session_id);
295 break;
296 case SME_PS_DISABLE:
297 status = sme_ps_disable_ps_req_params(mac_ctx, session_id);
298 break;
299 case SME_PS_UAPSD_ENABLE:
300 status = sme_ps_enable_uapsd_req_params(mac_ctx, session_id);
301 break;
302 case SME_PS_UAPSD_DISABLE:
303 status = sme_ps_disable_uapsd_req_params(mac_ctx, session_id);
304 break;
305 default:
306 sme_err("Invalid command type: %d", command);
307 status = QDF_STATUS_E_FAILURE;
308 break;
309 }
310 if (status != QDF_STATUS_SUCCESS) {
311 sme_err("Not able to enter in PS, Command: %d", command);
312 }
313 return status;
314 }
315
316 /**
317 * sme_enable_sta_ps_check(): Checks if it is ok to enable power save or not.
318 * @mac_ctx: global mac context
319 * @session_id: session id
320 * @ command: sme_ps_cmd
321 * Pre Condition for enabling sta mode power save
322 * 1) Sta Mode Ps should be enabled in ini file.
323 * 2) Session should be in infra mode and in connected state.
324 *
325 * Return: QDF_STATUS
326 */
sme_enable_sta_ps_check(struct mac_context * mac_ctx,uint32_t vdev_id,enum sme_ps_cmd command)327 QDF_STATUS sme_enable_sta_ps_check(struct mac_context *mac_ctx,
328 uint32_t vdev_id, enum sme_ps_cmd command)
329 {
330 bool usr_cfg_ps_enable;
331
332 QDF_BUG(vdev_id < WLAN_MAX_VDEVS);
333 if (vdev_id >= WLAN_MAX_VDEVS)
334 return QDF_STATUS_E_INVAL;
335
336 if (!mac_ctx->mlme_cfg->ps_params.is_bmps_enabled) {
337 sme_debug("vdev:%d power save mode is disabled via ini", vdev_id);
338 return QDF_STATUS_E_FAILURE;
339 }
340
341 usr_cfg_ps_enable = mlme_get_user_ps(mac_ctx->psoc, vdev_id);
342 if (command == SME_PS_ENABLE && !usr_cfg_ps_enable) {
343 sme_debug("vdev:%d power save mode is disabled by usr(ioctl)",
344 vdev_id);
345 return QDF_STATUS_E_FAILURE;
346 }
347
348 /*
349 * If command is power save disable there is not need to check for
350 * connected or roaming state as firmware can handle this
351 */
352 if (command == SME_PS_DISABLE)
353 return QDF_STATUS_SUCCESS;
354
355 /* If command is power save enable, check whether the given session is
356 * in connected or roaming state or not
357 */
358 if (!cm_is_vdevid_active(mac_ctx->pdev, vdev_id))
359 return QDF_STATUS_E_FAILURE;
360
361 return QDF_STATUS_SUCCESS;
362 }
363
364 /**
365 * sme_ps_enable_disable(): function to enable/disable PS.
366 * @mac_handle: Opaque handle to the global MAC context
367 * @vdev_id: session id
368 * sme_ps_cmd: power save message
369 *
370 * Return: QDF_STATUS
371 */
sme_ps_enable_disable(mac_handle_t mac_handle,uint32_t vdev_id,enum sme_ps_cmd command)372 QDF_STATUS sme_ps_enable_disable(mac_handle_t mac_handle, uint32_t vdev_id,
373 enum sme_ps_cmd command)
374 {
375 QDF_STATUS status;
376 struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle);
377
378 status = sme_acquire_global_lock(&mac_ctx->sme);
379 if (QDF_IS_STATUS_ERROR(status)) {
380 sme_err("can't acquire sme global lock");
381 return status;
382 }
383
384 status = sme_enable_sta_ps_check(mac_ctx, vdev_id, command);
385 if (QDF_IS_STATUS_ERROR(status)) {
386 /*
387 * In non associated state driver won't handle the power save
388 * But kernel expects return status success even in the
389 * disconnected state.
390 */
391 if (!cm_is_vdevid_active(mac_ctx->pdev, vdev_id))
392 status = QDF_STATUS_SUCCESS;
393 sme_release_global_lock(&mac_ctx->sme);
394 return status;
395 }
396 status = sme_ps_process_command(mac_ctx, vdev_id, command);
397 sme_release_global_lock(&mac_ctx->sme);
398
399 return status;
400 }
401
sme_ps_update(mac_handle_t mac_handle,uint32_t vdev_id)402 QDF_STATUS sme_ps_update(mac_handle_t mac_handle, uint32_t vdev_id)
403 {
404 struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle);
405 QDF_STATUS status;
406 bool usr_ps_cfg;
407 enum sme_ps_cmd command;
408
409 status = sme_acquire_global_lock(&mac_ctx->sme);
410 if (QDF_IS_STATUS_ERROR(status)) {
411 sme_err("can't acquire sme global lock");
412 return status;
413 }
414 usr_ps_cfg = mlme_get_user_ps(mac_ctx->psoc, vdev_id);
415 command = usr_ps_cfg ? SME_PS_ENABLE : SME_PS_DISABLE;
416 sme_debug("Allow power save %d vdev %d",
417 usr_ps_cfg, vdev_id);
418 status = sme_ps_enable_disable(mac_handle, vdev_id, command);
419 sme_release_global_lock(&mac_ctx->sme);
420
421 return status;
422 }
423
424 #ifdef QCA_WIFI_EMULATION
425 static
sme_ps_set_powersave_disable_auto_timer(mac_handle_t mac_handle,uint8_t vdev_id)426 QDF_STATUS sme_ps_set_powersave_disable_auto_timer(mac_handle_t mac_handle,
427 uint8_t vdev_id)
428 {
429 return QDF_STATUS_SUCCESS;
430 }
431 #else
432 static
sme_ps_set_powersave_disable_auto_timer(mac_handle_t mac_handle,uint8_t vdev_id)433 QDF_STATUS sme_ps_set_powersave_disable_auto_timer(mac_handle_t mac_handle,
434 uint8_t vdev_id)
435 {
436 return sme_ps_disable_auto_ps_timer(mac_handle,
437 vdev_id);
438 }
439 #endif
440
sme_ps_set_powersave(mac_handle_t mac_handle,uint8_t vdev_id,bool allow_power_save,uint32_t timeout,bool ap_supports_immediate_power_save)441 QDF_STATUS sme_ps_set_powersave(mac_handle_t mac_handle,
442 uint8_t vdev_id, bool allow_power_save,
443 uint32_t timeout,
444 bool ap_supports_immediate_power_save)
445 {
446 struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle);
447 QDF_STATUS status;
448 bool is_bmps_enabled;
449 enum QDF_OPMODE device_mode;
450
451 status = sme_acquire_global_lock(&mac_ctx->sme);
452 if (QDF_IS_STATUS_ERROR(status)) {
453 sme_err("can't acquire sme global lock");
454 return status;
455 }
456 sme_debug("Allow power save %d vdev %d timeout %d imm ps %d",
457 allow_power_save, vdev_id, timeout,
458 ap_supports_immediate_power_save);
459
460 mlme_set_user_ps(mac_ctx->psoc, vdev_id, allow_power_save);
461 /*
462 * This is a workaround for defective AP's that send a disassoc
463 * immediately after WPS connection completes. Defer powersave by a
464 * small amount if the affected AP is detected.
465 */
466 device_mode = wlan_get_opmode_from_vdev_id(mac_ctx->pdev, vdev_id);
467 if (allow_power_save &&
468 device_mode == QDF_STA_MODE &&
469 !ap_supports_immediate_power_save) {
470 timeout = AUTO_PS_DEFER_TIMEOUT_MS;
471 sme_debug("Defer power-save due to AP spec non-conformance");
472 }
473
474 if (allow_power_save) {
475 if (device_mode == QDF_STA_MODE ||
476 device_mode == QDF_P2P_CLIENT_MODE) {
477 sme_debug("Disabling Auto Power save timer");
478 status = sme_ps_disable_auto_ps_timer(
479 mac_handle, vdev_id);
480 if (status != QDF_STATUS_SUCCESS)
481 goto end;
482 }
483
484 wlan_mlme_is_bmps_enabled(mac_ctx->psoc, &is_bmps_enabled);
485 if (is_bmps_enabled) {
486 sme_debug("Wlan driver Entering Power save");
487
488 /*
489 * Enter Power Save command received from GUI
490 * this means DHCP is completed
491 */
492 if (timeout) {
493 status = sme_ps_enable_auto_ps_timer(
494 mac_handle,
495 vdev_id,
496 timeout);
497 if (status != QDF_STATUS_SUCCESS)
498 goto end;
499 } else {
500 status = sme_ps_enable_disable(
501 mac_handle,
502 vdev_id,
503 SME_PS_ENABLE);
504 if (status != QDF_STATUS_SUCCESS)
505 goto end;
506 }
507 } else {
508 sme_debug("Power Save is not enabled in the cfg");
509 }
510 } else {
511 sme_debug("Wlan driver Entering Full Power");
512
513 /*
514 * Enter Full power command received from GUI
515 * this means we are disconnected
516 */
517 status = sme_ps_set_powersave_disable_auto_timer(mac_handle,
518 vdev_id);
519 if (status != QDF_STATUS_SUCCESS)
520 goto end;
521
522 wlan_mlme_is_bmps_enabled(mac_ctx->psoc, &is_bmps_enabled);
523 if (is_bmps_enabled) {
524 status = sme_ps_enable_disable(mac_handle,
525 vdev_id,
526 SME_PS_DISABLE);
527 if (status != QDF_STATUS_SUCCESS)
528 goto end;
529 }
530 }
531
532 end:
533 sme_release_global_lock(&mac_ctx->sme);
534
535 return status;
536 }
537
sme_ps_timer_flush_sync(mac_handle_t mac_handle,uint8_t session_id)538 QDF_STATUS sme_ps_timer_flush_sync(mac_handle_t mac_handle, uint8_t session_id)
539 {
540 QDF_STATUS status;
541 struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle);
542 struct ps_params *ps_parm;
543 enum ps_state ps_state;
544 QDF_TIMER_STATE tstate;
545 struct sEnablePsParams *req;
546
547 QDF_BUG(session_id < WLAN_MAX_VDEVS);
548 if (session_id >= WLAN_MAX_VDEVS)
549 return QDF_STATUS_E_INVAL;
550
551 status = sme_acquire_global_lock(&mac_ctx->sme);
552 if (QDF_IS_STATUS_ERROR(status)) {
553 sme_err("can't acquire sme global lock");
554 return status;
555 }
556
557 status = sme_enable_sta_ps_check(mac_ctx, session_id, SME_PS_ENABLE);
558 if (QDF_IS_STATUS_ERROR(status)) {
559 sme_debug("Power save not allowed for vdev id %d", session_id);
560 status = QDF_STATUS_SUCCESS;
561 goto end;
562 }
563
564 ps_parm = &mac_ctx->sme.ps_global_info.ps_params[session_id];
565 tstate = qdf_mc_timer_get_current_state(&ps_parm->auto_ps_enable_timer);
566 if (tstate != QDF_TIMER_STATE_RUNNING) {
567 status = QDF_STATUS_SUCCESS;
568 goto end;
569 }
570
571 sme_debug("flushing powersave enable for vdev %u", session_id);
572
573 qdf_mc_timer_stop(&ps_parm->auto_ps_enable_timer);
574
575 req = qdf_mem_malloc(sizeof(*req));
576 if (!req) {
577 status = QDF_STATUS_E_NOMEM;
578 goto end;
579 }
580
581 if (ps_parm->uapsd_per_ac_bit_mask) {
582 req->psSetting = eSIR_ADDON_ENABLE_UAPSD;
583 sme_ps_fill_uapsd_req_params(mac_ctx, &req->uapsdParams,
584 session_id, &ps_state);
585 ps_state = UAPSD_MODE;
586 req->uapsdParams.enable_ps = true;
587 } else {
588 req->psSetting = eSIR_ADDON_NOTHING;
589 ps_state = LEGACY_POWER_SAVE_MODE;
590 }
591 req->sessionid = session_id;
592
593 wma_enable_sta_ps_mode(req);
594 qdf_mem_free(req);
595
596 ps_parm->ps_state = ps_state;
597 end:
598 sme_release_global_lock(&mac_ctx->sme);
599
600 return status;
601 }
602
603 /**
604 * sme_ps_uapsd_enable(): function to enable UAPSD.
605 * @mac_handle: Opaque handle to the global MAC context
606 * @session_id: session id
607 *
608 * Return: QDF_STATUS
609 */
sme_ps_uapsd_enable(mac_handle_t mac_handle,uint32_t session_id)610 QDF_STATUS sme_ps_uapsd_enable(mac_handle_t mac_handle, uint32_t session_id)
611 {
612
613 QDF_STATUS status = QDF_STATUS_E_FAILURE;
614 struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle);
615
616 status = sme_enable_sta_ps_check(mac_ctx, session_id,
617 SME_PS_UAPSD_ENABLE);
618 if (status != QDF_STATUS_SUCCESS)
619 return status;
620 status = sme_ps_process_command(mac_ctx, session_id,
621 SME_PS_UAPSD_ENABLE);
622 if (status == QDF_STATUS_SUCCESS)
623 sme_offload_qos_process_into_uapsd_mode(mac_ctx, session_id);
624
625 return status;
626 }
627
628 /**
629 * sme_ps_uapsd_disable(): function to disable UAPSD.
630 * @mac_handle: Opaque handle to the global MAC context
631 * @session_id: session id
632 *
633 * Return: QDF_STATUS
634 */
sme_ps_uapsd_disable(mac_handle_t mac_handle,uint32_t session_id)635 QDF_STATUS sme_ps_uapsd_disable(mac_handle_t mac_handle, uint32_t session_id)
636 {
637
638 QDF_STATUS status = QDF_STATUS_E_FAILURE;
639 struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle);
640
641 status = sme_enable_sta_ps_check(mac_ctx, session_id,
642 SME_PS_UAPSD_DISABLE);
643 if (status != QDF_STATUS_SUCCESS)
644 return status;
645 status = sme_ps_process_command(mac_ctx, session_id,
646 SME_PS_UAPSD_DISABLE);
647 if (status == QDF_STATUS_SUCCESS)
648 sme_offload_qos_process_out_of_uapsd_mode(mac_ctx, session_id);
649
650 return status;
651 }
652
653 /**
654 * sme_set_tspec_uapsd_mask_per_session(): set tspec UAPSD mask per session
655 * @mac_ctx: global mac context
656 * @ts_info: tspec info.
657 * @session_id: session id
658 *
659 * Return: QDF_STATUS
660 */
sme_set_tspec_uapsd_mask_per_session(struct mac_context * mac_ctx,struct mac_ts_info * ts_info,uint8_t session_id)661 void sme_set_tspec_uapsd_mask_per_session(struct mac_context *mac_ctx,
662 struct mac_ts_info *ts_info,
663 uint8_t session_id)
664 {
665 uint8_t user_prio = (uint8_t) ts_info->traffic.userPrio;
666 uint16_t direction = ts_info->traffic.direction;
667 uint8_t ac = upToAc(user_prio);
668 struct ps_global_info *ps_global_info = &mac_ctx->sme.ps_global_info;
669 struct ps_params *ps_param = &ps_global_info->ps_params[session_id];
670
671 sme_debug("Set UAPSD mask for AC: %d dir: %d action: %d",
672 ac, direction, ts_info->traffic.psb);
673
674 /* Converting AC to appropriate Uapsd Bit Mask
675 * AC_BE(0) --> UAPSD_BITOFFSET_ACVO(3)
676 * AC_BK(1) --> UAPSD_BITOFFSET_ACVO(2)
677 * AC_VI(2) --> UAPSD_BITOFFSET_ACVO(1)
678 * AC_VO(3) --> UAPSD_BITOFFSET_ACVO(0)
679 */
680 ac = ((~ac) & 0x3);
681 if (ts_info->traffic.psb) {
682 ps_param->uapsd_per_ac_bit_mask |= (1 << ac);
683 if (direction == SIR_MAC_DIRECTION_UPLINK)
684 ps_param->uapsd_per_ac_trigger_enable_mask |=
685 (1 << ac);
686 else if (direction == SIR_MAC_DIRECTION_DNLINK)
687 ps_param->uapsd_per_ac_delivery_enable_mask |=
688 (1 << ac);
689 else if (direction == SIR_MAC_DIRECTION_BIDIR) {
690 ps_param->uapsd_per_ac_trigger_enable_mask |=
691 (1 << ac);
692 ps_param->uapsd_per_ac_delivery_enable_mask |=
693 (1 << ac);
694 }
695 } else {
696 ps_param->uapsd_per_ac_bit_mask &= ~(1 << ac);
697 if (direction == SIR_MAC_DIRECTION_UPLINK)
698 ps_param->uapsd_per_ac_trigger_enable_mask &=
699 ~(1 << ac);
700 else if (direction == SIR_MAC_DIRECTION_DNLINK)
701 ps_param->uapsd_per_ac_delivery_enable_mask &=
702 ~(1 << ac);
703 else if (direction == SIR_MAC_DIRECTION_BIDIR) {
704 ps_param->uapsd_per_ac_trigger_enable_mask &=
705 ~(1 << ac);
706 ps_param->uapsd_per_ac_delivery_enable_mask &=
707 ~(1 << ac);
708 }
709 }
710
711 /*
712 * ADDTS success, so AC is now admitted. We shall now use the default
713 * EDCA parameters as advertised by AP and send the updated EDCA params
714 * to HAL.
715 */
716 if (direction == SIR_MAC_DIRECTION_UPLINK) {
717 ps_param->ac_admit_mask[SIR_MAC_DIRECTION_UPLINK] |=
718 (1 << ac);
719 } else if (direction == SIR_MAC_DIRECTION_DNLINK) {
720 ps_param->ac_admit_mask[SIR_MAC_DIRECTION_DNLINK] |=
721 (1 << ac);
722 } else if (direction == SIR_MAC_DIRECTION_BIDIR) {
723 ps_param->ac_admit_mask[SIR_MAC_DIRECTION_UPLINK] |=
724 (1 << ac);
725 ps_param->ac_admit_mask[SIR_MAC_DIRECTION_DNLINK] |=
726 (1 << ac);
727 }
728
729 sme_debug("New ps_param->uapsd_per_ac_trigger_enable_mask: 0x%x",
730 ps_param->uapsd_per_ac_trigger_enable_mask);
731 sme_debug("New ps_param->uapsd_per_ac_delivery_enable_mask: 0x%x",
732 ps_param->uapsd_per_ac_delivery_enable_mask);
733 sme_debug("New ps_param->ac_admit_mask[SIR_MAC_DIRECTION_UPLINK]: 0x%x",
734 ps_param->ac_admit_mask[SIR_MAC_DIRECTION_UPLINK]);
735 }
736
737 /**
738 * sme_ps_start_uapsd(): function to start UAPSD.
739 * @mac_handle: Opaque handle to the global MAC context
740 * @session_id: session id
741 *
742 * Return: QDF_STATUS
743 */
sme_ps_start_uapsd(mac_handle_t mac_handle,uint32_t session_id)744 QDF_STATUS sme_ps_start_uapsd(mac_handle_t mac_handle, uint32_t session_id)
745 {
746 QDF_STATUS status = QDF_STATUS_E_FAILURE;
747
748 status = sme_ps_uapsd_enable(mac_handle, session_id);
749 return status;
750 }
751
752 /**
753 * sme_set_ps_host_offload(): Set the host offload feature.
754 * @mac_handle - The handle returned by mac_open.
755 * @request - Pointer to the offload request.
756 *
757 * Return QDF_STATUS
758 * QDF_STATUS_E_FAILURE Cannot set the offload.
759 * QDF_STATUS_SUCCESS Request accepted.
760 */
sme_set_ps_host_offload(mac_handle_t mac_handle,struct sir_host_offload_req * request,uint8_t session_id)761 QDF_STATUS sme_set_ps_host_offload(mac_handle_t mac_handle,
762 struct sir_host_offload_req *request,
763 uint8_t session_id)
764 {
765 struct sir_host_offload_req *request_buf;
766 struct scheduler_msg msg = {0};
767 struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle);
768
769 sme_debug("IP address = %d.%d.%d.%d",
770 request->params.hostIpv4Addr[0],
771 request->params.hostIpv4Addr[1],
772 request->params.hostIpv4Addr[2],
773 request->params.hostIpv4Addr[3]);
774
775 if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) {
776 sme_err("CSR session is invalid");
777 return QDF_STATUS_E_INVAL;
778 }
779
780 request_buf = qdf_mem_malloc(sizeof(struct sir_host_offload_req));
781 if (!request_buf)
782 return QDF_STATUS_E_NOMEM;
783
784 wlan_mlme_get_bssid_vdev_id(mac_ctx->pdev, session_id,
785 &request->bssid);
786 qdf_mem_copy(request_buf, request, sizeof(struct sir_host_offload_req));
787
788 msg.type = WMA_SET_HOST_OFFLOAD;
789 msg.reserved = 0;
790 msg.bodyptr = request_buf;
791 MTRACE(qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG,
792 session_id, msg.type));
793 if (QDF_STATUS_SUCCESS !=
794 scheduler_post_message(QDF_MODULE_ID_SME,
795 QDF_MODULE_ID_WMA,
796 QDF_MODULE_ID_WMA, &msg)) {
797 sme_err("Not able to post WMA_SET_HOST_OFFLOAD msg to WMA");
798 qdf_mem_free(request_buf);
799 return QDF_STATUS_E_FAILURE;
800 }
801
802 return QDF_STATUS_SUCCESS;
803 }
804
805 #ifdef WLAN_NS_OFFLOAD
806 /**
807 * sme_set_ps_ns_offload(): Set the host offload feature.
808 * @mac_handle - The handle returned by mac_open.
809 * @request - Pointer to the offload request.
810 *
811 * Return QDF_STATUS
812 * QDF_STATUS_E_FAILURE Cannot set the offload.
813 * QDF_STATUS_SUCCESS Request accepted.
814 */
sme_set_ps_ns_offload(mac_handle_t mac_handle,struct sir_host_offload_req * request,uint8_t session_id)815 QDF_STATUS sme_set_ps_ns_offload(mac_handle_t mac_handle,
816 struct sir_host_offload_req *request,
817 uint8_t session_id)
818 {
819 struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle);
820 struct sir_host_offload_req *request_buf;
821 struct scheduler_msg msg = {0};
822
823 if (!CSR_IS_SESSION_VALID(mac_ctx, session_id)) {
824 sme_err("CSR session is invalid");
825 return QDF_STATUS_E_INVAL;
826 }
827
828 wlan_mlme_get_bssid_vdev_id(mac_ctx->pdev, session_id,
829 &request->bssid);
830
831 request_buf = qdf_mem_malloc(sizeof(*request_buf));
832 if (!request_buf)
833 return QDF_STATUS_E_NOMEM;
834
835 *request_buf = *request;
836
837 msg.type = WMA_SET_NS_OFFLOAD;
838 msg.reserved = 0;
839 msg.bodyptr = request_buf;
840 MTRACE(qdf_trace(QDF_MODULE_ID_SME, TRACE_CODE_SME_TX_WMA_MSG,
841 session_id, msg.type));
842 if (QDF_STATUS_SUCCESS !=
843 scheduler_post_message(QDF_MODULE_ID_SME,
844 QDF_MODULE_ID_WMA,
845 QDF_MODULE_ID_WMA, &msg)) {
846 sme_err(
847 "Not able to post SIR_HAL_SET_HOST_OFFLOAD message to HAL");
848 qdf_mem_free(request_buf);
849 return QDF_STATUS_E_FAILURE;
850 }
851
852 return QDF_STATUS_SUCCESS;
853 }
854
855 #endif /* WLAN_NS_OFFLOAD */
856 /**
857 * sme_post_pe_message
858 *
859 * FUNCTION:
860 * Post a message to the pmm message queue
861 *
862 * LOGIC:
863 *
864 * ASSUMPTIONS:
865 *
866 * NOTE:
867 *
868 * @param msg pointer to message
869 * @return None
870 */
871
sme_post_pe_message(struct mac_context * mac_ctx,struct scheduler_msg * msg)872 QDF_STATUS sme_post_pe_message(struct mac_context *mac_ctx,
873 struct scheduler_msg *msg)
874 {
875 QDF_STATUS qdf_status;
876
877 qdf_status = scheduler_post_message(QDF_MODULE_ID_SME,
878 QDF_MODULE_ID_PE,
879 QDF_MODULE_ID_PE,
880 msg);
881 if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
882 sme_err("scheduler_post_msg failed with status: %d",
883 qdf_status);
884 return QDF_STATUS_E_FAILURE;
885 }
886
887 return QDF_STATUS_SUCCESS;
888 }
889
890 #ifdef QCA_WIFI_EMULATION
sme_ps_scale_auto_ps_timeout(uint32_t timeout)891 static QDF_STATUS sme_ps_scale_auto_ps_timeout(uint32_t timeout)
892 {
893 return AUTO_PS_EMULATION_TIMEOUT;
894 }
895 #else
sme_ps_scale_auto_ps_timeout(uint32_t timeout)896 static QDF_STATUS sme_ps_scale_auto_ps_timeout(uint32_t timeout)
897 {
898 return timeout;
899 }
900 #endif
901
sme_ps_enable_auto_ps_timer(mac_handle_t mac_handle,uint32_t session_id,uint32_t timeout)902 QDF_STATUS sme_ps_enable_auto_ps_timer(mac_handle_t mac_handle,
903 uint32_t session_id, uint32_t timeout)
904 {
905 struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle);
906 struct ps_global_info *ps_global_info = &mac_ctx->sme.ps_global_info;
907 struct ps_params *ps_param = &ps_global_info->ps_params[session_id];
908 QDF_STATUS qdf_status;
909 QDF_TIMER_STATE cur_state;
910 bool usr_cfg_ps_enable;
911
912 usr_cfg_ps_enable =
913 mlme_get_user_ps(mac_ctx->psoc, session_id);
914 if (!timeout && !usr_cfg_ps_enable) {
915 sme_debug("auto_ps_timer called with timeout 0; ignore");
916 return QDF_STATUS_SUCCESS;
917 }
918 if (!timeout)
919 timeout = AUTO_PS_ENTRY_TIMER_DEFAULT_VALUE;
920
921 cur_state =
922 qdf_mc_timer_get_current_state(&ps_param->auto_ps_enable_timer);
923 if (cur_state == QDF_TIMER_STATE_STARTING ||
924 cur_state == QDF_TIMER_STATE_RUNNING) {
925 sme_debug("auto_ps_timer is already started: %d", cur_state);
926 return QDF_STATUS_SUCCESS;
927 }
928
929 timeout = sme_ps_scale_auto_ps_timeout(timeout);
930 sme_debug("Start auto_ps_timer for %d ms", timeout);
931 qdf_status = qdf_mc_timer_start(&ps_param->auto_ps_enable_timer,
932 timeout);
933 if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
934 if (QDF_STATUS_E_ALREADY == qdf_status) {
935 /* Consider this ok since the timer is already started*/
936 sme_debug("auto_ps_timer is already started");
937 } else {
938 sme_err("Cannot start auto_ps_timer");
939 return QDF_STATUS_E_FAILURE;
940 }
941 }
942
943 return QDF_STATUS_SUCCESS;
944 }
945
sme_ps_disable_auto_ps_timer(mac_handle_t mac_handle,uint32_t session_id)946 QDF_STATUS sme_ps_disable_auto_ps_timer(mac_handle_t mac_handle,
947 uint32_t session_id)
948 {
949 struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle);
950 struct ps_global_info *ps_global_info = &mac_ctx->sme.ps_global_info;
951 struct ps_params *ps_param;
952
953 if (!sme_is_session_id_valid(mac_handle, session_id))
954 return QDF_STATUS_SUCCESS;
955
956 ps_param = &ps_global_info->ps_params[session_id];
957 /*
958 * Stop the auto ps entry timer if running
959 */
960 if (QDF_TIMER_STATE_RUNNING ==
961 qdf_mc_timer_get_current_state(
962 &ps_param->auto_ps_enable_timer)) {
963 sme_debug("Stop auto_ps_enable_timer Timer for session ID: %d",
964 session_id);
965 qdf_mc_timer_stop(&ps_param->auto_ps_enable_timer);
966 }
967 return QDF_STATUS_SUCCESS;
968 }
969
970
sme_ps_open(mac_handle_t mac_handle)971 QDF_STATUS sme_ps_open(mac_handle_t mac_handle)
972 {
973 uint32_t i;
974 struct mac_context *mac = MAC_CONTEXT(mac_handle);
975
976 for (i = 0; i < WLAN_MAX_VDEVS; i++) {
977 mlme_set_user_ps(mac->psoc, i, true);
978
979 if (QDF_STATUS_SUCCESS != sme_ps_open_per_session(mac_handle,
980 i)) {
981 sme_err("PMC Init Failed for session: %d", i);
982 return QDF_STATUS_E_FAILURE;
983 }
984 }
985 return QDF_STATUS_SUCCESS;
986 }
987
988
sme_ps_open_per_session(mac_handle_t mac_handle,uint32_t session_id)989 QDF_STATUS sme_ps_open_per_session(mac_handle_t mac_handle, uint32_t session_id)
990 {
991 struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle);
992 struct ps_global_info *ps_global_info = &mac_ctx->sme.ps_global_info;
993 struct ps_params *ps_param = &ps_global_info->ps_params[session_id];
994
995 ps_param->session_id = session_id;
996 ps_param->mac_ctx = mac_ctx;
997 /* Allocate a timer to enable ps automatically */
998 if (!QDF_IS_STATUS_SUCCESS(qdf_mc_timer_init(
999 &ps_param->auto_ps_enable_timer,
1000 QDF_TIMER_TYPE_SW,
1001 sme_auto_ps_entry_timer_expired,
1002 ps_param))) {
1003 sme_err("Cannot allocate timer for auto ps entry");
1004 return QDF_STATUS_E_FAILURE;
1005 }
1006 return QDF_STATUS_SUCCESS;
1007
1008 }
1009
sme_auto_ps_entry_timer_expired(void * data)1010 void sme_auto_ps_entry_timer_expired(void *data)
1011 {
1012 struct ps_params *ps_params = (struct ps_params *)data;
1013 struct mac_context *mac_ctx;
1014 uint32_t session_id;
1015 QDF_STATUS status;
1016
1017 if (!ps_params) {
1018 sme_err("ps_params is NULL");
1019 return;
1020 }
1021 mac_ctx = (struct mac_context *)ps_params->mac_ctx;
1022 if (!mac_ctx) {
1023 sme_err("mac_ctx is NULL");
1024 return;
1025 }
1026 status = sme_acquire_global_lock(&mac_ctx->sme);
1027 if (QDF_IS_STATUS_ERROR(status)) {
1028 sme_err("can't acquire sme global lock");
1029 return;
1030 }
1031
1032 session_id = ps_params->session_id;
1033 sme_debug("auto_ps_timer expired, enabling powersave");
1034
1035 status = sme_enable_sta_ps_check(mac_ctx, session_id, SME_PS_ENABLE);
1036 if (QDF_STATUS_SUCCESS == status)
1037 sme_ps_enable_disable(MAC_HANDLE(mac_ctx), session_id,
1038 SME_PS_ENABLE);
1039 sme_release_global_lock(&mac_ctx->sme);
1040 }
1041
sme_ps_close(mac_handle_t mac_handle)1042 QDF_STATUS sme_ps_close(mac_handle_t mac_handle)
1043 {
1044 uint32_t i;
1045
1046 for (i = 0; i < WLAN_MAX_VDEVS; i++)
1047 sme_ps_close_per_session(mac_handle, i);
1048
1049 return QDF_STATUS_SUCCESS;
1050 }
1051
sme_ps_close_per_session(mac_handle_t mac_handle,uint32_t session_id)1052 QDF_STATUS sme_ps_close_per_session(mac_handle_t mac_handle,
1053 uint32_t session_id)
1054 {
1055
1056 struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle);
1057 struct ps_global_info *ps_global_info = &mac_ctx->sme.ps_global_info;
1058 struct ps_params *ps_param = &ps_global_info->ps_params[session_id];
1059 QDF_STATUS qdf_status = QDF_STATUS_SUCCESS;
1060
1061 /*
1062 * Stop the auto ps entry timer if running
1063 */
1064 if (QDF_TIMER_STATE_RUNNING ==
1065 qdf_mc_timer_get_current_state(
1066 &ps_param->auto_ps_enable_timer))
1067 qdf_mc_timer_stop(&ps_param->auto_ps_enable_timer);
1068
1069 qdf_status =
1070 qdf_mc_timer_destroy(&ps_param->auto_ps_enable_timer);
1071 if (!QDF_IS_STATUS_SUCCESS(qdf_status))
1072 sme_err("Cannot deallocate suto PS timer");
1073 return qdf_status;
1074 }
1075