1 /*
2 * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for
5 * any purpose with or without fee is hereby granted, provided that the
6 * above copyright notice and this permission notice appear in all
7 * copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16 * PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 /**
20 * DOC: wlan_hdd_mcc_quota.c
21 *
22 * WLAN Host Device Driver MCC quota feature cfg80211 APIs implementation
23 *
24 */
25
26 #include <linux/version.h>
27 #include <linux/module.h>
28 #include <linux/kernel.h>
29 #include <linux/init.h>
30 #include <linux/etherdevice.h>
31 #include <linux/wireless.h>
32 #include "osif_sync.h"
33 #include <wlan_hdd_includes.h>
34 #include <net/cfg80211.h>
35 #include "sme_api.h"
36 #include "wlan_hdd_cfg80211.h"
37 #include "wlan_hdd_hostapd.h"
38 #include "wlan_hdd_main.h"
39 #include "wlan_hdd_mcc_quota.h"
40 #include "wlan_hdd_trace.h"
41 #include "qdf_str.h"
42 #include "qdf_trace.h"
43 #include "qdf_types.h"
44 #include "wlan_policy_mgr_api.h"
45 #include <qca_vendor.h>
46 #include "wlan_utility.h"
47 #include "wlan_policy_mgr_ucfg.h"
48 #include "wlan_mlme_ucfg_api.h"
49 #include "wlan_mlme_public_struct.h"
50 #include "wlan_hdd_object_manager.h"
51 #include "sme_api.h"
52 #include "wlan_p2p_ucfg_api.h"
53 #include "wlan_osif_priv.h"
54 #include "wlan_p2p_mcc_quota_public_struct.h"
55 #include "wma.h"
56
57 const struct nla_policy
58 set_mcc_quota_policy[QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_MAX + 1] = {
59 [QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_TYPE] = { .type = NLA_U32 },
60 [QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_ENTRIES] =
61 VENDOR_NLA_POLICY_NESTED(set_mcc_quota_policy),
62 [QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_CHAN_FREQ] = {
63 .type = NLA_U32 },
64 [QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_CHAN_TIME_PERCENTAGE] = {
65 .type = NLA_U32 },
66 [QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_IFINDEX] = { .type = NLA_U32 },
67 [QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_LOW_LATENCY_MODE_ENABLE] = {
68 .type = NLA_U8 },
69 };
70
wlan_hdd_set_mcc_adaptive_sched(struct wlan_objmgr_psoc * psoc,bool enable)71 int wlan_hdd_set_mcc_adaptive_sched(struct wlan_objmgr_psoc *psoc, bool enable)
72 {
73 bool enable_mcc_adaptive_sch;
74
75 hdd_debug("enable : %d", enable);
76 ucfg_policy_mgr_get_mcc_adaptive_sch(psoc, &enable_mcc_adaptive_sch);
77 if (enable_mcc_adaptive_sch) {
78 ucfg_policy_mgr_set_dynamic_mcc_adaptive_sch(psoc, enable);
79 if (QDF_IS_STATUS_ERROR(sme_set_mas(enable))) {
80 hdd_err("Fail to config mcc adaptive sched.");
81 return -EINVAL;
82 }
83 }
84
85 return 0;
86 }
87
88 /**
89 * wlan_hdd_set_mcc_fixed_quota() - Set/Clear MCC fix quota
90 * @hdd_ctx: hdd context
91 * @quota_type: quota type
92 * @tb: attribute information
93 *
94 * Return: 0 on success, negative errno on failure
95 */
96 static int
wlan_hdd_set_mcc_fixed_quota(struct hdd_context * hdd_ctx,enum qca_wlan_vendor_mcc_quota_type quota_type,struct nlattr * tb[])97 wlan_hdd_set_mcc_fixed_quota(struct hdd_context *hdd_ctx,
98 enum qca_wlan_vendor_mcc_quota_type quota_type,
99 struct nlattr *tb[])
100 {
101 struct hdd_adapter *if_adapter;
102 struct nlattr *quota_entries[QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_MAX + 1];
103 struct nlattr *curr_attr;
104 struct wlan_objmgr_psoc *psoc;
105 uint32_t duty_cycle, cmd_id, rem_bytes, entries, if_idx;
106 struct wlan_user_mcc_quota mcc_quota;
107 int att_id, rc;
108
109 hdd_enter();
110
111 if (wlan_hdd_validate_context(hdd_ctx))
112 return -EINVAL;
113
114 psoc = hdd_ctx->psoc;
115 if (!psoc)
116 return -EINVAL;
117
118 if (quota_type != QCA_WLAN_VENDOR_MCC_QUOTA_TYPE_FIXED &&
119 quota_type != QCA_WLAN_VENDOR_MCC_QUOTA_TYPE_CLEAR) {
120 hdd_err("Quota type is not valid %u", quota_type);
121 return -EINVAL;
122 }
123
124 if (quota_type == QCA_WLAN_VENDOR_MCC_QUOTA_TYPE_CLEAR) {
125 /* Remove quota, enable MCC adaptive scheduling */
126 if (wlan_hdd_set_mcc_adaptive_sched(hdd_ctx->psoc, true))
127 return -EAGAIN;
128 mcc_quota.op_mode = QDF_MAX_NO_OF_MODE;
129 mcc_quota.vdev_id = WLAN_UMAC_VDEV_ID_MAX;
130 ucfg_mlme_set_user_mcc_quota(psoc, &mcc_quota);
131 return 0;
132 }
133
134 cmd_id = QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_ENTRIES;
135 if (!tb[cmd_id]) {
136 hdd_err("No entries present");
137 return -EINVAL;
138 }
139
140 entries = 0;
141 nla_for_each_nested(curr_attr, tb[cmd_id], rem_bytes) {
142 if (entries > 0) {
143 hdd_debug("Only one entry permitted");
144 hdd_debug("Entry (%d) for (%u) is ignored",
145 entries, nla_type(curr_attr));
146 entries++;
147 continue;
148 }
149 rc = wlan_cfg80211_nla_parse_nested(quota_entries,
150 QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_MAX,
151 curr_attr,
152 set_mcc_quota_policy);
153 if (rc) {
154 hdd_err("Entry parse error %d", rc);
155 return -EINVAL;
156 }
157
158 att_id = QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_IFINDEX;
159 if (!quota_entries[att_id]) {
160 hdd_err("if_index not specified");
161 return -EINVAL;
162 }
163
164 if_idx = nla_get_u32(quota_entries[att_id]);
165 if (if_idx == 0) {
166 hdd_debug("Invalid if_index");
167 return -EINVAL;
168 }
169 if_adapter = hdd_get_adapter_by_ifindex(hdd_ctx, if_idx);
170
171 if (!if_adapter) {
172 hdd_err("interface (%u) not found", if_idx);
173 return -EINVAL;
174 }
175
176 if (wlan_hdd_validate_vdev_id(if_adapter->deflink->vdev_id))
177 return -EINVAL;
178
179 att_id = QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_CHAN_TIME_PERCENTAGE;
180 if (!quota_entries[att_id]) {
181 hdd_err("Quota not specified");
182 return -EINVAL;
183 }
184 mcc_quota.quota = nla_get_u32(quota_entries[att_id]);
185 mcc_quota.vdev_id = if_adapter->deflink->vdev_id;
186 mcc_quota.op_mode = if_adapter->device_mode;
187
188 entries++;
189 }
190
191 if (entries == 0) {
192 hdd_err("No entries found");
193 return -EINVAL;
194 }
195
196 if (mcc_quota.op_mode != QDF_P2P_GO_MODE) {
197 hdd_debug("Support only P2P GO mode now");
198 return -EOPNOTSUPP;
199 }
200
201 ucfg_mlme_set_user_mcc_quota(psoc, &mcc_quota);
202
203 duty_cycle = ucfg_mlme_get_user_mcc_quota_percentage(psoc);
204
205 if (duty_cycle == 0) {
206 hdd_debug("Quota will be configured when MCC scenario exists");
207 return 0;
208 }
209
210 if (wlan_hdd_set_mcc_adaptive_sched(hdd_ctx->psoc, false))
211 return -EAGAIN;
212
213 if (wlan_hdd_send_mcc_vdev_quota(if_adapter, duty_cycle))
214 return -EINVAL;
215
216 return 0;
217 }
218
219 /**
220 * wlan_hdd_set_mcc_low_latency_quota() - Enable/disable MCC low latency
221 * mode
222 * @hdd_ctx: hdd context
223 * @wdev: wdev object
224 * @quota_type: quota type
225 * @tb: attribute information
226 *
227 * Return: 0 on success, negative errno on failure
228 */
wlan_hdd_set_mcc_low_latency_quota(struct hdd_context * hdd_ctx,struct wireless_dev * wdev,enum qca_wlan_vendor_mcc_quota_type quota_type,struct nlattr * tb[])229 static int wlan_hdd_set_mcc_low_latency_quota(
230 struct hdd_context *hdd_ctx,
231 struct wireless_dev *wdev,
232 enum qca_wlan_vendor_mcc_quota_type quota_type,
233 struct nlattr *tb[])
234 {
235 struct net_device *dev = wdev->netdev;
236 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
237 uint32_t cmd_id;
238 uint8_t ll_enable;
239 int rc;
240 uint32_t ll_mode = 0;
241
242 if (wlan_hdd_validate_context(hdd_ctx))
243 return -EINVAL;
244
245 if (quota_type != QCA_WLAN_VENDOR_MCC_QUOTA_TYPE_LOW_LATENCY) {
246 hdd_err("Quota type %u is not expected %d", quota_type,
247 QCA_WLAN_VENDOR_MCC_QUOTA_TYPE_LOW_LATENCY);
248 return -EINVAL;
249 }
250 cmd_id = QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_LOW_LATENCY_MODE_ENABLE;
251 if (!tb[cmd_id]) {
252 hdd_err("No MCC LL mode attr id %d", cmd_id);
253 return -EINVAL;
254 }
255 ll_enable = nla_get_u8(tb[cmd_id]);
256 if (ll_enable)
257 ll_mode = 1;
258 hdd_debug("set conc ll mode 0x%08x", ll_mode);
259 rc = wma_cli_set_command(adapter->deflink->vdev_id,
260 wmi_pdev_param_set_conc_low_latency_mode,
261 ll_mode, PDEV_CMD);
262 if (rc)
263 hdd_err("Failed to set conc low latency mode, %d", rc);
264
265 return 0;
266 }
267
wlan_hdd_cfg80211_set_mcc_quota(struct wiphy * wiphy,struct wireless_dev * wdev,const void * attr,int attr_len)268 int wlan_hdd_cfg80211_set_mcc_quota(struct wiphy *wiphy,
269 struct wireless_dev *wdev,
270 const void *attr,
271 int attr_len)
272 {
273 struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
274 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_MAX + 1];
275 struct wlan_objmgr_psoc *psoc;
276 uint32_t cmd_id, quota_type;
277 int rc;
278
279 hdd_enter();
280
281 if (wlan_hdd_validate_context(hdd_ctx))
282 return -EINVAL;
283
284 psoc = hdd_ctx->psoc;
285 if (!psoc)
286 return -EINVAL;
287
288 if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_MAX,
289 attr, attr_len, set_mcc_quota_policy)) {
290 hdd_err("Error parsing attributes");
291 return -EINVAL;
292 }
293
294 cmd_id = QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_TYPE;
295 if (!tb[cmd_id]) {
296 hdd_err("Quota type not specified");
297 return -EINVAL;
298 }
299 quota_type = nla_get_u32(tb[cmd_id]);
300 if (quota_type == QCA_WLAN_VENDOR_MCC_QUOTA_TYPE_FIXED ||
301 quota_type == QCA_WLAN_VENDOR_MCC_QUOTA_TYPE_CLEAR) {
302 rc = wlan_hdd_set_mcc_fixed_quota(hdd_ctx, quota_type, tb);
303 } else if (quota_type == QCA_WLAN_VENDOR_MCC_QUOTA_TYPE_LOW_LATENCY) {
304 rc = wlan_hdd_set_mcc_low_latency_quota(hdd_ctx, wdev,
305 quota_type, tb);
306 } else {
307 hdd_err("Quota type is not valid %u", quota_type);
308 return -EINVAL;
309 }
310
311 return rc;
312 }
313
wlan_hdd_apply_user_mcc_quota(struct hdd_adapter * adapter)314 int wlan_hdd_apply_user_mcc_quota(struct hdd_adapter *adapter)
315 {
316 struct hdd_context *hdd_ctx;
317 uint32_t quota_val;
318
319 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
320 if (!hdd_ctx)
321 return -EINVAL;
322
323 quota_val =
324 ucfg_mlme_get_user_mcc_quota_percentage(hdd_ctx->psoc);
325
326 if (quota_val == 0) {
327 hdd_debug("no mcc/quota for mode %d, vdev_id : %u",
328 adapter->device_mode, adapter->deflink->vdev_id);
329 return 0;
330 }
331
332 if (wlan_hdd_set_mcc_adaptive_sched(hdd_ctx->psoc, false))
333 return 0;
334
335 if (wlan_hdd_send_mcc_vdev_quota(adapter, quota_val)) {
336 hdd_info("Could not send quota");
337 wlan_hdd_set_mcc_adaptive_sched(hdd_ctx->psoc, true);
338 }
339
340 return 0;
341 }
342
343 /**
344 * wlan_cfg80211_indicate_mcc_quota() - Callback to indicate mcc quota
345 * event to upper layer
346 * @psoc: pointer to soc object
347 * @vdev: vdev object
348 * @quota_info: quota info
349 *
350 * This callback will be used to indicate mcc quota info to upper layer
351 *
352 * Return: QDF_STATUS_SUCCESS if event is indicated to OS successfully.
353 */
354 static QDF_STATUS
wlan_cfg80211_indicate_mcc_quota(struct wlan_objmgr_psoc * psoc,struct wlan_objmgr_vdev * vdev,struct mcc_quota_info * quota_info)355 wlan_cfg80211_indicate_mcc_quota(struct wlan_objmgr_psoc *psoc,
356 struct wlan_objmgr_vdev *vdev,
357 struct mcc_quota_info *quota_info)
358 {
359 uint32_t data_len;
360 struct sk_buff *vendor_event;
361 QDF_STATUS status;
362 struct vdev_osif_priv *vdev_osif_priv;
363 struct wireless_dev *wdev;
364 struct pdev_osif_priv *pdev_osif_priv;
365 struct wlan_objmgr_pdev *pdev;
366 uint32_t idx;
367 uint32_t vdev_id;
368 struct nlattr *quota_attrs, *quota_element;
369
370 if (!vdev) {
371 hdd_debug("null vdev");
372 return QDF_STATUS_E_INVAL;
373 }
374
375 vdev_osif_priv = wlan_vdev_get_ospriv(vdev);
376 if (!vdev_osif_priv || !vdev_osif_priv->wdev) {
377 hdd_debug("null wdev");
378 return QDF_STATUS_E_INVAL;
379 }
380
381 wdev = vdev_osif_priv->wdev;
382 vdev_id = wlan_vdev_get_id(vdev);
383 pdev = wlan_vdev_get_pdev(vdev);
384 if (!pdev) {
385 hdd_debug("null pdev");
386 return QDF_STATUS_E_INVAL;
387 }
388
389 pdev_osif_priv = wlan_pdev_get_ospriv(pdev);
390 if (!pdev_osif_priv || !pdev_osif_priv->wiphy) {
391 hdd_debug("null wiphy");
392 return QDF_STATUS_E_INVAL;
393 }
394
395 /* nested element of QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_CHAN_FREQ and
396 * QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_CHAN_TIME_PERCENTAGE
397 */
398 data_len = nla_total_size(nla_total_size(sizeof(uint32_t)) +
399 nla_total_size(sizeof(uint32_t)));
400 /* nested array of quota element */
401 data_len = nla_total_size(data_len * quota_info->num_chan_quota);
402 /* QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_TYPE and NL msg header */
403 data_len += nla_total_size(sizeof(uint32_t)) + NLMSG_HDRLEN;
404
405 vendor_event = wlan_cfg80211_vendor_event_alloc(pdev_osif_priv->wiphy,
406 wdev, data_len,
407 QCA_NL80211_VENDOR_SUBCMD_MCC_QUOTA_INDEX,
408 GFP_KERNEL);
409 if (!vendor_event) {
410 hdd_debug("wlan_cfg80211_vendor_event_alloc failed");
411 return QDF_STATUS_E_NOMEM;
412 }
413 if (nla_put_u32(vendor_event,
414 QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_TYPE,
415 quota_info->type)) {
416 status = QDF_STATUS_E_NOMEM;
417 hdd_debug("add QUOTA_TYPE failed");
418 goto err;
419 }
420
421 quota_attrs = nla_nest_start(vendor_event,
422 QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_ENTRIES);
423 if (!quota_attrs) {
424 status = QDF_STATUS_E_NOMEM;
425 hdd_debug("add QUOTA_ENTRIES failed");
426 goto err;
427 }
428 hdd_debug("mcc quota vdev %d type %d num %d",
429 vdev_id, quota_info->type, quota_info->num_chan_quota);
430
431 for (idx = 0; idx < quota_info->num_chan_quota; idx++) {
432 quota_element = nla_nest_start(vendor_event, idx);
433 if (!quota_element) {
434 status = QDF_STATUS_E_NOMEM;
435 hdd_debug("add quota idx failed");
436 goto err;
437 }
438
439 if (nla_put_u32(vendor_event,
440 QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_CHAN_FREQ,
441 quota_info->chan_quota[idx].chan_mhz)) {
442 status = QDF_STATUS_E_NOMEM;
443 hdd_debug("add QUOTA_CHAN_FREQ failed");
444 goto err;
445 }
446
447 if (nla_put_u32(vendor_event,
448 QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_CHAN_TIME_PERCENTAGE,
449 quota_info->chan_quota[idx].channel_time_quota)) {
450 status = QDF_STATUS_E_NOMEM;
451 hdd_debug("add QUOTA_CHAN_TIME_PERCENTAGE failed");
452 goto err;
453 }
454
455 nla_nest_end(vendor_event, quota_element);
456 hdd_debug("mcc quota vdev %d [%d] %d quota %d",
457 vdev_id, idx, quota_info->chan_quota[idx].chan_mhz,
458 quota_info->chan_quota[idx].channel_time_quota);
459 }
460 nla_nest_end(vendor_event, quota_attrs);
461
462 wlan_cfg80211_vendor_event(vendor_event, GFP_KERNEL);
463
464 return QDF_STATUS_SUCCESS;
465 err:
466 wlan_cfg80211_vendor_free_skb(vendor_event);
467
468 return status;
469 }
470
471 /**
472 * wlan_hdd_register_mcc_quota_event_callback() - Register hdd callback to get
473 * mcc quota event to upper layer
474 * @hdd_ctx: pointer to hdd context
475 *
476 * Return: void
477 */
wlan_hdd_register_mcc_quota_event_callback(struct hdd_context * hdd_ctx)478 void wlan_hdd_register_mcc_quota_event_callback(struct hdd_context *hdd_ctx)
479 {
480 ucfg_p2p_register_mcc_quota_event_os_if_cb(hdd_ctx->psoc,
481 wlan_cfg80211_indicate_mcc_quota);
482 }
483