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