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 ®_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 = ®_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 = ®_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