1 /*
2  * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for
6  * any purpose with or without fee is hereby granted, provided that the
7  * above copyright notice and this permission notice appear in all
8  * copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /**
21  * DOC: wlan_hdd_active_tos.c
22  *
23  * WLAN active tos functions
24  *
25  */
26 
27 #include "osif_sync.h"
28 #include <wlan_hdd_includes.h>
29 #include <linux/netdevice.h>
30 #include <linux/skbuff.h>
31 #include <linux/etherdevice.h>
32 #include <linux/if_ether.h>
33 #include <wlan_hdd_active_tos.h>
34 #include "wlan_policy_mgr_ucfg.h"
35 #include "wlan_scan_ucfg_api.h"
36 
37 #define HDD_AC_BK_BIT                   1
38 #define HDD_AC_BE_BIT                   2
39 #define HDD_AC_VI_BIT                   4
40 #define HDD_AC_VO_BIT                   8
41 
42 #define HDD_MAX_OFF_CHAN_TIME_FOR_VO    20
43 #define HDD_MAX_OFF_CHAN_TIME_FOR_VI    20
44 #define HDD_MAX_OFF_CHAN_TIME_FOR_BE    40
45 #define HDD_MAX_OFF_CHAN_TIME_FOR_BK    40
46 
47 #define HDD_MAX_OFF_CHAN_ENTRIES        2
48 
49 #define HDD_AC_BIT_INDX                 0
50 #define HDD_DWELL_TIME_INDX             1
51 
52 static int limit_off_chan_tbl[QCA_WLAN_AC_ALL][HDD_MAX_OFF_CHAN_ENTRIES] = {
53 		{ HDD_AC_BK_BIT, HDD_MAX_OFF_CHAN_TIME_FOR_BK },
54 		{ HDD_AC_BE_BIT, HDD_MAX_OFF_CHAN_TIME_FOR_BE },
55 		{ HDD_AC_VI_BIT, HDD_MAX_OFF_CHAN_TIME_FOR_VI },
56 		{ HDD_AC_VO_BIT, HDD_MAX_OFF_CHAN_TIME_FOR_VO },
57 };
58 
59 const struct nla_policy
60 wlan_hdd_set_limit_off_channel_param_policy
61 [QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_MAX + 1] = {
62 	[QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS] = {.type = NLA_U8 },
63 	[QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_START] = {.type = NLA_U8 },
64 };
65 
66 /**
67  * hdd_set_limit_off_chan_for_tos() - set limit off-channel command parameters
68  * @adapter: HDD adapter
69  * @tos: type of service
70  * @is_tos_active: status of the traffic
71  *
72  * Return: 0 on success and non zero value on failure
73  */
74 
75 static int
hdd_set_limit_off_chan_for_tos(struct hdd_adapter * adapter,enum qca_wlan_ac_type tos,bool is_tos_active)76 hdd_set_limit_off_chan_for_tos(struct hdd_adapter *adapter,
77 			       enum qca_wlan_ac_type tos,
78 			       bool is_tos_active)
79 {
80 	int ac_bit;
81 	struct hdd_context *hdd_ctx;
82 	uint32_t max_off_chan_time = 0;
83 	QDF_STATUS status;
84 	int ret;
85 	uint8_t def_sys_pref = 0;
86 	uint32_t rest_conc_time;
87 
88 	hdd_ctx = WLAN_HDD_GET_CTX(adapter);
89 	ret = wlan_hdd_validate_context(hdd_ctx);
90 
91 	if (ret < 0)
92 		return ret;
93 	ucfg_policy_mgr_get_sys_pref(hdd_ctx->psoc,
94 				     &def_sys_pref);
95 
96 	ac_bit = limit_off_chan_tbl[tos][HDD_AC_BIT_INDX];
97 
98 	if (is_tos_active)
99 		adapter->active_ac |= ac_bit;
100 	else
101 		adapter->active_ac &= ~ac_bit;
102 
103 	if (adapter->active_ac) {
104 		if (adapter->active_ac & HDD_AC_VO_BIT) {
105 			max_off_chan_time =
106 				limit_off_chan_tbl[QCA_WLAN_AC_VO][HDD_DWELL_TIME_INDX];
107 			policy_mgr_set_cur_conc_system_pref(hdd_ctx->psoc,
108 							    PM_LATENCY);
109 		} else if (adapter->active_ac & HDD_AC_VI_BIT) {
110 			max_off_chan_time =
111 				limit_off_chan_tbl[QCA_WLAN_AC_VI][HDD_DWELL_TIME_INDX];
112 			policy_mgr_set_cur_conc_system_pref(hdd_ctx->psoc,
113 							    PM_LATENCY);
114 		} else {
115 			/*ignore this command if only BE/BK is active */
116 			is_tos_active = false;
117 			policy_mgr_set_cur_conc_system_pref(hdd_ctx->psoc,
118 							    def_sys_pref);
119 		}
120 	} else {
121 		/* No active tos */
122 		policy_mgr_set_cur_conc_system_pref(hdd_ctx->psoc,
123 						    def_sys_pref);
124 	}
125 
126 	ucfg_scan_cfg_get_conc_max_resttime(hdd_ctx->psoc, &rest_conc_time);
127 	status = sme_send_limit_off_channel_params(hdd_ctx->mac_handle,
128 					adapter->deflink->vdev_id,
129 					is_tos_active,
130 					max_off_chan_time,
131 					rest_conc_time,
132 					true);
133 	if (!QDF_IS_STATUS_SUCCESS(status)) {
134 		hdd_err("failed to set limit off chan params");
135 		ret = -EINVAL;
136 	}
137 
138 	return ret;
139 }
140 
141 /**
142  * __wlan_hdd_cfg80211_set_limit_offchan_param() - set limit off-channel cmd
143  * parameters
144  * @wiphy: pointer to wireless wiphy structure.
145  * @wdev: pointer to wireless_dev structure.
146  * @data: pointer to limit off-channel command parameters.
147  * @data_len: the length in byte of  limit off-channel command parameters.
148  *
149  * This is called when application wants to limit the off channel time due to
150  * active voip traffic.
151  *
152  * Return: An error code or 0 on success.
153  */
154 static int
__wlan_hdd_cfg80211_set_limit_offchan_param(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)155 __wlan_hdd_cfg80211_set_limit_offchan_param(struct wiphy *wiphy,
156 					    struct wireless_dev *wdev,
157 					    const void *data,
158 					    int data_len)
159 {
160 	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_MAX + 1];
161 	struct net_device   *dev = wdev->netdev;
162 	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
163 	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
164 	int ret = 0;
165 	uint8_t tos;
166 	uint8_t tos_status;
167 
168 	hdd_enter();
169 
170 	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
171 		hdd_err("Command not allowed in FTM mode");
172 		return -EPERM;
173 	}
174 
175 	ret = wlan_hdd_validate_context(hdd_ctx);
176 	if (ret < 0)
177 		return ret;
178 
179 	if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_MAX,
180 				 data, data_len,
181 				 wlan_hdd_set_limit_off_channel_param_policy)) {
182 		hdd_err("Invalid ATTR");
183 		return -EINVAL;
184 	}
185 
186 	if (!tb[QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS]) {
187 		hdd_err("attr tos failed");
188 		goto fail;
189 	}
190 
191 	tos = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS]);
192 	if (tos >= QCA_WLAN_AC_ALL) {
193 		hdd_err("tos value %d exceeded Max value %d",
194 			tos, QCA_WLAN_AC_ALL);
195 		goto fail;
196 	}
197 	hdd_debug("tos %d", tos);
198 
199 	if (!tb[QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_START]) {
200 		hdd_err("attr tos active failed");
201 		goto fail;
202 	}
203 	tos_status = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_START]);
204 
205 	hdd_debug("tos status %d", tos_status);
206 	ret = hdd_set_limit_off_chan_for_tos(adapter, tos, tos_status);
207 
208 fail:
209 	return ret;
210 }
211 
wlan_hdd_cfg80211_set_limit_offchan_param(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)212 int wlan_hdd_cfg80211_set_limit_offchan_param(struct wiphy *wiphy,
213 					      struct wireless_dev *wdev,
214 					      const void *data, int data_len)
215 
216 {
217 	struct osif_vdev_sync *vdev_sync;
218 	int errno;
219 
220 	errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
221 	if (errno)
222 		return errno;
223 
224 	errno = __wlan_hdd_cfg80211_set_limit_offchan_param(wiphy, wdev, data,
225 							    data_len);
226 
227 	osif_vdev_sync_op_stop(vdev_sync);
228 
229 	return errno;
230 }
231 
232