aboutsummaryrefslogtreecommitdiff
path: root/src/HciAdapter.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/HciAdapter.h')
-rw-r--r--src/HciAdapter.h368
1 files changed, 349 insertions, 19 deletions
diff --git a/src/HciAdapter.h b/src/HciAdapter.h
index b5e8ee7..a19d395 100644
--- a/src/HciAdapter.h
+++ b/src/HciAdapter.h
@@ -36,6 +36,7 @@
#include <stdint.h>
#include <vector>
+#include <thread>
#include "HciSocket.h"
#include "Utils.h"
@@ -46,7 +47,53 @@ class HciAdapter
{
public:
- struct Header
+ //
+ // Constants
+ //
+
+ // 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;
@@ -66,11 +113,51 @@ public:
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;
+
+ 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 ResponseEvent
+ struct CommandStatusEvent
{
- Header header;
+ HciHeader header;
uint16_t commandCode;
uint8_t status;
@@ -86,8 +173,248 @@ public:
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;
+
+ 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;
+
+ 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<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));
+
+ //
+ // Accessors
+ //
+
+ // Returns the instance to this singleton class
+ static HciAdapter &getInstance()
+ {
+ static HciAdapter instance;
+ return instance;
+ }
+
+ AdapterSettings getAdapterSettings();
+ ControllerInformation getControllerInformation();
+ VersionInformation getVersionInformation();
+ LocalName getLocalName();
+
+ 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
//
// If a connection already exists, this method will do nothing and return true.
@@ -115,28 +442,31 @@ public:
// a failure is returned.
//
// Returns true on success, otherwise false
- bool sendCommand(Header &request, ResponseEvent &response, int responseLen);
+ bool sendCommand(HciHeader &request);
-private:
-
- // Reads a response from the HCI socket
+ // Event processor, responsible for receiving events from the HCI socket
//
- // Responses are generally triggered by sending commands (see `sendCommand`) but not always. In HCI parlance, a response is
- // actually an event. Performing commands triggers events. There is not always a 1:1 ratio betwee command and event, and a
- // command may trigger different events based on the result of the command.
- //
- // Unlike the other methods in this class, this method does not auto-connect, as this method is private and can only be called
- // from methods that have alreay auto-connected.
- bool readResponse(uint16_t commandCode, ResponseEvent &response, size_t responseLen) const;
+ // This mehtod should not be called directly. Rather, it runs continuously on a thread until the server shuts down
+ void runEventThread();
- // Filter out events that we aren't interested in
- //
- // NOTE: We're just dipping our toe into the HCI stuff here, so we only care about command complete and status events. This
- // isn't the most robust way to do things, but it is effective.
- bool filterAndValidateEvents(uint16_t commandCode, std::vector<uint8_t> &buffer) const;
+private:
+ // Private constructor for our Singleton
+ HciAdapter() : activeConnections(0) {}
// 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;
+
+ // Our active connection count
+ int activeConnections;
};
}; // namespace ggk \ No newline at end of file