From 6feca1dc60a7e3277c504e50b566f08760c69baa Mon Sep 17 00:00:00 2001 From: Paul Nettle Date: Wed, 27 Sep 2017 19:37:25 -0500 Subject: Added a threaded worker that listens for HCI events, resolving a number of problem areas. --- src/HciAdapter.h | 368 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 349 insertions(+), 19 deletions(-) (limited to 'src/HciAdapter.h') 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 #include +#include #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(&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(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(); + 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 &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 -- cgit v1.2.3