| /* Copyright (c) 2015, 2018 The Linux Foundation. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * * Neither the name of The Linux Foundation nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * Changes from Qualcomm Innovation Center are provided under the following license: |
| * |
| * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted (subject to the limitations in the |
| * disclaimer below) provided that the following conditions are met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * |
| * * Neither the name of Qualcomm Innovation Center, Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE |
| * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT |
| * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR |
| * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
| * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER |
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "sync.h" |
| |
| #include "wifi_hal.h" |
| #include "common.h" |
| #include "cpp_bindings.h" |
| #include <errno.h> |
| #include <utils/Log.h> |
| #include "wifiloggercmd.h" |
| #include "rb_wrapper.h" |
| #include <stdlib.h> |
| |
| #define LOGGER_MEMDUMP_FILENAME "/proc/debug/fwdump" |
| #define DRIVER_MEMDUMP_FILENAME "/proc/debugdriver/driverdump" |
| #define LOGGER_MEMDUMP_CHUNKSIZE (4 * 1024) |
| #define DRIVER_MEMDUMP_MAX_FILESIZE (16 * 1024) |
| |
| char power_events_ring_name[] = "power_events_rb"; |
| char connectivity_events_ring_name[] = "connectivity_events_rb"; |
| char pkt_stats_ring_name[] = "pkt_stats_rb"; |
| char driver_prints_ring_name[] = "driver_prints_rb"; |
| char firmware_prints_ring_name[] = "firmware_prints_rb"; |
| |
| static int get_ring_id(hal_info *info, char *ring_name) |
| { |
| int rb_id; |
| |
| for (rb_id = 0; rb_id < NUM_RING_BUFS; rb_id++) { |
| if (is_rb_name_match(&info->rb_infos[rb_id], ring_name)) { |
| return rb_id; |
| } |
| } |
| return -1; |
| } |
| |
| //Implementation of the functions exposed in wifi_logger.h |
| |
| /* Function to intiate logging */ |
| wifi_error wifi_start_logging(wifi_interface_handle iface, |
| u32 verbose_level, u32 flags, |
| u32 max_interval_sec, u32 min_data_size, |
| char *buffer_name) |
| { |
| int requestId; |
| wifi_error ret; |
| WifiLoggerCommand *wifiLoggerCommand = NULL; |
| struct nlattr *nlData; |
| interface_info *ifaceInfo = getIfaceInfo(iface); |
| wifi_handle wifiHandle = getWifiHandle(iface); |
| hal_info *info = getHalInfo(wifiHandle); |
| int ring_id = 0; |
| |
| /* Check Supported logger capability */ |
| if (!(info->supported_logger_feature_set & LOGGER_RING_BUFFER)) { |
| ALOGE("%s: Ring buffer logging feature not supported %x", __FUNCTION__, |
| info->supported_logger_feature_set); |
| return WIFI_ERROR_NOT_SUPPORTED; |
| } |
| /* |
| * No request id from caller, so generate one and pass it on to the driver. |
| * Generate one randomly. |
| */ |
| requestId = get_requestid(); |
| |
| if (buffer_name == NULL) { |
| ALOGE("%s: Invalid Ring Name. \n", __FUNCTION__); |
| return WIFI_ERROR_UNKNOWN; |
| } |
| |
| ring_id = get_ring_id(info, buffer_name); |
| if (ring_id < 0) { |
| ALOGE("%s: Invalid Ring Buffer Name ", __FUNCTION__); |
| return WIFI_ERROR_UNKNOWN; |
| } |
| |
| wifiLoggerCommand = new WifiLoggerCommand( |
| wifiHandle, |
| requestId, |
| OUI_QCA, |
| QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START); |
| |
| if (wifiLoggerCommand == NULL) { |
| ALOGE("%s: Error WifiLoggerCommand NULL", __FUNCTION__); |
| return WIFI_ERROR_UNKNOWN; |
| } |
| /* Create the NL message. */ |
| ret = wifiLoggerCommand->create(); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| /* Set the interface Id of the message. */ |
| ret = wifiLoggerCommand->set_iface_id(ifaceInfo->name); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| /* Add the vendor specific attributes for the NL command. */ |
| nlData = wifiLoggerCommand->attr_start(NL80211_ATTR_VENDOR_DATA); |
| if (!nlData){ |
| ret = WIFI_ERROR_UNKNOWN; |
| goto cleanup; |
| } |
| |
| ret = wifiLoggerCommand->put_u32(QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID, |
| ring_id); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| ret = wifiLoggerCommand->put_u32( |
| QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL, |
| verbose_level); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| ret = wifiLoggerCommand->put_u32(QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS, |
| flags); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| wifiLoggerCommand->attr_end(nlData); |
| |
| /* Send the msg and wait for a response. */ |
| ret = wifiLoggerCommand->requestResponse(); |
| if (ret != WIFI_SUCCESS) |
| ALOGE("%s: Error %d happened. ", __FUNCTION__, ret); |
| |
| ALOGV("%s: Logging Started for %s. with verboselevel %d", |
| __FUNCTION__, buffer_name,verbose_level); |
| rb_start_logging(&info->rb_infos[ring_id], verbose_level, |
| flags, max_interval_sec, min_data_size); |
| cleanup: |
| delete wifiLoggerCommand; |
| return ret; |
| } |
| |
| /* Function to get each ring related info */ |
| wifi_error wifi_get_ring_buffers_status(wifi_interface_handle iface, |
| u32 *num_buffers, |
| wifi_ring_buffer_status *status) |
| { |
| wifi_handle wifiHandle = getWifiHandle(iface); |
| hal_info *info = getHalInfo(wifiHandle); |
| wifi_ring_buffer_status *rbs; |
| struct rb_info *rb_info; |
| int rb_id; |
| |
| /* Check Supported logger capability */ |
| if (!(info->supported_logger_feature_set & LOGGER_RING_BUFFER)) { |
| ALOGE("%s: Ring buffer logging feature not supported %x", __FUNCTION__, |
| info->supported_logger_feature_set); |
| return WIFI_ERROR_NOT_SUPPORTED; |
| } |
| |
| if ((*num_buffers) < NUM_RING_BUFS) { |
| ALOGE("%s: Input num_buffers:%u cannot be accommodated, " |
| "Total ring buffer num:%d", __FUNCTION__, *num_buffers, |
| NUM_RING_BUFS); |
| *num_buffers = 0; |
| return WIFI_ERROR_OUT_OF_MEMORY; |
| } |
| for (rb_id = 0; rb_id < NUM_RING_BUFS; rb_id++) { |
| rb_info = &info->rb_infos[rb_id]; |
| rbs = status + rb_id; |
| |
| get_rb_status(rb_info, rbs); |
| } |
| *num_buffers = NUM_RING_BUFS; |
| return WIFI_SUCCESS; |
| } |
| |
| void push_out_all_ring_buffers(hal_info *info) |
| { |
| int rb_id; |
| |
| for (rb_id = 0; rb_id < NUM_RING_BUFS; rb_id++) { |
| push_out_rb_data(&info->rb_infos[rb_id]); |
| } |
| } |
| |
| void send_alert(hal_info *info, int reason_code) |
| { |
| wifi_alert_handler handler; |
| char alert_msg[20] = "Fatal Event"; |
| pthread_mutex_lock(&info->ah_lock); |
| handler.on_alert = info->on_alert; |
| pthread_mutex_unlock(&info->ah_lock); |
| |
| if (handler.on_alert) { |
| handler.on_alert(0, alert_msg, strlen(alert_msg), reason_code); |
| } |
| } |
| |
| void WifiLoggerCommand::setFeatureSet(u32 *support) { |
| mSupportedSet = support; |
| } |
| |
| /* Function to get the supported feature set for logging.*/ |
| wifi_error wifi_get_logger_supported_feature_set(wifi_interface_handle iface, |
| u32 *support) |
| { |
| int requestId; |
| wifi_error ret; |
| WifiLoggerCommand *wifiLoggerCommand; |
| struct nlattr *nlData; |
| interface_info *ifaceInfo = getIfaceInfo(iface); |
| wifi_handle wifiHandle = getWifiHandle(iface); |
| |
| /* No request id from caller, so generate one and pass it on to the driver. |
| * Generate one randomly. |
| */ |
| requestId = get_requestid(); |
| |
| wifiLoggerCommand = new WifiLoggerCommand( |
| wifiHandle, |
| requestId, |
| OUI_QCA, |
| QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET); |
| |
| if (wifiLoggerCommand == NULL) { |
| ALOGE("%s: Error WifiLoggerCommand NULL", __FUNCTION__); |
| return WIFI_ERROR_UNKNOWN; |
| } |
| /* Create the NL message. */ |
| ret = wifiLoggerCommand->create(); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| /* Set the interface Id of the message. */ |
| ret = wifiLoggerCommand->set_iface_id(ifaceInfo->name); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| /* Add the vendor specific attributes for the NL command. */ |
| nlData = wifiLoggerCommand->attr_start(NL80211_ATTR_VENDOR_DATA); |
| if (!nlData){ |
| ret = WIFI_ERROR_UNKNOWN; |
| goto cleanup; |
| } |
| |
| ret = wifiLoggerCommand->put_u32(QCA_WLAN_VENDOR_ATTR_LOGGER_SUPPORTED, |
| requestId); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| wifiLoggerCommand->attr_end(nlData); |
| |
| wifiLoggerCommand->setFeatureSet(support); |
| |
| /* Send the msg and wait for a response. */ |
| ret = wifiLoggerCommand->requestResponse(); |
| if (ret != WIFI_SUCCESS) |
| ALOGE("%s: Error %d happened. ", __FUNCTION__, ret); |
| |
| cleanup: |
| delete wifiLoggerCommand; |
| return ret; |
| } |
| |
| /* Function to get the data in each ring for the given ring ID.*/ |
| wifi_error wifi_get_ring_data(wifi_interface_handle iface, |
| char *ring_name) |
| { |
| int requestId; |
| wifi_error ret; |
| WifiLoggerCommand *wifiLoggerCommand; |
| struct nlattr *nlData; |
| interface_info *ifaceInfo = getIfaceInfo(iface); |
| wifi_handle wifiHandle = getWifiHandle(iface); |
| hal_info *info = getHalInfo(wifiHandle); |
| int ring_id = 0; |
| |
| /* Check Supported logger capability */ |
| if (!(info->supported_logger_feature_set & LOGGER_RING_BUFFER)) { |
| ALOGE("%s: Ring buffer logging feature not supported %x", __FUNCTION__, |
| info->supported_logger_feature_set); |
| return WIFI_ERROR_NOT_SUPPORTED; |
| } |
| |
| ring_id = get_ring_id(info, ring_name); |
| if (ring_id < 0) { |
| ALOGE("%s: Invalid Ring Buffer Name ", __FUNCTION__); |
| return WIFI_ERROR_UNKNOWN; |
| } |
| |
| requestId = get_requestid(); |
| |
| wifiLoggerCommand = new WifiLoggerCommand( |
| wifiHandle, |
| requestId, |
| OUI_QCA, |
| QCA_NL80211_VENDOR_SUBCMD_GET_RING_DATA); |
| if (wifiLoggerCommand == NULL) { |
| ALOGE("%s: Error WifiLoggerCommand NULL", __FUNCTION__); |
| return WIFI_ERROR_UNKNOWN; |
| } |
| /* Create the NL message. */ |
| ret = wifiLoggerCommand->create(); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| /* Set the interface Id of the message. */ |
| ret = wifiLoggerCommand->set_iface_id(ifaceInfo->name); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| /* Add the vendor specific attributes for the NL command. */ |
| nlData = wifiLoggerCommand->attr_start(NL80211_ATTR_VENDOR_DATA); |
| if (!nlData){ |
| ret = WIFI_ERROR_UNKNOWN; |
| goto cleanup; |
| } |
| |
| if (wifiLoggerCommand->put_u32( |
| QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID, ring_id)) |
| { |
| ret = WIFI_ERROR_UNKNOWN; |
| goto cleanup; |
| } |
| wifiLoggerCommand->attr_end(nlData); |
| |
| /* Send the msg and wait for a response. */ |
| ret = wifiLoggerCommand->requestResponse(); |
| if (ret != WIFI_SUCCESS) |
| ALOGE("%s: Error %d happened. ", __FUNCTION__, ret); |
| |
| cleanup: |
| delete wifiLoggerCommand; |
| return ret; |
| } |
| |
| void WifiLoggerCommand::setVersionInfo(char *buffer, int buffer_size) { |
| mVersion = buffer; |
| mVersionLen = buffer_size; |
| } |
| |
| /* Function to send enable request to the wifi driver.*/ |
| wifi_error wifi_get_firmware_version(wifi_interface_handle iface, |
| char *buffer, int buffer_size) |
| { |
| int requestId; |
| wifi_error ret; |
| WifiLoggerCommand *wifiLoggerCommand; |
| struct nlattr *nlData; |
| interface_info *ifaceInfo = getIfaceInfo(iface); |
| wifi_handle wifiHandle = getWifiHandle(iface); |
| |
| /* No request id from caller, so generate one and pass it on to the driver. |
| * Generate one randomly. |
| */ |
| requestId = get_requestid_u8(); |
| |
| wifiLoggerCommand = new WifiLoggerCommand( |
| wifiHandle, |
| requestId, |
| OUI_QCA, |
| QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO); |
| if (wifiLoggerCommand == NULL) { |
| ALOGE("%s: Error WifiLoggerCommand NULL", __FUNCTION__); |
| return WIFI_ERROR_UNKNOWN; |
| } |
| /* Create the NL message. */ |
| ret = wifiLoggerCommand->create(); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| /* Set the interface Id of the message. */ |
| ret = wifiLoggerCommand->set_iface_id(ifaceInfo->name); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| /* Add the vendor specific attributes for the NL command. */ |
| nlData = wifiLoggerCommand->attr_start(NL80211_ATTR_VENDOR_DATA); |
| if (!nlData) |
| goto cleanup; |
| |
| ret = wifiLoggerCommand->put_u8( |
| QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION, requestId); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| wifiLoggerCommand->attr_end(nlData); |
| |
| wifiLoggerCommand->setVersionInfo(buffer, buffer_size); |
| |
| /* Send the msg and wait for a response. */ |
| ret = wifiLoggerCommand->requestResponse(); |
| if (ret != WIFI_SUCCESS) |
| ALOGE("%s: Error %d happened. ", __FUNCTION__, ret); |
| |
| cleanup: |
| delete wifiLoggerCommand; |
| return ret; |
| |
| } |
| |
| /* Function to get wlan driver version.*/ |
| wifi_error wifi_get_driver_version(wifi_interface_handle iface, |
| char *buffer, int buffer_size) |
| { |
| |
| int requestId; |
| wifi_error ret; |
| WifiLoggerCommand *wifiLoggerCommand; |
| struct nlattr *nlData; |
| interface_info *ifaceInfo = getIfaceInfo(iface); |
| wifi_handle wifiHandle = getWifiHandle(iface); |
| |
| /* No request id from caller, so generate one and pass it on to the driver. |
| * Generate one randomly. |
| */ |
| requestId = get_requestid_u8(); |
| |
| wifiLoggerCommand = new WifiLoggerCommand( |
| wifiHandle, |
| requestId, |
| OUI_QCA, |
| QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO); |
| if (wifiLoggerCommand == NULL) { |
| ALOGE("%s: Error WifiLoggerCommand NULL", __FUNCTION__); |
| return WIFI_ERROR_UNKNOWN; |
| } |
| /* Create the NL message. */ |
| ret = wifiLoggerCommand->create(); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| /* Set the interface Id of the message. */ |
| ret = wifiLoggerCommand->set_iface_id(ifaceInfo->name); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| /* Add the vendor specific attributes for the NL command. */ |
| nlData = wifiLoggerCommand->attr_start(NL80211_ATTR_VENDOR_DATA); |
| if (!nlData){ |
| ret = WIFI_ERROR_UNKNOWN; |
| goto cleanup; |
| } |
| |
| ret = wifiLoggerCommand->put_u8( |
| QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION, requestId); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| wifiLoggerCommand->attr_end(nlData); |
| |
| wifiLoggerCommand->setVersionInfo(buffer, buffer_size); |
| |
| /* Send the msg and wait for a response. */ |
| ret = wifiLoggerCommand->requestResponse(); |
| if (ret != WIFI_SUCCESS) |
| ALOGE("%s: Error %d happened. ", __FUNCTION__, ret); |
| |
| cleanup: |
| delete wifiLoggerCommand; |
| return ret; |
| } |
| |
| |
| /* Function to get the Firmware memory dump. */ |
| wifi_error wifi_get_firmware_memory_dump(wifi_interface_handle iface, |
| wifi_firmware_memory_dump_handler handler) |
| { |
| wifi_error ret; |
| int requestId; |
| WifiLoggerCommand *wifiLoggerCommand; |
| struct nlattr *nlData; |
| interface_info *ifaceInfo = getIfaceInfo(iface); |
| wifi_handle wifiHandle = getWifiHandle(iface); |
| hal_info *info = getHalInfo(wifiHandle); |
| |
| /* Check Supported logger capability */ |
| if (!(info->supported_logger_feature_set & |
| WIFI_LOGGER_MEMORY_DUMP_SUPPORTED)) { |
| ALOGE("%s: Firmware memory dump logging feature not supported %x", |
| __FUNCTION__, info->supported_logger_feature_set); |
| return WIFI_ERROR_NOT_SUPPORTED; |
| } |
| |
| /* No request id from caller, so generate one and pass it on to the driver. |
| * Generate one randomly. |
| */ |
| requestId = get_requestid(); |
| |
| wifiLoggerCommand = new WifiLoggerCommand( |
| wifiHandle, |
| requestId, |
| OUI_QCA, |
| QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_MEMORY_DUMP); |
| if (wifiLoggerCommand == NULL) { |
| ALOGE("%s: Error WifiLoggerCommand NULL", __FUNCTION__); |
| return WIFI_ERROR_UNKNOWN; |
| } |
| /* Create the NL message. */ |
| ret = wifiLoggerCommand->create(); |
| |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| /* Set the interface Id of the message. */ |
| ret = wifiLoggerCommand->set_iface_id(ifaceInfo->name); |
| |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| /* Add the vendor specific attributes for the NL command. */ |
| nlData = wifiLoggerCommand->attr_start(NL80211_ATTR_VENDOR_DATA); |
| if (!nlData){ |
| ret = WIFI_ERROR_UNKNOWN; |
| goto cleanup; |
| } |
| |
| wifiLoggerCommand->attr_end(nlData); |
| |
| /* copy the callback into callback handler */ |
| WifiLoggerCallbackHandler callbackHandler; |
| memset(&callbackHandler, 0, sizeof(callbackHandler)); |
| callbackHandler.on_firmware_memory_dump = \ |
| handler.on_firmware_memory_dump; |
| |
| ret = wifiLoggerCommand->setCallbackHandler(callbackHandler); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| /* Send the msg and wait for the memory dump response */ |
| ret = wifiLoggerCommand->requestResponse(); |
| if (ret != WIFI_SUCCESS) |
| ALOGE("%s: Error %d happened. ", __FUNCTION__, ret); |
| |
| cleanup: |
| delete wifiLoggerCommand; |
| return ret; |
| } |
| |
| wifi_error wifi_set_log_handler(wifi_request_id id, |
| wifi_interface_handle iface, |
| wifi_ring_buffer_data_handler handler) |
| { |
| wifi_handle wifiHandle = getWifiHandle(iface); |
| hal_info *info = getHalInfo(wifiHandle); |
| |
| pthread_mutex_lock(&info->lh_lock); |
| info->on_ring_buffer_data = handler.on_ring_buffer_data; |
| pthread_mutex_unlock(&info->lh_lock); |
| if (handler.on_ring_buffer_data == NULL) { |
| ALOGE("Set log handler is NULL"); |
| return WIFI_ERROR_UNKNOWN; |
| } |
| return WIFI_SUCCESS; |
| } |
| |
| wifi_error wifi_reset_log_handler(wifi_request_id id, |
| wifi_interface_handle iface) |
| { |
| wifi_handle wifiHandle = getWifiHandle(iface); |
| hal_info *info = getHalInfo(wifiHandle); |
| |
| pthread_mutex_lock(&info->lh_lock); |
| info->on_ring_buffer_data = NULL; |
| pthread_mutex_unlock(&info->lh_lock); |
| return WIFI_SUCCESS; |
| } |
| |
| wifi_error wifi_set_alert_handler(wifi_request_id id, |
| wifi_interface_handle iface, |
| wifi_alert_handler handler) |
| { |
| wifi_handle wifiHandle = getWifiHandle(iface); |
| hal_info *info = getHalInfo(wifiHandle); |
| |
| if (handler.on_alert == NULL) { |
| ALOGE("Set alert handler is NULL"); |
| return WIFI_ERROR_UNKNOWN; |
| } |
| pthread_mutex_lock(&info->ah_lock); |
| info->on_alert = handler.on_alert; |
| pthread_mutex_unlock(&info->ah_lock); |
| return WIFI_SUCCESS; |
| } |
| |
| wifi_error wifi_reset_alert_handler(wifi_request_id id, |
| wifi_interface_handle iface) |
| { |
| wifi_handle wifiHandle = getWifiHandle(iface); |
| hal_info *info = getHalInfo(wifiHandle); |
| |
| pthread_mutex_lock(&info->ah_lock); |
| info->on_alert = NULL; |
| pthread_mutex_unlock(&info->ah_lock); |
| return WIFI_SUCCESS; |
| } |
| |
| |
| /** |
| API to start packet fate monitoring. |
| - Once stared, monitoring should remain active until HAL is unloaded. |
| - When HAL is unloaded, all packet fate buffers should be cleared. |
| */ |
| wifi_error wifi_start_pkt_fate_monitoring(wifi_interface_handle iface) |
| { |
| wifi_handle wifiHandle = getWifiHandle(iface); |
| hal_info *info = getHalInfo(wifiHandle); |
| |
| /* Check Supported logger capability */ |
| if (!(info->supported_logger_feature_set & |
| WIFI_LOGGER_PACKET_FATE_SUPPORTED)) { |
| ALOGE("%s: packet fate logging feature not supported %x", |
| __FUNCTION__, info->supported_logger_feature_set); |
| return WIFI_ERROR_NOT_SUPPORTED; |
| } |
| |
| if (info->fate_monitoring_enabled == true) { |
| ALOGV("Packet monitoring is already enabled"); |
| return WIFI_SUCCESS; |
| } |
| |
| info->pkt_fate_stats = (packet_fate_monitor_info *) malloc ( |
| sizeof(packet_fate_monitor_info)); |
| if (info->pkt_fate_stats == NULL) { |
| ALOGE("Failed to allocate memory for : %zu bytes", |
| sizeof(packet_fate_monitor_info)); |
| return WIFI_ERROR_OUT_OF_MEMORY; |
| } |
| memset(info->pkt_fate_stats, 0, sizeof(packet_fate_monitor_info)); |
| |
| pthread_mutex_lock(&info->pkt_fate_stats_lock); |
| info->fate_monitoring_enabled = true; |
| pthread_mutex_unlock(&info->pkt_fate_stats_lock); |
| |
| return WIFI_SUCCESS; |
| } |
| |
| |
| /** |
| API to retrieve fates of outbound packets. |
| - HAL implementation should fill |tx_report_bufs| with fates of |
| _first_ min(n_requested_fates, actual packets) frames |
| transmitted for the most recent association. The fate reports |
| should follow the same order as their respective packets. |
| - Packets reported by firmware, but not recognized by driver |
| should be included. However, the ordering of the corresponding |
| reports is at the discretion of HAL implementation. |
| - Framework may call this API multiple times for the same association. |
| - Framework will ensure |n_requested_fates <= MAX_FATE_LOG_LEN|. |
| - Framework will allocate and free the referenced storage. |
| */ |
| wifi_error wifi_get_tx_pkt_fates(wifi_interface_handle iface, |
| wifi_tx_report *tx_report_bufs, |
| size_t n_requested_fates, |
| size_t *n_provided_fates) |
| { |
| wifi_handle wifiHandle = getWifiHandle(iface); |
| hal_info *info = getHalInfo(wifiHandle); |
| wifi_tx_report_i *tx_fate_stats; |
| size_t i; |
| |
| if (info->fate_monitoring_enabled != true) { |
| ALOGE("Packet monitoring is not yet triggered"); |
| return WIFI_ERROR_UNINITIALIZED; |
| } |
| pthread_mutex_lock(&info->pkt_fate_stats_lock); |
| |
| tx_fate_stats = &info->pkt_fate_stats->tx_fate_stats[0]; |
| |
| *n_provided_fates = min(n_requested_fates, |
| info->pkt_fate_stats->n_tx_stats_collected); |
| |
| for (i=0; i < *n_provided_fates; i++) { |
| memcpy(tx_report_bufs[i].md5_prefix, |
| tx_fate_stats[i].md5_prefix, MD5_PREFIX_LEN); |
| tx_report_bufs[i].fate = tx_fate_stats[i].fate; |
| tx_report_bufs[i].frame_inf.payload_type = |
| tx_fate_stats[i].frame_inf.payload_type; |
| tx_report_bufs[i].frame_inf.driver_timestamp_usec = |
| tx_fate_stats[i].frame_inf.driver_timestamp_usec; |
| tx_report_bufs[i].frame_inf.firmware_timestamp_usec = |
| tx_fate_stats[i].frame_inf.firmware_timestamp_usec; |
| tx_report_bufs[i].frame_inf.frame_len = |
| tx_fate_stats[i].frame_inf.frame_len; |
| |
| if (tx_report_bufs[i].frame_inf.payload_type == FRAME_TYPE_ETHERNET_II) |
| memcpy(tx_report_bufs[i].frame_inf.frame_content.ethernet_ii_bytes, |
| tx_fate_stats[i].frame_inf.frame_content, |
| min(tx_fate_stats[i].frame_inf.frame_len, |
| MAX_FRAME_LEN_ETHERNET)); |
| else if (tx_report_bufs[i].frame_inf.payload_type == |
| FRAME_TYPE_80211_MGMT) |
| memcpy( |
| tx_report_bufs[i].frame_inf.frame_content.ieee_80211_mgmt_bytes, |
| tx_fate_stats[i].frame_inf.frame_content, |
| min(tx_fate_stats[i].frame_inf.frame_len, |
| MAX_FRAME_LEN_80211_MGMT)); |
| else |
| /* Currently framework is interested only two types( |
| * FRAME_TYPE_ETHERNET_II and FRAME_TYPE_80211_MGMT) of packets, so |
| * ignore the all other types of packets received from driver */ |
| ALOGI("Unknown format packet"); |
| } |
| pthread_mutex_unlock(&info->pkt_fate_stats_lock); |
| |
| return WIFI_SUCCESS; |
| } |
| |
| /** |
| API to retrieve fates of inbound packets. |
| - HAL implementation should fill |rx_report_bufs| with fates of |
| _first_ min(n_requested_fates, actual packets) frames |
| received for the most recent association. The fate reports |
| should follow the same order as their respective packets. |
| - Packets reported by firmware, but not recognized by driver |
| should be included. However, the ordering of the corresponding |
| reports is at the discretion of HAL implementation. |
| - Framework may call this API multiple times for the same association. |
| - Framework will ensure |n_requested_fates <= MAX_FATE_LOG_LEN|. |
| - Framework will allocate and free the referenced storage. |
| */ |
| wifi_error wifi_get_rx_pkt_fates(wifi_interface_handle iface, |
| wifi_rx_report *rx_report_bufs, |
| size_t n_requested_fates, |
| size_t *n_provided_fates) |
| { |
| wifi_handle wifiHandle = getWifiHandle(iface); |
| hal_info *info = getHalInfo(wifiHandle); |
| wifi_rx_report_i *rx_fate_stats; |
| size_t i; |
| |
| if (info->fate_monitoring_enabled != true) { |
| ALOGE("Packet monitoring is not yet triggered"); |
| return WIFI_ERROR_UNINITIALIZED; |
| } |
| pthread_mutex_lock(&info->pkt_fate_stats_lock); |
| |
| rx_fate_stats = &info->pkt_fate_stats->rx_fate_stats[0]; |
| |
| *n_provided_fates = min(n_requested_fates, |
| info->pkt_fate_stats->n_rx_stats_collected); |
| |
| for (i=0; i < *n_provided_fates; i++) { |
| memcpy(rx_report_bufs[i].md5_prefix, |
| rx_fate_stats[i].md5_prefix, MD5_PREFIX_LEN); |
| rx_report_bufs[i].fate = rx_fate_stats[i].fate; |
| rx_report_bufs[i].frame_inf.payload_type = |
| rx_fate_stats[i].frame_inf.payload_type; |
| rx_report_bufs[i].frame_inf.driver_timestamp_usec = |
| rx_fate_stats[i].frame_inf.driver_timestamp_usec; |
| rx_report_bufs[i].frame_inf.firmware_timestamp_usec = |
| rx_fate_stats[i].frame_inf.firmware_timestamp_usec; |
| rx_report_bufs[i].frame_inf.frame_len = |
| rx_fate_stats[i].frame_inf.frame_len; |
| |
| if (rx_report_bufs[i].frame_inf.payload_type == FRAME_TYPE_ETHERNET_II) |
| memcpy(rx_report_bufs[i].frame_inf.frame_content.ethernet_ii_bytes, |
| rx_fate_stats[i].frame_inf.frame_content, |
| min(rx_fate_stats[i].frame_inf.frame_len, |
| MAX_FRAME_LEN_ETHERNET)); |
| else if (rx_report_bufs[i].frame_inf.payload_type == |
| FRAME_TYPE_80211_MGMT) |
| memcpy( |
| rx_report_bufs[i].frame_inf.frame_content.ieee_80211_mgmt_bytes, |
| rx_fate_stats[i].frame_inf.frame_content, |
| min(rx_fate_stats[i].frame_inf.frame_len, |
| MAX_FRAME_LEN_80211_MGMT)); |
| else |
| /* Currently framework is interested only two types( |
| * FRAME_TYPE_ETHERNET_II and FRAME_TYPE_80211_MGMT) of packets, so |
| * ignore the all other types of packets received from driver */ |
| ALOGI("Unknown format packet"); |
| } |
| pthread_mutex_unlock(&info->pkt_fate_stats_lock); |
| |
| return WIFI_SUCCESS; |
| } |
| |
| WifiLoggerCommand::WifiLoggerCommand(wifi_handle handle, int id, u32 vendor_id, u32 subcmd) |
| : WifiVendorCommand(handle, id, vendor_id, subcmd) |
| { |
| mVersion = NULL; |
| mVersionLen = 0; |
| mRequestId = id; |
| memset(&mHandler, 0,sizeof(mHandler)); |
| mWaitforRsp = false; |
| mMoreData = false; |
| mSupportedSet = NULL; |
| } |
| |
| WifiLoggerCommand::~WifiLoggerCommand() |
| { |
| unregisterVendorHandler(mVendor_id, mSubcmd); |
| } |
| |
| /* This function implements creation of Vendor command */ |
| wifi_error WifiLoggerCommand::create() { |
| wifi_error ret = mMsg.create(NL80211_CMD_VENDOR, 0, 0); |
| if (ret != WIFI_SUCCESS) |
| return ret; |
| |
| /* Insert the oui in the msg */ |
| ret = mMsg.put_u32(NL80211_ATTR_VENDOR_ID, mVendor_id); |
| if (ret != WIFI_SUCCESS) |
| goto out; |
| /* Insert the subcmd in the msg */ |
| ret = mMsg.put_u32(NL80211_ATTR_VENDOR_SUBCMD, mSubcmd); |
| if (ret != WIFI_SUCCESS) |
| goto out; |
| |
| ALOGV("%s: mVendor_id = %d, Subcmd = %d.", |
| __FUNCTION__, mVendor_id, mSubcmd); |
| |
| out: |
| return ret; |
| } |
| |
| void rb_timerhandler(hal_info *info) |
| { |
| struct timeval now; |
| int rb_id; |
| |
| gettimeofday(&now,NULL); |
| for (rb_id = 0; rb_id < NUM_RING_BUFS; rb_id++) { |
| rb_check_for_timeout(&info->rb_infos[rb_id], &now); |
| } |
| } |
| |
| wifi_error wifi_logger_ring_buffers_init(hal_info *info) |
| { |
| wifi_error ret; |
| |
| /* Check Supported logger capability */ |
| if (!(info->supported_logger_feature_set & LOGGER_RING_BUFFER)) { |
| ALOGE("%s: Ring buffer logging feature not supported %x", __FUNCTION__, |
| info->supported_logger_feature_set); |
| return WIFI_ERROR_NOT_SUPPORTED; |
| } |
| |
| ret = rb_init(info, &info->rb_infos[POWER_EVENTS_RB_ID], |
| POWER_EVENTS_RB_ID, |
| POWER_EVENTS_RB_BUF_SIZE, |
| POWER_EVENTS_NUM_BUFS, |
| power_events_ring_name); |
| if (ret != WIFI_SUCCESS) { |
| ALOGE("Failed to initialize power events ring buffer"); |
| goto cleanup; |
| } |
| |
| ret = rb_init(info, &info->rb_infos[CONNECTIVITY_EVENTS_RB_ID], |
| CONNECTIVITY_EVENTS_RB_ID, |
| CONNECTIVITY_EVENTS_RB_BUF_SIZE, |
| CONNECTIVITY_EVENTS_NUM_BUFS, |
| connectivity_events_ring_name); |
| if (ret != WIFI_SUCCESS) { |
| ALOGE("Failed to initialize connectivity events ring buffer"); |
| goto cleanup; |
| } |
| |
| ret = rb_init(info, &info->rb_infos[PKT_STATS_RB_ID], |
| PKT_STATS_RB_ID, |
| PKT_STATS_RB_BUF_SIZE, |
| PKT_STATS_NUM_BUFS, |
| pkt_stats_ring_name); |
| if (ret != WIFI_SUCCESS) { |
| ALOGE("Failed to initialize per packet stats ring buffer"); |
| goto cleanup; |
| } |
| |
| ret = rb_init(info, &info->rb_infos[DRIVER_PRINTS_RB_ID], |
| DRIVER_PRINTS_RB_ID, |
| DRIVER_PRINTS_RB_BUF_SIZE, |
| DRIVER_PRINTS_NUM_BUFS, |
| driver_prints_ring_name); |
| if (ret != WIFI_SUCCESS) { |
| ALOGE("Failed to initialize driver prints ring buffer"); |
| goto cleanup; |
| } |
| |
| ret = rb_init(info, &info->rb_infos[FIRMWARE_PRINTS_RB_ID], |
| FIRMWARE_PRINTS_RB_ID, |
| FIRMWARE_PRINTS_RB_BUF_SIZE, |
| FIRMWARE_PRINTS_NUM_BUFS, |
| firmware_prints_ring_name); |
| if (ret != WIFI_SUCCESS) { |
| ALOGE("Failed to initialize firmware prints ring buffer"); |
| goto cleanup; |
| } |
| |
| pthread_mutex_init(&info->lh_lock, NULL); |
| pthread_mutex_init(&info->ah_lock, NULL); |
| |
| return ret; |
| |
| cleanup: |
| wifi_logger_ring_buffers_deinit(info); |
| return ret; |
| } |
| |
| void wifi_logger_ring_buffers_deinit(hal_info *info) |
| { |
| int i; |
| |
| if (!(info->supported_logger_feature_set & LOGGER_RING_BUFFER)) |
| return; |
| |
| for (i = 0; i < NUM_RING_BUFS; i++) { |
| rb_deinit(&info->rb_infos[i]); |
| } |
| pthread_mutex_destroy(&info->lh_lock); |
| pthread_mutex_destroy(&info->ah_lock); |
| } |
| |
| |
| /* Callback handlers registered for nl message send */ |
| static int error_handler_wifi_logger(struct sockaddr_nl *nla, |
| struct nlmsgerr *err, |
| void *arg) |
| { |
| struct sockaddr_nl *tmp; |
| int *ret = (int *)arg; |
| tmp = nla; |
| *ret = err->error; |
| ALOGE("%s: Error code:%d (%s)", __FUNCTION__, *ret, strerror(-(*ret))); |
| return NL_STOP; |
| } |
| |
| /* Callback handlers registered for nl message send */ |
| static int ack_handler_wifi_logger(struct nl_msg *msg, void *arg) |
| { |
| int *ret = (int *)arg; |
| struct nl_msg * a; |
| |
| a = msg; |
| *ret = 0; |
| return NL_STOP; |
| } |
| |
| /* Callback handlers registered for nl message send */ |
| static int finish_handler_wifi_logger(struct nl_msg *msg, void *arg) |
| { |
| int *ret = (int *)arg; |
| struct nl_msg * a; |
| |
| a = msg; |
| *ret = 0; |
| return NL_SKIP; |
| } |
| |
| wifi_error WifiLoggerCommand::requestEvent() |
| { |
| int status; |
| wifi_error res = WIFI_SUCCESS; |
| struct nl_cb *cb = NULL; |
| |
| cb = nl_cb_alloc(NL_CB_DEFAULT); |
| if (!cb) { |
| ALOGE("%s: Callback allocation failed",__FUNCTION__); |
| res = WIFI_ERROR_OUT_OF_MEMORY; |
| goto out; |
| } |
| |
| /* Send message */ |
| status = nl_send_auto_complete(mInfo->cmd_sock, mMsg.getMessage()); |
| if (status < 0) { |
| res = mapKernelErrortoWifiHalError(status); |
| goto out; |
| } |
| |
| status = 1; |
| |
| nl_cb_err(cb, NL_CB_CUSTOM, error_handler_wifi_logger, &status); |
| nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler_wifi_logger, &status); |
| nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler_wifi_logger, &status); |
| |
| /* Err is populated as part of finish_handler. */ |
| while (status > 0){ |
| nl_recvmsgs(mInfo->cmd_sock, cb); |
| } |
| |
| ALOGV("%s: Msg sent, status=%d, mWaitForRsp=%d", __FUNCTION__, status, mWaitforRsp); |
| /* Only wait for the asynchronous event if HDD returns success, res=0 */ |
| if (!status && (mWaitforRsp == true)) { |
| struct timespec abstime; |
| abstime.tv_sec = 4; |
| abstime.tv_nsec = 0; |
| res = mCondition.wait(abstime); |
| if (res == WIFI_ERROR_TIMED_OUT) |
| ALOGE("%s: Time out happened.", __FUNCTION__); |
| |
| ALOGV("%s: Command invoked return value:%d, mWaitForRsp=%d", |
| __FUNCTION__, res, mWaitforRsp); |
| } |
| out: |
| nl_cb_put(cb); |
| /* Cleanup the mMsg */ |
| mMsg.destroy(); |
| return res; |
| } |
| |
| wifi_error WifiLoggerCommand::requestResponse() |
| { |
| return WifiCommand::requestResponse(mMsg); |
| } |
| |
| int WifiLoggerCommand::handleResponse(WifiEvent &reply) { |
| int len = 0, version; |
| char version_type[20]; |
| char* memBuffer = NULL; |
| FILE* memDumpFilePtr = NULL; |
| WifiVendorCommand::handleResponse(reply); |
| |
| memset(version_type, 0, 20); |
| switch(mSubcmd) |
| { |
| case QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO: |
| { |
| struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX + 1]; |
| |
| nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX, |
| (struct nlattr *)mVendorData, mDataLen, NULL); |
| |
| if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION]) { |
| len = nla_len(tb_vendor[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION]); |
| memcpy(version_type, "Driver", strlen("Driver")); |
| version = QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION; |
| } else if ( |
| tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION]) { |
| len = nla_len( |
| tb_vendor[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION]); |
| memcpy(version_type, "Firmware", strlen("Firmware")); |
| version = QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION; |
| } |
| if (len && mVersion && mVersionLen) { |
| memset(mVersion, 0, mVersionLen); |
| /* if len is greater than the incoming length then |
| accommodate 1 lesser than mVersionLen to have the |
| string terminated with '\0' */ |
| len = (len > mVersionLen)? (mVersionLen - 1) : len; |
| memcpy(mVersion, nla_data(tb_vendor[version]), len); |
| ALOGV("%s: WLAN %s version : %s ", __FUNCTION__, |
| version_type, mVersion); |
| } |
| } |
| break; |
| case QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET: |
| { |
| struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_LOGGER_MAX + 1]; |
| |
| nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_LOGGER_MAX, |
| (struct nlattr *)mVendorData, mDataLen, NULL); |
| |
| if (tb_vendor[QCA_WLAN_VENDOR_ATTR_LOGGER_SUPPORTED]) { |
| *mSupportedSet = |
| nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LOGGER_SUPPORTED]); |
| #ifdef QC_HAL_DEBUG |
| ALOGV("%s: Supported Feature Set : val 0x%x", |
| __FUNCTION__, *mSupportedSet); |
| #endif |
| } |
| } |
| break; |
| |
| case QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_MEMORY_DUMP: |
| { |
| u32 memDumpSize = 0; |
| int numRecordsRead = 0; |
| u32 remaining = 0; |
| char* buffer = NULL; |
| struct nlattr *tbVendor[ |
| QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_MAX + 1]; |
| |
| nla_parse(tbVendor, QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_MAX, |
| (struct nlattr *)mVendorData, |
| mDataLen, NULL); |
| |
| if (!tbVendor[ |
| QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_MEMDUMP_SIZE]) { |
| ALOGE("%s: LOGGER_RESULTS_MEMDUMP_SIZE not" |
| "found", __FUNCTION__); |
| break; |
| } |
| |
| memDumpSize = nla_get_u32( |
| tbVendor[QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_MEMDUMP_SIZE] |
| ); |
| |
| /* Allocate the memory indicated in memDumpSize */ |
| memBuffer = (char*) malloc(sizeof(char) * memDumpSize); |
| if (memBuffer == NULL) { |
| ALOGE("%s: No Memory for allocating Buffer size of %d", |
| __func__, memDumpSize); |
| break; |
| } |
| memset(memBuffer, 0, sizeof(char) * memDumpSize); |
| |
| ALOGI("%s: Memory Dump size: %u", __func__, |
| memDumpSize); |
| |
| /* Open the proc or debugfs filesystem */ |
| memDumpFilePtr = fopen(LOGGER_MEMDUMP_FILENAME, "r"); |
| if (memDumpFilePtr == NULL) { |
| ALOGE("Failed to open %s file", LOGGER_MEMDUMP_FILENAME); |
| break; |
| } |
| |
| /* Read the memDumpSize value at once */ |
| numRecordsRead = fread(memBuffer, 1, memDumpSize, |
| memDumpFilePtr); |
| if (numRecordsRead <= 0 || |
| numRecordsRead != (int) memDumpSize) { |
| ALOGE("%s: Read %d failed for reading at once.", |
| __func__, numRecordsRead); |
| /* Lets try to read in chunks */ |
| rewind(memDumpFilePtr); |
| remaining = memDumpSize; |
| buffer = memBuffer; |
| while (remaining) { |
| u32 readSize = 0; |
| if (remaining >= LOGGER_MEMDUMP_CHUNKSIZE) { |
| readSize = LOGGER_MEMDUMP_CHUNKSIZE; |
| } |
| else { |
| readSize = remaining; |
| } |
| numRecordsRead = fread(buffer, 1, |
| readSize, memDumpFilePtr); |
| if (numRecordsRead) { |
| remaining -= readSize; |
| buffer += readSize; |
| ALOGV("%s: Read successful for size:%u " |
| "remaining:%u", __func__, readSize, |
| remaining); |
| } |
| else { |
| ALOGE("%s: Chunk read failed for size:%u", |
| __func__, readSize); |
| break; |
| } |
| } |
| } |
| |
| /* After successful read, call the callback handler*/ |
| if (mHandler.on_firmware_memory_dump) { |
| mHandler.on_firmware_memory_dump(memBuffer, |
| memDumpSize); |
| |
| } |
| } |
| break; |
| case QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS: |
| { |
| struct nlattr *tbVendor[QCA_WLAN_VENDOR_GET_WAKE_STATS_MAX+1]; |
| |
| /* parse and extract wake reason stats */ |
| nla_parse(tbVendor, QCA_WLAN_VENDOR_GET_WAKE_STATS_MAX, |
| (struct nlattr *)mVendorData, |
| mDataLen, NULL); |
| |
| mGetWakeStats->cmd_event_wake_cnt_used = 0; |
| |
| mGetWakeStats->driver_fw_local_wake_cnt_used = 0; |
| |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_RX_DATA_WAKE]) { |
| ALOGE("%s: TOTAL_RX_DATA_WAKE not found", __FUNCTION__); |
| break; |
| } |
| mGetWakeStats->total_rx_data_wake = nla_get_u32(tbVendor[ |
| QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_RX_DATA_WAKE]); |
| |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_UNICAST_CNT]) { |
| ALOGE("%s: RX_UNICAST_CNT not found", __FUNCTION__); |
| break; |
| } |
| mGetWakeStats->rx_wake_details.rx_unicast_cnt = nla_get_u32( |
| tbVendor[QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_UNICAST_CNT]); |
| |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_MULTICAST_CNT]) { |
| ALOGE("%s: RX_MULTICAST_CNT not found", __FUNCTION__); |
| break; |
| } |
| mGetWakeStats->rx_wake_details.rx_multicast_cnt = nla_get_u32( |
| tbVendor[QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_MULTICAST_CNT]); |
| |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_BROADCAST_CNT]) { |
| ALOGE("%s: RX_BROADCAST_CNT not found", __FUNCTION__); |
| break; |
| } |
| mGetWakeStats->rx_wake_details.rx_broadcast_cnt = nla_get_u32( |
| tbVendor[QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_BROADCAST_CNT]); |
| |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP_PKT]) { |
| ALOGE("%s: ICMP_PKT not found", __FUNCTION__); |
| break; |
| } |
| mGetWakeStats->rx_wake_pkt_classification_info.icmp_pkt = |
| nla_get_u32(tbVendor[QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP_PKT]); |
| |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_PKT]) { |
| ALOGE("%s: ICMP6_PKT not found", __FUNCTION__); |
| break; |
| } |
| mGetWakeStats->rx_wake_pkt_classification_info.icmp6_pkt = |
| nla_get_u32(tbVendor[QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_PKT]); |
| |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_RA]) { |
| ALOGE("%s: ICMP6_RA not found", __FUNCTION__); |
| break; |
| } |
| mGetWakeStats->rx_wake_pkt_classification_info.icmp6_ra = |
| nla_get_u32(tbVendor[QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_RA]); |
| |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_NA]) { |
| ALOGE("%s: ICMP6_NA not found", __FUNCTION__); |
| break; |
| } |
| mGetWakeStats->rx_wake_pkt_classification_info.icmp6_na = |
| nla_get_u32(tbVendor[QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_NA]); |
| |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_NS]) { |
| ALOGE("%s: ICMP6_NS not found", __FUNCTION__); |
| break; |
| } |
| mGetWakeStats->rx_wake_pkt_classification_info.icmp6_ns = |
| nla_get_u32(tbVendor[QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_NS]); |
| |
| if (!tbVendor[ |
| QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP4_RX_MULTICAST_CNT]) { |
| ALOGE("%s: ICMP4_RX_MULTICAST_CNT not found", __FUNCTION__); |
| break; |
| } |
| mGetWakeStats->rx_multicast_wake_pkt_info.ipv4_rx_multicast_addr_cnt = |
| nla_get_u32(tbVendor[ |
| QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP4_RX_MULTICAST_CNT]); |
| |
| if (!tbVendor[ |
| QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_RX_MULTICAST_CNT]) { |
| ALOGE("%s: ICMP6_RX_MULTICAST_CNT not found", __FUNCTION__); |
| break; |
| } |
| mGetWakeStats->rx_multicast_wake_pkt_info.ipv6_rx_multicast_addr_cnt = |
| nla_get_u32(tbVendor[ |
| QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_RX_MULTICAST_CNT]); |
| |
| if (!tbVendor[ |
| QCA_WLAN_VENDOR_ATTR_WAKE_STATS_OTHER_RX_MULTICAST_CNT]) { |
| ALOGE("%s: OTHER_RX_MULTICAST_CNT not found", __FUNCTION__); |
| break; |
| } |
| mGetWakeStats->rx_multicast_wake_pkt_info.other_rx_multicast_addr_cnt = |
| nla_get_u32(tbVendor[ |
| QCA_WLAN_VENDOR_ATTR_WAKE_STATS_OTHER_RX_MULTICAST_CNT]); |
| |
| } |
| break; |
| |
| default : |
| ALOGE("%s: Wrong Wifi Logger subcmd response received %d", |
| __FUNCTION__, mSubcmd); |
| } |
| |
| /* free the allocated memory */ |
| if (memBuffer) { |
| free(memBuffer); |
| } |
| if (memDumpFilePtr) { |
| fclose(memDumpFilePtr); |
| } |
| return NL_SKIP; |
| } |
| |
| /* This function will be the main handler for incoming (from driver) |
| * WIFI_LOGGER_SUBCMD. |
| * Calls the appropriate callback handler after parsing the vendor data. |
| */ |
| int WifiLoggerCommand::handleEvent(WifiEvent &event) |
| { |
| WifiVendorCommand::handleEvent(event); |
| |
| switch(mSubcmd) |
| { |
| default: |
| /* Error case should not happen print log */ |
| ALOGE("%s: Wrong subcmd received %d", __func__, mSubcmd); |
| break; |
| } |
| |
| return NL_SKIP; |
| } |
| |
| wifi_error WifiLoggerCommand::setCallbackHandler(WifiLoggerCallbackHandler nHandler) |
| { |
| wifi_error res; |
| mHandler = nHandler; |
| res = registerVendorHandler(mVendor_id, mSubcmd); |
| if (res != WIFI_SUCCESS) { |
| ALOGE("%s: Unable to register Vendor Handler Vendor Id=0x%x subcmd=%u", |
| __FUNCTION__, mVendor_id, mSubcmd); |
| } |
| return res; |
| } |
| |
| void WifiLoggerCommand::unregisterHandler(u32 subCmd) |
| { |
| unregisterVendorHandler(mVendor_id, subCmd); |
| } |
| |
| wifi_error WifiLoggerCommand::timed_wait(u16 wait_time) |
| { |
| struct timespec absTime; |
| absTime.tv_sec = wait_time; |
| absTime.tv_nsec = 0; |
| return mCondition.wait(absTime); |
| } |
| |
| void WifiLoggerCommand::waitForRsp(bool wait) |
| { |
| mWaitforRsp = wait; |
| } |
| |
| /* Function to get Driver memory dump */ |
| wifi_error wifi_get_driver_memory_dump(wifi_interface_handle iface, |
| wifi_driver_memory_dump_callbacks callback) |
| { |
| FILE *fp; |
| size_t fileSize, remaining, readSize; |
| size_t numRecordsRead; |
| char *memBuffer = NULL, *buffer = NULL; |
| wifi_handle wifiHandle = getWifiHandle(iface); |
| hal_info *info = getHalInfo(wifiHandle); |
| |
| /* Check Supported logger capability */ |
| if (!(info->supported_logger_feature_set & |
| WIFI_LOGGER_DRIVER_DUMP_SUPPORTED)) { |
| ALOGE("%s: Driver memory dump logging feature not supported %x", |
| __FUNCTION__, info->supported_logger_feature_set); |
| return WIFI_ERROR_NOT_SUPPORTED; |
| } |
| /* Open File */ |
| fp = fopen(DRIVER_MEMDUMP_FILENAME, "r"); |
| if (fp == NULL) { |
| ALOGE("Failed to open %s file", DRIVER_MEMDUMP_FILENAME); |
| return WIFI_ERROR_UNKNOWN; |
| } |
| |
| memBuffer = (char *) malloc(DRIVER_MEMDUMP_MAX_FILESIZE); |
| if (memBuffer == NULL) { |
| ALOGE("%s: malloc failed for size %d", __FUNCTION__, |
| DRIVER_MEMDUMP_MAX_FILESIZE); |
| fclose(fp); |
| return WIFI_ERROR_OUT_OF_MEMORY; |
| } |
| |
| /* Read the DRIVER_MEMDUMP_MAX_FILESIZE value at once */ |
| numRecordsRead = fread(memBuffer, 1, DRIVER_MEMDUMP_MAX_FILESIZE, fp); |
| if (feof(fp)) |
| fileSize = numRecordsRead; |
| else if (numRecordsRead == DRIVER_MEMDUMP_MAX_FILESIZE) { |
| ALOGE("%s: Reading only first %zu bytes from file", __FUNCTION__, |
| numRecordsRead); |
| fileSize = numRecordsRead; |
| } else { |
| ALOGE("%s: Read failed for reading at once, ret: %zu. Trying to read in" |
| "chunks", __FUNCTION__, numRecordsRead); |
| /* Lets try to read in chunks */ |
| rewind(fp); |
| remaining = DRIVER_MEMDUMP_MAX_FILESIZE; |
| buffer = memBuffer; |
| fileSize = 0; |
| while (remaining) { |
| readSize = 0; |
| if (remaining >= LOGGER_MEMDUMP_CHUNKSIZE) |
| readSize = LOGGER_MEMDUMP_CHUNKSIZE; |
| else |
| readSize = remaining; |
| |
| numRecordsRead = fread(buffer, 1, readSize, fp); |
| fileSize += numRecordsRead; |
| if (feof(fp)) |
| break; |
| else if (numRecordsRead == readSize) { |
| remaining -= readSize; |
| buffer += readSize; |
| ALOGV("%s: Read successful for size:%zu remaining:%zu", |
| __FUNCTION__, readSize, remaining); |
| } else { |
| ALOGE("%s: Chunk read failed for size:%zu", __FUNCTION__, |
| readSize); |
| free(memBuffer); |
| memBuffer = NULL; |
| fclose(fp); |
| return WIFI_ERROR_UNKNOWN; |
| } |
| } |
| } |
| ALOGV("%s filename: %s fileSize: %zu", __FUNCTION__, DRIVER_MEMDUMP_FILENAME, |
| fileSize); |
| /* After successful read, call the callback function*/ |
| callback.on_driver_memory_dump(memBuffer, fileSize); |
| |
| /* free the allocated memory */ |
| free(memBuffer); |
| fclose(fp); |
| return WIFI_SUCCESS; |
| } |
| |
| /* Function to get wake lock stats */ |
| wifi_error wifi_get_wake_reason_stats(wifi_interface_handle iface, |
| WLAN_DRIVER_WAKE_REASON_CNT *wifi_wake_reason_cnt) |
| { |
| int requestId; |
| wifi_error ret; |
| WifiLoggerCommand *wifiLoggerCommand; |
| interface_info *ifaceInfo = getIfaceInfo(iface); |
| wifi_handle wifiHandle = getWifiHandle(iface); |
| hal_info *info = getHalInfo(wifiHandle); |
| |
| /* Check Supported logger capability */ |
| if (!(info->supported_logger_feature_set & |
| WIFI_LOGGER_WAKE_LOCK_SUPPORTED)) { |
| ALOGE("%s: Wake lock logging feature not supported %x", |
| __FUNCTION__, info->supported_logger_feature_set); |
| return WIFI_ERROR_NOT_SUPPORTED; |
| } |
| |
| /* No request id from caller, so generate one and pass it on to the driver. |
| * Generate it randomly. |
| */ |
| requestId = get_requestid(); |
| |
| if (!wifi_wake_reason_cnt) { |
| ALOGE("%s: Invalid buffer provided. Exit.", |
| __FUNCTION__); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| |
| wifiLoggerCommand = new WifiLoggerCommand( |
| wifiHandle, |
| requestId, |
| OUI_QCA, |
| QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS); |
| if (wifiLoggerCommand == NULL) { |
| ALOGE("%s: Error WifiLoggerCommand NULL", __FUNCTION__); |
| return WIFI_ERROR_UNKNOWN; |
| } |
| |
| /* Create the NL message. */ |
| ret = wifiLoggerCommand->create(); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| /* Set the interface Id of the message. */ |
| ret = wifiLoggerCommand->set_iface_id(ifaceInfo->name); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| wifiLoggerCommand->getWakeStatsRspParams(wifi_wake_reason_cnt); |
| |
| /* Send the msg and wait for a response. */ |
| ret = wifiLoggerCommand->requestResponse(); |
| if (ret != WIFI_SUCCESS) |
| ALOGE("%s: Error %d happened. ", __FUNCTION__, ret); |
| |
| cleanup: |
| delete wifiLoggerCommand; |
| return ret; |
| } |
| |
| void WifiLoggerCommand::getWakeStatsRspParams( |
| WLAN_DRIVER_WAKE_REASON_CNT *wifi_wake_reason_cnt) |
| { |
| mGetWakeStats = wifi_wake_reason_cnt; |
| } |