/* Copyright (c) 2014, 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.
 */

#define LOG_TAG  "WifiHAL"
#include <cutils/sched_policy.h>
#include <unistd.h>

#include <utils/Log.h>
#include <time.h>

#include "common.h"
#include "cpp_bindings.h"
#include "rtt.h"
#include "wifi_hal.h"
#include "wifihal_internal.h"

/* Implementation of the API functions exposed in rtt.h */
wifi_error wifi_get_rtt_capabilities(wifi_interface_handle iface,
                                     wifi_rtt_capabilities *capabilities)
{
    int ret = WIFI_SUCCESS;
    lowi_cb_table_t *lowiWifiHalApi = NULL;

    ALOGD("wifi_get_rtt_capabilities: Entry");

    if (iface == NULL) {
        ALOGE("wifi_get_rtt_capabilities: NULL iface pointer provided."
            " Exit.");
        return WIFI_ERROR_INVALID_ARGS;
    }

    interface_info *ifaceInfo = getIfaceInfo(iface);
    wifi_handle wifiHandle = getWifiHandle(iface);

    if (capabilities == NULL) {
        ALOGE("wifi_get_rtt_capabilities: NULL capabilities pointer provided."
            " Exit.");
        return WIFI_ERROR_INVALID_ARGS;
    }

    /* RTT commands are diverted through LOWI interface. */
    /* Open LOWI dynamic library, retrieve handler to LOWI APIs and initialize
     * LOWI if it isn't up yet.
     */
    lowiWifiHalApi = getLowiCallbackTable(
                ONE_SIDED_RANGING_SUPPORTED|DUAL_SIDED_RANGING_SUPPORED);
    if (lowiWifiHalApi == NULL ||
        lowiWifiHalApi->get_rtt_capabilities == NULL) {
        ALOGE("wifi_get_rtt_capabilities: getLowiCallbackTable returned NULL or "
            "the function pointer is NULL. Exit.");
        ret = WIFI_ERROR_NOT_SUPPORTED;
        goto cleanup;
    }

    ret = lowiWifiHalApi->get_rtt_capabilities(iface, capabilities);
    if (ret != WIFI_SUCCESS) {
        ALOGE("wifi_get_rtt_capabilities: lowi_wifihal_get_rtt_capabilities "
            "returned error:%d. Exit.", ret);
        goto cleanup;
    }

cleanup:
    return (wifi_error)ret;
}

/* API to request RTT measurement */
wifi_error wifi_rtt_range_request(wifi_request_id id,
                                    wifi_interface_handle iface,
                                    unsigned num_rtt_config,
                                    wifi_rtt_config rtt_config[],
                                    wifi_rtt_event_handler handler)
{
    int ret = WIFI_SUCCESS;
    lowi_cb_table_t *lowiWifiHalApi = NULL;

    ALOGD("wifi_rtt_range_request: Entry");

    if (iface == NULL) {
        ALOGE("wifi_rtt_range_request: NULL iface pointer provided."
            " Exit.");
        return WIFI_ERROR_INVALID_ARGS;
    }

    interface_info *ifaceInfo = getIfaceInfo(iface);
    wifi_handle wifiHandle = getWifiHandle(iface);

    if (rtt_config == NULL) {
        ALOGE("wifi_rtt_range_request: NULL rtt_config pointer provided."
            " Exit.");
        return WIFI_ERROR_INVALID_ARGS;
    }

    if (num_rtt_config <= 0) {
        ALOGE("wifi_rtt_range_request: number of destination BSSIDs to "
            "measure RTT on = 0. Exit.");
        return WIFI_ERROR_INVALID_ARGS;
    }

    if (handler.on_rtt_results == NULL) {
        ALOGE("wifi_rtt_range_request: NULL capabilities pointer provided."
            " Exit.");
        return WIFI_ERROR_INVALID_ARGS;
    }

    /* RTT commands are diverted through LOWI interface. */
    /* Open LOWI dynamic library, retrieve handler to LOWI APIs and initialize
     * LOWI if it isn't up yet.
     */
    lowiWifiHalApi = getLowiCallbackTable(
                    ONE_SIDED_RANGING_SUPPORTED|DUAL_SIDED_RANGING_SUPPORED);
    if (lowiWifiHalApi == NULL ||
        lowiWifiHalApi->rtt_range_request == NULL) {
        ALOGE("wifi_rtt_range_request: getLowiCallbackTable returned NULL or "
            "the function pointer is NULL. Exit.");
        ret = WIFI_ERROR_NOT_SUPPORTED;
        goto cleanup;
    }

    ret = lowiWifiHalApi->rtt_range_request(id,
                                            iface,
                                            num_rtt_config,
                                            rtt_config,
                                            handler);
    if (ret != WIFI_SUCCESS) {
        ALOGE("wifi_rtt_range_request: lowi_wifihal_rtt_range_request "
            "returned error:%d. Exit.", ret);
        goto cleanup;
    }

cleanup:
    return (wifi_error)ret;
}

/* API to cancel RTT measurements */
wifi_error wifi_rtt_range_cancel(wifi_request_id id,
                                   wifi_interface_handle iface,
                                   unsigned num_devices,
                                   mac_addr addr[])
{
    int ret = WIFI_SUCCESS;
    lowi_cb_table_t *lowiWifiHalApi = NULL;

    ALOGD("wifi_rtt_range_cancel: Entry");

    if (iface == NULL) {
        ALOGE("wifi_rtt_range_cancel: NULL iface pointer provided."
            " Exit.");
        return WIFI_ERROR_INVALID_ARGS;
    }

    interface_info *ifaceInfo = getIfaceInfo(iface);
    wifi_handle wifiHandle = getWifiHandle(iface);

    if (addr == NULL) {
        ALOGE("wifi_rtt_range_cancel: NULL addr pointer provided."
            " Exit.");
        return WIFI_ERROR_INVALID_ARGS;
    }

    if (num_devices <= 0) {
        ALOGE("wifi_rtt_range_cancel: number of destination BSSIDs to "
            "measure RTT on = 0. Exit.");
        return WIFI_ERROR_INVALID_ARGS;
    }

    /* RTT commands are diverted through LOWI interface. */
    /* Open LOWI dynamic library, retrieve handler to LOWI APIs and initialize
     * LOWI if it isn't up yet.
     */
    lowiWifiHalApi = getLowiCallbackTable(
                    ONE_SIDED_RANGING_SUPPORTED|DUAL_SIDED_RANGING_SUPPORED);
    if (lowiWifiHalApi == NULL ||
        lowiWifiHalApi->rtt_range_cancel == NULL) {
        ALOGE("wifi_rtt_range_cancel: getLowiCallbackTable returned NULL or "
            "the function pointer is NULL. Exit.");
        ret = WIFI_ERROR_NOT_SUPPORTED;
        goto cleanup;
    }

    ret = lowiWifiHalApi->rtt_range_cancel(id, num_devices, addr);
    if (ret != WIFI_SUCCESS) {
        ALOGE("wifi_rtt_range_cancel: lowi_wifihal_rtt_range_cancel "
            "returned error:%d. Exit.", ret);
        goto cleanup;
    }

cleanup:
    return (wifi_error)ret;
}

// API to configure the LCI. Used in RTT Responder mode only
wifi_error wifi_set_lci(wifi_request_id id, wifi_interface_handle iface,
                        wifi_lci_information *lci)
{
    int ret = WIFI_SUCCESS;
    lowi_cb_table_t *lowiWifiHalApi = NULL;

    ALOGD("%s: Entry", __FUNCTION__);

    if (iface == NULL) {
        ALOGE("%s: NULL iface pointer provided."
            " Exit.", __FUNCTION__);
        return WIFI_ERROR_INVALID_ARGS;
    }

    interface_info *ifaceInfo = getIfaceInfo(iface);
    wifi_handle wifiHandle = getWifiHandle(iface);

    if (lci == NULL) {
        ALOGE("%s: NULL lci pointer provided."
            " Exit.", __FUNCTION__);
        return WIFI_ERROR_INVALID_ARGS;
    }

    /* RTT commands are diverted through LOWI interface. */
    /* Open LOWI dynamic library, retrieve handler to LOWI APIs and initialize
     * LOWI if it isn't up yet.
     */
    lowiWifiHalApi = getLowiCallbackTable(
                    ONE_SIDED_RANGING_SUPPORTED|DUAL_SIDED_RANGING_SUPPORED);
    if (lowiWifiHalApi == NULL ||
        lowiWifiHalApi->rtt_set_lci == NULL) {
        ALOGE("%s: getLowiCallbackTable returned NULL or "
            "the function pointer is NULL. Exit.", __FUNCTION__);
        ret = WIFI_ERROR_NOT_SUPPORTED;
        goto cleanup;
    }

    ret = lowiWifiHalApi->rtt_set_lci(id, iface, lci);
    if (ret != WIFI_SUCCESS) {
        ALOGE("%s: returned error:%d. Exit.",
              __FUNCTION__, ret);
        goto cleanup;
    }

cleanup:
    return (wifi_error)ret;
}

// API to configure the LCR. Used in RTT Responder mode only.
wifi_error wifi_set_lcr(wifi_request_id id, wifi_interface_handle iface,
                        wifi_lcr_information *lcr)
{
    int ret = WIFI_SUCCESS;
    lowi_cb_table_t *lowiWifiHalApi = NULL;

    ALOGD("%s: Entry", __FUNCTION__);

    if (iface == NULL) {
        ALOGE("%s: NULL iface pointer provided."
            " Exit.", __FUNCTION__);
        return WIFI_ERROR_INVALID_ARGS;
    }

    interface_info *ifaceInfo = getIfaceInfo(iface);
    wifi_handle wifiHandle = getWifiHandle(iface);

    if (lcr == NULL) {
        ALOGE("%s: NULL lcr pointer provided."
            " Exit.", __FUNCTION__);
        return WIFI_ERROR_INVALID_ARGS;
    }

    /* RTT commands are diverted through LOWI interface. */
    /* Open LOWI dynamic library, retrieve handler to LOWI APIs and initialize
     * LOWI if it isn't up yet.
     */
    lowiWifiHalApi = getLowiCallbackTable(
                    ONE_SIDED_RANGING_SUPPORTED|DUAL_SIDED_RANGING_SUPPORED);
    if (lowiWifiHalApi == NULL ||
        lowiWifiHalApi->rtt_set_lcr == NULL) {
        ALOGE("%s: getLowiCallbackTable returned NULL or "
            "the function pointer is NULL. Exit.", __FUNCTION__);
        ret = WIFI_ERROR_NOT_SUPPORTED;
        goto cleanup;
    }

    ret = lowiWifiHalApi->rtt_set_lcr(id, iface, lcr);
    if (ret != WIFI_SUCCESS) {
        ALOGE("%s: returned error:%d. Exit.",
              __FUNCTION__, ret);
        goto cleanup;
    }

cleanup:
    return (wifi_error)ret;
}