blob: 60274d27c8d837510222b6f835c2a5bacc35b051 [file] [log] [blame]
Qiwei Caie689a262018-07-26 15:50:22 +08001/*
2 * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for
5 * any purpose with or without fee is hereby granted, provided that the
6 * above copyright notice and this permission notice appear in all
7 * copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16 * PERFORMANCE OF THIS SOFTWARE.
17 */
18
19/**
20 * DOC: wlan_hdd_sap_cond_chan_switch.c
21 *
22 * WLAN SAP conditional channel switch functions
23 *
24 */
25
26#include <wlan_hdd_includes.h>
27#include <linux/netdevice.h>
28#include <linux/skbuff.h>
29#include <linux/etherdevice.h>
30#include <linux/if_ether.h>
31#include <cds_utils.h>
32#include <qdf_str.h>
33#include <wlan_hdd_sap_cond_chan_switch.h>
34
35/**
36 * wlan_hdd_set_pre_cac_status() - Set the pre cac status
37 * @pre_cac_adapter: AP adapter used for pre cac
38 * @status: Status (true or false)
39 * @handle: Global handle
40 *
41 * Sets the status of pre cac i.e., whether the pre cac is active or not
42 *
43 * Return: Zero on success, non-zero on failure
44 */
45static int wlan_hdd_set_pre_cac_status(struct hdd_adapter *pre_cac_adapter,
46 bool status, mac_handle_t handle)
47{
48 QDF_STATUS ret;
49
50 ret = wlan_sap_set_pre_cac_status(
51 WLAN_HDD_GET_SAP_CTX_PTR(pre_cac_adapter), status, handle);
52 if (QDF_IS_STATUS_ERROR(ret))
53 return -EINVAL;
54
55 return 0;
56}
57
58/**
59 * wlan_hdd_set_chan_before_pre_cac() - Save the channel before pre cac
60 * @ap_adapter: AP adapter
61 * @chan_before_pre_cac: Channel
62 *
63 * Saves the channel which the AP was beaconing on before moving to the pre
64 * cac channel. If radar is detected on the pre cac channel, this saved
65 * channel will be used for AP operations.
66 *
67 * Return: Zero on success, non-zero on failure
68 */
69static int wlan_hdd_set_chan_before_pre_cac(struct hdd_adapter *ap_adapter,
70 uint8_t chan_before_pre_cac)
71{
72 QDF_STATUS ret;
73
74 ret = wlan_sap_set_chan_before_pre_cac(
75 WLAN_HDD_GET_SAP_CTX_PTR(ap_adapter), chan_before_pre_cac);
76 if (QDF_IS_STATUS_ERROR(ret))
77 return -EINVAL;
78
79 return 0;
80}
81
82/**
83 * wlan_hdd_validate_and_get_pre_cac_ch() - Validate and get pre cac channel
84 * @hdd_ctx: HDD context
85 * @ap_adapter: AP adapter
86 * @channel: Channel requested by userspace
87 * @pre_cac_chan: Pointer to the pre CAC channel
88 *
89 * Validates the channel provided by userspace. If user provided channel 0,
90 * a valid outdoor channel must be selected from the regulatory channel.
91 *
92 * Return: Zero on success and non zero value on error
93 */
94static int wlan_hdd_validate_and_get_pre_cac_ch(struct hdd_context *hdd_ctx,
95 struct hdd_adapter *ap_adapter,
96 uint8_t channel,
97 uint8_t *pre_cac_chan)
98{
99 uint32_t i;
100 QDF_STATUS status;
101 uint32_t weight_len = 0;
102 uint32_t len = WNI_CFG_VALID_CHANNEL_LIST_LEN;
103 uint8_t channel_list[QDF_MAX_NUM_CHAN] = {0};
104 uint8_t pcl_weights[QDF_MAX_NUM_CHAN] = {0};
105 mac_handle_t mac_handle;
106
107 if (channel == 0) {
108 /* Channel is not obtained from PCL because PCL may not have
109 * the entire channel list. For example: if SAP is up on
110 * channel 6 and PCL is queried for the next SAP interface,
111 * if SCC is preferred, the PCL will contain only the channel
112 * 6. But, we are in need of a DFS channel. So, going with the
113 * first channel from the valid channel list.
114 */
115 status = policy_mgr_get_valid_chans(hdd_ctx->hdd_psoc,
116 channel_list, &len);
117 if (QDF_IS_STATUS_ERROR(status)) {
118 hdd_err("Failed to get channel list");
119 return -EINVAL;
120 }
121 policy_mgr_update_with_safe_channel_list(hdd_ctx->hdd_psoc,
122 channel_list, &len,
123 pcl_weights,
124 weight_len);
125 for (i = 0; i < len; i++) {
126 if (wlan_reg_is_dfs_ch(hdd_ctx->hdd_pdev,
127 channel_list[i])) {
128 *pre_cac_chan = channel_list[i];
129 break;
130 }
131 }
132 if (*pre_cac_chan == 0) {
133 hdd_err("unable to find outdoor channel");
134 return -EINVAL;
135 }
136 } else {
137 /* Only when driver selects a channel, check is done for
138 * unnsafe and NOL channels. When user provides a fixed channel
139 * the user is expected to take care of this.
140 */
141 mac_handle = hdd_ctx->mac_handle;
142 if (!sme_is_channel_valid(mac_handle, channel) ||
143 !wlan_reg_is_dfs_ch(hdd_ctx->hdd_pdev, channel)) {
144 hdd_err("Invalid channel for pre cac:%d", channel);
145 return -EINVAL;
146 }
147 *pre_cac_chan = channel;
148 }
149 hdd_debug("selected pre cac channel:%d", *pre_cac_chan);
150 return 0;
151}
152
153/**
154 * wlan_hdd_request_pre_cac() - Start pre CAC in the driver
155 * @channel: Channel option provided by userspace
156 *
157 * Sets the driver to the required hardware mode and start an adapter for
158 * pre CAC which will mimic an AP.
159 *
160 * Return: Zero on success, non-zero value on error
161 */
162int wlan_hdd_request_pre_cac(uint8_t channel)
163{
164 uint8_t pre_cac_chan = 0, *mac_addr;
165 struct hdd_context *hdd_ctx;
166 int ret;
167 struct hdd_adapter *ap_adapter, *pre_cac_adapter;
168 struct hdd_ap_ctx *hdd_ap_ctx;
169 QDF_STATUS status;
170 struct wiphy *wiphy;
171 struct net_device *dev;
172 struct cfg80211_chan_def chandef;
173 enum nl80211_channel_type channel_type;
174 uint32_t freq;
175 struct ieee80211_channel *chan;
176 mac_handle_t mac_handle;
177 bool val;
178
179 hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
180 if (wlan_hdd_validate_context(hdd_ctx) != 0)
181 return -EINVAL;
182
183 if (policy_mgr_get_connection_count(hdd_ctx->hdd_psoc) > 1) {
184 hdd_err("pre cac not allowed in concurrency");
185 return -EINVAL;
186 }
187
188 ap_adapter = hdd_get_adapter(hdd_ctx, QDF_SAP_MODE);
189 if (!ap_adapter) {
190 hdd_err("unable to get SAP adapter");
191 return -EINVAL;
192 }
193
194 mac_handle = hdd_ctx->mac_handle;
195 val = wlan_sap_is_pre_cac_active(mac_handle);
196 if (val) {
197 hdd_err("pre cac is already in progress");
198 return -EINVAL;
199 }
200
201 hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter);
202 if (!hdd_ap_ctx) {
203 hdd_err("SAP context is NULL");
204 return -EINVAL;
205 }
206
207 if (wlan_reg_is_dfs_ch(hdd_ctx->hdd_pdev,
208 hdd_ap_ctx->operating_channel)) {
209 hdd_err("SAP is already on DFS channel:%d",
210 hdd_ap_ctx->operating_channel);
211 return -EINVAL;
212 }
213
214 if (!WLAN_REG_IS_24GHZ_CH(hdd_ap_ctx->operating_channel)) {
215 hdd_err("pre CAC alllowed only when SAP is in 2.4GHz:%d",
216 hdd_ap_ctx->operating_channel);
217 return -EINVAL;
218 }
219
220 mac_addr = wlan_hdd_get_intf_addr(hdd_ctx);
221 if (!mac_addr) {
222 hdd_err("can't add virtual intf: Not getting valid mac addr");
223 return -EINVAL;
224 }
225
226 hdd_debug("channel:%d", channel);
227
228 ret = wlan_hdd_validate_and_get_pre_cac_ch(hdd_ctx, ap_adapter, channel,
229 &pre_cac_chan);
230 if (ret != 0) {
231 hdd_err("can't validate pre-cac channel");
232 goto release_intf_addr_and_return_failure;
233 }
234
235 hdd_debug("starting pre cac SAP adapter");
236
237 /* Starting a SAP adapter:
238 * Instead of opening an adapter, we could just do a SME open session
239 * for AP type. But, start BSS would still need an adapter.
240 * So, this option is not taken.
241 *
242 * hdd open adapter is going to register this precac interface with
243 * user space. This interface though exposed to user space will be in
244 * DOWN state. Consideration was done to avoid this registration to the
245 * user space. But, as part of SAP operations multiple events are sent
246 * to user space. Some of these events received from unregistered
247 * interface was causing crashes. So, retaining the registration.
248 *
249 * So, this interface would remain registered and will remain in DOWN
250 * state for the CAC duration. We will add notes in the feature
251 * announcement to not use this temporary interface for any activity
252 * from user space.
253 */
254 pre_cac_adapter = hdd_open_adapter(hdd_ctx, QDF_SAP_MODE, "precac%d",
255 mac_addr, NET_NAME_UNKNOWN, true);
256 if (!pre_cac_adapter) {
257 hdd_err("error opening the pre cac adapter");
258 goto release_intf_addr_and_return_failure;
259 }
260
261 /*
262 * This interface is internally created by the driver. So, no interface
263 * up comes for this interface from user space and hence starting
264 * the adapter internally.
265 */
266 if (hdd_start_adapter(pre_cac_adapter)) {
267 hdd_err("error starting the pre cac adapter");
268 goto close_pre_cac_adapter;
269 }
270
271 hdd_debug("preparing for start ap/bss on the pre cac adapter");
272
273 wiphy = hdd_ctx->wiphy;
274 dev = pre_cac_adapter->dev;
275
276 /* Since this is only a dummy interface lets us use the IEs from the
277 * other active SAP interface. In regular scenarios, these IEs would
278 * come from the user space entity
279 */
280 pre_cac_adapter->session.ap.beacon = qdf_mem_malloc(
281 sizeof(*ap_adapter->session.ap.beacon));
282 if (!pre_cac_adapter->session.ap.beacon) {
283 hdd_err("failed to alloc mem for beacon");
284 goto stop_close_pre_cac_adapter;
285 }
286 qdf_mem_copy(pre_cac_adapter->session.ap.beacon,
287 ap_adapter->session.ap.beacon,
288 sizeof(*pre_cac_adapter->session.ap.beacon));
289 pre_cac_adapter->session.ap.sap_config.ch_width_orig =
290 ap_adapter->session.ap.sap_config.ch_width_orig;
291 pre_cac_adapter->session.ap.sap_config.authType =
292 ap_adapter->session.ap.sap_config.authType;
293
294 /* Premise is that on moving from 2.4GHz to 5GHz, the SAP will continue
295 * to operate on the same bandwidth as that of the 2.4GHz operations.
296 * Only bandwidths 20MHz/40MHz are possible on 2.4GHz band.
297 */
298 switch (ap_adapter->session.ap.sap_config.ch_width_orig) {
299 case CH_WIDTH_20MHZ:
300 channel_type = NL80211_CHAN_HT20;
301 break;
302 case CH_WIDTH_40MHZ:
303 if (ap_adapter->session.ap.sap_config.sec_ch >
304 ap_adapter->session.ap.sap_config.channel)
305 channel_type = NL80211_CHAN_HT40PLUS;
306 else
307 channel_type = NL80211_CHAN_HT40MINUS;
308 break;
309 default:
310 channel_type = NL80211_CHAN_NO_HT;
311 break;
312 }
313
314 freq = cds_chan_to_freq(pre_cac_chan);
315 chan = ieee80211_get_channel(wiphy, freq);
316 if (!chan) {
317 hdd_err("channel converion failed");
318 goto stop_close_pre_cac_adapter;
319 }
320
321 cfg80211_chandef_create(&chandef, chan, channel_type);
322
323 hdd_debug("orig width:%d channel_type:%d freq:%d",
324 ap_adapter->session.ap.sap_config.ch_width_orig,
325 channel_type, freq);
326 /*
327 * Doing update after opening and starting pre-cac adapter will make
328 * sure that driver won't do hardware mode change if there are any
329 * initial hick-ups or issues in pre-cac adapter's configuration.
330 * Since current SAP is in 2.4GHz and pre CAC channel is in 5GHz, this
331 * connection update should result in DBS mode
332 */
333 status = policy_mgr_update_and_wait_for_connection_update(
334 hdd_ctx->hdd_psoc,
335 ap_adapter->session_id,
336 pre_cac_chan,
337 POLICY_MGR_UPDATE_REASON_PRE_CAC);
338 if (QDF_IS_STATUS_ERROR(status)) {
339 hdd_err("error in moving to DBS mode");
340 goto stop_close_pre_cac_adapter;
341 }
342
343 ret = wlan_hdd_set_channel(wiphy, dev, &chandef, channel_type);
344 if (ret != 0) {
345 hdd_err("failed to set channel");
346 goto stop_close_pre_cac_adapter;
347 }
348
349 status = wlan_hdd_cfg80211_start_bss(pre_cac_adapter, NULL,
350 PRE_CAC_SSID, qdf_str_len(PRE_CAC_SSID),
351 NL80211_HIDDEN_SSID_NOT_IN_USE, false);
352 if (QDF_IS_STATUS_ERROR(status)) {
353 hdd_err("start bss failed");
354 goto stop_close_pre_cac_adapter;
355 }
356
357 /*
358 * The pre cac status is set here. But, it would not be reset explicitly
359 * anywhere, since after the pre cac success/failure, the pre cac
360 * adapter itself would be removed.
361 */
362 ret = wlan_hdd_set_pre_cac_status(pre_cac_adapter, true, mac_handle);
363 if (ret != 0) {
364 hdd_err("failed to set pre cac status");
365 goto stop_close_pre_cac_adapter;
366 }
367
368 ret = wlan_hdd_set_chan_before_pre_cac(ap_adapter,
369 hdd_ap_ctx->operating_channel);
370 if (ret != 0) {
371 hdd_err("failed to set channel before pre cac");
372 goto stop_close_pre_cac_adapter;
373 }
374
375 ap_adapter->pre_cac_chan = pre_cac_chan;
376
377 return 0;
378
379stop_close_pre_cac_adapter:
380 hdd_stop_adapter(hdd_ctx, pre_cac_adapter);
381 qdf_mem_free(pre_cac_adapter->session.ap.beacon);
382 pre_cac_adapter->session.ap.beacon = NULL;
383close_pre_cac_adapter:
384 hdd_close_adapter(hdd_ctx, pre_cac_adapter, false);
385release_intf_addr_and_return_failure:
386 /*
387 * Release the interface address as the adapter
388 * failed to start, if you don't release then next
389 * adapter which is trying to come wouldn't get valid
390 * mac address. Remember we have limited pool of mac addresses
391 */
392 wlan_hdd_release_intf_addr(hdd_ctx, mac_addr);
393 return -EINVAL;
394}
395
396/**
397 * __wlan_hdd_cfg80211_conditional_chan_switch() - Conditional channel switch
398 * @wiphy: Pointer to wireless phy
399 * @wdev: Pointer to wireless device
400 * @data: Pointer to data
401 * @data_len: Data length
402 *
403 * Processes the conditional channel switch request and invokes the helper
404 * APIs to process the channel switch request.
405 *
406 * Return: 0 on success, negative errno on failure
407 */
408static int
409__wlan_hdd_cfg80211_conditional_chan_switch(struct wiphy *wiphy,
410 struct wireless_dev *wdev,
411 const void *data,
412 int data_len)
413{
414 int ret;
415 struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
416 struct net_device *dev = wdev->netdev;
417 struct hdd_adapter *adapter;
418 struct nlattr
419 *tb[QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_MAX + 1];
420 uint32_t freq_len, i;
421 uint32_t *freq;
422 uint8_t chans[QDF_MAX_NUM_CHAN] = {0};
423
424 hdd_enter_dev(dev);
425
426 ret = wlan_hdd_validate_context(hdd_ctx);
427 if (ret)
428 return ret;
429
430 if (!hdd_ctx->config->enableDFSMasterCap) {
431 hdd_err("DFS master capability is not present in the driver");
432 return -EINVAL;
433 }
434
435 if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) {
436 hdd_err("Command not allowed in FTM mode");
437 return -EPERM;
438 }
439
440 adapter = WLAN_HDD_GET_PRIV_PTR(dev);
441 if (adapter->device_mode != QDF_SAP_MODE) {
442 hdd_err("Invalid device mode %d", adapter->device_mode);
443 return -EINVAL;
444 }
445
446 /*
447 * audit note: it is ok to pass a NULL policy here since only
448 * one attribute is parsed which is array of frequencies and
449 * it is explicitly validated for both under read and over read
450 */
451 if (wlan_cfg80211_nla_parse(tb,
452 QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_MAX,
453 data, data_len, NULL)) {
454 hdd_err("Invalid ATTR");
455 return -EINVAL;
456 }
457
458 if (!tb[QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST]) {
459 hdd_err("Frequency list is missing");
460 return -EINVAL;
461 }
462
463 freq_len = nla_len(
464 tb[QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST])/
465 sizeof(uint32_t);
466
467 if (freq_len > QDF_MAX_NUM_CHAN) {
468 hdd_err("insufficient space to hold channels");
469 return -ENOMEM;
470 }
471
472 hdd_debug("freq_len=%d", freq_len);
473
474 freq = nla_data(
475 tb[QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST]);
476
477 for (i = 0; i < freq_len; i++) {
478 if (freq[i] == 0)
479 chans[i] = 0;
480 else
481 chans[i] = ieee80211_frequency_to_channel(freq[i]);
482
483 hdd_debug("freq[%d]=%d", i, freq[i]);
484 }
485
486 /*
487 * The input frequency list from user space is designed to be a
488 * priority based frequency list. This is only to accommodate any
489 * future request. But, current requirement is only to perform CAC
490 * on a single channel. So, the first entry from the list is picked.
491 *
492 * If channel is zero, any channel in the available outdoor regulatory
493 * domain will be selected.
494 */
495 ret = wlan_hdd_request_pre_cac(chans[0]);
496 if (ret) {
497 hdd_err("pre cac request failed with reason:%d", ret);
498 return ret;
499 }
500
501 return 0;
502}
503
504int wlan_hdd_cfg80211_conditional_chan_switch(struct wiphy *wiphy,
505 struct wireless_dev *wdev,
506 const void *data,
507 int data_len)
508{
509 int ret;
510
511 cds_ssr_protect(__func__);
512 ret = __wlan_hdd_cfg80211_conditional_chan_switch(wiphy, wdev,
513 data, data_len);
514 cds_ssr_unprotect(__func__);
515
516 return ret;
517}
518