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: defines driver functions interfacing with linux kernel
22  */
23 
24 #include <qdf_list.h>
25 #include <qdf_status.h>
26 #include <linux/wireless.h>
27 #include <linux/netdevice.h>
28 #include <wlan_cfg80211.h>
29 #include <wlan_osif_priv.h>
30 #include <wlan_interop_issues_ap_ucfg_api.h>
31 #include <wlan_cfg80211_interop_issues_ap.h>
32 #include <osif_psoc_sync.h>
33 #include <qdf_mem.h>
34 #include <wlan_utility.h>
35 #include "wlan_hdd_main.h"
36 #include "cfg_ucfg_api.h"
37 #include "wlan_hdd_object_manager.h"
38 
39 const struct nla_policy
40 interop_issues_ap_policy[QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX + 1] = {
41 	[QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_TYPE] = {
42 						.type = NLA_U32,
43 						.len = sizeof(uint32_t) },
44 	[QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_LIST] = {
45 						.type = NLA_U32,
46 						.len = sizeof(uint32_t) },
47 	[QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID] =
48 						VENDOR_NLA_POLICY_MAC_ADDR,
49 };
50 
51 /**
52  * wlan_cfg80211_send_interop_issues_ap_cb() - report information
53  * @data: interop issues ap mac received from fw
54  *
55  * Generate a wlan interop issues ap info package and send it to user
56  * space daemon through netlink.
57  *
58  * Return: none
59  */
60 static void
wlan_cfg80211_send_interop_issues_ap_cb(struct wlan_interop_issues_ap_event * data)61 wlan_cfg80211_send_interop_issues_ap_cb(
62 				struct wlan_interop_issues_ap_event *data)
63 {
64 	struct wlan_objmgr_pdev *pdev;
65 	struct pdev_osif_priv *os_priv;
66 	struct sk_buff *skb;
67 	uint32_t index, len;
68 
69 	if (!data) {
70 		osif_err("Invalid result.");
71 		return;
72 	}
73 
74 	pdev = data->pdev;
75 	if (!pdev) {
76 		osif_err("pdev is null.");
77 		return;
78 	}
79 	os_priv = wlan_pdev_get_ospriv(pdev);
80 	if (!os_priv) {
81 		osif_err("os_priv is null.");
82 		return;
83 	}
84 
85 	index = QCA_NL80211_VENDOR_SUBCMD_INTEROP_ISSUES_AP_INDEX;
86 	len = nla_total_size(QDF_MAC_ADDR_SIZE + NLMSG_HDRLEN);
87 	skb = wlan_cfg80211_vendor_event_alloc(os_priv->wiphy, NULL, len, index,
88 					       GFP_KERNEL);
89 	if (!skb) {
90 		osif_err("skb alloc failed");
91 		return;
92 	}
93 
94 	osif_debug("interop issues ap mac:" QDF_MAC_ADDR_FMT,
95 		   QDF_MAC_ADDR_REF(data->rap_addr.bytes));
96 
97 	if (nla_put(skb,
98 		    QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID,
99 		    QDF_MAC_ADDR_SIZE, data->rap_addr.bytes)) {
100 		osif_err("nla put fail");
101 		wlan_cfg80211_vendor_free_skb(skb);
102 		return;
103 	}
104 
105 	wlan_cfg80211_vendor_event(skb, GFP_KERNEL);
106 }
107 
wlan_interop_issues_ap_register_cbk(struct wlan_objmgr_pdev * pdev)108 static void wlan_interop_issues_ap_register_cbk(struct wlan_objmgr_pdev *pdev)
109 {
110 	struct wlan_interop_issues_ap_callbacks cb;
111 
112 	cb.os_if_interop_issues_ap_event_handler =
113 					wlan_cfg80211_send_interop_issues_ap_cb;
114 	ucfg_register_interop_issues_ap_callback(pdev, &cb);
115 }
116 
117 /**
118  * wlan_parse_interop_issues_ap() - parse the interop issues ap info
119  * @interop_issues_ap: the pointer of interop issues ap
120  * @attr: list of attributes
121  *
122  * Return: 0 on success; error number on failure
123  */
124 static int
wlan_parse_interop_issues_ap(struct qdf_mac_addr * interop_issues_ap,struct nlattr * attr)125 wlan_parse_interop_issues_ap(struct qdf_mac_addr *interop_issues_ap,
126 			     struct nlattr *attr)
127 {
128 	struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX + 1];
129 	struct nlattr *curr_attr = NULL;
130 	uint32_t rem;
131 	qdf_size_t i = 0;
132 
133 	nla_for_each_nested(curr_attr, attr, rem) {
134 		if (i == MAX_INTEROP_ISSUES_AP_NUM) {
135 			osif_err("Ignoring excess");
136 			break;
137 		}
138 
139 		if (wlan_cfg80211_nla_parse(tb2,
140 				QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX,
141 				nla_data(curr_attr),
142 				nla_len(curr_attr),
143 				interop_issues_ap_policy)) {
144 			osif_err("nla_parse failed");
145 			return -EINVAL;
146 		}
147 		if (!tb2[QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID]) {
148 			osif_err("attr addr failed");
149 			return -EINVAL;
150 		}
151 		nla_memcpy(interop_issues_ap[i].bytes,
152 			   tb2[QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID],
153 			   QDF_MAC_ADDR_SIZE);
154 		osif_debug(QDF_MAC_ADDR_FMT,
155 			   QDF_MAC_ADDR_REF(interop_issues_ap[i].bytes));
156 		i++;
157 	}
158 
159 	return i;
160 }
161 
162 /**
163  * __wlan_cfg80211_set_interop_issues_ap_config() - set config status
164  * @wiphy: WIPHY structure pointer
165  * @wdev: Wireless device structure pointer
166  * @data: Pointer to the data received
167  * @data_len: Length of the data received
168  *
169  * Return: 0 on success and errno on failure
170  */
171 static int
__wlan_cfg80211_set_interop_issues_ap_config(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)172 __wlan_cfg80211_set_interop_issues_ap_config(struct wiphy *wiphy,
173 					     struct wireless_dev *wdev,
174 					     const void *data, int data_len)
175 {
176 	struct net_device *dev = wdev->netdev;
177 	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
178 	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX + 1];
179 	struct nlattr *attr;
180 	uint32_t count = 0;
181 	struct wlan_interop_issues_ap_info interop_issues_ap = {0};
182 	struct wlan_objmgr_psoc *psoc;
183 	struct wlan_objmgr_vdev *vdev;
184 
185 	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
186 		hdd_err("Command not allowed in FTM mode");
187 		return -EPERM;
188 	}
189 
190 	vdev = hdd_objmgr_get_vdev_by_user(adapter->deflink,
191 					   WLAN_INTEROP_ISSUES_AP_ID);
192 	if (!vdev) {
193 		osif_err("Invalid vdev");
194 		return -EINVAL;
195 	}
196 
197 	psoc = wlan_vdev_get_psoc(vdev);
198 	hdd_objmgr_put_vdev_by_user(vdev, WLAN_INTEROP_ISSUES_AP_ID);
199 	if (!psoc) {
200 		osif_err("Invalid psoc");
201 		return -EINVAL;
202 	}
203 
204 	if (wlan_cfg80211_nla_parse(tb,
205 				    QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX,
206 				    data, data_len,
207 				    interop_issues_ap_policy)) {
208 		osif_err("Invalid ATTR");
209 		return -EINVAL;
210 	}
211 
212 	attr = tb[QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_LIST];
213 	if (attr) {
214 		count =
215 		     wlan_parse_interop_issues_ap(interop_issues_ap.rap_items,
216 						  attr);
217 		if (count < 0)
218 			return -EINVAL;
219 	}
220 
221 	osif_debug("Num of interop issues ap: %d", count);
222 	interop_issues_ap.count = count;
223 	interop_issues_ap.detect_enable = true;
224 
225 	/*
226 	 * need to figure out a converged way of obtaining the vdev for
227 	 * a given netdev that doesn't involve the legacy mechanism.
228 	 */
229 	ucfg_set_interop_issues_ap_config(psoc, &interop_issues_ap);
230 
231 	return 0;
232 }
233 
wlan_cfg80211_set_interop_issues_ap_config(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)234 int wlan_cfg80211_set_interop_issues_ap_config(struct wiphy *wiphy,
235 					       struct wireless_dev *wdev,
236 					       const void *data, int data_len)
237 {
238 	struct osif_psoc_sync *psoc_sync;
239 	int ret;
240 
241 	ret = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
242 	if (ret)
243 		return ret;
244 
245 	ret = __wlan_cfg80211_set_interop_issues_ap_config(wiphy, wdev,
246 							   data, data_len);
247 	osif_psoc_sync_op_stop(psoc_sync);
248 
249 	return ret;
250 }
251 
wlan_cfg80211_init_interop_issues_ap(struct wlan_objmgr_pdev * pdev)252 void wlan_cfg80211_init_interop_issues_ap(struct wlan_objmgr_pdev *pdev)
253 {
254 	/*
255 	 * the special mac is used to trigger uplayer sets
256 	 * interop issues ap list to fw when driver reloads but
257 	 * cnss-daemon does not restart.
258 	 */
259 	uint8_t fmac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
260 	struct wlan_interop_issues_ap_info interop_issues_ap = {0};
261 	struct wlan_interop_issues_ap_event data;
262 	struct wlan_objmgr_psoc *psoc;
263 
264 	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
265 		hdd_err("Operation not supported in FTM mode");
266 		return;
267 	}
268 
269 	wlan_interop_issues_ap_register_cbk(pdev);
270 
271 	psoc = wlan_pdev_get_psoc(pdev);
272 	if (!psoc) {
273 		osif_err("Invalid psoc");
274 		return;
275 	}
276 	interop_issues_ap.detect_enable = true;
277 	ucfg_set_interop_issues_ap_config(psoc, &interop_issues_ap);
278 
279 	data.pdev = pdev;
280 	qdf_mem_copy(data.rap_addr.bytes, fmac, QDF_MAC_ADDR_SIZE);
281 
282 	wlan_cfg80211_send_interop_issues_ap_cb(&data);
283 }
284