1 /*
2 * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved.
3 * Copyright (c) 2021-2022 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_sap_cond_chan_switch.c
22 *
23 * WLAN SAP conditional channel switch functions
24 *
25 */
26
27 #include <cds_utils.h>
28 #include <linux/netdevice.h>
29 #include <linux/skbuff.h>
30 #include <linux/etherdevice.h>
31 #include <linux/if_ether.h>
32 #include "osif_sync.h"
33 #include <qdf_str.h>
34 #include <wlan_hdd_includes.h>
35 #include <wlan_hdd_sap_cond_chan_switch.h>
36 #include "wlan_hdd_pre_cac.h"
37
38 const struct nla_policy conditional_chan_switch_policy[
39 QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_MAX + 1] = {
40 [QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST] = {
41 .type = NLA_BINARY },
42 [QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_STATUS] = {
43 .type = NLA_U32 },
44 };
45
46 /**
47 * __wlan_hdd_cfg80211_conditional_chan_switch() - Conditional channel switch
48 * @wiphy: Pointer to wireless phy
49 * @wdev: Pointer to wireless device
50 * @data: Pointer to data
51 * @data_len: Data length
52 *
53 * Processes the conditional channel switch request and invokes the helper
54 * APIs to process the channel switch request.
55 *
56 * Return: 0 on success, negative errno on failure
57 */
58 static int
__wlan_hdd_cfg80211_conditional_chan_switch(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)59 __wlan_hdd_cfg80211_conditional_chan_switch(struct wiphy *wiphy,
60 struct wireless_dev *wdev,
61 const void *data,
62 int data_len)
63 {
64 int ret;
65 struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
66 struct net_device *dev = wdev->netdev;
67 struct hdd_adapter *adapter;
68 struct nlattr
69 *tb[QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_MAX + 1];
70 uint32_t freq_len, i;
71 uint32_t *freq;
72 bool is_dfs_mode_enabled = false;
73
74 hdd_enter_dev(dev);
75
76 ret = wlan_hdd_validate_context(hdd_ctx);
77 if (ret)
78 return ret;
79
80 if (QDF_STATUS_SUCCESS != ucfg_mlme_get_dfs_master_capability(
81 hdd_ctx->psoc, &is_dfs_mode_enabled)) {
82 hdd_err("Failed to get dfs master capability");
83 return -EINVAL;
84 }
85
86 if (!is_dfs_mode_enabled) {
87 hdd_err("DFS master capability is not present in the driver");
88 return -EINVAL;
89 }
90
91 if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) {
92 hdd_err("Command not allowed in FTM mode");
93 return -EPERM;
94 }
95
96 adapter = WLAN_HDD_GET_PRIV_PTR(dev);
97 if (adapter->device_mode != QDF_SAP_MODE) {
98 hdd_err("Invalid device mode %d", adapter->device_mode);
99 return -EINVAL;
100 }
101
102 /*
103 * audit note: it is ok to pass a NULL policy here since only
104 * one attribute is parsed which is array of frequencies and
105 * it is explicitly validated for both under read and over read
106 */
107 if (wlan_cfg80211_nla_parse(tb,
108 QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_MAX,
109 data, data_len, conditional_chan_switch_policy)) {
110 hdd_err("Invalid ATTR");
111 return -EINVAL;
112 }
113
114 if (!tb[QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST]) {
115 hdd_err("Frequency list is missing");
116 return -EINVAL;
117 }
118
119 freq_len = nla_len(
120 tb[QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST])/
121 sizeof(uint32_t);
122
123 if (freq_len > NUM_CHANNELS) {
124 hdd_err("insufficient space to hold channels");
125 return -ENOMEM;
126 }
127
128 hdd_debug("freq_len=%d", freq_len);
129
130 freq = nla_data(
131 tb[QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST]);
132
133 for (i = 0; i < freq_len; i++)
134 hdd_debug("freq[%d]=%d", i, freq[i]);
135
136 /*
137 * The input frequency list from user space is designed to be a
138 * priority based frequency list. This is only to accommodate any
139 * future request. But, current requirement is only to perform CAC
140 * on a single channel. So, the first entry from the list is picked.
141 *
142 * If channel is zero, any channel in the available outdoor regulatory
143 * domain will be selected.
144 */
145 ret = wlan_hdd_request_pre_cac(hdd_ctx, freq[0]);
146 if (ret) {
147 hdd_err("pre cac request failed with reason:%d", ret);
148 return ret;
149 }
150
151 return 0;
152 }
153
wlan_hdd_cfg80211_conditional_chan_switch(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)154 int wlan_hdd_cfg80211_conditional_chan_switch(struct wiphy *wiphy,
155 struct wireless_dev *wdev,
156 const void *data,
157 int data_len)
158 {
159 struct osif_vdev_sync *vdev_sync;
160 int errno;
161
162 errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
163 if (errno)
164 return errno;
165
166 errno = __wlan_hdd_cfg80211_conditional_chan_switch(wiphy, wdev,
167 data, data_len);
168
169 osif_vdev_sync_op_stop(vdev_sync);
170
171 return errno;
172 }
173
174