xref: /wlan-driver/qcacld-3.0/core/sme/src/common/sme_power_save.c (revision 5113495b16420b49004c444715d2daae2066e7dc)
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