diff --git a/core/hdd/src/wlan_hdd_stats.c b/core/hdd/src/wlan_hdd_stats.c index 6ceb554bd43b..777f1e0984fc 100644 --- a/core/hdd/src/wlan_hdd_stats.c +++ b/core/hdd/src/wlan_hdd_stats.c @@ -807,24 +807,19 @@ static void hdd_link_layer_process_radio_stats(hdd_adapter_t *pAdapter, return; hdd_notice("LL_STATS_RADIO" - " number of radios = %u" - " radio is %d onTime is %u" - " txTime is %u rxTime is %u" - " onTimeScan is %u onTimeNbd is %u" - " onTimeGscan is %u onTimeRoamScan is %u" - " onTimePnoScan is %u onTimeHs20 is %u" - " numChannels is %u", - num_radio, - pWifiRadioStat->radio, - pWifiRadioStat->onTime, - pWifiRadioStat->txTime, - pWifiRadioStat->rxTime, - pWifiRadioStat->onTimeScan, - pWifiRadioStat->onTimeNbd, - pWifiRadioStat->onTimeGscan, + " number of radios: %u radio: %d onTime: %u" + " txTime: %u rxTime: %u onTimeScan: %u onTimeNbd: %u" + " onTimeGscan: %u onTimeRoamScan: %u" + " onTimePnoScan: %u onTimeHs20: %u" + " numChannels: %u total_num_tx_power_levels: %u", + num_radio, pWifiRadioStat->radio, + pWifiRadioStat->onTime, pWifiRadioStat->txTime, + pWifiRadioStat->rxTime, pWifiRadioStat->onTimeScan, + pWifiRadioStat->onTimeNbd, pWifiRadioStat->onTimeGscan, pWifiRadioStat->onTimeRoamScan, pWifiRadioStat->onTimePnoScan, - pWifiRadioStat->onTimeHs20, pWifiRadioStat->numChannels); + pWifiRadioStat->onTimeHs20, pWifiRadioStat->numChannels, + pWifiRadioStat->total_num_tx_power_levels); /* * Allocate a size of 4096 for the Radio stats comprising @@ -884,11 +879,7 @@ static void hdd_link_layer_process_radio_stats(hdd_adapter_t *pAdapter, pWifiRadioStat->onTimeHs20) || nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_TX_LEVELS, - MAX_TPC_LEVELS) || - nla_put(vendor_event, - QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME_PER_LEVEL, - sizeof(u32) * MAX_TPC_LEVELS, - pWifiRadioStat->tx_time_per_tpc) || + pWifiRadioStat->total_num_tx_power_levels) || nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS, pWifiRadioStat->numChannels)) { @@ -898,6 +889,18 @@ static void hdd_link_layer_process_radio_stats(hdd_adapter_t *pAdapter, return; } + if (pWifiRadioStat->total_num_tx_power_levels) { + if (nla_put(vendor_event, + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME_PER_LEVEL, + sizeof(u32) * + pWifiRadioStat->total_num_tx_power_levels, + pWifiRadioStat->tx_time_per_power_level)) { + hdd_err("nla_put fail"); + kfree_skb(vendor_event); + return; + } + } + if (pWifiRadioStat->numChannels) { struct nlattr *chList; struct nlattr *chInfo; diff --git a/core/mac/inc/sir_api.h b/core/mac/inc/sir_api.h index e5d11883e850..8ab3e325b111 100644 --- a/core/mac/inc/sir_api.h +++ b/core/mac/inc/sir_api.h @@ -5034,6 +5034,11 @@ typedef struct { * (32 bits number accruing over time) */ uint32_t onTimeHs20; + + /* tx time (in milliseconds) per TPC level (0.5 dBm) */ + uint32_t total_num_tx_power_levels; + uint32_t *tx_time_per_power_level; + /* number of channels */ uint32_t numChannels; diff --git a/core/wma/inc/wma.h b/core/wma/inc/wma.h index 06a939232f38..0a0b08209a68 100644 --- a/core/wma/inc/wma.h +++ b/core/wma/inc/wma.h @@ -1,4 +1,4 @@ -/* + /* * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. @@ -1608,6 +1608,7 @@ typedef struct { bool sub_20_support; tp_wma_packetdump_cb wma_mgmt_tx_packetdump_cb; tp_wma_packetdump_cb wma_mgmt_rx_packetdump_cb; + tSirLLStatsResults *link_stats_results; } t_wma_handle, *tp_wma_handle; /** diff --git a/core/wma/src/wma_main.c b/core/wma/src/wma_main.c index c14d8b737765..c8a6c6bc7aa8 100644 --- a/core/wma/src/wma_main.c +++ b/core/wma/src/wma_main.c @@ -3536,6 +3536,11 @@ QDF_STATUS wma_close(void *cds_ctx) wma_handle->pGetRssiReq = NULL; } + if (wma_handle->link_stats_results) { + qdf_mem_free(wma_handle->link_stats_results); + wma_handle->link_stats_results = NULL; + } + wma_ndp_unregister_all_event_handlers(wma_handle); if (WMI_SERVICE_IS_ENABLED(wma_handle->wmi_service_bitmap, diff --git a/core/wma/src/wma_utils.c b/core/wma/src/wma_utils.c index 679b1732c11b..82ee8bb39df4 100644 --- a/core/wma/src/wma_utils.c +++ b/core/wma/src/wma_utils.c @@ -609,6 +609,107 @@ static int wma_unified_link_peer_stats_event_handler(void *handle, return 0; } +/** + * wma_unified_radio_tx_power_level_stats_event_handler() - tx power level stats + * @handle: WMI handle + * @cmd_param_info: command param info + * @len: Length of @cmd_param_info + * + * This is the WMI event handler function to receive radio stats tx + * power level stats. + * + * Return: 0 on success, error number otherwise. +*/ +static int wma_unified_radio_tx_power_level_stats_event_handler(void *handle, + u_int8_t *cmd_param_info, u_int32_t len) +{ + tp_wma_handle wma_handle = (tp_wma_handle) handle; + WMI_RADIO_TX_POWER_LEVEL_STATS_EVENTID_param_tlvs *param_tlvs; + wmi_tx_power_level_stats_evt_fixed_param *fixed_param; + uint8_t *tx_power_level_values; + tSirLLStatsResults *link_stats_results; + tSirWifiRadioStat *rs_results; + + tpAniSirGlobal mac = cds_get_context(QDF_MODULE_ID_PE); + + if (!mac) { + WMA_LOGD("%s: NULL pMac ptr. Exiting", __func__); + return -EINVAL; + } + + if (!mac->sme.pLinkLayerStatsIndCallback) { + WMA_LOGD("%s: HDD callback is null", __func__); + return -EINVAL; + } + + param_tlvs = (WMI_RADIO_TX_POWER_LEVEL_STATS_EVENTID_param_tlvs *)cmd_param_info; + if (!param_tlvs) { + WMA_LOGA("%s: Invalid tx power level stats event", __func__); + return -EINVAL; + } + + fixed_param = param_tlvs->fixed_param; + if (!fixed_param) { + WMA_LOGA("%s: Invalid param_tlvs for Radio tx_power level Stats", __func__); + return -EINVAL; + } + + link_stats_results = wma_handle->link_stats_results; + rs_results = (tSirWifiRadioStat *) &link_stats_results->results[0]; + tx_power_level_values = (uint8_t *) param_tlvs->tx_time_per_power_level; + + WMA_LOGD("%s: total_num_tx_power_levels: %u num_tx_power_levels: %u power_level_offset: %u", + __func__, fixed_param->total_num_tx_power_levels, + fixed_param->num_tx_power_levels, + fixed_param->power_level_offset); + + rs_results->total_num_tx_power_levels = + fixed_param->total_num_tx_power_levels; + if (!rs_results->total_num_tx_power_levels) + goto post_stats; + + if (!rs_results->tx_time_per_power_level) { + rs_results->tx_time_per_power_level = qdf_mem_malloc( + sizeof(uint32_t) * + rs_results->total_num_tx_power_levels); + if (!rs_results->tx_time_per_power_level) { + WMA_LOGA("%s: Mem alloc failed for tx power level stats", __func__); + /* In error case, atleast send the radio stats without + * tx_power_level stats */ + rs_results->total_num_tx_power_levels = 0; + goto post_stats; + } + } + qdf_mem_copy(&rs_results->tx_time_per_power_level[fixed_param->power_level_offset], + tx_power_level_values, + sizeof(uint32_t) * fixed_param->num_tx_power_levels); + if (rs_results->total_num_tx_power_levels == + (fixed_param->num_tx_power_levels + fixed_param->power_level_offset)) + link_stats_results->moreResultToFollow = 0; + + WMA_LOGD("%s: moreResultToFollow: %u", + __func__, link_stats_results->moreResultToFollow); + + /* If still data to receive, return from here */ + if (link_stats_results->moreResultToFollow) + return 0; + +post_stats: + /* call hdd callback with Link Layer Statistics + * vdev_id/ifacId in link_stats_results will be + * used to retrieve the correct HDD context + */ + mac->sme.pLinkLayerStatsIndCallback(mac->hHdd, + WMA_LINK_LAYER_STATS_RESULTS_RSP, + link_stats_results); + WMA_LOGD("%s: Radio Stats event posted to HDD", __func__); + qdf_mem_free(rs_results->tx_time_per_power_level); + qdf_mem_free(wma_handle->link_stats_results); + rs_results->tx_time_per_power_level = NULL; + wma_handle->link_stats_results = NULL; + + return 0; +} /** * wma_unified_link_radio_stats_event_handler() - radio link stats event handler @@ -622,15 +723,18 @@ static int wma_unified_link_radio_stats_event_handler(void *handle, uint8_t *cmd_param_info, uint32_t len) { + tp_wma_handle wma_handle = (tp_wma_handle) handle; WMI_RADIO_LINK_STATS_EVENTID_param_tlvs *param_tlvs; wmi_radio_link_stats_event_fixed_param *fixed_param; wmi_radio_link_stats *radio_stats; wmi_channel_stats *channel_stats; tSirLLStatsResults *link_stats_results; uint8_t *results, *t_radio_stats, *t_channel_stats; - uint32_t next_res_offset, next_chan_offset, count; + uint32_t next_chan_offset, count; size_t radio_stats_size, chan_stats_size; size_t link_stats_results_size; + tSirWifiRadioStat *rs_results; + tSirWifiChannelStats *chn_results; tpAniSirGlobal pMac = cds_get_context(QDF_MODULE_ID_PE); @@ -672,15 +776,14 @@ static int wma_unified_link_radio_stats_event_handler(void *handle, link_stats_results_size = sizeof(*link_stats_results) + radio_stats_size + (radio_stats->num_channels * chan_stats_size); - link_stats_results = qdf_mem_malloc(link_stats_results_size); - if (NULL == link_stats_results) { + wma_handle->link_stats_results = qdf_mem_malloc(link_stats_results_size); + if (NULL == wma_handle->link_stats_results) { WMA_LOGD("%s: could not allocate mem for stats results-len %zu", __func__, link_stats_results_size); return -ENOMEM; } - WMA_LOGD("Radio stats from FW event buf"); - WMA_LOGD("Fixed Param:"); + WMA_LOGD("Radio stats Fixed Param:"); WMA_LOGD("request_id %u num_radio %u more_radio_events %u", fixed_param->request_id, fixed_param->num_radio, fixed_param->more_radio_events); @@ -696,6 +799,7 @@ static int wma_unified_link_radio_stats_event_handler(void *handle, radio_stats->on_time_pno_scan, radio_stats->on_time_hs20, radio_stats->num_channels); + link_stats_results = wma_handle->link_stats_results; qdf_mem_zero(link_stats_results, link_stats_results_size); link_stats_results->paramId = WMI_LINK_STATS_RADIO; @@ -703,16 +807,37 @@ static int wma_unified_link_radio_stats_event_handler(void *handle, link_stats_results->ifaceId = 0; link_stats_results->num_radio = fixed_param->num_radio; link_stats_results->peer_event_number = 0; + + /* + * Backward compatibility: + * There are firmware(s) which will send Radio stats only with + * more_radio_events set to 0 and firmware which sends Radio stats + * followed by tx_power level stats with more_radio_events set to 1. + * if more_radio_events is set to 1, buffer the radio stats and + * wait for tx_power_level stats. + */ link_stats_results->moreResultToFollow = fixed_param->more_radio_events; results = (uint8_t *) link_stats_results->results; t_radio_stats = (uint8_t *) radio_stats; t_channel_stats = (uint8_t *) channel_stats; - qdf_mem_copy(results, t_radio_stats + WMI_TLV_HDR_SIZE, - radio_stats_size); + rs_results = (tSirWifiRadioStat *) &results[0]; + rs_results->radio = radio_stats->radio_id; + rs_results->onTime = radio_stats->on_time; + rs_results->txTime = radio_stats->tx_time; + rs_results->rxTime = radio_stats->rx_time; + rs_results->onTimeScan = radio_stats->on_time_scan; + rs_results->onTimeNbd = radio_stats->on_time_nbd; + rs_results->onTimeGscan = radio_stats->on_time_gscan; + rs_results->onTimeRoamScan = radio_stats->on_time_roam_scan; + rs_results->onTimePnoScan = radio_stats->on_time_pno_scan; + rs_results->onTimeHs20 = radio_stats->on_time_hs20; + rs_results->total_num_tx_power_levels = 0; + rs_results->tx_time_per_power_level = NULL; + rs_results->numChannels = radio_stats->num_channels; - next_res_offset = radio_stats_size; + chn_results = (tSirWifiChannelStats *) &rs_results->channels[0]; next_chan_offset = WMI_TLV_HDR_SIZE; WMA_LOGD("Channel Stats Info"); for (count = 0; count < radio_stats->num_channels; count++) { @@ -726,21 +851,23 @@ static int wma_unified_link_radio_stats_event_handler(void *handle, channel_stats->cca_busy_time); channel_stats++; - qdf_mem_copy(results + next_res_offset, + qdf_mem_copy(chn_results, t_channel_stats + next_chan_offset, chan_stats_size); - next_res_offset += chan_stats_size; + chn_results++; next_chan_offset += sizeof(*channel_stats); } - /* call hdd callback with Link Layer Statistics - * vdev_id/ifacId in link_stats_results will be - * used to retrieve the correct HDD context - */ + if (link_stats_results->moreResultToFollow) { + /* More results coming, don't post yet */ + return 0; + } + pMac->sme.pLinkLayerStatsIndCallback(pMac->hHdd, WMA_LINK_LAYER_STATS_RESULTS_RSP, link_stats_results); - qdf_mem_free(link_stats_results); + qdf_mem_free(wma_handle->link_stats_results); + wma_handle->link_stats_results = NULL; return 0; } @@ -771,6 +898,10 @@ void wma_register_ll_stats_event_handler(tp_wma_handle wma_handle) WMI_RADIO_LINK_STATS_EVENTID, wma_unified_link_radio_stats_event_handler, WMA_RX_SERIALIZER_CTX); + wmi_unified_register_event_handler(wma_handle->wmi_handle, + WMI_RADIO_TX_POWER_LEVEL_STATS_EVENTID, + wma_unified_radio_tx_power_level_stats_event_handler, + WMA_RX_SERIALIZER_CTX); return; }