1 /*
2  * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 /**
18  * DOC: wlan_hdd_txq_flush.c
19  *
20  * WLAN Host Device Driver Peer TX queue flush configuration APIs implementation
21  *
22  */
23 
24 #include "osif_sync.h"
25 #include <wlan_hdd_includes.h>
26 #include "wlan_hdd_main.h"
27 #include "wlan_hdd_peer_txq_flush.h"
28 #include <qca_vendor.h>
29 #include "wlan_hdd_object_manager.h"
30 
31 const struct nla_policy
32 peer_txq_flush_policy[QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_MAX + 1] = {
33 	[QCA_WLAN_VENDOR_ATTR_PEER_ADDR] = VENDOR_NLA_POLICY_MAC_ADDR,
34 	[QCA_WLAN_VENDOR_ATTR_AC] = { .type = NLA_U8 },
35 	[QCA_WLAN_VENDOR_ATTR_TID_MASK] = { .type = NLA_U32 },
36 	[QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_POLICY] = { .type = NLA_U32 },
37 };
38 
39 /**
40  * map_txq_policy() - Map NL flush policy attribute value to DP
41  * @policy: NL flush policy attribute value
42  *
43  * This function maps NL flush policy attribute value to DP
44  *
45  * Return: Valid DP policy value, else invalid
46  */
47 static enum cdp_peer_txq_flush_policy
map_txq_policy(enum qca_wlan_vendor_flush_pending_policy policy)48 map_txq_policy(enum qca_wlan_vendor_flush_pending_policy policy)
49 {
50 	switch (policy) {
51 	case QCA_WLAN_VENDOR_FLUSH_PENDING_POLICY_NONE:
52 		return CDP_PEER_TXQ_FLUSH_POLICY_NONE;
53 	case QCA_WLAN_VENDOR_FLUSH_PENDING_POLICY_IMMEDIATE:
54 		return CDP_PEER_TXQ_FLUSH_POLICY_IMMEDIATE;
55 	case QCA_WLAN_VENDOR_FLUSH_PENDING_POLICY_TWT_SP_END:
56 		return CDP_PEER_TXQ_FLUSH_POLICY_TWT_SP_END;
57 	default:
58 		return CDP_PEER_TXQ_FLUSH_POLICY_INVALID;
59 	}
60 }
61 
62 /**
63  * hdd_peer_txq_flush_config() - Propagate txq flush config to DP
64  * @adapter: Pointer to HDD adapter structure
65  * @tb: NL attributes
66  *
67  * This function maps NL to DP attributes and proagates the configuration
68  *
69  * Return: 0 on success, negative errno on failure
70  */
hdd_peer_txq_flush_config(struct hdd_adapter * adapter,struct nlattr * tb[])71 static int hdd_peer_txq_flush_config(struct hdd_adapter *adapter,
72 				     struct nlattr *tb[])
73 {
74 	void *dp_soc = cds_get_context(QDF_MODULE_ID_SOC);
75 	uint8_t addr[QDF_MAC_ADDR_SIZE];
76 	uint32_t ac, tid, cmd_id;
77 	enum qca_wlan_vendor_flush_pending_policy txq_policy;
78 	enum cdp_peer_txq_flush_policy cdp_policy;
79 
80 	if (!tb || !dp_soc) {
81 		hdd_err("Invalid attributes");
82 		return -EINVAL;
83 	}
84 
85 	nla_memcpy(addr, tb[QCA_WLAN_VENDOR_ATTR_PEER_ADDR], QDF_MAC_ADDR_SIZE);
86 
87 	if (tb[QCA_WLAN_VENDOR_ATTR_TID_MASK]) {
88 		tid = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_TID_MASK]);
89 
90 		cmd_id = QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_POLICY;
91 		if (!tb[cmd_id]) {
92 			hdd_err("Flush policy not provided");
93 			return -EINVAL;
94 		}
95 		txq_policy = nla_get_u32(tb[cmd_id]);
96 		cdp_policy = map_txq_policy(txq_policy);
97 		if (cdp_policy == CDP_PEER_TXQ_FLUSH_POLICY_INVALID) {
98 			hdd_err("Invalid dp flush policy %d", txq_policy);
99 			return -EINVAL;
100 		}
101 		ac = 0;
102 	} else if (tb[QCA_WLAN_VENDOR_ATTR_AC]) {
103 		ac = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_AC]);
104 		cdp_policy = CDP_PEER_TXQ_FLUSH_POLICY_INVALID;
105 		tid = 0;
106 	} else {
107 		hdd_err("No ac/tid mask");
108 		return -EINVAL;
109 	}
110 
111 	return cdp_set_peer_txq_flush_config(dp_soc, adapter->deflink->vdev_id,
112 					     addr, ac, tid, cdp_policy);
113 }
114 
115 /**
116  * __wlan_hdd_cfg80211_peer_txq_flush_config() - flush peer txq config
117  * @wiphy: Pointer to wireless phy
118  * @wdev: Pointer to wireless device
119  * @data: Pointer to data
120  * @data_len: Length of @data
121  *
122  * This function is used to flush peer pending packets using vendor commands
123  *
124  * Return: 0 on success, negative errno on failure
125  */
126 static int
__wlan_hdd_cfg80211_peer_txq_flush_config(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)127 __wlan_hdd_cfg80211_peer_txq_flush_config(struct wiphy *wiphy,
128 					  struct wireless_dev *wdev,
129 					  const void *data, int data_len)
130 {
131 	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
132 	struct net_device *dev = wdev->netdev;
133 	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
134 	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_MAX + 1];
135 	int ret;
136 
137 	hdd_enter();
138 
139 	ret = wlan_hdd_validate_context(hdd_ctx);
140 	if (ret)
141 		return ret;
142 
143 	if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) {
144 		hdd_err("Not allowed in FTM mode");
145 		return -EINVAL;
146 	}
147 
148 	if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_MAX,
149 				    data, data_len, peer_txq_flush_policy)) {
150 		hdd_err("Invalid attributes");
151 		return -EINVAL;
152 	}
153 
154 	if (!tb[QCA_WLAN_VENDOR_ATTR_PEER_ADDR]) {
155 		hdd_err("Peer mac not provided");
156 		return -EINVAL;
157 	}
158 
159 	if (!tb[QCA_WLAN_VENDOR_ATTR_AC] &&
160 	    !tb[QCA_WLAN_VENDOR_ATTR_TID_MASK]) {
161 		hdd_err("AC/TID mask not provided");
162 		return -EINVAL;
163 	}
164 
165 	ret = hdd_peer_txq_flush_config(adapter, tb);
166 
167 	hdd_exit();
168 
169 	return ret;
170 }
171 
wlan_hdd_cfg80211_peer_txq_flush_config(struct wiphy * wiphy,struct wireless_dev * wdev,const void * attr,int attr_len)172 int wlan_hdd_cfg80211_peer_txq_flush_config(struct wiphy *wiphy,
173 					    struct wireless_dev *wdev,
174 					    const void *attr,
175 					    int attr_len)
176 {
177 	int ret;
178 	struct osif_vdev_sync *vdev_sync;
179 
180 	ret = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
181 	if (ret)
182 		return ret;
183 
184 	ret = __wlan_hdd_cfg80211_peer_txq_flush_config(wiphy, wdev,
185 							attr, attr_len);
186 
187 	osif_vdev_sync_op_stop(vdev_sync);
188 
189 	return ret;
190 }
191