Browse Source

qcacld-3.0: HDD: Add support to retrieve SAR power limits

Recently change "qcacld-3.0: Add SAR power limit configuration"
(Change-Id: I137ce9bde2fa05e363d5af17e155dc739d862c5f) added the
ability to dynamically configure Specific Absorption Rate (SAR) power
limits. Now update the HDD layer to add the ability to retrieve the
current active power limits via a cfg80211 vendor command

Change-Id: I8d48ecfc84094f8704ef048ed4d21f63dba8c1e0
CRs-Fixed: 2161449
Jeff Johnson 7 năm trước cách đây
mục cha
commit
8c83f13e45
1 tập tin đã thay đổi với 302 bổ sung0 xóa
  1. 302 0
      core/hdd/src/wlan_hdd_cfg80211.c

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

@@ -11544,6 +11544,49 @@ static int wlan_hdd_cfg80211_sar_convert_modulation(u32 nl80211_value,
 	return ret;
 }
 
+static u32 hdd_sar_wmi_to_nl_enable(uint32_t wmi_value)
+{
+	switch (wmi_value) {
+	default:
+	case WMI_SAR_FEATURE_OFF:
+		return QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE;
+	case WMI_SAR_FEATURE_ON_SET_0:
+		return QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0;
+	case WMI_SAR_FEATURE_ON_SET_1:
+		return QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF1;
+	case WMI_SAR_FEATURE_ON_SET_2:
+		return QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF2;
+	case WMI_SAR_FEATURE_ON_SET_3:
+		return QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF3;
+	case WMI_SAR_FEATURE_ON_SET_4:
+		return QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4;
+	case WMI_SAR_FEATURE_ON_USER_DEFINED:
+		return QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER;
+	}
+}
+
+static u32 hdd_sar_wmi_to_nl_band(uint32_t wmi_value)
+{
+	switch (wmi_value) {
+	default:
+	case WMI_SAR_2G_ID:
+		return HDD_NL80211_BAND_2GHZ;
+	case WMI_SAR_5G_ID:
+		return HDD_NL80211_BAND_5GHZ;
+	}
+}
+
+static u32 hdd_sar_wmi_to_nl_modulation(uint32_t wmi_value)
+{
+	switch (wmi_value) {
+	default:
+	case WMI_SAR_MOD_CCK:
+		return QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_CCK;
+	case WMI_SAR_MOD_OFDM:
+		return QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_OFDM;
+	}
+}
+
 static const struct nla_policy
 sar_limits_policy[QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_MAX + 1] = {
 	[QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE] = {.type = NLA_U32},
@@ -11554,6 +11597,258 @@ sar_limits_policy[QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_MAX + 1] = {
 	[QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT] = {.type = NLA_U32},
 };
 
+#define WLAN_WAIT_TIME_SAR 5000
+
+/**
+ * hdd_sar_context - hdd sar context
+ * @event: sar limit event
+ */
+struct hdd_sar_context {
+	struct sar_limit_event event;
+};
+
+/**
+ * hdd_sar_cb () - sar response message handler
+ * @cookie: hdd request cookie
+ * @event: sar response event
+ *
+ * Return: none
+ */
+static void hdd_sar_cb(void *cookie,
+		       struct sar_limit_event *event)
+{
+	struct hdd_request *request;
+	struct hdd_sar_context *context;
+
+	ENTER();
+
+	if (!event) {
+		hdd_err("response is NULL");
+		return;
+	}
+
+	request = hdd_request_get(cookie);
+	if (!request) {
+		hdd_debug("Obsolete request");
+		return;
+	}
+
+	context = hdd_request_priv(request);
+	context->event = *event;
+	hdd_request_complete(request);
+	hdd_request_put(request);
+
+	EXIT();
+}
+
+static uint32_t hdd_sar_get_response_len(const struct sar_limit_event *event)
+{
+	uint32_t len;
+	uint32_t row_len;
+
+	len = NLMSG_HDRLEN;
+	/* QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE */
+	len += NLA_HDRLEN + sizeof(u32);
+	/* QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS */
+	len += NLA_HDRLEN + sizeof(u32);
+
+	/* nest */
+	row_len = NLA_HDRLEN;
+	/* QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND */
+	row_len += NLA_HDRLEN + sizeof(u32);
+	/* QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN */
+	row_len += NLA_HDRLEN + sizeof(u32);
+	/* QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION */
+	row_len += NLA_HDRLEN + sizeof(u32);
+	/* QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT */
+	row_len += NLA_HDRLEN + sizeof(u32);
+
+	/* QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC */
+	len += NLA_HDRLEN + (row_len * event->num_limit_rows);
+
+	return len;
+}
+
+static int hdd_sar_fill_response(struct sk_buff *skb,
+				 const struct sar_limit_event *event)
+{
+	int errno;
+	u32 value;
+	u32 attr;
+	struct nlattr *nla_spec_attr;
+	struct nlattr *nla_row_attr;
+	uint32_t row;
+	const struct sar_limit_event_row *event_row;
+
+	attr = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE;
+	value = hdd_sar_wmi_to_nl_enable(event->sar_enable);
+	errno = nla_put_u32(skb, attr, value);
+	if (errno)
+		return errno;
+
+	attr = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS;
+	value = event->num_limit_rows;
+	errno = nla_put_u32(skb, attr, value);
+	if (errno)
+		return errno;
+
+	attr = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC;
+	nla_spec_attr = nla_nest_start(skb, attr);
+	if (!nla_spec_attr)
+		return -EINVAL;
+
+	for (row = 0, event_row = event->sar_limit_row;
+	     row < event->num_limit_rows;
+	     row++, event_row++) {
+		nla_row_attr = nla_nest_start(skb, attr);
+		if (!nla_row_attr)
+			return -EINVAL;
+
+		attr = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND;
+		value = hdd_sar_wmi_to_nl_band(event_row->band_id);
+		errno = nla_put_u32(skb, attr, value);
+		if (errno)
+			return errno;
+
+		attr = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN;
+		value = event_row->chain_id;
+		errno = nla_put_u32(skb, attr, value);
+		if (errno)
+			return errno;
+
+		attr = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION;
+		value = hdd_sar_wmi_to_nl_modulation(event_row->mod_id);
+		errno = nla_put_u32(skb, attr, value);
+		if (errno)
+			return errno;
+
+		attr = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT;
+		value = event_row->limit_value;
+		errno = nla_put_u32(skb, attr, value);
+		if (errno)
+			return errno;
+
+		nla_nest_end(skb, nla_row_attr);
+	}
+	nla_nest_end(skb, nla_spec_attr);
+
+	return 0;
+}
+
+static int hdd_sar_send_response(struct wiphy *wiphy,
+				 const struct sar_limit_event *event)
+{
+	uint32_t len;
+	struct sk_buff *skb;
+	int errno;
+
+	len = hdd_sar_get_response_len(event);
+	skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, len);
+	if (!skb) {
+		hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
+		return -ENOMEM;
+	}
+
+	errno = hdd_sar_fill_response(skb, event);
+	if (errno) {
+		kfree_skb(skb);
+		return errno;
+	}
+
+	return cfg80211_vendor_cmd_reply(skb);
+}
+
+/**
+ * __wlan_hdd_get_sar_power_limits() - Get SAR power limits
+ * @wiphy: Pointer to wireless phy
+ * @wdev: Pointer to wireless device
+ * @data: Pointer to data
+ * @data_len: Length of @data
+ *
+ * This function is used to retrieve Specific Absorption Rate limit specs.
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+static int __wlan_hdd_get_sar_power_limits(struct wiphy *wiphy,
+					   struct wireless_dev *wdev,
+					   const void *data, int data_len)
+{
+	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
+	struct hdd_request *request;
+	struct hdd_sar_context *context;
+	void *cookie;
+	QDF_STATUS status;
+	int ret;
+	static const struct hdd_request_params params = {
+		.priv_size = sizeof(*context),
+		.timeout_ms = WLAN_WAIT_TIME_SAR,
+	};
+
+	ENTER();
+
+	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
+		hdd_err("Command not allowed in FTM mode");
+		return -EPERM;
+	}
+
+	if (wlan_hdd_validate_context(hdd_ctx))
+		return -EINVAL;
+
+	request = hdd_request_alloc(&params);
+	if (!request) {
+		hdd_err("Request allocation failure");
+		return -ENOMEM;
+	}
+
+	cookie = hdd_request_cookie(request);
+
+	status = sme_get_sar_power_limits(hdd_ctx->hHal, hdd_sar_cb, cookie);
+	if (!QDF_IS_STATUS_SUCCESS(status)) {
+		hdd_err("Unable to post sar message");
+		ret = -EINVAL;
+		goto cleanup;
+	}
+
+	ret = hdd_request_wait_for_response(request);
+	if (ret) {
+		hdd_err("Target response timed out");
+		goto cleanup;
+	}
+
+	context = hdd_request_priv(request);
+	ret = hdd_sar_send_response(wiphy, &context->event);
+
+cleanup:
+	hdd_request_put(request);
+
+	return ret;
+}
+
+/**
+ * wlan_hdd_cfg80211_get_sar_power_limits() - Get SAR power limits
+ * @wiphy: Pointer to wireless phy
+ * @wdev: Pointer to wireless device
+ * @data: Pointer to data
+ * @data_len: Length of @data
+ *
+ * Wrapper function of __wlan_hdd_cfg80211_get_sar_power_limits()
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+static int wlan_hdd_cfg80211_get_sar_power_limits(struct wiphy *wiphy,
+						  struct wireless_dev *wdev,
+						  const void *data,
+						  int data_len)
+{
+	int ret;
+
+	cds_ssr_protect(__func__);
+	ret = __wlan_hdd_get_sar_power_limits(wiphy, wdev, data, data_len);
+	cds_ssr_unprotect(__func__);
+
+	return ret;
+}
+
 /**
  * __wlan_hdd_set_sar_power_limits() - Set SAR power limits
  * @wiphy: Pointer to wireless phy
@@ -13494,6 +13789,13 @@ const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] = {
 		.doit = wlan_hdd_cfg80211_configure_tdls_mode
 	},
 #endif
+	{
+		.info.vendor_id = QCA_NL80211_VENDOR_ID,
+		.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS,
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+			 WIPHY_VENDOR_CMD_NEED_RUNNING,
+		.doit = wlan_hdd_cfg80211_get_sar_power_limits
+	},
 	{
 		.info.vendor_id = QCA_NL80211_VENDOR_ID,
 		.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS,