1 /*
2  * Copyright (c) 2021, The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /**
19  * DOC:  wlan_hdd_avoid_freq_ext.c
20  *
21  * WLAN Host Device Driver extended avoid frequency interface implementation.
22  */
23 
24 /* Include Files */
25 
26 #include <osif_psoc_sync.h>
27 #include "wlan_hdd_avoid_freq_ext.h"
28 #include "wlan_reg_ucfg_api.h"
29 #include "wlan_reg_services_api.h"
30 
31 #define AVOID_FREQ_EXT_MAX QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_MAX
32 
33 const struct nla_policy
34 avoid_freq_ext_policy [QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_MAX + 1] = {
35 	[QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_RANGE] = { .type = NLA_NESTED },
36 	[QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_START] = {.type = NLA_U32},
37 	[QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_END] = {.type = NLA_U32},
38 	[QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_POWER_CAP_DBM] = {.type =
39 								NLA_S32},
40 	[QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_IFACES_BITMASK] = {.type =
41 								NLA_U32},
42 };
43 
44 /**
45  * __wlan_hdd_cfg80211_avoid_freq_ext() - exclude channels from upper layer.
46  * @wiphy: wiphy structure pointer
47  * @wdev: Wireless device structure pointer
48  * @data: Pointer to the data received
49  * @data_len: Length of @data
50  *
51  * __wlan_hdd_cfg80211_avoid_freq_ext() extract the valid avoid frequency
52  * list from upper layer and prepared one extended avoid frequency list for
53  * regulatory component.
54  *
55  * Return: 0 on success; errno on failure
56  */
57 static int
__wlan_hdd_cfg80211_avoid_freq_ext(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)58 __wlan_hdd_cfg80211_avoid_freq_ext(struct wiphy *wiphy,
59 				   struct wireless_dev *wdev,
60 		const void *data, int data_len)
61 {
62 	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
63 	int ret = 0;
64 	struct ch_avoid_ind_type avoid_freq_list;
65 	enum QDF_GLOBAL_MODE curr_mode;
66 	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_MAX + 1];
67 	struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_MAX + 1];
68 	struct nlattr *freq_ext;
69 	int id, rem, i, sub_id;
70 	struct ch_avoid_freq_type *avoid_freq_range;
71 
72 	hdd_enter_dev(wdev->netdev);
73 
74 	if (!ucfg_mlme_get_coex_unsafe_chan_nb_user_prefer(hdd_ctx->psoc)) {
75 		hdd_debug_rl("Coex unsafe chan nb user prefer is not set");
76 		return -EOPNOTSUPP;
77 	}
78 
79 	curr_mode = hdd_get_conparam();
80 	if (curr_mode == QDF_GLOBAL_FTM_MODE ||
81 	    curr_mode == QDF_GLOBAL_MONITOR_MODE) {
82 		hdd_debug_rl("Command not allowed in FTM/MONITOR mode");
83 		return -EINVAL;
84 	}
85 
86 	ret = wlan_hdd_validate_context(hdd_ctx);
87 	if (ret)
88 		return ret;
89 
90 	if (hdd_is_connection_in_progress(NULL, NULL)) {
91 		hdd_debug_rl("Update chan list refused: conn in progress");
92 		ret = -EPERM;
93 		goto out;
94 	}
95 
96 	qdf_mem_zero(&avoid_freq_list, sizeof(struct ch_avoid_ind_type));
97 
98 	if (!data && data_len == 0) {
99 		hdd_debug_rl("Clear extended avoid frequency list");
100 		goto process_avoid_channel_ext;
101 	}
102 
103 	ret = wlan_cfg80211_nla_parse(tb,
104 				      QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_MAX,
105 				      data,
106 				      data_len,
107 				      avoid_freq_ext_policy);
108 	if (ret) {
109 		hdd_err_rl("Invalid avoid freq ext ATTR");
110 		ret = -EINVAL;
111 		goto out;
112 	}
113 
114 	id = QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_RANGE;
115 
116 	if (!tb[id]) {
117 		hdd_err_rl("Attr avoid frequency ext range failed");
118 		ret = -EINVAL;
119 		goto out;
120 	}
121 
122 	i = 0;
123 	avoid_freq_list.ch_avoid_range_cnt = CH_AVOID_MAX_RANGE;
124 
125 	/* restriction mask */
126 	sub_id = QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_IFACES_BITMASK;
127 
128 	if (tb[sub_id])
129 		avoid_freq_list.restriction_mask = nla_get_u32(tb[sub_id]);
130 
131 	if (avoid_freq_list.restriction_mask & BIT(NL80211_IFTYPE_AP))
132 		avoid_freq_list.restriction_mask = (1 << QDF_SAP_MODE);
133 
134 	nla_for_each_nested(freq_ext, tb[id], rem) {
135 		if (i == CH_AVOID_MAX_RANGE) {
136 			hdd_warn_rl("Ignoring excess range number");
137 			break;
138 		}
139 
140 		if (wlan_cfg80211_nla_parse(tb2, AVOID_FREQ_EXT_MAX,
141 					    nla_data(freq_ext),
142 					    nla_len(freq_ext),
143 					    avoid_freq_ext_policy)) {
144 			hdd_err_rl("nla_parse failed");
145 			ret = -EINVAL;
146 			goto out;
147 		}
148 
149 		avoid_freq_range = &avoid_freq_list.avoid_freq_range[i];
150 
151 		/* ext avoid freq start */
152 		sub_id = QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_START;
153 		if (!tb2[sub_id]) {
154 			ret = -EINVAL;
155 			goto out;
156 		}
157 		avoid_freq_range->start_freq = nla_get_u32(tb2[sub_id]);
158 
159 		/* ext avoid freq end */
160 		sub_id = QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_END;
161 		if (!tb2[sub_id]) {
162 			ret = -EINVAL;
163 			goto out;
164 		}
165 		avoid_freq_range->end_freq = nla_get_u32(tb2[sub_id]);
166 
167 		if (!avoid_freq_range->start_freq &&
168 		    !avoid_freq_range->end_freq && (i < 1)) {
169 			hdd_debug_rl("Clear unsafe channel list");
170 		} else if (!wlan_reg_is_same_band_freqs(
171 			   avoid_freq_range->start_freq,
172 			   avoid_freq_range->end_freq)) {
173 			hdd_debug_rl("start freq %d end freq %d not in same band",
174 				     avoid_freq_range->start_freq,
175 				     avoid_freq_range->end_freq);
176 			ret = -EINVAL;
177 			goto out;
178 		}
179 
180 		if (avoid_freq_range->end_freq <
181 		    avoid_freq_range->start_freq) {
182 			ret = -EINVAL;
183 			goto out;
184 		}
185 
186 		/* ext txpower */
187 		sub_id = QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_POWER_CAP_DBM;
188 
189 		if (tb2[sub_id]) {
190 			avoid_freq_range->txpower = nla_get_s32(tb2[sub_id]);
191 			avoid_freq_range->is_valid_txpower = true;
192 		}
193 
194 		hdd_debug_rl("ext avoid freq start: %u end: %u txpower %d mask %d",
195 			     avoid_freq_range->start_freq,
196 			     avoid_freq_range->end_freq,
197 			     avoid_freq_range->txpower,
198 			     avoid_freq_list.restriction_mask);
199 		i++;
200 	}
201 
202 	if (i < CH_AVOID_MAX_RANGE) {
203 		hdd_warn_rl("Number of freq range %u less than expected %u",
204 			    i, CH_AVOID_MAX_RANGE);
205 		avoid_freq_list.ch_avoid_range_cnt = i;
206 	}
207 
208 process_avoid_channel_ext:
209 	ucfg_reg_ch_avoid_ext(hdd_ctx->psoc, &avoid_freq_list);
210 out:
211 	return ret;
212 }
213 
214 /**
215  * wlan_hdd_cfg80211_avoid_freq_ext() - exclude channels from upper layer.
216  * @wiphy: wiphy structure pointer
217  * @wdev: Wireless device structure pointer
218  * @data: Pointer to the data received
219  * @data_len: Length of @data
220  *
221  * wlan_hdd_cfg80211_avoid_freq_ext() will pass the extended avoid frequency
222  * list from upper layer to regulatory component to compute one new channel
223  * list.
224  *
225  * Return: 0 on success; errno on failure
226  */
wlan_hdd_cfg80211_avoid_freq_ext(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)227 int wlan_hdd_cfg80211_avoid_freq_ext(struct wiphy *wiphy,
228 				     struct wireless_dev *wdev,
229 				     const void *data, int data_len)
230 {
231 	struct osif_psoc_sync *psoc_sync;
232 	int errno;
233 
234 	errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
235 	if (errno)
236 		return errno;
237 
238 	errno = __wlan_hdd_cfg80211_avoid_freq_ext(wiphy, wdev, data, data_len);
239 
240 	osif_psoc_sync_op_stop(psoc_sync);
241 
242 	return errno;
243 }
244