Select one of the symbols to view example projects that use it.
 
Outline
#include <string.h>
#include <inttypes.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_wnm.h"
#include "esp_rrm.h"
#include "esp_mbo.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_mac.h"
#include "nvs_flash.h"
#include <sys/time.h>
#include "regex.h"
#include <stdio.h>
#include "esp_roaming.h"
#include "utils/common.h"
#include "esp_wifi_driver.h"
#include "utils/eloop.h"
#include "rom/ets_sys.h"
#include "common/ieee802_11_defs.h"
g_roaming_app
scan_done_cb_t
scan_results_lock
#define ROAM_SCAN_RESULTS_LOCK
#define ROAM_SCAN_RESULTS_UNLOCK
neighbor_list_lock
#define ROAM_NEIGHBOR_LIST_LOCK
#define ROAM_NEIGHBOR_LIST_UNLOCK
ROAMING_TAG
time_diff_sec(struct timeval *, struct timeval *)
roaming_app_disable_reconnect()
roaming_app_enable_reconnect()
roaming_app_get_ap_info(wifi_ap_record_t *)
legacy_roam_clear_bssid_flag()
initialize_roaming_event()
init_periodic_rrm_event()
init_periodic_scan_roam_event()
roaming_app_disconnected_event_handler(void *, esp_event_base_t, int32_t, void *)
roaming_app_sta_stop_event_handler(void *, esp_event_base_t, int32_t, void *)
roaming_app_connected_event_handler(void *, esp_event_base_t, int32_t, void *)
#define MAX_NEIGHBOR_LEN
get_btm_neighbor_list(uint8_t *, size_t)
roaming_app_neighbor_report_recv_handler(void *, esp_event_base_t, int32_t, void *)
roaming_app_rssi_low_handler(void *, esp_event_base_t, int32_t, void *)
trigger_network_assisted_roam()
trigger_legacy_roam(struct cand_bss *)
roaming_app_trigger_roam(struct cand_bss *)
roaming_app_trigger_roam_internal_handler(void *, void *)
wifi_post_roam_event(struct cand_bss *)
print_ap_records(struct scanned_ap_info *)
periodic_rrm_request(struct timeval *)
candidate_security_match(wifi_ap_record_t)
candidate_profile_match(wifi_ap_record_t)
parse_scan_results_and_roam()
scan_done_event_handler(void *, ETS_STATUS)
conduct_scan()
determine_best_ap(int8_t)
periodic_scan_roam(struct timeval *)
roaming_app_periodic_rrm_internal_handler(void *, void *)
roaming_app_periodic_scan_internal_handler(void *, void *)
validate_scan_chan_list(const char *)
parse_scan_chan_list()
init_scan_params()
init_roaming_app()
deinit_roaming_app()
Files
loading...
SourceVuESP-IDF Framework and ExamplesESP-IDFcomponents/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
/* * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 *//* ... */ #include <string.h> #include <inttypes.h> #include <stdlib.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" #include "esp_wifi.h" #include "esp_wnm.h" #include "esp_rrm.h" #include "esp_mbo.h" #include "esp_event.h" #include "esp_log.h" #include "esp_mac.h" #include "nvs_flash.h" #include <sys/time.h> #include "regex.h" #include <stdio.h> #include "esp_roaming.h" #include "utils/common.h" #include "esp_wifi_driver.h" #include "utils/eloop.h" #include "rom/ets_sys.h" #include "common/ieee802_11_defs.h"23 includes static struct roaming_app g_roaming_app; typedef void (* scan_done_cb_t)(void *arg, ETS_STATUS status); extern int esp_wifi_promiscuous_scan_start(wifi_scan_config_t *config, scan_done_cb_t cb); static void *scan_results_lock = NULL; #define ROAM_SCAN_RESULTS_LOCK() os_mutex_lock(scan_results_lock) #define ROAM_SCAN_RESULTS_UNLOCK() os_mutex_unlock(scan_results_lock) #if PERIODIC_RRM_MONITORING static void *neighbor_list_lock = NULL; #define ROAM_NEIGHBOR_LIST_LOCK() os_mutex_lock(neighbor_list_lock) #define ROAM_NEIGHBOR_LIST_UNLOCK() os_mutex_unlock(neighbor_list_lock)/* ... */ #endif /*PERIODIC_RRM_MONITORING*/ static int wifi_post_roam_event(struct cand_bss *bss); static void determine_best_ap(int8_t rssi_threshold); static const char *ROAMING_TAG = "ROAM"; static inline long time_diff_sec(struct timeval *a, struct timeval *b) { return (a->tv_sec - b->tv_sec); }{ ... } void roaming_app_disable_reconnect(void) { ESP_LOGD(ROAMING_TAG, "Switching off reconnect due to application trigerred disconnect"); g_roaming_app.allow_reconnect = false; }{ ... } void roaming_app_enable_reconnect(void) { ESP_LOGD(ROAMING_TAG, "Switching on reconnect due to application trigerred reconnect"); g_roaming_app.allow_reconnect = true; }{ ... } static void roaming_app_get_ap_info(wifi_ap_record_t *ap_info) { esp_wifi_sta_get_ap_info(ap_info); #if LOW_RSSI_ROAMING_ENABLED /* * If the current rssi is below the configured rssi threshold for * low rssi based roaming and the current rssi threshold is below that, * we should reset the rssi threshold back to the configured rssi threshold *//* ... */ if ((ap_info->rssi > ROAMING_LOW_RSSI_THRESHOLD) && (g_roaming_app.current_low_rssi_threshold < ROAMING_LOW_RSSI_THRESHOLD)) { g_roaming_app.current_low_rssi_threshold = ROAMING_LOW_RSSI_THRESHOLD; esp_wifi_set_rssi_threshold(ROAMING_LOW_RSSI_THRESHOLD); ESP_LOGD(ROAMING_TAG, "Reset the low rssi threshold back to %d", ROAMING_LOW_RSSI_THRESHOLD); }{...} #endif/* ... */ /*LOW_RSSI_ROAMING_ENABLED*/ }{ ... } #if LEGACY_ROAM_ENABLED static void legacy_roam_clear_bssid_flag(void) { wifi_config_t *config = {0}; config = os_zalloc(sizeof(wifi_config_t)); if (!config) { ESP_LOGE(ROAMING_TAG, "failed to allocate memory"); return; }{...} esp_wifi_get_config(WIFI_IF_STA, config); if (config->sta.bssid_set) { config->sta.bssid_set = 0; esp_wifi_set_config(WIFI_IF_STA, config); }{...} os_free(config); ESP_LOGD(ROAMING_TAG, "cleared bssid flag"); }{ ... } /* ... */#endif /*LEGACY_ROAM_ENABLED*/ static int8_t initialize_roaming_event(void) { #if LEGACY_ROAM_ENABLED /* * Resetting the Bssid param as it is possible that a previous force * roam has set config to connect to a specific bssid and now further * roaming attempts using BTM could lead to a spiral of connecting to * the previous AP *//* ... */ if (g_roaming_app.force_roam_ongoing) { legacy_roam_clear_bssid_flag(); }{...} #endif/* ... */ /*LEGACY_ROAM_ENABLED*/ return ESP_OK; }{ ... } #if PERIODIC_RRM_MONITORING static void init_periodic_rrm_event(void) { if (!neighbor_list_lock) { neighbor_list_lock = os_recursive_mutex_create(); if (!neighbor_list_lock) { ESP_LOGE(ROAMING_TAG, "%s: failed to create roaming neighbor list lock", __func__); }{...} }{...} ESP_LOGV(ROAMING_TAG, "Initialised Periodic RRM Monitoring event!"); g_roaming_app.periodic_rrm_active = true; if (eloop_register_timeout(RRM_MONITOR_TIME, 0, roaming_app_periodic_rrm_internal_handler, NULL, NULL)) { ESP_LOGE(ROAMING_TAG, "Could not register periodic neighbor report event."); }{...} }{ ... } /* ... */#endif /*PERIODIC_RRM_MONITORING*/ #if PERIODIC_SCAN_MONITORING static void init_periodic_scan_roam_event(void) { ESP_LOGV(ROAMING_TAG, "Initialised Periodic Scan Roam event!"); g_roaming_app.periodic_scan_active = true; if (eloop_register_timeout(SCAN_MONITOR_INTERVAL, 0, roaming_app_periodic_scan_internal_handler, NULL, NULL)) { ESP_LOGE(ROAMING_TAG, "Could not register periodic scan monitoring event"); }{...} }{ ... } /* ... */#endif /*PERIODIC_SCAN_ROAM_MONITORING*/ static void roaming_app_disconnected_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { #if PERIODIC_RRM_MONITORING g_roaming_app.periodic_rrm_active = false; #endif /*PERIODIC_RRM_MONITORING*/ #if PERIODIC_SCAN_MONITORING g_roaming_app.periodic_scan_active = false; #endif /*PERIODIC_SCAN_MONITORING*/ wifi_event_sta_disconnected_t *disconn = event_data; ESP_LOGD(ROAMING_TAG, "station got disconnected reason=%d", disconn->reason); if (disconn->reason == WIFI_REASON_ROAMING) { ESP_LOGD(ROAMING_TAG, "station roaming, do nothing"); }{...} else if (g_roaming_app.allow_reconnect == false) { ESP_LOGD(ROAMING_TAG, "station initiated disconnect, do nothing"); }{...} else { #if LEGACY_ROAM_ENABLED /* * Resetting the Bssid param as it is possible that a previous force * roam has set config to connect to a specific bssid and now further * roaming attempts using BTM could lead to a spiral of connecting to * the previous AP *//* ... */ if (g_roaming_app.force_roam_ongoing) { legacy_roam_clear_bssid_flag(); }{...} #endif/* ... */ /*LEGACY_ROAM_ENABLED*/ esp_wifi_connect(); }{...} }{ ... } static void roaming_app_sta_stop_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { g_roaming_app.allow_reconnect = false; }{ ... } static void roaming_app_connected_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { #if LOW_RSSI_ROAMING_ENABLED roaming_app_get_ap_info(&g_roaming_app.ap_info); g_roaming_app.scan_params.ssid = g_roaming_app.ap_info.ssid; if (g_roaming_app.ap_info.rssi < ROAMING_LOW_RSSI_THRESHOLD) { /* To ensure that the threshold is set to one offset below the current AP RSSI * in case, the AP is already below the RSSI threshold *//* ... */ g_roaming_app.current_low_rssi_threshold = g_roaming_app.ap_info.rssi - RSSI_THRESHOLD_REDUCTION_OFFSET; }{...} else { g_roaming_app.current_low_rssi_threshold = ROAMING_LOW_RSSI_THRESHOLD; }{...} ESP_LOGD(ROAMING_TAG, "setting rssi threshold as %d", g_roaming_app.current_low_rssi_threshold); esp_wifi_set_rssi_threshold(g_roaming_app.current_low_rssi_threshold);/* ... */ #endif /*LOW_RSSI_ROAMING_ENABLED*/ g_roaming_app.rrm_support = esp_rrm_is_rrm_supported_connection(); g_roaming_app.btm_support = esp_wnm_is_btm_supported_connection(); ESP_LOGD(ROAMING_TAG, "Station connected, RRM %ssupported, BTM %ssupported", g_roaming_app.rrm_support ? " " : "not ", g_roaming_app.btm_support ? " " : "not "); gettimeofday(&g_roaming_app.last_roamed_time, NULL); if (!initialize_roaming_event()) { #if PERIODIC_RRM_MONITORING if (g_roaming_app.rrm_support) { init_periodic_rrm_event(); }{...} #endif/* ... */ /*PERIODIC_RRM_MONITORING*/ #if PERIODIC_SCAN_MONITORING init_periodic_scan_roam_event(); #endif /*PERIODIC_SCAN_ROAM_MONITORING*/ ESP_LOGD(ROAMING_TAG, "Initialised initialise roaming events!"); }{...} else { ESP_LOGE(ROAMING_TAG, "Failed to Initialise roaming events"); }{...} #if LEGACY_ROAM_ENABLED g_roaming_app.force_roam_ongoing = true; #endif /*LEGACY_ROAM_ENABLED*/ g_roaming_app.allow_reconnect = true; }{ ... } #define MAX_NEIGHBOR_LEN 512 #if PERIODIC_RRM_MONITORING static char * get_btm_neighbor_list(uint8_t *report, size_t report_len) { size_t len = 0; const uint8_t *data; int ret = 0; /* * Neighbor Report element (IEEE P802.11-REVmc/D5.0) * BSSID[6] * BSSID Information[4] * Operating Class[1] * Channel Number[1] * PHY Type[1] * Optional Subelements[variable] *//* ... */ #define NR_IE_MIN_LEN (ETH_ALEN + 4 + 1 + 1 + 1) if (!report || report_len == 0) { ESP_LOGE(ROAMING_TAG, "RRM neighbor report is not valid"); return NULL; }{...} char *buf = os_calloc(1, MAX_NEIGHBOR_LEN); if (!buf) { ESP_LOGE(ROAMING_TAG, "get_btm_list : Memory alloc failed for buf"); return NULL; }{...} data = report; while (report_len >= 2 + NR_IE_MIN_LEN) { const uint8_t *nr; uint8_t nr_len = data[1]; const uint8_t *pos = data, *end; if (pos[0] != WLAN_EID_NEIGHBOR_REPORT || nr_len < NR_IE_MIN_LEN) { ESP_LOGD(ROAMING_TAG, "CTRL: Invalid Neighbor Report element: id=%u len=%u", data[0], nr_len); ret = -1; goto cleanup; }{...} if (2U + nr_len > report_len) { ESP_LOGD(ROAMING_TAG, "CTRL: Invalid Neighbor Report element: id=%u len=%zu nr_len=%u", data[0], report_len, nr_len); ret = -1; goto cleanup; }{...} pos += 2; end = pos + nr_len; nr = pos; pos += NR_IE_MIN_LEN; while (end - pos > 2) { uint8_t s_len; s_len = *pos++; if (s_len > end - pos) { ret = -1; goto cleanup; }{...} pos += s_len; }{...} ESP_LOGD(ROAMING_TAG, "RMM neighbor report bssid=" MACSTR " info=0x%" PRIx32 " op_class=%u chan=%u phy_type=%u", MAC2STR(nr), WPA_GET_LE32(nr + ETH_ALEN), nr[ETH_ALEN + 4], nr[ETH_ALEN + 5], nr[ETH_ALEN + 6]); /* neighbor start */ len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, " neighbor="); /* bssid */ len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, MACSTR, MAC2STR(nr)); /* , */ len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ","); /* bssid info */ len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "0x%04" PRIx32 "", WPA_GET_LE32(nr + ETH_ALEN)); len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ","); /* operating class */ len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "%u", nr[ETH_ALEN + 4]); len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ","); /* channel number */ len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "%u", nr[ETH_ALEN + 5]); len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ","); /* phy type */ len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "%u", nr[ETH_ALEN + 6]); /* optional elements, skip */ data = end; report_len -= 2 + nr_len; }{...} cleanup: if (ret < 0) { os_free(buf); buf = NULL; }{...} return buf; }{ ... } static void roaming_app_neighbor_report_recv_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { if (!g_roaming_app.rrm_request_active) { ESP_LOGV(ROAMING_TAG, "Not the response for our Neighbor Report Request"); return; }{...} g_roaming_app.rrm_request_active = false; if (!event_data) { ESP_LOGE(ROAMING_TAG, "No data received for neighbor report"); return; }{...} wifi_event_neighbor_report_t *neighbor_report_event = (wifi_event_neighbor_report_t*)event_data; ESP_LOGD(ROAMING_TAG, "Received cb for Neighbor Report Request"); uint8_t *pos = (uint8_t *)neighbor_report_event->report; if (!pos) { ESP_LOGE(ROAMING_TAG, "Neighbor report is empty"); return; }{...} uint8_t report_len = neighbor_report_event->report_len; /* dump report info */ ESP_LOGD(ROAMING_TAG, "rrm: neighbor report len=%d", report_len); ESP_LOG_BUFFER_HEXDUMP(ROAMING_TAG, pos, report_len, ESP_LOG_DEBUG); ROAM_NEIGHBOR_LIST_LOCK(); if (g_roaming_app.btm_neighbor_list) { os_free(g_roaming_app.btm_neighbor_list); g_roaming_app.btm_neighbor_list = NULL; }{...} /* create neighbor list */ g_roaming_app.btm_neighbor_list = get_btm_neighbor_list(pos + 1, report_len - 1); ROAM_NEIGHBOR_LIST_UNLOCK(); }{ ... } #endif/* ... */ /*PERIODIC_RRM_MONITORING*/ #if LOW_RSSI_ROAMING_ENABLED static void roaming_app_rssi_low_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { wifi_event_bss_rssi_low_t *event = event_data; ESP_LOGI(ROAMING_TAG, "%s:bss rssi is=%ld", __func__, event->rssi); roaming_app_get_ap_info(&g_roaming_app.ap_info); determine_best_ap(0); g_roaming_app.current_low_rssi_threshold -= RSSI_THRESHOLD_REDUCTION_OFFSET; ESP_LOGD(ROAMING_TAG, "Resetting RSSI Threshold to %d", g_roaming_app.current_low_rssi_threshold); esp_wifi_set_rssi_threshold(g_roaming_app.current_low_rssi_threshold); }{ ... } /* ... */#endif #if NETWORK_ASSISTED_ROAMING_ENABLED static void trigger_network_assisted_roam(void) { #if PERIODIC_RRM_MONITORING ROAM_NEIGHBOR_LIST_LOCK(); #endif /*PERIODIC_RRM_MONITORING*/ if (esp_wnm_send_bss_transition_mgmt_query(REASON_RSSI, g_roaming_app.btm_neighbor_list, 1) < 0) { ESP_LOGD(ROAMING_TAG, "failed to send btm query"); }{...} #if PERIODIC_RRM_MONITORING ROAM_NEIGHBOR_LIST_UNLOCK(); #endif /*PERIODIC_RRM_MONITORING*/ ESP_LOGD(ROAMING_TAG, "Sent BTM Query"); gettimeofday(&g_roaming_app.last_roamed_time, NULL); g_roaming_app.btm_attempt++; }{ ... } /* ... */#endif /*NETWORK_ASSISTED_ROAMING*/ #if LEGACY_ROAM_ENABLED static void trigger_legacy_roam(struct cand_bss *bss) { wifi_config_t wifi_cfg = {0}; esp_wifi_get_config(WIFI_IF_STA, &wifi_cfg); wifi_cfg.sta.channel = bss->channel; wifi_cfg.sta.bssid_set = true; os_memcpy(wifi_cfg.sta.bssid, bss->bssid, ETH_ALEN); esp_wifi_internal_issue_disconnect(WIFI_REASON_BSS_TRANSITION_DISASSOC); esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg); esp_wifi_connect(); ESP_LOGI(ROAMING_TAG, "Disconnecting and connecting to "MACSTR" on account of better rssi",MAC2STR(bss->bssid)); gettimeofday(&g_roaming_app.last_roamed_time, NULL); g_roaming_app.force_roam_ongoing = true; }{ ... } /* ... */#endif /*LEGACY_ROAM_ENABLED*/ void roaming_app_trigger_roam(struct cand_bss *bss) { struct timeval now; gettimeofday(&now, NULL); ESP_LOGD(ROAMING_TAG,"Processing trigger roaming request."); if (time_diff_sec(&now, &g_roaming_app.last_roamed_time) < ROAMING_BACKOFF_TIME ) { ESP_LOGD(ROAMING_TAG,"Ignoring request as time difference to last request is %ld",time_diff_sec(&now, &g_roaming_app.last_roamed_time)); goto free_bss; }{...} #if NETWORK_ASSISTED_ROAMING_ENABLED if (g_roaming_app.btm_support) { #if LEGACY_ROAM_ENABLED && NETWORK_ASSISTED_ROAMING_ENABLED if (g_roaming_app.btm_attempt <= BSS_TM_RETRY_COUNT) { #endif trigger_network_assisted_roam(); goto free_bss; #if LEGACY_ROAM_ENABLED && NETWORK_ASSISTED_ROAMING_ENABLED }{...} else { ESP_LOGD(ROAMING_TAG, "Not Sending BTM query as this method has failed too many times."); g_roaming_app.btm_attempt = 0; }{...} #endif }{...} #endif/* ... */ /*NETWORK_ASSISTED_ROAMING_ENABLED*/ #if LEGACY_ROAM_ENABLED trigger_legacy_roam(bss); #endif /*LEGACY_ROAM_ENABLED*/ free_bss : os_free(bss); }{ ... } void roaming_app_trigger_roam_internal_handler(void *ctx, void *data) { if (!data) { ESP_LOGE(ROAMING_TAG, "No data received for roaming event"); }{...} else { roaming_app_trigger_roam((struct cand_bss *)data); }{...} }{ ... } static int wifi_post_roam_event(struct cand_bss *bss) { if (bss) { struct cand_bss *cand_bss = (struct cand_bss *)os_zalloc(sizeof(struct cand_bss)); if (!cand_bss) { ESP_LOGE(ROAMING_TAG, "Cannot allocate data for roaming event"); return -1; }{...} os_memcpy(cand_bss, bss, sizeof(struct cand_bss)); /* trigger the roaming event */ if (eloop_register_timeout(0, 0, roaming_app_trigger_roam_internal_handler, NULL, (void*)cand_bss)) { ESP_LOGE(ROAMING_TAG, "Could not register roaming event."); os_free(cand_bss); return -1; }{...} }{...} else { ESP_LOGE(ROAMING_TAG, "Cannot trigger roaming event without any candidate APs"); return -1; }{...} return 0; }{ ... } void print_ap_records(struct scanned_ap_info *ap_info) { ESP_LOGD(ROAMING_TAG, "Scanned AP List"); for (int i = 0; i < ap_info->current_count ; i++) { ESP_LOGD(ROAMING_TAG, "%d. ssid : %s bssid :"MACSTR" channel : %d rssi : %d authmode : %d", i, ap_info->ap_records[i].ssid,MAC2STR(ap_info->ap_records[i].bssid), ap_info->ap_records[i].primary,ap_info->ap_records[i].rssi, ap_info->ap_records[i].authmode); }{...} }{ ... } #if PERIODIC_RRM_MONITORING static void periodic_rrm_request(struct timeval *now) { roaming_app_get_ap_info(&g_roaming_app.ap_info); if (esp_rrm_is_rrm_supported_connection() && (g_roaming_app.ap_info.rssi < RRM_MONITOR_RSSI_THRESHOLD)) { if (esp_rrm_send_neighbor_report_request() < 0) { ESP_LOGE(ROAMING_TAG, "failed to send neighbor report request"); return; }{...} g_roaming_app.rrm_request_active = true; }{...} }{ ... } /* ... */#endif static bool candidate_security_match(wifi_ap_record_t candidate) { wifi_auth_mode_t curr_auth = g_roaming_app.ap_info.authmode; wifi_auth_mode_t cand_auth = candidate.authmode; ESP_LOGV(ROAMING_TAG, "Cand authmode : %d, Current Authmode : %d", cand_auth, curr_auth); if (cand_auth == curr_auth) { ESP_LOGV(ROAMING_TAG, "Authmode matched!"); return true; }{...} wifi_config_t wifi_cfg = {0}; esp_wifi_get_config(WIFI_IF_STA, &wifi_cfg); if (wifi_cfg.sta.owe_enabled && OWE_COMPATIBLE(curr_auth, cand_auth)) { /* * OWE <--> Open allowed if threshold is Open *//* ... */ if (wifi_cfg.sta.threshold.authmode == WIFI_AUTH_OPEN) { ESP_LOGV(ROAMING_TAG, "transition between OWE and open permitted"); return true; }{...} else { ESP_LOGV(ROAMING_TAG, "transition between OWE and open not permitted"); return false; }{...} }{...} else if (wifi_cfg.sta.threshold.authmode > cand_auth) { /* If the authmode of the candidate AP is less than our threshold, it * will fail during connection*//* ... */ ESP_LOGV(ROAMING_TAG, "Authmode threshold failure %d -> %d", wifi_cfg.sta.threshold.authmode, cand_auth); return false; }{...} else if (PSK_COMPATIBLE(curr_auth, cand_auth)) { /* * PSK based authmodes are compatible with each other for roaming *//* ... */ ESP_LOGV(ROAMING_TAG, "Roaming between a PSK APs"); return true; }{...} return false; }{ ... } static bool candidate_profile_match(wifi_ap_record_t candidate) { return candidate_security_match(candidate); }{ ... } /* Remember to always call this function with the ROAM_SCAN_RESULTS_LOCK */ static void parse_scan_results_and_roam(void) { int8_t rssi_threshold = g_roaming_app.current_rssi_threshold; uint8_t best_rssi_diff = rssi_threshold; struct cand_bss *best_ap = NULL; int8_t rssi_diff = 0; uint8_t i; int8_t best_ap_index = -1; wifi_ap_record_t ap_info; roaming_app_get_ap_info(&ap_info); for (i = 0; i < g_roaming_app.scanned_aps.current_count; i++) { rssi_diff = g_roaming_app.scanned_aps.ap_records[i].rssi - ap_info.rssi; ESP_LOGD(ROAMING_TAG, "The difference between ("MACSTR", "MACSTR") with rssi (%d,%d) is : %d while the threshold is %d and the best rssi diff yet is %d, thecand_auth is %d", MAC2STR(g_roaming_app.scanned_aps.ap_records[i].bssid),MAC2STR(ap_info.bssid), g_roaming_app.scanned_aps.ap_records[i].rssi, ap_info.rssi, rssi_diff, rssi_threshold, best_rssi_diff, g_roaming_app.scanned_aps.ap_records[i].authmode); if ((memcmp(g_roaming_app.scanned_aps.ap_records[i].bssid, ap_info.bssid, ETH_ALEN) != 0) && candidate_profile_match(g_roaming_app.scanned_aps.ap_records[i]) && rssi_diff > best_rssi_diff ) { best_rssi_diff = rssi_diff; best_ap_index = i; }{...} }{...} if (best_ap_index >= 0) { best_ap = (struct cand_bss*)os_zalloc(sizeof(struct cand_bss)); if (!best_ap) { ESP_LOGE(ROAMING_TAG,"Memory Allocation for candidate bss failed"); return; }{...} os_memcpy(best_ap->bssid,g_roaming_app.scanned_aps.ap_records[best_ap_index].bssid , ETH_ALEN); best_ap->channel = g_roaming_app.scanned_aps.ap_records[best_ap_index].primary; }{...} if (best_ap) { ESP_LOGI(ROAMING_TAG,"Found a better AP "MACSTR" at channel %d", MAC2STR(best_ap->bssid), best_ap->channel); if (wifi_post_roam_event(best_ap)) { ESP_LOGE(ROAMING_TAG, "Posting of roaming event failed"); }{...} os_free(best_ap); }{...} else { ESP_LOGI(ROAMING_TAG, "Could not find a better AP with the threshold set to %d", g_roaming_app.current_rssi_threshold + 1); }{...} }{ ... } static void scan_done_event_handler(void *arg, ETS_STATUS status) { if (status == ETS_OK) { ROAM_SCAN_RESULTS_LOCK(); ESP_LOGD(ROAMING_TAG, "Scan Done properly"); g_roaming_app.scanned_aps.current_count = MAX_CANDIDATE_COUNT; esp_wifi_scan_get_ap_records(&g_roaming_app.scanned_aps.current_count, g_roaming_app.scanned_aps.ap_records); print_ap_records(&g_roaming_app.scanned_aps); parse_scan_results_and_roam(); g_roaming_app.scan_ongoing = false; ROAM_SCAN_RESULTS_UNLOCK(); }{...} else { ESP_LOGD(ROAMING_TAG, "Scan Done with error %d ", status); }{...} }{ ... } static void conduct_scan(void) { /* Update scan time in global structure */ gettimeofday(&g_roaming_app.scanned_aps.time, NULL); /* Issue scan */ os_memset(&g_roaming_app.scanned_aps, 0, sizeof(struct scanned_ap_info)); if (esp_wifi_promiscuous_scan_start(&g_roaming_app.scan_params, scan_done_event_handler) < 0) { ESP_LOGE(ROAMING_TAG, "failed to issue scan"); return; }{...} ESP_LOGI(ROAMING_TAG, "Issued Scan"); }{ ... } static void determine_best_ap(int8_t rssi_threshold) { struct timeval now; gettimeofday(&now, NULL); ROAM_SCAN_RESULTS_LOCK(); /* If the scan results are recent enough or a scan is already ongoing we should not trigger a new scan */ if (!g_roaming_app.scan_ongoing) { g_roaming_app.scan_ongoing = true; g_roaming_app.current_rssi_threshold = rssi_threshold; if (time_diff_sec(&now,&g_roaming_app.scanned_aps.time) > SCAN_RESULTS_USABILITY_WINDOW) { conduct_scan(); }{...} else { parse_scan_results_and_roam(); g_roaming_app.scan_ongoing = false; }{...} }{...} else if(rssi_threshold < g_roaming_app.current_rssi_threshold) { g_roaming_app.current_rssi_threshold = rssi_threshold; }{...} ROAM_SCAN_RESULTS_UNLOCK(); }{ ... } #if PERIODIC_SCAN_MONITORING static void periodic_scan_roam(struct timeval *now) { #if NETWORK_ASSISTED_ROAMING_ENABLED && !LEGACY_ROAM_ENABLED /* * In case we support only network assisted roaming, there is no need to scan * if we currently have over 10secs to go for backoff time to expire * as the results produced by a scan at this time would not be used by * supplicant to build candidate lists. * *//* ... */ if (time_diff_sec(now, &g_roaming_app.last_roamed_time) < ROAMING_BACKOFF_TIME - SUPPLICANT_CANDIDATE_LIST_EXPIRY) { return; }{...} #endif/* ... */ /*NETWORK_ASSISTED_ROAMING_ENABLED && !LEGACY_ROAM_ENABLED*/ /* If the current RSSI is not worse than the configured threshold * for station initiated roam, then do not trigger roam *//* ... */ roaming_app_get_ap_info(&g_roaming_app.ap_info); if (g_roaming_app.ap_info.rssi > SCAN_MONITOR_RSSI_THRESHOLD) { return; }{...} determine_best_ap(SCAN_ROAM_RSSI_DIFF - 1); }{ ... } /* ... */#endif /*PERIODIC_SCAN_MONITORING*/ #if PERIODIC_RRM_MONITORING void roaming_app_periodic_rrm_internal_handler(void *data, void *ctx) { struct timeval now; if (g_roaming_app.periodic_rrm_active) { ESP_LOGD(ROAMING_TAG,"Triggered periodic rrm event!"); gettimeofday(&now, NULL); /* This will be done every RRM_MONITOR_TIME */ periodic_rrm_request(&now); if (eloop_register_timeout(RRM_MONITOR_TIME, 0, roaming_app_periodic_rrm_internal_handler, NULL, NULL)) { ESP_LOGE(ROAMING_TAG,"Could not register periodic neighbor report request event."); }{...} }{...} }{ ... } /* ... */#endif /*PERIODIC_RRM_MONITORING*/ #if PERIODIC_SCAN_MONITORING void roaming_app_periodic_scan_internal_handler(void *data, void *ctx) { struct timeval now; if (g_roaming_app.periodic_scan_active) { ESP_LOGD(ROAMING_TAG,"Started the periodic scan roam event!"); gettimeofday(&now, NULL); /* This will be done every SCAN_MONITOR_INTERVAL */ periodic_scan_roam(&now); if (eloop_register_timeout(SCAN_MONITOR_INTERVAL, 0, roaming_app_periodic_scan_internal_handler, NULL, NULL)) { ESP_LOGE(ROAMING_TAG,"Could not register periodic scan event."); }{...} }{...} }{ ... } /* ... */#endif /*PERIODIC_SCAN_ROAM_MONITORING*/ static bool validate_scan_chan_list(const char* scan_chan_list) { regex_t regex; const char* pattern = "^[0-9]+(,[0-9]+)*$"; uint8_t ret = regcomp(&regex, pattern, REG_EXTENDED); if (ret != 0) { ESP_LOGE(ROAMING_TAG, "Regex compilation failed."); return false; }{...} ret = regexec(&regex, scan_chan_list, 0, NULL, 0); regfree(&regex); if (ret == REG_NOMATCH) { return false; }{...} /* Check for consecutive commas or comma at start/end */ if (strstr(scan_chan_list, ",,") != NULL || scan_chan_list[0] == ',' || scan_chan_list[strlen(scan_chan_list) - 1] == ',') { return false; }{...} return true; }{ ... } static int8_t parse_scan_chan_list(void) { int8_t ret = 0; char *scan_chan_string = NULL; if (validate_scan_chan_list(SCAN_PREFERRED_CHAN_LIST) == false) { ESP_LOGE(ROAMING_TAG, "scan chan list validation failed."); ret = -1; goto cleanup; }{...} scan_chan_string = (char *)os_zalloc(sizeof(char) * strlen(SCAN_PREFERRED_CHAN_LIST) + 1); if (scan_chan_string == NULL) { ESP_LOGE(ROAMING_TAG, "Memory allocation failed."); ret = -1; goto cleanup; }{...} strlcpy(scan_chan_string, SCAN_PREFERRED_CHAN_LIST, strlen(SCAN_PREFERRED_CHAN_LIST) + 1); char* token; token = strsep(&scan_chan_string, ","); g_roaming_app.scan_params.channel_bitmap.ghz_2_channels = 0; while (token != NULL) { uint8_t channel = atoi(token); /* Check if the number is within the required range */ if (channel >= 1 && channel <= 14) { /* Check if the number is already present in the array */ g_roaming_app.scan_params.channel_bitmap.ghz_2_channels |= (1 << channel); }{...} else { ESP_LOGE(ROAMING_TAG, "Channel out of range: %d", channel); ret = -1; goto cleanup; }{...} token = strsep(&scan_chan_string, ","); }{...} cleanup: if (scan_chan_string) { os_free(scan_chan_string); }{...} return ret; }{ ... } esp_err_t init_scan_params(void) { if (!scan_results_lock) { scan_results_lock = os_recursive_mutex_create(); if (!scan_results_lock) { ESP_LOGE(ROAMING_TAG, "%s: failed to create scan results lock", __func__); return ESP_FAIL; }{...} }{...} if (strcmp(DEFAULT_PREFERRED_SCAN_CHAN_LIST, SCAN_PREFERRED_CHAN_LIST)) { ESP_ERROR_CHECK(parse_scan_chan_list()); }{...} g_roaming_app.scan_params.scan_type = 0; g_roaming_app.scan_params.scan_time.active.min = SCAN_TIME_MIN_DURATION; g_roaming_app.scan_params.scan_time.active.max = SCAN_TIME_MAX_DURATION; g_roaming_app.scan_params.home_chan_dwell_time = HOME_CHANNEL_DWELL_TIME; gettimeofday(&g_roaming_app.scanned_aps.time, NULL); return ESP_OK; }{ ... } void init_roaming_app(void) { #if !LOW_RSSI_ROAMING_ENABLED && !PERIODIC_SCAN_MONITORING ESP_LOGE(ROAMING_TAG, "No roaming trigger enabled. Roaming app cannot be initialized"); return;/* ... */ #endif #if !NETWORK_ASSISTED_ROAMING_ENABLED && !LEGACY_ROAM_ENABLED ESP_LOGE(ROAMING_TAG, "No roaming method enabled. Roaming app cannot be initialized"); return;/* ... */ #endif ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, roaming_app_connected_event_handler, NULL)); ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, roaming_app_disconnected_event_handler, NULL)); ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_STOP, roaming_app_sta_stop_event_handler, NULL)); #if LOW_RSSI_ROAMING_ENABLED ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW, &roaming_app_rssi_low_handler, NULL));/* ... */ #endif /*ROAMING_LOW_RSSI_THRESHOLD*/ ESP_ERROR_CHECK(init_scan_params()); #if PERIODIC_RRM_MONITORING ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_NEIGHBOR_REP, &roaming_app_neighbor_report_recv_handler, NULL));/* ... */ #endif /*PERIODIC_RRM_MONITORING*/ ESP_LOGI(ROAMING_TAG, "Roaming app initialization done"); }{ ... } void deinit_roaming_app(void) { #if !LOW_RSSI_ROAMING_ENABLED && !PERIODIC_SCAN_MONITORING ESP_LOGE(ROAMING_TAG, "No roaming trigger enabled. Roaming app cannot be de-initialized"); return;/* ... */ #endif #if !NETWORK_ASSISTED_ROAMING_ENABLED && !LEGACY_ROAM_ENABLED ESP_LOGE(ROAMING_TAG, "No roaming trigger enabled. Roaming app cannot be de-initialized"); return;/* ... */ #endif /* Unregister Event handlers */ ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, roaming_app_connected_event_handler)); ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, roaming_app_disconnected_event_handler)); ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_STOP, roaming_app_sta_stop_event_handler)); #if LOW_RSSI_ROAMING_ENABLED ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW, &roaming_app_rssi_low_handler));/* ... */ #endif /*ROAMING_LOW_RSSI_THRESHOLD*/ #if PERIODIC_RRM_MONITORING ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_NEIGHBOR_REP, &roaming_app_neighbor_report_recv_handler));/* ... */ #endif /*PERIODIC_RRM_MONITORING*/ /* Disabling the periodic scan and RRM events */ #if PERIODIC_RRM_MONITORING g_roaming_app.periodic_rrm_active = false; #endif /*PERIODIC_RRM_MONITORING*/ #if PERIODIC_SCAN_MONITORING g_roaming_app.periodic_scan_active = false; #endif /*PERIODIC_SCAN_MONITORING*/ #if PERIODIC_RRM_MONITORING if (neighbor_list_lock) { os_mutex_delete(neighbor_list_lock); neighbor_list_lock = NULL; }{...} #endif/* ... */ /*PERIODIC_RRM_MONITORING*/ if (scan_results_lock) { os_mutex_delete(scan_results_lock); scan_results_lock = NULL; }{...} }{ ... }
Details
Show:
from
Types: Columns:
This file uses the notable symbols shown below. Click anywhere in the file to view more details.