// Copyright 2017 Paul Nettle.
//
// This file is part of Gobbledegook.
//
// Gobbledegook is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Gobbledegook is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Gobbledegook. If not, see .
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// >>
// >>> INSIDE THIS FILE
// >>
//
// Protocol-level code for the Bluetooth Management API, which is used to configure the Bluetooth adapter
//
// >>
// >>> DISCUSSION
// >>
//
// This class is intended for use by `Mgmt` (see Mgmt.cpp).
//
// See the discussion at the top of HciAdapter.cpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#pragma once
#include
#include
#include
#include
#include
#include "HciSocket.h"
#include "Utils.h"
#include "Logger.h"
namespace ggk {
class HciAdapter
{
public:
//
// Constants
//
// How long to wait for a response event for commands sent to the adapter
static const int kMaxEventWaitTimeMS = 1000;
// A constant referring to a 'non-controller' (for commands that do not require a controller index)
static const uint16_t kNonController = 0xffff;
// Command code names
static const int kMinCommandCode = 0x0001;
static const int kMaxCommandCode = 0x0043;
static const char * const kCommandCodeNames[kMaxCommandCode + 1];
// Event type names
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];
//
// Types
//
// HCI Controller Settings
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 &data)
{
*this = *reinterpret_cast(data.data());
toHost();
// Log it
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 &data)
{
*this = *reinterpret_cast(data.data());
toHost();
// Log it
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 &data)
{
*this = *reinterpret_cast(data.data());
toHost();
// Log it
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(&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 &data)
{
*this = *reinterpret_cast(data.data());
toHost();
// Log it
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;
}
// Returns a human-readable string of flags and settings
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));
// The comments documenting these fields are very high level. There is a lot of detailed information not present, for example
// some values are not available at all times. This is fully documented in:
//
// https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/mgmt-api.txt
struct ControllerInformation
{
uint8_t address[6]; // The Bluetooth address
uint8_t bluetoothVersion; // Bluetooth version
uint16_t manufacturer; // The manufacturer
AdapterSettings supportedSettings; // Bits for various supported settings (see HciControllerSettings)
AdapterSettings currentSettings; // Bits for various currently configured settings (see HciControllerSettings)
uint8_t classOfDevice[3]; // Um, yeah. That.
char name[249]; // Null terminated name
char shortName[11]; // Null terminated short name
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(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(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));
//
// Accessors
//
// Returns the instance to this singleton class
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; }
//
// Disallow copies of our singleton (c++11)
//
HciAdapter(HciAdapter const&) = delete;
void operator=(HciAdapter const&) = delete;
// Reads current values from the controller
//
// This effectively requests data from the controller but that data may not be available instantly, but within a few
// milliseconds. Therefore, it is not recommended attempt to retrieve the results from their accessors immediately.
void sync(uint16_t controllerIndex);
// Connects the HCI socket if a connection does not already exist and starts the run thread
//
// If a connection already exists, this method will fail
//
// Note that it shouldn't be necessary to connect manually; any action requiring a connection will automatically connect
//
// Returns true if the HCI socket is connected (either via a new connection or an existing one), otherwise false
bool start();
// Waits for the HciAdapter run thread to join
//
// This method will block until the thread joins
void stop();
// Sends a command over the HCI socket
//
// If the HCI socket is not connected, it will auto-connect prior to sending the command. In the case of a failed auto-connect,
// a failure is returned.
//
// Returns true on success, otherwise false
bool sendCommand(HciHeader &request);
// Event processor, responsible for receiving events from the HCI socket
//
// This mehtod should not be called directly. Rather, it runs continuously on a thread until the server shuts down
void runEventThread();
private:
// Private constructor for our Singleton
HciAdapter() : commandResponseLock(commandResponseMutex), activeConnections(0) {}
// Uses a std::condition_variable to wait for a response event for the given `commandCode` or `timeoutMS` milliseconds.
//
// Returns true if the response event was received for `commandCode` or false if the timeout expired.
//
// Command responses are set via `setCommandResponse()`
bool waitForCommandResponse(uint16_t commandCode, int timeoutMS);
// Sets the command response and notifies the waiting std::condition_variable (see `waitForCommandResponse`)
void setCommandResponse(uint16_t commandCode);
// Our HCI Socket, which allows us to talk directly to the kernel
HciSocket hciSocket;
// Our event thread listens for events coming from the adapter and deals with them appropriately
static std::thread eventThread;
// Our adapter information
AdapterSettings adapterSettings;
ControllerInformation controllerInformation;
VersionInformation versionInformation;
LocalName localName;
std::condition_variable cvCommandResponse;
std::mutex commandResponseMutex;
std::unique_lock commandResponseLock;
int conditionalValue;
// Our active connection count
int activeConnections;
};
}; // namespace ggk