1 /*
2  * Copyright (c) 2012-2018, 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: wlan_hdd_green_ap.c
22  *
23  *  WLAN Host Device Driver Green AP implementation
24  *
25  */
26 
27 #include <net/cfg80211.h>
28 #include <wlan_hdd_green_ap.h>
29 #include <wlan_hdd_main.h>
30 #include <wlan_policy_mgr_api.h>
31 #include <wlan_green_ap_ucfg_api.h>
32 #include "wlan_mlme_ucfg_api.h"
33 #include <osif_vdev_sync.h>
34 #include "wlan_osif_priv.h"
35 #include "wlan_lmac_if_def.h"
36 
37 /**
38  * hdd_green_ap_check_enable() - to check whether to enable green ap or not
39  * @hdd_ctx: hdd context
40  * @enable_green_ap: true - enable green ap, false - disable green ap
41  *
42  * Return: 0 - success, < 0 - failure
43  */
hdd_green_ap_check_enable(struct hdd_context * hdd_ctx,bool * enable_green_ap)44 static int hdd_green_ap_check_enable(struct hdd_context *hdd_ctx,
45 				     bool *enable_green_ap)
46 {
47 	uint8_t num_sessions, mode;
48 	QDF_STATUS status;
49 
50 	for (mode = 0;
51 	     mode < QDF_MAX_NO_OF_MODE;
52 	     mode++) {
53 		if (mode == QDF_SAP_MODE || mode == QDF_P2P_GO_MODE)
54 			continue;
55 
56 		status = policy_mgr_mode_specific_num_active_sessions(
57 					hdd_ctx->psoc, mode, &num_sessions);
58 		if (status != QDF_STATUS_SUCCESS) {
59 			hdd_err("Failed to get num sessions for mode: %d",
60 				mode);
61 			return -EINVAL;
62 		} else if (num_sessions) {
63 			*enable_green_ap = false;
64 			hdd_debug("active sessions for mode: %d is %d disable green AP",
65 				  mode, num_sessions);
66 			return 0;
67 		}
68 	}
69 	*enable_green_ap = true;
70 	return 0;
71 }
72 
hdd_green_ap_add_sta(struct hdd_context * hdd_ctx)73 void hdd_green_ap_add_sta(struct hdd_context *hdd_ctx)
74 {
75 	wlan_green_ap_add_sta(hdd_ctx->pdev);
76 }
77 
hdd_green_ap_del_sta(struct hdd_context * hdd_ctx)78 void hdd_green_ap_del_sta(struct hdd_context *hdd_ctx)
79 {
80 	wlan_green_ap_del_sta(hdd_ctx->pdev);
81 }
82 
hdd_green_ap_enable_egap(struct hdd_context * hdd_ctx)83 int hdd_green_ap_enable_egap(struct hdd_context *hdd_ctx)
84 {
85 	QDF_STATUS status;
86 
87 	status = ucfg_green_ap_enable_egap(hdd_ctx->pdev);
88 	if (QDF_IS_STATUS_ERROR(status)) {
89 		hdd_debug("enhance green ap is not enabled, status %d",
90 			  status);
91 		return qdf_status_to_os_return(status);
92 	}
93 
94 	return 0;
95 }
96 
hdd_green_ap_start_state_mc(struct hdd_context * hdd_ctx,enum QDF_OPMODE mode,bool is_session_start)97 int hdd_green_ap_start_state_mc(struct hdd_context *hdd_ctx,
98 				enum QDF_OPMODE mode, bool is_session_start)
99 {
100 	struct hdd_config *cfg;
101 	bool enable_green_ap = false;
102 	uint8_t num_sap_sessions = 0, num_p2p_go_sessions = 0, ret = 0;
103 	QDF_STATUS status;
104 	bool bval = false;
105 	uint8_t ps_enable;
106 
107 	cfg = hdd_ctx->config;
108 	if (!cfg) {
109 		hdd_err("NULL hdd config");
110 		return -EINVAL;
111 	}
112 
113 	status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, &bval);
114 	if (!QDF_IS_STATUS_SUCCESS(status)) {
115 		hdd_err("unable to get vht_enable2x2");
116 		return -EINVAL;
117 	}
118 
119 	if (!bval) {
120 		hdd_debug(" 2x2 not enabled");
121 	}
122 
123 	if (QDF_IS_STATUS_ERROR(ucfg_green_ap_get_ps_config(hdd_ctx->pdev,
124 							     &ps_enable)))
125 		return 0;
126 
127 	if (!ps_enable) {
128 		hdd_debug("Green AP not enabled");
129 		return 0;
130 	}
131 
132 	policy_mgr_mode_specific_num_active_sessions(hdd_ctx->psoc,
133 						     QDF_SAP_MODE,
134 						     &num_sap_sessions);
135 	policy_mgr_mode_specific_num_active_sessions(hdd_ctx->psoc,
136 						     QDF_P2P_GO_MODE,
137 						     &num_p2p_go_sessions);
138 
139 	switch (mode) {
140 	case QDF_STA_MODE:
141 	case QDF_P2P_CLIENT_MODE:
142 		if (!num_sap_sessions && !num_p2p_go_sessions)
143 			return 0;
144 
145 		if (is_session_start) {
146 			hdd_debug("Disabling Green AP");
147 			ucfg_green_ap_set_ps_config(hdd_ctx->pdev,
148 						    false);
149 			wlan_green_ap_stop(hdd_ctx->pdev);
150 		} else {
151 			ret = hdd_green_ap_check_enable(hdd_ctx,
152 							&enable_green_ap);
153 			if (!ret) {
154 				if (enable_green_ap) {
155 					hdd_debug("Enabling Green AP");
156 					ucfg_green_ap_set_ps_config(
157 						hdd_ctx->pdev, true);
158 					wlan_green_ap_start(hdd_ctx->pdev);
159 				}
160 			} else {
161 				hdd_err("Failed to check Green AP enable status");
162 			}
163 		}
164 		break;
165 	case QDF_SAP_MODE:
166 	case QDF_P2P_GO_MODE:
167 		if (is_session_start) {
168 			ret = hdd_green_ap_check_enable(hdd_ctx,
169 							&enable_green_ap);
170 			if (!ret) {
171 				if (enable_green_ap) {
172 					hdd_debug("Enabling Green AP");
173 					ucfg_green_ap_set_ps_config(
174 						hdd_ctx->pdev, true);
175 					wlan_green_ap_start(hdd_ctx->pdev);
176 				}
177 			} else {
178 				hdd_err("Failed to check Green AP enable status");
179 			}
180 		} else {
181 			if (!num_sap_sessions && !num_p2p_go_sessions) {
182 				hdd_debug("Disabling Green AP");
183 				ucfg_green_ap_set_ps_config(hdd_ctx->pdev,
184 							    false);
185 				wlan_green_ap_stop(hdd_ctx->pdev);
186 			}
187 		}
188 		break;
189 	default:
190 		break;
191 	}
192 	return ret;
193 }
194 
195 #ifdef WLAN_SUPPORT_GAP_LL_PS_MODE
196 const struct nla_policy
197 wlan_hdd_sap_low_pwr_mode[QCA_WLAN_VENDOR_ATTR_DOZED_AP_MAX + 1] = {
198 	[QCA_WLAN_VENDOR_ATTR_DOZED_AP_STATE] = {.type = NLA_U8},
199 };
200 
201 /**
202  * __wlan_hdd_enter_sap_low_pwr_mode() - Green AP low latency power
203  * save mode
204  * vendor command
205  * @wiphy: wiphy device pointer
206  * @wdev: wireless device pointer
207  * @data: Vendor command data buffer
208  * @data_len: Buffer length
209  *
210  * Return: 0 for Success and negative value for failure
211  */
212 static int
__wlan_hdd_enter_sap_low_pwr_mode(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)213 __wlan_hdd_enter_sap_low_pwr_mode(struct wiphy *wiphy,
214 				  struct wireless_dev *wdev,
215 				  const void *data, int data_len)
216 {
217 	uint8_t lp_flags, len;
218 	uint64_t cookie_id;
219 	QDF_STATUS status;
220 	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
221 	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(wdev->netdev);
222 	struct hdd_ap_ctx *ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter->deflink);
223 	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_DOZED_AP_MAX + 1];
224 	struct sk_buff *skb;
225 
226 	hdd_enter_dev(wdev->netdev);
227 
228 	if (wlan_cfg80211_nla_parse(tb,
229 				    QCA_WLAN_VENDOR_ATTR_DOZED_AP_MAX,
230 				    data, data_len,
231 				    wlan_hdd_sap_low_pwr_mode)) {
232 		hdd_err("Invalid ATTR");
233 		return -EINVAL;
234 	}
235 
236 	if (!tb[QCA_WLAN_VENDOR_ATTR_DOZED_AP_STATE]) {
237 		hdd_err("low power flag is not present");
238 		return -EINVAL;
239 	}
240 
241 	lp_flags =
242 		nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_DOZED_AP_STATE]);
243 
244 	if (lp_flags > QCA_WLAN_DOZED_AP_ENABLE) {
245 		hdd_err("Invalid state received");
246 		return -EINVAL;
247 	}
248 
249 	hdd_debug("state: %s",
250 		  lp_flags == QCA_WLAN_DOZED_AP_ENABLE ? "ENABLE" : "DISABLE");
251 
252 	status = ucfg_green_ap_ll_ps(
253 			hdd_ctx->pdev, adapter->deflink->vdev, lp_flags,
254 			ap_ctx->sap_config.beacon_int, &cookie_id);
255 	if (status != QDF_STATUS_SUCCESS) {
256 		hdd_err("unable to send low latency power save cmd");
257 		return -EINVAL;
258 	}
259 
260 	hdd_debug("Cookie id received : %llu", cookie_id);
261 
262 	len = NLMSG_HDRLEN;
263 	/*QCA_WLAN_VENDOR_ATTR_DOZED_AP_COOKIE*/
264 	len += nla_total_size(sizeof(u64));
265 
266 	skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, len);
267 	if (!skb) {
268 		hdd_err("skb allocation failed");
269 		return -ENOMEM;
270 	}
271 
272 	if (wlan_cfg80211_nla_put_u64(skb, QCA_WLAN_VENDOR_ATTR_DOZED_AP_COOKIE,
273 				      cookie_id)) {
274 		hdd_err("nla_put for cookie id failed");
275 		goto fail;
276 	}
277 
278 	cfg80211_vendor_cmd_reply(skb);
279 
280 	hdd_exit();
281 
282 	return 0;
283 fail:
284 	wlan_cfg80211_vendor_free_skb(skb);
285 	return 0;
286 }
287 
288 int
wlan_hdd_enter_sap_low_pwr_mode(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)289 wlan_hdd_enter_sap_low_pwr_mode(struct wiphy *wiphy,
290 				struct wireless_dev *wdev,
291 				const void *data, int data_len)
292 {
293 	int errno;
294 	struct osif_vdev_sync *vdev_sync;
295 
296 	errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
297 	if (errno)
298 		return errno;
299 
300 	errno = __wlan_hdd_enter_sap_low_pwr_mode(wiphy, wdev,
301 						  data, data_len);
302 
303 	osif_vdev_sync_op_stop(vdev_sync);
304 
305 	return 0;
306 }
307 
wlan_hdd_send_green_ap_ll_ps_event(struct wlan_objmgr_vdev * vdev,struct wlan_green_ap_ll_ps_event_param * ll_ps_param)308 QDF_STATUS wlan_hdd_send_green_ap_ll_ps_event(
309 					struct wlan_objmgr_vdev *vdev,
310 					struct wlan_green_ap_ll_ps_event_param
311 					*ll_ps_param)
312 {
313 	int index = QCA_NL80211_VENDOR_SUBCMD_DOZED_AP_INDEX;
314 	QDF_STATUS status = QDF_STATUS_SUCCESS;
315 	uint32_t len;
316 	struct vdev_osif_priv *osif_priv;
317 	struct sk_buff *skb;
318 
319 	osif_priv = wlan_vdev_get_ospriv(vdev);
320 	if (!osif_priv) {
321 		hdd_err("OSIF priv is NULL");
322 		return QDF_STATUS_E_FAILURE;
323 	}
324 
325 	hdd_enter_dev(osif_priv->wdev->netdev);
326 
327 	len = NLMSG_HDRLEN;
328 	/*QCA_WLAN_VENDOR_ATTR_DOZED_AP_NEXT_TSF*/
329 	len += nla_total_size(sizeof(u64));
330 	/*QCA_WLAN_VENDOR_ATTR_DOZED_AP_COOKIE*/
331 	len += nla_total_size(sizeof(u64));
332 	/*QCA_WLAN_VENDOR_ATTR_DOZED_AP_BI_MULTIPLIER*/
333 	len += nla_total_size(sizeof(u16));
334 
335 	skb = wlan_cfg80211_vendor_event_alloc(osif_priv->wdev->wiphy,
336 					       osif_priv->wdev, len,
337 					       index, qdf_mem_malloc_flags());
338 	if (!skb) {
339 		hdd_err("skb allocation failed");
340 		return QDF_STATUS_E_NOMEM;
341 	}
342 
343 	if (wlan_cfg80211_nla_put_u64(skb, QCA_WLAN_VENDOR_ATTR_DOZED_AP_COOKIE,
344 				      ll_ps_param->dialog_token)) {
345 		hdd_err("nla_put failed");
346 		status = QDF_STATUS_E_FAILURE;
347 		goto nla_put_failure;
348 	}
349 
350 	if (wlan_cfg80211_nla_put_u64(skb, QCA_WLAN_VENDOR_ATTR_DOZED_AP_NEXT_TSF,
351 				      ll_ps_param->next_tsf)) {
352 		hdd_err("nla_put failed for next tsf");
353 		status = QDF_STATUS_E_FAILURE;
354 		goto nla_put_failure;
355 	}
356 
357 	if (nla_put_u16(skb, QCA_WLAN_VENDOR_ATTR_DOZED_AP_BI_MULTIPLIER,
358 			ll_ps_param->bcn_mult)) {
359 		hdd_err("nla_put for BI multiplier failed");
360 		status = QDF_STATUS_E_FAILURE;
361 		goto nla_put_failure;
362 	}
363 
364 	hdd_debug("next_tsf : %llu, cookie: %u beacon multiplier: %u",
365 		  ll_ps_param->next_tsf, ll_ps_param->dialog_token,
366 		  ll_ps_param->bcn_mult);
367 
368 	wlan_cfg80211_vendor_event(skb, qdf_mem_malloc_flags());
369 
370 	hdd_exit();
371 
372 	return status;
373 
374 nla_put_failure:
375 	wlan_cfg80211_vendor_free_skb(skb);
376 	return status;
377 }
378 
green_ap_register_hdd_callback(struct wlan_objmgr_pdev * pdev,struct green_ap_hdd_callback * hdd_cback)379 QDF_STATUS green_ap_register_hdd_callback(struct wlan_objmgr_pdev *pdev,
380 					  struct green_ap_hdd_callback *hdd_cback)
381 {
382 	struct wlan_pdev_green_ap_ctx *green_ap_ctx;
383 
384 	green_ap_ctx = wlan_objmgr_pdev_get_comp_private_obj(
385 			pdev, WLAN_UMAC_COMP_GREEN_AP);
386 
387 	if (!green_ap_ctx) {
388 		hdd_err("green ap context obtained is NULL");
389 		return QDF_STATUS_E_FAILURE;
390 	}
391 
392 	green_ap_ctx->hdd_cback = *hdd_cback;
393 
394 	return QDF_STATUS_SUCCESS;
395 }
396 #endif
397