diff --git a/include/wifi_base.h b/include/wifi_base.h index 47a0e7c0e..f7e205ab9 100644 --- a/include/wifi_base.h +++ b/include/wifi_base.h @@ -332,6 +332,11 @@ typedef struct { } __attribute__((packed)) wifi_csi_dev_t; #ifdef EM_APP +typedef struct { + mac_address_t sta_mac; + wifi_BeaconRequest_t data; +} beacon_query_params_t; + typedef struct wifi_hal_rrm_request { uint8_t dialog_token; uint8_t duration; @@ -1359,11 +1364,10 @@ typedef struct { wifi_BeaconReport_t *beacon_repo; } wifi_hal_rrm_report_t; -#define EM_MAX_BR_DATA 400 typedef struct { mac_address_t mac_addr; unsigned int data_len; - unsigned char data[EM_MAX_BR_DATA]; + unsigned char *data; unsigned int ap_index; unsigned int num_br_data; int sched_handler_id; diff --git a/source/apps/em/wifi_em.c b/source/apps/em/wifi_em.c index 5320aad9f..a46cbb1ab 100644 --- a/source/apps/em/wifi_em.c +++ b/source/apps/em/wifi_em.c @@ -24,6 +24,8 @@ #include "wifi_hal.h" #include "wifi_hal_ap.h" #include "wifi_mgr.h" +#include "common/ieee802_11_defs.h" +#include "utils/common.h" #include #include @@ -806,6 +808,135 @@ static int em_prepare_scan_response_data(wifi_provider_response_t *provider_resp return RETURN_OK; } +static int em_send_action_frame(void *data) +{ + unsigned int op_class; + char country[8] = { 0 }; + char client_mac[32]; + char bssid_str[32]; + unsigned int global_op_class; + UCHAR out_dialog = rand() % 256; + wifi_mgr_t *wifi_mgr = get_wifimgr_obj(); + wifi_platform_property_t *wifi_prop = &wifi_mgr->hal_cap.wifi_prop; + unsigned int num_of_radios = getNumberRadios(); + wifi_vap_info_map_t *vap_map; + + beacon_query_params_t *query = (beacon_query_params_t *)data; + wifi_BeaconRequest_t *params = &query->data; + int ap_index = RETURN_ERR; + + for (unsigned int i = 0; i < num_of_radios; i++) { + vap_map = (wifi_vap_info_map_t *)get_wifidb_vap_map(i); + if (vap_map == NULL) { + continue; + } + + for (int j = 0; j < vap_map->num_vaps; j++) { + if (memcmp(params->bssid, vap_map->vap_array[j].u.bss_info.bssid, sizeof(mac_addr_t)) == 0) { + ap_index = vap_map->vap_array[j].vap_index; + break; + } + } + + if (ap_index != RETURN_ERR) { + break; + } + } + + to_mac_str((unsigned char *)query->sta_mac, client_mac); + to_mac_str((unsigned char *)params->bssid, bssid_str); + wifi_util_dbg_print(WIFI_EM, "%s:%d: Sending beacon query action frame for mac %s with ap_index %d bssid:%s\n", __func__, __LINE__, + client_mac, ap_index, bssid_str); + if (ap_index == RETURN_ERR) { + wifi_util_error_print(WIFI_EM, "%s:%d Failed to resolve ap_index for bssid:%s\n", + __func__, __LINE__, bssid_str); + return RETURN_ERR; + } + + // If the query was sent with the serving BSS BSSID just for ap_index resolution, + // override to wildcard so the STA reports all neighboring BSSes. + memset(params->bssid, 0xff, sizeof(mac_addr_t)); + unsigned int radio_index = get_radio_index_for_vap_index( + wifi_prop, ap_index); + wifi_radio_operationParam_t *radio_oper_param = + (wifi_radio_operationParam_t *)get_wifidb_radio_map(radio_index); + + if (radio_oper_param == NULL) { + wifi_util_error_print(WIFI_EM, "%s:%d Unable to get radio params with radio_index:%d\n", __func__, __LINE__, radio_index); + return 0; + } + + if (RETURN_OK != get_coutry_str_from_code(radio_oper_param->countryCode, country)) { + wifi_util_error_print(WIFI_EM, "%s:%d Unable to read country code\n", __func__, __LINE__); + return RETURN_ERR; + } + + op_class = radio_oper_param->operatingClass; + global_op_class = country_to_global_op_class(country, op_class); + wifi_util_dbg_print(WIFI_EM, + "%s:%d Beacon request opClass:%u channel:%u radio_opClass:%u radio_channel:%u global_radio_opClass:%u\n", + __func__, __LINE__, params->opClass, params->channel, op_class, + radio_oper_param->channel, global_op_class); + + /* Normalize the controller-supplied opClass to a global op_class first. */ + if (params->opClass != 0) { + unsigned int normalized = country_to_global_op_class(country, params->opClass); + if (normalized != params->opClass) { + wifi_util_dbg_print(WIFI_EM, + "%s:%d Normalizing beacon request opClass from %u to global opClass %u\n", + __func__, __LINE__, params->opClass, normalized); + params->opClass = normalized; + } + } + + /* Validate band coherence: global op_classes 81-84 are 2.4 GHz (channels 1-14); + * op_classes 115-130 are 5 GHz (channels >= 36). + * If the channel doesn't match the op_class band, fall back to the radio's + * own operating class and channel so the HAL accepts the request. */ + bool chan_is_2g = (params->channel >= 1 && params->channel <= 14); + bool opclass_is_2g = (params->opClass >= 81 && params->opClass <= 84); + bool opclass_is_5g = (params->opClass >= 115 && params->opClass <= 130); + bool band_mismatch = (chan_is_2g && opclass_is_5g) || + (!chan_is_2g && opclass_is_2g) || + /* op_class not a known global class (e.g. country-local class) */ + (params->opClass != 0 && !opclass_is_2g && !opclass_is_5g && + params->opClass < 131); + if (band_mismatch) { + wifi_util_dbg_print(WIFI_EM, + "%s:%d opClass:%u channel:%u band mismatch, overriding with radio opClass:%u channel:%u\n", + __func__, __LINE__, params->opClass, params->channel, + global_op_class, radio_oper_param->channel); + params->opClass = global_op_class; + params->channel = radio_oper_param->channel; + } + + params->duration = 200; + + /* channel=255 requires a Channel Report subelement listing primary channels. + * If one is present (now containing correct 20 MHz primary channels), use it + * and align the outer op_class to match so the STA scans all listed channels. + * Without a channel report, fall back to the radio's own primary channel. */ + if (params->channel == 255) { + if (params->channelReportPresent) { + params->opClass = params->channelReport.opClass; + wifi_util_dbg_print(WIFI_EM, + "%s:%d channel=255 with channelReport opClass=%u, STA will scan listed channels\n", + __func__, __LINE__, params->opClass); + } else { + params->opClass = global_op_class; + params->channel = radio_oper_param->channel; + wifi_util_dbg_print(WIFI_EM, + "%s:%d channel=255 without channelReport, falling back to radio channel=%u opClass=%u\n", + __func__, __LINE__, params->channel, params->opClass); + } + } + + wifi_hal_setRMBeaconRequest(ap_index, query->sta_mac, params, &out_dialog); + wifi_util_dbg_print(WIFI_EM, "%s:%d: dialogue token is %d\n", __func__, __LINE__, out_dialog); + + return 0; +} + static int em_publish_stats_data(channel_scan_response_t *scan_response) { webconfig_subdoc_data_t *data; @@ -2416,11 +2547,86 @@ void em_beacon_report_frame_event(wifi_app_t *apps, void *data) em_beacon_report_publish(&wifi_app->ctrl->handle, data); } +static int em_process_beacon_rep(mac_address_t sta_mac, wifi_hal_rrm_report_t *rep, + wifi_app_t *app, const struct ieee80211_mgmt *mgmt, size_t len, int ap_index) +{ + sta_beacon_report_reponse_t report; + size_t data_len; + mac_addr_str_t mac_str; + + memset(&report, 0, sizeof(report)); + memcpy(report.mac_addr, sta_mac, sizeof(mac_address_t)); + report.ap_index = ap_index; + report.dialog_token = rep->dialog_token; + report.num_br_data = rep->size; + + data_len = len - IEEE80211_HDRLEN - 1 - sizeof(mgmt->u.action.u.rrm); + report.data = (unsigned char *)malloc(data_len); + if (report.data == NULL) { + wifi_util_error_print(WIFI_EM, "%s:%d failed to allocate %zu bytes for beacon report data\n", + __func__, __LINE__, data_len); + return -1; + } + report.data_len = data_len; + memcpy(report.data, mgmt->u.action.u.rrm.variable, data_len); + + to_mac_str(sta_mac, mac_str); + wifi_util_dbg_print(WIFI_EM, "%s:%d forwarding beacon report: sta=%s ap_index=%d num_reports=%zu data_len=%u\n", + __func__, __LINE__, + mac_str, + ap_index, rep->size, report.data_len); + + int rc = em_beacon_report_publish(&app->ctrl->handle, &report); + free(report.data); + return rc; +} + +static int em_handle_action_frame(wifi_app_t *apps, void *arg) +{ + frame_data_t *mgmt_frame = (frame_data_t *)arg; + wifi_hal_rrm_report_t rep; + mac_address_t mac_addr; + + const struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)mgmt_frame->data; + size_t len = mgmt_frame->frame.len; + + wifi_ctrl_t *ctrl = (wifi_ctrl_t *)get_wifictrl_obj(); + + if (len <= 32) { + wifi_util_dbg_print(WIFI_EM, "%s:%d Invalid Beacon Report Not processing\n", __func__, + __LINE__); + return -1; + } + + memcpy(mac_addr, mgmt->sa, ETH_ALEN); + wifi_util_dbg_print(WIFI_EM, "%s:%d: rrm.action:%d, mode:%d and len:%zu\n", __func__, __LINE__, + mgmt->u.action.u.rrm.action, ctrl->network_mode, len); + + switch (mgmt->u.action.u.rrm.action) { + case WLAN_RRM_RADIO_MEASUREMENT_REPORT: + if (ctrl->network_mode == rdk_dev_mode_type_gw || + ctrl->network_mode == rdk_dev_mode_type_em_colocated_node || + ctrl->network_mode == rdk_dev_mode_type_em_node) { + if (wifi_hal_parse_rm_beaon_report(mgmt_frame->frame.ap_index, mgmt, len, &rep) == + RETURN_OK) { + em_process_beacon_rep(mac_addr, &rep, apps, mgmt, len, mgmt_frame->frame.ap_index); + } + } + break; + default: + wifi_util_dbg_print(WIFI_EM, "%s:%d: unhandled RRM action type %d\n", __func__, __LINE__, + mgmt->u.action.u.rrm.action); + break; + } + return 0; +} + int handle_em_hal_event(wifi_app_t *app, wifi_event_subtype_t sub_type, void *data) { switch (sub_type) { case wifi_event_br_report: - em_beacon_report_frame_event(app, data); + wifi_util_info_print(WIFI_EM, "%s:%d: wifi_event_br_report.\n", __func__, __LINE__); + em_handle_action_frame(app, data); break; case wifi_event_hal_disassoc_device: @@ -2428,7 +2634,7 @@ int handle_em_hal_event(wifi_app_t *app, wifi_event_subtype_t sub_type, void *da __LINE__); em_handle_disassoc_device(app, data); break; - + case wifi_event_hal_sta_conn_status: em_handle_sta_conn_status(app, data); break; @@ -2699,6 +2905,31 @@ bus_error_t set_disconn_scan_none_state(char *name, raw_data_t *p_data, bus_user return bus_error_success; } +static bus_error_t send_beacon_query(char *event_name, raw_data_t *p_data, void *userData) +{ + if (strcmp(event_name, WIFI_EM_BEACON_QUERY) != 0) { + wifi_util_error_print(WIFI_EM, "%s:%d Not EasyMesh beacon query event, %s\n", __func__, __LINE__, event_name); + return bus_error_invalid_namespace; + } + + if (p_data->data_type != bus_data_type_bytes) { + wifi_util_error_print(WIFI_EM, "%s:%d: Invalid Received:%s data type:%x\n", + + __func__, __LINE__, event_name, p_data->data_type); + return bus_error_invalid_input; + } + + if (p_data->raw_data.bytes == NULL) { + wifi_util_error_print(WIFI_EM, "%s:%d: Invalid Received:%s raw_data.bytes is NULL\n", + __func__, __LINE__, event_name); + return bus_error_invalid_input; + } + + //now create map for response and send + em_send_action_frame(p_data->raw_data.bytes); + return bus_error_success; +} + int em_init(wifi_app_t *app, unsigned int create_flag) { int rc = RETURN_OK; @@ -2719,6 +2950,9 @@ int em_init(wifi_app_t *app, unsigned int create_flag) { WIFI_EM_CHANNEL_SCAN_REPORT, bus_element_type_event, { NULL, NULL, NULL, NULL, NULL, NULL}, slow_speed, ZERO_TABLE, { bus_data_type_bytes, false, 0, 0, 0, NULL } }, + { WIFI_EM_BEACON_QUERY, bus_element_type_event, + { NULL, send_beacon_query, NULL, NULL, NULL, NULL }, slow_speed, ZERO_TABLE, + { bus_data_type_bytes, false, 0, 0, 0, NULL } }, { WIFI_EM_BEACON_REPORT, bus_element_type_method, { NULL, NULL, NULL, NULL, NULL, NULL }, slow_speed, ZERO_TABLE, { bus_data_type_string, false, 0, 0, 0, NULL } }, @@ -2734,7 +2968,6 @@ int em_init(wifi_app_t *app, unsigned int create_flag) { WIFI_EM_CLIENT_ASSOC_CTRL_REQ, bus_element_type_method, { NULL, controller_set_client_acl_rules, NULL, NULL, NULL, NULL }, slow_speed, ZERO_TABLE, { bus_data_type_bytes, true, 0, 0, 0, NULL } } - }; policy_config->btm_steering_dslw_policy.sta_count = 0; diff --git a/source/apps/em/wifi_em.h b/source/apps/em/wifi_em.h index 1598b8510..b0e678076 100644 --- a/source/apps/em/wifi_em.h +++ b/source/apps/em/wifi_em.h @@ -26,6 +26,7 @@ extern "C" { #define WIFI_EM_CHANNEL_SCAN_REQUEST "Device.WiFi.EM.ChannelScanRequest" #define WIFI_EM_CHANNEL_SCAN_REPORT "Device.WiFi.EM.ChannelScanReport" +#define WIFI_EM_BEACON_QUERY "Device.WiFi.EM.BeaconQuery" #define WIFI_EM_BEACON_REPORT "Device.WiFi.EM.BeaconReport" #define WIFI_EM_STA_LINK_METRICS_REPORT "Device.WiFi.EM.STALinkMetricsReport" #define WIFI_EM_ASSOCIATION_STATUS "Device.WiFi.EM.AssociationStatus" @@ -60,6 +61,8 @@ typedef enum { typedef struct { em_config_t em_config; int sched_handler_id; + hash_map_t *beacon_map; + ap_metrics_policy_t ap_metrics_policy; } em_data_t; typedef struct { diff --git a/source/core/wifi_ctrl.c b/source/core/wifi_ctrl.c index 960d445d6..f071b3383 100644 --- a/source/core/wifi_ctrl.c +++ b/source/core/wifi_ctrl.c @@ -1264,6 +1264,9 @@ int mgmt_wifi_frame_recv(int ap_index, mac_address_t sta_mac, uint8_t *frame, ui case wifi_action_frame_type_public: get_action_frame_evt_params(frame, len, &mgmt_frame, &evt_subtype); break; + case wifi_action_frame_type_radio_msmt: + evt_subtype = wifi_event_br_report; + break; default: break; } diff --git a/source/webconfig/wifi_decoder.c b/source/webconfig/wifi_decoder.c index 83e43defe..596dca17d 100644 --- a/source/webconfig/wifi_decoder.c +++ b/source/webconfig/wifi_decoder.c @@ -6277,12 +6277,21 @@ webconfig_error_t decode_sta_beacon_report_object(const cJSON *obj_sta_cfg, decode_param_integer(obj_sta_cfg, "FrameLen", param); sta_data->data_len = param->valuedouble; + sta_data->data = (unsigned char *)malloc(sta_data->data_len); + if (sta_data->data == NULL) { + wifi_util_error_print(WIFI_WEBCONFIG, "%s:%d: failed to allocate %u bytes for report data\n", + __func__, __LINE__, sta_data->data_len); + return webconfig_error_decode; + } + decode_param_string(obj_sta_cfg, "ReportData", param); out_ptr = stringtohex(strlen(param->valuestring), param->valuestring, sta_data->data_len, sta_data->data); if (out_ptr == NULL) { wifi_util_error_print(WIFI_WEBCONFIG, "%s:%d: Error to convert ot string \n", __func__, __LINE__); + free(sta_data->data); + sta_data->data = NULL; return webconfig_error_decode; } diff --git a/source/webconfig/wifi_easymesh_translator.c b/source/webconfig/wifi_easymesh_translator.c index a75e3039d..1ac7213d0 100644 --- a/source/webconfig/wifi_easymesh_translator.c +++ b/source/webconfig/wifi_easymesh_translator.c @@ -2053,15 +2053,11 @@ webconfig_error_t translate_beacon_report_object_to_easymesh_sta_info(webconfig_ { em_sta_info_t em_sta_dev_info; webconfig_external_easymesh_t *proto; - em_radio_info_t *radio_info; - em_bss_info_t *bss_info; - int vap_index = 0, radio_index = 0; - wifi_platform_property_t *wifi_prop; + em_bss_info_t *bss_info = NULL; + em_bss_info_t *candidate; + int vap_index = 0; + unsigned int num_bss = 0, i = 0; webconfig_subdoc_decoded_data_t *params = &data->u.decoded; - rdk_wifi_radio_t *radio = NULL; - wifi_vap_info_t *vap = NULL; - wifi_vap_info_map_t *vap_map = NULL; - int i = 0; if (params == NULL) { wifi_util_error_print(WIFI_WEBCONFIG, "%s:%d: decoded_params is NULL\n", __func__, @@ -2070,50 +2066,40 @@ webconfig_error_t translate_beacon_report_object_to_easymesh_sta_info(webconfig_ } vap_index = params->sta_beacon_report.ap_index; - wifi_prop = &data->u.decoded.hal_cap.wifi_prop; - radio_index = get_radio_index_for_vap_index(wifi_prop, vap_index); proto = (webconfig_external_easymesh_t *)params->external_protos; if (proto == NULL) { - wifi_util_error_print(WIFI_WEBCONFIG, "%s:%d: em_sta_info_t is NULL\n", __func__, __LINE__); + wifi_util_error_print(WIFI_WEBCONFIG, "%s:%d: external_protos is NULL\n", __func__, __LINE__); return webconfig_error_translate_to_easymesh; } - radio = ¶ms->radios[radio_index]; - vap_map = &radio->vaps.vap_map; - - for (i = 0; i < radio->vaps.num_vaps; i++) { - vap = &vap_map->vap_array[i]; - if (vap->vap_index == vap_index) { + /* Iterate UWM BSS list matching on vap_index (not array index) */ + num_bss = proto->get_num_bss(proto->data_model); + for (i = 0; i < num_bss; i++) { + candidate = proto->get_bss_info(proto->data_model, i); + if (candidate != NULL && (int)candidate->vap_index == vap_index) { + bss_info = candidate; break; } } - if (vap == NULL) { - wifi_util_error_print(WIFI_WEBCONFIG, "%s:%d: vap is NULL\n", __func__, __LINE__); - return webconfig_error_translate_to_easymesh; - } - - if (vap->vap_mode != wifi_vap_mode_ap) { - wifi_util_error_print(WIFI_WEBCONFIG, "%s:%d: vap_mode:%d is not wifi_vap_mode_ap\n", - __func__, __LINE__, vap->vap_mode); - return webconfig_error_translate_to_easymesh; - } - - bss_info = proto->get_bss_info_with_mac(proto->data_model, vap->u.bss_info.bssid); - if (bss_info == NULL) { - wifi_util_error_print(WIFI_WEBCONFIG, "%s:%d: bss_info is NULL\n", __func__, __LINE__); + wifi_util_error_print(WIFI_WEBCONFIG, "%s:%d: no BSS found for vap_index %d (num_bss=%u)\n", + __func__, __LINE__, vap_index, num_bss); return webconfig_error_translate_to_easymesh; } + memset(&em_sta_dev_info, 0, sizeof(em_sta_info_t)); memcpy(em_sta_dev_info.id, params->sta_beacon_report.mac_addr, sizeof(mac_address_t)); memcpy(em_sta_dev_info.bssid, bss_info->bssid.mac, sizeof(mac_address_t)); memcpy(em_sta_dev_info.radiomac, bss_info->ruid.mac, sizeof(mac_address_t)); em_sta_dev_info.beacon_report_len = params->sta_beacon_report.data_len; em_sta_dev_info.num_beacon_meas_report = params->sta_beacon_report.num_br_data; - memcpy(em_sta_dev_info.beacon_report_elem, params->sta_beacon_report.data, params->sta_beacon_report.data_len); + if (params->sta_beacon_report.data != NULL && params->sta_beacon_report.data_len > 0) { + memcpy(em_sta_dev_info.beacon_report_elem, params->sta_beacon_report.data, + params->sta_beacon_report.data_len); + } proto->put_sta_info(proto->data_model, &em_sta_dev_info, em_target_sta_map_consolidated); diff --git a/source/webconfig/wifi_encoder.c b/source/webconfig/wifi_encoder.c index 587e1a3eb..953dc82a0 100644 --- a/source/webconfig/wifi_encoder.c +++ b/source/webconfig/wifi_encoder.c @@ -2862,7 +2862,6 @@ void print_hex_dump(unsigned int length, unsigned char *buffer) webconfig_error_t encode_beacon_report_object(sta_beacon_report_reponse_t *sta_data, cJSON **beacon_report_obj) { - char assoc_frame_string[MAX_FRAME_SZ * 2 + 1]; if (sta_data == NULL) { wifi_util_error_print(WIFI_WEBCONFIG, "%s:%d NULL sta_data Pointer\n", __func__, __LINE__); return webconfig_error_encode; @@ -2874,16 +2873,21 @@ webconfig_error_t encode_beacon_report_object(sta_beacon_report_reponse_t *sta_d return webconfig_error_encode; } - memset(assoc_frame_string, 0, sizeof(assoc_frame_string)); - if (sta_data->data_len != 0) { - // print_hex_dump(sta_data->data_len, sta_data->data); - hextostring(sta_data->data_len, sta_data->data, MAX_FRAME_SZ * 2 + 1, assoc_frame_string); - // printf("assoc_frame_string:%s\n", assoc_frame_string); - } else { + if (sta_data->data_len == 0 || sta_data->data == NULL) { wifi_util_error_print(WIFI_WEBCONFIG, "%s:%d No Report Data\n", __func__, __LINE__); return webconfig_error_encode; } + + size_t hex_buf_len = sta_data->data_len * 2 + 1; + char *assoc_frame_string = (char *)malloc(hex_buf_len); + if (assoc_frame_string == NULL) { + wifi_util_error_print(WIFI_WEBCONFIG, "%s:%d failed to allocate hex buffer\n", __func__, __LINE__); + return webconfig_error_encode; + } + memset(assoc_frame_string, 0, hex_buf_len); + hextostring(sta_data->data_len, sta_data->data, hex_buf_len, assoc_frame_string); cJSON_AddStringToObject(*beacon_report_obj, "ReportData", assoc_frame_string); + free(assoc_frame_string); return webconfig_error_none; } #endif diff --git a/source/webconfig/wifi_webconfig_beacon_report.c b/source/webconfig/wifi_webconfig_beacon_report.c index 662af2a46..2331e0531 100644 --- a/source/webconfig/wifi_webconfig_beacon_report.c +++ b/source/webconfig/wifi_webconfig_beacon_report.c @@ -164,6 +164,7 @@ webconfig_error_t decode_beacon_report_subdoc(webconfig_t *config, webconfig_sub return webconfig_error_decode; } + free(params->sta_beacon_report.data); memset(¶ms->sta_beacon_report, 0, sizeof(sta_beacon_report_reponse_t)); cJSON *obj_config = cJSON_GetObjectItem(json, "WiFiBeaconReport"); if (obj_config == NULL) {