1 /*
2 * Copyright (c) 2017-2021 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: Implements ns offload feature API's
22 */
23
24 #include "wlan_pmo_ns.h"
25 #include "wlan_pmo_tgt_api.h"
26 #include "wlan_pmo_main.h"
27 #include "wlan_pmo_obj_mgmt_public_struct.h"
28
pmo_core_fill_ns_addr(struct pmo_ns_offload_params * request,struct pmo_ns_req * ns_req)29 static void pmo_core_fill_ns_addr(struct pmo_ns_offload_params *request,
30 struct pmo_ns_req *ns_req)
31 {
32 int i;
33
34 for (i = 0; i < ns_req->count; i++) {
35 /*
36 * Filling up the request structure
37 * Filling the selfIPv6Addr with solicited address
38 * A Solicited-Node multicast address is created by
39 * taking the last 24 bits of a unicast or anycast
40 * address and appending them to the prefix
41 *
42 * FF02:0000:0000:0000:0000:0001:FFXX:XXXX
43 *
44 * here XX is the unicast/anycast bits
45 */
46 request->self_ipv6_addr[i][0] = 0xFF;
47 request->self_ipv6_addr[i][1] = 0x02;
48 request->self_ipv6_addr[i][11] = 0x01;
49 request->self_ipv6_addr[i][12] = 0xFF;
50 request->self_ipv6_addr[i][13] =
51 ns_req->ipv6_addr[i][13];
52 request->self_ipv6_addr[i][14] =
53 ns_req->ipv6_addr[i][14];
54 request->self_ipv6_addr[i][15] =
55 ns_req->ipv6_addr[i][15];
56 request->slot_idx = i;
57 qdf_mem_copy(&request->target_ipv6_addr[i],
58 &ns_req->ipv6_addr[i][0], QDF_IPV6_ADDR_SIZE);
59
60 request->target_ipv6_addr_valid[i] =
61 PMO_IPV6_ADDR_VALID;
62 request->target_ipv6_addr_ac_type[i] =
63 ns_req->ipv6_addr_type[i];
64
65 request->scope[i] = ns_req->scope[i];
66
67 pmo_debug("NSoffload solicitIp: %pI6 targetIp: %pI6 Index: %d",
68 &request->self_ipv6_addr[i],
69 &request->target_ipv6_addr[i], i);
70 }
71 }
72
pmo_core_cache_ns_in_vdev_priv(struct pmo_ns_req * ns_req,struct wlan_objmgr_vdev * vdev)73 static QDF_STATUS pmo_core_cache_ns_in_vdev_priv(
74 struct pmo_ns_req *ns_req,
75 struct wlan_objmgr_vdev *vdev)
76 {
77 QDF_STATUS status = QDF_STATUS_SUCCESS;
78 struct pmo_vdev_priv_obj *vdev_ctx;
79 struct pmo_ns_offload_params *request;
80 struct wlan_objmgr_peer *peer;
81 uint8_t *self_addr, *peer_addr;
82
83 vdev_ctx = pmo_vdev_get_priv(vdev);
84
85 request = qdf_mem_malloc(sizeof(*request));
86 if (!request) {
87 pmo_err("malloc failed for offload params");
88 return QDF_STATUS_E_NOMEM;
89 }
90
91 pmo_core_fill_ns_addr(request, ns_req);
92
93 request->enable = PMO_OFFLOAD_ENABLE;
94 request->is_offload_applied = false;
95
96 /* set number of ns offload address count */
97 request->num_ns_offload_count = ns_req->count;
98
99 peer = wlan_objmgr_vdev_try_get_bsspeer(vdev, WLAN_PMO_ID);
100 if (!peer) {
101 pmo_err("peer is null");
102 status = QDF_STATUS_E_INVAL;
103 goto out;
104 }
105
106 if (wlan_vdev_mlme_is_mlo_vdev(vdev)) {
107 self_addr = wlan_vdev_mlme_get_mldaddr(vdev);
108 peer_addr = wlan_peer_mlme_get_mldaddr(peer);
109 } else {
110 self_addr = wlan_vdev_mlme_get_macaddr(vdev);
111 peer_addr = wlan_peer_get_macaddr(peer);
112 }
113
114 pmo_debug("vdev self mac addr: "QDF_MAC_ADDR_FMT" bss peer mac addr: "QDF_MAC_ADDR_FMT,
115 QDF_MAC_ADDR_REF(self_addr),
116 QDF_MAC_ADDR_REF(peer_addr));
117
118 qdf_mem_copy(&request->self_macaddr.bytes, self_addr,
119 QDF_MAC_ADDR_SIZE);
120 /* get peer and peer mac accdress aka ap mac address */
121 qdf_mem_copy(&request->bssid, peer_addr,
122 QDF_MAC_ADDR_SIZE);
123 wlan_objmgr_peer_release_ref(peer, WLAN_PMO_ID);
124 /* cache ns request */
125 qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
126 qdf_mem_copy(&vdev_ctx->vdev_ns_req, request,
127 sizeof(vdev_ctx->vdev_ns_req));
128 qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
129 out:
130 qdf_mem_free(request);
131 return status;
132 }
133
pmo_core_flush_ns_from_vdev_priv(struct wlan_objmgr_vdev * vdev)134 static QDF_STATUS pmo_core_flush_ns_from_vdev_priv(
135 struct wlan_objmgr_vdev *vdev)
136 {
137 struct pmo_vdev_priv_obj *vdev_ctx;
138
139 pmo_enter();
140
141 vdev_ctx = pmo_vdev_get_priv(vdev);
142
143 /* clear ns request */
144 qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
145 qdf_mem_zero(&vdev_ctx->vdev_ns_req, sizeof(vdev_ctx->vdev_ns_req));
146 vdev_ctx->vdev_ns_req.enable = PMO_OFFLOAD_DISABLE;
147 vdev_ctx->vdev_ns_req.is_offload_applied = false;
148 qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
149
150 pmo_exit();
151
152 return QDF_STATUS_SUCCESS;
153 }
154
pmo_core_do_enable_ns_offload(struct wlan_objmgr_vdev * vdev,uint8_t vdev_id,enum pmo_offload_trigger trigger)155 static QDF_STATUS pmo_core_do_enable_ns_offload(struct wlan_objmgr_vdev *vdev,
156 uint8_t vdev_id, enum pmo_offload_trigger trigger)
157 {
158 QDF_STATUS status = QDF_STATUS_SUCCESS;
159 struct pmo_psoc_priv_obj *psoc_ctx;
160 struct pmo_vdev_priv_obj *vdev_ctx;
161
162 vdev_ctx = pmo_vdev_get_priv(vdev);
163
164 psoc_ctx = vdev_ctx->pmo_psoc_ctx;
165 if (!psoc_ctx) {
166 pmo_err("psoc_ctx is NULL");
167 status = QDF_STATUS_E_INVAL;
168 goto out;
169 }
170
171 switch (trigger) {
172 case pmo_ipv6_change_notify:
173 case pmo_ns_offload_dynamic_update:
174 if (!psoc_ctx->psoc_cfg.active_mode_offload) {
175 pmo_debug("active offload is disabled, skip in mode:%d",
176 trigger);
177 goto out;
178 }
179 /* enable arp when active offload is true (ipv6 notifier) */
180 status = pmo_tgt_enable_ns_offload_req(vdev, vdev_id);
181 break;
182 case pmo_apps_suspend:
183 case pmo_arp_ns_offload_dynamic_update:
184 /* enable arp when active offload is false (apps suspend) */
185 status = pmo_tgt_enable_ns_offload_req(vdev, vdev_id);
186 break;
187 default:
188 status = QDF_STATUS_E_INVAL;
189 pmo_err("invalid pmo trigger");
190 break;
191 }
192 out:
193
194 return status;
195 }
196
pmo_core_do_disable_ns_offload(struct wlan_objmgr_vdev * vdev,uint8_t vdev_id,enum pmo_offload_trigger trigger)197 static QDF_STATUS pmo_core_do_disable_ns_offload(struct wlan_objmgr_vdev *vdev,
198 uint8_t vdev_id, enum pmo_offload_trigger trigger)
199 {
200 QDF_STATUS status = QDF_STATUS_SUCCESS;
201 struct pmo_psoc_priv_obj *psoc_ctx;
202
203 pmo_enter();
204
205 psoc_ctx = pmo_vdev_get_psoc_priv(vdev);
206
207 switch (trigger) {
208 case pmo_ipv6_change_notify:
209 case pmo_ns_offload_dynamic_update:
210 if (!psoc_ctx->psoc_cfg.active_mode_offload) {
211 pmo_debug("active offload is disabled, skip in mode:%d",
212 trigger);
213 goto out;
214 }
215 /* config ns when active offload is enable */
216 status = pmo_tgt_disable_ns_offload_req(vdev, vdev_id);
217 break;
218 case pmo_apps_resume:
219 case pmo_arp_ns_offload_dynamic_update:
220 status = pmo_tgt_disable_ns_offload_req(vdev, vdev_id);
221 break;
222 default:
223 status = QDF_STATUS_E_INVAL;
224 pmo_err("invalid pmo trigger");
225 break;
226 }
227 out:
228 pmo_exit();
229
230 return status;
231 }
232
233
pmo_core_ns_offload_sanity(struct wlan_objmgr_vdev * vdev)234 static QDF_STATUS pmo_core_ns_offload_sanity(struct wlan_objmgr_vdev *vdev)
235 {
236 struct pmo_vdev_priv_obj *vdev_ctx;
237
238 vdev_ctx = pmo_vdev_get_priv(vdev);
239
240 if (!vdev_ctx->pmo_psoc_ctx->psoc_cfg.ns_offload_enable_static) {
241 pmo_debug("ns offload statically disable");
242 return QDF_STATUS_E_INVAL;
243 }
244
245 if (!pmo_core_is_vdev_supports_offload(vdev)) {
246 pmo_debug("vdev in invalid opmode for ns offload %d",
247 pmo_get_vdev_opmode(vdev));
248 return QDF_STATUS_E_INVAL;
249 }
250
251 if (wlan_vdev_is_up(vdev) != QDF_STATUS_SUCCESS)
252 return QDF_STATUS_E_INVAL;
253
254 return QDF_STATUS_SUCCESS;
255 }
256
pmo_core_ns_check_offload(struct wlan_objmgr_psoc * psoc,enum pmo_offload_trigger trigger,uint8_t vdev_id)257 QDF_STATUS pmo_core_ns_check_offload(struct wlan_objmgr_psoc *psoc,
258 enum pmo_offload_trigger trigger,
259 uint8_t vdev_id)
260 {
261 QDF_STATUS status = QDF_STATUS_SUCCESS;
262 struct pmo_psoc_priv_obj *psoc_ctx;
263 struct pmo_vdev_priv_obj *vdev_ctx;
264 struct wlan_objmgr_vdev *vdev;
265 bool active_offload_cond, is_applied_cond;
266 enum QDF_OPMODE opmode;
267
268 vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, WLAN_PMO_ID);
269 if (!vdev) {
270 pmo_err("vdev is NULL");
271 status = QDF_STATUS_E_INVAL;
272 goto out;
273 }
274
275 opmode = pmo_get_vdev_opmode(vdev);
276 if (opmode == QDF_NDI_MODE) {
277 pmo_debug("NS offload not supported in NaN mode");
278 wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
279 return QDF_STATUS_E_INVAL;
280 }
281
282 if (wlan_vdev_mlme_get_is_mlo_link(psoc, vdev_id)) {
283 pmo_debug("NS offload not supported for MLO partner link "
284 "with vdev_id[%d]", vdev_id);
285 wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
286 return QDF_STATUS_E_INVAL;
287 }
288
289 vdev_ctx = pmo_vdev_get_priv(vdev);
290 psoc_ctx = vdev_ctx->pmo_psoc_ctx;
291
292 if (trigger == pmo_apps_suspend || trigger == pmo_apps_resume) {
293 active_offload_cond = psoc_ctx->psoc_cfg.active_mode_offload;
294
295 qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
296 is_applied_cond = vdev_ctx->vdev_ns_req.enable &&
297 vdev_ctx->vdev_ns_req.is_offload_applied;
298 qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
299
300 if (active_offload_cond && is_applied_cond) {
301 pmo_debug("active offload is enabled and offload already sent");
302 wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
303 return QDF_STATUS_E_INVAL;
304 }
305 }
306 wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
307 out:
308 return status;
309 }
310
pmo_core_cache_ns_offload_req(struct pmo_ns_req * ns_req)311 QDF_STATUS pmo_core_cache_ns_offload_req(struct pmo_ns_req *ns_req)
312 {
313 QDF_STATUS status;
314 struct wlan_objmgr_vdev *vdev;
315
316 if (!ns_req) {
317 pmo_err("ns is NULL");
318 status = QDF_STATUS_E_INVAL;
319 goto out;
320 }
321
322 if (!ns_req->psoc) {
323 pmo_err("psoc is NULL");
324 status = QDF_STATUS_E_INVAL;
325 goto out;
326 }
327
328 vdev = wlan_objmgr_get_vdev_by_id_from_psoc(ns_req->psoc,
329 ns_req->vdev_id,
330 WLAN_PMO_ID);
331 if (!vdev) {
332 pmo_err("vdev is NULL");
333 status = QDF_STATUS_E_INVAL;
334 goto out;
335 }
336
337 status = pmo_core_ns_offload_sanity(vdev);
338 if (status != QDF_STATUS_SUCCESS)
339 goto dec_ref;
340
341 if (ns_req->count == 0) {
342 pmo_debug("skip ns offload caching as ns count is 0");
343 status = QDF_STATUS_E_INVAL;
344 goto dec_ref;
345 }
346
347 status = pmo_core_cache_ns_in_vdev_priv(ns_req, vdev);
348 dec_ref:
349 wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
350 out:
351
352 return status;
353 }
354
pmo_core_flush_ns_offload_req(struct wlan_objmgr_vdev * vdev)355 QDF_STATUS pmo_core_flush_ns_offload_req(struct wlan_objmgr_vdev *vdev)
356 {
357 QDF_STATUS status;
358 uint8_t vdev_id;
359
360 pmo_enter();
361 if (!vdev) {
362 pmo_err("vdev is NULL");
363 status = QDF_STATUS_E_INVAL;
364 goto out;
365 }
366
367 status = pmo_vdev_get_ref(vdev);
368 if (status != QDF_STATUS_SUCCESS)
369 goto out;
370
371 status = pmo_core_ns_offload_sanity(vdev);
372 if (status != QDF_STATUS_SUCCESS)
373 goto dec_ref;
374
375 vdev_id = pmo_vdev_get_id(vdev);
376 pmo_debug("Flush ns offload on vdev id: %d vdev: %pK", vdev_id, vdev);
377
378 status = pmo_core_flush_ns_from_vdev_priv(vdev);
379 dec_ref:
380 wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
381 out:
382 pmo_exit();
383
384 return status;
385 }
386
pmo_core_enable_ns_offload_in_fwr(struct wlan_objmgr_vdev * vdev,enum pmo_offload_trigger trigger)387 QDF_STATUS pmo_core_enable_ns_offload_in_fwr(struct wlan_objmgr_vdev *vdev,
388 enum pmo_offload_trigger trigger)
389 {
390 QDF_STATUS status;
391 struct pmo_vdev_priv_obj *vdev_ctx;
392 struct pmo_psoc_priv_obj *pmo_psoc_ctx;
393 uint8_t vdev_id;
394
395 if (!vdev) {
396 pmo_err("vdev is NULL");
397 status = QDF_STATUS_E_INVAL;
398 goto out;
399 }
400
401 status = pmo_vdev_get_ref(vdev);
402 if (status != QDF_STATUS_SUCCESS)
403 goto out;
404
405 vdev_ctx = pmo_vdev_get_priv(vdev);
406 pmo_psoc_ctx = vdev_ctx->pmo_psoc_ctx;
407
408 status = pmo_core_ns_offload_sanity(vdev);
409 if (status != QDF_STATUS_SUCCESS)
410 goto dec_ref;
411
412 if (trigger == pmo_ns_offload_dynamic_update) {
413 /*
414 * user enable ns offload using ioctl/vendor cmd dynamically.
415 */
416 pmo_psoc_ctx->psoc_cfg.ns_offload_enable_dynamic = true;
417 goto skip_ns_dynamic_check;
418 }
419
420 if (!pmo_psoc_ctx->psoc_cfg.ns_offload_enable_dynamic) {
421 pmo_debug("ns offload dynamically disable");
422 status = QDF_STATUS_E_INVAL;
423 goto dec_ref;
424 }
425
426 skip_ns_dynamic_check:
427 qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
428 if (vdev_ctx->vdev_ns_req.num_ns_offload_count == 0) {
429 qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
430 pmo_debug("skip ns offload enable as ns count is 0");
431 status = QDF_STATUS_E_INVAL;
432 goto dec_ref;
433 }
434 qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
435
436 vdev_id = pmo_vdev_get_id(vdev);
437 pmo_debug("Enable ns offload in fwr vdev id: %d vdev: %pK trigger: %d",
438 vdev_id, vdev, trigger);
439 status = pmo_core_do_enable_ns_offload(vdev, vdev_id, trigger);
440 dec_ref:
441 wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
442 out:
443
444 return status;
445 }
446
pmo_core_disable_ns_offload_in_fwr(struct wlan_objmgr_vdev * vdev,enum pmo_offload_trigger trigger)447 QDF_STATUS pmo_core_disable_ns_offload_in_fwr(struct wlan_objmgr_vdev *vdev,
448 enum pmo_offload_trigger trigger)
449 {
450 QDF_STATUS status;
451 uint8_t vdev_id;
452 struct pmo_vdev_priv_obj *vdev_ctx;
453 struct pmo_psoc_priv_obj *pmo_psoc_ctx;
454
455 pmo_enter();
456 if (!vdev) {
457 pmo_err("vdev is NULL");
458 status = QDF_STATUS_E_INVAL;
459 goto out;
460 }
461
462 status = pmo_vdev_get_ref(vdev);
463 if (status != QDF_STATUS_SUCCESS)
464 goto out;
465
466 vdev_ctx = pmo_vdev_get_priv(vdev);
467 pmo_psoc_ctx = vdev_ctx->pmo_psoc_ctx;
468
469 status = pmo_core_ns_offload_sanity(vdev);
470 if (status != QDF_STATUS_SUCCESS)
471 goto dec_ref;
472
473 if (trigger == pmo_ns_offload_dynamic_update) {
474 /*
475 * user disable ns offload using ioctl/vendor cmd dynamically.
476 */
477 if (!pmo_psoc_ctx->psoc_cfg.ns_offload_enable_dynamic) {
478 /* Already disabled, skip to end */
479 status = QDF_STATUS_SUCCESS;
480 goto dec_ref;
481 }
482
483 pmo_psoc_ctx->psoc_cfg.ns_offload_enable_dynamic = false;
484 goto skip_ns_dynamic_check;
485 }
486
487 if (!pmo_psoc_ctx->psoc_cfg.ns_offload_enable_dynamic) {
488 pmo_debug("ns offload dynamically disable");
489 status = QDF_STATUS_E_INVAL;
490 goto dec_ref;
491 }
492
493 skip_ns_dynamic_check:
494 vdev_id = pmo_vdev_get_id(vdev);
495 pmo_debug("disable ns offload in fwr vdev id: %d vdev: %pK trigger: %d",
496 vdev_id, vdev, trigger);
497
498 status = pmo_core_do_disable_ns_offload(vdev, vdev_id, trigger);
499 dec_ref:
500 wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
501 out:
502 pmo_exit();
503
504 return status;
505 }
506
507 QDF_STATUS
pmo_core_get_ns_offload_params(struct wlan_objmgr_vdev * vdev,struct pmo_ns_offload_params * params)508 pmo_core_get_ns_offload_params(struct wlan_objmgr_vdev *vdev,
509 struct pmo_ns_offload_params *params)
510 {
511 QDF_STATUS status;
512 struct pmo_vdev_priv_obj *vdev_ctx;
513
514 pmo_enter();
515
516 if (!params)
517 return QDF_STATUS_E_INVAL;
518
519 qdf_mem_zero(params, sizeof(*params));
520
521 if (!vdev) {
522 pmo_err("vdev is NULL");
523 status = QDF_STATUS_E_INVAL;
524 goto out;
525 }
526
527 status = pmo_vdev_get_ref(vdev);
528 if (status != QDF_STATUS_SUCCESS)
529 goto out;
530
531 vdev_ctx = pmo_vdev_get_priv(vdev);
532
533 status = pmo_core_ns_offload_sanity(vdev);
534 if (status != QDF_STATUS_SUCCESS)
535 goto dec_ref;
536
537 qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
538 *params = vdev_ctx->vdev_ns_req;
539 qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
540
541 dec_ref:
542 wlan_objmgr_vdev_release_ref(vdev, WLAN_PMO_ID);
543 out:
544 pmo_exit();
545
546 return status;
547 }
548