/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#include <inttypes.h>
#include "syscfg/syscfg.h"
#include "ble_hs_mbuf_priv.h"

#if MYNEWT_VAL(BLE_CHANNEL_SOUNDING)

#include "os/os_mbuf.h"
#include "host/ble_hs_log.h"
#include "host/ble_hs.h"
#include "host/ble_cs.h"
#include "nimble/hci_common.h"
#include "sys/queue.h"
#include "ble_hs_hci_priv.h"

struct ble_cs_rd_rem_supp_cap_cp {
    uint16_t conn_handle;
} __attribute__((packed));

struct ble_cs_wr_cached_rem_supp_cap_cp {
    uint16_t conn_handle;
    uint8_t num_config_supported;
    uint16_t max_consecutive_procedures_supported;
    uint8_t num_antennas_supported;
    uint8_t max_antenna_paths_supported;
    uint8_t roles_supported;
    uint8_t optional_modes_supported;
    uint8_t rtt_capability;
    uint8_t rtt_aa_only_n;
    uint8_t rtt_sounding_n;
    uint8_t rtt_random_payload_n;
    uint16_t optional_nadm_sounding_capability;
    uint16_t optional_nadm_random_capability;
    uint8_t optional_cs_sync_phys_supported;
    uint16_t optional_subfeatures_supported;
    uint16_t optional_t_ip1_times_supported;
    uint16_t optional_t_ip2_times_supported;
    uint16_t optional_t_fcs_times_supported;
    uint16_t optional_t_pm_times_supported;
    uint8_t t_sw_time_supported;
} __attribute__((packed));
struct ble_cs_wr_cached_rem_supp_cap_rp {
    uint16_t conn_handle;
} __attribute__((packed));

struct ble_cs_sec_enable_cp {
    uint16_t conn_handle;
} __attribute__((packed));

struct ble_cs_set_def_settings_cp {
    uint16_t conn_handle;
    uint8_t role_enable;
    uint8_t cs_sync_antenna_selection;
    uint8_t max_tx_power;
} __attribute__((packed));
struct ble_cs_set_def_settings_rp {
    uint16_t conn_handle;
} __attribute__((packed));

struct ble_cs_rd_rem_fae_cp {
    uint16_t conn_handle;
} __attribute__((packed));

struct ble_cs_wr_cached_rem_fae_cp {
    uint16_t conn_handle;
    uint8_t remote_fae_table[72];
} __attribute__((packed));
struct ble_cs_wr_cached_rem_fae_rp {
    uint16_t conn_handle;
} __attribute__((packed));

struct ble_cs_create_config_cp {
    uint16_t conn_handle;
    uint8_t config_id;
    /* If the config should be created on the remote controller too */
    uint8_t create_context;
    /* The main mode to be used in the CS procedures */
    uint8_t main_mode_type;
    /* The sub mode to be used in the CS procedures */
    uint8_t sub_mode_type;
    /* Minimum/maximum number of CS main mode steps to be executed before
     * a submode step.
     */
    uint8_t min_main_mode_steps;
    uint8_t max_main_mode_steps;
    /* The number of main mode steps taken from the end of the last
     * CS subevent to be repeated at the beginning of the current CS subevent
     * directly after the last mode 0 step of that event
     */
    uint8_t main_mode_repetition;
    uint8_t mode_0_steps;
    uint8_t role;
    uint8_t rtt_type;
    uint8_t cs_sync_phy;
    uint8_t channel_map[10];
    uint8_t channel_map_repetition;
    uint8_t channel_selection_type;
    uint8_t ch3c_shape;
    uint8_t ch3c_jump;
    uint8_t companion_signal_enable;
} __attribute__((packed));

struct ble_cs_remove_config_cp {
    uint16_t conn_handle;
    uint8_t config_id;
} __attribute__((packed));

struct ble_cs_set_chan_class_cp {
    uint8_t channel_classification[10];
} __attribute__((packed));

struct ble_cs_set_proc_params_cp {
    uint16_t conn_handle;
    uint8_t config_id;
    /* The maximum duration of each CS procedure (time = N × 0.625 ms) */
    uint16_t max_procedure_len;
    /* The minimum and maximum number of connection events between
     * consecutive CS procedures. Ignored if only one CS procedure. */
    uint16_t min_procedure_interval;
    uint16_t max_procedure_interval;
    /* The maximum number of consecutive CS procedures to be scheduled */
    uint16_t max_procedure_count;
    /* Minimum/maximum suggested durations for each CS subevent in microseconds.
     * Only 3 bytes meaningful. */
    uint32_t min_subevent_len;
    uint32_t max_subevent_len;
    /* Antenna Configuration Index (ACI) for swithing during phase measuement */
    uint8_t tone_antenna_config_selection;
    /* The remote device’s Tx PHY. A 4 bits bitmap. */
    uint8_t phy;
    /* How many more or fewer of transmit power levels should the remote device’s
     * Tx PHY use during the CS tones and RTT transmission */
    uint8_t tx_power_delta;
    /* Preferred peer-ordered antenna elements to be used by the peer for
     * the selected antenna configuration (ACI). A 4 bits bitmap. */
    uint8_t preferred_peer_antenna;
    /* SNR Output Index (SOI) for SNR control adjustment. */
    uint8_t snr_control_initiator;
    uint8_t snr_control_reflector;
} __attribute__((packed));
struct ble_cs_set_proc_params_rp {
    uint16_t conn_handle;
} __attribute__((packed));

struct ble_cs_proc_enable_cp {
    uint16_t conn_handle;
    uint8_t config_id;
    uint8_t enable;
} __attribute__((packed));

struct ble_cs_state {
    uint8_t op;
    ble_cs_event_fn *cb;
    void *cb_arg;
};

static struct ble_cs_state cs_state;

static int
ble_cs_call_event_cb(struct ble_cs_event *event)
{
    int rc;

    if (cs_state.cb != NULL) {
        rc = cs_state.cb(event, cs_state.cb_arg);
    } else {
        rc = 0;
    }

    return rc;
}

static void
ble_cs_call_procedure_complete_cb(uint16_t conn_handle, uint8_t status)
{
    struct ble_cs_event event;

    memset(&event, 0, sizeof event);
    event.type = BLE_CS_EVENT_CS_PROCEDURE_COMPLETE;
    event.procedure_complete.conn_handle = conn_handle;
    event.procedure_complete.status = status;
    ble_cs_call_event_cb(&event);
}

static int
ble_cs_rd_loc_supp_cap(void)
{
    int rc;
    struct ble_hci_le_cs_rd_loc_supp_cap_rp rp;

    rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
                                      BLE_HCI_OCF_LE_CS_RD_LOC_SUPP_CAP),
                           NULL, 0, &rp, sizeof(rp));

    rp.max_consecutive_procedures_supported = le16toh(rp.max_consecutive_procedures_supported);
    rp.optional_nadm_sounding_capability = le16toh(rp.optional_nadm_sounding_capability);
    rp.optional_nadm_random_capability = le16toh(rp.optional_nadm_random_capability);
    rp.optional_subfeatures_supported = le16toh(rp.optional_subfeatures_supported);
    rp.optional_t_ip1_times_supported = le16toh(rp.optional_t_ip1_times_supported);
    rp.optional_t_ip2_times_supported = le16toh(rp.optional_t_ip2_times_supported);
    rp.optional_t_fcs_times_supported = le16toh(rp.optional_t_fcs_times_supported);
    rp.optional_t_pm_times_supported = le16toh(rp.optional_t_pm_times_supported);
    (void) rp;

    return rc;
}

static int
ble_cs_rd_rem_supp_cap(const struct ble_cs_rd_rem_supp_cap_cp *cmd)
{
    struct ble_hci_le_cs_rd_rem_supp_cap_cp cp;

    cp.conn_handle = htole16(cmd->conn_handle);

    return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
                                        BLE_HCI_OCF_LE_CS_RD_REM_SUPP_CAP),
                             &cp, sizeof(cp), NULL, 0);
}

static int
ble_cs_wr_cached_rem_supp_cap(const struct ble_cs_wr_cached_rem_supp_cap_cp *cmd,
                              struct ble_cs_wr_cached_rem_supp_cap_rp *rsp)
{
    struct ble_hci_le_cs_wr_cached_rem_supp_cap_cp cp;
    struct ble_hci_le_cs_wr_cached_rem_supp_cap_rp rp;
    int rc;

    cp.conn_handle = htole16(cmd->conn_handle);
    cp.num_config_supported = cmd->num_config_supported;
    cp.max_consecutive_procedures_supported = htole16(cmd->max_consecutive_procedures_supported);
    cp.num_antennas_supported = cmd->num_antennas_supported;
    cp.max_antenna_paths_supported = cmd->max_antenna_paths_supported;
    cp.roles_supported = cmd->roles_supported;
    cp.optional_modes_supported = cmd->optional_modes_supported;
    cp.rtt_capability = cmd->rtt_capability;
    cp.rtt_aa_only_n = cmd->rtt_aa_only_n;
    cp.rtt_sounding_n = cmd->rtt_sounding_n;
    cp.rtt_random_payload_n = cmd->rtt_random_payload_n;
    cp.optional_nadm_sounding_capability = htole16(cmd->optional_nadm_sounding_capability);
    cp.optional_nadm_random_capability = htole16(cmd->optional_nadm_random_capability);
    cp.optional_cs_sync_phys_supported = cmd->optional_cs_sync_phys_supported;
    cp.optional_subfeatures_supported = htole16(cmd->optional_subfeatures_supported);
    cp.optional_t_ip1_times_supported = htole16(cmd->optional_t_ip1_times_supported);
    cp.optional_t_ip2_times_supported = htole16(cmd->optional_t_ip2_times_supported);
    cp.optional_t_fcs_times_supported = htole16(cmd->optional_t_fcs_times_supported);
    cp.optional_t_pm_times_supported = htole16(cmd->optional_t_pm_times_supported);
    cp.t_sw_time_supported = cmd->t_sw_time_supported;

    rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
                                      BLE_HCI_OCF_LE_CS_WR_CACHED_REM_SUPP_CAP),
                           &cp, sizeof(cp), &rp, sizeof(rp));

    rsp->conn_handle = le16toh(rp.conn_handle);

    return rc;
}

static int
ble_cs_sec_enable(const struct ble_cs_sec_enable_cp *cmd)
{
    struct ble_hci_le_cs_sec_enable_cp cp;

    cp.conn_handle = htole16(cmd->conn_handle);

    return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
                                        BLE_HCI_OCF_LE_CS_SEC_ENABLE),
                             &cp, sizeof(cp), NULL, 0);
}

static int
ble_cs_set_def_settings(const struct ble_cs_set_def_settings_cp *cmd,
                        struct ble_cs_set_def_settings_rp *rsp)
{
    struct ble_hci_le_cs_set_def_settings_cp cp;
    struct ble_hci_le_cs_set_def_settings_rp rp;
    int rc;

    cp.conn_handle = htole16(cmd->conn_handle);
    cp.role_enable = cmd->role_enable;
    cp.cs_sync_antenna_selection = cmd->cs_sync_antenna_selection;
    cp.max_tx_power = cmd->max_tx_power;

    rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
                                      BLE_HCI_OCF_LE_CS_SET_DEF_SETTINGS),
                           &cp, sizeof(cp), &rp, sizeof(rp));

    rsp->conn_handle = le16toh(rp.conn_handle);

    return rc;
}

static int
ble_cs_rd_rem_fae(const struct ble_cs_rd_rem_fae_cp *cmd)
{
    struct ble_hci_le_cs_rd_rem_fae_cp cp;

    cp.conn_handle = htole16(cmd->conn_handle);

    return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
                                        BLE_HCI_OCF_LE_CS_RD_REM_FAE),
                             &cp, sizeof(cp), NULL, 0);
}

static int
ble_cs_wr_cached_rem_fae(const struct ble_cs_wr_cached_rem_fae_cp *cmd,
                         struct ble_cs_wr_cached_rem_fae_rp *rsp)
{
    struct ble_hci_le_cs_wr_cached_rem_fae_cp cp;
    struct ble_hci_le_cs_wr_cached_rem_fae_rp rp;
    int rc;

    cp.conn_handle = htole16(cmd->conn_handle);
    memcpy(cp.remote_fae_table, cmd->remote_fae_table, 72);

    rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
                                      BLE_HCI_OCF_LE_CS_WR_CACHED_REM_FAE),
                           &cp, sizeof(cp), &rp, sizeof(rp));

    rsp->conn_handle = le16toh(rp.conn_handle);

    return rc;
}

static int
ble_cs_create_config(const struct ble_cs_create_config_cp *cmd)
{
    struct ble_hci_le_cs_create_config_cp cp;

    cp.conn_handle = htole16(cmd->conn_handle);
    cp.config_id = cmd->config_id;
    cp.create_context = cmd->create_context;
    cp.main_mode_type = cmd->main_mode_type;
    cp.sub_mode_type = cmd->sub_mode_type;
    cp.min_main_mode_steps = cmd->min_main_mode_steps;
    cp.max_main_mode_steps = cmd->max_main_mode_steps;
    cp.main_mode_repetition = cmd->main_mode_repetition;
    cp.mode_0_steps = cmd->mode_0_steps;
    cp.role = cmd->role;
    cp.rtt_type = cmd->rtt_type;
    cp.cs_sync_phy = cmd->cs_sync_phy;
    memcpy(cp.channel_map, cmd->channel_map, 10);
    cp.channel_map_repetition = cmd->channel_map_repetition;
    cp.channel_selection_type = cmd->channel_selection_type;
    cp.ch3c_shape = cmd->ch3c_shape;
    cp.ch3c_jump = cmd->ch3c_jump;
    cp.companion_signal_enable = cmd->companion_signal_enable;

    return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
                                        BLE_HCI_OCF_LE_CS_CREATE_CONFIG),
                             &cp, sizeof(cp), NULL, 0);
}

static int
ble_cs_remove_config(const struct ble_cs_remove_config_cp *cmd)
{
    struct ble_hci_le_cs_remove_config_cp cp;

    cp.conn_handle = htole16(cmd->conn_handle);
    cp.config_id = cmd->config_id;

    return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
                                        BLE_HCI_OCF_LE_CS_REMOVE_CONFIG),
                             &cp, sizeof(cp), NULL, 0);
}

static int
ble_cs_set_chan_class(const struct ble_cs_set_chan_class_cp *cmd)
{
    struct ble_hci_le_cs_set_chan_class_cp cp;
    int rc;

    memcpy(cp.channel_classification, cmd->channel_classification, 10);

    rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
                                      BLE_HCI_OCF_LE_CS_SET_CHAN_CLASS),
                           &cp, sizeof(cp), NULL, 0);

    return rc;
}

static int
ble_cs_set_proc_params(const struct ble_cs_set_proc_params_cp *cmd,
                       struct ble_cs_set_proc_params_rp *rsp)
{
    struct ble_hci_le_cs_set_proc_params_cp cp;
    struct ble_hci_le_cs_set_proc_params_rp rp;
    int rc;

    cp.conn_handle = htole16(cmd->conn_handle);
    cp.config_id = cmd->config_id;
    cp.max_procedure_len = htole16(cmd->max_procedure_len);
    cp.min_procedure_interval = htole16(cmd->min_procedure_interval);
    cp.max_procedure_interval = htole16(cmd->max_procedure_interval);
    cp.max_procedure_count = htole16(cmd->max_procedure_count);
    put_le24(cp.min_subevent_len, cmd->min_subevent_len);
    put_le24(cp.max_subevent_len, cmd->max_subevent_len);
    cp.tone_antenna_config_selection = cmd->tone_antenna_config_selection;
    cp.phy = cmd->phy;
    cp.tx_power_delta = cmd->tx_power_delta;
    cp.preferred_peer_antenna = cmd->preferred_peer_antenna;
    cp.snr_control_initiator = cmd->snr_control_initiator;
    cp.snr_control_reflector = cmd->snr_control_reflector;

    rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
                                      BLE_HCI_OCF_LE_CS_SET_PROC_PARAMS),
                           &cp, sizeof(cp), &rp, sizeof(rp));

    rsp->conn_handle = le16toh(rp.conn_handle);

    return rc;
}

static int
ble_cs_proc_enable(const struct ble_cs_proc_enable_cp *cmd)
{
    struct ble_hci_le_cs_proc_enable_cp cp;

    cp.conn_handle = htole16(cmd->conn_handle);
    cp.config_id = cmd->config_id;
    cp.enable = cmd->enable;

    return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
                                        BLE_HCI_OCF_LE_CS_PROC_ENABLE),
                             &cp, sizeof(cp), NULL, 0);
}

int
ble_hs_hci_evt_le_cs_rd_rem_supp_cap_complete(uint8_t subevent, const void *data,
                                              unsigned int len)
{
    int rc;
    const struct ble_hci_ev_le_subev_cs_rd_rem_supp_cap_complete *ev = data;
    struct ble_cs_set_def_settings_cp set_cmd;
    struct ble_cs_set_def_settings_rp set_rsp;
    struct ble_cs_rd_rem_fae_cp fae_cmd;

    if (len != sizeof(*ev) || ev->status) {
        return BLE_HS_ECONTROLLER;
    }

    BLE_HS_LOG(DEBUG, "CS capabilities exchanged");

    /* TODO: Save the remote capabilities somewhere */

    set_cmd.conn_handle = le16toh(ev->conn_handle);
    /* Only initiator role is enabled */
    set_cmd.role_enable = 0x01;
    /* Use antenna with ID 0x01 */
    set_cmd.cs_sync_antenna_selection = 0x01;
    /* Set max TX power to the max supported */
    set_cmd.max_tx_power = 0x7F;

    rc = ble_cs_set_def_settings(&set_cmd, &set_rsp);
    if (rc) {
        BLE_HS_LOG(DEBUG, "Failed to set the default CS settings, err %dt", rc);

        return rc;
    }

    /* Read the mode 0 Frequency Actuation Error table */
    fae_cmd.conn_handle = le16toh(ev->conn_handle);
    rc = ble_cs_rd_rem_fae(&fae_cmd);
    if (rc) {
        BLE_HS_LOG(DEBUG, "Failed to read FAE table");
    }

    return rc;
}

int
ble_hs_hci_evt_le_cs_rd_rem_fae_complete(uint8_t subevent, const void *data,
                                         unsigned int len)
{
    const struct ble_hci_ev_le_subev_cs_rd_rem_fae_complete *ev = data;
    struct ble_cs_create_config_cp cmd;
    int rc;

    if (len != sizeof(*ev) || ev->status) {
        return BLE_HS_ECONTROLLER;
    }

    cmd.conn_handle = le16toh(ev->conn_handle);
    /* The config will use ID 0x00 */
    cmd.config_id = 0x00;
    /* Create the config on the remote controller too */
    cmd.create_context = 0x01;
    /* Measure phase rotations in main mode */
    cmd.main_mode_type = 0x01;
    /* Do not use sub mode for now. */
    cmd.sub_mode_type = 0xFF;
    /* Range from which the number of CS main mode steps to execute
     * will be randomly selected.
     */
    cmd.min_main_mode_steps = 0x02;
    cmd.max_main_mode_steps = 0x06;
    /* The number of main mode steps to be repeated at the beginning of
     * the current CS, irrespectively if there are some overlapping main
     * mode steps from previous CS subevent or not.
     */
    cmd.main_mode_repetition = 0x00;
    /* Number of CS mode 0 steps to be included at the beginning of
     * each CS subevent
     */
    cmd.mode_0_steps = 0x03;
    /* Take the Initiator role */
    cmd.role = 0x00;
    cmd.rtt_type = 0x01;
    cmd.cs_sync_phy = 0x01;
    memcpy(cmd.channel_map, (uint8_t[10]) {0x0a, 0xfa, 0xcf, 0xac, 0xfa, 0xc0}, 10);
    cmd.channel_map_repetition = 0x01;
    /* Use Channel Selection Algorithm #3b */
    cmd.channel_selection_type = 0x00;
    /* Ignore these as used only with #3c algorithm */
    cmd.ch3c_shape = 0x00;
    cmd.ch3c_jump = 0x00;
    /* EDLC/ECLD attack protection not supported */
    cmd.companion_signal_enable = 0x00;

    /* Create CS config */
    rc = ble_cs_create_config(&cmd);
    if (rc) {
        BLE_HS_LOG(DEBUG, "Failed to create CS config");
    }

    return rc;
}

int
ble_hs_hci_evt_le_cs_sec_enable_complete(uint8_t subevent, const void *data,
                                         unsigned int len)
{
    int rc;
    struct ble_cs_set_proc_params_cp cmd;
    struct ble_cs_set_proc_params_rp rsp;
    struct ble_cs_proc_enable_cp enable_cmd;
    const struct ble_hci_ev_le_subev_cs_sec_enable_complete *ev = data;

    if (len != sizeof(*ev)) {
        BLE_HS_LOG(DEBUG, "Failed to enable CS security");

        return BLE_HS_ECONTROLLER;
    }

    BLE_HS_LOG(DEBUG, "CS setup phase completed");

    cmd.conn_handle = le16toh(ev->conn_handle);
    cmd.config_id = 0x00;
    /* The maximum duration of each CS procedure (time = N × 0.625 ms) */
    cmd.max_procedure_len = 8;
    /* The maximum number of consecutive CS procedures to be scheduled
     * as part of this measurement
     */
    cmd.max_procedure_count = 0x0001;
    /* The minimum and maximum number of connection events between
     * consecutive CS procedures. Ignored if only one CS procedure.
     */
    cmd.min_procedure_interval = 0x0000;
    cmd.max_procedure_interval = 0x0000;
    /* Minimum/maximum suggested durations for each CS subevent in microseconds.
     * 1250us and 5000us selected.
     */
    cmd.min_subevent_len = 1250;
    cmd.max_subevent_len = 5000;
    /* Use ACI 0 as we have only one antenna on each side */
    cmd.tone_antenna_config_selection = 0x00;
    /* Use LE 1M PHY for CS procedures */
    cmd.phy = 0x01;
    /* Transmit power delta set to 0x80 means Host does not have a recommendation. */
    cmd.tx_power_delta = 0x80;
    /* Preferred antenna array elements to use. We have only a single antenna here. */
    cmd.preferred_peer_antenna = 0x01;
    /* SNR Output Index (SOI) for SNR control adjustment. 0xFF means SNR control
     * is not to be applied.
     */
    cmd.snr_control_initiator = 0xff;
    cmd.snr_control_reflector = 0xff;

    rc = ble_cs_set_proc_params(&cmd, &rsp);
    if (rc) {
        BLE_HS_LOG(DEBUG, "Failed to set CS procedure parameters");
    }

    enable_cmd.conn_handle = le16toh(ev->conn_handle);
    enable_cmd.config_id = 0x00;
    enable_cmd.enable = 0x01;

    rc = ble_cs_proc_enable(&enable_cmd);
    if (rc) {
        BLE_HS_LOG(DEBUG, "Failed to enable CS procedure");
    }

    return rc;
}

int
ble_hs_hci_evt_le_cs_config_complete(uint8_t subevent, const void *data,
                                     unsigned int len)
{
    int rc;
    const struct ble_hci_ev_le_subev_cs_config_complete *ev = data;
    struct ble_cs_sec_enable_cp cmd;

    if (len != sizeof(*ev) || ev->status) {
        return BLE_HS_ECONTROLLER;
    }

    cmd.conn_handle = le16toh(ev->conn_handle);

    /* Exchange CS security keys */
    rc = ble_cs_sec_enable(&cmd);
    if (rc) {
        BLE_HS_LOG(DEBUG, "Failed to enable CS security");
        ble_cs_call_procedure_complete_cb(le16toh(ev->conn_handle), ev->status);
    }

    return 0;
}

int
ble_hs_hci_evt_le_cs_proc_enable_complete(uint8_t subevent, const void *data,
                                          unsigned int len)
{
    const struct ble_hci_ev_le_subev_cs_proc_enable_complete *ev = data;

    if (len != sizeof(*ev) || ev->status) {
        return BLE_HS_ECONTROLLER;
    }

    return 0;
}

int
ble_hs_hci_evt_le_cs_subevent_result(uint8_t subevent, const void *data,
                                     unsigned int len)
{
    const struct ble_hci_ev_le_subev_cs_subevent_result *ev = data;

    if (len != sizeof(*ev)) {
        return BLE_HS_ECONTROLLER;
    }

    return 0;
}

int
ble_hs_hci_evt_le_cs_subevent_result_continue(uint8_t subevent, const void *data,
                                              unsigned int len)
{
    const struct ble_hci_ev_le_subev_cs_subevent_result_continue *ev = data;

    if (len != sizeof(*ev)) {
        return BLE_HS_ECONTROLLER;
    }

    return 0;
}

int
ble_hs_hci_evt_le_cs_test_end_complete(uint8_t subevent, const void *data,
                                       unsigned int len)
{
    const struct ble_hci_ev_le_subev_cs_test_end_complete *ev = data;

    if (len != sizeof(*ev)) {
        return BLE_HS_ECONTROLLER;
    }

    return 0;
}

int
ble_cs_initiator_procedure_start(const struct ble_cs_initiator_procedure_start_params *params)
{
    struct ble_hci_le_cs_rd_loc_supp_cap_rp rsp;
    struct ble_cs_rd_rem_supp_cap_cp cmd;
    int rc;

    /* Channel Sounding setup phase:
     * 1. Set local default CS settings
     * 2. Exchange CS capabilities with the remote
     * 3. Read or write the mode 0 Frequency Actuation Error table
     * 4. Create CS configurations
     * 5. Start the CS Security Start procedure
     */

    cs_state.cb = params->cb;
    cs_state.cb_arg = params->cb_arg;

    cmd.conn_handle = params->conn_handle;
    rc = ble_cs_rd_rem_supp_cap(&cmd);
    if (rc) {
        BLE_HS_LOG(DEBUG, "Failed to read local supported CS capabilities,"
                   "err %dt", rc);
    }

    return rc;
}

int
ble_cs_initiator_procedure_terminate(uint16_t conn_handle)
{
    return 0;
}

int
ble_cs_reflector_setup(struct ble_cs_reflector_setup_params *params)
{
    cs_state.cb = params->cb;
    cs_state.cb_arg = params->cb_arg;

    return 0;
}
#endif
