blob: e982a7e5559d42dac7da7e7fbce123d59f3feaa2 [file] [log] [blame] [edit]
// Copyright 2022 The Android Open Source Project
//
// 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 "hci/hci_packet_transport.h"
#include <limits>
#include <memory>
#include <optional>
#include "hci/hci_debug.h"
#include "hci_packet.pb.h"
#include "model/hci/hci_transport.h"
#include "packet_hub/packet_hub.h"
#include "util/log.h"
using netsim::packet::HCIPacket;
namespace netsim {
namespace hci {
std::unordered_map<uint32_t, std::shared_ptr<HciPacketTransport>>
device_to_transport_;
/**
* @class HciPacketTransport
*
* Connects hci packets between packet_hub and rootcanal.
*
*/
HciPacketTransport::HciPacketTransport(
std::shared_ptr<rootcanal::AsyncManager> async_manager)
: mDeviceId(std::nullopt), mAsyncManager(std::move(async_manager)) {}
/**
* @brief Connect the phy device to the transport
*
* @param - device_id identifier of the owning device
*/
void HciPacketTransport::Connect(rootcanal::PhyDevice::Identifier device_id) {
assert(!mDeviceId.has_value());
mDeviceId.emplace(device_id);
}
// Called by HCITransport (rootcanal)
void HciPacketTransport::SendEvent(const std::vector<uint8_t> &data) {
this->Response(HCIPacket::EVENT, data);
}
// Called by HCITransport (rootcanal)
void HciPacketTransport::SendAcl(const std::vector<uint8_t> &data) {
this->Response(HCIPacket::ACL, data);
}
// Called by HCITransport (rootcanal)
void HciPacketTransport::SendSco(const std::vector<uint8_t> &data) {
this->Response(HCIPacket::SCO, data);
}
// Called by HCITransport (rootcanal)
void HciPacketTransport::SendIso(const std::vector<uint8_t> &data) {
this->Response(HCIPacket::ISO, data);
}
// Called by HCITransport (rootcanal)
void HciPacketTransport::RegisterCallbacks(
rootcanal::PacketCallback commandCallback,
rootcanal::PacketCallback aclCallback,
rootcanal::PacketCallback scoCallback,
rootcanal::PacketCallback isoCallback,
rootcanal::CloseCallback closeCallback) {
BtsLog("hci_packet_transport: registered");
mCommandCallback = commandCallback;
mAclCallback = aclCallback;
mScoCallback = scoCallback;
mIsoCallback = isoCallback;
mCloseCallback = closeCallback;
}
// Called by HCITransport (rootcanal)
void HciPacketTransport::Tick() {}
void HciPacketTransport::Request(
packet::HCIPacket_PacketType packet_type,
const std::shared_ptr<std::vector<uint8_t>> &packet) {
auto packet_callback = PacketTypeCallback(packet_type);
if (!packet_callback) {
BtsLog("hci_transport: unknown packet_callback");
return;
}
if (packet_type == HCIPacket::COMMAND) {
auto cmd = HciCommandToString(packet->at(0), packet->at(1));
}
// Copy the packet bytes for rootcanal.
mAsyncManager->Synchronize(
[packet_callback, packet]() { packet_callback(packet); });
}
void HciPacketTransport::Add(
rootcanal::PhyDevice::Identifier device_id,
const std::shared_ptr<HciPacketTransport> &transport) {
transport->Connect(device_id);
device_to_transport_[device_id] = transport;
}
void HciPacketTransport::Close() {
if (mDeviceId.has_value()) {
device_to_transport_.erase(mDeviceId.value());
}
BtsLog("hci_packet_transport close from rootcanal");
mDeviceId = std::nullopt;
}
// Send response to packet_hub.
// TODO: future optimization by having rootcanal send shared_ptr.
void HciPacketTransport::Response(packet::HCIPacket_PacketType packet_type,
const std::vector<uint8_t> &packet) {
if (!mDeviceId.has_value()) {
BtsLog("hci_packet_transport: response with no device.");
return;
}
auto shared_packet = std::make_shared<std::vector<uint8_t>>(packet);
netsim::packet_hub::HandleBtResponse(mDeviceId.value(), packet_type,
shared_packet);
}
rootcanal::PacketCallback HciPacketTransport::PacketTypeCallback(
packet::HCIPacket_PacketType packet_type) {
switch (packet_type) {
case HCIPacket::COMMAND:
assert(mCommandCallback);
return mCommandCallback;
case HCIPacket::ACL:
return mAclCallback;
case HCIPacket::SCO:
return mScoCallback;
case HCIPacket::ISO:
return mIsoCallback;
default:
BtsLog("hci_transport Ignoring unknown packet.");
return nullptr;
}
}
// handle_request is the main entry for incoming packets called by
// netsim::packet_hub
//
// Transfer the request to the HciTransport to deliver to Rootcanal via the
// acl/sco/iso/command callback methods under synchronization.
void handle_bt_request(uint32_t facade_id,
packet::HCIPacket_PacketType packet_type,
const std::shared_ptr<std::vector<uint8_t>> &packet) {
if (device_to_transport_.find(facade_id) != device_to_transport_.end() &&
device_to_transport_[facade_id]) {
auto transport = device_to_transport_[facade_id];
transport->Request(packet_type, packet);
} else {
std::cout << "device_to_transport_ ids ";
for (auto [k, _] : device_to_transport_) std::cout << k << " ";
std::cout << std::endl;
BtsLog(
"hci_packet_transport: handle_request with no transport for device %d",
facade_id);
}
}
} // namespace hci
} // namespace netsim