Selaa lähdekoodia

qcacld-3.0: Add vendor command support for query TPC information

Add support for QCA_NL80211_VENDOR_SUBCMD_REGULATORY_TPC_INFO vendor
command to return the STA TPC information used within the BSS.
If STA is ML STA, multiple link TPC information will be returned to
userspace.

Change-Id: I6fd30893c057c876e9a01e112595e5cb0c906a70
CRs-Fixed: 3665538
Liangwei Dong 1 vuosi sitten
vanhempi
sitoutus
7a843bc437

+ 11 - 0
components/mlme/dispatcher/inc/wlan_mlme_ucfg_api.h

@@ -4604,6 +4604,17 @@ QDF_STATUS
 ucfg_mlme_get_current_tx_power_level(struct wlan_objmgr_psoc *psoc,
 				     uint8_t *value);
 
+/**
+ * ucfg_wlan_mlme_get_reg_tpc_info() - get current regulatory tpc info
+ * @vdev:   pointer to vdev object
+ * @tpc_info:  pointer to tpc info buffer
+ *
+ * Return: QDF Status
+ */
+QDF_STATUS
+ucfg_wlan_mlme_get_reg_tpc_info(struct wlan_objmgr_vdev *vdev,
+				struct reg_tpc_power_info *tpc_info);
+
 /**
  * ucfg_mlme_set_obss_detection_offload_enabled() - Enable obss offload
  * @psoc:   pointer to psoc object

+ 18 - 0
components/mlme/dispatcher/src/wlan_mlme_ucfg_api.c

@@ -1732,6 +1732,24 @@ ucfg_mlme_get_current_tx_power_level(struct wlan_objmgr_psoc *psoc,
 	return QDF_STATUS_SUCCESS;
 }
 
+QDF_STATUS ucfg_wlan_mlme_get_reg_tpc_info(struct wlan_objmgr_vdev *vdev,
+					   struct reg_tpc_power_info *tpc_info)
+{
+	struct vdev_mlme_obj *mlme_obj;
+
+	mlme_obj = wlan_vdev_mlme_get_cmpt_obj(vdev);
+
+	if (!mlme_obj) {
+		mlme_legacy_err("vdev component object is NULL");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	qdf_mem_copy(tpc_info, &mlme_obj->reg_tpc_obj,
+		     sizeof(struct reg_tpc_power_info));
+
+	return QDF_STATUS_SUCCESS;
+}
+
 QDF_STATUS
 ucfg_mlme_set_obss_detection_offload_enabled(struct wlan_objmgr_psoc *psoc,
 					     uint8_t value)

+ 1 - 0
core/hdd/src/wlan_hdd_cfg80211.c

@@ -20884,6 +20884,7 @@ const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] = {
 	FEATURE_AFC_VENDOR_COMMANDS
 	FEATURE_LL_LT_SAP_VENDOR_COMMANDS
 	FEATURE_TX_LATENCY_STATS_COMMANDS
+	FEATURE_REGULATORY_TPC_INFO_VENDOR_COMMANDS
 };
 
 struct hdd_context *hdd_cfg80211_wiphy_alloc(void)

+ 256 - 0
core/hdd/src/wlan_hdd_tx_power.c

@@ -32,6 +32,7 @@
 #include <linux/if_ether.h>
 #include <wma_api.h>
 #include <wlan_hdd_tx_power.h>
+#include "wlan_hdd_object_manager.h"
 
 #define MAX_TXPOWER_SCALE 4
 
@@ -211,3 +212,258 @@ int wlan_hdd_cfg80211_txpower_scale_decr_db(struct wiphy *wiphy,
 	return errno;
 }
 
+static uint32_t get_default_tpc_info_vendor_sbk_len(void)
+{
+	uint32_t skb_len;
+
+	/* QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL_FREQUENCY */
+	skb_len = nla_total_size(sizeof(u32));
+	/* QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL_TX_POWER */
+	skb_len += nla_total_size(sizeof(s8));
+	skb_len = nla_total_size(skb_len) * MAX_NUM_PWR_LEVEL;
+	skb_len = nla_total_size(skb_len);
+
+	/* QCA_WLAN_VENDOR_ATTR_TPC_BSSID */
+	skb_len += nla_total_size(QDF_MAC_ADDR_SIZE);
+	/* QCA_WLAN_VENDOR_ATTR_TPC_PSD_POWER */
+	skb_len += nla_total_size(sizeof(u8));
+	/* QCA_WLAN_VENDOR_ATTR_TPC_EIRP_POWER */
+	skb_len += nla_total_size(sizeof(s8));
+	/* QCA_WLAN_VENDOR_ATTR_TPC_POWER_TYPE_6GHZ */
+	skb_len += nla_total_size(sizeof(u8));
+	/* QCA_WLAN_VENDOR_ATTR_TPC_AP_CONSTRAINT_POWER */
+	skb_len += nla_total_size(sizeof(u8));
+
+	skb_len = nla_total_size(skb_len) * WLAN_MAX_ML_BSS_LINKS;
+	skb_len = nla_total_size(skb_len) + NLMSG_HDRLEN;
+
+	return skb_len;
+}
+
+static int
+__wlan_hdd_cfg80211_get_reg_tpc_info(struct wiphy *wiphy,
+				     struct wireless_dev *wdev,
+				     const void *data,
+				     int data_len)
+{
+	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
+	struct net_device *dev = wdev->netdev;
+	struct hdd_adapter *adapter;
+	int ret;
+	QDF_STATUS status;
+	struct wlan_objmgr_vdev *vdev;
+	int num_of_links = 0;
+	struct qdf_mac_addr link_bssid[WLAN_MAX_ML_BSS_LINKS];
+	struct reg_tpc_power_info reg_tpc_info[WLAN_MAX_ML_BSS_LINKS];
+	struct sk_buff *reply_skb = NULL;
+	uint32_t skb_len, i, j;
+	struct wlan_hdd_link_info *link_info;
+	struct nlattr *tpc_links_attr, *tpc_attr, *levels_attr, *tpc_level;
+	int attr_id;
+	struct chan_power_info *chan_power_info;
+
+	hdd_enter_dev(dev);
+
+	if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) {
+		hdd_err("Command not allowed in FTM mode");
+		return -EPERM;
+	}
+
+	ret = wlan_hdd_validate_context(hdd_ctx);
+	if (ret)
+		return ret;
+
+	adapter = WLAN_HDD_GET_PRIV_PTR(dev);
+	if (!adapter) {
+		hdd_err("adapter null");
+		return -EPERM;
+	}
+
+	if (adapter->device_mode != QDF_STA_MODE) {
+		hdd_err("device mode %d not support", adapter->device_mode);
+		return -EPERM;
+	}
+
+	hdd_adapter_for_each_link_info(adapter, link_info) {
+		if (num_of_links >= WLAN_MAX_ML_BSS_LINKS)
+			break;
+		if (link_info->vdev_id == WLAN_INVALID_VDEV_ID)
+			continue;
+		if (!hdd_cm_is_vdev_connected(link_info))
+			continue;
+
+		vdev = hdd_objmgr_get_vdev_by_user(link_info,
+						   WLAN_OSIF_POWER_ID);
+		if (!vdev)
+			continue;
+
+		status = wlan_vdev_get_bss_peer_mac(vdev,
+						    &link_bssid[num_of_links]);
+		if (QDF_IS_STATUS_ERROR(status)) {
+			hdd_err("failed to get bssid for vdev %d",
+				link_info->vdev_id);
+			goto next_link;
+		}
+
+		status =
+		ucfg_wlan_mlme_get_reg_tpc_info(vdev,
+						&reg_tpc_info[num_of_links]);
+		if (QDF_IS_STATUS_ERROR(status)) {
+			hdd_err("failed to get tpc info for vdev %d",
+				link_info->vdev_id);
+			goto next_link;
+		}
+
+		num_of_links++;
+next_link:
+		hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_POWER_ID);
+	}
+
+	if (!num_of_links) {
+		hdd_err("get tpc info failed - nun of links 0");
+		return -EINVAL;
+	}
+
+	skb_len = get_default_tpc_info_vendor_sbk_len();
+
+	reply_skb = wlan_cfg80211_vendor_cmd_alloc_reply_skb(
+						wiphy,
+						skb_len);
+	if (!reply_skb) {
+		hdd_err("alloc reply_skb failed");
+		status = QDF_STATUS_E_NOMEM;
+		goto free_skb;
+	}
+
+	tpc_links_attr = nla_nest_start(reply_skb,
+					QCA_WLAN_VENDOR_ATTR_TPC_LINKS);
+	if (!tpc_links_attr) {
+		hdd_err("failed to add QCA_WLAN_VENDOR_ATTR_TPC_LINKS");
+		status = QDF_STATUS_E_INVAL;
+		goto free_skb;
+	}
+
+	for (i = 0; i < num_of_links; i++) {
+		tpc_attr = nla_nest_start(reply_skb, i);
+
+		if (nla_put(reply_skb, QCA_WLAN_VENDOR_ATTR_TPC_BSSID,
+			    QDF_MAC_ADDR_SIZE, &link_bssid[i])) {
+			hdd_err("failed to put mac_addr");
+			status = QDF_STATUS_E_INVAL;
+			goto free_skb;
+		}
+
+		if (reg_tpc_info[i].is_psd_power &&
+		    nla_put_flag(reply_skb,
+				 QCA_WLAN_VENDOR_ATTR_TPC_PSD_POWER)) {
+			osif_err("failed to put psd flag");
+			return QDF_STATUS_E_INVAL;
+		}
+
+		if (nla_put_s8(reply_skb,
+			       QCA_WLAN_VENDOR_ATTR_TPC_EIRP_POWER,
+			       reg_tpc_info[i].reg_max[0])) {
+			hdd_err("failed to put eirp_power");
+			status = QDF_STATUS_E_INVAL;
+			goto free_skb;
+		}
+
+		chan_power_info = &reg_tpc_info[i].chan_power_info[0];
+		if (WLAN_REG_IS_6GHZ_CHAN_FREQ(chan_power_info->chan_cfreq) &&
+		    nla_put_u8(reply_skb,
+			       QCA_WLAN_VENDOR_ATTR_TPC_POWER_TYPE_6GHZ,
+			       reg_tpc_info[i].power_type_6g)) {
+			hdd_err("failed to put power_type_6g");
+			status = QDF_STATUS_E_INVAL;
+			goto free_skb;
+		}
+
+		if (nla_put_u8(reply_skb,
+			       QCA_WLAN_VENDOR_ATTR_TPC_AP_CONSTRAINT_POWER,
+			       reg_tpc_info[i].ap_constraint_power)) {
+			hdd_err("failed to put ap_constraint_power");
+			status = QDF_STATUS_E_INVAL;
+			goto free_skb;
+		}
+
+		hdd_debug("%d tpc for bssid "QDF_MAC_ADDR_FMT" is_psd %d reg power %d 6ghz pwr type %d ap_constraint_power %d",
+			  i, link_bssid[i].bytes, reg_tpc_info[i].is_psd_power,
+			  reg_tpc_info[i].reg_max[0],
+			  reg_tpc_info[i].power_type_6g,
+			  reg_tpc_info[i].ap_constraint_power);
+
+		levels_attr = nla_nest_start(
+			reply_skb, QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL);
+		if (!levels_attr) {
+			hdd_err("failed to add QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL");
+			status = QDF_STATUS_E_INVAL;
+			goto free_skb;
+		}
+		for (j = 0; j < reg_tpc_info[i].num_pwr_levels; j++) {
+			tpc_level = nla_nest_start(reply_skb, j);
+			chan_power_info = &reg_tpc_info[i].chan_power_info[j];
+
+			attr_id = QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL_FREQUENCY;
+			if (nla_put_u32(reply_skb,
+					attr_id,
+					chan_power_info->chan_cfreq)) {
+				hdd_err("failed to put chan_cfreq %d",
+					chan_power_info->chan_cfreq);
+				status = QDF_STATUS_E_INVAL;
+				goto free_skb;
+			}
+
+			attr_id = QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL_TX_POWER;
+			if (nla_put_s8(reply_skb,
+				       attr_id,
+				       chan_power_info->tx_power)) {
+				hdd_err("failed to put tx_power %d",
+					chan_power_info->tx_power);
+				status = QDF_STATUS_E_INVAL;
+				goto free_skb;
+			}
+
+			hdd_debug("%d cfreq %d tx_power %d", j,
+				  chan_power_info->chan_cfreq,
+				  chan_power_info->tx_power);
+
+			nla_nest_end(reply_skb, tpc_level);
+		}
+		nla_nest_end(reply_skb, levels_attr);
+
+		nla_nest_end(reply_skb, tpc_attr);
+	}
+
+	nla_nest_end(reply_skb, tpc_links_attr);
+
+	ret = wlan_cfg80211_vendor_cmd_reply(reply_skb);
+
+	hdd_exit();
+
+	return ret;
+free_skb:
+	if (reply_skb)
+		wlan_cfg80211_vendor_free_skb(reply_skb);
+	hdd_exit();
+	return qdf_status_to_os_return(status);
+}
+
+int wlan_hdd_cfg80211_get_reg_tpc_info(struct wiphy *wiphy,
+				       struct wireless_dev *wdev,
+				       const void *data,
+				       int data_len)
+{
+	struct osif_vdev_sync *vdev_sync;
+	int errno;
+
+	errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
+	if (errno)
+		return errno;
+
+	errno = __wlan_hdd_cfg80211_get_reg_tpc_info(wiphy, wdev,
+						     data, data_len);
+
+	osif_vdev_sync_op_stop(vdev_sync);
+
+	return errno;
+}

+ 29 - 0
core/hdd/src/wlan_hdd_tx_power.h

@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2012-2018,2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
@@ -61,6 +62,21 @@ int wlan_hdd_cfg80211_txpower_scale_decr_db(struct wiphy *wiphy,
 					    const void *data,
 					    int data_len);
 
+/**
+ * wlan_hdd_cfg80211_get_reg_tpc_info () - get regulatory tpc information of
+ * connected AP
+ * @wiphy: Pointer to wireless phy
+ * @wdev: Pointer to wireless device
+ * @data: Pointer to data
+ * @data_len: Data length
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+int wlan_hdd_cfg80211_get_reg_tpc_info(struct wiphy *wiphy,
+				       struct wireless_dev *wdev,
+				       const void *data,
+				       int data_len);
+
 #define FEATURE_TX_POWER_VENDOR_COMMANDS				\
 {									\
 	.info.vendor_id = QCA_NL80211_VENDOR_ID,			\
@@ -83,8 +99,21 @@ int wlan_hdd_cfg80211_txpower_scale_decr_db(struct wiphy *wiphy,
 	vendor_command_policy(txpower_scale_decr_db_policy,             \
 			      QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB_MAX) \
 },
+
+#define FEATURE_REGULATORY_TPC_INFO_VENDOR_COMMANDS                     \
+{                                                                       \
+	.info.vendor_id = QCA_NL80211_VENDOR_ID,                        \
+	.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_REGULATORY_TPC_INFO,   \
+	.flags = WIPHY_VENDOR_CMD_NEED_WDEV |                           \
+		 WIPHY_VENDOR_CMD_NEED_NETDEV |                         \
+		 WIPHY_VENDOR_CMD_NEED_RUNNING,                         \
+	.doit = wlan_hdd_cfg80211_get_reg_tpc_info,                     \
+	vendor_command_policy(VENDOR_CMD_RAW_DATA, 0)                   \
+},
+
 #else /* FEATURE_TX_POWER */
 #define FEATURE_TX_POWER_VENDOR_COMMANDS
+#define FEATURE_REGULATORY_TPC_INFO_VENDOR_COMMANDS
 #endif /* FEATURE_TX_POWER */
 
 #endif /* __WLAN_HDD_TX_POWER_H */