/* 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.
 */

#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;

    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)
        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.", __FUNCTION__, buffer_name);
    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)
        goto cleanup;

    ret = wifiLoggerCommand->put_u32(QCA_WLAN_VENDOR_ATTR_FEATURE_SET,
                                     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)
        goto cleanup;

    if (wifiLoggerCommand->put_u32(
                QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID, ring_id))
    {
        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();

    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_u32(
                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();

    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_u32(
                      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)
        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);

    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;

    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;

    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:
    /* 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_FEATURE_SET_MAX + 1];

            nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_FEATURE_SET_MAX,
                            (struct nlattr *)mVendorData, mDataLen, NULL);

            if (tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_SET]) {
                *mSupportedSet =
                nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_SET]);
#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_ATTR_WAKE_STATS_MAX +1];

            /* parse and extract wake reason stats */
            nla_parse(tbVendor, QCA_WLAN_VENDOR_ATTR_WAKE_STATS_MAX,
                      (struct nlattr *)mVendorData,
                      mDataLen, NULL);

            if (!tbVendor[
                    QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_CMD_EVENT_WAKE]) {
                ALOGE("%s: TOTAL_CMD_EVENT_WAKE not found", __FUNCTION__);
                break;
            }
            mGetWakeStats->total_cmd_event_wake = nla_get_u32(
                tbVendor[QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_CMD_EVENT_WAKE]);

            if (mGetWakeStats->total_cmd_event_wake &&
                    mGetWakeStats->cmd_event_wake_cnt) {
                if (!tbVendor[
                    QCA_WLAN_VENDOR_ATTR_WAKE_STATS_CMD_EVENT_WAKE_CNT_PTR]) {
                    ALOGE("%s: CMD_EVENT_WAKE_CNT_PTR not found", __FUNCTION__);
                    break;
                }
                len = nla_len(tbVendor[
                        QCA_WLAN_VENDOR_ATTR_WAKE_STATS_CMD_EVENT_WAKE_CNT_PTR]);
                mGetWakeStats->cmd_event_wake_cnt_used =
                        (len < mGetWakeStats->cmd_event_wake_cnt_sz) ? len :
                                    mGetWakeStats->cmd_event_wake_cnt_sz;
                memcpy(mGetWakeStats->cmd_event_wake_cnt,
                    nla_data(tbVendor[
                        QCA_WLAN_VENDOR_ATTR_WAKE_STATS_CMD_EVENT_WAKE_CNT_PTR]),
                    (mGetWakeStats->cmd_event_wake_cnt_used * sizeof(int)));
            } else
                mGetWakeStats->cmd_event_wake_cnt_used = 0;

            if (!tbVendor[
                    QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_DRIVER_FW_LOCAL_WAKE])
            {
                ALOGE("%s: TOTAL_DRIVER_FW_LOCAL_WAKE not found", __FUNCTION__);
                break;
            }
            mGetWakeStats->total_driver_fw_local_wake = nla_get_u32(tbVendor[
                QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_DRIVER_FW_LOCAL_WAKE]);

            if (mGetWakeStats->total_driver_fw_local_wake &&
                    mGetWakeStats->driver_fw_local_wake_cnt) {
                if (!tbVendor[
                    QCA_WLAN_VENDOR_ATTR_WAKE_STATS_DRIVER_FW_LOCAL_WAKE_CNT_PTR])
                {
                    ALOGE("%s: DRIVER_FW_LOCAL_WAKE_CNT_PTR not found",
                        __FUNCTION__);
                    break;
                }
                len = nla_len(tbVendor[
                    QCA_WLAN_VENDOR_ATTR_WAKE_STATS_DRIVER_FW_LOCAL_WAKE_CNT_PTR]);
                mGetWakeStats->driver_fw_local_wake_cnt_used =
                    (len < mGetWakeStats->driver_fw_local_wake_cnt_sz) ? len :
                                    mGetWakeStats->driver_fw_local_wake_cnt_sz;

                memcpy(mGetWakeStats->driver_fw_local_wake_cnt,
                    nla_data(tbVendor[
                        QCA_WLAN_VENDOR_ATTR_WAKE_STATS_DRIVER_FW_LOCAL_WAKE_CNT_PTR]),
                    (mGetWakeStats->driver_fw_local_wake_cnt_used * sizeof(int)));
            } else
                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;
    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_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);

    /* Add the vendor specific attributes for the NL command. */
    nlData = wifiLoggerCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
    if (!nlData)
        goto cleanup;

    ret = wifiLoggerCommand->put_u32(
                QCA_WLAN_VENDOR_ATTR_WAKE_STATS_CMD_EVENT_WAKE_CNT_SZ,
                wifi_wake_reason_cnt->cmd_event_wake_cnt_sz);
    if (ret != WIFI_SUCCESS)
        goto cleanup;

    ret = wifiLoggerCommand->put_u32(
                QCA_WLAN_VENDOR_ATTR_WAKE_STATS_DRIVER_FW_LOCAL_WAKE_CNT_SZ,
                wifi_wake_reason_cnt->driver_fw_local_wake_cnt_sz);
    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);

cleanup:
    delete wifiLoggerCommand;
    return ret;
}

void WifiLoggerCommand::getWakeStatsRspParams(
                            WLAN_DRIVER_WAKE_REASON_CNT *wifi_wake_reason_cnt)
{
    mGetWakeStats = wifi_wake_reason_cnt;
}