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