#include <string.h>
#include "HciAdapter.h"
#include "HciSocket.h"
#include "Utils.h"
#include "Logger.h"
namespace ggk {
static const int kMinCommandCode = 0x0001;
static const int kMaxCommandCode = 0x0043;
static const char *kCommandCodeNames[kMaxCommandCode] =
{
"Read Management Version Information Command",
"Read Management Supported Commands Command",
"Read Controller Index List Command",
"Read Controller Information Command",
"Set Powered Command",
"Set Discoverable Command",
"Set Connectable Command",
"Set Fast Connectable Command",
"Set Bondable Command",
"Set Link Security Command",
"Set Secure Simple Pairing Command",
"Set High Speed Command",
"Set Low Energy Command",
"Set Device Class",
"Set Local Name Command",
"Add UUID Command",
"Remove UUID Command",
"Load Link Keys Command",
"Load Long Term Keys Command",
"Disconnect Command",
"Get Connections Command",
"PIN Code Reply Command",
"PIN Code Negative Reply Command",
"Set IO Capability Command",
"Pair Device Command",
"Cancel Pair Device Command",
"Unpair Device Command",
"User Confirmation Reply Command",
"User Confirmation Negative Reply Command",
"User Passkey Reply Command",
"User Passkey Negative Reply Command",
"Read Local Out Of Band Data Command",
"Add Remote Out Of Band Data Command",
"Remove Remote Out Of Band Data Command",
"Start Discovery Command",
"Stop Discovery Command",
"Confirm Name Command",
"Block Device Command",
"Unblock Device Command",
"Set Device ID Command",
"Set Advertising Command",
"Set BR/EDR Command",
"Set Static Address Command",
"Set Scan Parameters Command",
"Set Secure Connections Command",
"Set Debug Keys Command",
"Set Privacy Command",
"Load Identity Resolving Keys Command",
"Get Connection Information Command",
"Get Clock Information Command",
"Add Device Command",
"Remove Device Command",
"Load Connection Parameters Command",
"Read Unconfigured Controller Index List Command",
"Read Controller Configuration Information Command",
"Set External Configuration Command",
"Set Public Address Command",
"Start Service Discovery Command",
"Read Local Out Of Band Extended Data Command",
"Read Extended Controller Index List Command",
"Read Advertising Features Command",
"Add Advertising Command",
"Remove Advertising Command",
"Get Advertising Size Information Command",
"Start Limited Discovery Command",
"Read Extended Controller Information Command",
"Set Appearance Command",
};
static const int kMinEventType = 0x0001;
static const int kMaxEventType = 0x0025;
static const char *kEventTypeNames[kMaxEventType] =
{
"Command Complete Event",
"Command Status Event",
"Controller Error Event",
"Index Added Event",
"Index Removed Event",
"New Settings Event",
"Class Of Device Changed Event",
"Local Name Changed Event",
"New Link Key Event",
"New Long Term Key Event",
"Device Connected Event",
"Device Disconnected Event",
"Connect Failed Event",
"PIN Code Request Event",
"User Confirmation Request Event",
"User Passkey Request Event",
"Authentication Failed Event",
"Device Found Event",
"Discovering Event",
"Device Blocked Event",
"Device Unblocked Event",
"Device Unpaired Event",
"Passkey Notify Event",
"New Identity Resolving Key Event",
"New Signature Resolving Key Event",
"Device Added Event",
"Device Removed Event",
"New Connection Parameter Event",
"Unconfigured Index Added Event",
"Unconfigured Index Removed Event",
"New Configuration Options Event",
"Extended Index Added Event",
"Extended Index Removed Event",
"Local Out Of Band Extended Data Updated Event",
"Advertising Added Event",
"Advertising Removed Event",
"Extended Controller Information Changed Event"
};
bool HciAdapter::connect()
{
if (!isConnected() && !hciSocket.connect())
{
disconnect();
return false;
}
return true;
}
bool HciAdapter::isConnected() const
{
return hciSocket.isConnected();
}
void HciAdapter::disconnect()
{
if (isConnected())
{
hciSocket.disconnect();
}
}
bool HciAdapter::sendCommand(Header &request, ResponseEvent &response, int responseLen)
{
if (!connect()) { return false; }
Logger::debug(" + Request header");
Logger::debug(SSTR << " + Event code : " << Utils::hex(request.code));
Logger::debug(SSTR << " + Controller Id : " << Utils::hex(request.controllerId));
Logger::debug(SSTR << " + Data size : " << request.dataSize << " bytes");
request.toNetwork();
uint8_t *pRequest = reinterpret_cast<uint8_t *>(&request);
std::vector<uint8_t> requestPacket = std::vector<uint8_t>(pRequest, pRequest + sizeof(request) + request.dataSize);
if (!hciSocket.write(requestPacket))
{
return false;
}
return readResponse(request.code, response, responseLen);
}
bool HciAdapter::readResponse(uint16_t commandCode, ResponseEvent &response, size_t responseLen) const
{
if (!hciSocket.isConnected()) { return false; }
std::vector<uint8_t> responsePacket = std::vector<uint8_t>();
if (!hciSocket.read(responsePacket))
{
return false;
}
if (!filterAndValidateEvents(commandCode, responsePacket))
{
return false;
}
if (responsePacket.size() < 2)
{
Logger::error(SSTR << "Command's response was invalid");
return false;
}
uint16_t eventCode = Utils::endianToHost(*reinterpret_cast<uint16_t *>(responsePacket.data()));
if (eventCode != 1)
{
return false;
}
if (responsePacket.size() != responseLen)
{
Logger::error(SSTR << "Command's response was " << responsePacket.size() << " bytes but was supposed to be " << responseLen << " bytes");
return false;
}
memcpy(&response, responsePacket.data(), responseLen);
response.toHost();
Logger::debug(" + Response event");
Logger::debug(SSTR << " + Event code : " << Utils::hex(response.header.code));
Logger::debug(SSTR << " + Controller Id : " << Utils::hex(response.header.controllerId));
Logger::debug(SSTR << " + Data size : " << response.header.dataSize << " bytes");
Logger::debug(SSTR << " + Command code : " << response.commandCode);
Logger::debug(SSTR << " + Status : " << Utils::hex(response.status));
int adjustedHeaderSize = sizeof(ResponseEvent) - 3;
int expectedDataSize = responseLen - adjustedHeaderSize;
if (response.header.dataSize != expectedDataSize)
{
Logger::error(SSTR << "The data size from the response (" << response.header.dataSize
<< ") does not match the response structure (" << expectedDataSize
<< ": " << responseLen << " - " << adjustedHeaderSize
<< ") - this is likely a kernel version mismatch or bug in the code");
}
return true;
}
bool HciAdapter::filterAndValidateEvents(uint16_t commandCode, std::vector<uint8_t> &buffer) const
{
std::vector<uint8_t> lastGoodEvent;
while (!buffer.empty())
{
ResponseEvent *pEvent = reinterpret_cast<ResponseEvent *>(buffer.data());
size_t dataLength = sizeof(Header) + pEvent->header.dataSize;
if (dataLength > buffer.size())
{
Logger::error(SSTR << " + Not enough data for the current event");
return false;
}
if (pEvent->header.code < kMinEventType || pEvent->header.code > kMaxEventType)
{
Logger::error(SSTR << " + Unknown event type " << Utils::hex(pEvent->header.code) << " - ditching all response data to resynchronize");
return false;
}
std::string eventTypeName = kEventTypeNames[pEvent->header.code - kMinEventType];
std::string commandCodeName = pEvent->commandCode < kMinCommandCode || pEvent->commandCode > kMaxCommandCode ? "Unknown" : kCommandCodeNames[pEvent->commandCode - kMinCommandCode];
Logger::debug(SSTR << " + Received event type " << Utils::hex(pEvent->header.code) << " (" << eventTypeName << ")");
if (pEvent->header.code != 1 && pEvent->commandCode != commandCode)
{
Logger::debug(SSTR << " + Skipping event type " << Utils::hex(pEvent->header.code) << " (" << eventTypeName << ")");
}
else
{
lastGoodEvent = std::vector<uint8_t>(buffer.begin(), buffer.begin() + dataLength);
}
buffer.erase(buffer.begin(), buffer.begin() + dataLength);
}
if (lastGoodEvent.empty())
{
return false;
}
buffer = lastGoodEvent;
return true;
}
};