1 #include <string.h>
2 #include <stdlib.h>
3 #include "freertos/FreeRTOS.h"
4 #include "freertos/task.h"
5 #include "freertos/event_groups.h"
6 #include "esp_wifi.h"
7 #include "esp_wnm.h"
8 #include "esp_rrm.h"
9 #include "esp_mbo.h"
10 #include "esp_event.h"
11 #include "esp_log.h"
12 #include "esp_system.h"
13 #include "nvs_flash.h"
14 #include "esp_netif.h"
15
16 /* Configuration */
17 #define EXAMPLE_WIFI_SSID CONFIG_EXAMPLE_WIFI_SSID
18 #define EXAMPLE_WIFI_PASSWORD CONFIG_EXAMPLE_WIFI_PASSWORD
19 #define EXAMPLE_WIFI_RSSI_THRESHOLD CONFIG_EXAMPLE_WIFI_RSSI_THRESHOLD
20
21 /* rrm ctx */
22 int rrm_ctx = 0;
23
24 /* FreeRTOS event group to signal when we are connected & ready to make a request */
25 static EventGroupHandle_t wifi_event_group;
26
27 /* esp netif object representing the WIFI station */
28 static esp_netif_t *sta_netif = NULL;
29
30 static const char *TAG = "roaming_example";
31
WPA_GET_LE32(const uint8_t * a)32 static inline uint32_t WPA_GET_LE32(const uint8_t *a)
33 {
34 return ((uint32_t) a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
35 }
36
event_handler(void * arg,esp_event_base_t event_base,int32_t event_id,void * event_data)37 static void event_handler(void* arg, esp_event_base_t event_base,
38 int32_t event_id, void* event_data)
39 {
40 if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
41 esp_wifi_connect();
42 } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
43 wifi_event_sta_disconnected_t *disconn = event_data;
44 ESP_LOGI(TAG, "station got disconnected reason=%d", disconn->reason);
45 if (disconn->reason == WIFI_REASON_ROAMING) {
46 ESP_LOGI(TAG, "station roaming, do nothing");
47 } else {
48 esp_wifi_connect();
49 }
50 } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) {
51 #if EXAMPLE_WIFI_RSSI_THRESHOLD
52 if (EXAMPLE_WIFI_RSSI_THRESHOLD) {
53 ESP_LOGI(TAG, "setting rssi threshold as %d\n", EXAMPLE_WIFI_RSSI_THRESHOLD);
54 esp_wifi_set_rssi_threshold(EXAMPLE_WIFI_RSSI_THRESHOLD);
55 }
56 #endif
57 }
58 }
59
60 #ifndef WLAN_EID_MEASURE_REPORT
61 #define WLAN_EID_MEASURE_REPORT 39
62 #endif
63 #ifndef MEASURE_TYPE_LCI
64 #define MEASURE_TYPE_LCI 9
65 #endif
66 #ifndef MEASURE_TYPE_LOCATION_CIVIC
67 #define MEASURE_TYPE_LOCATION_CIVIC 11
68 #endif
69 #ifndef WLAN_EID_NEIGHBOR_REPORT
70 #define WLAN_EID_NEIGHBOR_REPORT 52
71 #endif
72 #ifndef ETH_ALEN
73 #define ETH_ALEN 6
74 #endif
75
76 #define MAX_NEIGHBOR_LEN 512
77 #if 1
get_btm_neighbor_list(uint8_t * report,size_t report_len)78 static char * get_btm_neighbor_list(uint8_t *report, size_t report_len)
79 {
80 size_t len = 0;
81 const uint8_t *data;
82 int ret = 0;
83
84 /*
85 * Neighbor Report element (IEEE P802.11-REVmc/D5.0)
86 * BSSID[6]
87 * BSSID Information[4]
88 * Operating Class[1]
89 * Channel Number[1]
90 * PHY Type[1]
91 * Optional Subelements[variable]
92 */
93 #define NR_IE_MIN_LEN (ETH_ALEN + 4 + 1 + 1 + 1)
94
95 if (!report || report_len == 0) {
96 ESP_LOGI(TAG, "RRM neighbor report is not valid");
97 return NULL;
98 }
99
100 char *buf = calloc(1, MAX_NEIGHBOR_LEN);
101 data = report;
102
103 while (report_len >= 2 + NR_IE_MIN_LEN) {
104 const uint8_t *nr;
105 char lci[256 * 2 + 1];
106 char civic[256 * 2 + 1];
107 uint8_t nr_len = data[1];
108 const uint8_t *pos = data, *end;
109
110 if (pos[0] != WLAN_EID_NEIGHBOR_REPORT ||
111 nr_len < NR_IE_MIN_LEN) {
112 ESP_LOGI(TAG, "CTRL: Invalid Neighbor Report element: id=%u len=%u",
113 data[0], nr_len);
114 ret = -1;
115 goto cleanup;
116 }
117
118 if (2U + nr_len > report_len) {
119 ESP_LOGI(TAG, "CTRL: Invalid Neighbor Report element: id=%u len=%zu nr_len=%u",
120 data[0], report_len, nr_len);
121 ret = -1;
122 goto cleanup;
123 }
124 pos += 2;
125 end = pos + nr_len;
126
127 nr = pos;
128 pos += NR_IE_MIN_LEN;
129
130 lci[0] = '\0';
131 civic[0] = '\0';
132 while (end - pos > 2) {
133 uint8_t s_id, s_len;
134
135 s_id = *pos++;
136 s_len = *pos++;
137 if (s_len > end - pos) {
138 ret = -1;
139 goto cleanup;
140 }
141 if (s_id == WLAN_EID_MEASURE_REPORT && s_len > 3) {
142 /* Measurement Token[1] */
143 /* Measurement Report Mode[1] */
144 /* Measurement Type[1] */
145 /* Measurement Report[variable] */
146 switch (pos[2]) {
147 case MEASURE_TYPE_LCI:
148 if (lci[0])
149 break;
150 memcpy(lci, pos, s_len);
151 break;
152 case MEASURE_TYPE_LOCATION_CIVIC:
153 if (civic[0])
154 break;
155 memcpy(civic, pos, s_len);
156 break;
157 }
158 }
159
160 pos += s_len;
161 }
162
163 ESP_LOGI(TAG, "RMM neigbor report bssid=" MACSTR
164 " info=0x%x op_class=%u chan=%u phy_type=%u%s%s%s%s",
165 MAC2STR(nr), WPA_GET_LE32(nr + ETH_ALEN),
166 nr[ETH_ALEN + 4], nr[ETH_ALEN + 5],
167 nr[ETH_ALEN + 6],
168 lci[0] ? " lci=" : "", lci,
169 civic[0] ? " civic=" : "", civic);
170
171 /* neighbor start */
172 len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, " neighbor=");
173 /* bssid */
174 len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, MACSTR, MAC2STR(nr));
175 /* , */
176 len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ",");
177 /* bssid info */
178 len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "0x%04x", WPA_GET_LE32(nr + ETH_ALEN));
179 len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ",");
180 /* operating class */
181 len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "%u", nr[ETH_ALEN + 4]);
182 len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ",");
183 /* channel number */
184 len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "%u", nr[ETH_ALEN + 5]);
185 len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ",");
186 /* phy type */
187 len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "%u", nr[ETH_ALEN + 6]);
188 /* optional elements, skip */
189
190 data = end;
191 report_len -= 2 + nr_len;
192 }
193
194 cleanup:
195 if (ret < 0) {
196 free(buf);
197 buf = NULL;
198 }
199 return buf;
200 }
201
202 #else
203
204 /* Sample API to create neighbor list */
get_tmp_neighbor_list(uint8_t * report,size_t report_len)205 char * get_tmp_neighbor_list(uint8_t *report, size_t report_len)
206 {
207 #define MAC1 "00:01:02:03:04:05"
208 #define MAC2 "00:02:03:04:05:06"
209
210 char * buf = calloc(1, MAX_NEIGHBOR_LEN);
211 size_t len = 0;
212 char *pos;
213 if (!buf)
214 return NULL;
215
216 pos = buf;
217 /* create two temp neighbors */
218 /* format for neighbor list : neighbor=11:22:33:44:55:66,0x0000,81,3,7,0301ff */
219
220 /* neighbor1 start */
221 len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, " neighbor=");
222 /* bssid */
223 len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, MAC1);
224 /* , */
225 len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, ",");
226 /* bssid info */
227 len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, "0x0000");
228 len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, ",");
229 /* operating class */
230 len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, "81");
231 len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, ",");
232 /* channel number */
233 len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, "6");
234 len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, ",");
235 /* phy type */
236 len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, "7");
237 /* optional elements, skip */
238
239 /* neighbor2 start */
240 len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, " neighbor=");
241 /* bssid */
242 len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, MAC2);
243 /* , */
244 len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, ",");
245 /* bssid info */
246 len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, "0x0000");
247 len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, ",");
248 /* operating class */
249 len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, "81");
250 len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, ",");
251 /* channel number */
252 len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, "6");
253 len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, ",");
254 /* phy type */
255 len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, "7");
256 /* optional elements, skip */
257
258 len += snprintf(pos + len, MAX_NEIGHBOR_LEN - len, " ");
259
260 #undef MAC1
261 #undef MAC2
262 return buf;
263 }
264 #endif
265
neighbor_report_recv_cb(void * ctx,const uint8_t * report,size_t report_len)266 void neighbor_report_recv_cb(void *ctx, const uint8_t *report, size_t report_len)
267 {
268 int *val = ctx;
269 uint8_t *pos = (uint8_t *)report;
270 int cand_list = 0;
271
272 if (!report) {
273 ESP_LOGE(TAG, "report is null");
274 return;
275 }
276 if (*val != rrm_ctx) {
277 ESP_LOGE(TAG, "rrm_ctx value didn't match, not initiated by us");
278 return;
279 }
280 /* dump report info */
281 ESP_LOGI(TAG, "rrm: neighbor report len=%d", report_len);
282 ESP_LOG_BUFFER_HEXDUMP(TAG, pos, report_len, ESP_LOG_INFO);
283
284 /* create neighbor list */
285 char *neighbor_list = get_btm_neighbor_list(pos + 1, report_len - 1);
286
287 /* In case neighbor list is not present issue a scan and get the list from that */
288 if (!neighbor_list) {
289 /* issue scan */
290 wifi_scan_config_t params;
291 memset(¶ms, 0, sizeof(wifi_scan_config_t));
292 if (esp_wifi_scan_start(¶ms, true) < 0) {
293 goto cleanup;
294 }
295 /* cleanup from net802.11 */
296 uint16_t number = 1;
297 wifi_ap_record_t ap_records;
298 esp_wifi_scan_get_ap_records(&number, &ap_records);
299 cand_list = 1;
300 }
301 /* send AP btm query, this will cause STA to roam as well */
302 esp_wnm_send_bss_transition_mgmt_query(REASON_FRAME_LOSS, neighbor_list, cand_list);
303
304 cleanup:
305 if (neighbor_list)
306 free(neighbor_list);
307 }
308
309 #if EXAMPLE_WIFI_RSSI_THRESHOLD
esp_bss_rssi_low_handler(void * arg,esp_event_base_t event_base,int32_t event_id,void * event_data)310 static void esp_bss_rssi_low_handler(void* arg, esp_event_base_t event_base,
311 int32_t event_id, void* event_data)
312 {
313 wifi_event_bss_rssi_low_t *event = event_data;
314
315 ESP_LOGI(TAG, "%s:bss rssi is=%d", __func__, event->rssi);
316 /* Lets check channel conditions */
317 rrm_ctx++;
318 if (esp_rrm_send_neighbor_rep_request(neighbor_report_recv_cb, &rrm_ctx) < 0) {
319 /* failed to send neighbor report request */
320 ESP_LOGI(TAG, "failed to send neighbor report request");
321 if (esp_wnm_send_bss_transition_mgmt_query(REASON_FRAME_LOSS, NULL, 0) < 0) {
322 ESP_LOGI(TAG, "failed to send btm query");
323 }
324 }
325 }
326 #endif
327
328 #if 0
329 /* Example code to update channel preference in MBO */
330 static void clear_chan_preference()
331 {
332 esp_mbo_update_non_pref_chan(NULL);
333 }
334
335 static void update_chan_preference(void)
336 {
337 struct non_pref_chan_s *chans = malloc(sizeof(struct non_pref_chan_s) + 2 * sizeof(struct non_pref_chan));
338 chans->non_pref_chan_num = 2;
339
340 /* first */
341 struct non_pref_chan *chan = &chans->chan[0];
342 chan->reason = NON_PREF_CHAN_REASON_UNSPECIFIED;
343 chan->oper_class = 0x51;
344 chan->chan = 1;
345 chan->preference = 0;
346
347 /* second */
348 chan = &chans->chan[1];
349 chan->reason = NON_PREF_CHAN_REASON_UNSPECIFIED;
350 chan->oper_class = 0x51;
351 chan->chan = 11;
352 chan->preference = 1;
353
354 esp_mbo_update_non_pref_chan(chans);
355 free(chans);
356 }
357 #endif
358
initialise_wifi(void)359 static void initialise_wifi(void)
360 {
361 ESP_ERROR_CHECK(esp_netif_init());
362 wifi_event_group = xEventGroupCreate();
363 ESP_ERROR_CHECK(esp_event_loop_create_default());
364 sta_netif = esp_netif_create_default_wifi_sta();
365 assert(sta_netif);
366
367 wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
368 ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
369 ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );
370 ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) );
371 #if EXAMPLE_WIFI_RSSI_THRESHOLD
372 ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW,
373 &esp_bss_rssi_low_handler, NULL));
374 #endif
375
376
377 ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
378 wifi_config_t wifi_config = {
379 .sta = {
380 .ssid = EXAMPLE_WIFI_SSID,
381 .password = EXAMPLE_WIFI_PASSWORD,
382 .rm_enabled =1,
383 .btm_enabled =1,
384 .mbo_enabled =1,
385 .pmf_cfg.capable = 1,
386 },
387 };
388
389 ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
390 ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
391 ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
392 ESP_ERROR_CHECK( esp_wifi_start() );
393 }
394
app_main(void)395 void app_main(void)
396 {
397 ESP_ERROR_CHECK( nvs_flash_init() );
398 initialise_wifi();
399 }
400