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