wpa_supplicant: Add initial roaming support

This commit adds different features from 802.11k and 802.11v
specifications to make the device ready for network assisted
roaming. It also adds initial framework for device to detect
whether it needs to move to a better AP.

Followings are added as part of this.

1. Support for sending neighbor report request and provide
   the report back to the APP.
2. Support for beacon measurement report.
3. Support for link measurement report.
4. Support for sending bss transition management query frame
   (triggered by the APP).
5. Support for bss transition management request and move
   to the candidate based on that.
6. Sending the bss transition management response.
This commit is contained in:
kapil.gupta
2020-11-12 13:48:24 +05:30
parent ac477ad6b9
commit 27101f9454
48 changed files with 5553 additions and 144 deletions

View File

@@ -0,0 +1,403 @@
/**
* Copyright 2020 Espressif Systems (Shanghai) PTE LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "esp_wifi_types.h"
#include "esp_wifi_driver.h"
#include "drivers/driver.h"
#include "common/bss.h"
#include "common/rrm.h"
#include "common/wnm_sta.h"
#include "common/wpa_supplicant_i.h"
#include "esp_supplicant/esp_scan_i.h"
#include "esp_supplicant/esp_common_i.h"
#include "common/ieee802_11_common.h"
#include "esp_rrm.h"
#include "esp_wnm.h"
struct wpa_supplicant g_wpa_supp;
static void *s_supplicant_task_hdl = NULL;
static void *s_supplicant_evt_queue = NULL;
static void *s_supplicant_api_lock = NULL;
static int esp_handle_action_frm(u8 *frame, size_t len,
u8 *sender, u32 rssi, u8 channel)
{
struct ieee_mgmt_frame *frm = os_malloc(sizeof(struct ieee_mgmt_frame) + len);
if (!frm) {
wpa_printf(MSG_ERROR, "memory allocation failed");
return -1;
}
os_memcpy(frm->sender, sender, ETH_ALEN);
frm->len = len;
frm->channel = channel;
frm->rssi = rssi;
os_memcpy(frm->payload, frame, len);
if (esp_supplicant_post_evt(SIG_SUPPLICANT_RX_ACTION, (u32)frm) != 0) {
os_free(frm);
return -1;
}
return 0;
}
static void esp_rx_rrm_frame(struct wpa_supplicant *wpa_s, u8 *sender,
u8 *payload, size_t len, u32 rssi)
{
if (payload[0] == WLAN_RRM_NEIGHBOR_REPORT_RESPONSE) {
/* neighbor report parsing */
wpas_rrm_process_neighbor_rep(wpa_s, payload + 1, len - 1);
} else if (payload[0] == WLAN_RRM_RADIO_MEASUREMENT_REQUEST) {
/* Beacon measurement */
wpas_rrm_handle_radio_measurement_request(wpa_s, NULL,
sender, payload + 1, len - 1);
} else if (payload[0] == WLAN_RRM_LINK_MEASUREMENT_REQUEST) {
/* Link measurement */
wpas_rrm_handle_link_measurement_request(wpa_s, NULL,
payload + 1, len - 1, rssi);
}
}
static int esp_mgmt_rx_action(u8 *sender, u8 *payload, size_t len, u8 channel, u32 rssi)
{
u8 category;
u8 bssid[ETH_ALEN];
struct wpa_supplicant *wpa_s = &g_wpa_supp;
int ret = esp_wifi_get_assoc_bssid_internal(bssid);
if (ret < 0) {
wpa_printf(MSG_INFO, "STA not associated");
return -1;
}
category = *payload++;
len--;
if (category == WLAN_ACTION_WNM) {
ieee802_11_rx_wnm_action(wpa_s, sender, payload, len);
} else if (category == WLAN_ACTION_RADIO_MEASUREMENT) {
esp_rx_rrm_frame(wpa_s, sender, payload, len, rssi);
}
return 0;
}
static void esp_btm_rrm_task(void *pvParameters)
{
supplicant_event_t *evt;
bool task_del = false;
while(1) {
if (xQueueReceive(s_supplicant_evt_queue, &evt, portMAX_DELAY) != pdTRUE)
continue;
/* event validation failed */
if (evt->id >= SIG_SUPPLICANT_MAX) {
os_free(evt);
continue;
}
/* get lock */
SUPPLICANT_API_LOCK();
switch (evt->id) {
case SIG_SUPPLICANT_RX_ACTION:
{
struct ieee_mgmt_frame *frm = (struct ieee_mgmt_frame *)evt->data;
esp_mgmt_rx_action(frm->sender, frm->payload, frm->len, frm->channel, frm->rssi);
os_free(frm);
break;
}
case SIG_SUPPLICANT_SCAN_DONE:
esp_supplicant_handle_scan_done_evt();
break;
case SIG_SUPPLICANT_DEL_TASK:
task_del = true;
break;
default:
break;
}
os_free(evt);
SUPPLICANT_API_UNLOCK();
if (task_del)
break;
}
vQueueDelete(s_supplicant_evt_queue);
s_supplicant_evt_queue = NULL;
if (s_supplicant_api_lock) {
vSemaphoreDelete(s_supplicant_api_lock);
s_supplicant_api_lock = NULL;
}
/* At this point, we completed */
vTaskDelete(NULL);
}
static void esp_clear_bssid_flag(struct wpa_supplicant *wpa_s)
{
wifi_config_t *config;
/* Reset only if btm is enabled */
if (esp_wifi_is_btm_enabled_internal(ESP_IF_WIFI_STA) == false)
return;
config = os_zalloc(sizeof(wifi_config_t));
if (!config) {
wpa_printf(MSG_ERROR, "failed to allocate memory");
return;
}
esp_wifi_get_config(ESP_IF_WIFI_STA, config);
config->sta.bssid_set = 0;
esp_wifi_set_config(ESP_IF_WIFI_STA, config);
os_free(config);
wpa_printf(MSG_DEBUG, "cleared bssid flag");
}
static void esp_register_action_frame(struct wpa_supplicant *wpa_s)
{
wpa_s->type &= ~WLAN_FC_STYPE_ACTION;
/* subtype is defined only for action frame */
wpa_s->subtype = 0;
/* current supported features in supplicant: rrm and btm */
if (esp_wifi_is_rm_enabled_internal(ESP_IF_WIFI_STA))
wpa_s->subtype = 1 << WLAN_ACTION_RADIO_MEASUREMENT;
if (esp_wifi_is_btm_enabled_internal(ESP_IF_WIFI_STA))
wpa_s->subtype |= 1 << WLAN_ACTION_WNM;
if (wpa_s->subtype)
wpa_s->type |= 1 << WLAN_FC_STYPE_ACTION;
esp_wifi_register_mgmt_frame_internal(wpa_s->type, wpa_s->subtype);
}
static void esp_supplicant_sta_conn_handler(void* arg, esp_event_base_t event_base,
int event_id, void* event_data)
{
u8 bssid[ETH_ALEN];
u8 *ie;
struct wpa_supplicant *wpa_s = &g_wpa_supp;
int ret = esp_wifi_get_assoc_bssid_internal(bssid);
if (ret < 0) {
wpa_printf(MSG_ERROR, "Not able to get connected bssid");
return;
}
struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, bssid);
if (!bss) {
wpa_printf(MSG_INFO, "connected bss entry not present in scan cache");
return;
}
wpa_s->current_bss = bss;
ie = (u8 *)bss;
ie += sizeof(struct wpa_bss);
ieee802_11_parse_elems(wpa_s, ie, bss->ie_len);
wpa_bss_flush(wpa_s);
/* Register for action frames */
esp_register_action_frame(wpa_s);
/* clear set bssid flag */
esp_clear_bssid_flag(wpa_s);
}
static void esp_supplicant_sta_disconn_handler(void* arg, esp_event_base_t event_base,
int event_id, void* event_data)
{
struct wpa_supplicant *wpa_s = &g_wpa_supp;
wpas_rrm_reset(wpa_s);
if (wpa_s->current_bss) {
wpa_s->current_bss = NULL;
}
}
void esp_supplicant_common_init(struct wpa_funcs *wpa_cb)
{
struct wpa_supplicant *wpa_s = &g_wpa_supp;
s_supplicant_evt_queue = xQueueCreate(3, sizeof(supplicant_event_t));
xTaskCreate(esp_btm_rrm_task, "btm_rrm_t", SUPPLICANT_TASK_STACK_SIZE, NULL, 2, s_supplicant_task_hdl);
s_supplicant_api_lock = xSemaphoreCreateRecursiveMutex();
if (!s_supplicant_api_lock) {
wpa_printf(MSG_ERROR, "esp_supplicant_common_init: failed to create Supplicant API lock");
return;
}
esp_scan_init(wpa_s);
wpas_rrm_reset(wpa_s);
wpas_clear_beacon_rep_data(wpa_s);
esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED,
&esp_supplicant_sta_conn_handler, NULL);
esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED,
&esp_supplicant_sta_disconn_handler, NULL);
wpa_s->type = 0;
wpa_s->subtype = 0;
wpa_cb->wpa_sta_rx_mgmt = esp_ieee80211_handle_rx_frm;
}
int esp_rrm_send_neighbor_rep_request(neighbor_rep_request_cb cb,
void *cb_ctx)
{
struct wpa_supplicant *wpa_s = &g_wpa_supp;
struct wpa_ssid_value wpa_ssid = {0};
struct wifi_ssid *ssid = esp_wifi_sta_get_prof_ssid_internal();
os_memcpy(wpa_ssid.ssid, ssid->ssid, ssid->len);
wpa_ssid.ssid_len = ssid->len;
wpas_rrm_send_neighbor_rep_request(wpa_s, &wpa_ssid, 0, 0, cb, cb_ctx);
return 0;
}
int esp_wnm_send_bss_transition_mgmt_query(enum btm_query_reason query_reason,
const char *btm_candidates,
int cand_list)
{
struct wpa_supplicant *wpa_s = &g_wpa_supp;
return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason, btm_candidates, cand_list);
}
void wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, char *ssid)
{
wifi_config_t *config = os_zalloc(sizeof(wifi_config_t));
if (!config) {
wpa_printf(MSG_ERROR, "failed to allocate memory");
return;
}
esp_wifi_get_config(ESP_IF_WIFI_STA, config);
/* We only support roaming in same ESS, therefore only bssid setting is needed */
os_memcpy(config->sta.bssid, bss->bssid, ETH_ALEN);
config->sta.bssid_set = 1;
esp_wifi_internal_issue_disconnect(WIFI_REASON_ROAMING);
esp_wifi_set_config(ESP_IF_WIFI_STA, config);
os_free(config);
esp_wifi_connect();
}
void esp_set_rm_enabled_ie(void)
{
uint8_t rmm_ie[5] = {0};
uint8_t rrm_ie_len = 5;
uint8_t *pos = rmm_ie;
*pos |= WLAN_RRM_CAPS_LINK_MEASUREMENT;
*pos |= WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
#ifdef SCAN_CACHE_SUPPORTED
WLAN_RRM_CAPS_BEACON_REPORT_TABLE |
#endif
WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE;
/* set rm enabled IE if enabled in driver */
if (esp_wifi_is_rm_enabled_internal(ESP_IF_WIFI_STA)) {
esp_wifi_set_appie_internal(WIFI_APPIE_RM_ENABLED_CAPS, rmm_ie, rrm_ie_len, 0);
}
}
void esp_get_tx_power(uint8_t *tx_power)
{
#define DEFAULT_MAX_TX_POWER 19 /* max tx power is 19.5 dbm */
s8 power;
/* esp sends management frames at max tx power configured */
int ret = esp_wifi_get_max_tx_power(&power);
if (ret != 0) {
wpa_printf(MSG_ERROR, "failed to get tx power");
*tx_power = DEFAULT_MAX_TX_POWER;
return;
}
*tx_power = power/4;
#undef DEFAULT_MAX_TX_POWER
}
int wpa_drv_send_action(struct wpa_supplicant *wpa_s,
unsigned int channel,
unsigned int wait,
const u8 *data, size_t data_len,
int no_cck)
{
int ret = 0;
wifi_mgmt_frm_req_t *req = os_zalloc(sizeof(*req) + data_len);;
if (!req)
return -1;
if (!wpa_s->current_bss) {
wpa_printf(MSG_ERROR, "STA not associated, return");
ret = -1;
goto cleanup;
}
req->ifx = ESP_IF_WIFI_STA;
req->subtype = WLAN_FC_STYPE_ACTION;
req->data_len = data_len;
os_memcpy(req->data, data, req->data_len);
if (esp_wifi_send_mgmt_frm_internal(req) != 0) {
wpa_printf(MSG_ERROR, "action frame sending failed");
ret = -1;
goto cleanup;
}
wpa_printf(MSG_INFO, "action frame sent");
cleanup:
os_free(req);
return ret;
}
int esp_supplicant_post_evt(uint32_t evt_id, uint32_t data)
{
supplicant_event_t *evt = os_zalloc(sizeof(supplicant_event_t));
if (evt == NULL) {
return -1;
}
evt->id = evt_id;
evt->data = data;
SUPPLICANT_API_LOCK();
if (xQueueSend(s_supplicant_evt_queue, &evt, 10 / portTICK_PERIOD_MS ) != pdPASS) {
SUPPLICANT_API_UNLOCK();
os_free(evt);
return -1;
}
SUPPLICANT_API_UNLOCK();
return 0;
}
int esp_ieee80211_handle_rx_frm(u8 type, u8 *frame, size_t len, u8 *sender,
u32 rssi, u8 channel, u64 current_tsf)
{
if (type == WLAN_FC_STYPE_BEACON || type == WLAN_FC_STYPE_PROBE_RESP) {
return esp_handle_beacon_probe(type, frame, len, sender, rssi, channel, current_tsf);
} else if (type == WLAN_FC_STYPE_ACTION) {
return esp_handle_action_frm(frame, len, sender, rssi, channel);
}
return -1;
}