Просмотр исходного кода

qcacld-3.0: Add support for TWT Get_traffic_stats

Add support for TWT Get_traffic_stats command in TWT component.

Change-Id: I927ce90e1dd6b5cc047d99fcd7b54a16a34ca4ed
CRs-Fixed: 3085887
Srinivas Girigowda 3 лет назад
Родитель
Сommit
7ea8a92863

+ 0 - 11
components/umac/twt/core/src/wlan_twt_main.c

@@ -1046,17 +1046,6 @@ err:
 	return QDF_STATUS_E_FAILURE;
 }
 
-/**
- * wlan_twt_is_command_in_progress() - Check if given command is in progress
- * @psoc: Pointer to psoc object
- * @peer_mac: Pointer to peer mac address
- * @dialog_id: Dialog id
- * @cmd: TWT command
- * @active_cmd: Fill the active command in this output parameter
- *
- * Return: True if given command is in progress.
- */
-static
 bool wlan_twt_is_command_in_progress(struct wlan_objmgr_psoc *psoc,
 				     struct qdf_mac_addr *peer_mac,
 				     uint8_t dialog_id,

+ 16 - 0
components/umac/twt/core/src/wlan_twt_main.h

@@ -261,3 +261,19 @@ bool wlan_twt_is_setup_done(struct wlan_objmgr_psoc *psoc,
 enum wlan_twt_session_state
 wlan_twt_get_session_state(struct wlan_objmgr_psoc *psoc,
 			   struct qdf_mac_addr *peer_mac, uint8_t dialog_id);
+
+/**
+ * wlan_twt_is_command_in_progress() - Check if given command is in progress
+ * @psoc: Pointer to psoc object
+ * @peer_mac: Pointer to peer mac address
+ * @dialog_id: Dialog id
+ * @cmd: TWT command
+ * @active_cmd: Fill the active command in this output parameter
+ *
+ * Return: True if given command is in progress.
+ */
+bool wlan_twt_is_command_in_progress(struct wlan_objmgr_psoc *psoc,
+				     struct qdf_mac_addr *peer_mac,
+				     uint8_t dialog_id,
+				     enum wlan_twt_commands cmd,
+				     enum wlan_twt_commands *pactive_cmd);

+ 17 - 0
components/umac/twt/dispatcher/inc/wlan_twt_ucfg_ext_api.h

@@ -252,6 +252,13 @@ ucfg_twt_is_setup_done(struct wlan_objmgr_psoc *psoc,
 enum wlan_twt_session_state
 ucfg_twt_get_session_state(struct wlan_objmgr_psoc *psoc,
 			   struct qdf_mac_addr *peer_mac, uint8_t dialog_id);
+
+bool ucfg_twt_is_command_in_progress(struct wlan_objmgr_psoc *psoc,
+				     struct qdf_mac_addr *peer_mac,
+				     uint8_t dialog_id,
+				     enum wlan_twt_commands cmd,
+				     enum wlan_twt_commands *pactive_cmd);
+
 #else
 static inline
 QDF_STATUS ucfg_twt_psoc_open(struct wlan_objmgr_psoc *psoc)
@@ -331,5 +338,15 @@ ucfg_twt_get_session_state(struct wlan_objmgr_psoc *psoc,
 {
 	return WLAN_TWT_SETUP_STATE_NOT_ESTABLISHED;
 }
+
+static inline bool
+ucfg_twt_is_command_in_progress(struct wlan_objmgr_psoc *psoc,
+				struct qdf_mac_addr *peer_mac,
+				uint8_t dialog_id,
+				enum wlan_twt_commands cmd,
+				enum wlan_twt_commands *pactive_cmd)
+{
+	return false;
+}
 #endif
 #endif

+ 10 - 0
components/umac/twt/dispatcher/src/wlan_twt_ucfg_ext_api.c

@@ -183,3 +183,13 @@ ucfg_twt_get_session_state(struct wlan_objmgr_psoc *psoc,
 {
 	return wlan_twt_get_session_state(psoc, peer_mac, dialog_id);
 }
+
+bool ucfg_twt_is_command_in_progress(struct wlan_objmgr_psoc *psoc,
+				     struct qdf_mac_addr *peer_mac,
+				     uint8_t dialog_id,
+				     enum wlan_twt_commands cmd,
+				     enum wlan_twt_commands *pactive_cmd)
+{
+	return wlan_twt_is_command_in_progress(psoc, peer_mac, dialog_id, cmd,
+					       pactive_cmd);
+}

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

@@ -249,6 +249,7 @@ static int hdd_twt_configure(struct hdd_adapter *adapter,
 		ret = osif_twt_get_capabilities(vdev);
 		break;
 	case QCA_WLAN_TWT_GET_STATS:
+		ret = osif_twt_get_session_traffic_stats(vdev, twt_param_attr);
 		break;
 	case QCA_WLAN_TWT_CLEAR_STATS:
 		break;

+ 30 - 0
os_if/twt/inc/osif_twt_ext_req.h

@@ -185,6 +185,29 @@ int osif_fill_peer_macaddr(struct wlan_objmgr_vdev *vdev, uint8_t *mac_addr);
  */
 int osif_twt_get_session_req(struct wlan_objmgr_vdev *vdev,
 			     struct nlattr *twt_param_attr);
+
+/**
+ * osif_twt_get_session_traffic_stats() - Extract traffic stats NL attributes
+ * @vdev: vdev pointer
+ * @twt_param_attr: TWT NL attributes coming from the user space
+ *
+ * Return: errno
+ */
+int osif_twt_get_session_traffic_stats(struct wlan_objmgr_vdev *vdev,
+				       struct nlattr *twt_param_attr);
+
+/**
+ * osif_twt_get_stats_response() - Post get stats response to user space
+ * @vdev: vdev pointer
+ * @params: cp stats event params
+ * @num_session_stats: number of session stats
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS osif_twt_get_stats_response(struct wlan_objmgr_vdev *vdev,
+				       struct twt_infra_cp_stats_event *params,
+				       uint32_t num_session_stats);
+
 #else
 static inline
 int osif_twt_setup_req(struct wlan_objmgr_vdev *vdev,
@@ -241,6 +264,13 @@ int osif_twt_get_session_req(struct wlan_objmgr_vdev *vdev,
 	return 0;
 }
 
+static inline
+int osif_twt_get_session_traffic_stats(struct wlan_objmgr_vdev *vdev,
+				       struct nlattr *twt_param_attr)
+{
+	return 0;
+}
+
 #endif
 #endif /* _OSIF_TWT_EXT_REQ_H_ */
 

+ 111 - 0
os_if/twt/src/osif_twt_ext_req.c

@@ -86,6 +86,11 @@ qca_wlan_vendor_twt_nudge_dialog_policy[QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_MAX + 1]
 	[QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_MAC_ADDR] = VENDOR_NLA_POLICY_MAC_ADDR,
 };
 
+static const struct nla_policy
+qca_wlan_vendor_twt_stats_dialog_policy[QCA_WLAN_VENDOR_ATTR_TWT_STATS_MAX + 1] = {
+	[QCA_WLAN_VENDOR_ATTR_TWT_STATS_FLOW_ID] = {.type = NLA_U8 },
+};
+
 static int osif_is_twt_command_allowed(struct wlan_objmgr_vdev *vdev,
 				       uint8_t vdev_id,
 				       struct wlan_objmgr_psoc *psoc)
@@ -1955,3 +1960,109 @@ int osif_twt_get_session_req(struct wlan_objmgr_vdev *vdev,
 
 	return -EOPNOTSUPP;
 }
+
+/**
+ * osif_twt_request_session_traffic_stats() - Obtains twt session traffic
+ * statistics and sends response to the user space
+ * @vdev: vdev
+ * @dialog_id: dialog id of the twt session
+ * @peer_mac: Mac address of the peer
+ *
+ * Return: QDF_STATUS_SUCCESS on success, else other qdf error values
+ */
+static QDF_STATUS
+osif_twt_request_session_traffic_stats(struct wlan_objmgr_vdev *vdev,
+				       uint32_t dialog_id, uint8_t *peer_mac)
+{
+	int errno;
+	QDF_STATUS status = QDF_STATUS_E_INVAL;
+	struct infra_cp_stats_event *event;
+
+	if (!peer_mac)
+		return status;
+	event = wlan_cfg80211_mc_twt_get_infra_cp_stats(vdev, dialog_id,
+							peer_mac, &errno);
+
+	if (!event)
+		return errno;
+
+	status = osif_twt_get_stats_response(vdev, event->twt_infra_cp_stats,
+					     event->num_twt_infra_cp_stats);
+	if (QDF_IS_STATUS_ERROR(status))
+		osif_err("TWT: Get_traffic_stats failed status: %d", status);
+
+	qdf_mem_free(event->twt_infra_cp_stats);
+	qdf_mem_free(event);
+
+	return status;
+}
+
+int osif_twt_get_session_traffic_stats(struct wlan_objmgr_vdev *vdev,
+				       struct nlattr *twt_param_attr)
+{
+	struct wlan_objmgr_psoc *psoc;
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TWT_STATS_MAX + 1];
+	int ret, id;
+	QDF_STATUS qdf_status;
+	uint32_t dialog_id;
+	bool is_stats_tgt_cap_enabled;
+	struct qdf_mac_addr peer_mac;
+
+	psoc = wlan_vdev_get_psoc(vdev);
+	if (!psoc)
+		return -EINVAL;
+
+	ret = wlan_cfg80211_nla_parse_nested(tb,
+					     QCA_WLAN_VENDOR_ATTR_TWT_STATS_MAX,
+					     twt_param_attr,
+					     qca_wlan_vendor_twt_stats_dialog_policy);
+
+	if (ret)
+		return ret;
+
+	ucfg_twt_get_twt_stats_enabled(psoc, &is_stats_tgt_cap_enabled);
+	if (!is_stats_tgt_cap_enabled) {
+		osif_debug("TWT Stats not supported by target");
+		return -EOPNOTSUPP;
+	}
+
+	if (osif_fill_peer_macaddr(vdev, peer_mac.bytes))
+		return -EINVAL;
+
+	if (ucfg_twt_is_command_in_progress(psoc, &peer_mac,
+					    TWT_ALL_SESSIONS_DIALOG_ID,
+					    WLAN_TWT_STATISTICS,
+					    NULL) ||
+	    ucfg_twt_is_command_in_progress(psoc, &peer_mac,
+					    TWT_ALL_SESSIONS_DIALOG_ID,
+					    WLAN_TWT_CLEAR_STATISTICS,
+					    NULL)) {
+		osif_warn("Already TWT statistics or clear statistics exists");
+		return -EALREADY;
+	}
+
+	id = QCA_WLAN_VENDOR_ATTR_TWT_STATS_FLOW_ID;
+	if (tb[id])
+		dialog_id = (uint32_t)nla_get_u8(tb[id]);
+	else
+		dialog_id = 0;
+
+	osif_debug("get_stats dialog_id %d", dialog_id);
+	osif_debug("get_stats peer mac_addr " QDF_MAC_ADDR_FMT,
+		   QDF_MAC_ADDR_REF(peer_mac.bytes));
+
+	if (!ucfg_twt_is_setup_done(psoc, &peer_mac, dialog_id)) {
+		osif_debug("TWT session %d setup incomplete", dialog_id);
+		return -EAGAIN;
+	}
+
+	ucfg_twt_set_command_in_progress(psoc, &peer_mac, dialog_id,
+					 WLAN_TWT_STATISTICS);
+
+	qdf_status = osif_twt_request_session_traffic_stats(vdev, dialog_id,
+							    peer_mac.bytes);
+	ucfg_twt_set_command_in_progress(psoc, &peer_mac,
+					 dialog_id, WLAN_TWT_NONE);
+
+	return qdf_status_to_os_return(qdf_status);
+}

+ 230 - 0
os_if/twt/src/osif_twt_ext_rsp.c

@@ -31,6 +31,7 @@
 #include <wlan_reg_ucfg_api.h>
 #include <wlan_twt_ucfg_ext_api.h>
 #include <wlan_twt_ucfg_ext_cfg.h>
+#include <wlan_cp_stats_ucfg_api.h>
 
 /**
  * osif_twt_get_setup_event_len() - Calculates the length of twt
@@ -1275,3 +1276,232 @@ osif_twt_ack_complete_cb(struct wlan_objmgr_psoc *psoc,
 	return QDF_STATUS_SUCCESS;
 }
 
+static uint32_t
+osif_get_session_wake_duration(struct wlan_objmgr_vdev *vdev,
+			       uint32_t dialog_id,
+			       struct qdf_mac_addr *peer_macaddr)
+{
+	struct wlan_objmgr_psoc *psoc;
+	struct twt_session_stats_info params = {0};
+	int num_twt_session = 0;
+
+	psoc = wlan_vdev_get_psoc(vdev);
+	params.dialog_id = dialog_id;
+	qdf_copy_macaddr(&params.peer_mac, peer_macaddr);
+
+	osif_debug("Get_params peer mac_addr " QDF_MAC_ADDR_FMT,
+		   QDF_MAC_ADDR_REF(params.peer_mac.bytes));
+
+	num_twt_session = ucfg_cp_stats_twt_get_peer_session_params(psoc,
+								    &params);
+	if (num_twt_session)
+		return params.wake_dura_us;
+
+	return 0;
+}
+
+static int
+twt_get_stats_status_to_vendor_twt_status(enum HOST_TWT_GET_STATS_STATUS status)
+{
+	switch (status) {
+	case HOST_TWT_GET_STATS_STATUS_OK:
+		return QCA_WLAN_VENDOR_TWT_STATUS_OK;
+	case HOST_TWT_GET_STATS_STATUS_DIALOG_ID_NOT_EXIST:
+		return QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST;
+	case HOST_TWT_GET_STATS_STATUS_INVALID_PARAM:
+		return QCA_WLAN_VENDOR_TWT_STATUS_INVALID_PARAM;
+	default:
+		return QCA_WLAN_VENDOR_TWT_STATUS_UNKNOWN_ERROR;
+	}
+}
+
+/**
+ * osif_twt_pack_get_stats_resp_nlmsg() - Packs and sends twt get stats response
+ * @vdev: vdev
+ * @reply_skb: pointer to response skb buffer
+ * @params: Pointer to twt session parameter buffer
+ * @num_session_stats: number of twt statistics
+ *
+ * Return: QDF_STATUS_SUCCESS on success, else other qdf error values
+ */
+static QDF_STATUS
+osif_twt_pack_get_stats_resp_nlmsg(struct wlan_objmgr_vdev *vdev,
+				   struct sk_buff *reply_skb,
+				   struct twt_infra_cp_stats_event *params,
+				   uint32_t num_session_stats)
+{
+	struct nlattr *config_attr, *nla_params;
+	int i, attr;
+	int vendor_status;
+	uint32_t duration;
+
+	config_attr = nla_nest_start(reply_skb,
+				     QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS);
+
+	if (!config_attr) {
+		osif_err("get_params nla_nest_start error");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	for (i = 0; i < num_session_stats; i++) {
+		nla_params = nla_nest_start(reply_skb, i);
+		if (!nla_params) {
+			osif_err("get_stats nla_nest_start error");
+			return QDF_STATUS_E_INVAL;
+		}
+
+		attr = QCA_WLAN_VENDOR_ATTR_TWT_STATS_MAC_ADDR;
+		if (nla_put(reply_skb, attr, QDF_MAC_ADDR_SIZE,
+			    params[i].peer_macaddr.bytes)) {
+			osif_err("get_stats failed to put mac_addr");
+			return QDF_STATUS_E_INVAL;
+		}
+
+		osif_debug("get_stats peer mac_addr " QDF_MAC_ADDR_FMT,
+			   QDF_MAC_ADDR_REF(params[i].peer_macaddr.bytes));
+
+		attr = QCA_WLAN_VENDOR_ATTR_TWT_STATS_FLOW_ID;
+		if (nla_put_u8(reply_skb, attr, params[i].dialog_id)) {
+			osif_err("get_stats failed to put dialog_id");
+			return QDF_STATUS_E_INVAL;
+		}
+
+		duration = osif_get_session_wake_duration(vdev,
+						params[i].dialog_id,
+						&params[i].peer_macaddr);
+		attr = QCA_WLAN_VENDOR_ATTR_TWT_STATS_SESSION_WAKE_DURATION;
+		if (nla_put_u32(reply_skb, attr, duration)) {
+			osif_err("get_params failed to put Wake duration");
+			return QDF_STATUS_E_INVAL;
+		}
+
+		osif_debug("dialog_id %d wake duration %d num sp cycles %d",
+			   params[i].dialog_id, duration,
+			   params[i].num_sp_cycles);
+
+		attr = QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS;
+		if (nla_put_u32(reply_skb, attr, params[i].num_sp_cycles)) {
+			osif_err("get_params failed to put num_sp_cycles");
+			return QDF_STATUS_E_INVAL;
+		}
+
+		attr = QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVG_WAKE_DURATION;
+		if (nla_put_u32(reply_skb, attr, params[i].avg_sp_dur_us)) {
+			osif_err("get_params failed to put avg_sp_dur_us");
+			return QDF_STATUS_E_INVAL;
+		}
+
+		attr = QCA_WLAN_VENDOR_ATTR_TWT_STATS_MIN_WAKE_DURATION;
+		if (nla_put_u32(reply_skb, attr, params[i].min_sp_dur_us)) {
+			osif_err("get_params failed to put min_sp_dur_us");
+			return QDF_STATUS_E_INVAL;
+		}
+
+		attr = QCA_WLAN_VENDOR_ATTR_TWT_STATS_MAX_WAKE_DURATION;
+		if (nla_put_u32(reply_skb, attr, params[i].max_sp_dur_us)) {
+			osif_err("get_params failed to put max_sp_dur_us");
+			return QDF_STATUS_E_INVAL;
+		}
+
+		attr = QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_TX_MPDU;
+		if (nla_put_u32(reply_skb, attr, params[i].tx_mpdu_per_sp)) {
+			osif_err("get_params failed to put tx_mpdu_per_sp");
+			return QDF_STATUS_E_INVAL;
+		}
+
+		attr = QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_RX_MPDU;
+		if (nla_put_u32(reply_skb, attr, params[i].rx_mpdu_per_sp)) {
+			osif_err("get_params failed to put rx_mpdu_per_sp");
+			return QDF_STATUS_E_INVAL;
+		}
+
+		attr = QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_TX_PACKET_SIZE;
+		if (nla_put_u32(reply_skb, attr, params[i].tx_bytes_per_sp)) {
+			osif_err("get_params failed to put tx_bytes_per_sp");
+			return QDF_STATUS_E_INVAL;
+		}
+
+		attr = QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_RX_PACKET_SIZE;
+		if (nla_put_u32(reply_skb, attr, params[i].rx_bytes_per_sp)) {
+			osif_err("get_params failed to put rx_bytes_per_sp");
+			return QDF_STATUS_E_INVAL;
+		}
+
+		attr = QCA_WLAN_VENDOR_ATTR_TWT_STATS_STATUS;
+		vendor_status =
+		    twt_get_stats_status_to_vendor_twt_status(params[i].status);
+		if (nla_put_u32(reply_skb, attr, vendor_status)) {
+			osif_err("get_params failed to put status");
+			return QDF_STATUS_E_INVAL;
+		}
+
+		nla_nest_end(reply_skb, nla_params);
+	}
+
+	nla_nest_end(reply_skb, config_attr);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * osif_get_twt_get_stats_event_len() - calculate length of skb
+ * required for sending twt get statistics command responses.
+ *
+ * Return: length of skb
+ */
+static uint32_t osif_get_twt_get_stats_event_len(void)
+{
+	uint32_t len = 0;
+
+	len += NLMSG_HDRLEN;
+
+	/* QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID */
+	len += nla_total_size(sizeof(u8));
+
+	/* QCA_WLAN_VENDOR_ATTR_TWT_SETUP_STATUS */
+	len += nla_total_size(sizeof(u8));
+
+	return len;
+}
+
+QDF_STATUS osif_twt_get_stats_response(struct wlan_objmgr_vdev *vdev,
+				       struct twt_infra_cp_stats_event *params,
+				       uint32_t num_session_stats)
+{
+	int skb_len;
+	struct vdev_osif_priv *osif_priv;
+	struct wireless_dev *wdev;
+	QDF_STATUS status = QDF_STATUS_E_INVAL;
+	struct sk_buff *reply_skb;
+
+	osif_priv = wlan_vdev_get_ospriv(vdev);
+	if (!osif_priv) {
+		osif_err("osif_priv is null");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	wdev = osif_priv->wdev;
+	if (!wdev) {
+		osif_err("wireless dev is null");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	skb_len = osif_get_twt_get_stats_event_len();
+	reply_skb = wlan_cfg80211_vendor_cmd_alloc_reply_skb(wdev->wiphy,
+							     skb_len);
+	if (!reply_skb) {
+		osif_err("Get stats - alloc reply_skb failed");
+		return QDF_STATUS_E_NOMEM;
+	}
+
+	status = osif_twt_pack_get_stats_resp_nlmsg(vdev, reply_skb, params,
+						    num_session_stats);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		osif_err("Get stats - Failed to pack nl response");
+		wlan_cfg80211_vendor_free_skb(reply_skb);
+		return qdf_status_to_os_return(status);
+	}
+
+	return wlan_cfg80211_vendor_cmd_reply(reply_skb);
+}
+