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