Эх сурвалжийг харах

qcacld-3.0: QCA vendor command for add / delete TSPEC

Add QCA vendor subcommands QCA_NL80211_VENDOR_SUBCMD_CONFIG_TSPEC
to add / delete TSPEC in STA mode.
The attributes defined in enum qca_wlan_vendor_attr_config_tspec
are used to encapsulate required information.

Change-Id: I9553fba1189d562f6f0205e86b281172213344ba
CRs-Fixed: 2686250
Min Liu 4 жил өмнө
parent
commit
50ac9a47d4

+ 30 - 0
core/hdd/inc/wlan_hdd_wmm.h

@@ -47,6 +47,7 @@
 #include <wlan_hdd_main.h>
 #include <wlan_hdd_wext.h>
 #include <sme_qos_api.h>
+#include <qca_vendor.h>
 
 /*Maximum number of ACs */
 #define WLAN_MAX_AC                         4
@@ -401,4 +402,33 @@ hdd_wlan_wmm_status_e hdd_wmm_checkts(struct hdd_adapter *adapter,
 QDF_STATUS hdd_wmm_adapter_clear(struct hdd_adapter *adapter);
 
 void wlan_hdd_process_peer_unauthorised_pause(struct hdd_adapter *adapter);
+
+extern const struct nla_policy
+config_tspec_policy[QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX + 1];
+
+/**
+ * wlan_hdd_cfg80211_config_tspec() - config tpsec
+ * @wiphy: pointer to wireless wiphy structure.
+ * @wdev: pointer to wireless_dev structure.
+ * @data: pointer to config tspec command parameters.
+ * @data_len: the length in byte of config tspec command parameters.
+ *
+ * Return: An error code or 0 on success.
+ */
+int wlan_hdd_cfg80211_config_tspec(struct wiphy *wiphy,
+				   struct wireless_dev *wdev,
+				   const void *data, int data_len);
+
+#define FEATURE_WMM_COMMANDS						\
+{									\
+	.info.vendor_id = QCA_NL80211_VENDOR_ID,			\
+	.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_CONFIG_TSPEC,		\
+	.flags = WIPHY_VENDOR_CMD_NEED_WDEV |				\
+		WIPHY_VENDOR_CMD_NEED_NETDEV |				\
+		WIPHY_VENDOR_CMD_NEED_RUNNING,				\
+	.doit = wlan_hdd_cfg80211_config_tspec,				\
+	vendor_command_policy(config_tspec_policy,			\
+			      QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX)	\
+},
+
 #endif /* #ifndef _WLAN_HDD_WMM_H */

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

@@ -14600,6 +14600,7 @@ const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] = {
 	FEATURE_HW_CAPABILITY_COMMANDS
 	FEATURE_THERMAL_VENDOR_COMMANDS
 	FEATURE_BTC_CHAIN_MODE_COMMANDS
+	FEATURE_WMM_COMMANDS
 };
 
 struct hdd_context *hdd_cfg80211_wiphy_alloc(void)

+ 308 - 0
core/hdd/src/wlan_hdd_wmm.c

@@ -72,6 +72,65 @@ const uint8_t hdd_wmm_up_to_ac_map[] = {
 	SME_AC_VO
 };
 
+#define CONFIG_TSPEC_OPERATION \
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_OPERATION
+#define CONFIG_TSPEC_TSID \
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_TSID
+#define CONFIG_TSPEC_DIRECTION \
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_DIRECTION
+#define CONFIG_TSPEC_APSD \
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_APSD
+#define CONFIG_TSPEC_USER_PRIORITY \
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_USER_PRIORITY
+#define CONFIG_TSPEC_ACK_POLICY \
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_ACK_POLICY
+#define CONFIG_TSPEC_NOMINAL_MSDU_SIZE \
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_NOMINAL_MSDU_SIZE
+#define CONFIG_TSPEC_MAXIMUM_MSDU_SIZE \
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAXIMUM_MSDU_SIZE
+#define CONFIG_TSPEC_MIN_SERVICE_INTERVAL \
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MIN_SERVICE_INTERVAL
+#define CONFIG_TSPEC_MAX_SERVICE_INTERVAL \
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX_SERVICE_INTERVAL
+#define CONFIG_TSPEC_INACTIVITY_INTERVAL \
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_INACTIVITY_INTERVAL
+#define CONFIG_TSPEC_SUSPENSION_INTERVAL \
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SUSPENSION_INTERVAL
+#define CONFIG_TSPEC_MINIMUM_DATA_RATE \
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_DATA_RATE
+#define CONFIG_TSPEC_MEAN_DATA_RATE \
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MEAN_DATA_RATE
+#define CONFIG_TSPEC_PEAK_DATA_RATE \
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_PEAK_DATA_RATE
+#define CONFIG_TSPEC_BURST_SIZE \
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_BURST_SIZE
+#define CONFIG_TSPEC_MINIMUM_PHY_RATE \
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_PHY_RATE
+#define CONFIG_TSPEC_SURPLUS_BANDWIDTH_ALLOWANCE \
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SURPLUS_BANDWIDTH_ALLOWANCE
+
+const struct nla_policy
+config_tspec_policy[QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX + 1] = {
+	[CONFIG_TSPEC_OPERATION] = {.type = NLA_U8},
+	[CONFIG_TSPEC_TSID] = {.type = NLA_U8},
+	[CONFIG_TSPEC_DIRECTION] = {.type = NLA_U8},
+	[CONFIG_TSPEC_APSD] = {.type = NLA_FLAG},
+	[CONFIG_TSPEC_USER_PRIORITY] = {.type = NLA_U8},
+	[CONFIG_TSPEC_ACK_POLICY] = {.type = NLA_U8},
+	[CONFIG_TSPEC_NOMINAL_MSDU_SIZE] = {.type = NLA_U16},
+	[CONFIG_TSPEC_MAXIMUM_MSDU_SIZE] = {.type = NLA_U16},
+	[CONFIG_TSPEC_MIN_SERVICE_INTERVAL] = {.type = NLA_U32},
+	[CONFIG_TSPEC_MAX_SERVICE_INTERVAL] = {.type = NLA_U32},
+	[CONFIG_TSPEC_INACTIVITY_INTERVAL] = {.type = NLA_U32},
+	[CONFIG_TSPEC_SUSPENSION_INTERVAL] = {.type = NLA_U32},
+	[CONFIG_TSPEC_MINIMUM_DATA_RATE] = {.type = NLA_U32},
+	[CONFIG_TSPEC_MEAN_DATA_RATE] = {.type = NLA_U32},
+	[CONFIG_TSPEC_PEAK_DATA_RATE] = {.type = NLA_U32},
+	[CONFIG_TSPEC_BURST_SIZE] = {.type = NLA_U32},
+	[CONFIG_TSPEC_MINIMUM_PHY_RATE] = {.type = NLA_U32},
+	[CONFIG_TSPEC_SURPLUS_BANDWIDTH_ALLOWANCE] = {.type = NLA_U16},
+};
+
 /**
  * enum hdd_wmm_linuxac: AC/Queue Index values for Linux Qdisc to
  * operate on different traffic.
@@ -2699,3 +2758,252 @@ hdd_wlan_wmm_status_e hdd_wmm_checkts(struct hdd_adapter *adapter, uint32_t hand
 	mutex_unlock(&adapter->hdd_wmm_status.mutex);
 	return status;
 }
+
+/**
+ * __wlan_hdd_cfg80211_config_tspec() - config tspec
+ * @wiphy: pointer to wireless wiphy structure.
+ * @wdev: pointer to wireless_dev structure.
+ * @data: pointer to config tspec command parameters.
+ * @data_len: the length in byte of config tspec command parameters.
+ *
+ * Return: An error code or 0 on success.
+ */
+static int __wlan_hdd_cfg80211_config_tspec(struct wiphy *wiphy,
+					    struct wireless_dev *wdev,
+					    const void *data,
+					    int data_len)
+{
+	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(wdev->netdev);
+	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
+	struct sme_qos_wmmtspecinfo tspec;
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX + 1];
+	uint8_t oper, ts_id;
+	hdd_wlan_wmm_status_e status;
+	int ret;
+
+	hdd_enter_dev(wdev->netdev);
+
+	ret = wlan_hdd_validate_context(hdd_ctx);
+	if (ret != 0)
+		return ret;
+
+	ret = wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX,
+				      data, data_len, config_tspec_policy);
+	if (ret) {
+		hdd_err_rl("Invalid ATTR");
+		return -EINVAL;
+	}
+
+	if (!tb[CONFIG_TSPEC_OPERATION] || !tb[CONFIG_TSPEC_TSID]) {
+		hdd_err_rl("Mandatory attributes are not present");
+		return -EINVAL;
+	}
+
+	memset(&tspec, 0, sizeof(tspec));
+
+	oper = nla_get_u8(tb[CONFIG_TSPEC_OPERATION]);
+	ts_id = nla_get_u8(tb[CONFIG_TSPEC_TSID]);
+
+	switch (oper) {
+	case QCA_WLAN_TSPEC_ADD:
+
+		tspec.ts_info.tid = ts_id;
+
+		/* Mandatory attributes */
+		if (tb[CONFIG_TSPEC_DIRECTION]) {
+			uint8_t direction = nla_get_u8(
+						    tb[CONFIG_TSPEC_DIRECTION]);
+
+			switch (direction) {
+			case QCA_WLAN_TSPEC_DIRECTION_UPLINK:
+				tspec.ts_info.direction =
+						SME_QOS_WMM_TS_DIR_UPLINK;
+				break;
+			case QCA_WLAN_TSPEC_DIRECTION_DOWNLINK:
+				tspec.ts_info.direction =
+						SME_QOS_WMM_TS_DIR_DOWNLINK;
+				break;
+			case QCA_WLAN_TSPEC_DIRECTION_BOTH:
+				tspec.ts_info.direction =
+						SME_QOS_WMM_TS_DIR_BOTH;
+				break;
+			default:
+				hdd_err_rl("Invalid direction %d", direction);
+				return -EINVAL;
+			}
+		} else {
+			hdd_err_rl("Direction is not present");
+			return -EINVAL;
+		}
+
+		if (tb[CONFIG_TSPEC_APSD])
+			tspec.ts_info.psb = 1;
+
+		if (tb[CONFIG_TSPEC_ACK_POLICY]) {
+			uint8_t ack_policy = nla_get_u8(
+						   tb[CONFIG_TSPEC_ACK_POLICY]);
+
+			switch (ack_policy) {
+			case QCA_WLAN_TSPEC_NORMAL_ACK:
+				tspec.ts_info.ack_policy =
+					SME_QOS_WMM_TS_ACK_POLICY_NORMAL_ACK;
+				break;
+			case QCA_WLAN_TSPEC_BLOCK_ACK:
+				tspec.ts_info.ack_policy =
+			       SME_QOS_WMM_TS_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK;
+				break;
+			default:
+				hdd_err_rl("Invalid ack policy %d", ack_policy);
+				return -EINVAL;
+			}
+		} else {
+			hdd_err_rl("ACK policy is not present");
+			return -EINVAL;
+		}
+
+		if (tb[CONFIG_TSPEC_NOMINAL_MSDU_SIZE]) {
+			tspec.nominal_msdu_size = nla_get_u16(
+					    tb[CONFIG_TSPEC_NOMINAL_MSDU_SIZE]);
+		} else {
+			hdd_err_rl("Nominal msdu size is not present");
+			return -EINVAL;
+		}
+
+		if (tb[CONFIG_TSPEC_MAXIMUM_MSDU_SIZE]) {
+			tspec.maximum_msdu_size = nla_get_u16(
+					    tb[CONFIG_TSPEC_MAXIMUM_MSDU_SIZE]);
+		} else {
+			hdd_err_rl("Maximum msdu size is not present");
+			return -EINVAL;
+		}
+
+		if (tb[CONFIG_TSPEC_MIN_SERVICE_INTERVAL]) {
+			tspec.min_service_interval = nla_get_u32(
+					 tb[CONFIG_TSPEC_MIN_SERVICE_INTERVAL]);
+		} else {
+			hdd_err_rl("Min service interval is not present");
+			return -EINVAL;
+		}
+
+		if (tb[CONFIG_TSPEC_MAX_SERVICE_INTERVAL]) {
+			tspec.max_service_interval = nla_get_u32(
+					 tb[CONFIG_TSPEC_MAX_SERVICE_INTERVAL]);
+		} else {
+			hdd_err_rl("Max service interval is not present");
+			return -EINVAL;
+		}
+
+		if (tb[CONFIG_TSPEC_INACTIVITY_INTERVAL]) {
+			tspec.inactivity_interval = nla_get_u32(
+					  tb[CONFIG_TSPEC_INACTIVITY_INTERVAL]);
+		} else {
+			hdd_err_rl("Inactivity interval is not present");
+			return -EINVAL;
+		}
+
+		if (tb[CONFIG_TSPEC_SUSPENSION_INTERVAL]) {
+			tspec.suspension_interval = nla_get_u32(
+					  tb[CONFIG_TSPEC_SUSPENSION_INTERVAL]);
+		} else {
+			hdd_err_rl("Suspension interval is not present");
+			return -EINVAL;
+		}
+
+		if (tb[CONFIG_TSPEC_SURPLUS_BANDWIDTH_ALLOWANCE]) {
+			tspec.surplus_bw_allowance = nla_get_u16(
+				  tb[CONFIG_TSPEC_SURPLUS_BANDWIDTH_ALLOWANCE]);
+		} else {
+			hdd_err_rl("Surplus bw allowance is not present");
+			return -EINVAL;
+		}
+
+		/* Optional attributes */
+		if (tb[CONFIG_TSPEC_USER_PRIORITY])
+			tspec.ts_info.up = nla_get_u8(
+						tb[CONFIG_TSPEC_USER_PRIORITY]);
+
+		if (tb[CONFIG_TSPEC_MINIMUM_DATA_RATE])
+			tspec.min_data_rate = nla_get_u32(
+					    tb[CONFIG_TSPEC_MINIMUM_DATA_RATE]);
+
+		if (tb[CONFIG_TSPEC_MEAN_DATA_RATE])
+			tspec.mean_data_rate = nla_get_u32(
+					       tb[CONFIG_TSPEC_MEAN_DATA_RATE]);
+
+		if (tb[CONFIG_TSPEC_PEAK_DATA_RATE])
+			tspec.peak_data_rate = nla_get_u32(
+					       tb[CONFIG_TSPEC_PEAK_DATA_RATE]);
+
+		if (tb[CONFIG_TSPEC_BURST_SIZE])
+			tspec.max_burst_size = nla_get_u32(
+						   tb[CONFIG_TSPEC_BURST_SIZE]);
+
+		if (tspec.max_burst_size)
+			tspec.ts_info.burst_size_defn = 1;
+
+		if (tb[CONFIG_TSPEC_MINIMUM_PHY_RATE])
+			tspec.min_phy_rate = nla_get_u32(
+					     tb[CONFIG_TSPEC_MINIMUM_PHY_RATE]);
+
+		status = hdd_wmm_addts(adapter, ts_id, &tspec);
+		if (status == HDD_WLAN_WMM_STATUS_SETUP_FAILED ||
+		    status == HDD_WLAN_WMM_STATUS_SETUP_FAILED_BAD_PARAM ||
+		    status == HDD_WLAN_WMM_STATUS_SETUP_FAILED_NO_WMM ||
+		    status == HDD_WLAN_WMM_STATUS_MODIFY_FAILED ||
+		    status == HDD_WLAN_WMM_STATUS_MODIFY_FAILED_BAD_PARAM ||
+		    status == HDD_WLAN_WMM_STATUS_SETUP_UAPSD_SET_FAILED ||
+		    status == HDD_WLAN_WMM_STATUS_MODIFY_UAPSD_SET_FAILED ||
+		    status == HDD_WLAN_WMM_STATUS_INTERNAL_FAILURE) {
+			hdd_err_rl("hdd_wmm_addts failed %d", status);
+			return -EINVAL;
+		}
+		break;
+
+	case QCA_WLAN_TSPEC_DEL:
+
+		status = hdd_wmm_delts(adapter, ts_id);
+		if (status == HDD_WLAN_WMM_STATUS_RELEASE_FAILED ||
+		    status == HDD_WLAN_WMM_STATUS_RELEASE_FAILED_BAD_PARAM ||
+		    status == HDD_WLAN_WMM_STATUS_INTERNAL_FAILURE) {
+			hdd_err_rl("hdd_wmm_delts failed %d", status);
+			return -EINVAL;
+		}
+		break;
+
+	case QCA_WLAN_TSPEC_GET:
+
+		status = hdd_wmm_checkts(adapter, ts_id);
+		if (status == HDD_WLAN_WMM_STATUS_LOST ||
+		    status == HDD_WLAN_WMM_STATUS_INTERNAL_FAILURE) {
+			hdd_err_rl("hdd_wmm_checkts failed %d", status);
+			return -EINVAL;
+		}
+		break;
+
+	default:
+		hdd_err_rl("Invalid operation %d", oper);
+		return -EINVAL;
+	}
+
+	hdd_exit();
+
+	return 0;
+}
+
+int wlan_hdd_cfg80211_config_tspec(struct wiphy *wiphy,
+				   struct wireless_dev *wdev,
+				   const void *data, int data_len)
+{
+	int errno;
+	struct osif_vdev_sync *vdev_sync;
+
+	errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
+	if (errno)
+		return errno;
+
+	errno = __wlan_hdd_cfg80211_config_tspec(wiphy, wdev, data, data_len);
+
+	osif_vdev_sync_op_stop(vdev_sync);
+
+	return errno;
+}