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