blob: be4bb2dab22da9663b81916889de2901d7add921 [file] [log] [blame]
Mukul Sharmad75a6672017-06-22 15:40:53 +05301/*
2* Copyright (c) 2017 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 * DOC: Implements mc addr filtering offload feature API's
20 */
21
22#include "wlan_pmo_mc_addr_filtering.h"
23#include "wlan_pmo_tgt_api.h"
24#include "wlan_pmo_main.h"
25#include "wlan_pmo_obj_mgmt_public_struct.h"
26
27
28#define PMO_INVALID_MC_ADDR_COUNT (-1)
29
30static void pmo_core_fill_mc_list(struct pmo_vdev_priv_obj **vdev_ctx,
31 struct pmo_mc_addr_list_params *ip)
32{
33 struct pmo_mc_addr_list *op_list;
34 int i;
35 static const uint8_t ipv6_rs[] = {
36 0x33, 0x33, 0x00, 0x00, 0x00, 0x02};
37 struct pmo_vdev_priv_obj *temp_ctx;
38 uint8_t addr_fp;
39
40 temp_ctx = *vdev_ctx;
41 addr_fp = temp_ctx->addr_filter_pattern;
42 op_list = &temp_ctx->vdev_mc_list_req;
43
44 qdf_spin_lock_bh(&temp_ctx->pmo_vdev_lock);
45 op_list->mc_cnt = ip->count;
46 qdf_spin_unlock_bh(&temp_ctx->pmo_vdev_lock);
47
48 for (i = 0; i < ip->count; i++) {
49 pmo_debug("%pM", ip->mc_addr[i].bytes);
50 /*
51 * Skip following addresses:
52 * 1)IPv6 router solicitation address
53 * 2)Any other address pattern if its set during
54 * RXFILTER REMOVE driver command based on
55 * addr_filter_pattern
56 */
57 if ((!qdf_mem_cmp(ip->mc_addr[i].bytes, ipv6_rs,
58 QDF_MAC_ADDR_SIZE)) ||
59 (addr_fp &&
60 (!qdf_mem_cmp(ip->mc_addr[i].bytes, &addr_fp, 1)))) {
61 pmo_debug("MC/BC filtering Skip addr %pM",
62 ip->mc_addr[i].bytes);
63 qdf_spin_lock_bh(&temp_ctx->pmo_vdev_lock);
64 op_list->mc_cnt--;
65 qdf_spin_unlock_bh(&temp_ctx->pmo_vdev_lock);
66 continue;
67 }
68 qdf_spin_lock_bh(&temp_ctx->pmo_vdev_lock);
69 qdf_mem_set(&(op_list->mc_addr[i].bytes), 0,
70 QDF_MAC_ADDR_SIZE);
71 qdf_mem_copy(&(op_list->mc_addr[i].bytes),
72 ip->mc_addr[i].bytes, QDF_MAC_ADDR_SIZE);
73 qdf_spin_unlock_bh(&temp_ctx->pmo_vdev_lock);
74 pmo_debug("mlist[%pM] = ", op_list->mc_addr[i].bytes);
75 }
76}
77
78static QDF_STATUS pmo_core_cache_mc_addr_list_in_vdev_priv(
79 struct pmo_mc_addr_list_params *mc_list_config,
80 struct wlan_objmgr_vdev *vdev)
81{
82 struct pmo_vdev_priv_obj *vdev_ctx;
83
84 vdev_ctx = pmo_vdev_get_priv(vdev);
85 pmo_core_fill_mc_list(&vdev_ctx, mc_list_config);
86
87 return QDF_STATUS_SUCCESS;
88}
89
90static QDF_STATUS pmo_core_flush_mc_addr_list_from_vdev_priv(
91 struct wlan_objmgr_vdev *vdev)
92{
93 struct pmo_vdev_priv_obj *vdev_ctx;
94
95 vdev_ctx = pmo_vdev_get_priv(vdev);
96
97 qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
98 qdf_mem_zero(&vdev_ctx->vdev_mc_list_req,
99 sizeof(vdev_ctx->vdev_mc_list_req));
100 qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
101
102 return QDF_STATUS_SUCCESS;
103}
104
105QDF_STATUS pmo_core_set_mc_filter_req(struct wlan_objmgr_vdev *vdev,
106 struct pmo_mc_addr_list *mc_list)
107{
108 uint8_t vdev_id;
109 int i;
110
111 PMO_ENTER();
112
113 vdev_id = pmo_vdev_get_id(vdev);
114 /*
115 * Configure enhance multicast offload feature for filtering out
116 * multicast IP data packets transmitted using unicast MAC address
117 */
118
119 /*
120 * TODO
121 {//(WMI_SERVICE_IS_ENABLED(wma_handle->wmi_service_bitmap,
122 //WMI_SERVICE_ENHANCED_MCAST_FILTER)) {
123 */
124 if (1) {
125 pmo_info("FW supports enhance multicast offload");
126 pmo_tgt_send_enhance_multicast_offload_req(vdev, vdev_id,
127 false);
128 } else {
129 pmo_info("FW does not support enhance multicast offload");
130 }
131
132 /*
133 * set mc_param->action to clear MCList and reset
134 * to configure the MCList in FW
135 */
136 for (i = 0; i < mc_list->mc_cnt; i++) {
137 pmo_tgt_set_mc_filter_req(vdev,
138 mc_list->mc_addr[i]);
139 }
140
141 PMO_EXIT();
142
143 return QDF_STATUS_SUCCESS;
144}
145
146QDF_STATUS pmo_core_clear_mc_filter_req(struct wlan_objmgr_vdev *vdev,
147 struct pmo_mc_addr_list *mc_list)
148{
149 uint8_t vdev_id;
150 int i;
151
152 PMO_ENTER();
153
154 vdev_id = pmo_vdev_get_id(vdev);
155
156 /*
157 * Configure enhance multicast offload feature for filtering out
158 * multicast IP data packets transmitted using unicast MAC address
159 */
160
161 /*
162 * TODO
163 {//(WMI_SERVICE_IS_ENABLED(wma_handle->wmi_service_bitmap,
164 //WMI_SERVICE_ENHANCED_MCAST_FILTER)) {
165 */
166 if (1) {
167 pmo_info("FW supports enhance multicast offload");
168 pmo_tgt_send_enhance_multicast_offload_req(vdev, vdev_id,
169 true);
170 } else {
171 pmo_info("FW does not support enhance multicast offload");
172 }
173
174 /*
175 * set mcbc_param->action to clear MCList and reset
176 * to configure the MCList in FW
177 */
178 for (i = 0; i < mc_list->mc_cnt; i++) {
179 pmo_tgt_clear_mc_filter_req(vdev, mc_list->mc_addr[i]);
180 }
181
182 PMO_EXIT();
183
184 return QDF_STATUS_SUCCESS;
185}
186
187static QDF_STATUS pmo_core_do_enable_mc_addr_list(struct wlan_objmgr_vdev *vdev,
188 struct pmo_vdev_priv_obj *vdev_ctx,
189 struct pmo_mc_addr_list *op_mc_list_req)
190{
191 QDF_STATUS status;
192
193 PMO_ENTER();
194 qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
195 if (!vdev_ctx->vdev_mc_list_req.mc_cnt) {
196 qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
197 pmo_err("mc_cnt is zero so skip to add mc list");
198 status = QDF_STATUS_E_INVAL;
199 goto out;
200 }
201 qdf_mem_copy(op_mc_list_req, &vdev_ctx->vdev_mc_list_req,
202 sizeof(*op_mc_list_req));
203 qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
204
205 status = pmo_core_set_mc_filter_req(vdev, op_mc_list_req);
206 if (status != QDF_STATUS_SUCCESS) {
207 pmo_err("cannot apply mc filter request");
208 status = QDF_STATUS_E_INVAL;
209 goto out;
210 }
211
212 qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
213 vdev_ctx->vdev_mc_list_req.is_filter_applied = true;
214 qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
215out:
216 PMO_EXIT();
217
218 return status;
219}
220
221static QDF_STATUS pmo_core_do_disable_mc_addr_list(
222 struct wlan_objmgr_vdev *vdev,
223 struct pmo_vdev_priv_obj *vdev_ctx,
224 struct pmo_mc_addr_list *op_mc_list_req)
225{
226 QDF_STATUS status;
227
228 PMO_ENTER();
229 qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
230 /* validate filter is applied before clearing in fwr */
231 if (!vdev_ctx->vdev_mc_list_req.is_filter_applied) {
232 qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
233 pmo_err("mc filter is not applied in fwr");
234 status = QDF_STATUS_E_INVAL;
235 goto out;
236 }
237 qdf_mem_copy(op_mc_list_req, &vdev_ctx->vdev_mc_list_req,
238 sizeof(*op_mc_list_req));
239 qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
240
241 status = pmo_core_clear_mc_filter_req(vdev, op_mc_list_req);
242 if (status != QDF_STATUS_SUCCESS) {
243 pmo_err("cannot apply mc filter request");
244 status = QDF_STATUS_E_INVAL;
245 goto out;
246 }
247
248 qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
249 vdev_ctx->vdev_mc_list_req.is_filter_applied = false;
250 qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
251out:
252 PMO_EXIT();
253
254 return status;
255}
256
257uint8_t pmo_core_max_mc_addr_supported(struct wlan_objmgr_psoc *psoc)
258{
259 return PMO_MAX_MC_ADDR_LIST;
260}
261
262int pmo_core_get_mc_addr_list_count(struct wlan_objmgr_psoc *psoc,
263 uint8_t vdev_id)
264{
265 QDF_STATUS status;
266 struct wlan_objmgr_vdev *vdev;
267 struct pmo_vdev_priv_obj *vdev_ctx;
268 uint8_t mc_cnt;
269
270 vdev = pmo_psoc_get_vdev(psoc, vdev_id);
271 if (!vdev) {
272 pmo_err("vdev is NULL");
273 return PMO_INVALID_MC_ADDR_COUNT;
274 }
275
276 status = pmo_vdev_get_ref(vdev);
277 if (QDF_IS_STATUS_ERROR(status)) {
278 pmo_warn("failed to get vdev reference");
279 return PMO_INVALID_MC_ADDR_COUNT;
280 }
281
282 vdev_ctx = pmo_vdev_get_priv(vdev);
283 qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
284 mc_cnt = vdev_ctx->vdev_mc_list_req.mc_cnt;
285 qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
286
287 pmo_vdev_put_ref(vdev);
288
289 return mc_cnt;
290}
291
292void pmo_core_set_mc_addr_list_count(struct wlan_objmgr_psoc *psoc,
293 uint8_t vdev_id, uint8_t count)
294{
295 QDF_STATUS status;
296 struct pmo_vdev_priv_obj *vdev_ctx;
297 struct wlan_objmgr_vdev *vdev;
298
299 vdev = pmo_psoc_get_vdev(psoc, vdev_id);
300 if (!vdev) {
301 pmo_err("vdev is NULL");
302 return;
303 }
304
305 status = pmo_vdev_get_ref(vdev);
306 if (QDF_IS_STATUS_ERROR(status)) {
307 pmo_warn("failed to get vdev reference");
308 return;
309 }
310
311 vdev_ctx = pmo_vdev_get_priv(vdev);
312 qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
313 vdev_ctx->vdev_mc_list_req.mc_cnt = count;
314 qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
315
316 pmo_vdev_put_ref(vdev);
317}
318
319static QDF_STATUS pmo_core_mc_addr_flitering_sanity(
320 struct wlan_objmgr_vdev *vdev)
321{
322 struct pmo_vdev_priv_obj *vdev_ctx;
323
324 if (!vdev) {
325 pmo_err("vdev is NULL");
326 return QDF_STATUS_E_NULL_VALUE;
327 }
328
329 vdev_ctx = pmo_vdev_get_priv(vdev);
330
331 /* Check if INI is enabled or not, otherwise just return */
332 if (!vdev_ctx->pmo_psoc_ctx->psoc_cfg.enable_mc_list) {
333 pmo_info("user disabled mc_addr_list using INI");
334 return QDF_STATUS_E_INVAL;
335 }
336
337 if (!pmo_core_is_vdev_supports_offload(vdev)) {
338 pmo_info("vdev in invalid opmode for mc addr filtering %d",
339 pmo_get_vdev_opmode(vdev));
340 return QDF_STATUS_E_INVAL;
341 }
342
343 if (!pmo_core_is_vdev_connected(vdev))
344 return QDF_STATUS_E_INVAL;
345
346 return QDF_STATUS_SUCCESS;
347}
348QDF_STATUS pmo_core_cache_mc_addr_list(
349 struct pmo_mc_addr_list_params *mc_list_config)
350{
351 struct wlan_objmgr_vdev *vdev;
352 QDF_STATUS status;
353
354 PMO_ENTER();
355
356 if (!mc_list_config->psoc) {
357 pmo_err("psoc is NULL");
358 status = QDF_STATUS_E_NULL_VALUE;
359 goto out;
360 }
361
362 vdev = pmo_psoc_get_vdev(mc_list_config->psoc, mc_list_config->vdev_id);
363 if (!vdev) {
364 pmo_err("vdev is NULL");
365 status = QDF_STATUS_E_NULL_VALUE;
366 goto out;
367 }
368
369 status = pmo_vdev_get_ref(vdev);
370 if (QDF_IS_STATUS_ERROR(status)) {
371 pmo_warn("failed to get vdev reference");
372 status = QDF_STATUS_E_INVAL;
373 goto out;
374 }
375
376 status = pmo_core_mc_addr_flitering_sanity(vdev);
377 if (status != QDF_STATUS_SUCCESS)
378 goto dec_ref;
379
380 pmo_info("Cache mc addr list for vdev id: %d psoc: %p vdev: %p",
381 mc_list_config->vdev_id, mc_list_config->psoc, vdev);
382
383 status = pmo_core_cache_mc_addr_list_in_vdev_priv(mc_list_config, vdev);
384dec_ref:
385 pmo_vdev_put_ref(vdev);
386out:
387 PMO_EXIT();
388
389 return status;
390}
391
392QDF_STATUS pmo_core_flush_mc_addr_list(struct wlan_objmgr_psoc *psoc,
393 uint8_t vdev_id)
394{
395 struct wlan_objmgr_vdev *vdev;
396 QDF_STATUS status = QDF_STATUS_SUCCESS;
397
398 PMO_ENTER();
399 if (!psoc) {
400 pmo_err("psoc is NULL");
401 status = QDF_STATUS_E_NULL_VALUE;
402 goto out;
403 }
404
405 vdev = pmo_psoc_get_vdev(psoc, vdev_id);
406 if (!vdev) {
407 pmo_err("vdev is NULL");
408 status = QDF_STATUS_E_NULL_VALUE;
409 goto out;
410 }
411
412 status = pmo_vdev_get_ref(vdev);
413 if (QDF_IS_STATUS_ERROR(status)) {
414 pmo_warn("failed to get vdev reference");
415 status = QDF_STATUS_E_INVAL;
416 goto out;
417 }
418
419 status = pmo_core_mc_addr_flitering_sanity(vdev);
420 if (status != QDF_STATUS_SUCCESS)
421 goto dec_ref;
422
423 pmo_info("Flush mc addr list for vdev id: %d psoc: %p vdev: %p",
424 vdev_id, psoc, vdev);
425
426 status = pmo_core_flush_mc_addr_list_from_vdev_priv(vdev);
427
428dec_ref:
429 pmo_vdev_put_ref(vdev);
430out:
431 PMO_EXIT();
432
433 return status;
434}
435
436static QDF_STATUS pmo_core_handle_enable_mc_list_trigger(
437 struct wlan_objmgr_vdev *vdev,
438 enum pmo_offload_trigger trigger)
439{
440 struct pmo_vdev_priv_obj *vdev_ctx;
441 QDF_STATUS status;
442 struct pmo_mc_addr_list *op_mc_list_req = NULL;
443
444 vdev_ctx = pmo_vdev_get_priv(vdev);
445
446 op_mc_list_req = qdf_mem_malloc(sizeof(*op_mc_list_req));
447 if (!op_mc_list_req) {
448 pmo_err("op_mc_list_req is NULL");
449 status = QDF_STATUS_E_NULL_VALUE;
450 goto out;
451 }
452
453 switch (trigger) {
454 case pmo_mc_list_change_notify:
455 if (!vdev_ctx->pmo_psoc_ctx->psoc_cfg.active_mode_offload) {
456 pmo_info("active offload is disabled, skip in mode: %d",
457 trigger);
458 status = QDF_STATUS_E_INVAL;
459 goto out;
460 }
461 status = pmo_core_do_enable_mc_addr_list(vdev, vdev_ctx,
462 op_mc_list_req);
463 break;
464 case pmo_apps_suspend:
465 if (vdev_ctx->pmo_psoc_ctx->psoc_cfg.active_mode_offload) {
466 pmo_info("active offload is enabled, skip in mode: %d",
467 trigger);
468 status = QDF_STATUS_E_INVAL;
469 goto out;
470 }
471 status = pmo_core_do_enable_mc_addr_list(vdev, vdev_ctx,
472 op_mc_list_req);
473 break;
474 default:
475 status = QDF_STATUS_E_INVAL;
476 pmo_err("invalid pmo trigger for enable mc list");
477 break;
478 }
479out:
480 if (op_mc_list_req)
481 qdf_mem_free(op_mc_list_req);
482
483 return status;
484}
485
486QDF_STATUS pmo_core_enable_mc_addr_filtering_in_fwr(
487 struct wlan_objmgr_psoc *psoc,
488 uint8_t vdev_id,
489 enum pmo_offload_trigger trigger)
490{
491 QDF_STATUS status;
492 struct wlan_objmgr_vdev *vdev;
493
494 PMO_ENTER();
495 if (!psoc) {
496 pmo_err("psoc is NULL");
497 status = QDF_STATUS_E_NULL_VALUE;
498 goto out;
499 }
500
501 vdev = pmo_psoc_get_vdev(psoc, vdev_id);
502 if (!vdev) {
503 pmo_err("vdev is NULL");
504 status = QDF_STATUS_E_NULL_VALUE;
505 goto out;
506 }
507
508 status = pmo_vdev_get_ref(vdev);
509 if (QDF_IS_STATUS_ERROR(status))
510 goto out;
511
512 status = pmo_core_mc_addr_flitering_sanity(vdev);
513 if (status != QDF_STATUS_SUCCESS)
514 goto dec_ref;
515
516 pmo_info("enable mclist trigger: %d", trigger);
517 status = pmo_core_handle_enable_mc_list_trigger(vdev, trigger);
518dec_ref:
519 pmo_vdev_put_ref(vdev);
520out:
521 PMO_EXIT();
522
523 return status;
524}
525
526static QDF_STATUS pmo_core_handle_disable_mc_list_trigger(
527 struct wlan_objmgr_vdev *vdev,
528 enum pmo_offload_trigger trigger)
529{
530 struct pmo_vdev_priv_obj *vdev_ctx;
531 QDF_STATUS status;
532 struct pmo_mc_addr_list *op_mc_list_req = NULL;
533
534 vdev_ctx = pmo_vdev_get_priv(vdev);
535
536 op_mc_list_req = qdf_mem_malloc(sizeof(*op_mc_list_req));
537 if (!op_mc_list_req) {
538 pmo_err("op_mc_list_req is NULL");
539 status = QDF_STATUS_E_NULL_VALUE;
540 goto out;
541 }
542
543 switch (trigger) {
544 case pmo_peer_disconnect:
545 case pmo_mc_list_change_notify:
546 if (!vdev_ctx->pmo_psoc_ctx->psoc_cfg.active_mode_offload) {
547 pmo_info("active offload is disabled, skip in mode: %d",
548 trigger);
549 status = QDF_STATUS_E_INVAL;
550 goto out;
551 }
552 status = pmo_core_do_disable_mc_addr_list(vdev, vdev_ctx,
553 op_mc_list_req);
554 break;
555 case pmo_apps_resume:
556 if (vdev_ctx->pmo_psoc_ctx->psoc_cfg.active_mode_offload) {
557 pmo_info("active offload is enabled, skip in mode: %d",
558 trigger);
559 status = QDF_STATUS_E_INVAL;
560 goto out;
561 }
562 status = pmo_core_do_disable_mc_addr_list(vdev, vdev_ctx,
563 op_mc_list_req);
564 break;
565 default:
566 status = QDF_STATUS_E_INVAL;
567 pmo_err("invalid pmo trigger for disable mc list");
568 break;
569 }
570out:
571 if (op_mc_list_req)
572 qdf_mem_free(op_mc_list_req);
573
574 return status;
575}
576
577QDF_STATUS pmo_core_disable_mc_addr_filtering_in_fwr(
578 struct wlan_objmgr_psoc *psoc,
579 uint8_t vdev_id,
580 enum pmo_offload_trigger trigger)
581{
582 QDF_STATUS status;
583 struct wlan_objmgr_vdev *vdev;
584
585 PMO_ENTER();
586 if (!psoc) {
587 pmo_err("psoc is NULL");
588 status = QDF_STATUS_E_NULL_VALUE;
589 goto out;
590 }
591
592 vdev = pmo_psoc_get_vdev(psoc, vdev_id);
593 if (!vdev) {
594 pmo_err("vdev is NULL");
595 status = QDF_STATUS_E_NULL_VALUE;
596 goto out;
597 }
598
599 status = pmo_vdev_get_ref(vdev);
600 if (QDF_IS_STATUS_ERROR(status))
601 goto out;
602
603 status = pmo_core_mc_addr_flitering_sanity(vdev);
604 if (status != QDF_STATUS_SUCCESS)
605 goto dec_ref;
606
607 pmo_info("disable mclist trigger: %d", trigger);
608 status = pmo_core_handle_disable_mc_list_trigger(vdev, trigger);
609dec_ref:
610 pmo_vdev_put_ref(vdev);
611out:
612 PMO_EXIT();
613
614 return status;
615}
616