1 /*
2  * Copyright (c) 2017-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 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  * DOC: Implements arp offload feature API's
21  */
22 
23 #include "wlan_pmo_arp.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_arp_in_vdev_priv(struct pmo_arp_req * arp_req,struct wlan_objmgr_vdev * vdev)28 static QDF_STATUS pmo_core_cache_arp_in_vdev_priv(
29 			struct pmo_arp_req *arp_req,
30 			struct wlan_objmgr_vdev *vdev)
31 {
32 	QDF_STATUS status = QDF_STATUS_SUCCESS;
33 	struct pmo_arp_offload_params *request = NULL;
34 	struct pmo_vdev_priv_obj *vdev_ctx;
35 	int index;
36 	struct qdf_mac_addr peer_bssid;
37 
38 	vdev_ctx = pmo_vdev_get_priv(vdev);
39 
40 	request = qdf_mem_malloc(sizeof(*request));
41 	if (!request) {
42 		status = QDF_STATUS_E_NOMEM;
43 		goto exit_with_status;
44 	}
45 
46 	status = pmo_get_vdev_bss_peer_mac_addr(vdev, &peer_bssid);
47 	if (status != QDF_STATUS_SUCCESS)
48 		goto free_req;
49 
50 	qdf_mem_copy(&request->bssid.bytes, &peer_bssid.bytes,
51 			QDF_MAC_ADDR_SIZE);
52 	pmo_debug("vdev self mac addr: "QDF_MAC_ADDR_FMT" bss peer mac addr: "QDF_MAC_ADDR_FMT,
53 		QDF_MAC_ADDR_REF(wlan_vdev_mlme_get_macaddr(vdev)),
54 		QDF_MAC_ADDR_REF(peer_bssid.bytes));
55 
56 	request->enable = PMO_OFFLOAD_ENABLE;
57 	request->is_offload_applied = false;
58 	/* converting u32 to IPV4 address */
59 	for (index = 0; index < QDF_IPV4_ADDR_SIZE; index++)
60 		request->host_ipv4_addr[index] =
61 		(arp_req->ipv4_addr >> (index * 8)) & 0xFF;
62 
63 	/* cache arp request */
64 	qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
65 	qdf_mem_copy(&vdev_ctx->vdev_arp_req, request,
66 		     sizeof(vdev_ctx->vdev_arp_req));
67 	qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
68 
69 	pmo_debug("cached arp offload; addr:" QDF_IPV4_ADDR_STR ", enable:%d",
70 		  QDF_IPV4_ADDR_ARRAY(request->host_ipv4_addr),
71 		  request->enable);
72 
73 free_req:
74 	qdf_mem_free(request);
75 
76 exit_with_status:
77 
78 	return status;
79 }
80 
pmo_core_flush_arp_from_vdev_priv(struct wlan_objmgr_vdev * vdev)81 static QDF_STATUS pmo_core_flush_arp_from_vdev_priv(
82 			struct wlan_objmgr_vdev *vdev)
83 {
84 	struct pmo_vdev_priv_obj *vdev_ctx;
85 
86 	pmo_enter();
87 
88 	vdev_ctx = pmo_vdev_get_priv(vdev);
89 
90 	/* clear arp request */
91 	qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
92 	qdf_mem_zero(&vdev_ctx->vdev_arp_req, sizeof(vdev_ctx->vdev_arp_req));
93 	vdev_ctx->vdev_arp_req.enable = PMO_OFFLOAD_DISABLE;
94 	vdev_ctx->vdev_arp_req.is_offload_applied = false;
95 	qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
96 
97 	pmo_exit();
98 
99 	return QDF_STATUS_SUCCESS;
100 }
101 
102 static QDF_STATUS
pmo_core_do_enable_arp_offload(struct wlan_objmgr_vdev * vdev,uint8_t vdev_id,enum pmo_offload_trigger trigger)103 pmo_core_do_enable_arp_offload(struct wlan_objmgr_vdev *vdev,
104 			       uint8_t vdev_id,
105 			       enum pmo_offload_trigger trigger)
106 {
107 	QDF_STATUS status;
108 	struct pmo_psoc_priv_obj *psoc_ctx;
109 	struct pmo_vdev_priv_obj *vdev_ctx;
110 
111 	vdev_ctx = pmo_vdev_get_priv(vdev);
112 
113 	psoc_ctx = vdev_ctx->pmo_psoc_ctx;
114 	if (!psoc_ctx) {
115 		pmo_err("psoc_ctx is NULL");
116 		status = QDF_STATUS_E_NULL_VALUE;
117 		goto out;
118 	}
119 
120 	switch (trigger) {
121 	case pmo_ipv4_change_notify:
122 		if (!psoc_ctx->psoc_cfg.active_mode_offload) {
123 			pmo_debug("active offload is disabled, skip in mode %d",
124 				  trigger);
125 			status = QDF_STATUS_SUCCESS;
126 			goto out;
127 		}
128 		/* enable arp when active offload is true (ipv4 notifier) */
129 		status = pmo_tgt_enable_arp_offload_req(vdev, vdev_id);
130 		break;
131 	case pmo_apps_suspend:
132 	case pmo_arp_ns_offload_dynamic_update:
133 		/* enable arp when active offload is false (apps suspend) */
134 		status = pmo_tgt_enable_arp_offload_req(vdev, vdev_id);
135 		break;
136 	default:
137 		status = QDF_STATUS_E_INVAL;
138 		pmo_err("invalid pmo trigger");
139 		break;
140 	}
141 out:
142 
143 	return status;
144 }
145 
pmo_core_do_disable_arp_offload(struct wlan_objmgr_vdev * vdev,uint8_t vdev_id,enum pmo_offload_trigger trigger)146 static QDF_STATUS pmo_core_do_disable_arp_offload(struct wlan_objmgr_vdev *vdev,
147 		uint8_t vdev_id, enum pmo_offload_trigger trigger)
148 {
149 	QDF_STATUS status = QDF_STATUS_SUCCESS;
150 	struct pmo_psoc_priv_obj *psoc_ctx;
151 	struct pmo_vdev_priv_obj *vdev_ctx;
152 
153 	pmo_enter();
154 
155 	vdev_ctx = pmo_vdev_get_priv(vdev);
156 
157 	psoc_ctx = vdev_ctx->pmo_psoc_ctx;
158 	if (!psoc_ctx) {
159 		pmo_err("psoc_ctx is NULL");
160 		status = QDF_STATUS_E_NULL_VALUE;
161 		goto out;
162 	}
163 
164 	switch (trigger) {
165 	case pmo_apps_resume:
166 	case pmo_arp_ns_offload_dynamic_update:
167 		/* disable arp on apps resume when active offload is disable */
168 		status = pmo_tgt_disable_arp_offload_req(vdev, vdev_id);
169 		break;
170 	default:
171 		status = QDF_STATUS_E_INVAL;
172 		pmo_err("invalid pmo trigger");
173 		break;
174 	}
175 out:
176 	pmo_exit();
177 
178 	return status;
179 }
180 
pmo_core_arp_offload_sanity(struct wlan_objmgr_vdev * vdev)181 static QDF_STATUS pmo_core_arp_offload_sanity(
182 			struct wlan_objmgr_vdev *vdev)
183 {
184 	struct pmo_vdev_priv_obj *vdev_ctx;
185 
186 	if (!vdev) {
187 		pmo_err("vdev is NULL");
188 		return QDF_STATUS_E_NULL_VALUE;
189 	}
190 
191 	vdev_ctx = pmo_vdev_get_priv(vdev);
192 	if (!vdev_ctx->pmo_psoc_ctx->psoc_cfg.arp_offload_enable) {
193 		pmo_err("user disabled arp offload using ini");
194 		return QDF_STATUS_E_INVAL;
195 	}
196 
197 	if (!pmo_core_is_vdev_supports_offload(vdev)) {
198 		pmo_debug("vdev in invalid opmode for arp offload %d",
199 			pmo_get_vdev_opmode(vdev));
200 		return QDF_STATUS_E_INVAL;
201 	}
202 
203 	if (wlan_vdev_is_up(vdev) != QDF_STATUS_SUCCESS)
204 		return QDF_STATUS_E_INVAL;
205 
206 	return QDF_STATUS_SUCCESS;
207 }
208 
pmo_core_arp_check_offload(struct wlan_objmgr_psoc * psoc,enum pmo_offload_trigger trigger,uint8_t vdev_id)209 QDF_STATUS pmo_core_arp_check_offload(struct wlan_objmgr_psoc *psoc,
210 				      enum pmo_offload_trigger trigger,
211 				      uint8_t vdev_id)
212 {
213 	QDF_STATUS status = QDF_STATUS_SUCCESS;
214 	struct pmo_psoc_priv_obj *psoc_ctx;
215 	struct pmo_vdev_priv_obj *vdev_ctx;
216 	struct wlan_objmgr_vdev *vdev;
217 	bool active_offload_cond, is_applied_cond;
218 	enum QDF_OPMODE opmode;
219 
220 	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, WLAN_PMO_ID);
221 	if (!vdev) {
222 		pmo_err("vdev is NULL");
223 		status = QDF_STATUS_E_INVAL;
224 		goto out;
225 	}
226 
227 	opmode = pmo_get_vdev_opmode(vdev);
228 	if (opmode == QDF_NDI_MODE) {
229 		pmo_debug("ARP offload is not supported in NaN mode");
230 		wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
231 		return QDF_STATUS_E_INVAL;
232 	}
233 
234 	if (wlan_vdev_mlme_get_is_mlo_link(psoc, vdev_id)) {
235 		pmo_debug("ARP offload not supported for MLO partner link "
236 			  "with vdev_id[%d]", vdev_id);
237 		wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
238 		return QDF_STATUS_E_INVAL;
239 	}
240 
241 	vdev_ctx = pmo_vdev_get_priv(vdev);
242 	psoc_ctx = vdev_ctx->pmo_psoc_ctx;
243 
244 	if (trigger == pmo_apps_suspend || trigger == pmo_apps_resume) {
245 		active_offload_cond = psoc_ctx->psoc_cfg.active_mode_offload;
246 
247 		qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
248 		is_applied_cond = vdev_ctx->vdev_arp_req.enable &&
249 				  vdev_ctx->vdev_arp_req.is_offload_applied;
250 		qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
251 
252 		if (active_offload_cond && is_applied_cond) {
253 			pmo_debug("active offload is enabled and offload already sent");
254 			wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
255 			return QDF_STATUS_E_INVAL;
256 		}
257 	}
258 	wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
259 out:
260 	return status;
261 }
262 
pmo_core_cache_arp_offload_req(struct pmo_arp_req * arp_req)263 QDF_STATUS pmo_core_cache_arp_offload_req(struct pmo_arp_req *arp_req)
264 {
265 	QDF_STATUS status;
266 	struct wlan_objmgr_vdev *vdev;
267 
268 	if (!arp_req) {
269 		pmo_err("arp_req is NULL");
270 		status = QDF_STATUS_E_INVAL;
271 		goto out;
272 	}
273 
274 	if (!arp_req->psoc) {
275 		pmo_err("psoc is NULL");
276 		status = QDF_STATUS_E_INVAL;
277 		goto out;
278 	}
279 
280 	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(arp_req->psoc,
281 						    arp_req->vdev_id,
282 						    WLAN_PMO_ID);
283 	if (!vdev) {
284 		pmo_err("vdev is NULL");
285 		status = QDF_STATUS_E_INVAL;
286 		goto out;
287 	}
288 
289 	status = pmo_core_arp_offload_sanity(vdev);
290 	if (status != QDF_STATUS_SUCCESS)
291 		goto dec_ref;
292 
293 	pmo_debug("Cache arp for vdev id: %d psoc: %pK vdev: %pK",
294 			arp_req->vdev_id, arp_req->psoc, vdev);
295 
296 	status = pmo_core_cache_arp_in_vdev_priv(arp_req, vdev);
297 dec_ref:
298 	wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
299 out:
300 
301 	return status;
302 }
303 
pmo_core_flush_arp_offload_req(struct wlan_objmgr_vdev * vdev)304 QDF_STATUS pmo_core_flush_arp_offload_req(struct wlan_objmgr_vdev *vdev)
305 {
306 	QDF_STATUS status;
307 	uint8_t vdev_id;
308 
309 	pmo_enter();
310 	if (!vdev) {
311 		pmo_err("vdev is NULL");
312 		status = QDF_STATUS_E_NULL_VALUE;
313 		goto out;
314 	}
315 
316 	status = pmo_vdev_get_ref(vdev);
317 	if (status != QDF_STATUS_SUCCESS)
318 		goto out;
319 
320 	status = pmo_core_arp_offload_sanity(vdev);
321 	if (status != QDF_STATUS_SUCCESS)
322 		goto def_ref;
323 
324 	vdev_id = pmo_vdev_get_id(vdev);
325 	pmo_debug("Flush arp for vdev id: %d vdev: %pK", vdev_id, vdev);
326 
327 	status = pmo_core_flush_arp_from_vdev_priv(vdev);
328 def_ref:
329 	wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
330 out:
331 	pmo_exit();
332 
333 	return status;
334 }
335 
pmo_core_enable_arp_offload_in_fwr(struct wlan_objmgr_vdev * vdev,enum pmo_offload_trigger trigger)336 QDF_STATUS pmo_core_enable_arp_offload_in_fwr(struct wlan_objmgr_vdev *vdev,
337 		enum pmo_offload_trigger trigger)
338 {
339 	QDF_STATUS status;
340 	uint8_t vdev_id;
341 
342 	if (!vdev) {
343 		pmo_err("vdev is NULL");
344 		status = QDF_STATUS_E_NULL_VALUE;
345 		goto out;
346 	}
347 
348 	status = pmo_vdev_get_ref(vdev);
349 	if (status != QDF_STATUS_SUCCESS)
350 		goto out;
351 
352 	status = pmo_core_arp_offload_sanity(vdev);
353 	if (status != QDF_STATUS_SUCCESS)
354 		goto put_ref;
355 
356 	vdev_id = pmo_vdev_get_id(vdev);
357 	pmo_debug("Enable arp offload in fwr vdev id: %d vdev: %pK",
358 		vdev_id, vdev);
359 
360 	status = pmo_core_do_enable_arp_offload(vdev, vdev_id, trigger);
361 
362 put_ref:
363 	wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
364 out:;
365 
366 	return status;
367 }
368 
pmo_core_disable_arp_offload_in_fwr(struct wlan_objmgr_vdev * vdev,enum pmo_offload_trigger trigger)369 QDF_STATUS pmo_core_disable_arp_offload_in_fwr(struct wlan_objmgr_vdev *vdev,
370 		enum pmo_offload_trigger trigger)
371 {
372 	QDF_STATUS status;
373 	uint8_t vdev_id;
374 
375 	if (!vdev) {
376 		pmo_err("vdev is NULL");
377 		status = QDF_STATUS_E_NULL_VALUE;
378 		goto out;
379 	}
380 
381 	status = pmo_vdev_get_ref(vdev);
382 	if (status != QDF_STATUS_SUCCESS)
383 		goto out;
384 
385 	status = pmo_core_arp_offload_sanity(vdev);
386 	if (status != QDF_STATUS_SUCCESS)
387 		goto def_ref;
388 
389 	vdev_id = pmo_vdev_get_id(vdev);
390 	pmo_debug("Disable arp offload in fwr vdev id: %d vdev: %pK",
391 		vdev_id, vdev);
392 
393 	status = pmo_core_do_disable_arp_offload(vdev, vdev_id, trigger);
394 def_ref:
395 	wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
396 out:
397 
398 	return status;
399 }
400 
401 QDF_STATUS
pmo_core_get_arp_offload_params(struct wlan_objmgr_vdev * vdev,struct pmo_arp_offload_params * params)402 pmo_core_get_arp_offload_params(struct wlan_objmgr_vdev *vdev,
403 				struct pmo_arp_offload_params *params)
404 {
405 	QDF_STATUS status;
406 	struct pmo_vdev_priv_obj *vdev_ctx;
407 	uint8_t vdev_id;
408 
409 	pmo_enter();
410 
411 	if (!params)
412 		return QDF_STATUS_E_INVAL;
413 
414 	qdf_mem_zero(params, sizeof(*params));
415 
416 	if (!vdev) {
417 		pmo_err("vdev is NULL");
418 		status = QDF_STATUS_E_NULL_VALUE;
419 		goto out;
420 	}
421 
422 	status = pmo_vdev_get_ref(vdev);
423 	if (status != QDF_STATUS_SUCCESS)
424 		goto out;
425 
426 	status = pmo_core_arp_offload_sanity(vdev);
427 	if (status != QDF_STATUS_SUCCESS)
428 		goto put_ref;
429 
430 	vdev_id = pmo_vdev_get_id(vdev);
431 	vdev_ctx = pmo_vdev_get_priv(vdev);
432 	qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
433 	*params = vdev_ctx->vdev_arp_req;
434 	qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
435 
436 put_ref:
437 	wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
438 out:
439 	pmo_exit();
440 
441 	return status;
442 }
443