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(&params, 0, sizeof(wifi_scan_config_t));
292 		if (esp_wifi_scan_start(&params, 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