1 /*
2  * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022-2023 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_coex_config.c
22  *
23  * The implementation of coex configuration
24  *
25  */
26 
27 #include "wlan_hdd_main.h"
28 #include "wmi_unified_param.h"
29 #include "wlan_hdd_coex_config.h"
30 #include "qca_vendor.h"
31 #include "wlan_osif_request_manager.h"
32 #include "osif_sync.h"
33 #include "wlan_fwol_ucfg_api.h"
34 
35 const struct nla_policy
36 coex_config_three_way_policy[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_MAX + 1] = {
37 	[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE] = {
38 							      .type = NLA_U32},
39 	[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1] = {.type = NLA_U32},
40 	[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2] = {.type = NLA_U32},
41 	[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3] = {.type = NLA_U32},
42 	[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4] = {.type = NLA_U32},
43 };
44 
45 static const uint32_t
46 config_type_to_wmi_tbl[QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_TYPE_MAX] = {
47 	[QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_RESET] =
48 		WMI_COEX_CONFIG_THREE_WAY_COEX_RESET,
49 	[QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_START] =
50 		WMI_COEX_CONFIG_THREE_WAY_COEX_START,
51 };
52 
53 /**
54  * __wlan_hdd_cfg80211_set_coex_config() - set coex configuration
55  * parameters
56  * @wiphy: pointer to wireless wiphy structure.
57  * @wdev: pointer to wireless_dev structure.
58  * @data: pointer to limit off-channel command parameters.
59  * @data_len: the length in byte of  limit off-channel command parameters.
60  *
61  * Return: An error code or 0 on success.
62  */
__wlan_hdd_cfg80211_set_coex_config(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)63 static int __wlan_hdd_cfg80211_set_coex_config(struct wiphy *wiphy,
64 					       struct wireless_dev *wdev,
65 					       const void *data, int data_len)
66 {
67 	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(wdev->netdev);
68 	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
69 	struct nlattr *tb[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_MAX + 1];
70 	uint32_t config_type;
71 	struct coex_config_params coex_cfg_params = {0};
72 	struct wlan_fwol_coex_config config = {0};
73 	int errno;
74 	QDF_STATUS status;
75 
76 	hdd_enter();
77 
78 	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
79 		hdd_err("Command not allowed in FTM mode");
80 		return -EPERM;
81 	}
82 
83 	errno = wlan_hdd_validate_context(hdd_ctx);
84 	if (errno != 0)
85 		return errno;
86 
87 	status = ucfg_fwol_get_coex_config_params(hdd_ctx->psoc, &config);
88 	if (QDF_IS_STATUS_ERROR(status)) {
89 		hdd_err("Unable to get coex config params");
90 		return -EINVAL;
91 	}
92 	if (!config.btc_three_way_coex_config_legacy_enable) {
93 		hdd_err("Coex legacy feature should be enable first");
94 		return -EINVAL;
95 	}
96 
97 	if (wlan_cfg80211_nla_parse(tb,
98 				    QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_MAX,
99 				    data, data_len,
100 				    coex_config_three_way_policy)) {
101 		hdd_err("Invalid coex config ATTR");
102 		return -EINVAL;
103 	}
104 
105 	if (!tb[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE]) {
106 		hdd_err("coex config - attr config_type failed");
107 		return -EINVAL;
108 	}
109 
110 	config_type = nla_get_u32(
111 		tb[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE]);
112 	if (config_type >= QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_TYPE_MAX) {
113 		hdd_err("config_type value %d exceeded Max value %d",
114 			config_type,
115 			QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_TYPE_MAX);
116 		return -EINVAL;
117 	}
118 	coex_cfg_params.config_type = config_type_to_wmi_tbl[config_type];
119 	if (coex_cfg_params.config_type <
120 	    WMI_COEX_CONFIG_THREE_WAY_DELAY_PARA ||
121 	    coex_cfg_params.config_type >
122 	    WMI_COEX_CONFIG_THREE_WAY_COEX_START) {
123 		hdd_err("config_type_wmi val error %d",
124 			coex_cfg_params.config_type);
125 		return -EINVAL;
126 	}
127 
128 	hdd_debug("config_type %d, config_type_wmi %d",
129 		  config_type, coex_cfg_params.config_type);
130 
131 	if (!tb[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1]) {
132 		hdd_err("coex config - attr priority1 failed");
133 		return -EINVAL;
134 	}
135 	coex_cfg_params.config_arg1 = nla_get_u32(
136 		tb[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1]);
137 
138 	hdd_debug("priority1 0x%x", coex_cfg_params.config_arg1);
139 
140 	if (!tb[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2]) {
141 		hdd_err("coex config - attr priority2 failed");
142 		return -EINVAL;
143 	}
144 	coex_cfg_params.config_arg2 = nla_get_u32(
145 		tb[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2]);
146 
147 	hdd_debug("priority2 0x%x", coex_cfg_params.config_arg2);
148 
149 	if (!tb[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3]) {
150 		hdd_err("coex config - attr priority3 failed");
151 		return -EINVAL;
152 	}
153 	coex_cfg_params.config_arg3 = nla_get_u32(
154 		tb[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3]);
155 
156 	hdd_debug("priority3 0x%x", coex_cfg_params.config_arg3);
157 
158 	if (!tb[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4]) {
159 		hdd_err("coex config - attr priority4 failed");
160 		return -EINVAL;
161 	}
162 	coex_cfg_params.config_arg4 = nla_get_u32(
163 		tb[QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4]);
164 
165 	hdd_debug("priority4 0x%x", coex_cfg_params.config_arg4);
166 
167 	coex_cfg_params.vdev_id = adapter->deflink->vdev_id;
168 	status = sme_send_coex_config_cmd(&coex_cfg_params);
169 	if (QDF_IS_STATUS_ERROR(status)) {
170 		hdd_err("Failed to send coex config params");
171 		return -EINVAL;
172 	}
173 
174 	return 0;
175 }
176 
177 /**
178  * wlan_hdd_cfg80211_set_coex_config() - set coex configuration
179  * @wiphy: pointer to wireless wiphy structure.
180  * @wdev: pointer to wireless_dev structure.
181  * @data: pointer to limit off-channel command parameters.
182  * @data_len: the length in byte of  limit off-channel command parameters.
183  *
184  *
185  * Return: An error code or 0 on success.
186  */
wlan_hdd_cfg80211_set_coex_config(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)187 int wlan_hdd_cfg80211_set_coex_config(struct wiphy *wiphy,
188 				      struct wireless_dev *wdev,
189 				      const void *data, int data_len)
190 {
191 	int errno;
192 	struct osif_vdev_sync *vdev_sync;
193 
194 	errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
195 	if (errno)
196 		return errno;
197 
198 	errno = __wlan_hdd_cfg80211_set_coex_config(wiphy, wdev,
199 						    data, data_len);
200 
201 	osif_vdev_sync_op_stop(vdev_sync);
202 
203 	return errno;
204 }
205