1 /*
2  * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved.
3  *
4  * Permission to use, copy, modify, and/or distribute this software for
5  * any purpose with or without fee is hereby granted, provided that the
6  * above copyright notice and this permission notice appear in all
7  * copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16  * PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /**
20  * DOC: Implements gtk offload feature API's
21  */
22 
23 #include "wlan_pmo_gtk.h"
24 #include "wlan_pmo_tgt_api.h"
25 #include "wlan_pmo_main.h"
26 #include "wlan_pmo_obj_mgmt_public_struct.h"
27 
pmo_core_cache_gtk_req_in_vdev_priv(struct wlan_objmgr_vdev * vdev,struct pmo_gtk_req * gtk_req)28 static QDF_STATUS pmo_core_cache_gtk_req_in_vdev_priv(
29 		struct wlan_objmgr_vdev *vdev,
30 		struct pmo_gtk_req *gtk_req)
31 {
32 	struct pmo_vdev_priv_obj *vdev_ctx;
33 	QDF_STATUS status;
34 	struct qdf_mac_addr peer_bssid;
35 
36 	vdev_ctx = pmo_vdev_get_priv(vdev);
37 
38 	status = pmo_get_vdev_bss_peer_mac_addr(vdev, &peer_bssid);
39 	if (status != QDF_STATUS_SUCCESS)
40 		return status;
41 
42 	qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
43 	qdf_mem_copy(&vdev_ctx->vdev_gtk_req, gtk_req,
44 		sizeof(vdev_ctx->vdev_gtk_req));
45 	qdf_mem_copy(&vdev_ctx->vdev_gtk_req.bssid,
46 		&peer_bssid, QDF_MAC_ADDR_SIZE);
47 	vdev_ctx->vdev_gtk_req.flags = PMO_GTK_OFFLOAD_ENABLE;
48 	qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
49 
50 	return QDF_STATUS_SUCCESS;
51 }
52 
pmo_core_flush_gtk_req_from_vdev_priv(struct wlan_objmgr_vdev * vdev)53 static QDF_STATUS pmo_core_flush_gtk_req_from_vdev_priv(
54 		struct wlan_objmgr_vdev *vdev)
55 {
56 	struct pmo_vdev_priv_obj *vdev_ctx;
57 
58 	vdev_ctx = pmo_vdev_get_priv(vdev);
59 
60 	qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
61 	qdf_mem_zero(&vdev_ctx->vdev_gtk_req, sizeof(vdev_ctx->vdev_gtk_req));
62 	vdev_ctx->vdev_gtk_req.flags = PMO_GTK_OFFLOAD_DISABLE;
63 	qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
64 
65 	return QDF_STATUS_SUCCESS;
66 }
67 
pmo_core_do_enable_gtk_offload(struct wlan_objmgr_vdev * vdev,struct pmo_vdev_priv_obj * vdev_ctx,struct pmo_gtk_req * op_gtk_req)68 static QDF_STATUS pmo_core_do_enable_gtk_offload(
69 			struct wlan_objmgr_vdev *vdev,
70 			struct pmo_vdev_priv_obj *vdev_ctx,
71 			struct pmo_gtk_req *op_gtk_req)
72 {
73 	QDF_STATUS status = QDF_STATUS_SUCCESS;
74 	uint8_t vdev_id;
75 	enum QDF_OPMODE op_mode;
76 
77 	op_mode = pmo_get_vdev_opmode(vdev);
78 	if (QDF_NDI_MODE == op_mode) {
79 		pmo_debug("gtk offload is not supported in NaN mode");
80 		return QDF_STATUS_E_INVAL;
81 	}
82 
83 	if (!pmo_core_is_vdev_supports_offload(vdev)) {
84 		pmo_debug("vdev in invalid opmode for gtk offload %d",
85 			pmo_get_vdev_opmode(vdev));
86 		return QDF_STATUS_E_INVAL;
87 	}
88 
89 	if (wlan_vdev_is_up(vdev) != QDF_STATUS_SUCCESS)
90 		return QDF_STATUS_E_INVAL;
91 
92 	vdev_id = pmo_vdev_get_id(vdev);
93 
94 	qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
95 	qdf_mem_copy(op_gtk_req, &vdev_ctx->vdev_gtk_req,
96 		sizeof(*op_gtk_req));
97 	qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
98 
99 	if ((op_gtk_req->flags == PMO_GTK_OFFLOAD_ENABLE) &&
100 	    (qdf_atomic_read(&vdev_ctx->gtk_err_enable) == 1)) {
101 		pmo_debug("GTK Offload already enabled, Disabling vdev_id: %d",
102 			vdev_id);
103 		op_gtk_req->flags = PMO_GTK_OFFLOAD_DISABLE;
104 		status = pmo_tgt_send_gtk_offload_req(vdev, op_gtk_req);
105 		if (status != QDF_STATUS_SUCCESS) {
106 			pmo_err("Failed to disable GTK Offload");
107 			goto out;
108 		}
109 		pmo_debug("Enable GTK Offload again with updated inputs");
110 		op_gtk_req->flags = PMO_GTK_OFFLOAD_ENABLE;
111 	}
112 
113 	status = pmo_tgt_send_gtk_offload_req(vdev, op_gtk_req);
114 out:
115 
116 	return status;
117 }
118 
pmo_core_is_gtk_enabled_in_fwr(struct wlan_objmgr_vdev * vdev,struct pmo_vdev_priv_obj * vdev_ctx)119 static QDF_STATUS pmo_core_is_gtk_enabled_in_fwr(
120 			struct wlan_objmgr_vdev *vdev,
121 			struct pmo_vdev_priv_obj *vdev_ctx)
122 {
123 	QDF_STATUS status;
124 	struct qdf_mac_addr peer_bssid;
125 
126 	if (!pmo_core_is_vdev_supports_offload(vdev)) {
127 		pmo_debug("vdev in invalid opmode for gtk offload enable %d",
128 			pmo_get_vdev_opmode(vdev));
129 		return QDF_STATUS_E_INVAL;
130 	}
131 
132 	if (wlan_vdev_is_up(vdev) != QDF_STATUS_SUCCESS)
133 		return QDF_STATUS_E_INVAL;
134 
135 	status = pmo_get_vdev_bss_peer_mac_addr(vdev,
136 			&peer_bssid);
137 	if (status != QDF_STATUS_SUCCESS)
138 		return QDF_STATUS_E_INVAL;
139 
140 	qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
141 	if (qdf_mem_cmp(&vdev_ctx->vdev_gtk_req.bssid,
142 		&peer_bssid, QDF_MAC_ADDR_SIZE)) {
143 		qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
144 		pmo_err("cache request mac:"QDF_MAC_ADDR_FMT", peer mac:"QDF_MAC_ADDR_FMT" are not same",
145 			QDF_MAC_ADDR_REF(vdev_ctx->vdev_gtk_req.bssid.bytes),
146 			QDF_MAC_ADDR_REF(peer_bssid.bytes));
147 		return QDF_STATUS_E_INVAL;
148 	}
149 
150 	if (vdev_ctx->vdev_gtk_req.flags != PMO_GTK_OFFLOAD_ENABLE) {
151 		qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
152 		pmo_err("gtk flag is disabled hence no gtk rsp required");
153 		return QDF_STATUS_E_INVAL;
154 	}
155 	qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
156 
157 	return QDF_STATUS_SUCCESS;
158 }
159 
pmo_core_do_disable_gtk_offload(struct wlan_objmgr_vdev * vdev,struct pmo_vdev_priv_obj * vdev_ctx,struct pmo_gtk_req * op_gtk_req)160 static QDF_STATUS pmo_core_do_disable_gtk_offload(
161 			struct wlan_objmgr_vdev *vdev,
162 			struct pmo_vdev_priv_obj *vdev_ctx,
163 			struct pmo_gtk_req *op_gtk_req)
164 {
165 	QDF_STATUS status = QDF_STATUS_SUCCESS;
166 	enum QDF_OPMODE op_mode;
167 
168 	op_mode = pmo_get_vdev_opmode(vdev);
169 	if (QDF_NDI_MODE == op_mode) {
170 		pmo_debug("gtk offload is not supported in NaN mode");
171 		return QDF_STATUS_E_INVAL;
172 	}
173 
174 	status = pmo_core_is_gtk_enabled_in_fwr(vdev, vdev_ctx);
175 	if (status != QDF_STATUS_SUCCESS)
176 		return status;
177 
178 	op_gtk_req->flags = PMO_GTK_OFFLOAD_DISABLE;
179 	status = pmo_tgt_send_gtk_offload_req(vdev, op_gtk_req);
180 
181 	return status;
182 }
183 
pmo_core_cache_gtk_offload_req(struct wlan_objmgr_vdev * vdev,struct pmo_gtk_req * gtk_req)184 QDF_STATUS pmo_core_cache_gtk_offload_req(struct wlan_objmgr_vdev *vdev,
185 		struct pmo_gtk_req *gtk_req)
186 {
187 	QDF_STATUS status;
188 	enum QDF_OPMODE opmode;
189 	uint8_t vdev_id;
190 
191 	if (!gtk_req) {
192 		pmo_err("gtk_req is NULL");
193 		status = QDF_STATUS_E_INVAL;
194 		goto out;
195 	}
196 
197 	if (!vdev) {
198 		pmo_err("vdev is NULL");
199 		status = QDF_STATUS_E_INVAL;
200 		goto out;
201 	}
202 
203 	status = pmo_vdev_get_ref(vdev);
204 	if (status != QDF_STATUS_SUCCESS)
205 		goto out;
206 
207 	opmode = pmo_get_vdev_opmode(vdev);
208 	vdev_id = pmo_vdev_get_id(vdev);
209 	pmo_debug("vdev opmode: %d vdev_id: %d", opmode, vdev_id);
210 	if (!pmo_core_is_vdev_supports_offload(vdev)) {
211 		pmo_debug("vdev in invalid opmode for caching gtk request %d",
212 			opmode);
213 		status = QDF_STATUS_E_INVAL;
214 		goto dec_ref;
215 	}
216 
217 	status = pmo_core_cache_gtk_req_in_vdev_priv(vdev, gtk_req);
218 dec_ref:
219 	wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
220 out:
221 
222 	return status;
223 }
224 
pmo_core_flush_gtk_offload_req(struct wlan_objmgr_vdev * vdev)225 QDF_STATUS pmo_core_flush_gtk_offload_req(struct wlan_objmgr_vdev *vdev)
226 {
227 	enum QDF_OPMODE opmode;
228 	uint8_t vdev_id;
229 	QDF_STATUS status;
230 
231 	if (!vdev) {
232 		pmo_err("psoc is NULL");
233 		status = QDF_STATUS_E_INVAL;
234 		goto out;
235 	}
236 
237 	status = pmo_vdev_get_ref(vdev);
238 	if (status != QDF_STATUS_SUCCESS)
239 		goto out;
240 
241 	opmode = pmo_get_vdev_opmode(vdev);
242 	vdev_id = pmo_vdev_get_id(vdev);
243 	pmo_debug("vdev opmode: %d vdev_id: %d", opmode, vdev_id);
244 	if (!pmo_core_is_vdev_supports_offload(vdev)) {
245 		pmo_debug("vdev in invalid opmode for flushing gtk request %d",
246 			opmode);
247 		status = QDF_STATUS_E_INVAL;
248 		goto dec_ref;
249 	}
250 
251 	status = pmo_core_flush_gtk_req_from_vdev_priv(vdev);
252 dec_ref:
253 	wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
254 out:
255 
256 	return status;
257 }
258 
pmo_core_enable_gtk_offload_in_fwr(struct wlan_objmgr_vdev * vdev)259 QDF_STATUS pmo_core_enable_gtk_offload_in_fwr(struct wlan_objmgr_vdev *vdev)
260 {
261 	QDF_STATUS status;
262 	struct pmo_vdev_priv_obj *vdev_ctx;
263 	struct pmo_gtk_req *op_gtk_req = NULL;
264 
265 	pmo_enter();
266 	if (!vdev) {
267 		pmo_err("vdev is NULL");
268 		status = QDF_STATUS_E_INVAL;
269 		goto out;
270 	}
271 
272 	status = pmo_vdev_get_ref(vdev);
273 	if (status != QDF_STATUS_SUCCESS)
274 		goto out;
275 
276 	vdev_ctx = pmo_vdev_get_priv(vdev);
277 
278 	op_gtk_req = qdf_mem_malloc(sizeof(*op_gtk_req));
279 	if (!op_gtk_req) {
280 		status = QDF_STATUS_E_INVAL;
281 		goto dec_ref;
282 	}
283 	status = pmo_core_do_enable_gtk_offload(vdev, vdev_ctx, op_gtk_req);
284 dec_ref:
285 	wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
286 out:
287 	if (op_gtk_req)
288 		qdf_mem_free(op_gtk_req);
289 	pmo_exit();
290 
291 	return status;
292 }
293 
pmo_core_disable_gtk_offload_in_fwr(struct wlan_objmgr_vdev * vdev)294 QDF_STATUS pmo_core_disable_gtk_offload_in_fwr(struct wlan_objmgr_vdev *vdev)
295 {
296 	QDF_STATUS status;
297 	struct pmo_vdev_priv_obj *vdev_ctx;
298 	struct pmo_gtk_req *op_gtk_req = NULL;
299 
300 	pmo_enter();
301 	if (!vdev) {
302 		pmo_err("vdev is NULL");
303 		status = QDF_STATUS_E_INVAL;
304 		goto out;
305 	}
306 
307 	status = pmo_vdev_get_ref(vdev);
308 	if (status != QDF_STATUS_SUCCESS)
309 		goto out;
310 
311 	vdev_ctx = pmo_vdev_get_priv(vdev);
312 
313 	op_gtk_req = qdf_mem_malloc(sizeof(*op_gtk_req));
314 	if (!op_gtk_req) {
315 		status = QDF_STATUS_E_NOMEM;
316 		goto dec_ref;
317 	}
318 
319 	status = pmo_core_do_disable_gtk_offload(vdev, vdev_ctx, op_gtk_req);
320 dec_ref:
321 	wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
322 out:
323 	if (op_gtk_req)
324 		qdf_mem_free(op_gtk_req);
325 	pmo_exit();
326 
327 	return status;
328 }
329 
pmo_core_get_gtk_rsp(struct wlan_objmgr_vdev * vdev,struct pmo_gtk_rsp_req * gtk_rsp_req)330 QDF_STATUS pmo_core_get_gtk_rsp(struct wlan_objmgr_vdev *vdev,
331 			struct pmo_gtk_rsp_req *gtk_rsp_req)
332 {
333 	QDF_STATUS status = QDF_STATUS_SUCCESS;
334 	struct pmo_vdev_priv_obj *vdev_ctx;
335 
336 	pmo_enter();
337 	if (!gtk_rsp_req || !vdev) {
338 		pmo_err("%s is null", !vdev ? "vdev":"gtk_rsp_req");
339 		status = QDF_STATUS_E_INVAL;
340 		goto out;
341 	}
342 
343 	status = pmo_vdev_get_ref(vdev);
344 	if (status != QDF_STATUS_SUCCESS)
345 		goto out;
346 
347 	vdev_ctx = pmo_vdev_get_priv(vdev);
348 
349 	status = pmo_core_is_gtk_enabled_in_fwr(vdev, vdev_ctx);
350 	if (status != QDF_STATUS_SUCCESS)
351 		goto dec_ref;
352 
353 	/* cache gtk rsp request */
354 	qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
355 	qdf_mem_copy(&vdev_ctx->vdev_gtk_rsp_req, gtk_rsp_req,
356 		sizeof(vdev_ctx->vdev_gtk_rsp_req));
357 	qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
358 	/* send cmd to fwr */
359 	status = pmo_tgt_get_gtk_rsp(vdev);
360 dec_ref:
361 	wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
362 out:
363 	pmo_exit();
364 
365 	return status;
366 }
367 
368