Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9bfc706f authored by Jakub Pawlowski's avatar Jakub Pawlowski Committed by Gerrit Code Review
Browse files

Merge "btm: Initial Iso Manager implementation"

parents 395c6218 d093be05
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -69,6 +69,10 @@
      "name" : "net_test_btcore",
      "host" : true
    },
    {
      "name" : "net_test_btm_iso",
      "host" : true
    },
    {
      "name" : "net_test_btpackets",
      "host" : true
+40 −0
Original line number Diff line number Diff line
@@ -118,6 +118,7 @@ cc_library_static {
        "btm/btm_main.cc",
        "acl/btm_pm.cc",
        "btm/btm_sco.cc",
        "btm/btm_iso.cc",
        "btm/btm_sec.cc",
        "btu/btu_hcif.cc",
        "btu/btu_task.cc",
@@ -499,3 +500,42 @@ cc_test {
    ],
}


// Iso manager unit tests
cc_test {
    name: "net_test_btm_iso",
    test_suites: ["device-tests"],
    host_supported: true,
    defaults: ["fluoride_defaults"],
    local_include_dirs: [
        "btm",
        "include",
        "test/common",
    ],
    include_dirs: [
        "packages/modules/Bluetooth/system",
        "packages/modules/Bluetooth/system/btcore/include",
    ],
    srcs: [
        "btm/btm_iso.cc",
        "test/btm_iso_test.cc",
        "test/common/mock_controller.cc",
        "test/common/mock_hcic_layer.cc",
    ],
    static_libs: [
        "libbt-common",
        "libgmock",
        "liblog",
        "libosi",
    ],
    sanitize: {
        cfi: true,
        scs: true,
        address: true,
        all_undefined: true,
        integer_overflow: true,
        diag: {
            undefined : true
        },
    },
}
+1 −0
Original line number Diff line number Diff line
@@ -90,6 +90,7 @@ static_library("stack") {
    "btm/btm_dev.cc",
    "btm/btm_devctl.cc",
    "btm/btm_inq.cc",
    "btm/btm_iso.cc",
    "btm/btm_main.cc",
    "btm/btm_pm.cc",
    "btm/btm_sco.cc",
+120 −0
Original line number Diff line number Diff line
/*
 * Copyright 2020 HIMSA II K/S - www.himsa.com.
 * Represented by EHIMA - www.ehima.com
 *
 * Licensed 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 <memory>

#include "btm_iso_api.h"
#include "btm_iso_impl.h"
#include "btu.h"

using bluetooth::hci::iso_manager::CigCallbacks;
using bluetooth::hci::iso_manager::iso_impl;

namespace bluetooth {
namespace hci {

struct IsoManager::impl {
  impl(const IsoManager& iso_manager) : iso_manager_(iso_manager) {}

  void Start() {
    LOG_ASSERT(!iso_impl_);
    iso_impl_ = std::make_unique<iso_impl>();
  }

  void Stop() {
    LOG_ASSERT(iso_impl_);
    iso_impl_.reset();
  }

  bool IsRunning() { return iso_impl_ ? true : false; }

  const IsoManager& iso_manager_;
  std::unique_ptr<iso_impl> iso_impl_;
};

IsoManager::IsoManager() : pimpl_(std::make_unique<impl>(*this)) {}

void IsoManager::RegisterCigCallbacks(CigCallbacks* callbacks) const {
  pimpl_->iso_impl_->handle_register_cis_callbacks(callbacks);
}

void IsoManager::CreateCig(uint8_t cig_id,
                           struct iso_manager::cig_create_params cig_params) {
  pimpl_->iso_impl_->create_cig(cig_id, std::move(cig_params));
}

void IsoManager::ReconfigureCig(
    uint8_t cig_id, struct iso_manager::cig_create_params cig_params) {
  pimpl_->iso_impl_->reconfigure_cig(cig_id, std::move(cig_params));
}

void IsoManager::RemoveCig(uint8_t cig_id) {
  pimpl_->iso_impl_->remove_cig(cig_id);
}

void IsoManager::EstablishCis(
    struct iso_manager::cis_establish_params conn_params) {
  pimpl_->iso_impl_->establish_cis(std::move(conn_params));
}

void IsoManager::DisconnectCis(uint16_t cis_handle, uint8_t reason) {
  pimpl_->iso_impl_->disconnect_cis(cis_handle, reason);
}

void IsoManager::SetupIsoDataPath(
    uint16_t iso_handle, struct iso_manager::iso_data_path_params path_params) {
  pimpl_->iso_impl_->setup_iso_data_path(iso_handle, std::move(path_params));
}

void IsoManager::RemoveIsoDataPath(uint16_t iso_handle, uint8_t data_path_dir) {
  pimpl_->iso_impl_->remove_iso_data_path(iso_handle, data_path_dir);
}

void IsoManager::SendIsoData(uint16_t iso_handle, const uint8_t* data,
                             uint16_t data_len) {
  pimpl_->iso_impl_->send_iso_data(iso_handle, data, data_len);
}

void IsoManager::HandleIsoData(void* p_msg) {
  if (pimpl_->IsRunning())
    pimpl_->iso_impl_->handle_iso_data(static_cast<BT_HDR*>(p_msg));
}

void IsoManager::HandleDisconnect(uint16_t handle, uint8_t reason) {
  if (pimpl_->IsRunning())
    pimpl_->iso_impl_->disconnection_complete(handle, reason);
}

void IsoManager::HandleNumComplDataPkts(uint8_t* p, uint8_t evt_len) {
  if (pimpl_->IsRunning())
    pimpl_->iso_impl_->handle_num_completed_pkts(p, evt_len);
}

void IsoManager::HandleHciEvent(uint8_t sub_code, uint8_t* params,
                                uint16_t length) {
  if (pimpl_->IsRunning())
    pimpl_->iso_impl_->on_iso_event(sub_code, params, length);
}

void IsoManager::Start() { pimpl_->Start(); }

void IsoManager::Stop() { pimpl_->Stop(); }

IsoManager::~IsoManager() = default;

}  // namespace hci
}  // namespace bluetooth
+504 −0
Original line number Diff line number Diff line
/*
 * Copyright 2020 HIMSA II K/S - www.himsa.com.
 * Represented by EHIMA - www.ehima.com
 *
 * Licensed 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.
 */

#pragma once

#include <map>
#include <memory>
#include <set>

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "bt_types.h"
#include "btm_iso_api.h"
#include "btu.h"
#include "common/time_util.h"
#include "device/include/controller.h"
#include "osi/include/log.h"

namespace bluetooth {
namespace hci {
namespace iso_manager {
static constexpr uint8_t kIsoDataInTsBtHdrOffset = 0x0C;
static constexpr uint8_t kIsoHeaderWithTsLen = 12;
static constexpr uint8_t kIsoHeaderWithoutTsLen = 8;

static constexpr uint8_t kStateFlagsNone = 0x00;
static constexpr uint8_t kStateFlagIsConnected = 0x01;
static constexpr uint8_t kStateFlagHasDataPathSet = 0x02;

struct iso_sync_info {
  uint32_t first_sync_ts;
  uint16_t seq_nb;
};

struct iso_base {
  uint8_t cig_id;

  struct iso_sync_info sync_info;
  uint8_t state_flags;
  uint32_t sdu_itv;
};

typedef iso_base iso_cis;

struct iso_impl {
  iso_impl() {
    iso_credits_ = controller_get_interface()->get_iso_buffer_count();
    iso_buffer_size_ = controller_get_interface()->get_iso_data_size();
  }

  ~iso_impl() {}

  void handle_register_cis_callbacks(CigCallbacks* callbacks) {
    LOG_ASSERT(callbacks != nullptr) << "Invalid CIG callbacks";
    cig_callbacks_ = callbacks;
  }

  void on_set_cig_params(uint8_t cig_id, uint32_t sdu_itv_mtos, uint8_t* stream,
                         uint16_t len) {
    uint8_t cis_cnt;
    uint16_t conn_handle;
    cig_create_cmpl_evt evt;

    LOG_ASSERT(cig_callbacks_ != nullptr) << "Invalid CIG callbacks";
    LOG_ASSERT(len >= 3) << "Invalid packet length.";

    STREAM_TO_UINT8(evt.status, stream);
    STREAM_TO_UINT8(evt.cig_id, stream);
    STREAM_TO_UINT8(cis_cnt, stream);

    uint8_t evt_code = IsCigKnown(cig_id) ? kIsoEventCigOnReconfigureCmpl
                                          : kIsoEventCigOnCreateCmpl;

    if (evt.status == HCI_SUCCESS) {
      LOG_ASSERT(len >= (3) + (cis_cnt * sizeof(uint16_t)))
          << "Invalid CIS count.";

      /* Remove entries for the reconfigured CIG */
      if (evt_code == kIsoEventCigOnReconfigureCmpl) {
        auto cis_it = conn_hdl_to_cis_map_.cbegin();
        while (cis_it != conn_hdl_to_cis_map_.cend()) {
          if (cis_it->second->cig_id == evt.cig_id)
            cis_it = conn_hdl_to_cis_map_.erase(cis_it);
          else
            ++cis_it;
        }
      }

      evt.conn_handles.reserve(cis_cnt);
      for (int i = 0; i < cis_cnt; i++) {
        STREAM_TO_UINT16(conn_handle, stream);

        evt.conn_handles.push_back(conn_handle);
        conn_hdl_to_cis_map_[conn_handle] = std::unique_ptr<iso_cis>(
            new iso_cis({.sync_info = {.first_sync_ts = 0, .seq_nb = 0},
                         .cig_id = cig_id,
                         .state_flags = kStateFlagsNone,
                         .sdu_itv = sdu_itv_mtos}));
      }
    }

    cig_callbacks_->OnCigEvent(evt_code, &evt);
  }

  void create_cig(uint8_t cig_id,
                  struct iso_manager::cig_create_params cig_params) {
    LOG_ASSERT(!IsCigKnown(cig_id)) << "Invalid cig - already exists.";

    btsnd_hcic_set_cig_params(
        cig_id, cig_params.sdu_itv_mtos, cig_params.sdu_itv_stom,
        cig_params.sca, cig_params.packing, cig_params.framing,
        cig_params.max_trans_lat_stom, cig_params.max_trans_lat_mtos,
        cig_params.cis_cfgs.size(), cig_params.cis_cfgs.data(),
        base::BindOnce(&iso_impl::on_set_cig_params, base::Unretained(this),
                       cig_id, cig_params.sdu_itv_mtos));
  }

  void reconfigure_cig(uint8_t cig_id,
                       struct iso_manager::cig_create_params cig_params) {
    LOG_ASSERT(IsCigKnown(cig_id)) << "No such cig";

    btsnd_hcic_set_cig_params(
        cig_id, cig_params.sdu_itv_mtos, cig_params.sdu_itv_stom,
        cig_params.sca, cig_params.packing, cig_params.framing,
        cig_params.max_trans_lat_stom, cig_params.max_trans_lat_mtos,
        cig_params.cis_cfgs.size(), cig_params.cis_cfgs.data(),
        base::BindOnce(&iso_impl::on_set_cig_params, base::Unretained(this),
                       cig_id, cig_params.sdu_itv_mtos));
  }

  void on_remove_cig(uint8_t* stream, uint16_t len) {
    cig_remove_cmpl_evt evt;

    LOG_ASSERT(cig_callbacks_ != nullptr) << "Invalid CIG callbacks";
    LOG_ASSERT(len == 2) << "Invalid packet length.";

    STREAM_TO_UINT8(evt.status, stream);
    STREAM_TO_UINT8(evt.cig_id, stream);

    if (evt.status == HCI_SUCCESS) {
      auto cis_it = conn_hdl_to_cis_map_.cbegin();
      while (cis_it != conn_hdl_to_cis_map_.cend()) {
        if (cis_it->second->cig_id == evt.cig_id)
          cis_it = conn_hdl_to_cis_map_.erase(cis_it);
        else
          ++cis_it;
      }
    }

    cig_callbacks_->OnCigEvent(kIsoEventCigOnRemoveCmpl, &evt);
  }

  void remove_cig(uint8_t cig_id) {
    LOG_ASSERT(IsCigKnown(cig_id)) << "No such cig";

    btsnd_hcic_remove_cig(cig_id, base::BindOnce(&iso_impl::on_remove_cig,
                                                 base::Unretained(this)));
  }

  void establish_cis(struct iso_manager::cis_establish_params conn_params) {
    for (auto& el : conn_params.conn_pairs) {
      auto cis = GetCisIfKnown(el.cis_conn_handle);
      LOG_ASSERT(cis) << "No such cis";
      LOG_ASSERT(!(cis->state_flags & kStateFlagIsConnected))
          << "Already connected";
    }
    btsnd_hcic_create_cis(conn_params.conn_pairs.size(),
                          conn_params.conn_pairs.data());
  }

  void disconnect_cis(uint16_t cis_handle, uint8_t reason) {
    auto cis = GetCisIfKnown(cis_handle);
    LOG_ASSERT(cis) << "No such cis";
    LOG_ASSERT(cis->state_flags & kStateFlagIsConnected) << "Not connected";
    btsnd_hcic_disconnect(cis_handle, reason);
  }

  void on_setup_iso_data_path(uint8_t* stream, uint16_t len) {
    uint8_t status;
    uint16_t conn_handle;

    STREAM_TO_UINT8(status, stream);
    STREAM_TO_UINT16(conn_handle, stream);

    iso_base* iso = GetIsoIfKnown(conn_handle);
    LOG_ASSERT(iso != nullptr) << "Invalid connection handle: " << +conn_handle;

    if (status == HCI_SUCCESS) iso->state_flags |= kStateFlagHasDataPathSet;

    LOG_ASSERT(cig_callbacks_ != nullptr) << "Invalid CIG callbacks";
    cig_callbacks_->OnSetupIsoDataPath(status, conn_handle, iso->cig_id);
  }

  void setup_iso_data_path(
      uint16_t conn_handle,
      struct iso_manager::iso_data_path_params path_params) {
    iso_base* iso = GetIsoIfKnown(conn_handle);
    LOG_ASSERT(iso != nullptr) << "No such iso connection: " << +conn_handle;

    LOG_ASSERT(iso->state_flags & kStateFlagIsConnected)
        << "CIS not established";

    btsnd_hcic_setup_iso_data_path(
        conn_handle, path_params.data_path_dir, path_params.data_path_id,
        path_params.codec_id_format, path_params.codec_id_company,
        path_params.codec_id_vendor, path_params.controller_delay,
        std::move(path_params.codec_conf),
        base::BindOnce(&iso_impl::on_setup_iso_data_path,
                       base::Unretained(this)));
  }

  void on_remove_iso_data_path(uint8_t* stream, uint16_t len) {
    uint8_t status;
    uint16_t conn_handle;

    STREAM_TO_UINT8(status, stream);
    STREAM_TO_UINT16(conn_handle, stream);

    iso_base* iso = GetIsoIfKnown(conn_handle);
    LOG_ASSERT(iso != nullptr) << "Invalid connection handle: " << +conn_handle;

    if (status == HCI_SUCCESS) iso->state_flags &= ~kStateFlagHasDataPathSet;

    LOG_ASSERT(cig_callbacks_ != nullptr) << "Invalid CIG callbacks";
    cig_callbacks_->OnRemoveIsoDataPath(status, conn_handle, iso->cig_id);
  }

  void remove_iso_data_path(uint16_t iso_handle, uint8_t data_path_dir) {
    iso_base* iso = GetIsoIfKnown(iso_handle);
    LOG_ASSERT(iso != nullptr) << "No such iso connection";
    LOG_ASSERT((iso->state_flags & kStateFlagHasDataPathSet) ==
               kStateFlagHasDataPathSet)
        << "Data path not set";

    btsnd_hcic_remove_iso_data_path(
        iso_handle, data_path_dir,
        base::BindOnce(&iso_impl::on_remove_iso_data_path,
                       base::Unretained(this)));
  }

  BT_HDR* prepare_ts_hci_packet(uint16_t iso_handle, uint32_t ts,
                                uint16_t seq_nb, uint16_t data_len) {
    /* Add 2 for packet seq., 2 for length, 4 for the timestamp */
    uint16_t iso_data_load_len = data_len + 8;

    /* Add 2 for handle, 2 for length */
    uint16_t iso_full_len = iso_data_load_len + 4;
    BT_HDR* packet = (BT_HDR*)osi_malloc(iso_full_len + sizeof(BT_HDR));
    packet->len = iso_full_len;
    packet->offset = 0;
    packet->event = MSG_STACK_TO_HC_HCI_ISO;
    packet->layer_specific = 0;

    uint8_t* packet_data = packet->data;
    UINT16_TO_STREAM(packet_data, iso_handle);
    UINT16_TO_STREAM(packet_data, iso_data_load_len);

    packet->layer_specific |= BT_ISO_HDR_CONTAINS_TS;
    UINT32_TO_STREAM(packet_data, ts);

    UINT16_TO_STREAM(packet_data, seq_nb);
    UINT16_TO_STREAM(packet_data, data_len);

    return packet;
  }

  void send_iso_data_hci_packet(BT_HDR* packet) {
    bte_main_hci_send(packet, MSG_STACK_TO_HC_HCI_ISO | 0x0001);
  }

  void send_iso_data(uint16_t iso_handle, const uint8_t* data,
                     uint16_t data_len) {
    iso_base* iso = GetIsoIfKnown(iso_handle);
    LOG_ASSERT(iso != nullptr)
        << "No such iso connection handle: " << +iso_handle;

    LOG_ASSERT(iso->state_flags & kStateFlagIsConnected)
        << "CIS not established";
    LOG_ASSERT(iso->state_flags & kStateFlagHasDataPathSet)
        << "Data path not set for handle: " << +iso_handle;

    /* Calculate sequence number for the ISO data packet.
     * It should be incremented by 1 every SDU Interval.
     */
    uint32_t ts = bluetooth::common::time_get_os_boottime_us();
    iso->sync_info.seq_nb = (ts - iso->sync_info.first_sync_ts) / iso->sdu_itv;

    if (iso_credits_ == 0 || data_len > iso_buffer_size_) {
      LOG(WARNING) << __func__ << ", dropping ISO packet, len: "
                   << static_cast<int>(data_len)
                   << ", iso credits: " << static_cast<int>(iso_credits_);
      return;
    }

    iso_credits_--;

    BT_HDR* packet =
        prepare_ts_hci_packet(iso_handle, ts, iso->sync_info.seq_nb, data_len);
    memcpy(packet->data + kIsoDataInTsBtHdrOffset, data, data_len);
    send_iso_data_hci_packet(packet);
  }

  void process_cis_est_pkt(uint8_t len, uint8_t* data) {
    cis_establish_cmpl_evt evt;

    LOG_ASSERT(len == 28) << "Invalid packet length";
    LOG_ASSERT(cig_callbacks_ != nullptr) << "Invalid CIG callbacks";

    STREAM_TO_UINT8(evt.status, data);
    STREAM_TO_UINT16(evt.cis_conn_hdl, data);

    auto cis = GetCisIfKnown(evt.cis_conn_hdl);
    LOG_ASSERT(cis != nullptr) << "No such cis";

    cis->sync_info.first_sync_ts = bluetooth::common::time_get_os_boottime_us();

    STREAM_TO_UINT24(evt.cig_sync_delay, data);
    STREAM_TO_UINT24(evt.cis_sync_delay, data);
    STREAM_TO_UINT24(evt.trans_lat_mtos, data);
    STREAM_TO_UINT24(evt.trans_lat_stom, data);
    STREAM_TO_UINT8(evt.phy_mtos, data);
    STREAM_TO_UINT8(evt.phy_stom, data);
    STREAM_TO_UINT8(evt.nse, data);
    STREAM_TO_UINT8(evt.bn_mtos, data);
    STREAM_TO_UINT8(evt.bn_stom, data);
    STREAM_TO_UINT8(evt.ft_mtos, data);
    STREAM_TO_UINT8(evt.ft_stom, data);
    STREAM_TO_UINT16(evt.max_pdu_mtos, data);
    STREAM_TO_UINT16(evt.max_pdu_stom, data);
    STREAM_TO_UINT16(evt.iso_itv, data);

    if (evt.status == HCI_SUCCESS) cis->state_flags |= kStateFlagIsConnected;

    evt.cig_id = cis->cig_id;
    cig_callbacks_->OnCisEvent(kIsoEventCisEstablishCmpl, &evt);
  }

  void disconnection_complete(uint16_t handle, uint8_t reason) {
    /* Check if this is an ISO handle */
    auto cis = GetCisIfKnown(handle);
    if (cis == nullptr) return;

    LOG_ASSERT(cig_callbacks_ != nullptr) << "Invalid CIG callbacks";

    LOG_INFO("%s flags: %d", __func__, +cis->state_flags);
    if (cis->state_flags & kStateFlagIsConnected) {
      cis_disconnected_evt evt = {
          .reason = reason,
          .cis_conn_hdl = handle,
          .cig_id = cis->cig_id,
      };

      cig_callbacks_->OnCisEvent(kIsoEventCisDisconnected, &evt);
      cis->state_flags &= ~kStateFlagIsConnected;
      /* Data path is considered still valid, but can be reconfigured only once
       * CIS is reestablished.
       */
    }
  }

  void handle_num_completed_pkts(uint8_t* p, uint8_t evt_len) {
    uint8_t num_handles;

    STREAM_TO_UINT8(num_handles, p);

    LOG_ASSERT(evt_len == num_handles * 4 + 1);

    for (int i = 0; i < num_handles; i++) {
      uint16_t handle, num_sent;

      STREAM_TO_UINT16(handle, p);
      STREAM_TO_UINT16(num_sent, p);

      if (conn_hdl_to_cis_map_.find(handle) == conn_hdl_to_cis_map_.end())
        continue;

      iso_credits_ += num_sent;
    }
  }

  void on_iso_event(uint8_t code, uint8_t* packet, uint16_t packet_len) {
    switch (code) {
      case HCI_BLE_CIS_EST_EVT:
        process_cis_est_pkt(packet_len, packet);
        break;
      case HCI_BLE_CREATE_BIG_CPL_EVT:
        /* TODO: Implement */
        break;
      case HCI_BLE_TERM_BIG_CPL_EVT:
        /* TODO: Implement */
        break;
      case HCI_BLE_CIS_REQ_EVT:
        /* Not supported */
        break;
      case HCI_BLE_BIG_SYNC_EST_EVT:
        /* Not supported */
        break;
      case HCI_BLE_BIG_SYNC_LOST_EVT:
        /* Not supported */
        break;
      default:
        LOG_ERROR("Unhandled event code %d", +code);
    }
  }

  void handle_iso_data(BT_HDR* p_msg) {
    const uint8_t* stream = p_msg->data;
    cis_data_evt evt;
    uint16_t handle, seq_nb;

    if (p_msg->len <= ((p_msg->layer_specific & BT_ISO_HDR_CONTAINS_TS)
                           ? kIsoHeaderWithTsLen
                           : kIsoHeaderWithoutTsLen))
      return;

    LOG_ASSERT(cig_callbacks_ != nullptr) << "Invalid CIG callbacks";

    STREAM_TO_UINT16(handle, stream);
    evt.cis_conn_hdl = HCID_GET_HANDLE(handle);

    iso_base* iso = GetCisIfKnown(evt.cis_conn_hdl);
    if (iso == nullptr) {
      LOG(ERROR) << __func__ << ", received data for the non-registered CIS!";
      return;
    }

    STREAM_SKIP_UINT16(stream);
    if (p_msg->layer_specific & BT_ISO_HDR_CONTAINS_TS)
      STREAM_TO_UINT32(evt.ts, stream);

    STREAM_TO_UINT16(seq_nb, stream);

    uint32_t ts = bluetooth::common::time_get_os_boottime_us();
    uint32_t new_calc_seq_nb =
        (ts - iso->sync_info.first_sync_ts) / iso->sdu_itv;
    if (new_calc_seq_nb == iso->sync_info.seq_nb) ++new_calc_seq_nb;

    if (iso->sync_info.seq_nb == 0) {
      evt.evt_lost = 0;
    } else {
      evt.evt_lost = new_calc_seq_nb - iso->sync_info.seq_nb - 1;
      if (evt.evt_lost > 0) {
        LOG(WARNING) << evt.evt_lost << " packets possibly lost.";
      }

      if (new_calc_seq_nb != seq_nb) {
        LOG(WARNING) << "Sequence number mismatch. "
                        "Adjusting own time reference point.";
        iso->sync_info.first_sync_ts = ts - (seq_nb * iso->sdu_itv);
        new_calc_seq_nb = seq_nb;
      }
    }
    iso->sync_info.seq_nb = new_calc_seq_nb;

    evt.p_msg = p_msg;
    evt.cig_id = iso->cig_id;
    cig_callbacks_->OnCisEvent(kIsoEventCisDataAvailable, &evt);
  }

  iso_cis* GetCisIfKnown(uint16_t cis_conn_handle) {
    auto cis_it = conn_hdl_to_cis_map_.find(cis_conn_handle);
    return (cis_it != conn_hdl_to_cis_map_.end()) ? cis_it->second.get()
                                                  : nullptr;
  }

  iso_base* GetIsoIfKnown(uint16_t iso_handle) {
    return GetCisIfKnown(iso_handle);
  }

  bool IsCigKnown(uint8_t cig_id) const {
    auto const cis_it =
        std::find_if(conn_hdl_to_cis_map_.cbegin(), conn_hdl_to_cis_map_.cend(),
                     [&cig_id](auto& kv_pair) {
                       return (kv_pair.second->cig_id == cig_id);
                     });
    return (cis_it != conn_hdl_to_cis_map_.cend());
  }

  std::map<uint16_t, std::unique_ptr<iso_cis>> conn_hdl_to_cis_map_;

  uint16_t iso_credits_;
  uint16_t iso_buffer_size_;

  CigCallbacks* cig_callbacks_ = nullptr;
};

}  // namespace iso_manager
}  // namespace hci
}  // namespace bluetooth
Loading