diff options
Diffstat (limited to 'bcmdhd/wifi_hal/link_layer_stats.cpp')
-rw-r--r-- | bcmdhd/wifi_hal/link_layer_stats.cpp | 337 |
1 files changed, 300 insertions, 37 deletions
diff --git a/bcmdhd/wifi_hal/link_layer_stats.cpp b/bcmdhd/wifi_hal/link_layer_stats.cpp index 05ab438..c17b3b8 100644 --- a/bcmdhd/wifi_hal/link_layer_stats.cpp +++ b/bcmdhd/wifi_hal/link_layer_stats.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2017 The Android Open Source Project * - * Portions copyright (C) 2017 Broadcom Limited + * Portions copyright (C) 2023 Broadcom Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,7 +47,8 @@ typedef enum { ANDR_WIFI_ATTRIBUTE_INVALID = 0, ANDR_WIFI_ATTRIBUTE_NUM_RADIO = 1, ANDR_WIFI_ATTRIBUTE_STATS_INFO = 2, - ANDR_WIFI_ATTRIBUTE_STATS_MAX = 3 + ANDR_WIFI_ATTRIBUTE_ML_STATS_INFO = 3, + ANDR_WIFI_ATTRIBUTE_STATS_MAX = 4 } LINK_STAT_ATTRIBUTE; /* Internal radio statistics structure in the driver */ @@ -92,12 +93,15 @@ public: protected: virtual int handleResponse(WifiEvent& reply) { - void *data = NULL; + bool ml_data = false; wifi_radio_stat *radio_stat_ptr = NULL; - u8 *iface_stat = NULL; - u8 *radioStatsBuf = NULL, *output = NULL, *data_ptr = NULL; - uint32_t total_size = 0, per_radio_size = 0, data_len = 0, rem_len = 0; - int num_radios = 0, id = 0, subcmd = 0, len = 0; + wifi_iface_ml_stat *iface_ml_stat_ptr = NULL; + u8 *radioStatsBuf = NULL, *ifaceMlStatsBuf = NULL, *outbuf = NULL, *data_ptr = NULL; + u8 *data = NULL, *iface_stat = NULL; + uint32_t offset = 0, per_radio_size = 0, data_len = 0, outbuf_rem_len = 0, data_rem_len = 0; + int ret = 0, num_radios = 0, id = 0, subcmd = 0, len = 0; + u32 fixed_iface_ml_stat_size = 0, all_links_stat_size = 0; + u8 num_links = 0; ALOGI("In GetLinkStatsCommand::handleResponse"); @@ -121,38 +125,41 @@ protected: if (it.get_type() == ANDR_WIFI_ATTRIBUTE_NUM_RADIO) { num_radios = it.get_u32(); } else if (it.get_type() == ANDR_WIFI_ATTRIBUTE_STATS_INFO) { - data = it.get_data(); + data = (u8 *)it.get_data(); data_len = it.get_len(); + } else if (it.get_type() == ANDR_WIFI_ATTRIBUTE_ML_STATS_INFO) { + data = (u8 *)it.get_data(); + data_len = it.get_len(); + ml_data = true; } else { - ALOGW("Ignoring invalid attribute type = %d, size = %d\n", + ALOGW("Ignoring invalid attribute type = %d, size = %d", it.get_type(), it.get_len()); - return NL_SKIP; } } if (num_radios) { - if (!data || !data_len) { - ALOGE("%s: null data\n", __func__); - return NL_SKIP; - } - - rem_len = MAX_CMD_RESP_BUF_LEN; + outbuf_rem_len = MAX_CMD_RESP_BUF_LEN; radioStatsBuf = (u8 *)malloc(MAX_CMD_RESP_BUF_LEN); if (!radioStatsBuf) { ALOGE("No memory\n"); return NL_SKIP; } memset(radioStatsBuf, 0, MAX_CMD_RESP_BUF_LEN); - output = radioStatsBuf; + outbuf = radioStatsBuf; - data_ptr = (u8*)data; + if (!data || !data_len) { + ALOGE("%s: null data\n", __func__); + goto exit; + } + data_ptr = data; for (int i = 0; i < num_radios; i++) { - rem_len -= per_radio_size; - if (rem_len < per_radio_size) { + outbuf_rem_len -= per_radio_size; + if (outbuf_rem_len < per_radio_size) { ALOGE("No data left for radio %d\n", i); goto exit; } - data_ptr = (u8*)data + total_size; + + data_ptr = data + offset; if (!data_ptr) { ALOGE("Invalid data for radio index = %d\n", i); goto exit; @@ -161,40 +168,115 @@ protected: convertToExternalRadioStatStructure((wifi_radio_stat*)data_ptr, &per_radio_size); if (!radio_stat_ptr || !per_radio_size) { - if (radio_stat_ptr) { - free(radio_stat_ptr); - radio_stat_ptr = NULL; - } ALOGE("No data for radio %d\n", i); continue; } - memcpy(output, radio_stat_ptr, per_radio_size); - output += per_radio_size; - total_size += per_radio_size; - free(radio_stat_ptr); - radio_stat_ptr = NULL; + memcpy(outbuf, radio_stat_ptr, per_radio_size); + outbuf += per_radio_size; + /* Accounted size of all the radio data len */ + offset += per_radio_size; } - iface_stat = ((u8*)data + total_size); - if (!iface_stat || data_len < total_size) { - ALOGE("No data for iface stats!!, data_len = %d, total_size = %d\n", - data_len, total_size); + if (ml_data) { + outbuf = NULL; + data_rem_len = data_len - offset; + fixed_iface_ml_stat_size = offsetof(wifi_iface_ml_stat, links); + + /* Allocate vendor hal buffer, instead of using the nlmsg allocated buffer */ + ifaceMlStatsBuf = (u8 *)malloc(MAX_CMD_RESP_BUF_LEN); + if (!ifaceMlStatsBuf) { + ALOGE("No memory\n"); + goto exit; + } + outbuf_rem_len = MAX_CMD_RESP_BUF_LEN; + memset(ifaceMlStatsBuf, 0, MAX_CMD_RESP_BUF_LEN); + outbuf = ifaceMlStatsBuf; + + if (data_rem_len >= fixed_iface_ml_stat_size) { + data_ptr = (data + offset); + if (!data_ptr) { + ALOGE("No iface ml stats fixed data!!, data_len = %d, offset = %d\n", + data_len, offset); + ret = WIFI_ERROR_INVALID_ARGS; + goto exit; + } + + if (outbuf_rem_len < fixed_iface_ml_stat_size) { + ALOGE("No space to copy fixed iface ml stats!, rem_len %d, req_len %d\n", + outbuf_rem_len, fixed_iface_ml_stat_size); + ret = WIFI_ERROR_OUT_OF_MEMORY; + goto exit; + } + + memcpy(outbuf, data_ptr, fixed_iface_ml_stat_size); + data_rem_len -= fixed_iface_ml_stat_size; + outbuf_rem_len -= fixed_iface_ml_stat_size; + outbuf += fixed_iface_ml_stat_size; + offset += fixed_iface_ml_stat_size; + + iface_ml_stat_ptr = (wifi_iface_ml_stat *)ifaceMlStatsBuf; + if (!iface_ml_stat_ptr) { + ALOGE("No iface ml stats data!!"); + goto exit; + } + + num_links = iface_ml_stat_ptr->num_links; + all_links_stat_size = (num_links * offsetof(wifi_link_stat, peer_info)); + + if (num_links >= MAX_MLO_LINK) { + ALOGE("Invalid num links :%d\n", num_links); + goto exit; + } + if (num_links && (data_rem_len >= all_links_stat_size)) { + ret = convertToExternalIfaceMlstatStructure(&data, &offset, &outbuf, + &data_rem_len, num_links, &outbuf_rem_len); + if (ret < 0) { + ALOGE(("Failed to map data to iface ml struct\n")); + goto exit; + } + } else { + ALOGE("num_links %d, Required data not found: expected len %d," + " data_rem_len %d\n", num_links, all_links_stat_size, data_rem_len); + } + (*mHandler.on_multi_link_stats_results)(id, + (wifi_iface_ml_stat *)ifaceMlStatsBuf, num_radios, + (wifi_radio_stat *)radioStatsBuf); + } + } else if ((data_len >= (offset + sizeof(wifi_iface_stat)))) { + iface_stat = (data + offset); + if (!iface_stat) { + ALOGE("No data for legacy iface stats!!, data_len = %d, offset = %d\n", + data_len, offset); + goto exit; + } + (*mHandler.on_link_stats_results)(id, (wifi_iface_stat *)iface_stat, + num_radios, (wifi_radio_stat *)radioStatsBuf); + } else { + ALOGE("No data for iface stats!!, data_len = %d, offset = %d\n", + data_len, offset); goto exit; } - (*mHandler.on_link_stats_results)(id, (wifi_iface_stat *)iface_stat, - num_radios, (wifi_radio_stat *)radioStatsBuf); } exit: + if (radio_stat_ptr) { + free(radio_stat_ptr); + radio_stat_ptr = NULL; + } if (radioStatsBuf) { free(radioStatsBuf); radioStatsBuf = NULL; } + if (ifaceMlStatsBuf) { + free(ifaceMlStatsBuf); + ifaceMlStatsBuf = NULL; + } return NL_OK; } private: wifi_radio_stat *convertToExternalRadioStatStructure(wifi_radio_stat *internal_stat_ptr, - uint32_t *per_radio_size) { + uint32_t *per_radio_size) + { wifi_radio_stat *external_stat_ptr = NULL; if (!internal_stat_ptr) { ALOGE("Incoming data is null\n"); @@ -224,6 +306,187 @@ private: } return external_stat_ptr; } + + int convertToExternalRatestatsStructure(u8 **data, u32 *offset, u8 **outbuf, + u32 *data_rem_len, wifi_peer_info *peer_info, u8 num_rate, u32 *outbuf_rem_len) + { + u8 k = 0, num_peers = 0; + int ret = 0; + u8 *data_ptr = NULL; + u32 all_rates_size = 0, per_rate_size = 0; + + per_rate_size = sizeof(wifi_rate_stat); + all_rates_size = num_rate * per_rate_size; + if (!peer_info && (*data_rem_len != all_rates_size)) { + ALOGE("Insufficient data for rate_stats, data_rem_len %d," + "required data size %d \n", *data_rem_len, all_rates_size); + ret = WIFI_ERROR_INVALID_ARGS; + goto exit; + } + + for (k = 0; k < num_rate; k++) { + data_ptr = ((*data) + (*offset)); + if (!data_ptr) { + ALOGE("rate_stats not found!! num_rate %d, *data_rem_len = %d, *offset = %d\n", + num_rate, *data_rem_len, *offset); + ret = WIFI_ERROR_OUT_OF_MEMORY; + goto exit; + } + + if (*data_rem_len < per_rate_size) { + ALOGE("no rate_stats!!, data_rem_len %d, rate_stat size %d\n", + *data_rem_len, per_rate_size); + ret = WIFI_ERROR_INVALID_ARGS; + goto exit; + } + + if (*outbuf_rem_len < per_rate_size) { + ALOGE("No space to copy rate_stats of index [%d]!, rem_len %d, req_len %d\n", + k, *outbuf_rem_len, per_rate_size); + ret = WIFI_ERROR_OUT_OF_MEMORY; + goto exit; + } + + memcpy(*outbuf, data_ptr, per_rate_size); + *data_rem_len -= per_rate_size; + *outbuf_rem_len -= per_rate_size; + *outbuf += per_rate_size; + *offset += per_rate_size; + } + exit: + return ret; + } + + int convertToExternalIfaceLinkstatStructure(u8 **data, uint32_t *offset, u8 **outbuf, + u32 *data_rem_len, u8 num_peers, wifi_link_stat *links, u32 *outbuf_rem_len) + { + int ret = 0, j = 0, num_rate = 0; + u32 all_rate_stats_per_peer_per_link_size = 0, fixed_peer_info_size = 0; + u8 *data_ptr = NULL; + wifi_peer_info *peer_info_ptr = NULL; + + for (j = 0; j < num_peers; j++) { + data_ptr = ((*data) + (*offset)); + if (!data_ptr || (!*data_rem_len)) { + ALOGE("no peer_info data!! num_peers %d, data_rem_len = %d, *offset = %d\n", + num_peers, *data_rem_len, *offset); + ret = WIFI_ERROR_OUT_OF_MEMORY; + goto exit; + } + + fixed_peer_info_size = offsetof(wifi_peer_info, rate_stats); + if (*data_rem_len < fixed_peer_info_size) { + ALOGE("no fixed peer_info data!!, data_rem_len %d, fixed peer info %d\n", + *data_rem_len, fixed_peer_info_size); + ret = WIFI_ERROR_INVALID_ARGS; + goto exit; + } + + if (*outbuf_rem_len < fixed_peer_info_size) { + ALOGE("No space to copy fixed peer_info of index[%d]!, rem_len %d, req_len %d\n", + j, *outbuf_rem_len, fixed_peer_info_size); + ret = WIFI_ERROR_OUT_OF_MEMORY; + goto exit; + } + + memcpy(*outbuf, data_ptr, fixed_peer_info_size); + *data_rem_len -= fixed_peer_info_size; + *outbuf_rem_len -= fixed_peer_info_size; + *outbuf += fixed_peer_info_size; + *offset += fixed_peer_info_size; + + peer_info_ptr = (wifi_peer_info *)data_ptr; + if (!peer_info_ptr) { + ALOGE("no peer_info data!!"); + ret = WIFI_ERROR_INVALID_ARGS; + goto exit; + } + + num_rate = peer_info_ptr->num_rate; + all_rate_stats_per_peer_per_link_size = num_rate*sizeof(wifi_rate_stat); + if (num_rate && (*data_rem_len >= all_rate_stats_per_peer_per_link_size)) { + ret = convertToExternalRatestatsStructure(data, offset, outbuf, data_rem_len, + peer_info_ptr, num_rate, outbuf_rem_len); + if (ret != WIFI_SUCCESS) { + ALOGE(("Failed to convert it to rate stats\n")); + goto exit; + } + } else { + ALOGI("num_rate %d, Required rate_stats not found: expected len %d," + " data_rem_len %d\n", num_rate, all_rate_stats_per_peer_per_link_size, + *data_rem_len); + continue; + } + } + + exit: + return ret; + } + + int convertToExternalIfaceMlstatStructure(u8 **data, u32 *offset, u8 **outbuf, + u32 *data_rem_len, u8 num_links, u32 *outbuf_rem_len) + { + int ret = 0, i = 0; + u32 all_peers_per_link_size = 0, fixed_link_stat_size = 0; + u8 *data_ptr = NULL; + u8 num_peers = 0; + wifi_link_stat *links_ptr = NULL; + + for (i = 0; i < num_links; i++) { + data_ptr = ((*data) + (*offset)); + if (!data_ptr || !(*data_rem_len)) { + ALOGE("no variable links data!! num_links %d, data_rem_len = %d, offset = %d\n", + num_links, *data_rem_len, *offset); + ret = WIFI_ERROR_OUT_OF_MEMORY; + goto exit; + } + + fixed_link_stat_size = offsetof(wifi_link_stat, peer_info); + if (*data_rem_len < fixed_link_stat_size) { + ALOGE("no fixed wifi_link_stat data!!, data_rem_len %d, fixed link stat data %d\n", + *data_rem_len, fixed_link_stat_size); + ret = WIFI_ERROR_INVALID_ARGS; + goto exit; + } + + if (*outbuf_rem_len < fixed_link_stat_size) { + ALOGE("No space to copy fixed link stats of index[%d]!, rem_len %d, req_len %d\n", + i, *outbuf_rem_len, fixed_link_stat_size); + ret = WIFI_ERROR_OUT_OF_MEMORY; + goto exit; + } + + memcpy(*outbuf, data_ptr, fixed_link_stat_size); + *outbuf_rem_len -= fixed_link_stat_size; + *data_rem_len -= fixed_link_stat_size; + *outbuf += fixed_link_stat_size; + *offset += fixed_link_stat_size; + + links_ptr = (wifi_link_stat *)data_ptr; + if (!links_ptr) { + ALOGE("no link_stat data!!"); + ret = WIFI_ERROR_INVALID_ARGS; + goto exit; + } + + num_peers = links_ptr->num_peers; + all_peers_per_link_size = num_peers * offsetof(wifi_peer_info, rate_stats); + if (num_peers && (*data_rem_len >= all_peers_per_link_size)) { + ret = convertToExternalIfaceLinkstatStructure(data, offset, outbuf, + data_rem_len, num_peers, links_ptr, outbuf_rem_len); + if (ret != WIFI_SUCCESS) { + ALOGE(("Failed to convert it to iface link stats\n")); + goto exit; + } + } else { + ALOGI("num_peers %d, Required data not found: expected len %d, data_rem_len %d\n", + num_peers, all_peers_per_link_size, *data_rem_len); + continue; + } + } + exit: + return ret; + } }; wifi_error wifi_get_link_stats(wifi_request_id id, |