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