blob: e11454b97cec2099a1b8410d0e70f8cc686cca5e [file] [log] [blame]
/*
* Copyright 2019 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.
*/
#define LOG_TAG "bt_gd_shim"
#include <cstdint>
#include <memory>
#include <queue>
#include <unordered_map>
#include <vector>
#include "hci/hci_layer.h"
#include "hci/hci_packets.h"
#include "module.h"
#include "os/handler.h"
#include "os/log.h"
#include "packet/raw_builder.h"
#include "shim/hci_layer.h"
namespace bluetooth {
namespace shim {
using TokenQueue = std::queue<const void*>;
using OpCodeTokenQueueMap = std::unordered_map<hci::OpCode, TokenQueue>;
const ModuleFactory HciLayer::Factory = ModuleFactory([]() { return new HciLayer(); });
struct HciLayer::impl {
impl(os::Handler* handler, hci::HciLayer* hci_layer) : handler_(handler), hci_layer_(hci_layer) {}
void OnTransmitPacketCommandComplete(hci::CommandCompleteView view) {
if (command_complete_callback_ == nullptr) {
LOG_WARN("%s Received packet complete with no complete callback registered", __func__);
return;
}
uint16_t command_op_code = static_cast<uint16_t>(view.GetCommandOpCode());
std::vector<const uint8_t> data(view.begin(), view.end());
if (op_code_token_queue_map_.count(view.GetCommandOpCode()) == 0) {
LOG_WARN("%s Received unexpected command complete for opcode:0x%04x", __func__, command_op_code);
return;
}
const void* token = op_code_token_queue_map_[view.GetCommandOpCode()].front();
if (token == nullptr) {
LOG_WARN("%s Received expected command status but no token for opcode:0x%04x", __func__, command_op_code);
return;
}
op_code_token_queue_map_[view.GetCommandOpCode()].pop();
command_complete_callback_(command_op_code, data, token);
}
void OnTransmitPacketStatus(hci::CommandStatusView view) {
if (command_status_callback_ == nullptr) {
LOG_WARN("%s Received packet complete with no status callback registered", __func__);
return;
}
uint16_t command_op_code = static_cast<uint16_t>(view.GetCommandOpCode());
std::vector<const uint8_t> data(view.begin(), view.end());
if (op_code_token_queue_map_.count(view.GetCommandOpCode()) == 0) {
LOG_WARN("%s Received unexpected command status for opcode:0x%04x", __func__, command_op_code);
return;
}
const void* token = op_code_token_queue_map_[view.GetCommandOpCode()].front();
if (token == nullptr) {
LOG_WARN("%s Received expected command status but no token for opcode:0x%04x", __func__, command_op_code);
return;
}
op_code_token_queue_map_[view.GetCommandOpCode()].pop();
uint8_t status = static_cast<uint8_t>(view.GetStatus());
command_status_callback_(command_op_code, data, token, status);
}
void TransmitCommand(uint16_t command, const uint8_t* data, size_t len, const void* token) {
ASSERT(data != nullptr);
ASSERT(token != nullptr);
const hci::OpCode op_code = static_cast<const hci::OpCode>(command);
auto payload = MakeUniquePacket(data, len);
auto packet = hci::CommandPacketBuilder::Create(op_code, std::move(payload));
op_code_token_queue_map_[op_code].push(token);
if (IsCommandStatusOpcode(op_code)) {
hci_layer_->EnqueueCommand(std::move(packet),
common::BindOnce(&impl::OnTransmitPacketStatus, common::Unretained(this)), handler_);
} else {
hci_layer_->EnqueueCommand(std::move(packet),
common::BindOnce(&impl::OnTransmitPacketCommandComplete, common::Unretained(this)),
handler_);
}
}
void RegisterCommandComplete(CommandCompleteCallback callback) {
ASSERT(command_complete_callback_ == nullptr);
command_complete_callback_ = callback;
}
void UnregisterCommandComplete() {
ASSERT(command_complete_callback_ != nullptr);
command_complete_callback_ = nullptr;
}
void RegisterCommandStatus(CommandStatusCallback callback) {
ASSERT(command_status_callback_ == nullptr);
command_status_callback_ = callback;
}
void UnregisterCommandStatus() {
ASSERT(command_status_callback_ != nullptr);
command_status_callback_ = nullptr;
}
private:
os::Handler* handler_{nullptr};
hci::HciLayer* hci_layer_{nullptr};
CommandCompleteCallback command_complete_callback_;
CommandStatusCallback command_status_callback_;
OpCodeTokenQueueMap op_code_token_queue_map_;
/**
* Returns true if expecting command complete, false otherwise
*/
bool IsCommandStatusOpcode(hci::OpCode op_code) {
switch (op_code) {
case hci::OpCode::INQUIRY:
case hci::OpCode::CREATE_CONNECTION:
case hci::OpCode::DISCONNECT:
case hci::OpCode::ACCEPT_CONNECTION_REQUEST:
case hci::OpCode::REJECT_CONNECTION_REQUEST:
case hci::OpCode::CHANGE_CONNECTION_PACKET_TYPE:
case hci::OpCode::AUTHENTICATION_REQUESTED:
case hci::OpCode::SET_CONNECTION_ENCRYPTION:
case hci::OpCode::CHANGE_CONNECTION_LINK_KEY:
case hci::OpCode::MASTER_LINK_KEY:
case hci::OpCode::REMOTE_NAME_REQUEST:
case hci::OpCode::READ_REMOTE_SUPPORTED_FEATURES:
case hci::OpCode::READ_REMOTE_EXTENDED_FEATURES:
case hci::OpCode::READ_REMOTE_VERSION_INFORMATION:
case hci::OpCode::READ_CLOCK_OFFSET:
case hci::OpCode::SETUP_SYNCHRONOUS_CONNECTION:
case hci::OpCode::ACCEPT_SYNCHRONOUS_CONNECTION:
case hci::OpCode::REJECT_SYNCHRONOUS_CONNECTION:
case hci::OpCode::ENHANCED_SETUP_SYNCHRONOUS_CONNECTION:
case hci::OpCode::ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION:
case hci::OpCode::HOLD_MODE:
case hci::OpCode::SNIFF_MODE:
case hci::OpCode::EXIT_SNIFF_MODE:
case hci::OpCode::QOS_SETUP:
case hci::OpCode::SWITCH_ROLE:
case hci::OpCode::FLOW_SPECIFICATION:
case hci::OpCode::REFRESH_ENCRYPTION_KEY:
case hci::OpCode::LE_CREATE_CONNECTION:
case hci::OpCode::LE_CONNECTION_UPDATE:
case hci::OpCode::LE_READ_REMOTE_FEATURES:
case hci::OpCode::LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND:
case hci::OpCode::LE_GENERATE_DHKEY_COMMAND:
case hci::OpCode::LE_SET_PHY:
case hci::OpCode::LE_EXTENDED_CREATE_CONNECTION:
case hci::OpCode::LE_PERIODIC_ADVERTISING_CREATE_SYNC:
return true;
default:
return false;
}
}
std::unique_ptr<packet::RawBuilder> MakeUniquePacket(const uint8_t* data, size_t len) {
packet::RawBuilder builder;
std::vector<uint8_t> bytes(data, data + len);
auto payload = std::make_unique<packet::RawBuilder>();
payload->AddOctets(bytes);
return payload;
}
};
void HciLayer::TransmitCommand(uint16_t op_code, const uint8_t* data, size_t len, const void* token) {
pimpl_->TransmitCommand(op_code, data, len, std::move(token));
}
void HciLayer::RegisterCommandComplete(CommandCompleteCallback callback) {
pimpl_->RegisterCommandComplete(callback);
}
void HciLayer::UnregisterCommandComplete() {
pimpl_->UnregisterCommandComplete();
}
void HciLayer::RegisterCommandStatus(CommandStatusCallback callback) {
pimpl_->RegisterCommandStatus(callback);
}
void HciLayer::UnregisterCommandStatus() {
pimpl_->UnregisterCommandStatus();
}
/**
* Module methods
*/
void HciLayer::ListDependencies(ModuleList* list) {
list->add<hci::HciLayer>();
}
void HciLayer::Start() {
LOG_INFO("%s Starting controller shim layer", __func__);
pimpl_ = std::make_unique<impl>(GetHandler(), GetDependency<hci::HciLayer>());
}
void HciLayer::Stop() {
pimpl_.reset();
}
} // namespace shim
} // namespace bluetooth