1 /*
2  * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022-2024 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_tx_power.c
22  *
23  * WLAN tx power setting functions
24  *
25  */
26 
27 #include "osif_sync.h"
28 #include <wlan_hdd_includes.h>
29 #include <linux/netdevice.h>
30 #include <linux/skbuff.h>
31 #include <linux/etherdevice.h>
32 #include <linux/if_ether.h>
33 #include <wma_api.h>
34 #include <wlan_hdd_tx_power.h>
35 #include "wlan_hdd_object_manager.h"
36 
37 #define MAX_TXPOWER_SCALE 4
38 
39 const struct nla_policy
40 txpower_scale_policy[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_MAX + 1] = {
41 	[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE] = { .type = NLA_U8 },
42 };
43 
44 /**
45  * __wlan_hdd_cfg80211_txpower_scale () - txpower scaling
46  * @wiphy: Pointer to wireless phy
47  * @wdev: Pointer to wireless device
48  * @data: Pointer to data
49  * @data_len: Data length
50  *
51  * Return: 0 on success, negative errno on failure
52  */
__wlan_hdd_cfg80211_txpower_scale(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)53 static int __wlan_hdd_cfg80211_txpower_scale(struct wiphy *wiphy,
54 					     struct wireless_dev *wdev,
55 					     const void *data,
56 					     int data_len)
57 {
58 	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
59 	struct net_device *dev = wdev->netdev;
60 	struct hdd_adapter *adapter;
61 	int ret;
62 	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_MAX + 1];
63 	uint8_t scale_value;
64 	QDF_STATUS status;
65 
66 	hdd_enter_dev(dev);
67 
68 	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
69 		hdd_err("Command not allowed in FTM mode");
70 		return -EPERM;
71 	}
72 
73 	ret = wlan_hdd_validate_context(hdd_ctx);
74 	if (ret)
75 		return ret;
76 
77 	adapter = WLAN_HDD_GET_PRIV_PTR(dev);
78 
79 	if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_MAX,
80 				    data, data_len, txpower_scale_policy)) {
81 		hdd_err("Invalid ATTR");
82 		return -EINVAL;
83 	}
84 
85 	if (!tb[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE]) {
86 		hdd_err("attr tx power scale failed");
87 		return -EINVAL;
88 	}
89 
90 	scale_value = nla_get_u8(tb
91 		    [QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE]);
92 
93 	if (scale_value > MAX_TXPOWER_SCALE) {
94 		hdd_err("Invalid tx power scale level");
95 		return -EINVAL;
96 	}
97 
98 	status = wma_set_tx_power_scale(adapter->deflink->vdev_id, scale_value);
99 
100 	if (status != QDF_STATUS_SUCCESS) {
101 		hdd_err("Set tx power scale failed");
102 		return -EINVAL;
103 	}
104 
105 	return 0;
106 }
107 
wlan_hdd_cfg80211_txpower_scale(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)108 int wlan_hdd_cfg80211_txpower_scale(struct wiphy *wiphy,
109 				    struct wireless_dev *wdev,
110 				    const void *data,
111 				    int data_len)
112 {
113 	struct osif_vdev_sync *vdev_sync;
114 	int errno;
115 
116 	errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
117 	if (errno)
118 		return errno;
119 
120 	errno = __wlan_hdd_cfg80211_txpower_scale(wiphy, wdev, data, data_len);
121 
122 	osif_vdev_sync_op_stop(vdev_sync);
123 
124 	return errno;
125 }
126 
127 const struct nla_policy txpower_scale_decr_db_policy
128 [QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB_MAX + 1] = {
129 	[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB] = { .type = NLA_U8 },
130 };
131 
132 /**
133  * __wlan_hdd_cfg80211_txpower_scale_decr_db () - txpower scaling
134  * @wiphy: Pointer to wireless phy
135  * @wdev: Pointer to wireless device
136  * @data: Pointer to data
137  * @data_len: Data length
138  *
139  * Return: 0 on success, negative errno on failure
140  */
141 static int
__wlan_hdd_cfg80211_txpower_scale_decr_db(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)142 __wlan_hdd_cfg80211_txpower_scale_decr_db(struct wiphy *wiphy,
143 					  struct wireless_dev *wdev,
144 					  const void *data,
145 					  int data_len)
146 {
147 	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
148 	struct net_device *dev = wdev->netdev;
149 	struct hdd_adapter *adapter;
150 	int ret;
151 	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB_MAX + 1];
152 	uint8_t scale_value;
153 	QDF_STATUS status;
154 
155 	hdd_enter_dev(dev);
156 
157 	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
158 		hdd_err("Command not allowed in FTM mode");
159 		return -EPERM;
160 	}
161 
162 	ret = wlan_hdd_validate_context(hdd_ctx);
163 	if (ret)
164 		return ret;
165 
166 	adapter = WLAN_HDD_GET_PRIV_PTR(dev);
167 
168 	if (wlan_cfg80211_nla_parse(tb,
169 				QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB_MAX,
170 				data, data_len,
171 				txpower_scale_decr_db_policy)) {
172 		hdd_err("Invalid ATTR");
173 		return -EINVAL;
174 	}
175 
176 	if (!tb[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB]) {
177 		hdd_err("attr tx power decrease db value failed");
178 		return -EINVAL;
179 	}
180 
181 	scale_value = nla_get_u8(tb
182 		    [QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB]);
183 
184 	status = wma_set_tx_power_scale_decr_db(adapter->deflink->vdev_id,
185 						scale_value);
186 
187 	if (status != QDF_STATUS_SUCCESS) {
188 		hdd_err("Set tx power decrease db failed");
189 		return -EINVAL;
190 	}
191 
192 	return 0;
193 }
194 
wlan_hdd_cfg80211_txpower_scale_decr_db(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)195 int wlan_hdd_cfg80211_txpower_scale_decr_db(struct wiphy *wiphy,
196 					    struct wireless_dev *wdev,
197 					    const void *data,
198 					    int data_len)
199 {
200 	struct osif_vdev_sync *vdev_sync;
201 	int errno;
202 
203 	errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
204 	if (errno)
205 		return errno;
206 
207 	errno = __wlan_hdd_cfg80211_txpower_scale_decr_db(wiphy, wdev,
208 							  data, data_len);
209 
210 	osif_vdev_sync_op_stop(vdev_sync);
211 
212 	return errno;
213 }
214 
get_default_tpc_info_vendor_sbk_len(void)215 static uint32_t get_default_tpc_info_vendor_sbk_len(void)
216 {
217 	uint32_t skb_len;
218 
219 	/* QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL_FREQUENCY */
220 	skb_len = nla_total_size(sizeof(u32));
221 	/* QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL_TX_POWER */
222 	skb_len += nla_total_size(sizeof(s8));
223 	skb_len = nla_total_size(skb_len) * MAX_NUM_PWR_LEVEL;
224 	skb_len = nla_total_size(skb_len);
225 
226 	/* QCA_WLAN_VENDOR_ATTR_TPC_BSSID */
227 	skb_len += nla_total_size(QDF_MAC_ADDR_SIZE);
228 	/* QCA_WLAN_VENDOR_ATTR_TPC_PSD_POWER */
229 	skb_len += nla_total_size(sizeof(u8));
230 	/* QCA_WLAN_VENDOR_ATTR_TPC_EIRP_POWER */
231 	skb_len += nla_total_size(sizeof(s8));
232 	/* QCA_WLAN_VENDOR_ATTR_TPC_POWER_TYPE_6GHZ */
233 	skb_len += nla_total_size(sizeof(u8));
234 	/* QCA_WLAN_VENDOR_ATTR_TPC_AP_CONSTRAINT_POWER */
235 	skb_len += nla_total_size(sizeof(u8));
236 
237 	skb_len = nla_total_size(skb_len) * WLAN_MAX_ML_BSS_LINKS;
238 	skb_len = nla_total_size(skb_len) + NLMSG_HDRLEN;
239 
240 	return skb_len;
241 }
242 
243 static int
__wlan_hdd_cfg80211_get_reg_tpc_info(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)244 __wlan_hdd_cfg80211_get_reg_tpc_info(struct wiphy *wiphy,
245 				     struct wireless_dev *wdev,
246 				     const void *data,
247 				     int data_len)
248 {
249 	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
250 	struct net_device *dev = wdev->netdev;
251 	struct hdd_adapter *adapter;
252 	int ret;
253 	QDF_STATUS status;
254 	struct wlan_objmgr_vdev *vdev;
255 	int num_of_links = 0;
256 	struct qdf_mac_addr link_bssid[WLAN_MAX_ML_BSS_LINKS];
257 	struct reg_tpc_power_info reg_tpc_info[WLAN_MAX_ML_BSS_LINKS];
258 	struct sk_buff *reply_skb = NULL;
259 	uint32_t skb_len, i, j;
260 	struct wlan_hdd_link_info *link_info;
261 	struct nlattr *tpc_links_attr, *tpc_attr, *levels_attr, *tpc_level;
262 	int attr_id;
263 	struct chan_power_info *chan_power_info;
264 
265 	hdd_enter_dev(dev);
266 
267 	if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) {
268 		hdd_err("Command not allowed in FTM mode");
269 		return -EPERM;
270 	}
271 
272 	ret = wlan_hdd_validate_context(hdd_ctx);
273 	if (ret)
274 		return ret;
275 
276 	adapter = WLAN_HDD_GET_PRIV_PTR(dev);
277 	if (!adapter) {
278 		hdd_err("adapter null");
279 		return -EPERM;
280 	}
281 
282 	if (adapter->device_mode != QDF_STA_MODE) {
283 		hdd_err("device mode %d not support", adapter->device_mode);
284 		return -EPERM;
285 	}
286 
287 	hdd_adapter_for_each_link_info(adapter, link_info) {
288 		if (num_of_links >= WLAN_MAX_ML_BSS_LINKS)
289 			break;
290 		if (link_info->vdev_id == WLAN_INVALID_VDEV_ID)
291 			continue;
292 		if (!hdd_cm_is_vdev_connected(link_info))
293 			continue;
294 
295 		vdev = hdd_objmgr_get_vdev_by_user(link_info,
296 						   WLAN_OSIF_POWER_ID);
297 		if (!vdev)
298 			continue;
299 
300 		status = wlan_vdev_get_bss_peer_mac(vdev,
301 						    &link_bssid[num_of_links]);
302 		if (QDF_IS_STATUS_ERROR(status)) {
303 			hdd_err("failed to get bssid for vdev %d",
304 				link_info->vdev_id);
305 			goto next_link;
306 		}
307 
308 		status =
309 		ucfg_wlan_mlme_get_reg_tpc_info(vdev,
310 						&reg_tpc_info[num_of_links]);
311 		if (QDF_IS_STATUS_ERROR(status)) {
312 			hdd_err("failed to get tpc info for vdev %d",
313 				link_info->vdev_id);
314 			goto next_link;
315 		}
316 
317 		num_of_links++;
318 next_link:
319 		hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_POWER_ID);
320 	}
321 
322 	if (!num_of_links) {
323 		hdd_err("get tpc info failed - nun of links 0");
324 		return -EINVAL;
325 	}
326 
327 	skb_len = get_default_tpc_info_vendor_sbk_len();
328 
329 	reply_skb = wlan_cfg80211_vendor_cmd_alloc_reply_skb(
330 						wiphy,
331 						skb_len);
332 	if (!reply_skb) {
333 		hdd_err("alloc reply_skb failed");
334 		status = QDF_STATUS_E_NOMEM;
335 		goto free_skb;
336 	}
337 
338 	tpc_links_attr = nla_nest_start(reply_skb,
339 					QCA_WLAN_VENDOR_ATTR_TPC_LINKS);
340 	if (!tpc_links_attr) {
341 		hdd_err("failed to add QCA_WLAN_VENDOR_ATTR_TPC_LINKS");
342 		status = QDF_STATUS_E_INVAL;
343 		goto free_skb;
344 	}
345 
346 	for (i = 0; i < num_of_links; i++) {
347 		tpc_attr = nla_nest_start(reply_skb, i);
348 		if (!tpc_attr) {
349 			hdd_err("tpc_attr null, %d", i);
350 			continue;
351 		}
352 		if (nla_put(reply_skb, QCA_WLAN_VENDOR_ATTR_TPC_BSSID,
353 			    QDF_MAC_ADDR_SIZE, &link_bssid[i])) {
354 			hdd_err("failed to put mac_addr");
355 			status = QDF_STATUS_E_INVAL;
356 			goto free_skb;
357 		}
358 
359 		if (reg_tpc_info[i].is_psd_power &&
360 		    nla_put_flag(reply_skb,
361 				 QCA_WLAN_VENDOR_ATTR_TPC_PSD_POWER)) {
362 			osif_err("failed to put psd flag");
363 			return QDF_STATUS_E_INVAL;
364 		}
365 
366 		if (nla_put_s8(reply_skb,
367 			       QCA_WLAN_VENDOR_ATTR_TPC_EIRP_POWER,
368 			       reg_tpc_info[i].reg_max[0])) {
369 			hdd_err("failed to put eirp_power");
370 			status = QDF_STATUS_E_INVAL;
371 			goto free_skb;
372 		}
373 
374 		chan_power_info = &reg_tpc_info[i].chan_power_info[0];
375 		if (WLAN_REG_IS_6GHZ_CHAN_FREQ(chan_power_info->chan_cfreq) &&
376 		    nla_put_u8(reply_skb,
377 			       QCA_WLAN_VENDOR_ATTR_TPC_POWER_TYPE_6GHZ,
378 			       reg_tpc_info[i].power_type_6g)) {
379 			hdd_err("failed to put power_type_6g");
380 			status = QDF_STATUS_E_INVAL;
381 			goto free_skb;
382 		}
383 
384 		if (nla_put_u8(reply_skb,
385 			       QCA_WLAN_VENDOR_ATTR_TPC_AP_CONSTRAINT_POWER,
386 			       reg_tpc_info[i].ap_constraint_power)) {
387 			hdd_err("failed to put ap_constraint_power");
388 			status = QDF_STATUS_E_INVAL;
389 			goto free_skb;
390 		}
391 
392 		hdd_debug("%d tpc for bssid "QDF_MAC_ADDR_FMT" is_psd %d reg power %d 6ghz pwr type %d ap_constraint_power %d",
393 			  i, QDF_MAC_ADDR_REF(link_bssid[i].bytes),
394 			  reg_tpc_info[i].is_psd_power,
395 			  reg_tpc_info[i].reg_max[0],
396 			  reg_tpc_info[i].power_type_6g,
397 			  reg_tpc_info[i].ap_constraint_power);
398 
399 		levels_attr = nla_nest_start(
400 			reply_skb, QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL);
401 		if (!levels_attr) {
402 			hdd_err("failed to add QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL");
403 			status = QDF_STATUS_E_INVAL;
404 			goto free_skb;
405 		}
406 		for (j = 0; j < reg_tpc_info[i].num_pwr_levels; j++) {
407 			tpc_level = nla_nest_start(reply_skb, j);
408 			if (!tpc_level) {
409 				hdd_err("tpc_level null. level %d", j);
410 				continue;
411 			}
412 			chan_power_info = &reg_tpc_info[i].chan_power_info[j];
413 
414 			attr_id = QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL_FREQUENCY;
415 			if (nla_put_u32(reply_skb,
416 					attr_id,
417 					chan_power_info->chan_cfreq)) {
418 				hdd_err("failed to put chan_cfreq %d",
419 					chan_power_info->chan_cfreq);
420 				status = QDF_STATUS_E_INVAL;
421 				goto free_skb;
422 			}
423 
424 			attr_id = QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL_TX_POWER;
425 			if (nla_put_s8(reply_skb,
426 				       attr_id,
427 				       chan_power_info->tx_power)) {
428 				hdd_err("failed to put tx_power %d",
429 					chan_power_info->tx_power);
430 				status = QDF_STATUS_E_INVAL;
431 				goto free_skb;
432 			}
433 
434 			hdd_debug("%d cfreq %d tx_power %d", j,
435 				  chan_power_info->chan_cfreq,
436 				  chan_power_info->tx_power);
437 
438 			nla_nest_end(reply_skb, tpc_level);
439 		}
440 		nla_nest_end(reply_skb, levels_attr);
441 
442 		nla_nest_end(reply_skb, tpc_attr);
443 	}
444 
445 	nla_nest_end(reply_skb, tpc_links_attr);
446 
447 	ret = wlan_cfg80211_vendor_cmd_reply(reply_skb);
448 
449 	hdd_exit();
450 
451 	return ret;
452 free_skb:
453 	if (reply_skb)
454 		wlan_cfg80211_vendor_free_skb(reply_skb);
455 	hdd_exit();
456 	return qdf_status_to_os_return(status);
457 }
458 
wlan_hdd_cfg80211_get_reg_tpc_info(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)459 int wlan_hdd_cfg80211_get_reg_tpc_info(struct wiphy *wiphy,
460 				       struct wireless_dev *wdev,
461 				       const void *data,
462 				       int data_len)
463 {
464 	struct osif_vdev_sync *vdev_sync;
465 	int errno;
466 
467 	errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
468 	if (errno)
469 		return errno;
470 
471 	errno = __wlan_hdd_cfg80211_get_reg_tpc_info(wiphy, wdev,
472 						     data, data_len);
473 
474 	osif_vdev_sync_op_stop(vdev_sync);
475 
476 	return errno;
477 }
478