#pragma once
#include <stdint.h>
#include <vector>
#include <thread>
#include <mutex>
#include <condition_variable>
#include "HciSocket.h"
#include "Utils.h"
#include "Logger.h"
namespace ggk {
class HciAdapter
{
public:
static const int kMaxEventWaitTimeMS = 1000;
static const uint16_t kNonController = 0xffff;
static const int kMinCommandCode = 0x0001;
static const int kMaxCommandCode = 0x0043;
static const char * const kCommandCodeNames[kMaxCommandCode + 1];
static const int kMinEventType = 0x0001;
static const int kMaxEventType = 0x0025;
static const char * const kEventTypeNames[kMaxEventType + 1];
static const int kMinStatusCode = 0x00;
static const int kMaxStatusCode = 0x14;
static const char * const kStatusCodes[kMaxStatusCode + 1];
enum HciControllerSettings
{
EHciPowered = (1<<0),
EHciConnectable = (1<<1),
EHciFastConnectable = (1<<2),
EHciDiscoverable = (1<<3),
EHciBondable = (1<<4),
EHciLinkLevelSecurity = (1<<5),
EHciSecureSimplePairing = (1<<6),
EHciBasicRate_EnhancedDataRate = (1<<7),
EHciHighSpeed = (1<<8),
EHciLowEnergy = (1<<9),
EHciAdvertising = (1<<10),
EHciSecureConnections = (1<<11),
EHciDebugKeys = (1<<12),
EHciPrivacy = (1<<13),
EHciControllerConfiguration = (1<<14),
EHciStaticAddress = (1<<15)
};
struct HciHeader
{
uint16_t code;
uint16_t controllerId;
uint16_t dataSize;
void toNetwork()
{
code = Utils::endianToHci(code);
controllerId = Utils::endianToHci(controllerId);
dataSize = Utils::endianToHci(dataSize);
}
void toHost()
{
code = Utils::endianToHost(code);
controllerId = Utils::endianToHost(controllerId);
dataSize = Utils::endianToHost(dataSize);
}
std::string debugText()
{
std::string text = "";
text += "> Request header\n";
text += " + Command code : " + Utils::hex(code) + " (" + HciAdapter::kCommandCodeNames[code] + ")\n";
text += " + Controller Id : " + Utils::hex(controllerId) + "\n";
text += " + Data size : " + std::to_string(dataSize) + " bytes";
return text;
}
} __attribute__((packed));
struct CommandCompleteEvent
{
HciHeader header;
uint16_t commandCode;
uint8_t status;
CommandCompleteEvent(const std::vector<uint8_t> &data)
{
*this = *reinterpret_cast<const CommandCompleteEvent *>(data.data());
toHost();
Logger::debug(debugText());
}
void toNetwork()
{
header.toNetwork();
commandCode = Utils::endianToHci(commandCode);
}
void toHost()
{
header.toHost();
commandCode = Utils::endianToHost(commandCode);
}
std::string debugText()
{
std::string text = "";
text += "> Command complete event\n";
text += " + Event code : " + Utils::hex(header.code) + " (" + HciAdapter::kEventTypeNames[header.code] + ")\n";
text += " + Controller Id : " + Utils::hex(header.controllerId) + "\n";
text += " + Data size : " + std::to_string(header.dataSize) + " bytes\n";
text += " + Command code : " + Utils::hex(commandCode) + " (" + HciAdapter::kCommandCodeNames[commandCode] + ")\n";
text += " + Status : " + Utils::hex(status);
return text;
}
} __attribute__((packed));
struct CommandStatusEvent
{
HciHeader header;
uint16_t commandCode;
uint8_t status;
CommandStatusEvent(const std::vector<uint8_t> &data)
{
*this = *reinterpret_cast<const CommandStatusEvent *>(data.data());
toHost();
Logger::debug(debugText());
}
void toNetwork()
{
header.toNetwork();
commandCode = Utils::endianToHci(commandCode);
}
void toHost()
{
header.toHost();
commandCode = Utils::endianToHost(commandCode);
}
std::string debugText()
{
std::string text = "";
text += "> Command status event\n";
text += " + Event code : " + Utils::hex(header.code) + " (" + HciAdapter::kEventTypeNames[header.code] + ")\n";
text += " + Controller Id : " + Utils::hex(header.controllerId) + "\n";
text += " + Data size : " + std::to_string(header.dataSize) + " bytes\n";
text += " + Command code : " + Utils::hex(commandCode) + " (" + HciAdapter::kCommandCodeNames[commandCode] + ")\n";
text += " + Status : " + Utils::hex(status) + " (" + HciAdapter::kStatusCodes[status] + ")";
return text;
}
} __attribute__((packed));
struct DeviceConnectedEvent
{
HciHeader header;
uint8_t address[6];
uint8_t addressType;
uint32_t flags;
uint16_t eirDataLength;
DeviceConnectedEvent(const std::vector<uint8_t> &data)
{
*this = *reinterpret_cast<const DeviceConnectedEvent *>(data.data());
toHost();
Logger::debug(debugText());
}
void toNetwork()
{
header.toNetwork();
flags = Utils::endianToHci(flags);
eirDataLength = Utils::endianToHci(eirDataLength);
}
void toHost()
{
header.toHost();
flags = Utils::endianToHost(flags);
eirDataLength = Utils::endianToHost(eirDataLength);
}
std::string debugText()
{
std::string text = "";
text += "> DeviceConnected event\n";
text += " + Event code : " + Utils::hex(header.code) + " (" + HciAdapter::kEventTypeNames[header.code] + ")\n";
text += " + Controller Id : " + Utils::hex(header.controllerId) + "\n";
text += " + Data size : " + std::to_string(header.dataSize) + " bytes\n";
text += " + Address : " + Utils::bluetoothAddressString(address) + "\n";
text += " + Address type : " + Utils::hex(addressType) + "\n";
text += " + Flags : " + Utils::hex(flags) + "\n";
text += " + EIR Data Length : " + Utils::hex(eirDataLength);
if (eirDataLength > 0)
{
text += "\n";
text += " + EIR Data : " + Utils::hex(reinterpret_cast<uint8_t *>(&eirDataLength) + 2, eirDataLength);
}
return text;
}
} __attribute__((packed));
struct DeviceDisconnectedEvent
{
HciHeader header;
uint8_t address[6];
uint8_t addressType;
uint8_t reason;
DeviceDisconnectedEvent(const std::vector<uint8_t> &data)
{
*this = *reinterpret_cast<const DeviceDisconnectedEvent *>(data.data());
toHost();
Logger::debug(debugText());
}
void toNetwork()
{
header.toNetwork();
}
void toHost()
{
header.toHost();
}
std::string debugText()
{
std::string text = "";
text += "> DeviceDisconnected event\n";
text += " + Event code : " + Utils::hex(header.code) + " (" + HciAdapter::kEventTypeNames[header.code] + ")\n";
text += " + Controller Id : " + Utils::hex(header.controllerId) + "\n";
text += " + Data size : " + std::to_string(header.dataSize) + " bytes\n";
text += " + Address : " + Utils::bluetoothAddressString(address) + "\n";
text += " + Address type : " + Utils::hex(addressType) + "\n";
text += " + Reason : " + Utils::hex(reason);
return text;
}
} __attribute__((packed));
struct AdapterSettings
{
uint32_t masks;
void toHost()
{
masks = Utils::endianToHost(masks);
}
bool isSet(HciControllerSettings mask)
{
return (masks & mask) != 0;
}
std::string toString()
{
std::string text = "";
if (isSet(EHciPowered)) { text += "Powered, "; }
if (isSet(EHciConnectable)) { text += "Connectable, "; }
if (isSet(EHciFastConnectable)) { text += "FC, "; }
if (isSet(EHciDiscoverable)) { text += "Discov, "; }
if (isSet(EHciBondable)) { text += "Bondable, "; }
if (isSet(EHciLinkLevelSecurity)) { text += "LLS, "; }
if (isSet(EHciSecureSimplePairing)) { text += "SSP, "; }
if (isSet(EHciBasicRate_EnhancedDataRate)) { text += "BR/EDR, "; }
if (isSet(EHciHighSpeed)) { text += "HS, "; }
if (isSet(EHciLowEnergy)) { text += "LE, "; }
if (isSet(EHciAdvertising)) { text += "Adv, "; }
if (isSet(EHciSecureConnections)) { text += "SC, "; }
if (isSet(EHciDebugKeys)) { text += "DebugKeys, "; }
if (isSet(EHciPrivacy)) { text += "Privacy, "; }
if (isSet(EHciControllerConfiguration)) { text += "ControllerConfig, "; }
if (isSet(EHciStaticAddress)) { text += "StaticAddr, "; }
if (text.length() != 0)
{
text = text.substr(0, text.length() - 2);
}
return text;
}
std::string debugText()
{
std::string text = "";
text += "> Adapter settings\n";
text += " + " + toString();
return text;
}
} __attribute__((packed));
struct ControllerInformation
{
uint8_t address[6];
uint8_t bluetoothVersion;
uint16_t manufacturer;
AdapterSettings supportedSettings;
AdapterSettings currentSettings;
uint8_t classOfDevice[3];
char name[249];
char shortName[11];
void toHost()
{
manufacturer = Utils::endianToHost(manufacturer);
supportedSettings.toHost();
currentSettings.toHost();
}
std::string debugText()
{
std::string text = "";
text += "> Controller information\n";
text += " + Current settings : " + Utils::hex(currentSettings.masks) + "\n";
text += " + Address : " + Utils::bluetoothAddressString(address) + "\n";
text += " + BT Version : " + std::to_string(static_cast<int>(bluetoothVersion)) + "\n";
text += " + Manufacturer : " + Utils::hex(manufacturer) + "\n";
text += " + Supported settings : " + supportedSettings.toString() + "\n";
text += " + Current settings : " + currentSettings.toString() + "\n";
text += " + Name : " + std::string(name) + "\n";
text += " + Short name : " + std::string(shortName);
return text;
}
} __attribute__((packed));
struct VersionInformation
{
uint8_t version;
uint16_t revision;
void toHost()
{
revision = Utils::endianToHost(revision);
}
std::string debugText()
{
std::string text = "";
text += "> Version information\n";
text += " + Version : " + std::to_string(static_cast<int>(version)) + "\n";
text += " + Revision : " + std::to_string(revision);
return text;
}
} __attribute__((packed));
struct LocalName
{
char name[249];
char shortName[11];
std::string debugText()
{
std::string text = "";
text += "> Local name information\n";
text += " + Name : '" + std::string(name) + "'\n";
text += " + Short name : '" + std::string(shortName) + "'";
return text;
}
} __attribute__((packed));
static HciAdapter &getInstance()
{
static HciAdapter instance;
return instance;
}
AdapterSettings getAdapterSettings() { return adapterSettings; }
ControllerInformation getControllerInformation() { return controllerInformation; }
VersionInformation getVersionInformation() { return versionInformation; }
LocalName getLocalName() { return localName; }
int getActiveConnectionCount() { return activeConnections; }
HciAdapter(HciAdapter const&) = delete;
void operator=(HciAdapter const&) = delete;
void sync(uint16_t controllerIndex);
bool start();
void stop();
bool sendCommand(HciHeader &request);
void runEventThread();
private:
HciAdapter() : activeConnections(0) {}
bool waitFor(uint16_t commandCode, int timeoutMS);
HciSocket hciSocket;
static std::thread eventThread;
AdapterSettings adapterSettings;
ControllerInformation controllerInformation;
VersionInformation versionInformation;
LocalName localName;
std::condition_variable conditionalWait;
int conditionalValue;
int activeConnections;
};
};